音视频探索(3):浅析H.264编码格式

·  阅读 997
音视频探索(3):浅析H.264编码格式

 在学习H.264编码之前,我们先了解一下在视频直播的过程中,如果Camera采集的YUV图像不做任何处理进行传输,那么每秒钟需要传输的数据量是多少?Camera采集的YUV图像通常为YUV420,根据YUV420的采样结构,YUV图像中的一个像素中Y、U、V分量所占比例为1:1/4:1/4,而一个Y分量占1个字节,也就是说对于YUV图像,它的一个像素的大小为(1+1/4+1/4)*Y=3/2个字节。如果直播时的帧率设置为30fps,当分辨率为1280x720,那么每秒需要传输的数据量为1280*720(像素)*30(帧)*3/2(字节)=39.5MB;当分辨率为1920x720,那么每秒需要传输的数据量接近60MB,而在现实网络中,这么高的上行宽带一般是很难达到的,因此,我们就必须在传输之前对采集的视频数据进行压缩编码。

1. H.264简介

 H.264是MPEG-4的第十部分,是由VCEG和MPEG联合提出的高度压缩数字视频编码器标准,目前在多媒体开发应用中非常广泛。H.264具有低码率、高压缩、高质量的图像、容错能力强、网络适应性强等特点,它最大的优势拥有很高的数据压缩比率,在同等图像质量的条件下,H.264的压缩比是MPEG-2的两倍以上。

1.1 H.264编码原理

 在H.264协议里定义了三种帧,完整编码的帧叫I帧(关键帧),参考之前的I帧生成的只包含差异部分编码的帧叫P帧,还有一种参考前后的帧编码的帧叫B帧。H.264编码采用的核心算法是帧内压缩和帧间压缩。其中,帧内压缩是生成I帧的算法,它的原理是当压缩一帧图像时,仅考虑本帧的数据而不用考虑相邻帧之间的冗余信息,由于帧内压缩是编码一个完整的图像,所以可以独立的解码显示;帧间压缩是生成P、B帧的算法,它的原理是通过对比相邻两帧之间的数据进行压缩,进一步提高压缩量,减少压缩比。通俗的来说,H.264编码的就是对于一段变化不大图像画面,我们可以先编码出一个完整的图像帧A,随后的B帧就不编码全部图像,只写入与A帧的差别,这样B帧的大小就只有完整帧的1/10或更小。B帧之后的C帧如果变化不大,我们可以继续以参考B的方式编码C帧,这样循环下去。

 H.264编码框架分两层:

  • VCL(Video Coding Layer):负责高效的视频内容表示;
  • NAL(Network Abstraction Layer):负责以网络所要求的恰当的方式对数据进行打包和传送;

1.2 IDR(I帧)

 IDR(Instantaneous Decoding Refresh):即时解码刷新。一个序列的第一个图像叫做IDR 图像(立即刷新图像),IDR 图像都是I 帧图像(关键帧)。H.264引入 IDR 图像是为了解码的重同步,当解码器解码到IDR 图像时,立即将参考帧队列清空,将已解码的数据全部输出或抛弃,重新查找参数集,开始一个新的序列。这样,如果前一个序列出现重大错误,在这里可以获得重新同步的机会。IDR图像之后的图像永远不会使用IDR之前的图像的数据来解码。

  • SPS(Sequence Parameter Sets):序列参数集,作用于一系列连续的编码图像。
  • PPS(Picture Parameter Set):图像参数集,作用于编码视频序列中一个或多个独立的图像。
  • SEI(Supplemental Enhancement Information):附加增强信息,包含了视频画面定时等信息,一般放在主编码图像数据之前,在某些应用中,它可以被省略掉。
  • P帧:前向预测编码帧。P帧表示的是这一帧跟之前的一个关键帧(或P帧)的差别,它P帧是I帧后面相隔1~2帧的编码帧,其没有完整画面数据,只有与前一帧的、画面差别的数据。
  • B帧:双向预测内插编码帧。B帧记录的是本帧与前后帧的差别,它是由前面的I或P帧和后面的P帧来进行预测的。

1.3 H.264与X.264区别

 H.264是需要付费的编码格式,而x264是符合H.264标准的一个开源项目,是免费的,也就是H264的一个简化版,不支持某些高级特性。但x264非常优秀,并不比H264的商业编码器差。H264采用的核心算法是帧内压缩和帧间压缩,帧内压缩是生成I帧的算法,帧间压缩是生成B帧和P帧的算法。

2.H.264数据组织形式

 通常,数据的组织形式从大到小排序是:序列(sequence)、图像(frame/field-picture)、片组(slicegroup)、片(slice)、宏块(macroblock)、块(block)、子块(sub-block)、像素(pixel)。在H.264码流中图像是以序列为单位进行组织的,一个序列是由多帧图像被编码后的数据流,以I帧开始,到下一个I帧结束;一帧图像可以分成一个或多个片(slice),片由宏块组成,宏块是编码处理的基本单位,当片编码之后会被打包进一个NALU,也就是一帧图像对应于一个NALU。NALU是H.264编码数据存储或传输的基本单位,它除了容纳片还可以容纳其他数据,如SPS、PPS、SEI等。

 根据H.264编码原理可知,一个序列是一段内容差异不太大的图像编码生成的一串数据流。当运动变化比较少时,一个序列可以很长,这是由于运动变化少就代表图像画面的内容变动就很小,所有就可以编一个I帧,然后后面一直P帧、B帧;当运动变化较大,可能这个序列就比较短,因为图像画面的内容变动大,所以P帧、B帧就相对减少。总之,一个序列总是以I帧为开始,到下一个I帧结束,序列包含的图像帧的数量与画面变化情况有关。

3.H.264中的NAL技术

 从H.264的介绍可知,NAL是H.264/AVC编码框架中的网络抽象层,即NetworkAbstract Layer,它主要负责格式化数据并提供头信息,以保证数据适合各种信道和存储介质上的有效传输。由于实际中的信息传输系统可靠性、封装方式、服务质量等特征的多样化,NAL提供了一个视频编码器和传输系统的友好接口,使得编码后的视频数据能够有效地在各种不同的网络环境中传输。

 在NAL层中,NALU(Network Abstract Layer Unit,网络抽象层单元)是H.264编码存储或传输的基本单位,在H.264码流中每一帧数据就是一个NALU(注:SPS、PPS、SEI不属于帧)。每个NALU都包含一个头结构,这个头结构占1个字节(8位),它标明了该NAL单元的是否可丢弃、重要性指示和NALU类型,结构如下:

其中:

  • 禁止位:当网络发现NALU存在错误时,该位将被设置为1以方便接收方丢弃该NALU;
  • 重要性指示:用于标志该NALU用于重建时的重要性,其值越大表示越重要;
  • NALU类型:用于判断该NALU是否为PPS、SPS、I(关键)/P/B帧等,一般H.264码流

 最开始的两个NALU是SPS和PPS,第三个NALU是IDR(I帧)。NALU类型是判断帧类型重要工具,至于如何去利用它来实现SPS、PPS和I/P/B帧等信息的检测,我们接下来会举例详细介绍,以下是相关值与NALU类型的映射关系图:

 从上图可知,当NALU类型=5时,说明该NALU是关键帧(I帧);当NALU类型=6时,说明该NALU是附加增强信息;当NALU类型=7时,说明该NALU是序列参数集(SPS);当NALU类型=8时,说明该NALU是图像参数集(PPS),依次类推。

4.H.264中SPS、PPS、I/P/B帧检测与解析

4.1 H.264码流分层结构

 在分析SPS、PPS、I/P/B帧之前,我们先了解下H.264码流分层的结构。从外往里看,H.264码流实际是由多个NALU组成的码流序列集合(如第一层所示),而一个序列是以I帧开始,以下一个I帧结束。NALU是H.264编码存储或传输的基本单元,NALU由NALU 头部和NALU主体组成(如第二层所示),其中,NALU头部占1个字节,H.264中的SPS、PPS、I/P/B帧的检测正是通过NALU头部中的NALU类型来实现的。

H.264码流分层结构如下图所示:

4.2 H.264文件解析

 一般来说,编码器编出的首帧数据为SPS与PPS,接着为I帧(关键帧),再后面就是P帧、B帧…。而对于H.264码流而言,每帧图像的界定符为0x00000001、0x000001,也称起始码,它们分别占4个字节或3个字节,而起始码的后的一个字节便是NALU头,通过这个字节我们就可以很简单的找到所需的SPS、PPS、I/P/B帧。这里,我们通过分析一个H.264文件来进行讲解,使用H.264 Video ES Viewer工具打开一个test.264文件,至于h264文件的生成,我将再下一篇博文进行详细介绍,H264码流结构如下图:

 从上图可知,每一行表示一帧图像(除SPS、PPS除外),每一行包括四列,其中第一列为该帧图像存储的逻辑地址;第二列为该帧图像数据所占字节长度,由于H264的编码原理可以知道,H264码流中的每一帧图像并不是实际上的一帧图像,而是多帧图像的集合;第三列表示图像帧的起始码,均为0x00000001;第四列表示的是NAL类型,由图可知,H264编码器编出的首帧数据为SPS与PPS,接着为I帧(关键帧),再后面就是P帧或B帧(非I帧)…

4.3 SPS、PPS、I/P/B帧检测

 有了前面的理论和分析基础,再来判断H.264码流中的(3)      SPS、PPS、I/P/B帧,那就显得非常容易了。我们知道,H.264码流中的一帧数据总是以0x00000001或0x000001开始的,起始码的下一位就是NALU头,比如第一帧数据NALU头为0x67,我们截取码流中的前几帧数据进行分析:

   第一帧:0000 00 01 **67** 42 80 1F DA 02 D0 28 68 ….(占17个字节)
   第二帧:0000 00 01 **68** CE 06 E2 (占8个字节)
   第三帧:0000 00 01 **65** B8 40 F7 8F FC EB 04 …. (占31872个字节)
   第四帧:0000 00 01 **41** E2 01 10 EA 4E 9F … (占3408字节)
   第五帧:0000 00 01 **41** E4 01 10 EC 7B DF 13 … (占2096个字节)
复制代码

       由于NALU类型由NALU头的后五位决定的,即字节下标的3-7位,我们只需要得到这五位的十进制值,再与NAL类型对照表进行比较就可以知道该帧图像是否为SPS、PPS或I帧(关键帧)等。在编码过程中,我们可以通过将每帧数据起始码的下一个字节与0x1F相与取得NAL头的后五位即可得到该帧的类型,比如:

   0x67& 0x1F = (0110 0111) & (0001 1111) =(0000 0111)=7(十进制)–> SPS
   0x68& 0x1F = (0110 1000) & (0001 1111) =(0010 1000)=8(十进制)–> PPS
   0x65& 0x1F = (0110 0101) & (0001 1111) =(0000 0101)=5(十进制)–> 关键帧(I帧)
   0x41 & 0x1F = (01000001) & (0001 1111) =(0000 0001)=1(十进制) –> 非关键帧(I帧)
复制代码
分类:
Android
标签:
分类:
Android
标签:
收藏成功!
已添加到「」, 点击更改