對於語音通信而言,語音碼率較低,添加適當冗余是對抗網絡丟包的常見方式。冗余方式有多種,包括RED
,FEC
等都是冗余的一種,如果冗余份數較多,可以采取交織的方式實現。RFC 3350
是RTP的基礎標准協議,RFC 2198
是冗余數據RTP封裝的標准協議,RFC 5109
是添加FEC數據的RTP封裝標准協議。
RTP格式(RFC 3350)
文檔地址:RTP: A Transport Protocol for Real-Time Applications
RTP(Real-time Transport Protocol, 實時傳輸協議)是互聯網上常見的處理媒體數據流的網絡協議,包括單播和多播等多種場景下的網絡環境中媒體數據的傳輸。RTP是一種應用層協議,一般使用UDP作為底層協議實現數據傳輸,但並不強制底層協議的選擇。RTP不提供任何機制來保證實時的傳輸和服務質量保證,而是由底層的服務來完成。也就是說,它不保證可靠傳輸和按序傳輸,不假定下層網絡是否可靠,不限制按照順序傳送數據包。
RTP一般與RTCP同時出現,端口號相鄰。一般而言,RTP負責傳輸數據,RTCP用於傳輸控制信息,比如提供數據傳輸質量的反饋。RTCP為每個RTP源提供一個固定的識別符CNAME
。當SSRC因重啟或者沖突發生改變時,可以更加CNAME
跟蹤參與者;或者用CNAME
關聯一系列相關RTP會話中來自同一個成員的多個數據流,如用來同步音頻和視頻。注意需要控制RTCP流的快速增長,一般不超過總占用帶寬的5%。
RTP頭格式如下:
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|V=2|P|X| CC |M| PT | sequence number |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| timestamp |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| synchronization source (SSRC) identifier |
+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
| contributing source (CSRC) identifiers |
| .... |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
其中,主要字段有:
V
,version,2比特,RTP版本標識,定義了兩個版本。第一個RTP草案版本使用1標識,在vat
音頻工具的最初實現中使用0標識。P
,padding,1比特。如果設定padding,在報文末端會包含一個或多個padding字節,這不屬於負載playload。最后一個字節的padding有一個計數器以標識需要忽略多少個padding字節(包括自己)。一些加密算法可能需要固定塊長度的padding。X
,extension,1比特。如果 X=1,則在 RTP 報頭后將有且僅有一個擴展報頭。CC
,CSRC count,4比特。CSRC count標識了定長頭字段中包含的CSRC identifier的數量。M
,marker,1比特。由具體協議解釋其意義,用來在比特流中標記重要的事件,如音視頻幀邊界。PT
,payload type,7比特。payload type設置RTP負載的格式,由應用程序解釋該字段含義。接收方必須忽略未知的playload type數據包。sequence number
,16比特。序列號,每發送一個RTP數據包,序列號加1,接收端可以據此檢測丟包和重建包序列。timestamp
,32比特。時間戳,反映了RTP數據包中第一個八位字節的采樣時間。時間戳的初始值應當是隨機的,類似序號。時鍾頻率依賴於負載數據格式,因此時間戳增量依賴於當前數據格式和策略。如果RTP數據包周期性產生,那么將使用采樣時鍾確定的標稱采樣時刻,而非讀取系統時間。舉例而言,對於固定采樣率的音頻,時間戳時鍾可能會在每個采樣周期增加1;如果音頻應用程序從輸入設備讀取覆蓋160個采樣周期的塊,則對於每個這樣的塊,時間戳將增加160,無論該塊是在分組中傳輸還是作為靜默丟棄。SSRC
,Synchronization source,32比特。同步源,接收方可以將一個同步源的包放在一起進行重放。SSRC是一個隨機值,在特定的RTP會話中是全局唯一的。CSRC
,Contributing source,0~15項,32比特。若一個 RTP 流的源,對由 RTP 混頻器生成的組合流起了作用,則它就是一個作用源。
RED格式(RFC 2198)
文檔地址:RTP Payload for Redundant Audio Data
RTP本身不包含任何的質量保證,因此面對網絡擾動時表現較差,因此在通信過程中增加冗余數據,即使存在丟包,也可以在一定程度上恢復。RFC 2198
主要針對音頻數據。
- 每個包必須攜帶一個主包(主編碼數據),以及一個或多個冗余編碼數據;
- 冗余信息可以有多種形式,但每一個冗余塊必須有一個編碼類型標識;
- 可能存在變長編碼,因此每一個數據塊都存在長度標識;
- 每個編碼塊都有自己的時間戳,對於冗余塊可以參考與主包的時間戳差值。
增加RTP頭擴展或采用新的一個或多個負載類型,即使用PT
標識。鑒於使用擴展頭會帶來很多限制,因此建議采取新增負載類型的方式。在RTP中,冗余頭格式如下:
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|F| block PT | timestamp offset | block length |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
其中:
F
,1比特,標識后面是否還存在其它的塊。設置為1表示存在;0表示不存在,該塊為最后的頭塊。block PT
,7比特,該數據塊的RTP playload type。timestamp offset
,14比特,相對於RTP header中時間戳的偏移量,使用unsigned表示。block length
,10比特,對應於除了頭長度的有效負載長度。
主包頭位於整個數據包的最后,PT
和timestamp
和RTP數據包頭相同,格式如下:
0 1 2 3 4 5 6 7
+-+-+-+-+-+-+-+-+
|0| Block PT |
+-+-+-+-+-+-+-+-+
最后一個頭之后就是數據塊,存儲順序和頭的排序順序相同。數據塊之間不需要填充或者使用其它分隔,一般不需要32位對齊。主要是為了在損失一定的解碼時間降低寬帶負擔。如下包括一個DIV4(8kHz)主包和8kHz的LPC冗余編碼包:
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|V=2|P|X| CC=0 |M| PT | sequence number of primary |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| timestamp of primary encoding |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| synchronization source (SSRC) identifier |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|1| block PT=7 | timestamp offset | block length |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|0| block PT=5 | |
+-+-+-+-+-+-+-+-+ +
| |
+ LPC encoded redundant data (PT=7) +
| (14 bytes) |
+ +---------------+
| | |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +
| |
+ +
| |
+ +
| |
+ +
| DVI4 encoded primary data (PT=5) |
+ (84 bytes, not to scale) +
/ /
+ +
| |
+ +
| |
+ +---------------+
| |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
RED SDP協商
在進行雙方的通信協商時,需要通知雙方的SDP(Session Description Protocol,會話描述協議)完成信息交互,協議中建議的SDP形式如下:
m=audio 12345 RTP/AVP 121 0 5
a=rtpmap:121 red/8000/1
a=fmtp:121 0/5
其中,121為冗余編碼的PT值,0為PCM主編碼,5為DVI4第二編碼。
一般FEC的RTP格式(RFC 2733)
文檔地址:An RTP Payload Format for Generic Forward Error Correction
在網絡上,語音質量不佳的主要原因在於高丟包率。也正是基於此,FEC才被考慮解決丟包的問題,為了更好的應用這些糾錯碼,必須有相關的協議支持。
前置知識
FEC(Forward Error Correction,前向糾錯)可以確保信號的長距可靠傳輸。FEC技術是一種廣泛應用於通信系統的編碼技術。以典型的分組碼為例,在發送端通過將k比特信息作為分組進行編碼,加入(n-k)比特的冗余校驗信息,組成長度為n比特的碼字。碼字通過信道到達接收端之后,如果錯誤在可糾正的范圍之內,通過譯碼就可檢查並糾正錯誤的比特,從而抵抗信道帶來的干擾,提高通信的可靠性。校驗位長度(n-k)與信息位長度k的比值,稱為編碼開銷。開銷越大,FEC的理論極限性能越高,但性能的提高並不是線性增加的,開銷越大,增加的性能提升就越小。
RFC 2733
RFC 2733定義了一種傳輸實時媒體的一般性的RTP負載格式,一般性意味着:
- FEC協議獨立於其保護的媒體數據,該媒體數據可以是音頻、視頻或者其它;
- 具有足夠的靈活性以支持不同的FEC機制;
- 自適應設計,使得FEC技術修改但不影響傳輸的信令;
- 支持許多不同的傳輸FEC包機制。
基本原理
RFC 2733負載格式支持基於異或校驗算法的FEC機制。發送端在媒體流中取出一些包,並對其負載、RTC頭中的組件進行異或操作,得到包含FEC信息的RTP包,產生的FEC包可以在接收端恢復與這個FEC包關聯的任何一個包。實際使用中,是時延以及恢復能力之間的一種平衡。
為了能正常恢復,發送方還需要在負載格式中攜帶信息,包括哪些媒體包用來生成FEC包。接收端可以不了解糾錯碼的細節,使用FEC,發送端具有較大的靈活性並可根據網絡環境進行自適應的編碼,而接收端仍能正常恢復。FEC包在生成后立即發出,接收端即使不支持FEC也不影響媒體流的正常接收。但是也存在一些FEC糾錯碼,不傳輸原始媒體流,只使用FEC流,這樣做的缺點在於所有接收者都必須支持FEC。
FEC包可以不與媒體包在同一個RTP流中發送,以一個單獨的流進行發送,或者作為以各種冗余編碼發送(如RFC 2198)。當作為單獨流發送時,FEC包具有獨立的序列號空間,但時間戳從對應的媒體包獲得,單調遞增。FEC包流支持任何固定差值的包頭壓縮機制,對接收端來說,如果沒有丟包,則忽略FEC包,如果出現丟包,則FEC聯合收到的關聯生成的媒體包組,完全恢復丟失媒體包。
通用FEC方案
定義函數\(f(x,y,...)\)對包\(x,y,...\)等的異或操作,被叫做奇偶校驗包。為簡單起見,奇偶校驗包就是輸入的各個輸入包按位異或得到的,而使用奇偶校驗包對數據包進行恢復是通過產生這些校驗包的一組數據包完成,這一組數據包必須是線性無關的。
舉例而言,若兩個數據包產生一個校驗包。原始數據包\(a,b,c,d\),發送端產生數據包如下,時間從左至右:
a b c d <-- media stream
f(a,b) f(c,d) <-- FEC stream
如果數據包\(b\)丟失,則可以通過\(a\)和\(f(a,b)\)恢復。
-
方案一
a b c d e <-- media stream f(a,b) f(b,c) f(c,d) f(d,e) <-- FEC stream
方案一類似於上面的示例。但是在發送數據包\(b\)之前發送\(f(a,b)\)。該方案會增加發送端延遲,但允許對連續兩個數據包突發丟失的恢復。
-
方案二
f(a,b) f(a,c) f(a,b,c) f(c,d) f(c,e) f(c,d,e) <-- FEC stream
原始數據包並不是必傳的,在方案二中僅僅發送FEC數據包。該方案允許所有單數據包和一些連續數據包的丟失恢復,但開銷略低於方案一。
-
方案三
a b c d <-- media stream f(a,b,c) f(a,c,d) f(a,b,d) <-- FEC stream
該方案允許從連續一個、兩個或三個數據包丟失中恢復。
通用FEC的RTP格式
如果FEC作為獨立流發送,媒體數據包格式與傳輸不受影響;如果作為冗余編碼發送,參考RFC 2198將媒體數據作為主數據包,次編碼傳輸FEC包。
通過在RTP負載中放置FEC頭和FEC有效載荷來構造FEC包:
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| RTP Header |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| FEC Header |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| FEC Payload |
| |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+