视频是由视频序列组成的,而视频序列又由一帧帧图像组成,图像又由片组成,片由宏块组成,宏块由子宏块组成。
这种分层结构有助于更高效的节省码流。缺点也很大:1)每层中,头部和数据强依赖,头部丢失数据就无法解码;2)序列层和图像层数据量大,不能一次传输,若头部所在分组丢失,其他数据无法解码;3)图像层各片之间常携带相同数据,造成码流浪费。
因此,H.264取消了图像层和序列层,取而代之的是图像参数集(PPS)和序列参数集(SPS),其余部分放入片层。
序列参数集和图像参数集位于码流的最前面。如果编码器认为需要更新参数集时,会发送新的参数集。片成为携带图像像素数据的最上层的数据单位,同一个图像的每个片都要携带一致的图像编号、大小等信息。
在H.264/AVC视频编码标准中,整个系统框架被分为了两个层面:视频编码层面(VCL)和网络抽象层面(NAL)。其中,前者负责有效表示视频数据的内容,而后者则负责格式化数据并提供头信息,以保证数据适合各种信道和存储介质上的传播。VCL是H.264/AVC的规格,意思是压缩后、去冗余(Visual Redundancy)的视频信息,其技术核心包括动作估计、转换编码、预测编码、去区块效应滤波、及熵编码等。
H.264文档:http://www.itu.int/rec/T-REC-H.264 , http://www.itu.int/rec/T-REC-H.264-200503-S/en
字节流格式和RTP包格式。后者是封装格式,通常所用的是前者。字节流格式没有经过传输协议的封装,故称为裸流。
H264比特流 = Start_Code_Prefix + NALU + Start_Code_Prefix + NALU + …
从头开始,找到起始码0x000001或0x00000001,下一个字节开始,就是NALU的部分。
NALU = NALU Header + RBSP
1个字节,包括forbidden_zero_bit(1 bit)、nal_ref_idc(2 bits)和nal_unit_type(5 bits)。
1)forbidden_zero_bit:这个值应该为0,当它不为0时,表示网络传输过程中,当前NALU中可能存在错误,解码器可以考虑不对这个NALU进行解码。
2)nal_ref_idc:取值0~3,代表当前这个NALU的重要性,取值越大,代表当前NALU越重要,就需要优先被保护。尤其是当前NALU为图像参数集、序列参数集或IDR图像时,或者为参考图像条带(片/Slice),或者为参考图像的条带数据分割时,nal_ref_idc值肯定不为0。而当NALU 类型,nal_unit_type为6、9、10、11、或12时,nal_ref_idc都为0。
【注】IDR帧,即时解码刷新图像,它是一个序列的第一个图像,H.264引入IDR图像是为了解码的重新同步。当解码器解码到IDR图像时,立即将参考帧队列清空,将已解码的数据全部输出或抛弃(所有I帧共有特性),更新参数集SPS和PPS,开始一个新的序列。这样一来,如果前一个序列发生重大错误,在这里就可以获得重新同步。
所以IDR图像之后的图像,永远不会引用IDR图像之前的图像来解码。并且IDR图像一定是I图像,而I图像不一定是IDR图像(H264里没有图像层,图像可以理解为帧、片或宏块)。
3)nal_unit_type:表示RBSP的数据结构的类型。
标准的NAL-unit总共规范(profile)有12种,这12种型式可粗分成VCL NAL-unit(1-5)及non-VCL NAL-unit,其中 VCL NAL-unit是指NAL-unit中存放的完全是VCL的视频信息。现实中的传输系统是多样化的,其可靠性,服务质量,封装方式等特征各不相同,NAL这一概念的提出提供了一个视频编码器和传输系统的友好接口,使得编码后的视频数据能够有效的在各种不同的网络环境中传输。
(1)SODB:String Of Data Bits,最原始的编码数据,没有任何附加数据
(2)RBSP:Raw Byte Sequence Payload,在 SODB 的基础上加了rbsp_stop_one_bit(bit 值为 1)并用 0 按字节补位对齐
(3)EBSP:Encapsulated Byte Sequence Payload,在 RBSP 的基础上增加了防止伪起始码字节(0X03)
(4)NALU是对RBSP的封装。而RTP之类的是对NALU的封装。
在h264的文档中,并没有EBSP这一名词出现,但是在h264的官方参考软件JM里,却使用了EBSP。EBSP相较于RBSP,多了防止竞争的一个字节:0x03。
NALU的起始码为0x000001或0x00000001,同时H264规定,当检测到0x000000时,也可以表示当前NALU的结束。如果在NALU的内部,出现了0x000001或0x000000?防竞争机制:当编码器编码完一个NAL时,应该检测NALU内部,是否出现如下左侧的四个序列。当检测到它们存在时,编码器就在最后一个字节前,插入一个新的字节:0x03。
0x000000 -> 0x00000300 |
拿到EBSP时,检测是否有序列:0x000003,如果有,则去掉其中的0x03,得到原始字节序列载荷RBSP。
参考文档:
H264/AVC句法和语义详解 https://www.jianshu.com/nb/25987315
H264中的NALU概念解析 https://blog.csdn.net/pkx1993/article/details/79974858