H264碼流結(jié)構(gòu)
無論是解析視頻文件或這通過網(wǎng)絡(luò)傳輸, 其實都是一串字節(jié)序列. H264碼流就是按照一定的規(guī)則組織排列的字節(jié)串.
直觀理解的角度
按照從大到小分為: 視頻序列, 圖像幀, 片,宏塊,子塊
碼流功能的角度
從碼流功能的角度可以分為兩層:NAL層和VCL層
- NAL網(wǎng)絡(luò)提取層:負責以網(wǎng)絡(luò)所要求的恰當?shù)姆绞綄?shù)據(jù)進行打包和傳送
- VCL視頻編碼層:包括核心壓縮引擎和塊,宏塊和片的語法級別定義,設(shè)計目標是盡可能地獨立于網(wǎng)絡(luò)進行高效的編碼
碼流解析的角度
可以理解為有一個一個的NALU單元組成.
一個NALU單元分成兩部分: NAL頭和RBSP(Raw ByteSequence Payload)原始字節(jié)序列載荷.
前面提到的一幀圖像(I幀, P幀, B幀)就是一個NALU單元, NALU單元除了代表圖像外還能包含其他類型的數(shù)據(jù),如PPS和SPS, 詳細的內(nèi)容在下節(jié)列出來.
前面提到的VCL層, 或者說VCL數(shù)據(jù),是指視頻編碼生成的壓縮比特流片段,被稱為SODB(String of Data Bits),
SODB是RBSP的原始幀, 即RBSP包含了SODB數(shù)據(jù)
NALU
一個NALU由 固定長度的Header和RBSP組成
HEADER
NAL Header的結(jié)構(gòu)如下:
- forbidden_zero_bit
在網(wǎng)絡(luò)傳輸中發(fā)生錯誤時,會被置為1,告訴接收方丟掉該單元;否則為0 - nal_ref_idc
用于表示當前NALU的重要性,值越大,越重要.
解碼器在解碼處理不過來的時候,可以丟掉重要性為0的NALU. -
nal_unit_type
表示NALU數(shù)據(jù)的類型,有以下幾種:
72.png
其中比較注意的應(yīng)該是以下幾個:
- 1-4:I/P/B幀,如果nal_ref_idc 為0 則表示I幀,不為0則為P/B幀
- 5:IDR幀,I幀的一種,告訴解碼器,之前依賴的解碼參數(shù)集合(接下來要出現(xiàn)的SPS\PPS等)可以被刷新了。
- 6:SEI,英文全稱Supplemental Enhancement Information,翻譯為“補充增強信息”,提供了向視頻碼流中加入額外信息的方法。
- 7:SPS,全稱Sequence Paramater Set,翻譯為“序列參數(shù)集”。SPS中保存了一組編碼視頻序列(Coded Video Sequence)的全局參數(shù)。因此該類型保存的是和編碼序列相關(guān)的參數(shù)。
- 8: PPS,全稱Picture Paramater Set,翻譯為“圖像參數(shù)集”。該類型保存了整體圖像相關(guān)的參數(shù)。
- 9:AU分隔符,AU全稱Access Unit,它是一個或者多個NALU的集合,代表了一個完整的幀。
其中SPS,PPS 需要在I幀前出現(xiàn),不然解碼器沒法解碼.而SPS,PPS出現(xiàn)的頻率也跟不同應(yīng)用場景有關(guān),對于一個本地h264流,可能只要在第一個I幀前面出現(xiàn)一次就可以,但對于直播流,每個I幀前面都應(yīng)該插入sps或pps,因為直播時客戶端進入的時間是不確定的.
RBSP
RBSP的結(jié)構(gòu)如下:
這部分還沒研究,似乎目前解析時ffmpeg都給實現(xiàn)了.
每個NALU前有一個起始碼 0x00 00 01(或者0x00 00 00 01), 作為NALU的分割符
下面是一個H264碼流:
分析其中比較有代表性的3幀:
- 00 00 00 01 67
00 00 00 01是一個NALU 開始,67 是Header, 二進制為0110 0111, nal_unit_type 為00111, 即7為SPS幀 - 00 00 00 01 68
68 二進制為0110 1000, nal_unit_type 為00111, 即8為SPS幀 - 00 00 00 01 65
65 二進制為0110 0101,nal_unit_type 為00101, 即5為IDR幀
H264 更詳細的分層結(jié)構(gòu)
關(guān)于多slice這塊目前還沒弄清楚,后面弄清楚了再修改.