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字節。
- V:版本號,2 位。根據RFC3984,目前使用的RTP 版本號應設爲0x10。
- P:填充位,1 位。當前不使用特殊的加密算法,因此該位設爲 0。
- X:擴展位,1 位。當前固定頭後面不跟隨頭擴展,因此該位也爲 0。
- CC:CSRC 計數,4 位。表示跟在 RTP 固定包頭後面CSRC 的數目,對於本文所要實現的基本的流媒體服務器來說,沒有用到混合器,該位也設爲 0x0。
- M:標示位,1 位。如果當前 NALU爲一個接入單元最後的那個NALU,那麼將M位置 1;或者當前RTP 數據包爲一個NALU 的最後的那個分片時(NALU 的分片在後面講述),M位置 1。其餘情況下M 位保持爲 0。
- PT:載荷類型,7 位。對於H.264 視頻格式,當前並沒有規定一個默認的PT 值。因此選用大於 95 的值可以。此處設爲0x60(十進制96)。
RFC2250 建議96 表示PS 封裝,建議97 爲MPEG-4,建議98 爲H264
即我們接收到的RTP 包首先需要判斷負載類型,若負載類型爲96,則採用PS 解複用,將音視頻分開解碼。若負載類型爲98,直接按照H264 的解碼類型解碼。
各種說法不一,具體就看解包的時候,是自己寫解包代碼,還是用別人的代碼了,然後具體分析格式和type. - SQ:序號,16 位。序號的起始值爲隨機值,此處設爲 0,每發送一個RTP 數據包,序號值加 1。
- TS:時間戳,32 位。同序號一樣,時間戳的起始值也爲隨機值,此處設爲0。根據RFC3984, 與時間戳相應的時鐘頻率必須爲90000HZ。
- 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