基于Wi-Fi Direct的音频传输系统(APP前端+Java服务端)
資源下載地址:https://download.csdn.net/download/sheziqiong/85996746
資源下載地址:https://download.csdn.net/download/sheziqiong/85996746
摘要
我們實現了一個軟件系統:它可以通過Wi-Fi Direct建立一個網絡,自主發現網絡中的某一設備并向該設備發送音頻信號。該設備收到音頻信號后,如果有多個來源,將對這些音頻信號混頻。最終這個設備講接收的音頻信號播放。
目錄
摘要 1
一、 Wi-Fi Direct技術 1
二、 服務發現協議、心跳協議和音頻控制協議 1
三、 RTP協議和RTCP協議 3
2.服務發現協議、心跳協議和音頻控制協議
在該部分,我們稱音頻信號的發送者為Client端,音頻信號的接受者為Server端。
在我們設想的情景中,Server端可能是網絡中的任意一臺設備,因此Server端的地址是不確定的。如果讓用戶自行尋找Server端的地址,這顯然是一件不合理的事情,因此我們需要設計一個服務發現協議,讓Client端可以自動的發現網絡中的Server端。同時,為了確保通信的正確進行,我們需要Server端和Client端每隔一段時間都去確認雙方是否都還在當前網路中。為此我們設計了心跳協議。
為了方便Server端接收并播放音頻流,Client端在開始發送音頻前和停止發送音頻后,都應該向Server端告知自身當前的播放狀態。因此我們設計了音頻控制協議。
服務發現協議和心跳協議如圖1所示。其過程大致如下:
Client端:向網絡廣播消息“client”,表示有新的Client加入網絡中。
Server端:在接受到廣播消息后,向廣播源(即對應的Client)發送消息“server”,表明自己的身份。
心跳協議由Client端定時執行。其過程大致如下:
Client端:向Server發送心跳消息“server?”并等待回復。如果不能在較短時間內接收到Server的回復,則認為該Server已經離開網絡。
Server端:接收到Client的心跳消息后回復消息“serverYes”。如果在規定時間內沒有接收到Client的心跳消息,則認為該Client已經離開網絡。
圖1:服務發現協議和心跳協議
音頻控制協議如圖2所示。音頻控制協議包含兩種子協議:請求播放和請求暫停。其過程大致如下:
請求播放:
-
Client端:向Server發送消息“play”。
-
Server端:接收到消息后,準備好接收音頻流,并向Client發送消息“playOk”。
-
Client端:接收到消息后,通過RTP協議向Server發送音頻信號。
請求暫停:
- Client端:向Server發送消息“stop”并停止發送音頻信號。
圖2:音頻控制協議
3.RTP協議和RTCP協議
3.1 RTP概覽
我們項目中對音頻信號的傳輸是通過RTP實現的。RTP協議詳細說明了在互聯網上傳輸音頻和視頻的標準數據包格式,常用于流媒體系統中,是一個多播協議但也可以用于單播應用中,非常符合我們項目的需求。
RTP協議是一種基于UDP的無連接的傳輸協議,和RTCP控制協議一起使用。RTP協議為Internet上端到端的實時傳輸提供時間信息和流同步,但它并不保證服務質量。服務質量控制功能由RTCP協議來提供。
圖3展示了一個典型流媒體應用的體系結構。RTP協議被劃分在傳輸層,建立在UDP上。因此,同UDP協議一樣,為了實現實時傳輸功能,RTP也有固定的封裝形式,接下來將具體介紹RTP數據包的封裝結構。
圖3:典型流媒體應用的體系結構
3.2 RTP的封裝格式
圖4展示了RTP報文的頭部格式。其各部分含義如下:
- 版本號(V):2比特,用來標志使用的RTP版本。
- 填充位(P):1比特,如果該位置位,則該RTP包的尾部就包含附加的填充字節。
- 擴展位(X):1比特,如果該位置位的話,RTP固定頭部后面就跟有一個擴展頭部。
- CSRC計數器(CC):4比特,含有固定頭部后面跟著的CSRC的數目。
- 標記位(M):1比特,該位的解釋由配置文檔(Profile)來承擔.
- 載荷類型(PT):7比特,標識了RTP載荷的類型。
- 序列號(SN):16比特,發送方在每發送完一個RTP包后就將該域的值增加1,接收方可以由該域檢測包的丟失及恢復包序列。序列號的初始值是隨機的。
- 時間戳(timestamp):32比特,記錄了該包中數據的第一個字節的采樣時刻。在一次會話開始時,時間戳初始化成一個初始值。即使在沒有信號發送時,時間戳的數值也要隨時間而不斷地增加。時間戳是去除抖動和實現同步不可缺少的。
- 同步源標識符(SSRC):32比特,同步源就是指RTP包流的來源。在同一個RTP會話中不能有兩個相同的SSRC值。該標識符是隨機選取的,用于唯一標識RTP會話中的參與者。
- 貢獻源列表(CSRC List):0~15項,每項32比特,用來標志對一個RTP混合器產生的新包有貢獻的所有RTP包的源。由混合器將這些有貢獻的SSRC標識符插入表中。SSRC標識符都被列出來,以便接收端能正確指出交談雙方的身份。(混合器用于需要將多個源的音頻包進行合并以及接收者能接收的音頻編碼格式不一致的情況中)
圖4:RTP報文的頭部格式
3.3 RTCP的封裝結構
RTCP也是基于UDP進行傳送的,封裝的主要是一些控制信息,用于檢測與反饋服務質量、同步媒體、標識多播組中的成員等等。在RTP會話期間,各參與者周期性地傳送RTCP包。RTCP包中含有已發送的數據包的數量、丟失的數據包的數量等統計資料,各參與者可以利用這些信息動態地改變傳輸速率。RTP和RTCP配合使用,它們能以有效的反饋和最小的開銷使傳輸效率最佳化,因而特別適合傳送網上的實時數據。
由于RTCP僅包含一些控制信息,因此分組長度很短,可以將很多RTCP分組封裝在一個UDP包中。RTCP有如下五種分組類型:
| 200 | SR(Sender Report) | 發送端報告 |
| 201 | RR(Receiver Report) | 接收端報告 |
| 202 | SDES(Source Description Items) | 源點描述 |
| 203 | BYE | 結束傳輸 |
| 204 | APP | 特定應用 |
上述五種封裝類型的內部結構基本相同,下面以SR為例進行介紹:
發送端報告分組SR用于使發送端以多播的方式向所有接收端報告發送情況,主要內容包括:相應RTP流的SSRC值、RTP流中最新產生的RTP分組的時間戳、RTP流包含的分組數、RTP流包含的字節數等。圖5展示了RTCP SR頭的結構,其中各部分的含義如下:
- 版本(V):同RTP包頭域。
- 填充(P):同RTP包頭域。
- 接收報告計數器(RC):5比特,該SR包中的接收報告塊的數目,可以為零。
- 包類型(PT):8比特,對應上表的五種分組類型。
- 長度域(Length):16比特,其中存放的是該SR包以32比特為單位的總長度減一。
- 同步源(SSRC):SR包發送者的同步源標識符。與對應RTP包中的SSRC一樣。
- NTP Timestamp(Network time protocol)SR包發送時的絕對時間值。NTP的作用是同步不同的RTP媒體流。
- RTP Timestamp:與NTP時間戳對應,與RTP數據包中的RTP時間戳具有相同的單位和隨機初始值。
- Sender’s packet count:從開始發送包到產生這個SR包這段時間里,發送者發送的RTP數據包的總數. SSRC改變時,這個域清零。
- Sender’s octet count:從開始發送包到產生這個SR包這段時間里,發送者發送的凈荷數據的總字節數(不包括頭部和填充)。發送者改變其SSRC時,這個域要清零。
- 同步源n的SSRC標識符:該報告塊中包含的是從該源接收到的包的統計信息。
- 丟失率(Fraction Lost):表明從上一個SR或RR包發出以來從同步源n(SSRC_n)來的RTP數據包的丟失率。
- 累計的包丟失數目:從開始接收到SSRC_n的包到發送SR,從SSRC_n傳過來的RTP數據包的丟失總數。
- 收到的擴展最大序列號:從SSRC_n收到的RTP數據包中最大的序列號,
- 接收抖動(Interarrival jitter):RTP數據包接受時間的統計方差估計
- 上次SR時間戳(Last SR,LSR):取最近從SSRC_n收到的SR包中的NTP時間戳的中間32比特。如果目前還沒收到SR包,則該域清零。
- 上次SR以來的延時(Delay since last SR,DLSR):上次從SSRC_n收到SR包到發送本報告的延時。
圖5:RTCP SR頭的結構
4.4 RTP的會話過程
當應用程序建立一個RTP會話時,需要首先確定一對目的傳輸地址,包含一個網絡IP地址以及一對端口號。這對端口號一個用于RTP包,一個用于RTCP包,其中RTP數據發向偶數的UDP端口,RTCP包發向相鄰的奇數UDP端口,這樣就構成了一個UDP端口對。圖6展示了RTP包發送的過程。接收過程與此相反。
圖6:RTP包的發送流程
4.5 音頻數據的處理
處理數據前,我們會把音頻數據解碼為PCM格式。發送端讀取音頻文件獲取音頻輸入流,將音頻流切割為指定的塊大小,然后通過RTP發送。接收端收到字節流后對其進行處理,通過已打開的相應格式的源數據行將處理過的字節流寫入數據行中,通過混頻器傳遞到輸出端口。圖7展示了這個過程。
圖7:音頻數據的傳輸過程
對于每個發送者,動態添加新的與之對應的接收buffer和混合buffer,所有buffer大小一定。每次接收到數據時將數據存入到該數據發送者對應的接收buffer中(I),當接收buffer滿,數據轉移到混合buffer中。如果混合buffer為空,直接轉移(II),若混合buffer滿,考慮到兩個及以上個發送者時,進一步檢測是否所有的buffer均滿(III)。若所有的混合buffer均滿,將當前所有發送者的混合buffer進行混音,發送到輸出端口進行播放,并清空當前所有發送者的混合buffer(IV);若有混合buffer未滿,則丟棄當前接收者混合buffer中的數據,直接將接收buffer數據轉移(V)。圖8和圖9分別展示了單發送端和多發送端的這一處理過程。
同時,在每一輪接收數據過程中,檢測每一個發送者多少輪沒發送過數據。若一定輪次里均沒有發送過數據,則動態刪除該發送者的接收和混合buffer。
圖8:單發送端處理過程
圖8:多發送端處理過程
我們采用的是帶符號位的PCM編碼方式,每一字節數據在-128~127之間。混音時,取兩個數據包的每一字節,進行以下運算。這樣兩個聲音就可以混合起來。
技術細節
在我們的實現中,心跳協議的重復時間間隔為30秒,Client端等待時間為1秒。考慮到網絡傳輸的延時,Server端等待時間為30.5秒。
為了實現RTP包的封裝,我們使用jlibrtp庫。圖9展示了jlibrtp庫的使用過程:
- 首先,調用jlibrtp庫的實時傳輸會話類RTPSession建立收發端的會話。該類可以創建一個RTP會話,并設置傳輸的RTP端口和RTCP端口,以及與RTP包的相關的時間戳數據等。
- 然后,通過RTPSessionRegister方法用于添加RTP會話的參與者,同時開啟接收包的AppCallerThread線程類。線程類的run方法在接受到RTP數據包時,會去掉RTP包頭,獲取傳輸數據內容,并調用回調函數receiveData。在該函數中,我們將RTP負載存入buffer,然后進行音頻數據處理和混頻。
圖9:jlibrtp庫的使用過程
在實現音頻數據處理和混頻過程中,我們選擇每個數據塊的大小為1024字節,buffer的大小為30個數據塊。發送者30輪未發送數據后將會被刪除。
buffer的大小會影響丟包率。如果buffer過小,會導致丟包率升高。buffer過大,則會浪費大量的內存空間。
實現內容
1.安卓端
安卓端 Server 和 Client 由同一軟件完成,如圖10所示。在初始界面運行服務發現協議,由一臺終端發起連接 (Create New Network) 與其它終端互聯 (Search)。在網絡中終端互相成功發現后,每個終端可以通過按鈕切換在音頻傳輸中擔任的角色 (Server/Client)。通常每一組網絡中有且僅有一個終端為音頻 Server,用來接收網絡中 Client 的音頻數據并混音播放。
圖10:安卓終端界面
2.PC端
圖11:PC終端界面
PC端在進入軟件開始便選擇自身音頻傳輸中的身份 (Server/Client)。Server 端可以直接啟動并持續監聽網絡中是否有 Client 傳送數據。Client 端則需要搜索網絡中存在的 Server,主動與其連接并選擇音頻文件進行發送。
資源下載地址:https://download.csdn.net/download/sheziqiong/85996746
資源下載地址:https://download.csdn.net/download/sheziqiong/85996746
總結
以上是生活随笔為你收集整理的基于Wi-Fi Direct的音频传输系统(APP前端+Java服务端)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: linux strtol
- 下一篇: 限制浏览器翻译功能