1.帧和场的概念
视频的一场或一帧可用来产生一个编码图像。通常,视频帧可以分成两种类型:连续或隔行视频帧。我们平常看的电视是每秒25帧,即每秒更换25个图像,由于视觉暂留效应,所以人眼不会感到闪烁。每帧图像又是分为两场来进行扫描的,这里的扫描是指电子束在显像管内沿水平方向一行一行地从上到下扫描,第一场先扫奇数行,第二场扫偶数行,即我们常说的隔行扫描,扫完两场即完成一帧图像。当场频为50Hz,帧频为25Hz时,奇数场和偶数场扫描的是同一帧图像,除非图像静止不动,否则相邻两帧图像不同。基数行和偶数行构成的场分别为顶场和底场,如下图所示。
2.I B P 帧与SPS和PPS
I 帧 :帧内编码帧 又称intra picture,I 帧通常是每个 GOP(MPEG 所使用的一种视频压缩技术)的第一个帧,经过适度地压缩,做为随机访问的参考点,可以当成图象。I帧可以看成是一个图像经过压缩后的产物。I帧自身可以通过视频解压算法解压成一张单独的完整的图片。
P帧: 前向预测编码帧 又称predictive-frame,通过充分将低于图像序列中前面已编码帧的时间冗余信息来压缩传输数据量的编码图像,也叫预测帧。P帧需要参考其前面的一个I frame 或者B frame来生成一张完整的图片。
B帧: 双向预测内插编码帧 又称bi-directional interpolated prediction frame,既考虑与源图像序列前面已编码帧,也顾及源图像序列后面已编码帧之间的时间冗余信息来压缩传输数据量的编码图像,也叫双向预测帧。B帧要参考其前一个I或者P帧及其后面的一个P帧来生成一张完整的图片。
H.264引入了参数集的概念,每个参数集包含了相应的编码图像的信息:
序列参数集(SPS)包含的是针对一连续编码视频序列的参数,如标识符seq_parameter_set_id、帧数及POC的约束、参考帧数目、解码图像尺寸和帧场编码模式选择标识等。
图像参数集(PPS)对应的是一个序列中某一幅图像或者某几幅图像,其参数如标识符pic_parameter_set_id、可选的seq_parameter_set_id、熵编码模式选择标识、片组数目、初始量化参数和去方块滤波系数调整标识等。
通常,SPS和PPS在片的头信息和数据解码前传送至解码器。每个片的头信息对应一个pic_parameter_set_id,PPS被其激活后一直有效到下一个PPS被激活;类似的,每个PPS对应一个seq_parameter_set_id,SPS被其激活以后将一直有效到下一个SPS被激活。参数集机制将一些重要的、改变少的序列参数和图像参数与编码片分离,并在编码片之前传送至解码端,或者通过其他机制传输。
3.片(slice)和宏块
一个视频图像可编码成一个或更多个片,每片包含整数个宏块(MB),即每片至少包含一个宏块,最多时可包含整数个图像的宏块。总之,一幅图像中每片的宏块数不一定固定。一帧图像的分层结构如下图所示。
编码片相互间是独立的,这样做可以限制误码的扩散和传输。某一片的预测不能以其他片中的宏块为参考图像,这样该片中的预测误差才不会传播到其他片中去。编码片共有五种不同类型,I片、P片、B片以及SP片和SI片。其中SP(切换P)用于不同编码流之间的切换,它包含P宏块和/或I宏块,是扩展档次中的必备功能。SI片包含了一种特殊类型的编码宏块,叫做SI宏块,SI片也是扩展档次中的必备功能。片的句法结构如图所示。其中,片头规定了片的类型、该片属于哪个图像、有关的参考图像等;片的数据包含一系列的编码宏块(MB),和/或跳编码(不编码)数据。每个MB包含头单元和残差数据,头单元见表
宏块(Macro Block):一个编码图像首先要划分成多个块(4x4 像素)才能进行处理,显然宏块应该是整数个块组成,通常宏块大小为16x16个像素。宏块分为I、P、B宏块,I宏块只能利用当前片中已解码的像素作为参考进行帧内预测;P宏块可以利用前面已解码的图像作为参考图像进行帧内预测;B宏块则是利用前后向的参考图形进行帧内预测。
4. H.264的数据格式
在H.264中,分层结构最大的不同是取消了序列层和图像层,并将原本属于序列和图像头部的大部分句法元素游离出来形成序列和图像两级参数集,其余的部分则放入片层。参数集是一个独立的数据单位,不依赖于参数集外的其他句法元素。下图描述了参数集与参数集外句法元素的关系
在图中我们可以看到,参数集只是在片层句法元素需要的时候被引用,而且,一个参数集并不对应某个特定的图像或序列,同一个序列参数集可以被多个序列中的图像参数集引用。同理,同一个图像参数集也可以被多个图像引用。只在编码器认为需要更新参数集的内容时,才会发送出新的参数集。在这种机制下,由于参数集是独立的,它可以被多次重发或者采用特殊技术加以保护。
H.264的功能分为两层:视频编码层(VCL,Video Coding Layer)和网络提取层(NAL,Network Abstraction Layer)。VCL数据即编码处理的输出,它表示被压缩编码后的视频数据序列,是对核心算法引擎,块,宏块及片的语法级别的定义。在VCL数据传输或存储之前,这些编码的VCL数据,先被映射或封装进NAL单元中。NAL定义片级以上的语法级别(如序列参数集和图像参数集,针对网络传输)。
分层的好处显而易见,例如对于RTP传输数据,我们只需了解NAL层就足够了。
5. NALU结构分析
H.264 的基本流由一系列NALU (Network Abstraction Layer Unit )组成
H.264 草案指出,当数据流是储存在介质上时,在每个NALU 前添加起始码:0x000001 或 0x00000001,用来指示一个NALU 的起始和终止位置。在这样的机制下,在码流中检测起始码,作为一个NALU得起始标识,当检测到下一个起始码时,当前NALU结束。H.264 码流中每个帧的开头的3~4个字节是H.264 的start_code(起始码),0x00000001或者0x000001。3字节的0x000001只有一种场合下使用,就是一个完整的帧被编为多个slice(片)的时候,包含这些slice的NALU 使用3字节起始码。其余场合都是4字节0x00000001的。每个NALU单元由一个字节的 NALU头(NALU Header)和若干个字节的载荷数据(RBSP)组成。其中NALU 头的格式如图所示
- F:forbidden_zero_bit.1 位,如果有语法冲突,则为 1。当网络识别此单元存在比特错误时,可将其设为 1,以便接收方丢掉该单元。
- NRI:nal_ref_idc.2 位,用来指示该NALU 的重要性等级。值越大,表示当前NALU越重要。具体大于0 时取何值,没有具体规定。
- Type:5 位,指出NALU 的类型。
h264中NALU类型取值如下图
下表是H.264标准中定义所有NAL的类型
#define NALU_TYPE_SLICE 1 #define NALU_TYPE_DPA 2 #define NALU_TYPE_DPB 3 #define NALU_TYPE_DPC 4 #define NALU_TYPE_IDR 5 #define NALU_TYPE_SEI 6 #define NALU_TYPE_SPS 7 #define NALU_TYPE_PPS 8 #define NALU_TYPE_AUD 9 #define NALU_TYPE_EOSEQ 10 #define NALU_TYPE_EOSTREAM 11 #define NALU_TYPE_FILL 12
NALU类型分析
- 00 00 00 01 67
00 00 00 01 为NALU的起始标志。
00 00 00 01 后面的 67 占1个字节的NALU头。将十六进制的67转换为二进制,得 0110 0111。
字段 | 所占bit位数 | 二进制 | 十进制 | 类型 |
---|---|---|---|---|
forbidden_bit | 1 | 0 | 0 | |
nal_reference_bit | 2 | 11 | 3 | NALU 的重要性等级系数 |
nal_unit_type | 5 | 00111 | 7 | 序列参数集,sps |
- 00 00 00 01 68
00 00 00 01 为NALU的起始标志。
00 00 00 01 后面的 68 为占1个字节的NALU头。将十六进制的68转换为二进制,得 0110 1000。
字段 | 所占bit位数 | 二进制 | 十进制 | 类型 |
---|---|---|---|---|
forbidden_bit | 1 | 0 | 0 | |
nal_reference_bit | 2 | 11 | 3 | NALU 的重要性等级系数 |
nal_unit_type | 5 | 01000 | 8 | 图像参数集,pps |
- 00 00 03 00
H.264规定,当检测到0x000000时,也可以表征当前NAL的结束。那么NAL中数据出现0x000001或0x000000时怎么办?H.264引入了防止竞争机制,如果编码器检测到NAL数据存在0x000001或0x000000时,编码器会在最后个字节前插入一个新的字节0x03。
0x000000->0x00000300 0x000001->0x00000301 0x000002->0x00000302 0x000003->0x00000303
解码器检测到0x000003时,把03抛弃,恢复原始数据(脱壳操作)。解码器在解码时,首先逐个字节读取NAL的数据,统计NAL的长度,然后再开始解码。
- 00 00 00 01 65 或 00 00 01 65
00 00 00 01 或 00 00 01 为NALU的起始标志。
00 00 00 01 或 00 00 01 后面的 65 为占1个字节的NALU头。将十六进制的65转换为二进制,得 0110 0101。
字段 | 所占bit位数 | 二进制 | 十进制 | 类型 |
---|---|---|---|---|
forbidden_bit | 1 | 0 | 0 | |
nal_reference_bit | 2 | 11 | 3 | NALU 的重要性等级系数 |
nal_unit_type | 5 | 00101 | 5 | IDR图像中的片 |
- 00 00 00 01 41
00 00 00 01 为NALU的起始标志。
00 00 00 01 后面的 41 为占1个字节的NALU头。将十六进制的41转换为二进制,得 0100 0001。
字段 | 所占bit位数 | 二进制 | 十进制 | 类型 |
---|---|---|---|---|
forbidden_bit | 1 | 0 | 0 | |
nal_reference_bit | 2 | 10 | 2 | NALU 的重要性等级系数 |
nal_unit_type | 5 | 00001 | 1 | 不分区,非IDR图像的片 |
在baseline的档次中nal_unit_type表示的就是P帧,因为baseline没有B帧。
参考资料:
http://depthlove.github.io/2015/09/23/use-tool-to-analyze-h264-file/
新一代视频压缩编码标准H.264