H.264学习笔记

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

时间: 2024-11-18 10:28:05

H.264学习笔记的相关文章

H.264学习笔记——相关概念

此处记录学习AVC过程中的一些基本概念,不定时更新. frame:帧,相当于一幅图像,包含一个亮度矩阵和两个色度矩阵. field:场,一帧图像,通过隔行扫描得到奇偶两场,分别称为顶场和底场或奇场和偶场. macroblock/MB:宏块,H.264中处理(预测.变换.量化)的基本单元,大小16*16个像素. slice group:条带组,每一帧/场图像中,按照光栅扫面的顺序,将MB/MB对分成多个条带(slice). I/P/B 宏块:I宏块只能利用所在slice中已编码的像素进行帧内预测.

H.264学习笔记5——熵编码之CAVLC

H.264中,4x4的像素块经过变换和量化之后,低频信号集中在左上角,大量高频信号集中在右下角.左边的低频信号相对数值较大,而右下角的大量高频信号都被量化成0.1和-1:变换量化后的残差信息有一定的统计特性和规律. CAVLC(Context-based Adaptive Variable-Length Code):基于上下文的可变长度编码,是H.264中进行4x4像素块进行熵编码的方法,基本(baseline)档次中只能使用CAVLC,只有主要档次和扩展档次才能使用CABAC(见笔记:熵编码之

H.264学习笔记3——帧间预测

帧间预测主要包括运动估计(运动搜索方法.运动估计准则.亚像素插值和运动矢量估计)和运动补偿. 对于H.264,是对16x16的亮度块和8x8的色度块进行帧间预测编码. A.树状结构分块 H.264的宏块,对于16x16的亮度宏块,可以分成16x16.16x8.8x16和8x8的子块进行帧间预测.对于8x8的块(亚宏块,亮度和色度),往下又可以分成8x8.8x4.4x8.4x4的子块.在运动估计中,每一种分割都需要尝试,并计算出运动搜索结果的代价,选择最小代价的分割方式进行预测编码. B.运动估计

H.264学习笔记6——指数哥伦布编码

在H.264中,使用CABAC需要进行二值化处理,而指数哥伦布编码就是CABAC的一种二值化处理的方法.k阶指数哥伦布编解码具体过程如下: A.编码过程:假设待编码数字为CodeNum(必须非负整数) 指数哥伦布编码后的形式为[MZeors][1][Info],MZero表示M个0. 1.将CodeNum以二进制形式表示(若不足k位,前面补0),去掉后面k位(若刚好是k位,去掉k位后得0),将结果(数值)加1,得到二进制数T1: 2.M为二进制数T1的二进制位数减一: 3.然后将第一步中舍去的k

H.264学习笔记4——变换量化

A.变换量化过程总体介绍 经过帧内(16x16和4x4亮度.8x8色度)和帧间(4x4~16x16亮度.4x4~8x8色度)像素块预测之后,得到预测块的残差,为了压缩残差信息的统计冗余,需要对残差数据进行变换和量化操作.变换和量化的总体操作过程如下图: 对于Intra_16x16的亮度块,通过16(4x4)个4x4的前向DCT变换,然后对得到的16个DC系数再进行4x4的Hadamard变换,然后对于16个DC系数和240个AC系数进行量化(DC和AC的量化公式略有不同,为了控制量化死区大小,详

H.264学习笔记2——帧内预测

帧内预测:根据经过反量化和反变换(没有进行去块效应)之后的同一条带内的块进行预测. A.4x4亮度块预测: 用到的像素和预测方向如图: a~f是4x4块中要预测的像素值,A~Q是临块中解码后的参考值.0~8是4x4的亮度块的9个预测方向(模式).当E~H不可得时,用D代替. A~Q在下面情况下不可用: >不在当前图像或条带:在该4x4块之前还没有被编码:位于帧间编码宏块,且constrained_intra_pred为1: 对于9个预测模式,简述如下: >模式0:垂直模式,条件:A~D可用.

016-kruskal算法-贪心-《算法设计技巧与分析》M.H.A学习笔记

最小生成树: 在一给定的连通无向图G = (V, E)中,(u, v) 代表连接顶点u与顶点v的边,而 w(u, v)代表此边的权重,若存在T为G的子集且为无循环图,使得w(T) 最小,则此T为G的最小生成树. 基本思路: kruskal算法总共选择n- 1条边,所使用的贪婪准则是:从剩下的边中选择一条不会产生环路的具有最小耗费的边加入已选择的边的集合中.注意到所选取的边若产生环路则不可能形成一棵生成树.kruskal算法分e 步,其中e 是网络中边的数目.按耗费递增的顺序来考虑这e 条边,每次

019-dfs.bfs-图的遍历-《算法设计技巧与分析》M.H.A学习笔记

深度优先搜索DFS 深搜框架: bool dfs(int loc) { 标记状态loc已访问; if (loc为目标状态) return true; for (每个可能的操作) { 对loc应用操作产生新状态nstat; if (nstat合法且未被访问) { if (dfs(nstat)) return true; } } 撤销loc已访问标记; // 这步要具体问题具体分析了 return false; } 广度优先搜索BFS 实现方法 1. 首先将根节点放入队列中. 2. 从队列中取出第一

017-Prim算法-贪心-《算法设计技巧与分析》M.H.A学习笔记

基本思路: 定义结点集合U, V (U表示已经选择加入MST的结点集合,V表示未选) 1. 任选一个结点加入U 2. 选择一条边权最小的边,他的两个结点分别属于U, V,并把属于V的那个结点加入U 3. 重复执行2直到V空 伪代码: C++代码: int g[mnx][mnx]; int n, m; int d[mnx]; // 朴素 prim, 复杂度O(|V|^2) |V|:点数, |E|:边数 int prim() { memset(d, 0x3f, sizeof d); //初始化 in