基於WebRTC
的音視頻直播框架,使用RTP/RTCP
,這裏記錄下。
RTP/RTCP
一般情況下,在實時互動直播系統傳輸音視頻數據流時,我們並不直接將音視頻數據流交給 UDP 傳輸
,而是先給音視頻數據加個RTP 頭
,然後再交給 UDP
進行傳輸。爲什麼要這樣做呢?
以太網的 MTU 最大傳輸單元
爲 1500
字節 約爲1.5K,意味着傳輸一個I幀就需要幾十個包。意味着就需要拆包,然後重新組裝。
RTP協議
-
版本號(V)
:2比特,用來標誌使用的RTP版本。 -
填充位(P)
:1比特,如果該位置位,則該RTP包的尾部就包含附加的填充字節。 -
擴展位(X)
:1比特,如果該位置位的話,RTP固定頭部後面就跟有一個擴展頭部。 -
CSRC計數器(CC)
:4比特,含有固定頭部後面跟着的CSRC
的數目。 -
標記位(M)
:1比特,該位的解釋由配置文檔(Profile
)來承擔. -
載荷類型(PT)- Payload Type
:7比特,標識了RTP載荷的類型。音頻流的 PT 值與視頻的 PT 值是不同的,通過它就可以知道這個包存放的是什麼類型的數據。 -
sequence number
:序號,用於記錄包的順序。這與上面我們自己實現拆包、組包是同樣的道理。 -
timestamp
:時間戳,同一個幀的不同分片的時間戳是相同的。這樣就省去了前面所講的起始標記和結束標記。一定要記住,不同幀的時間戳肯定是不一樣的。
假設你從網上接收一組音視頻數據
...
{V=2,P=0,X=0,CC=0,M=0,PT:98,seq:13,ts:1122334455,ssrc=2345},
{V=2,P=0,X=0,CC=0,M=0,PT:111,seq:14,ts:1122334455,ssrc=888},
{V=2,P=0,X=0,CC=0,M=0,PT:98,seq:14,ts:1122334455,ssrc=2345},
{V=2,P=0,X=0,CC=0,M=0,PT:111,seq:15,ts:1122334455,ssrc=888},
{V=2,P=0,X=0,CC=0,M=0,PT:98,seq:15,ts:1122334455,ssrc=2345},
{V=2,P=0,X=0,CC=0,M=0,PT:111,seq:16,ts:1122334455,ssrc=888},
{V=2,P=0,X=0,CC=0,M=0,PT:98,seq:16,ts:1122334455,ssrc=2345},
{V=2,P=0,X=0,CC=0,M=0,PT:111,seq:17,ts:1122334455,ssrc=888},
{V=2,P=0,X=0,CC=0,M=0,PT:98,seq:17,ts:1122334455,ssrc=2345},
{V=2,P=0,X=0,CC=0,M=0,PT:111,seq:18,ts:1122334455,ssrc=888},
{V=2,P=0,X=0,CC=0,M=0,PT:98,seq:18,ts:1122334455,ssrc=2345},
{V=2,P=0,X=0,CC=0,M=0,PT:111,seq:19,ts:1122334455,ssrc=888},
{V=2,P=0,X=0,CC=0,M=0,PT:98,seq:19,ts:1122334455,ssrc=2345},
{V=2,P=0,X=0,CC=0,M=0,PT:111,seq:20,ts:1122334455,ssrc=888},
{V=2,P=0,X=0,CC=0,M=1,PT:98,seq:20,ts:1122334455,ssrc=2345},
...
PT=98
是視頻數據,PT=111
是音頻數據,那麼按照上面的規則很容易就能將視頻幀組裝起來。
RTCP 協議
在使用 RTP
包傳輸數據時,難免會發生丟包、亂序、抖動等問題,下面我們來看一下使用的網絡一般都會在什麼情況下出現問題:
- 網絡線路質量問題引起丟包率高;
- 傳輸的數據超過了帶寬的負載引起的丟包問題;
- 信號干擾(信號弱)引起的丟包問題;
- 跨運營商引入的丟包問題 ;
- ……
WebRTC
對這些問題在底層都有相應的處理策略,但在處理這些問題之前,它首先要讓各端都知道它們自己的網絡質量到底是怎樣的,這就是 RTCP
的作用。
RTCP
有兩個最重要的報文:RR(Reciever Report)和 SR(Sender Report)
。通過這兩個報文的交換,各端就知道自己的網絡質量到底如何了。
這裏我們以SR 報文爲例,看看 SR 報文中都包括哪些信息。
下面我就簡要說明一下該報文中字段的含義:
V=2
,指報文的版本。P
,表示填充位,如果該位置 1,則在 RTCP 報文的最後會有填充字節(內容是按字節對齊的)。RC
,全稱 Report Count,指 RTCP 報文中接收報告的報文塊個數。PT=200,Payload Type
,也就是說 SR 的值爲 200。
……
從上圖中我們可以瞭解到,SR
報文分成三部分:Header、Sender info和Report block
。在 NTP
時間戳之上的部分爲 SR
報文的 Header
部分,SSRC_1
字段之上到 Header 之間的部分爲 Sender info
部分,剩下的就是一個一個的 Report Block
了。那這每一部分是用於幹什麼的呢?
- Header 部分用於標識該報文的類型,比如是 SR 還是 RR。
- Sender info 部分用於指明作爲發送方,到底發了多少包。
- Report block 部分指明發送方作爲接收方時,它從各個 SSRC 接收包的情況。
通過以上的分析,你可以發現SR
報文並不僅是指發送方發了多少數據,它還報告了作爲接收方
,它接收到的數據的情況。當發送端收到對端的接收報告時,它就可以根據接收報告來評估它與對端之間的網絡質量了,隨後再根據網絡質量做傳輸策略的調整。
SR 報文
與RR 報文
無疑是RTCP
協議中最重要的兩個報文,不過 RTCP
中的其他報文也都非常重要的,如果你想學好 WebRTC
,那麼 RTCP
中的每個報文你都必須掌握。
比如,RTCP 類型
爲 206
、子類型爲 4 的 FIR
報文,其含義是 Full Intra Request (FIR) Command
,即完整幀請求命令。會緊接發送一幀I幀
該報文也是一個特別關鍵的報文,我爲什麼這麼說呢?試想一下,在一個房間裏有 3 個人進行音視頻聊天,然後又有一個人加入到房間裏,這時如果不做任何處理的話,那麼第四個人進入到房間後,在一段時間內很難直接看到其他三個人的視頻畫面了,這是爲什麼呢?
原因就在於解碼器在解碼時有一個上下文。在該上下文中,必須先拿到一個 IDR 幀之後才能將其後面的 P 幀、B 幀進行解碼。也就是說,在沒有 IDR 幀的情況下,對於收到的 P 幀、B 幀解碼器只能乾瞪眼了。
如何解決這個問題呢?這就引出了 FIR 報文。當第四個人加入到房間後,它首先發送 FIR 報文,當其他端收到該報文後,便立即產生各自的 IDR 幀發送給新加入的人,這樣當新加入的人拿到房間中其他的 IDR 幀後,它的解碼器就會解碼成功,於是其他人的畫面也就一下子全部展示出來了。
附圖
RTP協議頭
RTCP 協議頭:
RTCP PT 類型:
對於 205 和 206 兩種不同的反饋消息,又在 RFC5104 中做了更詳細的定義: