H264视频编码基础

1、H.264简介

​ MPEG(Moving Picture Experts Group)和 VCEG(Video Coding Experts Group)联合开发了一个比早期研发的MPEG 和 H.263 性能更好的视频压缩编码标准,这就是被命名 为 AVC(Advanced Video Coding),也被称为 ITU-T H.264 建议和 MPEG-4 的第 10 部分的标准,简称为 H.264/AVC 或 H.264。这个国际标准已经与 2003 年 3 月正式被 ITU-T 所通过并在国际上正式颁布。为适应高清视频压缩的需求,2004 年又增加了 FRExt 部分;为适应不同码率及质量的需求,2006 年又增加了可伸缩编码 SVC。

2、H.264编码格式

2.1 H.264原始码流结构

H.264原始码流(又称为“裸流”)是由一个一个的NALU组成的。他们的结构如下图所示。

image-20230510171508524

​ 其中每个NALU之间通过startcode(起始码)进行分隔,起始码分成两种:0x000001(3Byte)或者0x00000001(4Byte)。如果NALU对应的Slice为一帧的开始就用0x00000001,否则就用0x000001。H.264码流解析的步骤就是首先从码流中搜索0x000001和0x00000001,分离出NALU;然后再分析NALU的各个字段。

2.2 NALU结构

​ NALU结构分为两层,如下图所示:

image-20230511100542481

​ 由上图我们知道: NALU = NALU Header + RBSP,下面分别介绍一下NALU Header 和 RBSP,介绍之前先上H.264官方协议文档里面的关于NALU语法构成,位于h264文档的7.3.1节:

image-20230511105518867

NALU Header

​ 通过上面我们也可以看到,NALU Header由三个句法元素组成,分别为:forbidden_zero_bitnal_ref_idcnal_unit_type,它们总共占据一个字节,也就是说,NALU Header,在整个NALU中,占据一个字节。

​ 而且forbidden_zero_bit的值对应1个bit,nal_ref_idc的值对应2个bit,nal_unit_type的值对应5个bit,加起来刚好一个字节。下面分别来看看它们的语义:

  • forbidden_zero_bit:h264文档规定,这个值应该为0,当它不为0时,表示网络传输过程中,当前NALU中可能存在错误,解码器可以考虑不对这个NALU进行解码。

  • nal_ref_idc:取值0~3,代表当前这个NALU的重要性,取值越大,代表当前NALU越重要,就需要优先被保护。尤其是当前NALU为图像参数集(SPS)、序列参数集(PPS)或IDR图像时,或者为参考图像条带(片/Slice),或者为参考图像的条带数据分割时,nal_ref_idc值肯定不为0。而当NALU 类型,nal_unit_type为6、9、10、11、或12时,nal_ref_idc都为0。

  • nal_unit_type:它表示NALU Header后面的RBSP的数据结构的类型。下图为nal_unit_type所有可能的取值,和对应的语义。也可以参考H.264官方协议文档的7.4.1节。

    image-20230511110516068

​ 可以看到,nal_unit_type的值为1-5时,表示RBSP里面包含的数据为条带(片/Slice)数据,所以值为1-5的NALU统称为VCL(视像编码层)单元,其他的NALU则称为非VCL NAL单元。

​ 这里重点讲下nal_unit_type=5,即表示该RBSP的数据结构类型是IDR图像。

IDR图像:即时解码刷新图像,它是一个序列的第一个图像,H.264引入IDR图像是为了解码的重新同步。当解码器解码到IDR图像时,立即将参考帧队列清空,将已解码的数据全部输出或抛弃,重新查找参数集,开始一个新的序列。这样一来,如果前一个序列发生重大错误,在这里就可以获得重新同步。IDR帧可以单独解码成一帧图像。

​ 所以IDR图像之后的图像,永远不会引用IDR图像之前的图像来解码。并且IDR图像一定是I图像,而I图像不一定是IDR图像(H264里没有图像层,图像可以理解为帧、片或宏块)。

​ 当nal_unit_type为7时,代表当前NALU为序列参数集(SPS),为8时为图像参数集(PPS)。这也是我们打开.h264文件后,遇到的前两个NALU,它们位于码流的最前面。

​ 其中,SPS 主要包含的是图像的宽、高、YUV 格式和位深等基本信息;PPS 则主要包含熵编码类型、基础 QP 和 最大参考帧数量等基本编码信息。如果没有 SPS、PPS 里面的基础信息,之后的 I 帧、P 帧、B 帧就都没办法进行 解码。因此 SPS 和 PPS 是至关重要的。如果码流最前面没有这两个信息,放到播放器里面是播放不了的。

​ 而且当nal_unit_type为14-31时,我们可以不用理睬,目前几乎用不到。

RBSP

​ 上面我们说,NALU的组成部分为:

NALU = NALU Header + RBSP

​ 其实严格来说,这个等式是不成立的,因为RBSP并不等于NALU刨去NALU Header。严格来说,NALU的组成部分应为:

NALU = NALU Header + EBSP

​ 其中的EBSP为扩展字节序列载荷(Encapsulated Byte Sequence Payload),而RBSP为原始字节序列载荷(Raw Byte Sequence Payload)。那为什么我们上篇中,没有使用2式而使用了1式呢?那是因为,在h264的文档中,并没有EBSP这一名词出现,但是在h264的官方参考软件JM里,却使用了EBSP。

而在我们下面的分析中,我们会看到,使用EBSP是很易于理解的。

  • 1、防止竞争

    EBSP相较于RBSP,多了防止竞争的一个字节:0x03。

    我们知道,NALU的起始码为0x000001或0x00000001,同时H264规定,当检测到0x000000时,也可以表示当前NALU的结束。那这样就会产生一个问题,就是如果在NALU的内部,出现了0x000001或0x000000时该怎么办?

    所以H264就提出了“防止竞争”这样一种机制,当编码器编码完一个NAL时,应该检测NALU内部,是否出现如下左侧的四个序列。当检测到它们存在时,编码器就在最后一个字节前,插入一个新的字节:0x03。

    	0x000000 -> 0x00000300
    	0x000001 -> 0x00000301
    	0x000002 -> 0x00000302
    	0x000003 -> 0x00000303
    

    图中0x000000和0x000001前面介绍了,0x000002是作为保留使用,而0x000003,则是为了防止NALU内部,原本就有序列为0x000003这样的数据。

    这样一来,当我们拿到EBSP时,就需要检测EBSP内是否有序列:0x000003,如果有,则去掉其中的0x03。这样一来,我们就能得到原始字节序列载荷:RBSP。

  • 2、RBSP和SODB

​ 得到RBSP之后,我们迫切想做的,就是从RBSP中,提取出原始编码数据SODB(String Of Data Bits)。这就涉及到RBSP与SODB的关系:

RBSP = SODB + RBSP尾部

​ 而且RBSP的尾部,在规定中有两种,我们分别介绍。

  • 2.1 RBSP尾部

其中大多数类型的NALU,使用这种尾部。

image-20230511133407518

​ 图4 RBSP尾部语法(文档7.3.2.11)

其中:

rbsp_stop_one_bit 占1个比特位,值为1

rbsp_alignment_zero_bit 值为0,目的是为了进行字节对齐,占据若干比特位

所以RBSP就等于,SODB在它的最后一个字节的最后一个比特后,紧跟值为1的1个比特,然后增加若干比特的0,以补齐这个字节。

  • 条带RBSP尾部

    另一种尾部,就是当NALU类型为条带时,也即nal_unit_type等于1~5时,这时RBSP使用下面这种尾部:

    image-20230511133613427

    ​ 图5 条带尾比特RBSP语法(文档7.3.2.10)

    可以看到,rbsp_slice_trailing_bits()默认情况下,就是2.1介绍的第一种尾部。只是当entropy_coding_mode_flag值为1,也即当前采用的熵编码为CABAC,而且more_rbsp_trailing_data()返回为true,也即RBSP中有更多数据时,添加一个或多个0x0000。

    所以我们拿到RBSP,只需要按照上述语法,去掉RBSP的尾部,就可以得到SODB。然后就可以对照对应类型的NALU的句法,解析出语法元素的值。

3、H.264原始码流简单解析

​ 理论已经学习差不多了,接下来解析一段H.264码流。这里用到的是一代流媒体璀璨巨星雷神雷霄骅博士开发的工具:视音频编解码学习工程:H.264分析器,Respect!

​ 放入一段H264的码流文件,点击开始即可,如下图所示:

image-20230511152223740

从上面可以看到:视频流开头的第1帧和第2帧,是SPS和PPS信息,如果没有这两个信息,播放器就无法播放这个视频流。再接下来就是IDR帧,上面介绍过了。

​ 现在点一下SPS信息看一下:

image-20230511185426724

​ 可以看到forbidden_zero_bit = 0禁止位=0,代表该NALU没有错误;nal_ref_idc = 1表示该NALU的重要性,前面提到如果当前NALU为图像参数集(SPS)、序列参数集(PPS)或IDR图像时,或者为参考图像条带(片/Slice),或者为参考图像的条带数据分割时,nal_ref_idc值肯定不为0;nal_unit_type=7表示该NALU是SPS信息。下面还有一些数值,如图像的宽高参数:pic_width_in_mbs_minus1 : 61pic_height_in_map_units_minus1 : 34,这里的值乘上16,就是实际的值,所以这个视频流的宽是61 x 16 = 986,高是34 x 16 = 544。

image-20230511192701495

还有一些就不多介绍了,想继续深入可以参考官方协议文档第7.4.2.1节对照一下。

同样的点开PPS信息看一下,这里也差不多,感兴趣的可以参考官方协议文档第7.4.2.2节对照:

image-20230511192816755

IDR_SLICE看一下:

image-20230511193157778

SLICE看一下:

image-20230511193249454

参考文献

H.264官方协议文档

H.264官方协议文档中文版下载

视音频数据处理入门:H.264视频码流解析

视音频编解码学习工程:H.264分析器

H264/AVC 句法和语义详解

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值