引言
本系列文章将整理各路流媒體傳輸協定,包括RTP/RTCP,RTMP,希望通過深入了解各個流媒體傳輸協定的設計細節,對今後流媒體部分的開發工作有一定的啟發。
介紹
RTP,即real-time transport protocol(實時傳輸協定),為實時傳輸互動的音頻和視訊提供了端到端傳輸服務。其中包括載荷的類型确認,序列編碼,時間戳和傳輸監控功能。一般應用都是基于UDP協定,來使用RTP的多路技術以及驗和服務。然而,RTP還可以與其它适合的協定并用,如果底層網絡支援多路分發,RTP還可以将資料傳輸給多個目标。
需要注意的是RTP不提供任何機制以保證資料的實時性和QOS(quality-of-service),而是依賴底層的服務來提供這些功能,RTP既不保證傳輸的可靠性也不保證無序傳輸,同時也不假定底層網絡是可信任的和有序的。接收端可以利用RTP中的序列号排序收到的封包。
RTP與RTCP
- 實時傳輸協定(RTP),傳輸具有實時特性的資料
- RTP控制協定(RTCP),監控QOS和傳遞會話中參與者的資訊。它沒有明确的成員控制功能和Session建立過程,但這些對一個相對寬松的Session控制來說已經足夠了,它沒有必要包含一個應用的所有控制功能。
RTP代表了一種新型協定,它遵循Application level framing 和 Integrated layer processing。即RTP可以比較容易的拓展以傳遞某些特定需要的内容,而且可以比較容易地內建進某個應用,而不是作為一個獨立的補充層。RTP協定被故意地設計成不完整的協定架構。
RTP的使用場景
下面的例子描述了RTP的部分特性,選擇的例子是用來闡明基于RTP的應用的基本操作,而不是說RTP僅能用于此類應用。
簡單的多點傳播音頻會議
一個小組要通過網絡開一個音頻會議,他們用了IP多點傳播服務。基于某種配置設定機制,小組得到了一個多點傳播組位址和一對端口,其中一個端口是用來傳輸音頻資料的,另一個是用來傳輸RTCP封包的。這個多點傳播位址和端口發給了所有與會者。如果想要引入一些安全政策,可以對資料封包和控制封包加密,然後把加密時用到的密鑰分發給與會者。
這個音頻會議軟體,可能會一直發送時長為20ms的音頻資料包。每個實際音頻資料包,都以RTP頭資料開始,然後再以UDP協定封裝并發送。RTP包的頭部辨別了該包的資料類型,以便消息發送器來改變資料的編碼。例如,針對低帶寬的與會者進行一些調節,或者對網絡擁堵作出反應。
像UDP這類包類型的網絡,偶爾會丢包,亂序,延遲不定長時間。為了解決這類意外情況,RTP包中包含了時間資訊和序列号,這樣接受者就可以通過它們重排資料包的時序。在這個例子中,我們就可以按順序地播放每個20ms的音頻資料。在會議中對每個資料源的RTP封包時序重排都是獨立進行的。接受者也可以通過序列号來确定丢失了多少封包。
因為這個小組開會期間,會有一些人加入或退出這個網絡會議,是以我們需要知道具體是誰加入了會議,以及他們有沒有正常地接收到音頻資料。出于這個目的,每個網絡會議的用戶端都會周期性的通過RTCP端口報告使用者的名字以及自己接受資料的情況,如果有人接受資料不正常,可能就需要對應的改變編碼。而且,除了使用者的名字之外,還會有一些别的資訊,用來控制帶寬限制。當有人從視訊會議中退出時,還需要發送一個RTCP BYE封包。
音頻和視訊會議
如果這個會議既要傳輸音頻又要傳輸視訊的話,它們會以獨立的RTP Session傳輸。也就是說,負責音頻傳輸的部分和負責視訊傳輸的部分會通過不同的多點傳播位址(和端口對)分别傳輸各自的RTP封包和RTCP封包。在RTP協定這一層,音頻和視訊Session并沒有被組合到一起。我們期望與會者用同一個名字來建立音頻和視訊Session,這樣這兩個Session就能聯系起來了。
RTP協定之是以這樣設計,一個原因是某些與會者可以選擇隻接受某一種類型的資料(隻接受Audio)。即便Audio資料和Video資料是獨立分發的,但是我們仍然可以通過參考RTCP協定中時間資訊來同步播放它們。
Mixers & Translators
到目前為止,我們都是假設所有的與會者想要接收同意格式的媒體資料。但是這顯然不太合适,考慮一下,可能某些與會者可能網速相對比較慢,而其他人網速卻比較快。對于這種情況,我們不應該強迫所有人都用低帶寬并降低音頻編碼的品質,而是使用RTP級别的中繼節點(Mixer)來給周圍低帶寬使用者分發低帶寬消耗的資料。這個Mixer将接收到的不同與會者的音頻資料同步,并将它們耦合到一個單一流中,然後将這個流用低帶寬消耗的編碼方案進行壓縮,最後發送給那些低帶寬的與會者。Mixer可以在RTP頭部寫一些特殊内容,來表明該Mixer包具體耦合了哪些與會者,這樣,接收到該Mixer包的人就能确定目前說話的人是誰了。
此外,有些與會者可能處于在應用級防火牆後,無法僅通過IP多點傳播通路。這種情況下Mixer就沒有什麼意義了,他們需要另一類RTP級别的中繼(Translator)。我們需要兩個Translator,安裝在防火牆的兩面,外面的Translator将收到的所有多點傳播封包,通過一個安全連接配接傳輸給防火牆裡面的Translator。然後,防火牆裡的Translator再将這些封包分發給内網的與會者。
層編碼
多媒體應用可以根據接受者的能力或者網絡擁堵的情況調整傳輸速率。許多實作将碼率控制的責任放在了發送者。這群組播模式不太相容,因為各個不同的資料接受者會有不同的帶寬情況,這就會産生木桶效應,即帶寬最差的接受者會拖垮整個會議的通訊品質。
是以,帶寬自适應的工作應該放到接受者這裡,發送者需要拆分出面向不同帶寬與會者的媒體流(500K,2M,5M),它們分别對應了不同的多點傳播位址,資料的接收者根據自己的帶寬情況,選擇加入适合的多點傳播。
定義
- RTP payload:RTP包中傳輸的資料,比如音頻采樣資料或者壓縮過的視訊資料。
- RTP packet:由定長RTP頭部,資料來源者的清單,RTP payload組成的資料包。一些下層協定可能會自己定義RTP的封裝格式。一般來說,一個下層協定包隻包含一個RTP包,但是也有可能多個RTP包被合并到一起。
- RTCP packet:RTP控制封包,由定長的RTC頭部開始,之後會跟着一些結構化的元素,它們在RTCP發揮不同功能時,會有不同的結構。通常多個RTCP包會被合在一起,通過一個下層協定包一起發送。
- Port:傳輸層協定中用來區分某一主機下不同應用的抽象。RTP協定依賴更底層網絡提供端口機制,繼而提供多點傳播的RTP和RTCP封包。
- Transport address:網絡位址和端口的組合,用來定位傳輸層的節點。
- RTC media type:一個RTP Session中所用到的所有payload類型的合集。
- Multimedia session: 視訊會議組中同時工作的一組RTP session。例如,視訊會議中的Audio session和Video session。
- RTP session:一組參與者利用RTP來通訊的組合。一個參與者可以同時加入到多個RTP session中。在Multimedia session中,除非特意将多媒體編碼進同一資料流,否則,每個資料流會通過不同的RTP Session傳輸。與會者通過Transport address來區分不同的RTP session。同一RTP session的不同與會者會共享同一個Transport address,也可能每個與會者都有自己的Transport address。在單點傳播的情況時,一個與會者可能用同一對端口(RTP&RTCP)來接受所有其他與會者的資料,也可能對不同的與會者采用不同的端口對(RTP&RTCP)。
- Synchronization source (SSRC):RTP封包流的一個Source,由RTP頭中定義的32-bit的SSRC identifier來辨別,這樣做是為了不依賴網絡位址。同一個SSRC中發送的所有包都具有同一時序和序列号間隔,是以接收者可以通過SSRC将收到的資料包分組并排序。一個信号源(麥克風,攝像頭,Mixer)的封包流會有由一個SSRC的發送器發送。一個SSRC可能會随着時間的變化,改變其資料格式,例如音頻編碼。SSRC的身份識别碼都是随機生成的,但是必須保證整個RTP session中該身份識别碼不會重複,這些工作是通過RTCP來完成的。如果一個與會者在一個RTP session中發送不同的媒體資料流,那麼每個流的SSRC必須不同。
- Contributing source (CSRC):RTP Mixer所混合的所有資料對應的SSRC的清單。Mixer會将一個SSRC清單寫入RTP頭中,該清單包含了這個混合封包中包含的所有來源SSRC。
- End system:一個生成RTP payload和消費收到的RTP payload的應用。一個End system可以扮演一個或者多個SSRC角色,但是通常是一個。
- Mixer:一個中介系統,它接受一個或多個Source的資料,随後它可能會改變這些資料的格式,并将它們合并為一個新的RTP packet。因為,多個輸入源的時序通常來說都不一緻,是以Mixer通常會同步不同源的時間,并生成一個自己的時序來處理合并資料流。所有從Mixer輸出的資料包都會标記上該Mixer的SSRC。
- Translator:一個中介系統,它會轉發RTP packet但是不改變其原本的SSRC。
- Monitor:一個在RTP session中接收RTCP封包的應用,它會總結資料被接受的報告,并為目前分發系統評估QOS,診斷錯誤,長期統計。Monitor可以內建進會議應用中,也可以是獨立的第三方應用,隻接受RTCP封包,但是什麼都不發送。
- Non-RTP means:為了讓RTP提供可用服務而加入的協定或者機制。特别是在多媒體會議中,需要一種控制協定來分發多點傳播位址和加密密鑰,協調加密算法,定義RTP payload格式和RTP payload類型的動态映射。
位元組序,資料對齊,時間格式
所有的整數字段都使用網絡位元組序(大端序),除了特别聲明,數字常量由十進制表示。
所有頭部資料都會根據其資料的原始長度進行對齊,比如,16-bit的資料會對齊到偶數偏移,32-bit的資料會對齊到可被4整除的偏移。此外,用0來作為填充位元組。
Wallclock time(絕對日期和時間)是用網絡時間協定(NTP)的時間格式來表示,即從1900年一月一日0點到現在的秒數。NTP的時間戳使用了64-bit的無符号固定小數點的形式表示,其中頭32-bit用來表示整數部分,後32-bit用來表示小數部分。RTP的時間格式采用了NTP的簡化版,他隻用了NTP的64-bit資料的中間32-bit,即前16-bit表示整數,後16-bit表示小數。
NTP時間戳到2036年就會循環回0,但是因為RTP隻會使用不同NTP時間的內插補點,是以這不會有什麼影響。隻要一對時間戳都在同一個循環周期裡,直接用子產品化的架構相減或者比較就可以,NTP的循環問題就不重要了。
RTP資料傳輸協定
RTP的定長頭字段
RTP頭的格式如下:

上圖中前96-bit的資料是每個RTP包都有的部分,CSRC部分隻有Mixer發送的封包才會有。這些字段的意義如下:
- Version(V):2 bits
RTP版本号,現在用的是2。(第一個RTP草案用的1)
- Padding(P):1 bit
如果設定了該字段,封包的末尾會包含一個或多個填充位元組,這些填充位元組不是payload的内容。最後一個填充位元組辨別了總共需要忽略多少個填充位元組(包括自己)。Padding可能會被一些加密算法使用,因為有些加密算法需要定長的資料塊。Padding也可能被一些更下層的協定使用,用來一次發送多個RTP包。
- Extension(X):1 bit
如果設定了該字段,那麼頭資料後跟着一個拓展資料。
- CSRC count(CC):4 bits
CSRC清單的長度。
- Marker(M):1 bit
Marker會在預設中進行定義(預設和RTP的關系可以參考
rfc3551,我的了解是預設是對RTP的補充,以達到某一類實際使用場景的需要),在封包流中用它來劃分每一幀的邊界。預設中可能會定義附加的marker,或者移除Marker來拓展payload type字段的長度。
- Payload type(PT): 7 bits
該字段定義RTP payload的格式和他在預設中的意義。上層應用可能會定義一個(靜态的類型碼<->payload格式)映射關系。也可以用RTP協定外的方式來動态地定義payload類型。在一個RTP session中payload類型可能會改變,但是不應該用payload類型來區分不同的媒體流,正如之前所說,不同的媒體流應該通過不同session分别傳輸。
- Sequence number:16 bits
每發送一個RTP包該序列号+1,RTP包的接受者可以通過它來确定丢包情況并且利用它來重排包的順序。這個字段的初始值應該是随機的,這會讓
known-plaintext更加困難。
- Timestamp:32 bits
時間戳反映了RTP資料包生成第一塊資料時的時刻。這個時間戳必須恒定地線性增長,因為它會被用來同步資料包和計算網絡抖動,此外這個時鐘解決方案必須有足夠的精度,像是一個視訊幀隻有一個時鐘嘀嗒這樣是肯定不夠的。如果RTP包是周期性的生成的話,通常會使用采樣時鐘而不是系統時鐘,例如音頻傳輸中每個RTP封包包含20ms的音頻資料,那麼相鄰的下一個RTP封包的時間戳就是增加20ms而不是擷取系統時間。
和序列号一樣時間戳的初始值也應該是随機的,而且如果多個RTP包是一次性生成的,那它們就會有相同的時間戳。
不同媒體流的時間戳可能以不同的步幅增長,它們通常都是獨立的,具有随機的偏移。這些時間戳雖然足以重建單一媒體流的時序,但是直接比較多個媒體流的時間戳是沒辦法進行同步的。每一時間戳都會和參考時鐘(wallclock)組成時間對<timestamp,wallclockTime>,而且需要同步的不同流會共用同一個參考時鐘,通過對比不同流的時間對<timestamp,wallclockTime>,就能計算出不同流的時間戳偏移量。這個時間對<timestamp,wallclockTime>并不是和每個RTP包一同發送,而是通過RTCP協定,以一個相對較低的頻率進行共享。
- SSRC:32 bits
該字段用來确定資料的發送源。這個身份辨別應該随機生成,并且要保證同一個RTP session中沒有重複的SSRC。雖然SSRC沖突的機率很小,但是每個RTP用戶端都應該時刻警惕,如果發現沖突就要去解決。
- CSRC list:0 ~ 15 items, 32 bits each
CSRC list表示對該payload資料做出貢獻的所有SSRC。這個字段包含的SSRC數量由CC字段定義。如果有超過15個SSRC,隻有15個可以被記錄。
RTP Session多路複用
在RTP中,多路複用由目标傳輸位址(address:port)提供,不同的RTP session有不同的傳輸位址。
獨立的音頻和視訊流不應該包含在同一個RTP session中,也不應該通過payload類型和SSRC來區分不同的流。如果用同一個SSRC發送了不同的資料流,會引入如下問題:
- 假設2個音頻流共享了一個RTP session,并且用了同一個SSRC,如果其中一個要改變編碼,這就導緻了payload類型的改變,但是協定中沒有提供方法來讓接受者知道具體是哪個音頻流改變了編碼。
- 一個SSRC隻有一個對應的時序和序列号,如果多個流有不同的時鐘周期的話,就需要不同的時序。而且還不能用序列号來确認是哪個流丢包了。
- RTCP 發送者報告和接受者報告隻描述了時序和序列号而不包含payload類型資料。
- Mixer無法将不相容的兩個流合并
- 如果一個RTP session中包含了多個媒體流後就會失去如下優勢:
- 使用不同的網絡路徑或者配置設定網絡資源
- 隻接受某一種媒體資料(網絡較差時隻接受audio)
- 接收方對不同的媒體類型做不同的處理
不同的流使用不同的SSRC但是仍然用同一個RTP session發送确實可以解決前三個問題,但是仍然無法解決後兩個問題。
預設可能對RTP頭的改動
現有的這些RTP封包頭對一般應用來說已經足夠了。如果有需要,頭字段可以根據預設進行一些修改,但仍要保證檢測和統計功能的正常使用。
RTP頭拓展
RTP提供了一個拓展機制,讓上層應用可以将自定義的資訊存儲在RTP封包頭。如果上層應用收到了無法識别的頭部拓展資料,它們會忽略它。
值得一提的是,這個頭部拓展是有一些限制的。如果附加資訊隻對某些payload格式才有意義,那麼最好還是别把這些資訊放到頭部拓展中,而是放到payload部分。
如果RTP Header中的X位設定為1,那麼Header後必須跟着一個不定長度的拓展塊,緊跟着CSRC list(如果有的話)。拓展部分的頭部包含一個16-bit的資料來描述拓展塊包含多少個32-bit字(不包括拓展部分的頭部)。因為RTP頭部後面隻能連接配接一個拓展塊,考慮到有些應用可能會有多種類型的拓展塊,是以拓展塊的頭16-bit留給開發者去自定義一些參數。
RTP控制協定
同一個Session所有參與者會周期性地發送控制封包,RTP控制協定就是通過這種方式進行的,和RTP資料的傳播一樣采用了多點傳播的機制。下層協定必須提供資料包和控制封包的多路複用功能,例如使用獨立的UDP端口分别傳輸資料和控制封包。RTCP協定具有如下四大功能:
- 最主要的功能是回報資料分發的品質。這也是RTP作為一個傳輸協定來說最關鍵的功能,而且它和流量控制,擁塞控制息息相關。回報資訊可能會直接影響自适應編碼的控制。發送回報報告給所有的參與者可以讓它們評估遇到的資料分發問題是個人問題還是全局問題。通過IP多點傳播這樣的分發機制,像網絡提供商這樣的機構即便不加入到這個RTP session中也能收到回報資訊,它們會扮演一個第三方監測者的角色去确認資料分發問題。這個回報的功能無論是RTCP的發送者還是接受者都會進行報告。
- RTCP還會給每個RTP source帶一個不變的傳輸層身份識别符(CNAME),因為SSRC可能會中途改變(程式重新開機),是以接受者需要這個CNAME來持續追蹤每個與會者。而且,接受者可以通過CNAME來将同一個與會者的所有資料流聯系在一起,比如同步音頻和視訊。單個媒體内部的資料同步也需要NTP和RTP時間戳,這些資料都在資料發送者發送的RTCP封包中。
- 因為前兩個功能需要所有的與會者都發送RTCP封包,是以需要适當的控制封包發送的頻率以至于RTP協定可以在大量用戶端一同加入時也能正常工作。通過每個參與者都廣播控制封包的方式,每個人都能獨立地計算出參與者的總數。
- 還有一個可有可無的功能,RTCP可以用來共享小量的session控制資訊,例如辨認參與者的身份。它通常來說,會被與那些管理比較松散的Session使用。RTCP可以作為一個友善的與其他參與者溝通的通道,但是你也别期望RTCP可以滿足一個應用的所有傳輸控制需求,這類需求往往是通過一個更高層的session控制協定來滿足。
這四個功能中,前三個應該會被所有應用場景使用(IP多點傳播機制下)。RTP應用的設計者應該避免自己的應用隻能工作在單點傳播模式,RTP應用應該設計成可拓展的,要考慮大量使用者并發時的情況。此外,RTCP的傳輸應該根據發送者和接受者角色的不同而分别進行控制,例如一些單項連接配接,接受者的回報資訊就發不出來。
提醒:像是指定源多點傳播路由(SSM),隻有一個人可以發送資料,其他接受者不能用多點傳播來和其他人直接通訊。對于這種情況,建議完全關閉接受者的原始RTCP功能,然後為這個SSM設定一個RTCP的擴充卡,來接受所有的回報。
RTCP包格式
RTCP定義了許多包類型來傳輸不同的控制資訊:
- SR:發送者報告,發送者資料發送和接受的統計。
- RR:接收者報告,隻接受資料的節點的接受統計。
- SDES:Source描述,包括CNAME
- BYE:表示退出
- APP:上層應用自定義
每個RTCP包都有一個和RTP類似的固定格式的頭,後面跟着長度不定的結構化資料,在不同RTCP類型時,這些結構化資料各不一樣,但是它們必須都要32-bit對齊。RTCP的頭部是定長的,而且在頭部有一個字段來描述這個RTCP資料的長度,是以RTCP可以被複合成一組一同發送,還不需要任何分隔符來分割出單個的RTCP包。下層協定可能會根據自己的情況決定将多少個RTCP封包複合在一起組成一個複合包。
複合包中的每個獨立的RTCP封包都是無序的,而且可能會被随意複合。為了讓協定的功能正常運作,會有如下限制:
- 接受統計(SR|RR)的發送頻率需要達到帶寬的最大限制,是以每個周期發送的RTCP複合包都需要包含一個這類封包。
- 一個新來的接受者需要盡可能快地得到資料源的CNAME,因為它要用CNAME來确定每個資料源分别對應哪個人,并将資料源聯系在一起進行同步,是以每個RTCP複合包必須包含SDES CNAME(除非這個複合包被拆成兩半一半加密,一般明文,這部分後面會介紹)。
- 複合包中包類型的數量需要限制,這可以減少其他發錯的包或者不相關的包被識别成RTCP包的可能性,還能增加第一個字中固定比特的數量。
是以,一個複合包中至少需要含有2種類型的RTCP封包,它的格式如下:
- Encryption prefix:當且僅當這個複合包需要加密的時,那複合包在頭部插入一個随機的32-bit數。如果加密算法需要填充資料的話,需要填充到複合包中的最後一個RTCP包後。
- SS 或 RR:複合包中第一個RTCP包必須是一個報告封包,這可以加速封包頭部資料的校驗。即便沒有RTP資料的發送和接受也要有一個報告封包,這種情況下必須發送一個空的RR封包,并且即便是這個複合包中的其他RTCP封包是BYE也要這麼做。
- Additional RRs:如果接受的RTP資料來自超過31個不同的源,前31個接受報告會寫進SR或者RR封包中,多出來的接受報告應該緊跟着預設的報告封包(SR或RR)。
- SDES:SDES包必須包含CNAME,每個複合包必須包含一個SDES包。如果上層應用有需要,也可以加入一些别的SDES封包,這視帶寬限制而定。
- BYE或APP:其他RTCP包類型(包括協定中還未定義的),可能以任意順序跟在SDES後面,但是希望BYE包寫在最後面(BYE包需要和SSRC/CSRC一同發送)。
一個單獨的RTP參與者應該在一個報告周期中隻發送一個複合RTCP包,該周期每個參與者應該視帶寬情況來估算,除非一個複合包被拆分加密。如果資料發送者的數量太多,以至于除了增加MTU這個方法之外,沒辦法将所有RR封包塞進一個複合包時,那麼一次隻會将部分RR資料塞進這個複合包,其他的資料就不發送了。當然,為了讓所有源的接受情況都得到報告,會在多個周期内以環的形式循環共享所有源的接受情況。
為了減少資料包的開銷,一般建議Translator和Mixer無論何時都能将多個源的RTCP封包複合成一個複合包。下圖展示的就是一個Mixer生成的複合包的例子:
如果一個複合包的長度超過了下層網絡協定的MTU的話,這個複合包會被拆分成多個更小的複合包分别發送。這不會對RTCP的帶寬估計産生任何影響,因為即便Mixer的複合包被拆分成了多個更小的複合包,但是這個些更小的複合包也要滿足"每個複合包都要包含SS或RR"這一條件,是以每個更小的複合包至少也對應了一個參與者,這樣Mixer生成的複合包就和它收到的RTCP包數量基本比對,甚至更少。
如果某一用戶端收到了它無法解析的RTCP類型的包,那它應該忽略這個包。附加的RTCP包類型會通過
IANA進行注冊。
RTCP傳輸周期
RTP的設計理念是它要能根據session參與者的人數增加而進行自适應處理。例如,音頻會議中同一時刻說話的一般也就那麼一兩個人(這就從内部限制了音頻資料的傳輸),那麼可以認為多點傳播資料分發所用到的帶寬資源和與會人數無關。控制資訊的發送和音頻資料的傳輸不同,每個人都會不停的發送RTCP封包,如果每個參與者的接受報告以同一個周期發送的話,RTCP封包傳輸所消耗的資源會随着與會人數的增加而線性增加。是以,當與會人數增加時,RTCP封包的發送間隔應該相應的動态地增大。
對每個session來說,會有一個總的帶寬限制(Session bandwidth),它會被配置設定給每個獨立的與會者。整個網絡的帶寬可能會有所保留,并從網絡層面強制限制Session的帶寬。如果網絡的帶寬沒有保留的話,也可能會有一些别的限制,不過這些都跟網絡環境有關,總之最後會得出一個靠譜的Session最大帶寬。Session帶寬可能會通過實際會消耗的網絡資源進行評估,或者中途根據session的剩餘可用帶寬來變化。這些都和媒體資料的編碼無關,但是會根據帶寬的限制來選擇具體使用哪種編碼。通常來說,會預估session中有多少參與者會同時發送資料,然後根據同時發送這類資料大概需要多少帶寬這種方式來評估session的帶寬。在音頻會議中,通常來說就是一個音頻發送者所需要的帶寬(一般同一時間隻會有一個人說話)。對于分層編碼這種情況,每一層都在一個獨立的RTP session中,這些session都有自己獨立的帶寬限制。
在RTP session中應該有一個管理應用來調整session帶寬,但是那些音頻會議應用可能會基于session中選用的編碼格式,假設隻有一個發送者發送資料,給自己設定一個預設的帶寬限制。這個音頻會議應用可能也會受到多點傳播網絡(或其他因素)的帶寬限制。同一個session的所有參與者必須使用統一的session帶寬限制,因為隻有這樣大家才是以一個相同的頻率發送RTCP包。
Session帶寬評估過程需要考慮到下層的傳輸層和網絡層是否有一些資源保留機制。而且上層應用也需要知道RTP下層使用了什麼協定,但是不需要知道資料鍊路層及以下的協定,因為從資料鍊路層開始資料包的頭就各不相同了。
控制封包的傳輸應該隻使用Session帶寬中很小的一部分,這樣媒體資料的傳輸才會不受影響。建議RTCP傳輸使用session帶寬的5%,媒體資料發送者至少要占用1/4的RTCP帶寬,因為這樣做的話,新加進來的人可以更快的收到媒體資料發送者的CNAME。在某些預設中,如果發送者的數量超過1/4可能會完全關閉接受報告,雖然RTP協定标準并不推薦這樣做,但是那些隻有單向鍊路的系統或者不需要接受者回報的系統一般是這麼做的。
RTCP封包的傳輸間隔一般都會稍微長一點,這樣,當參與者的數量陡增時,封包的數量就不會超過帶寬限制太多。當一個應用啟動時,它應該等一段時間(一般是最小RTCP封包間隔的一半)再發送第一個RTCP封包,這樣這可讓發送間隔的計算更快的收斂。推薦RTCP封包發送的最小間隔是5秒。
RTP的上層應用可能會使用更短的RTCP發送間隔,但是也會遵循如下原則:
- 對于多點傳播形式的Session,隻有資料發送者會使用更短的RTCP發送間隔。
- 對于單點傳播形式的Session,無論是發送者還是接受者都有可能使用更短的RTCP間隔,并且它們發送初始RTCP前可能不會等待一段時間。
- 所有的Session都應該根據最小RTCP發送間隔來确定參與者的逾時時間。
- 推薦的最小RTCP發送間隔時間使用"360 kb/ session帶寬(kb/s)"這種方式計算。這樣當session帶寬大于72kb/s時,RTCP發送間隔會小于5秒。
此外,為了讓RTCP能在大型session中正常運作,現有的算法還具有如下特點:
- RTCP封包發送間隔随着session參與者的人數增加而線性地降低。
- RTCP發包廂隔通常會随機縮放0.5~1.5倍,這樣做是為了避免大量的參與者同時發送RTCP封包。
- RTCP複合包中包含的控制封包資料會根據收發包情況動态變化
- 因為RTCP封包間隔是根據已知的session參與者情況計算的,是以當有新的人要加入到session時,可能會錯估整個session的規模,而是用了較短的RTCP間隔,尤其是當大批量的人一齊加入session時這種現象更加明顯。是以,可能會有一個"發送時機重整"算法,它實作了一個簡單的撤回機制,可以在session規模持續增長時,适當的撤回一些RTCP封包。
- 當有人通過發送BYE封包或者因為逾時退出Session時, RTCP的發送間隔應該縮短。
- BYE封包和其他RTCP封包相比,有一些特殊的地方。當有人想要退出,并發送BYE封包時,它可以在下一個發送周期到來之前就發送。當然,如果一大批人同時退出時,也會受到前面提到的RTCP封包撤回機制的影響。
維護Session成員的數量
我們已經知道了,計算RTCP發送間隔是需要清楚整個Session中成員數量的,當一個新的節點被監聽到時,它就會被加入到Session總數中,并且大家要把它加入到一個SSRC(CSRC)身份識别表中然後持續追蹤。大家隻有收到這個新節點的多個資料包,或者收到他的SDES包(CNAME)時才覺得這個新節點是靠譜的。當某個節點發了一個BYE之後,它的資訊可能就會被大家删了,但是考慮到可能有丢包或者網絡擁堵的情況,是以大家會先把它标記為"收到BYE",然後等一段時間,如果還沒收到它的别的封包,這時候才會把它删了。
如果一個節點超過一個RTCP周期都沒收到另一個節點的封包,它可能就會将其标記為不活躍,或者删了它,這就需要丢包的情況盡可能别發生。但是不丢包是不可能的,是以大家一般會将RTCP傳輸間隔乘以一個系數(大于1的數)作為逾時時間。
對于那些參與者很超級多的Session,可能沒法去維護一個SSRC表來存儲所有參與者的資訊。通常大家都會簡化這個SSRC表,但是需要注意的是無論怎麼簡化這個表都不能低估了參與者的總數,可以允許高估參與者總數。
RTCP封包的收發規則
首先,無論是多點傳播還是多個節點的單點傳播都必須遵循前面提到的RTCP間隔。為了正常完成RTCP封包的收發操作,Session中的每個參與者都會維護如下資訊:
- TP:最後RTCP封包的發送時間
- TC:目前時間
- TN:下一個要發送封包的時間點
- P-Members:計算上一個TN時參考的session成員總數
- Members:目前的session成員總是
- Senders:資料發送者總數
- RTCP_BW:RTCP的目标帶寬
- WE_Sent: 從倒數第二個RTCP封包發送後,到現在為止,是否發送過資料
- AVG_RTCP_Size:平均RTCP複合包大小,包括傳輸層和網絡層的頭
- initial:是否一個RTCP封包都沒發過
計算RTCP發送間隔
為了讓RTP協定具有可伸縮性,RTCP的發送間隔需要随着Session總人數的變化而适當的縮放。結合上述的部分狀态,我們按如下方式計算RTCP封包間隔:
- 如果媒體流發送者的數量小于總人數的25%時,這個間隔和目前節點是否是媒體流發送者有關(通過WE_Sent判斷)。如果是媒體流發送者,計算公式為:
,如果是媒體流的接收者,計算公式為:Senders * AVG_RTCP_Size / (25% * RTCP_BW)
。當媒體流發送者的數量超過25%時,發送者和接受者會被同等對待,即它們的RTCP周期公式為:(Members - Senders)* AVG_RTCP_Size / (75% * RTCP_BW)
。Members * AVG_RTCP_Size / RTCP_BW
- 如果某個參與者一個RTCP包都還沒發送,最小發送間隔間隔(Tmin)為2.5秒,否則為5秒。
- 決定的發送間隔(Td)會是第一步計算的值和Tmin中較大的那個。
- 發包時會在Td的基礎上随機縮放0.5~1.5倍
- 最終這個間隔還要除以e-3/2=1.21828,這是為了彌補因為"發送時機重整"算法帶來的影響(因為這個算法會導緻最終RTCP使用的實際帶寬比預計使用的帶寬低)。
初始化
當一個人剛加入到Session中時,tp=0,tc=0,senders=0,p-members=0,members=1,we_sent=false,
rtcp_bw = 5% * session帶寬,initial=true,avg_rtcp_size=之後會發送的RTCP包的大小,然後計算發送間隔T時,會根據上述初始狀态進行計算,并以此作為參考發送第一個包,最後将自己的SSRC加入到成員清單中。
接受RTP和Non-BYE RTCP包
當RTP或者RTCP包被另一個人(A)接收到了,如果對A來說這個包的SSRC他沒見過,那麼他就會将其加入到SSRC表中,并更新session總人數(Members)。對每個CSRC也會做同樣的操作。
如果收到了一個RTP封包,并且其對應的SSRC沒在發送者SSRC表中,那他就會把它加進發送者SSRC表中,并更新發送者的總數(Senders)。
當每個複合RTCP封包被接受到時,平均RTCP封包大小(AVG_RTCP_Size)的狀态就會更新,更新公式為:
AVG_RTCP_Size = (1 / 16) * last_rtcp_package_size + (15 / 16) * previous_avg_rtcp_size
接受RTCP BYE封包
如果接收到了RTCP BYE封包,會在成員清單中确認一下,如果有對應的SSRC項,就會把他移除并更新成員總數(Members)。同時也會在發送者SSRC表中做類似的操作,如果找到了就删除它并更新發送者總數(Senders)。
此外,為了讓RTCP的傳輸率跟随Session中人數的變化而動态變化,如下算法會在收到BYE封包時執行:
- TN按照如下公式更新:TN = TC + (Members / P-Members) * (TN - TC)
- TP按照如下公式更新:TP = TC - (Members / P-Members) * (TC - TP)
- 下一個RTCP封包按照新的TN訓示發送(比原來發的更早了)
- 将P-Members設定成Members的值
這個算法沒有考慮到一個意外情況,那就是當一大波人(并不是所有人)同時退出Session時,會導緻RTCP的周期降到一個非常小的值,這樣可能出現錯誤的Timeout判斷,最終它會導緻整個Session的總人數降到0。但是,這種情況一般來說很少發生,是以大家都覺得它問題不是很大。
SSRC的逾時
我們需要偶爾确認一下是不是太久沒收到某個與會者的封包了,一般來說每個RTCP周期内都必須确認。如果發現了逾時,就需要将這個SSRC從成員清單(Members & Senders)中移除,并更新目前人數。
- Member表: 一般超過5個發送周期(不考慮随機縮放因素)未收到某人的消息,會被确定為逾時。
- Sender表: 一般是2個發送周期。
如果某個成員被确定為逾時,上一步介紹的算法就操作起來了。
發送倒計時
我們已經知道,每個RTCP都是周期性的發送的,當發送完一個RTCP封包時,就會根據TN建立一個倒計時,每次當倒計時歸零時就會重複如下操作:
- 計算傳輸周期T,引入随機縮放因素
- 如果TP + T <= TC,立即發送一個RTCP封包,并将TP設定為TC,TN設定為TC + T,下一個倒計時會在TN時刻歸零。如果TP + T > TC,就不發送了RTCP封包,計算TN = TC + T後,然後重設一個定時器在TN歸零。
- P-Members設定為Members
如果發送了RTCP封包,
initial
會被設定為FALSE,
AVG_RTCP_Size
會按如下方式更新:
AVG_RTCP_Size = (1 / 16) * last_rtcp_package_size + (15 / 16) * previous_avg_rtcp_size
發送BYE封包
當某個人想要退出Session時,他就會發一個BYE封包給其他人。為了防止一大幫人同時退出Session時出現BYE封包井噴的情況,是以當Session人數超過50時,會按下述方式操作:
- 當一個參與者想要離開時,TP會設定成TC,Members和P-Members會設定成1,initial設定成1,we_send設定成false,senders設定成0,avg_rtcp_size設定成複合BYE封包的大小。然後計算RTCP發送間隔T,下個BYE封包會在TN = TC + T後發送。
- 每當這個要離開的人收到了别人的BYE封包時,Members就會增加1,無論這個人是否在成員清單中。Members的數量隻有收到BYE封包時才增加,其他封包都不管。同樣,avg_rtcp_size也隻管收到的BYE封包的大小。Senders數量也不變。
- 對了BYE封包來說,除了狀态值的維護套路變了,發送邏輯和前面提到的都一樣
通過上述方案,即可以讓BYE封包正确地發送,還能控制整體帶寬。最差的情況下,也隻會導緻RTCP封包傳輸占用10%的Session總帶寬。
有些參與者可能不想按照上述的方式發送BYE封包,他們可能什麼也不發就離開了。這類情況會被Timeout機制hold住。
如果一個參與者要離開時,session的總人數小于50,他可能會直接發送一個BYE封包,也可能按照上述方案來進行。
此外還有一個無論如何都要遵循的規則,如果一個參與者一個RTP封包或者RTCP封包都沒發送過的話,那他離開Session時絕對不能發送BYE封包。
更新WE_Sent
當某個參與者最近發送過一個RTP後,他就會将WE_Sent置為true并将自己加入到Senders表中,否則如果超過兩個RTCP發送周期的時間内都沒發送過RTP封包,那他就會将自己從Sender表中移除,并将WE_Sent置為false。
SDES類封包的帶寬配置設定
SDES封包中除了必需要有的CNAME之外,還有一些别的資訊,比如NAME(個人名稱),EMAIL(email位址)等。上層應用也可以自定義的一些封包類型,但是要小心别付加了太多的自定義資訊以至于拖慢了整個RTCP協定的運轉。建議這些附加内容的帶寬占用不要超過整個RTCP協定帶寬的20%。此外,也不要覺得每個上層應用都會包含所有的SDES内容。上層應用要根據實際使用的情況給這些内容配置設定一定的帶寬,一般來說他們會通過控制發送間隔來控制這部分的帶寬。
比如,一個應用的SDES可能隻包含CNAME,NAME,和EMAIL,其中NAME可能就會比EMAIL配置設定更多的帶寬。因為NAME會一直顯示出來,而EMAIL可能隻在點選檢視的時候才顯示。在每個RTCP發送周期裡,SDES中都會包含CNAME。如果假設RTCP周期是5秒的話,可能每15秒SDES才會附帶一個除CNAME以外的資訊,以2分鐘為例,其中7次附帶的是NAME資訊,1次附帶的是EMAIL資訊。
Sender & Receiver報告
RTP使用Sender報告(SR)和Receiver報告(RR)來回報資料的接受品質,如果是媒體資料的發送者那就會發送SR,否則發送RR。這兩類封包是通過頭部的封包類型識别碼來做區分的。SR相對于RR來說多了20byte的Sender相關資訊,除此之外其他内容都是一樣的。
SR封包
SR封包包含三個部分,第一個部分是頭部,有8 BYTE,各個字段的含義如下:
-
version (V): 2 bits
RTP協定版本
-
padding (P): 1 bit
是否包含填充,最後一個填充位元組辨別了總共需要忽略多少個填充位元組(包括自己)。Padding可能會被一些加密算法使用,因為有些加密算法需要定長的資料塊。在複合包中,隻有最後一個RTCP包需要添加填充。
-
reception report count (RC): 5 bits
有多少個接受報告。可以為0。
-
packet type (PT): 8 bits
200表示SR封包。
-
length: 16 bits
封包長度(按32-bit字統計),包含頭部和填充位元組。
-
SSRC: 32 bits
身份定位符。
第二部分是發送者資訊,包含20 BYTE的資料,總結了這個發送的的傳輸統計,各個字段的含義如下:
-
NTP timestamp: 64 bits
Wallclock time,用于計算RTT。
-
RTP timestamp: 32 bits
RTP時間戳,基于NTP的某一随機偏移量。用于媒體資料内同步。
-
sender's packet count: 32 bits
這個SSRC總共發送了多少包
-
sender's octet count: 32 bits
這個SSRC總共發送了多少BYTE的資料。
第三部分可能什麼都沒有,也可能有多個接收報告,這取決的上次報告以後收到了多少個Sender的資料。每個報告塊統計了一個SSRC的包數。具體内容如下:
-
SSRC_n (source identifier): 32 bits
這個資訊塊對應的SSRC。
-
fraction lost: 8 bits
上次SR或RR發送後到目前為止的丢包率。
-
cumulative number of packets lost: 24 bits
整體過程的丢包總數。
-
extended highest sequence number received: 32 bits
低16-bit是收到的最新的RTP封包序列号,高16-bit是序列号循環的次數。
-
interarrival jitter: 32 bits
RTP資料封包抵達時間的抖動。如果Si代表i包中包含的RTP時間戳,Ri代表i包被接受時的RTP時間戳,那兩個包i和j的到達時間抖動算法如下:
我們在計算這個抖動時,要結合每個包的抖動,來計算一個平均值,計算平均值的方案如下:D(i,j) = (Rj - Ri) - (Sj - Si) = (Rj - Sj) - (Ri - Si)
J(i) = J(i-1) * (15 / 16) + (|D(i-1,i)|)/16
-
last SR timestamp (LSR): 32 bits
該SSRC最後一個RTCP封包(SR)中帶的NTP時間。
-
delay since last SR (DLSR): 32 bits
從該SSSR最後一個RTCP封包(SR)被收到以來經過的時間。
資料的發送者可以通過目前時間A,接收到RR部分中的LSR和DLSR來計算RTT,計算示意圖如下:
RR封包
接受報告的格式和發送封包格式一樣,隻不過它在頭部中用201表示這是一個RR封包。此外RR封包中不含有上述SR封包中的第二部分。如果RR封包是空的那麼需要在頭部标明
RC=0
發送/接受封包的拓展
一些預設可能根據自己的需求,要在接受報告和發送報告中附加一些資訊。那麼這些附加内容應該在SR或者RR的結尾之後。如果這些内容隻有發送者相關,那麼RR中就不包含這些資訊。
分析發送報告和接受報告
這些接受品質的報告資訊可能不光隻有發送者要使用,接受者或者第三方監控器也會使用。發送者可能根據接受品質調整自己的傳輸政策。接受者可以根據這個資訊來确定自己遇到的問題是本地網絡的問題還是整個Session的問題。網絡的管理者可以根據這些資訊來評估整個網絡環境的情況。
SDES封包
SDES是一個三級結構,它包含一個頭和0個或多個資料塊,每一個資料塊對應了一個SSRC或CSRC,它又由多個描述字段組成。頭部的資訊如下:
-
version (V), padding (P), length:
和上面一樣
- 202表示SDES類型
-
source count (SC): 5 bits
SSRC/CSRC塊的數量。
每一個塊中都包含多個描述内容,這些描述内容都是32對齊的,其中前8-bit描述了類型,接着8-bit描述了資訊長度(不包含前16-bit),然後資訊内容。注意資訊部分不能超過255 BYTE,這和前面的很多工作類似是為了限制RTCP的帶寬。
描述的文本内容是UTF-8編碼的。如果要使用多位元組的編碼,需要在醒目的地方表示用的什麼的編碼。
各個描述部分是沒有中間分隔的,是以要用空位元組來填充以達到對齊的效果。注意這裡的填充和RTCP頭部的P不是一個概念。
末端節點發送的SDES包含他自己的資料源辨別。而Mixer發送的SDES包含多個CSRC,如果CSRC的數量超過了31個,會拆分成多個SDES封包。
SDES的所有類型會在後面一一介紹。其中隻有CNAME是強制要有的。可能有一些類型的的描述隻有部分預設才會使用。但是這些内容都是在一個共通的地方來記載,以防止不同的預設使用的描述類型發生沖突。如果要注冊新的類型,需要通過IANA注冊。
CNAME:權威的末端節點身份辨別
CNAME有如下特征:
- 因為SSRC在許多意外情況下會重新生成,是以CNAME被用來綁定舊的SSRC和新的SSRC,來保持資料源的連續。
- 和SSRC一樣,CNAME也需要保證唯一性(同一個Session中)
- 為了讓同一個參與者的多個SSRC綁定在一起,我們需要CNAME是固定的
- 為了讓第三方監控用起來友善,CNAME應該即友善程式使用,也要設計成可讀的,可以根據它确認來源
是以CNAME應該通過算法來生成而不是手動生成。為了滿足如上需要,一般來說是按照如下的格式來描述CNAME:
- "user@host" eg: "[email protected]" or "doe@2201:056D::112E:144A:1E24"
- "host", 如果是單使用者系統,擷取不到user時隻使用host。eg: "sleepy.example.com","192.0.2.89" or "2201:056D::112E:144A:1E24"
有些人可能會發現,如果上述的host使用的是子網位址的話,就沒辦法保證整個Session的唯一性了,通常這類沒有直接IP的使用者是通過一個RTP級别的Translator來通路公共網絡。這個Translator會處理從私有位址到公網位址的轉換工作。
NAME:使用者名
這個是描述資料源的真實名字,eg:"John Doe, Bit Recycler"。整個Session過程中希望這個值不變。全Session不需要唯一。
EMAIL:電子郵箱位址
電子郵箱位址,eg: "[email protected]"。整個Session過程中希望這個值不變。
PHONE:電話号碼
電話号碼需要以國際通路碼開頭,eg: "+1 908 555 1212"
LOC:使用者地理位址
視應用不同,詳細程度會各不相同。
TOOL:應用名或工具名
帶版本号的應用名,可以用來DEBUG。
NOTE:提醒/狀态
用來發送暫時性的消息描述目前狀态。eg: "on the phone, can't talk"
PRIV:自定義拓展
上層應用自定義的格式。一般都是用過一個字首描述消息類型,然後後面跟着消息正文。
BYE封包
BYE封包表示一個或者多個SSRC已經走遠了。
- 同上
- 203表示BYE封包
- 退出Session的SSRC的數量
如果BYE封包被Mixer收到了,Mixer應該啥都不改動,就發給下一節點。如果Mixer關閉了,它要發送一個包含它管理的所有SSRC的BYE封包。BYE封包中可能也會跟着帶一些離開原因的描述。這些描述和SDES中帶的描述類似,需要32對齊,用空位元組填補空缺。
APP:應用定義的RTCP封包
APP封包一般用于實驗性的功能和開發。如果識别到了不認識NAME那麼上層應用一般都會忽略它。如果開發或者測試功能穩定了,一般是要通過IANA注冊一個新的RTCP封包類型。
-
subtype: 5 bits
APP封包子類型,一般是上層應用定義。
- 204表示APP類型的RTCP封包。
-
name: 4 octets
一般是應用名,防止subtype沖突。
-
application-dependent data: variable length
和上層應用相關的内容,需要32-bit對齊。
RTP Translator & Mixer
作為末端節點的補充,RTP引入了Translator和Mixer的概念,它們是RTP層的中間件。雖然這多少增加了協定的複雜度,但是它們對音視訊通話應用來說它們還是很關鍵的,因為他們能解決防火牆問題和低帶寬連接配接的問題。
描述
一個RTP Translator/Mixer 連接配接至少兩個傳輸層的使用者組。通常來說,這裡提到的使用者組是公共網絡的概念,傳輸層協定會為其生成一個多點傳播位址(ip:port)。網絡層協定,像是IPv4和IPv6對RTP協定來說是隐藏的。一個系統可能會有多個Translator和Mixer(多個Session),它們中的每一個都可以看作是一個使用者組的邏輯分割。
為了避免建立在建立Translator和Mixer造成了網絡包循環,必須遵循下列規則:
- 每個通過連接配接Translator和Mixer而加入Session的使用者組,要麼需要網絡層隔離,要麼最少互相知道知道(protocol,address,port)中的一個。
- 由上一個規則推廣的話,各個使用者組絕對不能同時連接配接多個Translator或者Mixer,除非有某種機制能保證他們之間資料被阻斷
Translator:在不改變RTP封包SSRC的條件下,向後傳播該封包,正因為如此,封包的接受者才能識别到Translator轉發後的封包到底是來自哪個人。有些Translator可能直接轉發封包,不做任何改動,也有可能改變資料編碼,payload類型和時間戳。如果多個資料封包被重新編碼并合并到一起的話,Translator必須為這類封包指定一個組新的序列号。這樣,輸入封包的丢失就會導緻輸出封包的斷層。資料的接受者一般是不知道Translator的存在的,除非通過payload類型的不同或者傳輸層封包的源位址來判斷。
Mixer:從一個或多個資料源那裡接收資料,随後可能會改變資料的格式,然後将這些資料合并,并傳遞給下家。因為多個資料源的時序并不一定是同步的,是以Mixer需要整合各個資料源的時序關系,并将其映射到自己的一套時序上,是以Mixer也是一個SSRC,所有通過Mixer的封包必須打上該Mixer的SSRC。為了表示這些資料的原始資料源,一般會通過CSRC清單來記錄。有些Mixer可能自己也是一個原始資料源,是以他自己的SSRC也會出現在CSRC清單中。有些應用可能不希望Mixer的SSRC出現在CSRC中,但是這樣可能就無法發現循環網絡包。
上圖是一個Mixers和Translators連接配接的例子。[]代表末端節點,()代表Mixer,<>代表Translator,"M1:48(1, 17)"表示Mixer1的封包,48是Mixer1的SSRC,括号裡的1,17是CSRC,它合并了E1:17和E2:1這兩個節點的資料。
Translator處理RTCP
除了要轉發資料包,進行資料包的更改,Translator和Mixer也要發送RTCP封包。在很多情況下,它會将收到的末端節點的RTCP封包合并到複合包中。當再次收到這些包時或者自己的RTCP周期到時,它會将複合包發送出去。
有的Translator可能對收到的RTCP封包不做任何改動,隻是簡單的轉發這個包。如果這個Translator改變了封包資料的payload,它必須對SR或者RR做相關的改動。通常來說,Translator不能将多個資料源的SR和RR合并,因為這樣會導緻RTT的計算出現問題(LSR,DLSR)。
- SR中的發送者資訊: Translator不會建立自己的發送者資訊,它會将收到SR傳給下家。其中SSRC不會發生任何改動,但是發送者資訊有必要的話一定要做适當的改動。如果Translator改變了資料編碼,那"byte count"字段就要更改。如果他将多個資料封包合并,那它需要修改"sender's packet count"字段。如果它改變了時間頻率,那就需要修改"RTP timestamp"。
-
SR/RR中的接受者資訊:SSRC不會發生任何改動,如果Translator改變了序列号,那就需要修改"extended last sequence number",在某些極端情況下,它可能完全沒有接受回報,或者根據接收到的SR/RR來建構自己的接受報告。
一般情況下Translator是不需要自己的SSRC的,但是如果是為了表示自己的資料接受情況,它可能也會生成自己的SSRC,并将這些RTCP封包發送過所有的連接配接者。
- SDES:一般Translator收到SDES後會什麼都不改就發給下家,但是也有可能為了節約帶寬篩掉CNAME之外的資訊的,如果Translator要發送自己的RR資訊,那它一定要發送一個自己的SDES給所有連接配接者。
- BYE:無改動轉發,如果Translator有自己的SSRC也要發送自己的BYE。
- APP:無改動轉發。
Mixer處理RTCP
因為Mixer會生成自己的資料流,是以他不會轉發經過他的SR和RR而是為連接配接雙方發送自己的SR和RR封包。
- SR的發送者資訊:Mixer不轉發資料來源的發送資訊。它會生成自己的發送者資訊并把它發送給下家。
- SR/RR中的接受者資訊:Mixer會生成自己的接受資訊,然後發送給所有資料來源,它絕對不能做接受報告的轉發工作,或者把自己的接收資訊發給錯誤的對象。
- SDES:Mixers通常會不做任何改動就轉發SDES資訊,但是也有可能為了節約帶寬過濾除了CNAME之外的其他資訊。Mixer必須發送自己的SDES封包。通常,Mixer會将多個收到的SDES打包一起發送。
- BYE:Mixer必須轉發BYE封包。如果Mixer要退出時,它會将所有資料來源的SSRC放進BYE封包,也包括自己的SSRC。
- APP:視上層應用。
瀑布型Mixer
一個RTP Session可能包含多個Mixer和Translator,就像上圖一樣。如果Mixer是瀑布型的,就像M2和M3,一個Mixer收到的資料可能是已經合并過的,它有自己的CSRC清單。那麼第二個Mixer需要将之前的CSRC和自己接受的所有SSRC合并。就像圖中M3的輸出是M3:89(64,45)。
SSRC的配置設定和使用
前面已經說過SSRC是一個随機的32-bit數,它需要在整個Session内保證唯一性。是以同一個網絡下的參與者在剛加入Session時使用不同的SSRC至關重要。
我們不能簡單的用本地的網絡位址,因為可能不唯一。也不能不考慮初始狀态而簡單地調一個随機數函數。
碰撞的可能性
因為SSRC是随機選擇的,這就可能多個資料源選用了相同的SSRC。如果大家是同時加入Session的話,這個碰撞的幾率就更高。如果SSRC的數量是N,L是SSRC的資料長度(這裡是32),那麼碰撞的可能性是
1 - exp(-N**2 / 2**(L+1))
,當N=1000時,碰撞率大概是10**-4。
通常來說,實際的碰撞率會比上述的最壞情況要低。通常一個新節點加入時,其他節點已經有了自己的唯一SSRC,這時候碰撞的機率隻是生成的新SSRC在這些現有SSRC之中的可能性。這時候碰撞率是
N/2**L
。當N=1000時,碰撞率大約是210*-7。
因為新加入的節點會先接受一段時間的封包然後才發送自己的第一個封包,是以在它生成SSRC時可以避開已知的SSRC,這也有效的降低了碰撞的幾率。
碰撞的解決方案和循環的發現
通常來說SSRC碰撞的可能性很小,所有的RTP實作必須有發現沖突的機制,并在發現沖突時作出适當的處理。如果資料源發現了任何一個别的資料源和自己使用同一個SSRC,它必須用原來的SSRC發送一個BYE封包,然後選用一個新的SSRC。如果一個資料的接受者發現了多個資料源的SSRC碰撞了(通過傳輸位址或者CNAME),那麼它會隻接受其中一個人的封包,丢棄另一個人的所有封包。
因為整個Session中的SSRC是唯一的,是以它也可以被用來發現環型封包。環形封包會導緻資料的重複以及控制資訊的重複。
- Translator可能會錯誤地将封包發送回該封包來的地方。
- 兩個Translator錯誤地同時啟動,它們兩個都會轉發同樣的資料。
- Mixer可能會錯誤地将合并封包發送回這些封包來的地方。
一個資料源可能發現自己的或者别人的封包被循環發送了。無論是封包循環還是SSRC的碰撞都會導緻同一個現象,即SSRC相同但是傳輸位址不同的封包。是以,如果資料源改變了自己的傳輸位址,那它就需要同時改變自己的SSRC來避免被檢測成環形封包。有一個需要注意的内容是,如果一個Translator再重新開機的過程中改變了自己的傳輸位址,那麼這個Translator轉發的所有資料都會被檢測成環。這類情況的解決方案一般有如下兩個:
- 重新開機的時候不改變傳輸位址
- 接受者的逾時機制
如果循環或者碰撞發生在離Translator和Mixer很遠的地方,我們就不能通過傳輸位址來發現。但是我們仍然可以通過CNAME的不同來發現SSRC碰撞。
為了解決上述問題,RTP的實作必須包含一個類似如下的算法。這個算法不包括多個資料源SSRC碰撞的情況,這類情況通常下都是先用原來的SSRC發送一個BYE然後重新選擇一個新的SSRC。
這個算法需要維護一個SSRC和傳輸位址的映射關系。因為RTP的資料和RTCP傳輸使用的是兩個不同的端口,是以一個SSRC對應的是兩個傳輸位址。
每次收到RTP封包和RTCP封包都會将其SSRC和CSRC在上述的表中進行比對。如果發現了傳輸位址對不上的情況,我們就可以說發現了一個循環或者碰撞。對于RTCP資料來說,可能每個資料塊都有自己獨立的SSRC,比如SDES資料,對于這種情況就需要分别比對。如果沒有在表中找到這個SSRC或者CSRC,就需要新添加一項。當收到BYE封包時,需要先比對這個BYE的傳輸位址,如果傳輸位址比對上了,就将這一項從表中删除。或者基于逾時機制,将逾時的資料從表中移除。
為了追蹤自己的資料封包循環情況,必須維護另一個清單,這個表存儲沖突封包的傳輸位址和收到該封包的時間。如果超過10個RTCP周期都沒有收到這個傳輸位址的沖突封包,就将該項從表中删除。
下面的算法還假設參與者自己的SSRC和狀态都包含在SSRC表中,它會先比對自己的SSRC。
if (SSRC or CSRC identifier is not found in the source
identifier table) {
create a new entry storing the data or control source
transport address, the SSRC or CSRC and other state;
}
/* Identifier is found in the table */
else if (table entry was created on receipt of a control packet
and this is the first data packet or vice versa) {
store the source transport address from this packet;
}
else if (source transport address from the packet does not match
the one saved in the table entry for this identifier) {
/* An identifier collision or a loop is indicated */
if (source identifier is not the participant's own) {
/* OPTIONAL error counter step */
if (source identifier is from an RTCP SDES chunk
containing a CNAME item that differs from the CNAME
in the table entry) {
count a third-party collision;
} else {
count a third-party loop;
}
abort processing of data packet or control element;
/* MAY choose a different policy to keep new source */
}
/* A collision or loop of the participant's own packets */
else if (source transport address is found in the list of
conflicting data or control source transport
addresses) {
/* OPTIONAL error counter step */
if (source identifier is not from an RTCP SDES chunk
containing a CNAME item or CNAME is the
participant's own) {
count occurrence of own traffic looped;
}
mark current time in conflicting address list entry;
abort processing of data packet or control element;
}
/* New collision, change SSRC identifier */
else {
log occurrence of a collision;
create a new entry in the conflicting data or control
source transport address list and mark current time;
send an RTCP BYE packet with the old SSRC identifier;
choose a new SSRC identifier;
create a new entry in the source identifier table with
the old SSRC plus the source transport address from
the data or control packet being processed;
}
}
層級編碼
對于不同Session的層級編碼傳輸,一般都是所有層都使用同一個SSRC,如果其中某一層發現了SSRC沖突,那麼隻改變這一層的SSRC,而且他層的SSRC不做改變。
安全
下層協定可能會提供RTP應用所需要的所有安全服務,包括認證,資料完整性,資料保密性。這些服務在IP協定中都有解決方案。因為Audio和Video初始化過程中需要資料加密,而這時候IP協定這一層的安全服務還沒有提供。是以,RTP需要實作一個RTP專用的保密服務。這個保密服務是非常輕量級的,而且保密部分的服務向後相容,以後可以随時進行更換。或者,某些預設會提供這部分加密服務,比如SRTP(Secure Real-time Transport Protocol),SRTP是基于Advanced Encryption Standard (AES)提供了一個比RTP預設加密服務更強大的實作。
保密性
保密性是指我們的封包隻希望一些特定的接受者可以解碼成明文,而其他人隻能得到無用的資訊,保密性是通過加密編碼來提供的。
當需要為RTP和RTCP封包提供加密服務時,所有傳輸的内容都會在下層封包那裡進行加密。對于RTCP來說,需要一個32-bit的随機數作為字首。而RTP封包不需要字首,取而代之的是随機序列号和時間戳偏移。因為随機部分很少,是以可以說這是一個非常弱的
初始向量。此外,SSRC也可被破解者修改,這是這個加密方案的另一個薄弱的環節。
對于RTCP來說,可能會将一個複合包分成兩批,第一批加密,後一批明文發送。例如,SDES部分的資訊可能加密,而接受報告部分不加密就發送出去,因為隻有這樣那些第三方監控器才能在不知道密鑰的情況下統計網絡狀況。如下圖所示,SDES資訊必須跟在一個空的RR後,并且要有一個随機字首。
RTP協定使用的Data Encryption Standard (DES)算法,使用cipher block chaining(CBC)模式,這需要資料填充到64-bit對齊。密碼算法使用零作為初始向量,因為RTCP封包中已經有一個随機字首了。
RTP之是以選擇這個預設協定是因為它用起來很容易,但是因為DES太容易破解了。是以推薦預設中使用更健壯的加密算法來替換這個預設方案,例如Triple-DES。這些算法普遍需要一個随機初始化塊,RTCP使用了32-bit的随機數作為字首,RTP使用了時間戳和序列号的随機偏移,可是相鄰的RTP封包之間的随機性就很差。需要注意的是,無論是RTCP還是RTP,它們的随機性都有限。加密型更好的應用,需要考慮更多的保密措施。例如SRTP配置檔案,就基于AES來加密,它的加密方案就更完備,選擇這個預設來使用RTP就挺不錯的。
前面提到過也可以用IP級的加密方案或者RTP級的加密,一些預設可能會定義别的payload類型來加密。這種方案,可能隻加密payload部分而頭部分使用明文,因為隻有payload部分才是應用真正需要的内容。這可能對硬體裝置來說非常有用,它即處了解密過程,又處了解碼過程。
身份認證和消息完整性
RTP協定這一層沒有身份認證和消息完整性服務,因為有些上層服務可能沒有認證就能使用。而消息完整性服務依賴下層協定來實作。
擁塞控制
所有的網絡傳輸協定都需要擁塞控制,RTP也不例外,但是因為RTP的資料傳輸不會發生特别大的激變(固定頻率發送,控制了帶寬),是以RTP的擁塞控制會和TCP這類的實作有很大差别,RTP不會像TCP那樣占用所有帶寬。RTP的這種資料傳輸模式雖然降低了擁塞的風險,但是一旦發生了擁塞,也不能莽莽地減少網絡負載。
因為RTP面向的是各種各樣的上層應用,沒有一個擁塞控制機制能适用于所有的情況。是以,擁塞控制部分應該定義在預設中,或者上層應用中。在有些預設中,會根據RTCP的回報來适當的調整資料傳輸帶寬。
RTP下的網絡層和傳輸層協定
RTP需要下層協定提供多路複用機制。對于UDP這類應用,推薦RTP應該使用一個偶數端口傳輸資料,和它相關的RTCP流應該是用高一位的奇數端口。在單點傳播模式下,每個參與者都需要一對端口來傳輸RTP和RTCP封包。兩個參與者可能使用相同的端口。絕對不能以接收到的封包網絡位址直接作為目标位址發送封包。
建議層編碼模式是,使用相鄰的端口,是以對于層N來說,資料端口是P+2N,控制端口是P+2N+1。對于IP多點傳播來說,可能不會得到相鄰的多點傳播位址。
RTP資料封包沒有描述封包長度的資訊。是以RTP封包依賴下層協定提供長度辨別。是以一個RTP封包的最大長度由下層協定限制。
如果RTP封包使用的下層協定是流傳輸協定的話,必須定義一套資料幀分割機制。
文章說明
更多有價值的文章均收錄于
貝貝貓的文章目錄版權聲明: 本部落格所有文章除特别聲明外,均采用 BY-NC-SA 許可協定。轉載請注明出處!
創作聲明: 本文基于下列所有參考内容進行創作,其中可能涉及複制、修改或者轉換,圖檔均來自網絡,如有侵權請聯系我,我會第一時間進行删除。
參考内容
[1]
rfc3550