H264的码流格式
一般有两种格式:
1、Annex B(字节流格式):由Network Abstraction Layer Units (NALU)组成,也被称为NAL。每个NALU包都可以被单独解析和处理。
2、MP4格式,也叫RTP包格式。MP4 格式没有起始码,而是在图像编码数据的开始使用了 4 个字节作为长度标识,用来表示编码数据的长度,这样我们每次读取 4 个字节,计算出编码数据长度,然后取出编码数据,再继续读取 4 个字节得到长度,一直继续下去就可以取出所有的编码数据了。
一、Annex B格式(字节流格式)
Annex B格式由起始码+NALU组成。
1、起始码(Start_Code_Prefix )
Annexb 格式以4 字节的“00 00 00 01” 或 3 字节的“00 00 01”为起始分隔NALU数据单元
注意:由于图像编码出来的数据中也有可能出现“00 00 00 01”和“00 00 01”的数据。那这种情况怎么办呢?为了防止出现这种情况,H264会将图像编码数据中的下面的几种字节串做如下处理:
(1)“00 00 00”修改为“00 00 03 00”;
(2)“00 00 01”修改为“00 00 03 01”;
(3)“00 00 02”修改为“00 00 03 02”;
(4)“00 00 03”修改为“00 00 03 03”。
Start_Code_Prefix = 00 00 00 01 或 00 00 01
2、NALU结构
作用:NALU的工作则是负责把网络视频流进行打包和传送。
NALU由NALU Header和NALU body组成。
2.1、 NALU Header
NALU Header由三个句法元素组成,分别为:forbidden_zero_bit、nal_ref_idc和nal_unit_type,它们总共占据一个字节。
forbidden_zero_bit的值对应1个bit,nal_ref_idc的值对应2个bit,nal_unit_type的值对应5个bit,加起来刚好一个字节。
2.1.1、forbidden_zero_bit(禁止位):h264文档规定,这个值应该为0,当它不为0时,表示网络传输过程中,当前NALU中可能存在错误,解码器可以考虑不对这个NALU进行解码。
2.1.2、nal_ref_idc:取值0~3,代表当前这个NALU的重要性,取值越大,代表当前NALU越重要,就需要优先被保护。尤其是当前NALU为图像参数集、序列参数集或IDR图像时,或者为参考图像条带(片/Slice),或者为参考图像的条带数据分割时,nal_ref_idc值肯定不为0。IDR帧,即:及时解码刷新图像,它是一个序列的第一个图像,H.264引入IDR图像是为了解码的重新同步。当解码器解码到IDR图像时,立即将参考帧队列清空,将已解码的数据全部输出或抛弃,重新查找参数集,开始一个新的序列。这样一来,如果前一个序列发生重大错误,在这里就可以获得重新同步。所以IDR图像之后的图像,永远不会引用IDR图像之前的图像来解码。并且IDR图像一定是I图像,而I图像不一定是IDR图像(H264里没有图像层,图像可以理解为帧、片或宏块)。
2.1.3、nal_unit_type:顾名思义,这个应该是最好理解的了,它表示NALU Header后面的RBSP的数据结构的类型:
PPS(图像参数集):PPS主要保存整体图像相关的参数。这些参数包括熵编码模式选择标识、片组数目、初始量化参数和去方块滤波系数调整标识等。PPS作用于视频序列中的图像,对单帧图像进行约束。
SPS(序列参数集):SPS主要保存编码序列的全局参数。这些参数包括标识符seq_parameter_set_id、帧数及POC(picture order count)的约束、参考帧数目、解码图像尺寸和帧场编码模式选择标识等。SPS作用于一串连续的视频图像,对帧组进行修饰。
IDR(即时解码器刷新):IDR是一种特殊的帧,其后的帧不会参考IDR之前的帧,这样可以刷新缓冲队列。在H.264码流中,IDR通常是第三个NALU(网络抽象层单元)。
SEI数据:是视频的附加增强信息,它包含了一些用户自定义的数据,如时间戳,字幕,弹幕等信息。SEI信息一般放在编码图像之前,很多时候SEI可以被忽略。
eg:
2.2、NALU Body
NALU的主体涉及到三个重要的名词,分别为EBSP、RBSP和SODB。其中EBSP完全等价于NALU主体,而且它们三个的结构关系为:
EBSP包含RBSP,RBSP包含SODB
2.2.1、SODB(String of Data Bits)数据比特串:最原始的编码数据,即VCL数据,没有任何附加数据。
2.2.3、RBSP(Raw ByteSequence Payload)原始字节序列载荷:在SODB的后面填加了结尾比特(RBSP trailing bits),一个bit“1”,若干bit “0”,以便字节对齐;
2.2.3、EBSP(EncapsulationByte Sequence Packets)扩展字节序列载荷:在RBSP基础上填加了仿校验字节(0X03)。它的原因是:在NALU加到Annexb上时,需要添加每组NALU之前的开始码StartCodePref ix,如果该NALU对应的slice为一帧的开始则用4位字节表示,ox00000001,否则用3位字节表示ox000001
(是一帧的一部分)。解码器检测每个起始码,作为一个NAL的起始标识,当检测到下一个起始码时,当前NAL结束。对于NAL中数据出现0x000001或
0x000000时,H.264引入了防止竞争机制,如果编码器遇到两个字节连续为0,就插入一个字节的0x03。解码时将0x03去掉,也称为脱壳操作。
h264的码流结构为: