H264功能分為兩層:視頻編碼層(VCL,VideoCodeing Layer)和網絡提取層(NAL,Network Abstraction Layer)。VCL數據即編碼處理的輸出,它表示被壓縮編碼后的視頻數據序列。在VCL數據傳輸或存儲之前,這些編碼的VCL數據,先被映射或封裝在NAL單元中。每個NAL單元包括一個原始字節序列負荷(RBSP,Raw Byte Sequence Payload)、一組對應于視頻編碼的NAL頭信息。RBSP的基本結構是:在原始編碼數據的后面填充bit,1個bit“1”和若干bit“0”,以便字節對齊。NAL單元序列如圖1所示。
圖1 NAL單元序列
其中NAL頭占用一個字節,如圖2所示:包含1個bit的forbidden_zero_bit(F)、2個bit的nal_ref_idc(NRI)和5個比特的nal_unit_type(TYPE)。
圖2 NAL頭
F:H264規范聲明設置為1指示語法為例。
NRI:00表示不是參考圖像,可以丟棄;大于0的如果丟去則可能造成圖像不完整。
TYPE:指示NAL類型,其取值如圖3所示。
TYPE | NAL類型 |
0 | 未使用 |
1 | 不分區、非IDR圖像的片 |
2 | 片分區A |
3 | 片分區B |
4 | 片分區C |
5 | IDR圖像中的片 |
6 | 補充增強信息單元(SEI) |
7 | 序列參數集(SPS) |
8 | 圖像參數集(PPS) |
9 | 分界符 |
10 | 序列結束 |
11 | 碼流結束 |
12 | 填充 |
13-23 | 保留 |
24-31 | 未使用 |
圖3 NAL單元類型
H264以字節流格式和RTP格式的碼流結構如圖4所示。
起始碼:如果NAL單元對應的片(Slice)為一幀的開始,則用4個字節的0x00000001表示,否則用3個字節表示,0x000001。
脫殼操作:為了使NAL主體不包含起始碼,在編碼時沒遇到連續兩個字節的0x00,就插入一個字節的0x03,以和起始碼相區別。解碼時,則將相應0x03移除。
圖4 H264碼流結構
RTP封裝h264結構的數據包包括[RTP頭]+[h264載荷],其中h264載荷的第一個字節(載荷頭)表示了使用什么結構來對H264數據進行封裝。并且載荷頭的結構和NAL頭是一致的,根據載荷頭的后5個字節可以知道具體使用的封裝結構,如圖5所示。其中,可能的結構包括:
單個NAL單元包:載荷中值包含一個NAL單元。載荷頭類型域等于原始NAL單元類型,即范圍在1-23之間。
聚合包:本類型用于聚合多個NAL單元到單個RTP載荷中。分為4類:單時間聚合包類型A(STAP-A),單時間聚合包類型B(STAP-B),多時間聚合包類型16位位移(MTAP16)、多時間聚合包類型24位位移(MTAP24),其載荷頭類型大小分別是:24,25,26,27。
分片單元:用于分片單個NAL單元到多個RTP包。分為兩類:FU-A、FU-B,其載荷頭類型大小分別是:28,29。
TYPE | Packet |
0 | 未使用 |
1-23 | 單個NAL單元包 |
24 | STAP-A |
25 | STAP-B |
26 | MTAP16 |
27 | MTAP24 |
28 | FU-A |
29 | FU-B |
30-31 | 未使用 |
圖5 RTP載荷頭類型
RTP載荷頭和NAL頭重合,也就是說RTP載荷僅僅包含一個NAL單元,如圖6所示
舉例:一個NAl單元的數據為00 0000 01 67 23 34 ……,則使用單個NAL單元包的封裝結構為[RTP頭] 67 23 34 ……。
圖6 單個NAL單元包
聚合相同NAL時間的NAL單元,且不包含DON(解碼順序號)。如圖7所示,RTP載荷部分包括一個字節的RTP載荷頭和若干NAL單元,其中每個NAL單元前包含2個字節的NAL長度。
舉例:有兩個NAL單元分別為00 00 00 01 67 12 34 56,00 00 00 01 68 23 5678 9A,則使用STAP-A的封裝結構為[RTP頭] 78 00 04 67 12 34 56 00 05 68 23 56 78 9A
圖7 STAP-A
聚合相同NAL時間的NAL單元,且包含DON(解碼順序號)。圖8所示,RTP載荷部分包括一個字節的RTP載荷頭、2個字節DON和若干NAL單元,其中每個NAL單元前包含2個字節的NAL長度。
舉例:有兩個NAL單元分別為00 00 00 01 67 12 34 56,00 00 00 01 68 23 5678 9A,則使用STAP-B的封裝結構為[RTP頭] 78 [DON]00 04 67 12 34 56 00 05 68 23 56 78 9A,其中DON的獲得暫不解釋。
如8 STAP-B
聚合具有差異NAL時間的NAL單元。如圖9所示,MTAP16的RTP載荷包括一個字節的載荷頭、2個字節的DONB(解碼順序號基址)和若干NAL單元,其中每個NAL單元前包含2字節的NAL單元長度、1個字節的DOND(解碼順序號差值)、2個字節的時間戳位移(TSoffset)。
如圖10所示,MTAP24的RTP載荷包括一個字節的載荷頭、2個字節的DONB(解碼順序號基址)和若干NAL單元,其中每個NAL單元前包含2字節的NAL單元長度、1個字節的DOND(解碼順序號差值)、3個字節的時間戳位移(TS offset)。
圖9 MTAP-16
圖10 MTAP-24
將一個NAL單元分片到多個RTP包中。如圖11所示,FU-A的RTP載荷包括1個字節的FUindicator、一個字節的FU header和NAL單元的一部分。如圖12所示,FU-B的RTP載荷包括1個字節的FU indicator、1個字節的FU header、2個字節的DON和NAL單元的一部分。
圖11 FU-A
圖12 FU-B
其中FU-indicator的結構如圖13所示:
F:和NAL頭的F一致。
NRI:和NAL的NRI一致。
TYPE:28或者29。
圖13FU-indicator
其中FU-header的結構如圖14所示:
S:為1表示是分片的開始,否則為0。
E:為1表示分片的結束,否則為0。
TYPE:和NAL頭的TYPE一致。
圖14 FU-header
有多個FU-A/B分片獲得完整的NAL單元:根據FU-header的S和E獲得每一個分片,然后由FU-indicator的前3位和FU-header的后5位合成NAL頭,再加上每一個分片的NAL單元的一部分就組成了完整的NAL單元。
注意:NAL頭只需合成一次即可。
參考:
RTC3984
H264碼流結構解析
新聞熱點
疑難解答