前向糾錯碼(FEC)的RTP荷載格式
本文檔規定了一般性的前向糾錯的媒體數據流的RTP打包格式。這種格式針對基於異或操作的FEC算法進行了特殊設計,它允許終端系統使用任意長度的糾錯碼,並且可以同時恢復出荷載數據和RTP頭中的關鍵數據。由於FEC作為一個分離的數據流進行傳送,這種方案可以向後兼容那些沒 TAG: 中文版 RTP荷載 FEC 前向糾錯碼
本備忘錄狀態
本文檔講述了一種Internet通信的標準Internet跟蹤協議,並對其改進提出了討論和建議。請參考最新版本的"Internet Official Protocol Standards"(STD1)來獲得本協議
版權註意
版權歸因特網協會(1999)所有,保留一切權利。
摘要
本文檔規定了一般性的前向糾錯的媒體數據流的RTP打包格式。這種格式針對基於異或操作的FEC算法進行了特殊設計,它允許終端系統使用任意長度的糾錯碼,並且可以同時恢復出荷載數據和RTP頭中的關鍵數據。由於FEC作為一個分離的數據流進行傳送,這種方案可以向後兼容那些沒有實現FEC解碼器的接收 終端。對於那樣的終端來說,可以簡單地將FEC數據丟掉。
目錄
1 簡介 2
2 術語 2
4 監督碼 3
5 RTP媒體數據包結構 5
6 FEC包結構 5
6.1 FEC包的RTP包頭 5
6.2 FEC頭 5
7 保護操作 6
8 恢復過程 7
8.1 重建 7
8.2 何時進行恢復 8
9 例子 11
11 在SDP中表示FEC 14
11.1 FEC作為獨立的流傳輸 14
11.2 在冗余編碼中使用FEC 15
11.3 在RTSP中的用法 16
12 安全性問題 16
13 致謝 17
14 作者地址 17
15 參考書目 17
16 版權聲明 18
致謝 18
1 簡介
在Internet上用分組傳送話音的質量不夠好的一個重要原因是比較高的丟包率。尤其在廣域網中,這個問題相當突出。不幸的是,實時多媒體業務對於延時 的要求相當嚴格,因此不大可能通過重傳來解決丟包的問題。正是出於這個原因,大家提出用前向糾錯(FEC)來解決Internet上的丟包問題[1] [2]。尤其是對於傳統糾錯碼如校驗碼、RS碼、漢明碼等的使用引起了很多人的註意。為了能夠更好地應用這些糾錯碼,必須有相關的協議來支持。
本文檔定義了一種RTP的荷載格式,允許對於實時媒體流進行一般性的前向糾錯。在這裏“一般性”指的是
(1)與被保護的媒體類型無關,即音頻、視頻或其它;
(2)足夠靈活,能夠支持多種FEC機制;
(3)自適應性,可以方便的修改FEC方案而不需要帶外的信令支持;
(4)支持若幹種不同的FEC包的傳輸機制。
2 術語
本文檔中使用了下面這些術語:
媒體荷載:一段待傳輸的未加保護的用戶數據。媒體荷載放在一個RTP包的內部。
媒體頭:包含媒體荷載的包的RTP頭
媒體包:媒體荷載與媒體頭合起來稱作媒體包
FEC包:發送端將媒體包作為前向糾錯算法的輸入,輸出除了這些媒體包之外,還有一些新的數據包稱作FEC包。FEC包的格式在本文檔中進行說明。
FEC頭:FEC包的頭信息稱作FEC頭。
FEC荷載:FEC荷載是FEC包中的荷載。
關聯的:一個FEC包稱作與一個或幾個媒體包是關聯的,如果在這個FEC包的產生過程中這幾個媒體包用作EC算法的輸入關鍵詞“必須”,“必須不”,“要求的”,“會“,”不會“,“應該”,“不應該”,“建議的”,“或許”,“可選的”在RFC2119[4]中解釋。
3 基本操作
這裏描述的荷載格式用於一個RTP會話中的某一端想要用FEC來保護它所傳送的媒體數據流的情況。這種格式所支持的FEC是基於簡單異或校驗的糾錯算法。 發送端從媒體數據流中取出若幹個包,並對它們整個施以異或操作,包括RTP頭。基於這樣一個過程,可以得到一個包含FEC信息的RTP包。這個包可以被接 收端用來恢復任何一個用來產生它的包。本文檔中並未規定多少個媒體包合起來產生一個FEC包。不同參數的選取會導致在overhead,延時和恢復能力之 間的一個不同的折中方案。第4節給出了一些可能的組合。
發送端需要告訴接收端哪些媒體包被用來產生了一個FEC包,這些信息都包含在荷載信息中。每個FEC包中包含一個24比特的mask,如果mask的第i 個比特為1,序號為N+i的媒體包就參與了這個FEC包的生成。N稱作基序號,也在FEC包中傳送。通過這樣一種方案就可以以相當小的overhead來 用任意的FEC糾錯方案恢復丟失的數據包。
本文檔也描述了如何使接收端在不了解具體糾錯碼細節的情況下利用FEC的方法。這就給了發送端更大的靈活性,它可以根據網絡狀態而自適應選擇糾錯碼,而接收端仍能夠正確解碼並用於恢復丟失的包。
發送端生成FEC包之後,就把它們發給接收端,同時,發送端也照常發送原來的媒體數據包,就好像沒有FEC一樣。這樣對於沒有FEC解碼能力的接收端,媒 體流也照常可以接收並解碼。然而,對於某些糾錯碼來說,原始的媒體數據包是不需要傳輸的,僅靠FEC包就足以恢復丟失的包了。這類碼就具有一個很大的缺 點,就是要求所有的接收者都具有FEC解碼能力。這類碼在本文檔中也是支持的。
FEC包並不與媒體包在同一個RTP流中傳輸,而是在一個獨立的流中傳輸,或者作為冗余編碼(redundantencoding)中的次編碼 (secondaryencoding)來傳輸[5]。當在另一個流中傳輸時,FEC包有它們自己的序號空間。FEC包的時間戳是從對應的媒體包中得來 的,同樣是單調遞增。因此,這樣的FEC包可以很好地應用於任何具有固定差值的包頭壓縮方案。本文並沒有規定何為“一個獨立的流”,而把它留給上層協議和 具體應用去定義。對於多播的情況,“一個獨立的流”可以通過不同的多播組來實現,或者同一個組的不同端口,或者同樣的組和端口中不同的SSRC。對於單播 的情況,可以使用不同的端口或者不同的SSRC。
這些方法都各有其優缺點,選用哪一種取決於具體的應用。接收端收到FEC包和媒體包之後,先判斷是否有媒體包丟失。如果沒有,FEC包就直接被丟棄。如果 有丟包,就使用接收到的FEC包和媒體包來進行丟包的重建。這樣一個重建過程是很精確的,荷載以及包頭的大部分數據都可以完全恢復出來。
按照本協議來進行打包的RTP包可以使用一個動態RTP荷載類型號來通知接收端。
4 監督碼
我們定義f(x,y,..)為數據包x,y,…等的異或,這個函數的輸出也是一個數據包,稱作監督包。為簡單起見,我們假定監督包就是輸入的各個包的對應比特異或得到。詳細的過程描述在第6節中給出。
用監督碼來恢復數據包是通過對一組數據包生成一個或多個監督包來完成的。為了提高編碼的效率,這些監督包必須是數據包的線性無關的組合生成的。某一個特定 的組合就稱為一個監督碼。對於k個一組的數據包,生成n-k個監督包,這樣的監督碼認為是同一類監督碼。對於給定的n,k,可能的監督碼有很多。荷載格式 並未要求使用某個特定的監督碼。
舉個例子,考慮這樣一種監督碼,輸入為兩個數據包,輸出為1一個監督包。如果原始媒體數據包是a,b,c,d,發送端送出的包如下所示:
abcd<--媒體流
f(a,b)f(c,d)<--FEC流
時間從左向右遞增。在這個例子裏,糾錯碼帶來了50%的overhead。但是如果b丟失了,用a和f(a,b)就可以恢復出b。
下面還給出了一些其它的監督碼。其中每一個的原始媒體數據流都包含數據包a,b,c,d等等。
方案1
--------
這個方案與上面的例子很類似。區別在於,並不是發送b之後再發送f(a,b)。f(a,b)在b之前就發送出去了。顯然這樣做會給發送端帶來額外的延時,但是用這種方案能夠恢復出突發的連續兩個丟包。發送端送出的包如下所示:
abcde<--媒體流
f(a,b)f(b,c)f(c,d)f(d,e)<--FEC流
方案2
--------
事實上,並沒有嚴格規定一定要傳送原始媒體數據流。在這個方案中,只傳送FEC數據包。這種方案能夠恢復出所有單個的丟包和某些連續的丟包,而且overhead比方案1略小一些。發送端送出的數據包如下所示:
f(a,b)f(a,c)f(a,b,c)f(c,d)f(c,e)f(c,d,e)<--FEC流
方案3
--------
這種方案要求接收端在恢復原始媒體數據包時等待4個數據包間隔的時間,也就是在接收端引入了4個數據包的延時。它的優越性在於它能夠恢復出單個丟包,連續兩個丟包乃至連續三個丟包。發送端送出的數據包如下所示:
abcd<--媒體流
f(a,b,c)f(a,c,d)f(a,b,d)<--FEC流
5 RTP媒體數據包結構
媒體數據包的結構並不受FEC的影響。如果FEC包作為一個獨立的流進行傳輸,媒體數據包的傳輸就與不使用FEC時完全沒有區別。如果FEC作為一個冗余 編碼(redundantcodec)來進行傳輸,媒體數據包就作為RFC2198[5]中定義的主編碼(primarycodec)。
這樣一種編碼方式是非常高效的。如果只使用了極少量的FEC,大部分數據包都是媒體數據包,這意味著overhead代表著FEC的冗余數據量。
6 FEC包結構
一個FEC包就是把一個FEC包頭和FEC包的荷載放進RTP包的荷載中組成的,如圖1所示。
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|RTP頭|
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|FEC頭|
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|FEC荷載|
||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
圖1:FEC包結構
6.1 FEC包的RTP包頭
在FEC包的RTP包頭中,版本域設定為2,填充位通過保護操作計算得到,具體方法在後面給出。擴展位也是通過保護操作計算得到的。一般情況下,SSRC 的值應當與它所保護的媒體數據包的SSRC值一致。如果FEC流通過SSRC值來進行解復用的話,SSRC的值就有可能會不同。CC的值也是在保護操作中 計算出來。不管CC域為何值,CSRC列表永遠不存在。不管X比特為何值,頭擴展部分永遠不存在。標記位的值通過保護操作計算得出。
對於次序號有一個標準的定義:當前包的次序號必須比前一個包的次序號大1。時間戳必須設定為當前這個FEC包發送時對應的媒體流的RTP時鐘的值。不管使用何種FEC方案,FEC包頭中的TS值是單調遞增的。
FEC包的荷載類型是動態確定的,即在帶外通過信令來協商確定的。按照RFC1889[3],RTP會話的某一方如果不能識別收到的RTP包的荷載類型的 話,就必須將其丟棄。這樣就很自然地提供了向後兼容的能力。FEC機制可以用在一個多播的環境中,某些接收端具有FEC解碼能力,而某些不具有。
6.2 FEC頭
FEC頭的長度為12個字節,其格式如圖2所示。它包含一個SN基數域,長度恢復域,E域,PT恢復域,mask域以及TS恢復域。
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|SN基數|長度恢復|
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|E|PT恢復|mask|
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|TS恢復|
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
圖2:FEC頭格式
長度恢復域用來確定待恢復的數據包的長度。它的值是當前組中被保護的媒體數據包的長度(以字節為單位)的二進制和(逐位異或),用一個網絡序的16比特無 符號整數表示。在這裏,媒體數據包的長度包括CSRC列表、擴展部分和填充的比特。這樣即使在媒體數據包長度不一致的情況下,也一樣可以使用FEC。舉個 例子,假定要用兩個媒體數據包的異或來產生一個FEC包,這兩個媒體數據包的長度分別為3(0b011)和5(0b101)個字節,那麽長度恢復域的值就 是0b011xor0b101=0b110。
比特E指示是否存在一個擴展部分。在當前版本中,這個比特必須設置為0。
PT恢復域是通過對相關的媒體數據包的荷載類型域的值進行異或操作得到的,從而能夠用來恢復丟失包的荷載類型。
Mask域長度為24個比特,如果其中的第i個比特設置為1,那麽序號為N+i的媒體數據包就與當前FEC包相關聯。其中N是SN基數域的值。最低位(LSB)對應於i=0,最高位(MSB)對應於i=23。
SN基數域的值必須設置為與當前FEC包相關的媒體數據包中的最小的序號。這樣一個FEC包最多可以與連續24個媒體數據包相關聯。
TS恢復域是通過計算相關聯的媒體數據包的時間戳的異或得到的。這樣就可以恢復出丟掉的數據包的時間戳。
FEC包的荷載是對相關聯的媒體數據包的CSRC列表、RTP頭部擴展、媒體包荷載以及填充比特連在一起進行異或操作得到的。
值得註意的是FEC包的長度有可能會比它保護的媒體數據包的長度大一點,因為多了一個FEC頭。如果FEC包的長度超出了下層協議允許的最大包長,這可能會給傳輸帶來很大的問題。
7 保護操作
保護操作涉及到將RTP頭中的某些域與媒體包的荷載級聯起來,再加上填充比特,然後對這些序列計算它們的異或值。得到的比特序列就成為FEC包的某個組成 部分。對於每一個要保護的媒體包,按照下面的次序將各個數據域級聯起來生成一個比特序列,如果中間還插入了其它操作的話,最終結果必須與下面所描述的一 致:
o填充位(1比特)
o擴展位(1比特)
oCC(4比特)
o標記位(1比特)
o荷載類型(7比特)
o時間戳(32比特)
o長度恢復域的值(網絡次序16比特無符號整數),也就是各個媒體數據包的荷載長度、CSRC列表長度以及填充比特長度的和異或的結果
o如果CC不為零,這裏是CSRC列表(變長)
o如果擴展位為1,這裏是頭部擴展(變長)
o荷載(變長)
o填充,如果存在的話(變長)
需要註意的是最前面的填充位是整個比特序列的最重要比特(MSB)。
如果各個媒體數據包的到的比特序列長度不相等,那麽每一個都必須填充到最長的序列那麽長。填充值可以是任意的,但必須填充在整個比特序列的最後。對所有這 些比特序列進行對位異或操作,就可以得到一個監督比特序列,這個監督比特序列就可以用來生成FEC包。稱這個比特序列為FEC比特序列。FEC比特序列中 的第一個比特填入FEC包的填充位,第二個比特填入FEC包的擴展位,接下來的四個比特填入FEC包的CC域,再下來的一個比特填入FEC包的標記位,然 後的七個比特寫入FEC包頭的PT恢復域,再後面的32個比特寫入FEC包頭的TS恢復域,再接下來的16個比特寫入FEC包頭中的長度恢復域。最後剩下 的比特就作為FEC包的荷載。
8 恢復過程
FEC包能夠使終端系統有能力恢復出丟失的媒體數據包。丟失包的的包頭中的所有數據域,包括CSRC列表,擴展位,填充位,標記位以及荷載類型,都是可以恢復的。這一節描述進行恢復的過程。
恢復過程中包含兩個不同的操作。第一個是確定需要用哪些包(包括媒體包和FEC包)來恢復一個丟失的包。這一步完成之後,第二步就是重建丟失的包。第二步 必須按照下面的規定來進行。第一步可以由實現者選擇任意的算法來完成。不同的算法會導致在復雜度和恢復丟包能力之間的一個不同的折中。
8.1 重建
設T為可用來恢復媒體數據包xi的一組包(包括FEC包和媒體包),重建過程如下所述:
1. 對於T中的媒體數據包,按照上一節保護操作中所規定的那樣計算它們的比特序列。
2. 對於T中的FEC包,基本以同樣的方式來計算比特序列,不同點僅在於用PT恢復域的值取代荷載類型,用TS恢復域的值取代時間戳,並將CSRC列表、擴展位和填充位都設為null。
3. 如果某個媒體數據包生成的比特序列比FEC包生成的比特序列短,就把它填充到域FEC包生成的比特序列一樣長度。填充部分必須加在比特序列的最後,可以為任意值。
4. 對這些比特序列進行按位異或操作,得到一個恢復出的比特序列。
5. 創建一個新的數據包,12個字節的標準RTP頭,沒有荷載。
6. 將這個新包的版本域設為2。
7. 將新包的填充位設為恢復出的比特序列的第一個比特。
8. 將新包的擴展位設為恢復出的比特序列的第二個比特。
9. 將新包的CC域設為恢復出的比特序列的接下來的4個比特。
10. 將新包的標記為設為恢復出的比特序列的接下來的一個比特。
11. 將新包的荷載類型設為恢復出的比特序列的接下來的7個比特。
12. 將新包的SN域設定為xi。
13. 將新包的TS域的值設定為恢復出的比特序列的接下來的32個比特。
14. 從恢復出的比特序列中取出接下來的16個比特,將其作為一個網絡序的無符號整數,然後從恢復出的比特序列中取出這個整數那麽多的字節,添加在新包之後,這代表新包的CSRC列表、擴展、荷載和填充。
15. 將新包的SSRC域設定為它所保護的媒體流的SSRC值。
上面的這個過程能夠完全恢復出一個丟失的RTP包的包頭和荷載。
8.2 何時進行恢復
前面一節討論了當要恢復一個序號為xi的包時,所有需要的包都可用時,如何來進行恢復。而並未涉及如何決定是否去試圖恢復某個包xi,以及如何確定是否有 足夠的數據來恢復這個包。這些問題將留給實現者去靈活設計,但我們將在本節給出一個可用於解決這些問題的簡單算法。
下面的這個算法是用C語言寫的。代碼中假定已經存在了幾個函數。recover_packet()的參數是一個包的序號和一個FEC包。用這個FEC包和 以前收到的數據包,這個函數能夠恢復出指定序號的數據包。add_fec_to_pending_list()將一個給定的FEC包加入到一個存放尚未用 於恢復操作的FEC包的鏈表中去。wait_for_packet()等待從網絡上發來的一個包,
FEC包或者是數據包。remove_from_pending_list()將一個FEC包從鏈表中刪除。結構體packet包含一個布爾變量 fec,當這個包為FEC包時fec為真,否則為假。當它是一個FEC包時,成員變量mask和snbase存放著FEC包頭中對應值;當它是一個媒體數 據包時,sn變量存放著包的序號。全局數組A用於指示出哪些媒體數據包已經收到了,而哪些還沒有。它是以包的序號為索引的。
函數fec_recovery給出了這個算法的實現。它等待網絡上發來的數據包,當它收到一個FEC包時,就調用recover_with_fec(), 即嘗試用它來恢復。如果恢復操作不可能(數據不足),就把這個FEC包存起來備用。如果收到的包是一個媒體數據包,就記錄下它已經收到了,然後檢查以前的 FEC包是否現在可以進行恢復了。我們對待恢復出來的包就象從網絡上收到這個包一樣,並會引發進一步的恢復嘗試。
一個實際的實現需要用一個循環緩沖區來代替數組A,以避免數組緩沖區溢出。並且,下面的代碼中並沒有釋放已經沒有任何用處的FEC包。一般來說,對FEC 包的釋放操作可以基於一個時間限制(playtime),這個時間限制取決於發送端以多少個包為一組進行保護操作。當一個FEC包所保護的數據包的 playtime已經過去的時候,這個FEC包就不再有用了。
- typedef struct packet_s{
- BOOLEAN fec;/*FECormedia*/
- int sn;/*SNofthepacket,formediaonly*/
- BOOLEAN mask[24];/*Mask,FEConly*/
- int snbase;/*SNBase,FEConly*/
- struct packet_s*next;
- }packet;
- BOOLEANA[65535];
- packet*pending_list;
- packet*recover_with_fec(packet*fec_pkt){
- packet*data_pkt;
- intpkts_present,/*numberofpacketsfromthemaskthatare
- present*/
- pkts_needed,/*numberofpacketsneededisthenumberofones
- inthemaskminus1*/
- pkt_to_recover,/*snofthepacketwearerecovering*/
- i;
- pkts_present=0;
- /*The number of packets needed is the number of ones in the mask
- minus1.The code below increments pkts_needed by the number
- of ones in the mask, so we initialize this to-1 so that the
- final count is correct*/
- pkts_needed=-1;
- /*Gothroughall24bitsinthemask,andcheckifwehave
- allbutoneofthemediapackets*/
- for(i=0;i<24;i++){
- /*Ifthepacketishereandinthemask,incrementcounter*/
- if(A[i+fec_pkt->snbase]&&fec_pkt->mask[i])pkts_present++;
- /*Countthenumberofpacketsneededaswell*/
- if(fec_pkt->mask[i])pkts_needed++;
- /*Thepackettorecoveristheonewithabitinthe
- maskthat‘snothereyet*/
- if(!A[i+fec_pkt->snbase]&&fec_pkt->mask[i])
- pkt_to_recover=i+fec_pkt->snbase;
- }
- /*Ifwecanrecover,doso.Otherwise,returnNULL*/
- if(pkts_present==pkts_needed){
- data_pkt=recover_packet(pkt_to_recover,fec_pkt);
- }else{
- data_pkt=NULL;
- }
- return(data_pkt);
- }
- voidfec_recovery(){
- packet*p,/*packetreceivedorregenerated*/
- *fecp,/*fecpacketfrompendinglist*/
- *pnew;/*newpacketsrecovered*/
- while(1){
- p=wait_for_packet();/*getpacketfromnetwork*/
- while(p){
- /*ifit‘sanFECpacket,trytorecoverwithit.Ifwecan‘t,
- storeitforlaterpotentialuse.Ifwecanrecover,actas
- iftherecoveredpacketisreceivedandtrytorecoversome
- more.Otherwise,ifit‘sadatapacket,markitasreceived,
- andcheckifwecannowrecoveradatapacketwiththelist
- ofpendingFECpackets*/
- if(p->fec==TRUE){
- pnew=recover_with_fec(p);
- if(pnew)
- A[pnew->sn]=TRUE;
- else
- add_fec_to_pending_list(p);
- /*Weassignpnewtopsincethewhileloopwillcontinue
- torecoverbasedonpnotbeingNULL*/
- p=pnew;
- }else{
- /*Markthisdatapacketashere*/
- A[p->sn]=TRUE;
- free(p);
- p=NULL;
- /*Gothroughpendinglist.Tryandrecoverapacketusing
- eachFEC.Ifwearesuccessful,addthedatapacketto
- thelistofreceivedpackets,removetheFECpacketfrom
- thependinglist,sincewe‘veusedit,andthentryto
- recoversomemore*/
- for(fecp=pending_list;fecp!=NULL;fecp=fecp->next){
- pnew=recover_with_fec(fecp);
- if(pnew){
- /*Thepacketisnowhere,aswe‘verecoveredit*/
- A[pnew->sn]=TRUE;
- /*OneFECpacketcanonlybeusedoncetorecover,
- soremoveitfromthependinglist*/
- remove_fec_from_pending_list(fecp);
- p=pnew;
- break;
- }
- }/*for*/
- }/*p->fecwasfalse*/
- }/*whilep*/
- }/*while1*/
- }
9 例子
考慮這樣的情況,有兩個媒體數據包x和y要從SSRC2發送出去。它們的序號分別為8和9,時間戳分別為3和5。x的荷載類型為11,y的荷載類型為18。x有十個字節的荷載,y有11個字節的荷載。y的標記位為1。x和y的RTP包頭分別如圖3和圖4所示。
媒體數據包x
0123
01234567890123456789012345678901
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|10|0|0|0000|0|0001011|0000000000001000|
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|00000000000000000000000000000011|
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|00000000000000000000000000000010|
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
版本:2
填充位:0
擴展位:0
標記位:0
PTI:11
SN:8
TS:3
SSRC:2
圖3:媒體包x的RTP頭
媒體包y
0123
01234567890123456789012345678901
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|10|0|0|0000|1|0010010|0000000000001001|
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|00000000000000000000000000000101|
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|00000000000000000000000000000010|
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
版本:2
填充位:0
擴展位:0
標記位:1
PTI:18
SN:9
TS:5
SSRC:2
圖4:媒體包y的RTP頭
由這兩個包生成一個FEC包。我們假定用荷載類型值127來指示一個FEC包。得到的FEC包的RTP包頭如圖5所示。
0123
01234567890123456789012345678901
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|10|0|0|0000|1|1111111|0000000000000001|
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|00000000000000000000000000000101|
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|00000000000000000000000000000010|
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
版本:2
填充位:0
擴展位:0
標記位:1
PTI:127
SN:1
TS:5
SSRC:2
圖5:x,y的FEC包的RTP頭
FEC包的FEC頭如圖6所示。
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|0000000000001000|0000000000000001|
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|0|0011001|000000000000000000000011|
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|00000000000000000000000000000110|
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
SN基數:8[min(8,9)]
長度恢復域:1[8xor9]
E:0
PTI恢復域:25[11xor18]
mask:3
TS恢復域:6[3xor5]
荷載長度為11個字節
圖6:FEC包的FEC頭
10 冗余編碼中使用FEC的用法
我們可以把一個FEC包看成是對媒體的一個冗余編碼(redundantencoding)。這樣一來,[5]中所描述的冗余音頻數據的荷載格式就可以用 於FEC數據的打包。這個過程如下所述:上面的FEC操作作用於一個RTP媒體數據包組成的流上。這個流也就是RFC2198[5]中進行封裝之前的流。 換句話說,將待保護的媒體數據流封裝在標準的RTP媒體包中,然後進行前面定義過的FEC操作(有一點小的改動),生成一個FEC數據包的流。這裏與前面 不同的一點在於:在進行FEC操作之前,必須把待保護的RTP媒體數據包的RTP頭擴展、填充部分以及CSRC列表去掉,並且CC域、填充位、擴展位必須 設置為零,用這些修改之後的RTP媒體數據包去生成FEC包。需要註意的是發送端要發送出去的仍然是原來沒有修改的媒體數據包,只是在計算FEC包的時候 才去除掉這些域。
一旦生成了FEC包,將媒體數據包中的荷載提取出來,作為RFC2198中定義的主編碼(primaryencoding),將FEC包中的FEC頭和荷 載提取出來,作為RFC2198中定義的冗余編碼(redundantencoding)。除了FEC之外,還可以向包中加入額外的冗余編碼,但這些信息 不受FEC的保護。
主編碼(primarycodec)的冗余編碼頭(redundantencodingsheader)按照RFC2198中的定義進行設置。下面給出 FEC數據的冗余編碼頭的設置。塊荷載類型域(blockPT)設置為與FEC格式相關聯的動態PT值,塊長度設置為FEC頭和FEC荷載的長度之和。時 間戳偏移(timestampoffset)應當設置為0。次編碼(secondarycodec)的荷載包括FEC頭和FEC荷載。
在接收端,主編碼和所有的次編碼都作為不同的RTP包提取出來。這是通過把冗余編碼包中的RTP頭的次序號、SSRC、標記位、CC域、RTP版本號和擴 展位等拷貝到每一個提取出的包的RTP頭中去。如果次編碼中包含FEC,FEC包的RTP頭中的CC域、擴展位、填充位都必須設置為零。提取出的包的荷載 類型碼是從冗余編碼頭中的塊荷載類型域復制過來。提取出的包的時間戳是RTP頭中的時間戳與塊頭中的時間戳偏移之差。提取包的荷載就是塊中的數據部分。這 樣一來,FEC流和媒體數據流就被分別提取出來了。
要利用FEC包和已接收到的媒體數據包來恢復丟失的包,必須把媒體數據包中的CSRC列表、擴展頭、填充部分去掉,並把CC域,擴展位、填充位都設置為 零。用這些修改之後的媒體包,加上FEC包,基於第8節中的步驟,就可以恢復出丟失的包。恢復出的包將肯定沒有擴展部分、填充部分和CSRC列表部分。在 具體實現中,如果其它包中存在這些部分,可以從其它包中將這些部分拷貝過來。
使用冗余編碼的荷載格式,有可能不能正確恢復出標記位。在使用RFC2198來進行FEC封裝的應用程序中,必須把恢復出的媒體數據包的標記位設置為零。相對於發送完整的FEC包,這種方法的優點在於它能夠減少overhead。
11 在SDP中表示FEC
FEC包的RTP荷載類型值是一個動態類型值。FEC包可以發到與媒體包不同的多播組或者不同的端口。如果使用[5]中定義的冗余編碼荷載格式,FEC數 據甚至可以放在媒體包中一起傳輸。這些配置選項必須在帶外明確指示出來。這一節講述如何用RFC2327[6]中定義的會話描述協議 (SessionDescriptionProtocol,SDP)來完成這一工作。
11.1 FEC作為獨立的流傳輸
在第一種情況中,FEC包是作為一個獨立的流來進行傳輸的。這可能意味著它們被發往與媒體包不同的端口或不同的多播組。這時,必須傳達以下幾條消息:
oFEC包被發往的地址和端口
oFEC包的荷載類型號
o它保護的是哪個媒體數據流
FEC的荷載類型號是在它所保護的媒體的m行(譯者註:媒體描述行,mediadescription line,見RFC2327)中傳送的。由於沒有為FEC分配靜態的荷載類型值,因此必須使用動態的荷載類型值。這個值的綁定是通過一個rtpmap屬性 來指示的,綁定中所使用的名稱為"parityfec"。
FEC的荷載類型號出現在它所保護的媒體的m行中並不意味著FEC包要發送到相同的地址和端口。事實上,FEC包的端口和地址信息是通過fmtp屬性行來傳遞的。在媒體的m行中出現FEC荷載類型指示為了指出FEC保護的是哪個媒體流。
FEC的fmtp行的格式如下所示:
a=fmtp:<荷載類型號><端口><網絡類型><地址類型><連接地址>
其中“荷載類型號”就是在m行中出現的荷載類型號。“端口”是FEC包將要發送的端口。其余的三項,網絡類型,地址類型和連接地址與SDP的c行中的語法 語義是相同的。這樣fmtp行可以部分地用與c行相同的解析器來進行解析。需要註意的是由於FEC不能夠分級編碼,<地址數量 (numberofaddresses)>參數必須不出現在連接地址中。
下面是一個FEC包的SDP例子:
v=0
o=hamming28908445262890842807INIP4126.16.64.4
s=FECSeminar
c=INIP4224.2.17.12/127
t=00
m=audio49170RTP/AVP078
a=rtpmap:78parityfec/8000
a=fmtp:7849172INIP4224.2.17.12/127
m=video51372RTP/AVP3179
a=rtpmap:79parityfec/8000
a=fmtp:7951372INIP4224.2.17.13/127
在上面的SDP描述中出現兩個m行是因為其中存在兩個媒體流,一個音頻流和一個視頻流。媒體格式為0代表用PCM編碼的音頻,它被荷載類型號為78的 FEC流保護。FEC流被發往與音頻相同的多播組,TTL參數也相同,但端口號大2(49172)。視頻流被荷載類型號為79的FEC流保護,這個FEC 流的端口號是一樣的,但是多播地址不一樣。
11.2 在冗余編碼中使用FEC
當FEC流以冗余編碼格式作為一個次編碼來發送的時候,必須通過SDP通知對方。為了做到這一點,使用RFC2198中定義的步驟來通知對方使用了冗余編 碼。FEC的荷載類型就象其它任意一個次編碼那樣表示出來。必須用一個rtpmap屬性行來指示出FEC包的動態荷載類型號。FEC必須只保護主編碼。這 時,FEC的fmtp屬性必須不出現。
舉個例子:
m=audio12345RTP/AVP12105100
a=rtpmap:121red/8000/1
a=rtpmap:100parityfec/8000
a=fmtp:1210/5/100
這個SDP例子指示有一個單一的音頻流,由PCM格式(媒體格式0)和DVI格式(媒體格式5)組成,一個冗余編碼(用媒體格式121表示,在 rtpmap屬性中綁定為red),以及一個FEC(媒體格式100,在rtpmap屬性中綁定為parityfec)。盡管FEC格式是作為媒體流的一 個可能編碼來描述的,但它必須不單獨傳送。它出現在m行中只是因為按照RFC2198,非主編碼(non-primarycodec)都必須在這裏列出 來。fmtp屬性指出冗余編碼格式可以這樣使用:DVI作為一個次編碼(secondarycoding),而FEC作為第三編碼 (tertiaryencoding)。
11.3 在RTSP中的用法
RTSP[7]可以用來請求將FEC包作為一個獨立的流進行傳輸。當在RTSP中使用SDP時,每個流的會話描述中並不包含連接地址和端口號。作為代替,RTSP使用了控制URL(Control URL)的概念。SDP中以兩種不同的方式來使用控制URL。
1.所有的流使用一個控制URL。這被稱為“集中控制”。在這種情況下,FEC流的fmtp行被省略了。
2.每一個流都有一個控制URL。這被稱為“非集中控制”。在這種情況下,FEC流的fmtp行指定它的控制URL。這個URL可以被用在RTSP客戶端的SETUP命令中。
非集中控制的FEC流用RTSP時,它的fmtp行的格式為:
a=fmtp:<荷載類型號><控制URL>
其中荷載類型號就是出現在m行中的荷載類型號。控制URL就是用於控制這個FEC流的URL。
需要註意的是控制URL並不一定是一個絕對URL。將一個相對的控制URL轉化為一個絕對的控制URL的規則在RFC2326的C.1.1中給出。
12 安全性問題
使用FEC對於加密時密鑰的用法和更改有一定的影響。由於FEC包組成了一個獨立的流,在加密的用法上可以有幾種不同的排列組合:
o可以對FEC流加密,而不對媒體流加密
o可以對媒體流加密,而不對FEC流加密
o可以對媒體流和FEC流同時加密,但使用不同的密鑰
o可以對媒體流和FEC流同時加密,但使用相同的密鑰
前面三種要求應用層的信令協議知道使用了FEC,並因此對媒體流和FEC流分別進行用法協商,並分別交換密鑰。在最後一種情況裏,並不需要有這些機制,只 要想對待媒體包一樣去對待FEC包就可以了。前面兩種情況可能會引起分層沖突,因為實際上不應該將FEC包與其它媒體包區別對待,並且只對其中一個流加密 會帶來明文攻擊的危險。出於這些考慮,使用了加密的應用就應當對兩個流都進行加密。
然而,在密鑰的變更中會出現一些問題。例如,如果發送了兩個數據包a和b,還有一個FEC包f(a,b)。如果a和b所使用的密鑰是不同的,那麽應該用哪一個密鑰來解密f(a,b)?
一般說來,用過的密鑰都會被緩存起來,這樣當媒體流的密鑰更改之後,舊的密鑰被保留下來備用,直到它發現FEC包的密鑰也發生更改了。
使用FEC的另一個問題是它對於網絡擁塞的影響。面對網絡丟包越來越多的情況加入FEC不是一個好辦法,它可能會導致擁塞更加嚴重並最終崩潰。因此,實現者在網絡丟包增加的情況下必須不大量增加FEC冗余數據,以保證整個網絡的性能。
13 致謝
這份方案是基於Budge和Mackenzie於1997年提交的一個早期FEC草案,在此表示感謝。我們感謝 SteveCasner,MarkHandley,OrionHodson和ColinPerkins對我們工作所提出的批評和建議,並感謝 AndersKlemets寫了“在RTSP中的用法”這一節。
14 作者地址
JonathanRosenberg
dynamicsoft
200ExecutiveDrive
Suite120
WestOrange,NJ07046
Email:[email protected]
HenningSchulzrinne
ColumbiaUniversity
M/S0401,1214AmsterdamAve.
NewYork,NY10027-7003
EMail:[email protected]
15 參考書目
[1]J.C.BolotandA.V.Garcia,"Control mechanisms for packet audio in the internet," in Proceeding soft he Conference on Computer Communications(IEEE Info com),(San Francisco, California),Mar.1996.
[2]Perkins,C.andO.Hodson,"Options for Repair of Streaming media",RFC2354,June1998.
[3]Schulzrinne,H.,Casner,S.,Frederick,R.andV.Jacobson,"RTP:A Transport Protocol for Real-Time Applications",RFC1889, January1996.
[4]Bradner,S.,"Keywords for use in RFCs to indicate requirement levels",BCP14,RFC2119,March1997.
[5]Perkins,C.,Kouvelas,I.,Hodson,O.,Hardman, V.,Handley,M., Bolot,J.C., Vega-Garcia, A.andS.Fosse-Parisis,"RTP Payload for Redundant Audio Data",RFC2198,September1997.
[6]Handley,M.andV.Jacobson,"SDP:SessionDescriptionProtocol",RFC2327,April1998.
[7]Schulzrinne,H.,Rao,A.andR.Lanphier,"RealTimeStreamingProtocol(RTSP)",RFC2326,April1998.
16 版權聲明
版權歸Internet協會所有(1999)。保留所有權利。
本文及其譯本可以提供給其他任何人,可以準備繼續進行註釋,可以繼續拷貝、出版、發布,無論是全部還是部分,沒有任何形式的限制,不過要在所有這樣的拷貝 和後續工作中提供上述聲明和本段文字。無論如何,本文檔本身不可以做任何的修改,比如刪除版權聲明或是關於Internet協會、其他的Internet 組織的參考資料等。除了是為了開發Internet標準的需要,或是需要把它翻譯成除英語外的其他語言的時候,在這種情況下,在Internet標準程序 中的版權定義必須被附加其中。
上面提到的有限授權允許永遠不會被Internet協會或它的繼承者或它的下屬機構廢除。
本文檔和包含在其中的信息以"Asis"提供給讀者,Internet社區和Internet工程任務組不做任何擔保、解釋和暗示,包括該信息使用不破壞任何權利或者任何可商用性擔保或特定目的。
前向糾錯碼(FEC)的RTP荷載格式