音视频开发浅析H。264编码格式
在学习H。264编码之前,我们先了解一下在视频直播的过程中,如果Camera采集的YUV图像不做任何处理进行传输,那么每秒钟需要传输的数据量是多少?
Camera采集的YUV图像通常为YUV420,根据YUV420的采样结构,YUV图像中的一个像素中Y、U、V分量所占比例为1:14:14,而一个Y分量占1个字节,也就是说对于YUV图像,它的一个像素的大小为(11414)Y32个字节。
如果直播时的帧率设置为30fps,当分辨率为1280x720,那么每秒需要传输的数据量为1280720(像素)30(帧)32(字节)39。5MB;当分辨率为1920x720,那么每秒需要传输的数据量接近60MB,而在现实网络中,这么高的上行宽带一般是很难达到的,因此,我们就必须在传输之前对采集的视频数据进行压缩编码。1。H。264简介
H。264是MPEG4的第十部分,是由VCEG和MPEG联合提出的高度压缩数字视频编码器标准,目前在多媒体开发应用中非常广泛。
H。264具有低码率、高压缩、高质量的图像、容错能力强、网络适应性强等特点,它最大的优势拥有很高的数据压缩比率,在同等图像质量的条件下,H。264的压缩比是MPEG2的两倍以上。1。1H。264编码原理
在H。264协议里定义了三种帧,完整编码的帧叫I帧(关键帧),参考之前的I帧生成的只包含差异部分编码的帧叫P帧,还有一种参考前后的帧编码的帧叫B帧。H。264编码采用的核心算法是帧内压缩和帧间压缩。
其中,帧内压缩是生成I帧的算法,它的原理是当压缩一帧图像时,仅考虑本帧的数据而不用考虑相邻帧之间的冗余信息,由于帧内压缩是编码一个完整的图像,所以可以独立的解码显示;
帧间压缩是生成P、B帧的算法,它的原理是通过对比相邻两帧之间的数据进行压缩,进一步提高压缩量,减少压缩比。
通俗的来说,H。264编码的就是对于一段变化不大图像画面,我们可以先编码出一个完整的图像帧A,随后的B帧就不编码全部图像,只写入与A帧的差别,这样B帧的大小就只有完整帧的110或更小。
B帧之后的C帧如果变化不大,我们可以继续以参考B的方式编码C帧,这样循环下去。
H。264编码框架分两层:VCL(VideoCodingLayer):负责高效的视频内容表示;NAL(NetworkAbstractionLayer):负责以网络所要求的恰当的方式对数据进行打包和传送;1。2IDR(I帧)
IDR(InstantaneousDecodingRefresh):即时解码刷新。一个序列的第一个图像叫做IDR图像(立即刷新图像),IDR图像都是I帧图像(关键帧)。
H。264引入IDR图像是为了解码的重同步,当解码器解码到IDR图像时,立即将参考帧队列清空,将已解码的数据全部输出或抛弃,重新查找参数集,开始一个新的序列。
这样,如果前一个序列出现重大错误,在这里可以获得重新同步的机会。IDR图像之后的图像永远不会使用IDR之前的图像的数据来解码。SPS(SequenceParameterSets):序列参数集,作用于一系列连续的编码图像。PPS(PictureParameterSet):图像参数集,作用于编码视频序列中一个或多个独立的图像。SEI(SupplementalEnhancementInformation):附加增强信息,包含了视频画面定时等信息,一般放在主编码图像数据之前,在某些应用中,它可以被省略掉。P帧:前向预测编码帧。P帧表示的是这一帧跟之前的一个关键帧(或P帧)的差别,它P帧是I帧后面相隔12帧的编码帧,其没有完整画面数据,只有与前一帧的、画面差别的数据。B帧:双向预测内插编码帧。B帧记录的是本帧与前后帧的差别,它是由前面的I或P帧和后面的P帧来进行预测的。1。3H。264与X。264区别
H。264是需要付费的编码格式,而x264是符合H。264标准的一个开源项目,是免费的,也就是H264的一个简化版,不支持某些高级特性。
但x264非常优秀,并不比H264的商业编码器差。H264采用的核心算法是帧内压缩和帧间压缩,帧内压缩是生成I帧的算法,帧间压缩是生成B帧和P帧的算法。2。H。264数据组织形式
通常,数据的组织形式从大到小排序是:序列(sequence)、图像(framefieldpicture)、片组(slicegroup)、片(slice)、宏块(macroblock)、块(block)、子块(subblock)、像素(pixel)。
在H。264码流中图像是以序列为单位进行组织的,一个序列是由多帧图像被编码后的数据流,以I帧开始,到下一个I帧结束;
一帧图像可以分成一个或多个片(slice),片由宏块组成,宏块是编码处理的基本单位,当片编码之后会被打包进一个NALU,也就是一帧图像对应于一个NALU。
NALU是H。264编码数据存储或传输的基本单位,它除了容纳片还可以容纳其他数据,如SPS、PPS、SEI等。
根据H。264编码原理可知,一个序列是一段内容差异不太大的图像编码生成的一串数据流。
当运动变化比较少时,一个序列可以很长,这是由于运动变化少就代表图像画面的内容变动就很小,所有就可以编一个I帧,然后后面一直P帧、B帧;当运动变化较大,可能这个序列就比较短,因为图像画面的内容变动大,所以P帧、B帧就相对减少。
总之,一个序列总是以I帧为开始,到下一个I帧结束,序列包含的图像帧的数量与画面变化情况有关。
相关视频推荐
音视频开发必备基础知识及如何用好开源项目哔哩哔哩bilibili
音视频学习最佳实践从FFmpeg到流媒体服务器开发哔哩哔哩bilibili
学习地址:【免费】FFmpegWebRTCRTMPNDKAndroid音视频流媒体高级开发学习视频教程腾讯课堂
需要更多ffmpegwebrtc。。音视频流媒体开发学习资料加群812855908领取
3。H。264中的NAL技术
从H。264的介绍可知,NAL是H。264AVC编码框架中的网络抽象层,即NetworkAbstractLayer,它主要负责格式化数据并提供头信息,以保证数据适合各种信道和存储介质上的有效传输。
由于实际中的信息传输系统可靠性、封装方式、服务质量等特征的多样化,NAL提供了一个视频编码器和传输系统的友好接口,使得编码后的视频数据能够有效地在各种不同的网络环境中传输。
在NAL层中,NALU(NetworkAbstractLayerUnit,网络抽象层单元)是H。264编码存储或传输的基本单位,在H。264码流中每一帧数据就是一个NALU(注:SPS、PPS、SEI不属于帧)。
每个NALU都包含一个头结构,这个头结构占1个字节(8位),它标明了该NAL单元的是否可丢弃、重要性指示和NALU类型,结构如下:
其中:禁止位:当网络发现NALU存在错误时,该位将被设置为1以方便接收方丢弃该NALU;重要性指示:用于标志该NALU用于重建时的重要性,其值越大表示越重要;NALU类型:用于判断该NALU是否为PPS、SPS、I(关键)PB帧等,一般H。264码流
最开始的两个NALU是SPS和PPS,第三个NALU是IDR(I帧)。NALU类型是判断帧类型重要工具,至于如何去利用它来实现SPS、PPS和IPB帧等信息的检测,我们接下来会举例详细介绍,以下是相关值与NALU类型的映射关系图:
从上图可知,当NALU类型5时,说明该NALU是关键帧(I帧);当NALU类型6时,说明该NALU是附加增强信息;当NALU类型7时,说明该NALU是序列参数集(SPS);当NALU类型8时,说明该NALU是图像参数集(PPS),依次类推。4。H。264中SPS、PPS、IPB帧检测与解析4。1H。264码流分层结构
在分析SPS、PPS、IPB帧之前,我们先了解下H。264码流分层的结构。从外往里看,H。264码流实际是由多个NALU组成的码流序列集合(如第一层所示),而一个序列是以I帧开始,以下一个I帧结束。
NALU是H。264编码存储或传输的基本单元,NALU由NALU头部和NALU主体组成(如第二层所示),其中,NALU头部占1个字节,H。264中的SPS、PPS、IPB帧的检测正是通过NALU头部中的NALU类型来实现的。
H。264码流分层结构如下图所示:
4。2H。264文件解析
一般来说,编码器编出的首帧数据为SPS与PPS,接着为I帧(关键帧),再后面就是P帧、B帧。
而对于H。264码流而言,每帧图像的界定符为0x00000001、0x000001,也称起始码,它们分别占4个字节或3个字节,而起始码的后的一个字节便是NALU头,通过这个字节我们就可以很简单的找到所需的SPS、PPS、IPB帧。
这里,我们通过分析一个H。264文件来进行讲解,使用H。264VideoESViewer工具打开一个test。264文件,至于h264文件的生成,我将再下一篇博文进行详细介绍,H264码流结构如下图:
从上图可知,每一行表示一帧图像(除SPS、PPS除外),每一行包括四列,其中第一列为该帧图像存储的逻辑地址;第二列为该帧图像数据所占字节长度,由于H264的编码原理可以知道,H264码流中的每一帧图像并不是实际上的一帧图像,而是多帧图像的集合;
第三列表示图像帧的起始码,均为0x00000001;第四列表示的是NAL类型,由图可知,H264编码器编出的首帧数据为SPS与PPS,接着为I帧(关键帧),再后面就是P帧或B帧(非I帧)4。3SPS、PPS、IPB帧检测
有了前面的理论和分析基础,再来判断H。264码流中的(3)SPS、PPS、IPB帧,那就显得非常容易了。
我们知道,H。264码流中的一帧数据总是以0x00000001或0x000001开始的,起始码的下一位就是NALU头,比如第一帧数据NALU头为0x67,我们截取码流中的前几帧数据进行分析:第一帧:000000016742801FDA02D02868。(占17个字节)第二帧:0000000168CE06E2(占8个字节)第三帧:0000000165B840F78FFCEB04。(占31872个字节)第四帧:0000000141E20110EA4E9F(占3408字节)第五帧:0000000141E40110EC7BDF13(占2096个字节)
由于NALU类型由NALU头的后五位决定的,即字节下标的37位,我们只需要得到这五位的十进制值,再与NAL类型对照表进行比较就可以知道该帧图像是否为SPS、PPS或I帧(关键帧)等。
在编码过程中,我们可以通过将每帧数据起始码的下一个字节与0x1F相与取得NAL头的后五位即可得到该帧的类型,比如:0x670x1F(01100111)(00011111)(00000111)7(十进制)SPS0x680x1F(01101000)(00011111)(00101000)8(十进制)PPS0x650x1F(01100101)(00011111)(00000101)5(十进制)关键帧(I帧)0x410x1F(01000001)(00011111)(00000001)1(十进制)非关键帧(I帧)