H264码流分析导读
导读
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”,以便字节对齐。
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删除掉。
-
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;
}