導(dǎo)讀
H.264編碼格式
H.264的功能分為兩層:視頻編碼層(VCL, Video Coding Layer)和網(wǎng)絡(luò)提取層(NAL, Network Abstraction Layer)
VCL數(shù)據(jù)即編碼處理的輸出,它表示被壓縮編碼后的視頻數(shù)據(jù)序列。在VCL數(shù)據(jù)傳輸或存儲(chǔ)之前,這些編碼的VCL數(shù)據(jù),先被映射或封裝進(jìn)NAL單元中。每個(gè)NAL單元包括一個(gè)原始字節(jié)序列負(fù)荷(RBSP, Raw Byte Sequence Payload)、一組對(duì)應(yīng)于視頻編碼的NAL頭信息。RBSP的基本結(jié)構(gòu)是:在原始編碼數(shù)據(jù)的后面填加了結(jié)尾比特。一個(gè)bit“1”若干比特“0”,以便字節(jié)對(duì)齊。

H.264傳輸
H.264的編碼視頻序列包括一系列的NAL單元,每個(gè)NAL單元包含一個(gè)RBSP,見(jiàn)表1。編碼片(包括數(shù)據(jù)分割片IDR片)和序列RBSP結(jié)束符被定義為VCL NAL單元,其余為NAL單元。典型的RBSP單元序列如圖2所示。每個(gè)單元都按獨(dú)立的NAL單元傳送。單元的信息頭(一個(gè)字節(jié))定義了RBSP單元的類(lèi)型,NAL單元的其余部分為RBSP數(shù)據(jù)。


-
H.264碼流結(jié)構(gòu)圖
image.png
起始碼:如果NALU對(duì)應(yīng)的Slice為一幀的開(kāi)始,則用4字節(jié)表示,即0x00000001;否則用3字節(jié)表示,0x000001。
NAL Header:forbidden_bit,nal_reference_bit(優(yōu)先級(jí)),nal_unit_type(類(lèi)型)。
脫殼操作:為了使NALU主體不包括起始碼,在編碼時(shí)每遇到兩個(gè)字節(jié)(連續(xù))的0,就插入一字節(jié)0x03,以和起始碼相區(qū)別。解碼時(shí),則將相應(yīng)的0x03刪除掉。

-
H.264解碼
NAL頭信息的nal_referrence_idc(NRI)用于在重建過(guò)程中標(biāo)記一個(gè)NAL單元的重要性,值為0表示這個(gè)NAL單元沒(méi)有用預(yù)測(cè),因此可以被解碼器拋棄而不會(huì)有錯(cuò)誤擴(kuò)散;值高于0表示NAL單元要用于無(wú)漂移重構(gòu),且值越高,對(duì)此NAL單元丟失的影響越大。
NAL頭信息的隱藏比特位,在H.264編碼器中默認(rèn)為0,當(dāng)網(wǎng)絡(luò)識(shí)別到單元中存在比特錯(cuò)誤時(shí),可將其置為1。隱藏比特位主要用于適應(yīng)不同種類(lèi)的網(wǎng)絡(luò)環(huán)境(比如有線(xiàn)無(wú)線(xiàn)相結(jié)合的環(huán)境)。
image.png
NAL單元解碼的流程為:首先從NAL單元中提取出RBSP語(yǔ)法結(jié)構(gòu),然后按照如圖4所示的流程處理RBSP語(yǔ)法結(jié)構(gòu)。輸入的是NAL單元,輸出結(jié)果是經(jīng)過(guò)解碼的當(dāng)前圖像的樣值點(diǎn)。
NAL單元中分別包含了序列參數(shù)集和圖像參數(shù)集。圖像參數(shù)集和序列參數(shù)集在其他NAL單元傳輸過(guò)程中作為參考使用,在這些數(shù)據(jù)NAL單元的片頭中,通過(guò)語(yǔ)法元素pic_parameter_set_id設(shè)置它們所使用的圖像參數(shù)集編號(hào);而相應(yīng)的每個(gè)圖像參數(shù)集中,通過(guò)語(yǔ)法元素seq_paramter_set_id設(shè)置他們使用的序列參數(shù)集編號(hào)。
示例分析
結(jié)合laifeng_Android中的AnnexbHelper來(lái)簡(jiǎn)單的看看,如果進(jìn)行解析。
Android硬編碼得到的H264是Annexb這種格式的。
- 如何獲取NAUL單元
其實(shí)就是找開(kāi)頭為 0x0001或者0x000001的字節(jié)段
/**
* 從硬編出來(lái)的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++;
}
}
- 根據(jù)
NAL Header內(nèi)的nal_unit_type判斷當(dāng)前幀的類(lèi)型
根據(jù)上面的類(lèi)型分析,我們知道NAUL里面前8位中的后5位,表示這個(gè)NAUL的類(lèi)型。
根據(jù)這些可以判斷類(lèi)型。
而剩下的就是NAUL內(nèi)的數(shù)據(jù)了。
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;
}

