本文記錄一下H.264的編碼格式。H.264官方文檔:https://github.com/jiayayao/DataSheet/tree/master/encode-decode/h264。
一、H.264編碼格式
H.264從層次來看分為兩層:視頻編碼層(VCL, Video Coding Layer)和網絡提取層(NAL,Network Abstraction Layer)。VCL輸出的是原始數據比特流(SODB,String of data bits),表示H.264的語法元素編碼完成后的實際的原始二進制碼流。SODB通常不能保證字節對齊,故需要補齊為原始字節序列負荷(RBSP,Raw Byte Sequence Payload)。H.264引入了片(slice)的概念,每個片只攜帶該片自身獨有的句法元素,一個片的丟失不會影響其他片的解碼,還可以通過該片前后的片來恢復該片的解碼。
NAL層實際上就是最終輸出的H.264碼流,它是由一個個NALU組成的,每個NALU包括一組對應於視頻編碼數據的NAL頭信息和一個原始字節序列負荷(RBSP,Raw Byte Sequence Payload)。以上名詞之間的關系如下:
RBSP = SODB + RBSP trailing bits NALU = NAL header(1 byte) + RBSP H.264 = Start Code Prefix(3 bytes) + NALU + Start Code Prefix(3 bytes) + NALU +…
所以H.264碼流的結構如下:
每個NALU之間由起始碼(Start Code Prefix)分隔,起始碼分為兩種:0x000001(3 bytes) or 0x00000001(4 bytes). 如果NALU 對應的Slice 為一幀的開始,則用4 字節表示,即0x00000001;否則用3 字節表示,0x000001.NALU針對起始碼設計了防止沖突機制,如果出現連續的0x000000,0x000001,0x000002,0x000003時,會在兩個0之間插入03,如下:
0x00 00 00 -> 0x00 00 03 00 0x00 00 01 -> 0x00 00 03 01 0x00 00 02 -> 0x00 00 03 02 0x00 00 03 -> 0x00 00 03 03
一個NALU就是編碼后的一幀數據。NAL header是一個字節:
forbidden_zero_bit(1 bit) 禁止位,等於0;
nal_ref_idc(2 bit)指示當前NAL的優先級,取值范圍為0~3,值越高,表示當前NAL越重要。H.264規定,如果當前NAL是序列參數集,或是圖像參數等,該值必須大於0.比如nal_unit_type等於5時,nal_ref_idc大於0;nal_unit_type等於6,9,10,11或12時,nal_ref_idc等於0;
nal_unit_type表示當前NALU的類型,表格如下:
nal_unit_type |
NAL類型 |
C |
0 |
未使用 |
|
1 |
不分區、非IDR圖像的片 |
2,3,4 |
2 |
片分區A |
2 |
3 |
片分區B |
3 |
4 |
片分區C |
4 |
5 |
IDR圖像中的片 |
2,3 |
6 |
補充增強信息單元(SEI) |
5 |
7 |
序列參數集(SPS) |
0 |
8 |
圖像參數集(PPS) |
1 |
9 |
分界符 |
6 |
10 |
序列結束 |
7 |
11 | 碼流結束 | 8 |
12 | 填充 | 9 |
13~23 | 保留 | |
24~31 | 未使用 |
nal_unit_type=5時,表示當前NAL是IDR圖像的一個片,此時,IDR圖像中的每個片的nal_unit_type都應該等於5.
一般H.264原始碼流是以SPS->PPS->SEI->IDR->SCLICE->SCLICE…開頭的。
GOP即Group of picture(圖像組),指兩個I幀之間的距離。即幾秒有一個關鍵幀。一般在2、3秒之間。
二、H.264 Tips
H.264有兩種封裝格式:
(1)annexb模式:傳統模式,使用start code來分隔NAL, SPS和PPS是在ES流的頭部;
Annex-B: startCode Nalu1, startCode Nalu2......startCode NaluN
(2)mp4模式:沒有start code,使用NALU長度(固定字節,通常為4個字節)來分隔NAL。AVCodecContext的extradata內部保存着分隔的字節數,SPS和PPS;
mp4(AVCC): NaluLength Nalu1, LaluLength Lalu2, ......NaluLength NaluN
SPS的頭部是0x67,PPS的頭部是0x68,要保持對數據的敏感性。
IDR幀:IDR幀都是I幀,H.264引入IDR圖像是為了解碼的重同步,當解碼器解碼到IDR幀時,會立即將參考幀隊列清空,將已解碼的數據全部輸出或拋棄,重新查找參數及,開始一個新的序列。IDR幀之后的圖像永遠不會引用IDR幀之前圖像的數據來解碼。IDR幀一定是I幀,I幀不一定是IDR幀。
當解碼器性能不足需要丟幀時,nal_ref_idc可以作為判斷能否丟幀的依據。如果nal_ref_idc為0,則可以丟棄。當該幀nal_unit_type等於6,9,10,11或12時,nal_ref_idc為0。部分非IDR幀的nal_ref_idc也為0,也可以丟棄。丟棄的同時也能保證不會花屏。