導讀
H.264碼流結構解析
H.264編碼格式
H.264的功能分為兩層:視訊編碼層(VCL, Video Coding Layer)和網絡提取層(NAL, Network Abstraction Layer)
VCL資料即編碼處理的輸出,它表示被壓縮編碼後的視訊資料序列。在VCL資料傳輸或存儲之前,這些編碼的VCL資料,先被映射或封裝進NAL單元中。每個NAL單元包括一個原始位元組序列負荷(RBSP, Raw Byte Sequence Payload)、一組對應于視訊編碼的NAL頭資訊。RBSP的基本結構是:在原始編碼資料的後面填加了結尾比特。一個bit“1”若幹比特“0”,以便位元組對齊。

image.png
H.264傳輸
H.264的編碼視訊序列包括一系列的NAL單元,每個NAL單元包含一個RBSP,見表1。編碼片(包括資料分割片IDR片)和序列RBSP結束符被定義為VCL NAL單元,其餘為NAL單元。典型的RBSP單元序列如圖2所示。每個單元都按獨立的NAL單元傳送。單元的資訊頭(一個位元組)定義了RBSP單元的類型,NAL單元的其餘部分為RBSP資料。
image.png
image.png
- H.264碼流結構圖
image.png
起始碼:如果NALU對應的Slice為一幀的開始,則用4位元組表示,即0x00000001;否則用3位元組表示,0x000001。
NAL Header:forbidden_bit,nal_reference_bit(優先級),nal_unit_type(類型)。
脫殼操作:為了使NALU主體不包括起始碼,在編碼時每遇到兩個位元組(連續)的0,就插入一位元組0x03,以和起始碼相差別。解碼時,則将相應的0x03删除掉。
image.png
-
H.264解碼
NAL頭資訊的nal_referrence_idc(NRI)用于在重建過程中标記一個NAL單元的重要性,值為0表示這個NAL單元沒有用預測,是以可以被解碼器抛棄而不會有錯誤擴散;值高于0表示NAL單元要用于無漂移重構,且值越高,對此NAL單元丢失的影響越大。
NAL頭資訊的隐藏比特位,在H.264編碼器中預設為0,當網絡識别到單元中存在比特錯誤時,可将其置為1。隐藏比特位主要用于适應不同種類的網絡環境(比如有線無線相結合的環境)。
image.png
NAL單元解碼的流程為:首先從NAL單元中提取出RBSP文法結構,然後按照如圖4所示的流程處理RBSP文法結構。輸入的是NAL單元,輸出結果是經過解碼的目前圖像的樣值點。
NAL單元中分别包含了序列參數集和圖像參數集。圖像參數集和序列參數集在其他NAL單元傳輸過程中作為參考使用,在這些資料NAL單元的片頭中,通過文法元素pic_parameter_set_id設定它們所使用的圖像參數集編号;而相應的每個圖像參數集中,通過文法元素seq_paramter_set_id設定他們使用的序列參數集編号。
示例分析
結合laifeng_Android中的AnnexbHelper來簡單的看看,如果進行解析。
Android寫死得到的H264是
Annexb
這種格式的。
-
如何擷取NAUL單元
其實就是找開頭為 0x0001或者0x000001的位元組段
/**
* 從硬編出來的byteBuffer中查找nal
* @param as
* @param bb
* @param bi
*/
private void avcStartWithAnnexb(AnnexbSearch as, ByteBuffer bb, MediaCodec.BufferInfo bi) {
as.match = false;
as.startCode = 0;
int pos = bb.position();
while (pos < bi.offset + bi.size - 3) {
// not match.
if (bb.get(pos) != 0x00 || bb.get(pos + 1) != 0x00) {
break;
}
// match N[00] 00 00 01, where N>=0
if (bb.get(pos + 2) == 0x01) {
as.match = true;
as.startCode = pos + 3 - bb.position();
break;
}
pos++;
}
}
複制
- 根據
NAL Header
内的nal_unit_type判斷目前幀的類型
根據上面的類型分析,我們知道NAUL裡面前8位中的後5位,表示這個NAUL的類型。
根據這些可以判斷類型。
而剩下的就是NAUL内的資料了。
private boolean isSps(byte[] frame) {
if (frame.length < 1) {
return false;
}
// 5bits, 7.3.1 NAL unit syntax,
// H.264-AVC-ISO_IEC_14496-10.pdf, page 44.
// 7: SPS, 8: PPS, 5: I Frame, 1: P Frame
int nal_unit_type = (frame[0] & 0x1f);
return nal_unit_type == SPS;
}
private boolean isPps(byte[] frame) {
if (frame.length < 1) {
return false;
}
// 5bits, 7.3.1 NAL unit syntax,
// H.264-AVC-ISO_IEC_14496-10.pdf, page 44.
// 7: SPS, 8: PPS, 5: I Frame, 1: P Frame
int nal_unit_type = (frame[0] & 0x1f);
return nal_unit_type == PPS;
}
private boolean isKeyFrame(byte[] frame) {
if (frame.length < 1) {
return false;
}
// 5bits, 7.3.1 NAL unit syntax,
// H.264-AVC-ISO_IEC_14496-10.pdf, page 44.
// 7: SPS, 8: PPS, 5: I Frame, 1: P Frame
int nal_unit_type = (frame[0] & 0x1f);
return nal_unit_type == IDR;
}
private static boolean isAccessUnitDelimiter(byte[] frame) {
if (frame.length < 1) {
return false;
}
// 5bits, 7.3.1 NAL unit syntax,
// H.264-AVC-ISO_IEC_14496-10.pdf, page 44.
// 7: SPS, 8: PPS, 5: I Frame, 1: P Frame
int nal_unit_type = (frame[0] & 0x1f);
return nal_unit_type == AccessUnitDelimiter;
}
複制