H.264(MPEG-4 Part 10, Advanced Video Coding),缩写为 MPEG-4 AVC。是一种面向块,基于运动补偿的视频编码标准。
压缩比:对于YUV420的裸流,压缩比约为1/100。
新特性:
- 多参考帧的运动补偿。可以带来一定的码率降低或者质量提高。
- 变块尺寸运动补偿。可使用最大 16x16 到最小 4x4 的块来进行运动估计与运动补偿,能够对运动区域进行更精确的分割。
- 使用六阶数字滤波器来产生二分之一像素的亮度分量预测值,可以较少混叠(Aliasing)并得到更锐化的图像。
- 灵活的隔行扫描视频编码。
- 1/4 像素精度的运动补偿,能够提供更高精度的运动块预测。
- 加权的运动预测。在一些特殊场合,如淡出、淡入等情况提供相当大的编码增益。
- 等等。。。https://zh.wikipedia.org/wiki/H.264/MPEG-4_AVC
编码原理
对一段变化不大的图像画面,先编码出一个完整的图像帧A,随后的B帧就不编码全部图像,只写入与A帧的差别,然后继续以B的方式编码C帧,这样循环下去得到一段序列。当某幅图像与之前的图像变化很大时,无法参考前面的帧来生成,就结束上一个序列,开始下一段序列的生成。
H.264 协议里定义了三种帧:
完整编码的 I 帧;
参考之前的 I 帧只生成的包含差异部分编码的 P 帧;
参考前后的帧编码的 B 帧;
H.264 采用的核心算法是帧内压缩和帧间压缩,帧内压缩是生成 I 帧的算法,帧间压缩是生成 B 帧和 P 帧的算法。
GOP序列
提出的意义:按照相关性进行分组便于进行帧间压缩。这样分组的一组图像差别较少,去除的冗余/重复数据就多,压缩比就高。
H.264 中以图像序列为单位进行组织,一个序列是一段图像编码后的数据流,从 I 帧开始,到下一个 I 帧结束。
序列的第一个图像叫 IDR 图像(立即刷新图像),IDR 图像都是 I 帧图像。当遇到 IDR 图像时,立即将参考帧队列清空,将已解码的数据全部输出或抛弃,重新查找参数集,开始一个新的序列。这样在前一个序列出现重大错误时,可以获得重新同步的机会。
一个序列就是一段内容差异不太大的图像编码后生成的一串数据流。当运动变化较少时,一个序列可以很长,所以就可以编一个 I 帧,让后一直 P 帧、B 帧了。当运动变化较多时,一个序列就可能比较短了。
GOP(Group Of Pictures,图像组)是一组连续的图像,由一个I帧和多个B/P帧组成,是编解码器存取的基本单位。GOP 中帧与帧之间的差别较小。
GOP结构常用的两个参数M和N,M指定GOP中首个P帧和I帧之间的距离,N指定一个GOP的大小。例如M=1,N=15,GOP结构为:
\[ IPBBPBBPBBPBBPB \]
GOP分两种:闭合式GOP和开放式GOP:
- 闭合式 GOP:闭合式GOP只需要参考本GOP内的图像即可,不需参考前后GOP的数据。这种模式决定了,闭合式GOP的显示顺序总是以I帧开始以P帧结束。
- 开发式 GOP:开放式GOP中的B帧解码时可能要用到其前一个GOP或后一个GOP的某些帧。码流里面包含B帧的时候才会出现开放式GOP。
如图特征,开发式GOP末尾是B帧,闭合式GOP末尾是P帧。但不管如果要称为GOP,首帧必须是I帧。
开放式GOP和闭合式GOP中I帧、P帧、B帧的依赖关系如下图所示:
GOP 指两个 I 帧之间的距离。参考周期(Reference)指两个 P 帧之间中距离。I、B、P 帧所占字节数大小:I > P > B。
所以在码率不变的前提下,GOP 值越大,P、B 帧的数量会越多,平均每个 I、P、B 帧所占的字节数就越多,也就容易获得较好的图像质量。通过提高 GOP 值来提高图像质量是有限度的,在遇到场景切换的情况下,编码器会自动强制插入一个 I 帧,此时实际的 GOP 值被缩短。另一方面,在一个 GOP 中,P 帧由 I 帧预测得到的,当 I 帧的图像质量比较差时,会影响一个 GOP 中后续 P、B 帧的图像质量。直到下一个 GOP 开始才有可能得到恢复,所以 GOP 值也不宜设置过大。
同时,由于 P、B 帧的复杂度大于 I 帧,所以过多的 P、B 帧会影响编码效率,使得编码效率降低。另外,过长的 GOP 还会影响 seek 操作的响应速度,因为 P、B 帧是由前面的 I 或 P 帧预测得到的,所以 seek 操作需要直接定位、解码某一个 P 或 B 帧时,需要先解码得到本 GOP 内的 I 帧及之前的 N 个预测帧才可以,GOP 值越长,需要解码的预测帧就越多,seek 响应的时间也就越长。
帧
I 帧
Intra-coded picture,帧内编码图像帧,常称为关键帧。
不参考其他图像帧,只利用本帧信息进行编码。包含一幅完整的图像信息,属于帧内编码图像,不含运动矢量,在解码时不需要参考其他帧图像。因此在I帧图像处可以切换频道,而不会导致图像丢失或无法解码。I帧图像用于阻止误差的累积和扩散。在闭合式GOP中,每个GOP的第一个帧一定是I帧(IDR帧),且当前GOP的数据不会参考前后GOP的数据。
IDR 帧
Instantaneous Decoding Refresh picture,即时解码刷新帧,是一种特殊的I帧。当解码器解码到IDR帧时,会将DPB(Decoded Picture Buffer,指前后向参考帧列表)清空,将已解码的数据全部输出或抛弃,然后开始一次全新的解码序列。IDR帧之后的图像不会参考IDR帧之前的图像。
在编码解码中为了方便,将GOP中首个I帧要和其他I帧区别开,把第一个I帧叫IDR,这样方便控制编码和解码流程,所以IDR帧一定是I帧,但I帧不一定是IDR帧;IDR帧的作用是立刻刷新,使错误不致传播,从IDR帧开始算新的序列开始编码。I帧有被跨帧参考的可能,IDR不会。
I帧不用参考任何帧,但是之后的P帧和B帧是有可能参考这个I帧之前的帧的。IDR就不允许这样,例如:
1 | IDR1 P4 B2 B3 P7 B5 B6 I10 B8 B9 P13 B11 B12 P16 B14 B15 |
总结:
- 解码器立即刷新,清空参考帧缓冲区(DPB),防止错误传播。
- GOP第一帧是IDR帧,是特殊的I帧。
- GOP只有一个IDR帧,但可能还有多个I帧。
- IDR帧之后的图像不会参考其之前的帧,但普通I帧就没有这个限制。
P 帧
Predictive-coded picture,预测编码图像帧,帧间编码帧。
利用之前的 I 帧或 P 帧,采用运动预测的方式进行预测编码。不会参考B帧。
- P 帧属于前向预测的帧间编码,只参考最靠近它的 I 帧或 P 帧。它只占 I 帧大小的一半。
- P 帧可以作为后面 P 帧的参考帧,也可以作为其前后的 B 帧的参考帧。
B 帧
Bidirectionally predicted picture,双向预测编码图像帧,帧间编码帧。
提供最高的压缩比,她既需要之前的图像帧(I 帧或 P 帧),也需要后来的图像帧(P 帧),采用运动预测的方式进行帧间双向预测编码。不会参考附近的 B 帧。
占 I 帧的 1/4 大小,压缩比最大,但解码的时候占用资源和耗时也是最大的。在实时通信中较少使用 B 帧,点播、存储的视频则可以较多地使用 B 帧。
- B 帧值反映两参考帧间运动主体的变化情况,预测比较准确。
- B 帧会比附近的 P 帧后解码,即先解码附近的帧才能解码当前B帧。
帧与分组的关系
箭头指向的帧参考箭头起点的帧。解码 B 帧需要解码参考的前后帧。解码的顺序与播放的顺序是不一致的,因此就有了下面的话题。
SPS、PPS
SPS、PPS 不称为帧,只是在 IDR 帧前的参数数据,这两个信息一般同时出现。
- SPS,Sequence Parameter Set,序列参数集
- 作用于一串连续的视频图像,对帧组 GOP 的参数设置。
- 如:seq_parameter_set_id、帧数、POC(picture order count)的约束、参考帧数量、解码图像尺寸、场编码模式选择标志等。
- PPS,Picture Parameter Set,图像参数集
- 作用于视频序列中的图像,GOP 中每一幅图像等参数设置。
- 如:pic_parameter_set_id、熵编码模式选择标志、片组数目、初始量化参数、去方块滤波系数调整标志等。
SPS
- H264 Profile:对视频压缩特性的描述,profile 越高,说明采用了越高级的压缩特性,对应的压缩比也越高。
- H264 Level:对视频规格的描述,level 越高,视频的码率、分辨率、帧率越高。
H264 Profile:
如上图,从Constrained Baseline Profile为核心发展出两个方向的分支,一个是Main Profile;另一个是Baseline Profile和Extended Profile。Main比Constrained Baseline压缩比高(有B帧和更高压缩比的CABAC无损压缩算法)。我们用得更多的是Main Profile方向的分支,下面是该分支的发展的具体Profile。
High应该是压缩比最高的profile,后面不断增加的是质量方面的特性。
H264 Level:
其他重要参数:
分辨率相关参数:
- pic_width_in_mbs_minus1:图像宽度包含的宏块个数-1(这里好获取具体宽度还要获取宏块宽度,默认是16)
- pic_height_in_mbs_minus1:图像高度包含的宏块个数-1
- frame_mbs_only_flag:帧编码还是场编码(场是隔行扫描,产生两张图,该参数会影响分辨率的计算)
- frame_cropping_flag:图像是否需要裁剪(有裁剪的,还要减去裁剪的尺寸)
- frame_crop_left_offset:减去左侧的偏移量
- frame_crop_right_offset:减去右侧的偏移量
- frame_crop_top_offset:减去顶部的偏移量
- frame_crop_bottom_offset:减去底部的偏移量
通过 pic_width_in_mbs_minus1、pic_height_in_mbs_minus1、宏块宽高(默认 16x16)以及考虑 frame_mbs_only_flag、frame_cropping_flag,可以得出分辨率。
GOP帧信息参数:
- log2_max_frame_num_minus4:可得出GOP的最大帧数:2的该值次方+4。
- 可通过该值与 slice header 的 frame_num,得出被解码的帧的序号。
- max_num_ref_frames:参考帧的数量。
- 用于设置解码时候的缓冲队列大小。
- pic_order_cnt_type:显示帧的序号类型。
- 通过计算可得出显示的顺序。
帧率计算:
1 | framerate = (float)(sps->vui.vui_time_scale) / |
PPS
- entropy_coding_mode_flag:熵编码类型,1表示使用 CABAC,0则为CAVLC。
- num_slice_groups_minus1:分片数量。
- weighted_pred_flag:在 P/SP Slice 中是否开启权重预测。
- weighted_bipred_idc:在 B Slice 中加权预测的方法类型。
- pic_init_qp_minus26/pic_init_qs_minus26:初始化量化参数,实际参数在 Slice Header 中。
- chroma_qp_index_offset:用于计算色度的量化参数。
- deblocking_filter_control_present_flag:表示 Slice Header 中是否存在去块滤波器控制的信息。
- constrained_intra_pred_flag:若为1,表示 I 宏块在进行帧内预测时只能使用来自 I 和 SI 类型的宏块的信息。
- redundant_pic_cnt_present_flag:用于表示 Slice Header 中是否存在 redundant_pic_cnt 语法元素。
Slice Header
- 帧类型
- GOP中解码帧序号(当有 B 帧的时候,并不是顺序解码的)
- 预测权重
- 滤波
DTS、PTS
- DTS(Decode Time Stamp,解码时间戳):表示packet的解码时间,主要用于视频的编码,在编码阶段使用。主要标识内存的包什么时候送入解码器中解码。解码阶段使用。
- PTS(Presentation Time Stamp,显示时间戳):表示packet解码后数据的显示时间,主要用于视频的同步和输出。显示阶段使用。
音频中DTS和PTS是相同的。视频中由于B帧需要双向预测,B帧依赖于其前和其后的帧,因此含B帧的视频解码顺序与显示顺序不同,即DTS与PTS不同。当然,不含B帧的视频,其DTS和PTS是相同的。下图以一个开放式GOP示意图为例,说明视频流的解码顺序和显示顺序。
采集顺序
:指图像传感器采集原始信号得到图像帧的顺序。编码顺序
:指编码器编码后图像帧的顺序。存储到磁盘的本地视频文件中图像帧的顺序与编码顺序相同。传输顺序
:指编码后的流在网络中传输过程中图像帧的顺序。解码顺序
:指解码器解码图像帧的顺序。显示顺序
:指图像帧在显示器上显示的顺序。
采集顺序与显示顺序相同。编码顺序、传输顺序和解码顺序相同。
图中“B[1]”帧依赖于“I[0]”帧和“P[3]”帧,因此“P[3]”帧必须比“B[1]”帧先解码。这就导致了解码顺序和显示顺序的不一致,后显示的帧需要先解码。
可见,在没有B帧的时候,DTS和PTS是是一致的。
码流
H.264原始码流(裸流)是由一个接一个NALU组成。
按其功能可能将其分层:
- VCL层,Video Coding Layer,视频数据编码层。
- 保存视频压缩后的数据。
- NAL层,Network Abstraction Layer,视频数据网络抽象层,最外层。
- 对VCL视频编码层数据拆成多个包传输,并提供header等信息。
- 为解决网络传输中丢包、乱序、重传问题提供标记。
相关术语:
- SODB,String Of Data Bits:位串。
- 原始数据比特流/二进制数据串,以位编码在一起,长度不一定是8的倍数,故需要补齐。
- 由 VCL 层产生。
- RBSP,Raw Byte Sequence Payload:字节序列负载数据。
- SODB + trailing bits,如果SODB最后一个字节不对齐,则补1和多个0。
- EBSP,Encapsulated Byte Sequence Payload:扩展字节序列负载数据。
- 在每一帧开头添加起始位。
- NALU,NAL 单元:多个NAL单元组成H264码流。
关系:
1 | RBSP = SODB + RBSP Stop bit + 0 bits |
NAL Unit
码流的总体结构:
- Annexb 格式用于文件存储、本地播放,是在NALU前面增加了StartCode。
- RTP 格式用于网络传播,直接就是传输NALU。
NALU有两种格式:
- Annex B/Elementary Stream:以
0x00_00_01
或0x00_00_00_01
开头。 - AVCC/MPEG-4:以所在NALU长度开头。
宏块存储数据:
- mb_type:宏块类型
- mb_pred:预测类型值
- coded residual,残差值
宏块与帧的关系:
1 | 1片 = N宏块 |
片的出现:设置片的目的是为了限制误码的扩散和传输,让编码片之间相互独立,如某片的预测不能以其他片中的宏块为参考图像,以防止某片中的预测错误传播到其他片中。
层级划分:
VCL结构关系:
其中,SPS、PPS不是VCL产生的,但以NALU传输,对于正确解码非常重要。可以通过独立的服务来发送参数集。
分析工具
- Elecard Stream Eye
- https://www.elecard.com/products/video-analysis
- CodecVisa
- 雷神开发的工具
- https://jaist.dl.sourceforge/project/h254streamanalysis/binary/SpecialVH264.exe
- https://sourceforge.net/projects/videoeye/files/