1、基本知識
https://blog.csdn.net/zpoison/article/details/86528327
元數據進行網絡傳輸,需要先進行逐層封裝和逐層解析的過程,如下圖:
協議與上下層關係簡圖
1.1 以太網幀 MAC幀
MAC幀是數據幀的一種,數據鏈路層的協議數據單元,包括三部分,幀頭和幀尾包含一些必要的控制信息,比如同步信息、地址信息、差錯控制信息等;數據部分則包含網絡層傳下來的數據,比如ip數據包。
MAC層要求定界字符之後的內容要在64字節到1518個字節之間,其中包括14字節的幀頭(目標MAC和源MAC,數據長度),4字節的幀尾(FCS,校驗值),46-1500數據部分(這裏是IP報文數據,IP報文數據通常最大爲MTU值1500,最小爲僅IP包頭40字節)。
通常我們用whireshark抓包TCP時,只會看到mac幀頭和數據部分,也就是一個包的數據長度爲14+(20+20 ~ 1500),即54 ~ 1514長度。(UDP爲14+(20+8 ~ 1500)即42~1514長度)
1.2 IP包
ip地址長度爲32bit,普通的IP包頭長爲20個字節(若有選項數據再增加m大小選項數據)。IP包的數據部分,通常是TCP數據包/UDP數據包。
IP包數據長度爲20(+m) + N,由於MTU=1500,所以IP數據長度至多爲1480字節。
1.3 UDP包
用戶數據報協議UDP(User Datagram Protocol),是無連接的,盡最大可能交付,沒有擁塞控制,面向報文(對於應用程序傳下來的報文不合並也不拆分,只是添加UDP首部)。
UDP首部佔用8個字節。當IP數據包首部爲20字節,所有UDP數據包爲下圖。
此時,UDP的數據長度至多爲(1500-20)-8 = 1480 - 8 = 1472字節。
抓包示意如下,顯示的Length是網絡包長度,1500+14, Len是UDP數據長度。
1.4 TCP包
TCP提供一種面向連接的、可靠的字節流服務。 所以TCP要比UDP可靠的多,UDP是把數據直接發出去,而不管對方是不是在收信。
TCP首部佔用20個字節。當IP數據包首部爲20字節,所有TCP數據包爲下圖。
此時,TCP的數據長度至多爲(1500-20)-20 = 1480 - 20 = 1460字節。
1.5 RTP/RTCP包
1.5.1 RTP包頭
- V:RTP協議的版本號,佔2位,當前協議版本號爲2
- P:填充標誌,佔1位,如果P=1,則在該報文的尾部填充一個或多個額外的八位組,它們不是有效載荷的一部分。
- X:擴展標誌,佔1位,如果X=1,則在RTP報頭後跟有一個擴展報頭
- CC:CSRC計數器,佔4位,指示CSRC 標識符的個數
- M: 標記,佔1位,不同的有效載荷有不同的含義,對於視頻,標記一幀的結束;對於音頻,標記會話的開始。
- PT: 有效荷載類型,佔7位,用於說明RTP報文中有效載荷的類型,如GSM音頻、JPEM圖像等,在流媒體中大部分是用來區分音頻流和視頻流的,這樣便於客戶端進行解析。
- 序列號:佔16位,用於標識發送者所發送的RTP報文的序列號,每發送一個報文,序列號增1。這個字段當下層的承載協議用UDP的時候,網絡狀況不好的時候可以用來檢查丟包。同時出現網絡抖動的情況可以用來對數據進行重新排序,序列號的初始值是隨機的,同時音頻包和視頻包的sequence是分別記數的。
- 時戳(Timestamp):佔32位,必須使用90 kHz 時鐘頻率。時戳反映了該RTP報文的第一個八位組的採樣時刻。接收者使用時戳來計算延遲和延遲抖動,並進行同步控制。
- 同步信源(SSRC)標識符:佔32位,用於標識同步信源。該標識符是隨機選擇的,參加同一視頻會議的兩個同步信源不能有相同的SSRC。
- 特約信源(CSRC)標識符:每個CSRC標識符佔32位,可以有0~15個。每個CSRC標識了包含在該RTP報文有效載荷中的所有特約信源。
注:基本的RTP說明並不定義任何頭擴展本身,如果遇到X=1,需要特殊處理,擴展數據在CSRC(若有)後、負載前,格式如下:
以上,rtp包的包頭當CC位爲0時,表示無CSRC,那麼RTP包頭長12個字節。因此 :
1)當rtp走tcp時,rtp載荷長度最多爲 1460 – 12 = 1448個字節。
2)當rtp走udp時,rtp載荷長度最多爲 1472 – 12 = 1460個字節。
1.5.2 RTP封包
載荷第一個字節(或NALU單元) 格式定義如下,包含禁止位F、重要級別NRI、類型Type。
常用的NALU Header對應的type數值:
由於i幀比較大,已經超出mtu最大1500,所以需要拆包分片傳輸,這裏說的拆包發送不是指發送超過1500的數據包時tcp的分段傳輸或者upd的ip分片傳輸,而是指rtp協議本身對264的拆包。(rtmp打包就比較簡單,由於是基於tcp的協議,大包直接交給tcp去做分段傳輸,rtmp通過設置合適的trunk size去發送一幀幀數據。)
如果小於MTU採用單個NAL單元包,如果大於MTU就採用FUs分片方式。常用的打包方式就是單個NAL包和FU-A方式,所以我們只解析這兩種。
1.5.2.1 單個NALU單元包
P幀或者B幀比較小的包,直接將NALU打包成RTP包進行傳輸。
RTP header(12bytes) + NALU header (1byte) + NALU payload
1.5.2.2 分片單元FU-A
相同NAL單元的分片必須使用遞增的RTP序號連續順序發送(第一和最後分片之間沒有其他的RTP包)。相似,NAL單元必須按照RTP順序號的順序裝配。
RTP header(12bytes) + FU Indicator(1byte) + FU header(1 byte) + FU payload
FU-A由1字節的分片單元指示(如下圖左),1字節的分片單元頭(如下圖右),
其中分片單元頭各字結位如下:
- S: 1 bit 當設置成1,開始位指示分片NAL單元的開始。
當跟隨的FU荷載不是分片NALU單元荷載的開始,開始位設爲0。 - E: 1 bit 當設置成1, 結束位指示分片NAL單元的結束,即荷載的最後字節也是分片NALU單元的最後一個字節。
當跟隨的FU荷載不是分片NAL單元的最後分片,結束位設置爲0。 - R: 1 bit 保留位必須設置爲0,接收者必須忽略該位
分片開始 SE = 10, 分片中間 SE = 00, 分片結束 SE = 01
這裏分片傳輸方式是無NALU頭,實際上是NALU頭被分散填充到FU indicator和FU header裏面了。bit位按照從左到右編號0-7來算,nalu頭中0-2前三個bit放在FU indicator的0-2前三個bit中,後3-7五個bit放入FU header的後3-7五個中。
反過來,NALU Header 由FU indicator前三位和FU header後五位組成,即:
NALU header = (FU indicator & 0xe0) | (FU header & 0x1F) 。
1.5.2.3 抓包演示
—>RTP包中接收的264包是不含有0x00,0x00,0x00,0x01頭的,這部分是rtp接收以後,另外再加上去的,解碼的時候再做判斷的。(whireshark不能解析rtp時,右邊解碼爲RTP)。
-
SPS、PPS
RTP數據部分第一個字節0x18,後五位11000, 24, STAP-A單一時間聚合包。
第一個包長度0x0002,數據0x0910, nalu header 0x09,不重要,分隔符
第二個包長度0x001b,nalu header 0x27,F=0, NRI=2, TYPE= SPS
第二個包長度0x0004,nalu header 0x28,F=0, NRI=2, TYPE= PPS -
I幀分片開始
RTP數據部分第一個字節0x3C,後五位11100, 28, FU-A分片
第二個字節分片頭0x85, S=1, E=0, 分片開始, 後五位是00101,5, I幀
-
I幀分片
分片指示單元 0x3c 後五位11100, 28, FU-A分片
分片單元頭 0x05 S=0, E=0, 分片,後五位是00101,5, I幀
同一個I幀的分片,時間戳相同。
-
I幀結束
這裏比較特殊,沒有使用分片單元描述結束。
使用的是NALU單元標記結束一個視頻幀的結束。