RTP封裝H264——學習筆記

H264結構詳解

從這個鏈接拿過來的圖。 

邏輯關係:

                                                  SODB  + RBSP trailing bits    =  RBSP

                                                       NAL header(1 byte)      +      RBSP   =  NALU

Start Code Prefix(3 bytes)  +   NALU  +  Start Code Prefix(3 bytes)  + NALU + ...+  = H.264BitsStream(非嚴格意義上的公式)

SODB:編碼形成的真實碼流,爲了使一個RBSP爲整字節數,需要加trailing bits。用1個1,若干0補齊。

RBSP:原始字節序列載荷,就是在SODB的後面添加了結尾比特

Start Code Prefix爲3個字節. 但是,爲了尋址方便,要求數據流在長度上對齊,因此H.264建議在Start Code Prefix前面加若干個0.    

EBSP相較於RBSP,多了防止競爭的一個字節:0x03。

我們知道,NALU的起始碼爲0x000001或0x00000001,同時H264規定,當檢測到0x000000時,也可以表示當前NALU的結束。那這樣就會產生一個問題,就是如果在NALU的內部,出現了0x000001或0x000000時該怎麼辦?

所以H264就提出了“防止競爭”這樣一種機制,當編碼器編碼完一個NAL時,應該檢測NALU內部,是否出現如下圖左側的四個序列。當檢測到它們存在時,編碼器就在最後一個字節前,插入一個新的字節:0x03。

所以,嚴格講:NALU = NAL Header(1 byte)  + EBSP

 

H264句法元素解析流程:

 

 

H264結構

 

H264流中,其中一個 NAL 單元的結構

NAL HEADER 上面的1-8數字,表示佔8位。後面RBSP沒有寫,是因爲佔位不定,類型也不確定。

F:       1個比特. forbidden_zero_bit. 在 H.264 規範中規定了這一位必須爲 0.
NRI:   2個比特. nal_ref_idc. 取00~11, 似乎指示這個NALU的重要性,如00的NALU解碼器可以丟棄它而不影響圖像的回放.
Type: 5個比特. nal_unit_type. 這個NALU單元的類型.簡述如下:

    

 

RTP協議頭

 12字節的RTP頭(不含CSRC的時候,是12個字節)後面的就是音視頻數據。

標號表示位。8位 = 1個字節,一共96位,就是12字節。

  1. V:版本號,2 位。根據RFC3984,目前使用的RTP 版本號應設爲0x10。 
  2. P:填充位,1 位。當前不使用特殊的加密算法,因此該位設爲 0。 
  3. X:擴展位,1 位。當前固定頭後面不跟隨頭擴展,因此該位也爲 0。 
  4. CC:CSRC 計數,4 位。表示跟在 RTP 固定包頭後面CSRC 的數目,對於本文所要實現的基本的流媒體服務器來說,沒有用到混合器,該位也設爲 0x0。 
  5. M:標示位,1 位。如果當前 NALU爲一個接入單元最後的那個NALU,那麼將M位置 1;或者當前RTP 數據包爲一個NALU 的最後的那個分片時(NALU 的分片在後面講述),M位置 1。其餘情況下M 位保持爲 0。 
  6. PT:載荷類型,7 位。對於H.264 視頻格式,當前並沒有規定一個默認的PT 值。因此選用大於 95 的值可以。此處設爲0x60(十進制96)。

    RFC2250 建議96 表示PS 封裝,建議97 爲MPEG-4,建議98 爲H264

    即我們接收到的RTP 包首先需要判斷負載類型,若負載類型爲96,則採用PS 解複用,將音視頻分開解碼。若負載類型爲98,直接按照H264 的解碼類型解碼。

     各種說法不一,具體就看解包的時候,是自己寫解包代碼,還是用別人的代碼了,然後具體分析格式和type.
  7. SQ:序號,16 位。序號的起始值爲隨機值,此處設爲 0,每發送一個RTP 數據包,序號值加 1。 
  8. TS:時間戳,32 位。同序號一樣,時間戳的起始值也爲隨機值,此處設爲0。根據RFC3984, 與時間戳相應的時鐘頻率必須爲90000HZ。 
  9. SSRC:同步源標示,32 位。SSRC應該被隨機生成,以使在同一個RTP會話期中沒有任何兩個同步源具有相同的SSRC 識別符。此處僅有一個同步源,因此將其設爲0x12345678。

 

取一段RTP傳輸的碼流如下:

80 e0 00 1e 00 00 d2 f0 00 00 00 00 41 9b 6b 49 €?....??....A?kI
e1 0f 26 53 02 1a ff06 59 97 1d d2 2e 8c 50 01 ?.&S....Y?.?.?P.
cc 13 ec 52 77 4e e50e 7b fd 16 11 66 27 7c b4 ?.?RwN?.{?..f'|?
f6 e1 29 d5 d6 a4 ef3e 12 d8 fd 6c 97 51 e7 e9 ??)????>.??l?Q??
cfc7 5e c8 a9 51 f6 82 65 d6 48 5a 86 b0 e0 8c ??^??Q??e?HZ????

其中(80,是十六進制,代表1個字節,換成二進制,是8位。1111 1111 換成16進制,爲 FF),
80               是V_P_X_CC
e0               是M_PT
00 1e          是SequenceNum
00 00 d2 f0 是Timestamp
00 00 00 00是SSRC

把前兩字節換成二進制如下
1000 0000 1110 0000

按順序解釋如下:
10               是V;
0                 是P;
0                 是X;
0000           是CC;
1                 是M;
110 0000    是PT;

參考鏈接 

對於每一個NALU,根據其包含的數據量的不同,其大小也有差異。在IP網絡中,當要傳輸的IP 報文大小超過最大傳輸單元MTU(Maximum Transmission Unit )時就會產生IP分片情況。在以太網環境中可傳輸的最大 IP 報文(MTU)的大小爲 1500 字節。如果發送的IP數據包大於MTU,數據包就會被拆開來傳送,這樣就會產生很多數據包碎片,增加丟包率,降低網絡速度。對於視頻傳輸而言,若RTP 包大於MTU 而由底層協議任意拆包,可能會導致接收端播放器的延時播放甚至無法正常播放。因此對於大於MTU 的NALU 單元,必須進行拆包處理。

RFC3984 給出了3 中不同的RTP 打包方案:

(1)Single NALU Packet:在一個RTP 包中只封裝一個NALU,在本文中對於小於 1400字節的NALU 便採用這種打包方案。 
(2)Aggregation Packet:在一個RTP 包中封裝多個NALU,對於較小的NALU 可以採用這種打包方案,從而提高傳輸效率。 
(3)Fragmentation Unit:一個NALU 封裝在多個RTP包中,在本文中,對於大於1400字節的NALU 便採用這種方案進行拆包處理。

注意:前12個字節出現在每個RTP包中,僅僅在被混合器插入時,纔出現CSRC識別符列表.

 

單一包傳輸模式

單一模式,是[RTP header] + { [nal header] + [rbsp] }   。其中花括號內,就是{ rtp payload}

NAL單元的第一字節。根據NAL Header 的後5位 TYPE,就知道載荷的類型。下面的圖僅是nalu的部分。

 

例:
  如有一個 H.264 的 NALU 是這樣的:

  [00 00 00 01 67 42 A0 1E 23 56 0E 2F ... ]

  這是一個序列參數集 NAL 單元. [00 00 00 01] 是四個字節的開始碼, 67 是 NALU 頭, 42 開始的數據是 NALU 內容.

  封裝成 RTP 包將如下:

  [ RTP Header ] [ 67 42 A0 1E 23 56 0E 2F ...]

  即只要去掉 4 個字節的開始碼就可以了.

nal->len 就是指去掉 RTP Header剩下部分的長度字節數。如上例子中,去掉 ... ,則nal->len = 8個字節(67 42 A0 1E 23 56 0E 2F)

 

 

組合傳輸模式

如有一個 H.264 的 NALU 是這樣的:

  [00 00 00 01 67 42 A0 1E 23 56 0E 2F ... ]

  [00 00 00 01 68 42 B0 12 58 6A D4 FF ... ]

  封裝成 RTP 包將如下:

  [ RTP Header ] [STAP-A頭 (佔用1個字節,用78表示)] [第一個NALU長度 (佔用兩個字節)] [ 67 42 A0 1E 23 56 0E 2F ...] [第二個NALU長度 (佔用兩個字節)] [68 42 B0 12 58 6A D4 FF ... ]

 

分片傳輸模式 (FU-A)

 

  

RTP HEADER + FU indicator + FU header + FU payload. 就是分片傳輸。 其中 FU payload 就是H264中一個nal unit去掉頭的 EBSP部分。
FU indicator 的 TYPE 就是 FU-A

FU header 的 TYPE 就是 nal header 的 type。取1-23的那個值

取一段碼流分析如下:

80 60 01 0f 00 0e 10 00 00 00 00 00 7c 85 88 82 €`..........|???
00 0a 7f ca 94 05 3b7f 3e 7f fe 14 2b 27 26 f8 ...??.;.>.?.+'&?
89 88 dd 85 62 e1 6dfc 33 01 38 1a 10 35 f2 14 ????b?m?3.8..5?.
84 6e 21 24 8f 72 62f0 51 7e 10 5f 0d 42 71 12 ?n!$?rb?Q~._.Bq.
17 65 62 a1 f1 44 dc df 4b 4a 38 aa 96 b7 dd 24 .eb??D??KJ8????$
 
前12字節(80 60 01 0f 00 0e 10 00 00 00 00 00)是RTP Header
7c是FU indicator
85是FU Header
FU indicator(0x7C)和FU Header(0x85)換成二進制如下:
0111 1100 1000 0101

按順序解析如下:
0                        是F
11                       是NRI
11100                    是FU Type,這裏是28,即FU-A
1                        是S,Start,說明是分片的第一包
0                        是E,End,如果是分片的最後一包,設置爲1,這裏不是
0                        是R,Remain,保留位,總是0
00101                    是NAl Type,這裏是5,說明是關鍵幀(不知道爲什麼是關鍵幀請自行谷歌)
 
打包時:
FUindicator的F、NRI是NAL Header中的F、NRI,Type是28;
FU Header的S、E、R分別按照分片起始位置設置,Type是 NAL Header中的Type。

解包時:
取FU indicator的前三位和FU Header的後五位,即0110 0101(0x65)爲NAL類型。

 

自己整理的學習筆記,可能有點混亂。如果讀者想深一步學習,可以參考下面的鏈接。


參考鏈接:

https://www.jianshu.com/p/a19f3e63b433

https://www.cnblogs.com/lidabo/p/4482480.html

https://www.jianshu.com/p/5f89ea2c3a28

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章