0%

H.264/MPEG-4 AVC

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
2
3
4
IDR1 P4 B2 B3 P7 B5 B6 I10 B8 B9 P13 B11 B12 P16 B14 B15
这里的B8可以跨过I10去参考P7
IDR1 P4 B2 B3 P7 B5 B6 IDR8 P11 B9 B10 P14 B11 B12
这里的B9就只能参照IDR8和P11,不可以参考IDR8前面的帧

总结:

  • 解码器立即刷新,清空参考帧缓冲区(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
2
3
framerate = (float)(sps->vui.vui_time_scale) /
(float)(sps->vui.vui_num_units_in_tick) /
2.0

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
2
3
RBSP = SODB + RBSP Stop bit + 0 bits
EBSP = RBSP part1 + 0x03 + RBSP part2 + 0x03 + ... + RBSP partN
NALU = NALU Header(1 byte) + EBSP

NAL Unit

码流的总体结构:

  • Annexb 格式用于文件存储、本地播放,是在NALU前面增加了StartCode。
  • RTP 格式用于网络传播,直接就是传输NALU。

NALU有两种格式:

  • Annex B/Elementary Stream:以0x00_00_010x00_00_00_01开头。
  • AVCC/MPEG-4:以所在NALU长度开头。

宏块存储数据:

  • mb_type:宏块类型
  • mb_pred:预测类型值
  • coded residual,残差值

宏块与帧的关系:

1
2
3
1片 = N宏块
1帧 = N片
常常一个NALU只包含1个片

片的出现:设置片的目的是为了限制误码的扩散和传输,让编码片之间相互独立,如某片的预测不能以其他片中的宏块为参考图像,以防止某片中的预测错误传播到其他片中。

层级划分:

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/

欢迎关注我的其它发布渠道