什么是网站源码分享编码,网站源码分类

老铁们,大家好,相信还有很多朋友对于什么是网站源码分享编码和网站源码分类的相关问题不太懂,没关系,今天就由我来为大家分享分享什么是网站源码分享编码以及网站源码分类的问题,文章篇幅可能偏长,希望可以帮助到大家,下面一起来看看吧!

(本文基本逻辑:视频编码的理论基础是什么→H.264视频编码的基本概念、编码工具、编码流程及码流结构→H.265的编码工具及改进→H.266的编码工具及改进)

跟音频编码一样,视频编码最重要的目的也是为了进行数据压缩,以此来降低数据传输和存储成本

以一路分辨率720×1280(常说的720P),帧率为30fps的视频为例,如果不经过编码压缩,直接传输或存储原始的RGB数据,对应的码率是:720*1280*3*8*30=632.8125Mbps(宽*高*像素字节数*字节比特数*帧数)。一分钟的时间所需要的数据量是:632.8125Mbps*60s=4.63GB。

这个数据量很大,需要很高的存储或传输成本,因此需要采用编码压缩技术以减少码率。

通常,对信息进行压缩,可以从这几个方面着手:

1)信源包含的符号出现概率的非均匀性,使得信源是可以被压缩的。熵编码就利用信源的统计特性进行码率压缩的编码方式。比如著名的哈夫曼编码(也是熵编码的一种),就是当信源中各符号的出现概率都一样时编码效率最低。2)信源的相关性,使得信源是可以被压缩的。比如信息A和信息B的相关性,使得我们可以由信息A加残差D(D=A-B)来推导信息B,这样只编码A和D来实现压缩,这就是所谓的差分编码技术。3)人的感知对不同信源的敏感度不一样,使得信源是可以被压缩的。对人感知不敏感的信息进行部分或全部忽略来实现压缩。

要对视频进行编码,则主要是研究视频数据中的冗余信息,并对其进行压缩。视频信息主要包括这几个方面的冗余:

空间冗余:在同一张帧之中,相邻的像素之间通常有很强的关连性,这样的关连性即为空间上的冗余信息。时间冗余:在视频信息中,相邻的帧与帧之间通常有很强的关连性,这样的关连性即为时间上的冗余信息。编码冗余:视频中不同数据出现的概率不同,欲编码的符号的几率分布是不均匀的。视觉冗余:人的视觉系统对某些细节不敏感。视觉上的冗余信息是指在人在观看视频时,人眼无法察觉的信息。

现在常见的视频编码格式有3个大的系列,分别由不同的组织主导制定:

1)ISO-MPEG/ITU-T系列:由国际标准组织机构(ISO)下属的运动图象专家组(MPEG)和国际电传视讯联盟远程通信标准化组织(ITU-T)开发的系列编码标准。

H.264,也被称为高级视频编码(AdvancedVideoCoding,简称AVC),是一种被广泛使用的高精度视频的录制、压缩和发布格式。该标准引入了一系列新的能够大大提高压缩性能的技术,并能够同时在高码率端和低码率端大大超越以前的诸标准。H.265,也被称为高效率视频编码(HighEfficiencyVideoCoding,简称HEVC),是H.264的继任者。HEVC被认为不仅提升图像质量,同时也能达到H.264两倍的压缩率(等同于同样画面质量下比特率减少了50%),可支持4K分辨率甚至到超高画质电视,最高分辨率可达到8192×4320(8K分辨率),这是目前发展的趋势。H.266,也被称为多功能视频编码(VersatileVideoCoding,简称VVC),是H.265的继任者。VVC对8K超高清、屏幕、高动态和360度全景视频等新的视频类型以及自适应带宽和分辨率的流媒体和实时通信等应用有了更好的支持。根据最近的JVET官方主观测试结果,VVC的平均编码性能相对HEVC的提高已经可以达到49%。

2)AOM系列:前身是由Google主导的VPx系列的编码标准。后续由多家公司组件成立了开放媒体联盟(AllianceforOpenMedia,AOM)继续开发新的编码标准。

VP8,是一个开放的图像压缩格式,最早由On2Technologiesis开发,随后由Google发布。同时Google也发布了VP8编码的实做库:libvpx,以BSD授权条款的方式发布,随后也附加了专利使用权。而在经过一些争论之后,最终VP8的授权确认为一个开放源代码授权。VP9,是Google提供的开源的免费视频编码格式,是VP8的后续版本。AV1,AllianceforOpenMediaVideo1是由AOM(AllianceforOpenMedia,开放媒体联盟)制定的一个开源、免版权费的视频编码格式,目标是解决H.265昂贵的专利费用和复杂的专利授权问题并成为新一代领先的免版权费的编码标准。此外,AV1是Google制定的VP9标准的继任者,也是H.265强有力的竞争者。

3)AVS系列:AVS(AudioVideocodingStandard)是中国具备自主知识产权的系列编码标准。

AVS2,第二代数字音视频编解码技术标准(AVS2),其首要应用目标是超高清晰度视频,支持超高分辨率(4K以上)、高动态范围视频的高效压缩。AVS3,AVS3增加了对8K分辨率的支持,该技术将使用于中央广播电视总台8K超高清频道。

这里我们只对H.264、H.265、H.266做一下介绍。

1、H.264编码

1.1、基本概念

1.1.1、句法元素分层结构

H.264中,句法元素可以分为『序列』、『图像』、『片』、『宏块』、『子宏块』五个层次。

在H.264中,分层结构相较之前最大的不同是取消了序列层和图像层,并将原本属于序列和图像头部的大部分句法元素游离出来形成序列和图像两级参数集,其余的部分则放入片层。在这种机制下,由于参数集是独立的,可以被多次重发或者采用特殊技术加以保护。参数集与参数集外部的句法元素处于不同信道中,这是H.264的一个建议,我们可以使用更安全但成本更昂贵的通道来传输参数集。

领取音视频开发资料:音视频流媒体高级开发FFmpegWebRTCRTMPRTSPHLSRTP播放器

企鵝君羊994289133领取资料

1.1.2、软件和硬件编解码

编解码分为软编软解和硬编硬解

软编/软解:CPU处理。硬编/硬解:使用显卡GPU、专用SDP等其它芯片硬件处理。

软编用的是CPU处理,优点是调节能力比较强,相对于硬编,软编可以通过参数调整可以在同一码率下编码出清晰度更高的视频,此外软编可以兼容性更好,可以适配所有设备,但缺点是性能可能比较差,不如硬编速度快、功耗低。

软解相对于硬解,则是性能可能较差,不如硬解功耗低,但是兼容性更好,适配性更好。

目前移动应用大部分业务场景采用的编码策略是:手机端尽量采用硬编编码出一路高清的视频,将高清视频发送给服务器,由服务器再进行软编转码为多路码率的视频,再通过CDN分发给观看端。另外,安卓的一些低端机可能由于硬件问题对硬编支持不完善,这时候可以使用软编,或者硬编出错的情况可以切换为软编来兜底。当然有时候,对于一些性能优越的高端机型或者编码时长不多的业务场景也可以优先用软编,例如录制15秒短视频的场景,首先时间比较短并且机器性能高不怕CPU消耗,这样相同码率可以再提高清晰度。

对于大部分的应用场景的解码策略则主要采用硬解,用软解作为兜底。此外,对于一些硬解不支持的编码类型,可以使用软解,比如有的机型不支持H.265解码,则只能使用软解。

1.1.3、序列

H.264编码的方式可以这样理解:在视频中,一段时间内相邻的图像的像素、亮度与色温的差别通常很小。所以没必要去对一段时间内的每一幅图像都进行完整一帧的编码,而是可以选取这段时间的第一帧图像进行完整编码,而下一幅图像只记录与第一帧完整编码图像的像素、亮度与色温等特征的差别即可,以此类推循环下去。

什么叫序列呢?上述的这段时间内图像变化不大的图像集就可以称之为一个序列。序列可以理解为有相同特点的一段图像数据。但是如果某个图像与之前的图像变换很大,很难参考之前的帧来生成新的帧,那么就结束上一个序列,开始下一个序列。重复上述做法,生成新的一段序列。

1.1.4、帧类型

H.264结构中,一幅视频图像编码后的数据叫做一帧,一帧由一个片(slice)或多个片组成,一个片由一个或多个宏块(MB)组成,一个宏块由16×16的YUV数据组成。宏块是H.264编码的基本单位。

在H.264协议内定义了三种帧,分别是I帧、B帧与P帧。

1)I帧

I帧,即帧内编码图像帧,不参考其他图像帧,只利用本帧的信息进行编码。

I帧的特点:

它是一个全帧压缩编码帧,将全帧图像信息进行压缩编码及传输;解码时仅用I帧的数据就可重构完整图像;I帧描述了图像背景和运动主体的详情;I帧不需要参考其他画面而生成;I帧是P帧和B帧的参考帧,其质量直接影响到同组中以后各帧的质量;一般地,I帧是图像组GOP的基础帧(第一帧),在一组中只有一个I帧;I帧所占数据的信息量比较大。

I帧编码流程:

进行帧内预测,决定所采用的帧内预测模式;当前像素值减去预测值,得到残差;对残差进行变换和量化;变长编码和算术编码;重构图像并滤波,得到的图像作为其它帧的参考帧。

2)P帧

P帧,即预测编码图像帧,利用之前的I帧或P帧,采用运动预测的方式进行帧间预测编码。

P帧的预测与重构:P帧是以I帧为参考帧,在I帧中找出P帧『某点』的预测值和运动矢量,取预测差值和运动矢量一起传送。在接收端根据运动矢量从I帧中找出P帧『某点』的预测值并与差值相加以得到P帧『某点』样值,从而可得到完整的P帧。

P帧特点:

P帧是I帧后面相隔1-2帧的编码帧;P帧采用运动补偿的方法传送它与前面的I或P帧的差值及运动矢量(预测误差);P帧属于前向预测的帧间编码,它只参考前面最靠近它的I帧或P帧;P帧可以是其后面P帧的参考帧,也可以是其前后的B帧的参考帧;由于P帧是参考帧,它可能造成解码错误的扩散;由于是差值传送,P帧的压缩比较高。

P帧编码的基本流程:

进行运动估计,计算采用帧间编码模式的率失真函数值。P帧只参考前面的帧;进行帧内预测,选取率失真函数值最小的帧内模式与帧间模式比较,确定采用哪种编码模式;计算实际值和预测值的差值;对残差进行变换和量化;若编码,如果是帧间编码模式,编码运动矢量。

3)B帧

B帧,即双向预测编码图像帧,提供最高的压缩比,它既需要之前的图像帧(I帧或P帧),也需要后来的图像帧(P帧),采用运动预测的方式进行帧间双向预测编码。

B帧的预测与重构:B帧以前面的I或P帧和后面的P帧为参考帧,找出B帧『某点』的预测值和两个运动矢量,并取预测差值和运动矢量传送。接收端根据运动矢量在两个参考帧中找出预测值并与差值求和,得到B帧『某点』样值,从而可得到完整的B帧。

B帧特点:

B帧是由前面的I或P帧和后面的P帧来进行预测的;B帧传送的是它与前面的I或P帧和后面的P帧之间的预测误差及运动矢量;B帧是双向预测编码帧;B帧压缩比最高,因为它只反映两参考帧间运动主体的变化情况,预测比较准确;B帧不是参考帧,不会造成解码错误的扩散。

B帧编码的基本流程:

进行运动估计,计算采用帧间编码模式的率失真函数值。B帧可参考后面的帧;进行帧内预测,选取率失真函数值最小的帧内模式与帧间模式比较,确定采用哪种编码模式;计算实际值和预测值的差值;对残差进行变换和量化;若编码,如果是帧间编码模式,编码运动矢量。

率失真函数的相关简介:

有损压缩算法,性能由编码输出的比特率失真共同决定。

编码的目的:就是在保证一定视频质量的条件下尽量减少编码比特率,或在一定编码比特率限制条件下尽量地减小编码失真。

编码器的工作:根据以上率失真准则找到最佳编码参数。

信息论中率失真概念:在允许一定程度失真的条件下,能够把信源信息压缩到什么程度,即最少需要多少比特数才能描述信源。由此得到率失真函数:R(D)=minI(X,Y),它给出了限定失真条件下信息压缩允许的下界。但其在视频编码中难以应用,因为各种概率和条件概率未知,只能作为理论值。

视频编码中的率失真曲线:为了研究视频码率与视频质量的平衡。由于系统性,不能达到理论上的R(D)值,只能由不同的编码参数(如QP和选择的模式)得到有限的(R,D)可操作点,形成凸包络。

视频编码中的率失真优化(RDO):遍历所有的参数候选模式对视频进行编码,满足码率限制的失真最小的一组参数集作为最优的视频编码参数。每一层级都找出,最终使整体系统性能最优。这里假设了无相关性的独立优化,如相关性较强则共同优化。

1.1.5、DTS和PTS

DTS、PTS的概念如下所述:

DTS(DecodingTimeStamp):即解码时间戳,这个时间戳的意义在于告诉播放器该在什么时候解码这一帧的数据。PTS(PresentationTimeStamp):即显示时间戳,这个时间戳用来告诉播放器该在什么时候显示这一帧的数据。

需要注意的是:虽然DTS、PTS是用于指导播放端的行为,但它们是在编码的时候由编码器生成的。

当视频流中没有B帧时,通常DTS和PTS的顺序是一致的。但如果有B帧时,就回到了我们前面说的问题:解码顺序和播放顺序不一致了。

比如一个视频中,帧的显示顺序是:IBBP,现在我们需要在解码B帧时知道P帧中信息,因此这几帧在视频流中的顺序可能是:IPBB,这时候就体现出每帧都有DTS和PTS的作用了。DTS告诉我们该按什么顺序解码这几帧图像,PTS告诉我们该按什么顺序显示这几帧图像。顺序大概如下:

Stream:IPBB\nDTS:1234\nPTS:1423

1.1.6、GOP

GOP(GroupOfPictures)是图像组的概念,它指的是视频编码序列中两个I帧之间的距离。通常意义上的GOP由I帧开始,到下一个I帧之前的帧结束。严格意义上讲,这个I帧是一个IDR帧。

H.264使用的是封闭GOP(ClosedGOP),即在一个GOP中所有帧的解码不依赖该GOP外的其他帧,除了第一帧必须是I帧,其他帧可以是P帧或B帧。

上图中是一个GOP为15帧的例子,如果视频的帧率是15fps,那么这个GOP就是1s时长。

关键帧的间隔调节会影响GOP的长度,进而影响到读取GOP的速度,为防止运动变化,一个GOP组内帧数不宜取多。如果关键帧的间隔设置过大的话(GOP长度过大),在必须用到关键帧的场合就可能被迫使用B/P帧来代替,这就会降低画面质量。

1.1.7、IDR帧

IDR帧全称叫做InstantaneousDecoderRefresh,是I帧的一种。IDR帧的作用是立刻刷新,重新算一个新的序列开始编码,使错误不致传播。I帧有被跨帧参考的可能,但IDR帧不会。

比如:

IDR1P2B3B4P5B6B7I8B9B10P11B12B13P14B15B16

这里的B8可以跨过I8去参考P7。

IDR1P2B3B4P5B6B7IDR8B9B10P11B12B13P14B15B16

这里的B9就不可以参考IDR8前面的帧。

H.264引入IDR帧是为了解码的重同步,当解码器解码到IDR帧时,立即将参考帧队列清空,将已解码的数据全部输出或抛弃,重新查找参数集,开始一个新的序列。这样,如果前一个序列出现错误,在这里可以获得重新同步的机会,不会将错误传导下去。IDR帧之后的帧永远不会使用IDR帧之前的帧来解码。

所以总结下来,IDR帧有如下特性:

IDR帧一定是I帧,严格来说I帧不一定是IDR帧(但一般I帧就是IDR帧);对于IDR帧来说,在IDR帧之后的所有帧都不能引用任何IDR帧之前的帧的内容。与此相反,对于普通的I帧来说,位于其之后的B和P帧可以引用位于普通I帧之前的I帧(普通I帧有被跨帧参考的可能);播放器永远可以从一个IDR帧播放,因为在它之后没有任何帧引用之前的帧。因此,视频开头的I帧一定是IDR帧;一个封闭类GOP的开头的I帧也一定是IDR帧。

1.1.8、压缩方式

H.264采用的核心算法是『帧内压缩』和『帧间压缩』,帧内压缩是生成I帧的算法,帧间压缩是生成B帧和P帧的算法。

帧内压缩也称为空间压缩。当压缩一帧图像时,仅考虑本帧的数据而不考虑相邻帧之间的冗余信息,这实际上与静态图像压缩类似。帧内一般采用有损压缩算法,由于帧内压缩是编码一个完整的图像,所以可以独立的解码、显示。帧内压缩一般达不到很高的压缩率,跟编码JPEG差不多。

帧间压缩的原理是:相邻几帧的数据有很大的相关性,或者说前后两帧信息变化很小的特点。也即连续的视频其相邻帧之间具有冗余信息,根据这一特性,压缩相邻帧之间的冗余量就可以进一步提高压缩量,减小压缩比。帧间压缩也称为时间压缩,它通过比较时间轴上不同帧之间的数据进行压缩。帧间压缩一般是无损的。帧差值算法是一种典型的时间压缩法,它通过比较本帧与相邻帧之间的差异,仅记录本帧与其相邻帧的差值,这样可以大大减少数据量。

编码压缩的步骤大致如下:

分组,也就是将一系列变换不大的图像归为一个组,也就是一个序列,也就是GOP;定义帧,将每组的图像帧归分为I帧、P帧和B帧三种类型;预测帧,以I帧做为基础帧,以I帧预测P帧,再由I帧和P帧预测B帧;数据传输,最后将I帧数据与预测的差值信息进行存储和传输。

1.2、分层结构

H.264的主要目标是为了有高的视频压缩比和良好的网络亲和性,为了达成这两个目标,H.264的解决方案是将系统框架分为两个层面:『视频编码层面(VCL)』和『网络抽象层面(NAL)』。

视频编码层(VCL),是对视频编码核心算法过程、子宏块、宏块、片等概念的定义。这层主要是为了尽可能的独立于网络来高效的对视频内容进行编码。编码完成后,输出的数据是SODB(StringOfDataBits)。网络适配层(NAL),是对图像序列、图像等片级别以上的概念的定义。这层负责将VCL产生的比特字符串适配到各种各样的网络和多元环境中。该层将VCL层输出的SODB数据打包成RBSP(RawByteSequencePayload)。SODB是编码后的原始数据,RBSP是在原始编码数据后面添加了结尾比特,一个比特1和若干个比特0,用于字节对齐。然后再在RBSP头部加上NALHeader来组成一个一个的NAL单元。

从视频编码层(VCL)角度去看H.264的结构:

从网络适配层(NAL)角度去看H.264的结构:

1.3、编码工具

下图为一个典型的视频编码器。在进行当前信号编码时,编码器首先会产生对当前信号做预测的信号,称作预测信号(PredictedSignal),预测的方式可以是时间上的帧间预测(InterPrediction),亦即使用先前帧的信号做预测,或是空间上的帧内预测(IntraPrediction),亦即使用同一张帧之中相邻像素的信号做预测。得到预测信号后,编码器会将当前信号与预测信号相减得到残差信号(ResidualSignal),并只对残差信号进行编码,如此一来,可以去除一部分时间上或是空间上的冗余信息。接着,编码器并不会直接对残差信号进行编码,而是先将残差信号经过变换(通常为离散余弦变换)然后量化以进一步去除空间上和感知上的冗余信息。量化后得到的量化系数会再透过熵编码,去除统计上的冗余信息。

H.264的编解码流程如下:

1.3.1、帧内预测

一般来说,对于一帧图像,相邻两个像素的亮度和色度值之间通常是比较接近的,也就是颜色是逐渐变化的,不会一下子突变成完全不一样的颜色。而进行视频编码,目的就是利用这个相关性,来进行压缩。帧内预测就是基于这个原理。

假设现在我们要对一个像素X进行编码,在编码这个像素之前,我们找到它临近的像素作为参考像素X’,根据X’我们经过预测算法得到对像素X的预测值Xp,然后我们再用X减去Xp得到二者的残差D,并用这个残差D代替X进行编码,起到节省码率的作用。最后,我们还用预测值Xp和残差D相加得到X’用于下一个像素的预测。这个就是我们用帧内预测进行编码压缩的大体思想。

在实际编码中,我们固然可以按像素为单位进行预测,但这样效率比较低,所以在H.264标准中提出按照块为单位进行计算。一个宏块是16×16像素,它可以分成子块,最小是4×4的(这个大小是对于亮度编码而言,至于色度编码,4:2:0采样格式的色度宏块的长和宽都是亮度宏块的一半),这样能大大提高计算速度。

在帧内预测模式中,预测块是基于已编码重建的块和当前块形成的。对亮度像素而言,预测块用于4×4子块或者16×16宏块的相关操作。4×4亮度子块有9种可选预测模式,独立预测每一个4×4亮度子块,适用于带有大量细节的图像编码;16×16亮度块有4种预测模式,预测整个16×16亮度块,适用于平坦区域图像编码;色度块也有4种预测模式,类似于16×16亮度块预测模式。编码器通常选择使预测块和编码块之间差异最小的预测模式。

1.3.2、帧间预测

帧间预测就是时域预测,旨在消除时域冗余信息,简单点说就是利用之前编码过的图像来预测要编码的图像。其中涉及到两个重要的概念:运动估计和运动补偿。

运动估计是寻找当前编码的块在已编码的图像(参考帧)中的最佳对应块,并且计算出对应块的偏移(运动矢量)。

运动补偿是根据运动矢量和帧间预测方法,求得当前帧的估计值过程。其实就是将运动矢量参数贴到参考帧上获取当前帧。另外运动补偿是一个过程。

H.264帧间预测是利用已编码视频帧/场和基于块的运动补偿的预测模式。与以往标准帧间预测的区别在于块尺寸范围更广(从16×16到4×4)、亚像素运动矢量的使用(亮度采用1/4像素精度MV)及多参考帧的运用等等。

每个宏块(16×16像素)可以4种方式分割:一个16×16,两个16×8,两个8×16,四个8×8。其运动补偿也相应有四种。而8×8模式的每个子宏块还可以四种方式分割:一个8×8,两个4×8或两个8×4及4个4×4。这些分割和子宏块大大提高了各宏块之间的关联性。这种分割下的运动补偿则称为树状结构运动补偿。

每个分割或子宏块都有一个独立的运动补偿。每个MV必须被编码、传输,分割的选择也需编码到压缩比特流中。对大的分割尺寸而言,MV选择和分割类型只需少量的比特,但运动补偿残差在多细节区域能量将非常高。小尺寸分割运动补偿残差能量低,但需要较多的比特表征MV和分割选择。分割尺寸的选择影响了压缩性能。整体而言,大的分割尺寸适合平坦区域,而小尺寸适合多细节区域。

宏块的色度成分(Cr和Cb)则为相应亮度的一半(水平和垂直各一半)。色度块采用和亮度块同样的分割模式,只是尺寸减半(水平和垂直方向都减半)。例如,8×16的亮度块相应色度块尺寸为4×8,8×4亮度块相应色度块尺寸为4×2等等。色度块的MV也是通过相应亮度MV水平和垂直分量减半而得。

1.3.3、变换和量化

绝大多数图像都有一个共同的特征:平坦区域和内容缓慢变化区域占据一幅图像的大部分,而细节区域和内容突变区域则占小部分。也可以说,图像中直流和低频区占大部分,高频区占小部分。这样,空间域的图像变换到频域或所谓的变换域,会产生相关性很小的一些变换系数,并可对其进行压缩编码,即所谓的变换编码。

此外,为了减小图像编码的动态范围,一般也会进行量化。

在图像编码中,变换编码和量化从原理上讲是两个独立的过程。但在H.264中,将两个过程中的乘法合二为一,并进一步采用整数运算,减少编解码的运算量,提高图像压缩的实时性。

H.264对图像或预测残差采用了4×4整数离散余弦变换(DCT)技术,避免了以往标准中使用的通用8×8离散余弦变换逆变换经常出现的失配问题。量化过程根据图像的动态范围大小确定量化参数,既保留图像必要的细节,又减少码流。

1.3.4、熵编码

熵的大小与信源的概率模型有着密切的关系,各个符号出现的概率不同,信源的熵也不同。当信源中各事件是等概率分布时,熵具有极大值。信源的熵与其可能达到的最大值之间的差值反映了该信源所含有的冗余度。信源的冗余度越小,即每个符号所独立携带的信息量越大,那么传送相同的信息量所需要的序列长度越短,符号位越少。因此,数据压缩的一个基本的途径是去除信源的符号之间的相关性,尽可能地使序列成为无记忆的,即前一符号的出现不影响以后任何一个符号出现的概率。

利用信源的统计特性进行码率压缩的编码就称为熵编码,也叫统计编码。熵编码是无损压缩编码方法,它生成的码流可以经解码无失真地恢复出原数据。熵编码是建立在随机过程的统计特性基础上的。

视频编码常用的有两种:变长编码(哈夫曼编码)、算术编码。

H.264最后将结果进行熵编码,分为上下文自适应的变长编码(Context-basedAdaptiveVariable-LengthCoding,CAVLC)与上下文自适应的二进制算术编码(Context-basedAdaptiveBinaryArithmeticCoding,CABAC)。

在H.264的CAVLC(基于上下文自适应的可变长编码)中,通过根据已编码句法元素的情况动态调整编码中使用的码表,取得了极高的压缩比。CAVLC用于亮度和色度残差数据的编码。残差经过变换量化后的数据表现出如下特性:4×4块数据经过预测、变换、量化后,非零系数主要集中在低频部分,而高频系数大部分是零;量化后的数据经过zig-zag扫描,DC系数附近的非零系数值较大,而高频位置上的非零系数值大部分是+1和-1;相邻的4×4块的非零系数的数目是相关的。CAVLC充分利用残差经过整数变换、量化后数据的特性进行压缩,进一步减少数据中的冗余信息,为H.264卓越的编码效率奠定了基础。

算术编码的思想是用0到1的区间上的一个数来表示一个字符输入流,它的本质是为整个输入流分配一个码字,而不是给输入流中的每个字符分别指定码字。算术编码是用区间递进的方法来为输入流寻找这个码字的,它从于第一个符号确定的初始区间(0到1)开始,逐个字符地读入输入流,在每一个新的字符出现后递归地划分当前区间,划分的根据是各个字符的概率,将当前区间按照各个字符的概率划分成若干子区间,将当前字符对应的子2区间取出,作为处理下一个字符时的当前区间。到处理完最后一个字符后,得到了最终区间,在最终区间中任意挑选一个数作为输出。解码器按照和编码相同的方法和步骤工作,不同的是作为逆过程,解码器每划分一个子区间就得到输入流中的一个字符。在实际过程中,输入流中字符的概率分布是动态改变的,这需要维护一个概率表去记录概率变化的信息。在作递进计算时,通过对概率表中的值估计当前字符的概率,当前字符处理后,需要重新刷新概率表。这个过程表现为对输入流字符的自适应。编码器和解码器按照同样的方法估计和刷新概率表,从而保证编码后的码流能够顺利解码。

用哈夫曼编码,必须为所有可能的长度为N的序列设计和存储码书,这样做的复杂度随N呈指数增长。用算术编码则不需要预先为每个可能的信源序列指定码书。而是每当所确定区间的下限和上限有公共最高有效位时,就可以连续地得到比特。编码序列的长度可以和信源的长度一样长。因此,实际上,算术编码可以更接近熵率。

算术编码的另一个优点是可以简单地通过更新符号概率表来实现对信源统计特性的自适应。通过对不同上下文用不同的概率表也可以容易地实现条件编码。对于哈夫曼编码,则不得不基于更新的概率表重新设计码书,或对不同的上下文设计多个码表。

由于较高的编码效率和易于自适应,只要所涉及的计算量是能接受的,无疑算术编码比哈夫曼编码是一种更好的选择。

1.4、码流结构

1.4.1、原始码流

H.264原始码流(又称为裸流),是由一个接一个的NAL单元组成的(NALHeader加上RBSP组成一个NAL单元),结构如下图所示:

在网络传输的环境下,编码器将每个NAL各自独立、完整地放入一个分组,由于分组都有头部,解码器可以很方便地检测出NAL的分界,依次取出NAL进行解码。为了节省码流,H.264没有另外在NAL的头部设立表示起始的句法元素。但是如果编码数据是储存在介质(如DVD光盘)上,由于NAL是依次紧密排列,解码器将无法在数据流中分辨每个NAL的起始和终止,所以必须要有另外的机制来解决这个问题。

针对这个问题,H.264草案的附录B中指明了一种简单又高效的方案。当数据流是存储在介质上时,在每个NAL前添加起始码:0x000001。这就是我们常说的Annex-b码流格式

在某些类型的介质上,为了寻址的方便,要求数据流在长度上对齐,或必须是某个常数的倍数。考虑到这种情况,H.264建议在起始码前添加若干字节的0来填充,直到该NAL的长度符合要求。在这样的机制下,解码器在码流中检测起始码,作为一个NAL的起始标识,当检测到下一个起始码时当前NAL结束。H.264规定当检测到0x00000001时也可以表征上一个NAL的结束,下一个NAL开始,这是因为连着的三个字节的0中的任何一个字节的0要么属于起始码要么是起始码前面添加的0。

添加起始码是一个解决问题的很好的方法,但上面关于起始码的介绍还不完整,因为忽略了一个重要的问题:如果在NAL内部出现了0x000001或是0x00000001的序列怎么办?毫无疑问这种情况是致命的,解码器将把这些本来不是起始码的字节序列当作起始码,而错误地认为这里往后是一个新的NAL的开始,进而造成解码数据的错位!而我们做的大量实验证明,NAL内部经常会出现这样的字节序列。因为0x000001的情况是覆盖0x00000001的情况,所以下面值讨论如何处理0x000001即可。

于是H.264提出了另外一种机制,叫做防止竞争,在编码器编码完一个NAL时,应该检测是否出现下表左侧中的四个字节序列,以防止它们和起始码竞争。如果检测到这些序列存在,编码器将在最后一个字节前插入一个新的字节:0x03,从而使它们变成下表右侧的样子。当解码器在NAL内部检测到有0x000003的序列时,将把0x03抛弃,恢复原始数据。

0x000000→0x00000300\n0x000001→0x00000301\n0x000002→0x00000302\n0x000003→0x00000303

上表中的前两个序列我们前文中已经提到,第三个0x000002是作保留用,而第四个0x000003是为了保证解码器能正常工作,因为我们刚才提到,解码器恢复原始数据的方法是检测到0x000003就抛弃其中的0x03,这样当出现原始数据为0x000003时会破坏数据,所以必须也应该给这个序列插入0x03。

解码器在逐个字节地读一个NAL时并不同时对它解码,而是要通过起始码机制将整个NAL读进、计算出长度后再开始解码。

到此,我们就知道如何在原始码流里分割NAL单元了。接下来,我们再来了解每个NAL单元的结构。

1.4.2、NAL单元

NAL单元由NALHeader和RBSP构成。

NALHeader的结构如下:

forbidden_zero_bit,第0位,表示禁止位,一般为值为0,值为1表示语法错误。nal_ref_idc,第1-2位,表示当前NAL的优先级。取值范围为0-3,值越高,表示当前NAL越重要,需要优先受到保护。H.264规定如果当前NAL是属于参考帧的片,或是序列参数集,或是图像参数集这些重要的数据单位时,本句法元素必须大于0。但在大于0时具体该取何值,却没有进一步规定,通信双方可以灵活地制定策略。nal_unit_type,第3-7位,表示当前NAL单元的类型。具体类型定义如下表:

nal_unit_type=5时,表示当前NAL是IDR图像的一个片,在这种情况下,IDR图像中的每个片的nal_unit_type都应该等于5。注意IDR图像不能使用片分区。

1.4.3、RBSP

前面也介绍过,RBSP指原始字节载荷,它是NAL单元的数据部分的封装格式,封装的数据来自SODB(原始数据比特流)。SODB是编码后的原始数据,SODB经封装为RBSP后放入NAL的数据部分。

从SODB到RBSP的生成过程:

如果SODB内容是空的,生成的RBSP也是空的。否则,RBSP由如下的方式生成:1)RBSP的第一个字节直接取自SODB的第1-8个比特,(RBSP字节内的比特按照从左到右对应为从高到低的顺序排列,mostsignificant),以此类推,RBSP其余的每个字节都直接取自SODB的相应比特。RBSP的最后一个字节包含SODB的最后几个比特和rbsp_trailing_bits()。2)rbsp_trailing_bits()的第一个比特是1,接下来填充0,直到字节对齐。3)最后添加若干个cabac_zero_word,其值等于0x0000。

1.4.4、NAL单元的不同类型

上面讲到了NAL单元是有多种类型的,这里我们就其中重要的几种类型做一下讲解:

SPSPPSSEISlice

序列参数集、图像参数集与图像、片之间的关系:

1)序列参数集SPS

SPS中保存了一组编码后的图像序列的依赖的全局参数。

SPS中的信息至关重要,如果其中的数据丢失,解码过程就可能失败。SPS和PPS通常作为解码器的初始化参数。一般情况,SPS和PPS所在的NAL单元位于整个码流的起始位置,但是在某些场景下,在码率中间也可能出现这两种结构:

解码器要在码流中间开始解码。比如,直播流。编码器在编码过程中改变了码率的参数。比如,图像的分辨率。

SPS其中的关键参数包括:

profile_idc:表示当前H.264码流的编码档次。其中部分档次:基础档次:BaselineProfile(profile_idc值为66)主要档次:MainProfile(profile_idc值为77)扩展档次:ExtentedProfile(profile_idc值为88)level_idc,表示当前码流的编码等级。编码的等级定义了某种条件下的最大视频分辨率、最大视频帧率等参数。seq_parameter_set_id,表示当前的序列参数集的id。通过该id值,图像参数集PPS可以引用其关联的SPS中的参数。log2_max_frame_num_minus4,用于计算MaxFrameNum的值。计算公式为MaxFrameNum=2^(log2_max_frame_num_minus4+4)。变量MaxFrameNum表示frame_num的最大值,frame_num标识所属图像的解码顺序,在解码过程中它也是一个非常重要的变量。值得注意的是frame_num是循环计数的,即当它到达MaxFrameNum后又从0重新开始新一轮的计数。解码器必须要有机制检测这种循环,不然会引起类似千年虫的问题,在图像的顺序上造成混乱。pic_order_cnt_type,指明了POC(PictureOrderCount)的编码方法,POC标识图像的播放顺序。由于H.264使用了B帧预测,使得图像的解码顺序并不一定等于播放顺序,但它们之间存在一定的映射关系。POC可以由frame-num通过映射关系计算得来,也可以索性由编码器显式地传送。H.264中一共定义了三种POC的编码方法,这个句法元素就是用来通知解码器该用哪种方法来计算POC。log2_max_pic_order_cnt_lsb_minus4,用于计算MaxPicOrderCntLsb的值。计算公式为MaxPicOrderCntLsb=2^(log2_max_pic_order_cnt_lsb_minus4+4)。MaxPicOrderCntLsb表示POC的最大值,该变量在pic_order_cnt_type=0时使用。num_ref_frames,指定参考帧队列可能达到的最大长度,解码器依照这个句法元素的值开辟存储区,这个存储区用于存放已解码的参考帧,H.264规定最多可用16个参考帧,本句法元素的值最大为16。值得注意的是这个长度以帧为单位,如果在场模式下,应该相应地扩展一倍。gaps_in_frame_num_value_allowed_flag,这个句法元素等于1时,表示允许句法元素frame_num可以不连续。当传输信道堵塞严重时,编码器来不及将编码后的图像全部发出,这时允许丢弃若干帧图像。在正常情况下每一帧图像都有依次连续的frame_num值,解码器检查到如果frame_num不连续,便能确定有图像被编码器丢弃。这时,解码器必须启动错误掩藏的机制来近似地恢复这些图像,因为这些图像有可能被后续图像用作参考帧。当这个句法元素等于0时,表不允许frame_num不连续,即编码器在任何情况下都不能丢弃图像。这时,H.264允许解码器可以不去检查frame_num的连续性以减少计算量。这种情况下如果依然发生frame_num不连续,表示在传输中发生丢包,解码器会通过其他机制检测到丢包的发生,然后启动错误掩藏的恢复图像。pic_width_in_mbs_minus1,本句法元素加1后指明图像宽度,以宏块为单位:frame_width=16*(pic_width_in_mbs_minus1+1),宏块尺寸是16×16。通过这个句法元素解码器可以计算得到亮度分量以像素为单位的图像宽度:PicWidthInSamplesL=PicWidthInMbs*16,从而也可以得到色度分量以像素为单位的图像宽度:PicWidthInSamplesC=PicWidthInMbs*8。以上变量PicWidthInSamplesL、PicWidthInSamplesC分别表示图像的亮度、色度分量以像素为单位的宽。H.264将图像的大小在SPS中定义,意味着可以在通信过程中随着SPS动态地改变图像的大小,甚至可以将传送的图像剪裁后输出。pic_height_in_map_units_minus1,本句法元素加1后指明图像高度:PicHeightInMapUnits=pic_height_in_map_units_minus1+1,PicSizeInMapUnits=PicWidthInMbs*PicHeightInMapUnits。图像的高度的计算要比宽度的计算复杂,因为一个图像可以是帧也可以是场,从这个句法元素可以在帧模式和场模式下分别计算出出亮度、色度的高。值得注意的是,这里以map_unit为单位。frame_mbs_only_flag,本句法元素等于0时表示本序列中所有图像的编码模式都是帧,没有其他编码模式存在;本句法元素等于1时,表示本序列中图像的编码模式可能是帧,也可能是场或帧场自适应,某个图像具体是哪一种要由其他句法元素决定。结合map_unit的含义,这里给出上一个句法元素pic_height_in_map_units_minus1的进一步解析步骤:当frame_mbs_only_flag等于1,pic_height_in_map_units_minus1指的是一个picture中帧的高度;当frame_mbs_only_flag等于0,pic_height_in_map_units_minus1指的是一个picture中场的高度。所以可以得到如下以宏块为单位的图像高度:FrameHeightInMbs=(2–frame_mbs_only_flag)*PicHeightInMapUnits。PictureHeightInMbs=(2–frame_mbs_only_flag)*PicHeightInMapUnits。

2)图像参数集PPS

PPS中保存了每一帧编码后的图像所依赖的参数。

PPS其中的关键参数包括:

pic_parameter_set_id,表示当前PPS的id,相关联的各片通过这个id来引用对应的PPS参数。seq_parameter_set_id,表示当前PPS引用的SPS的id。entropy_coding_mode_flag,表示熵编码的选择,本句法元素为0时,表示熵编码使用CAVLC,本句法元素为1时表示熵编码使用CABAC。pic_order_present_flag,POC的三种计算方法在片层还各需要用一些句法元素作为参数,本句法元素等于1时表示在片头会有句法元素表示这些参数;本句法元素等于0时,表示片头不会给出这些参数,这些参数使用默认值。num_slice_groups_minus1,本句法元素加1后表示图像中片组的个数。H.264中没有专门的句法元素用于表示是否使用片组模式,当本句法元素等于0(即只有一个片组),表示不使用片组模式,后面也不会跟有用于计算片组映射的句法元素。slice_group_map_type,当num_slice_group_minus1大于0,即使用片组模式时,本句法元素出现在码流中,用以表示片组分割类型。map_units的定义:帧场自适应模式时,map_units指的是宏块对;场模式时,map_units指的是宏块;帧模式时,map_units指的是与宏块对相类似的,上下两个连续宏块的组合体。当frame_mbs_only_flag等于1时,map_units指的就是宏块;当frame_mbs_only_falg等于0时:num_ref_idx_l0_active_minus1,加1后表示目前参考帧队列的长度,即有多少个参考帧(包括短期和长期)。值得注意的是,当目前解码图像是场模式下,参考帧队列的长度应该是本句法元素再乘以2,因为场模式下各帧必须被分解以场对形式存在。(这里所说的场模式包括图像的场及帧场自适应下的处于场模式的宏块对)本句法元素的值有可能在片头被重载。在序列参数集中有句法元素num_ref_frames也是跟参考帧队列有关,它们的区别是num_ref_frames表示参考帧队列的最大值,解码器用它的值来分配内存空间;num_ref_idx_l0_active_minus1表示在这个队列中当前实际的、已存在的参考帧数目,这从它的名字active中也可以看出来。这个句法元素是H.264中最重要的句法元素之一,编码器要通知解码器某个运动矢量所指向的是哪个参考图像时,并不是直接传送该图像的编号,而是传送该图像在参考帧队列中的序号。这个序号并不是在码流中传送的,而是编码器和解码器同步地、用相同的方法将参考图像放入队列,从而获得一个序号。这个队列在每解一个图像,甚至是每个片后都会动态地更新。维护参考帧队列是编解码器十分重要的工作,而本句法元素是维护参考帧队列的重要依据。参考帧队列的复杂的维护机制是H.264重要也是很有特色的组成部分。num_ref_idx_l1_active_minus1与上一个句法元素的语义一致,只是本句法元素用于list1,而上一句法元素用于list0。constrained_intra_pred_flag,在P和B片中,帧内编码的宏块的邻近宏块可能是采用的帧间编码。当本句法元素等于1时,表示帧内编码的宏块不能用帧间编码的宏块的像素作为自己的预测,即帧内编码的宏块只能用邻近帧内编码的宏块的像素作为自己的预测;而本句法元素等于0时,表示不存在这种限制。

3)补充增强信息SEI

SEI即补充增强信息(SupplementalEnhancementInformation),属于码流范畴,它提供了向视频码流中加入额外信息的方法,是H.264标准的特性之一。

SEI的基本特征如下:

并非解码过程的必须选项;可能对解码过程(容错、纠错)有帮助;集成在视频码流中。

也就是说,视频编码器在输出视频码流的时候,可以不提供SEI信息。虽然在视频的传输过程、解封装、解码这些环节,都可能因为某种原因丢弃SEI内容,但在视频内容的生成端和传输过程中,是可以插入SEI信息的。这些插入的信息,和其他视频内容一同经过传输链路到达消费端。

SEI是一种NAL单元类型。它的结构大致如下:

//H.264\n0x06,n个FF字节+1个非FF字节,16字节UUID,userData,0x80或0x0080

开始码:H.264中SEI的NAL单元类型是0x06;H.264中SEI的NAL单元类型是0x4E、0x01。自定义:SEI除了开始的NAL单元类型字段外,还存在不同的子类型。H.264中第2个字节或者H.265中第3个字节0x05表示后续是自定义数据。负载长度:表示后续跟着的自定义数据长度,计算方法是:n*255+XY,也就是将数据长度减去255,有多少个就写多少个FF,剩下的如果不为0,再写一个字节。负载内容:负载内容是UUID+payloadcontent。UUID固定16个字节,用于区分不同的业务。payloadcontent表示自定义数据。

4)片Slice

一帧图像可编码成一个或者多个片,每片包含整数个宏块,分片的目的是为了限制错误码的扩散和传输,使编码片相互间保持独立。

片的结构:

Slice中的关键参数包括:

slice_type,表示当前片的类型。具体类型如下。其中,IDR图像时,slice_type等于2、4、7、9。pic_parameter_set_id,表示引用的PPS的id。frame_num,表示解码顺序。每个参考帧都有一个依次连续的frame_num作为它们的标识,这指明了各图像的解码顺序。但事实上非参考帧的片头也会出现frame_num。只是当该个图像是参考帧时,它所携带的这个句法元素在解码时才有意义。H.264对frame_num的值作了如下规定:当参数集中的句法元素gaps_in_frame_num_value_allowed_flag不为1时,每个图像的frame_num值是它前一个参考帧的frame_num值增加1。field_pic_flag,这是在片层标识图像编码模式的唯一一个句法元素。所谓的编码模式是指的帧编码、场编码、帧场自适应编码。idr_pic_id,IDR图像的标识。不同的IDR图像有不同的idr_pic_id值。值得注意的是,IDR图像有不等价于I图像,只有在作为IDR图像的I帧才有这个句法元素,在场模式下,IDR帧的两个场有相同的idr_pic_id值。idr_pic_id的取值范围是[0,65535],和frame_num类似,当它的值超出这个范围时,它会以循环的方式重新开始计数。pic_order_cnt_lsb,在POC的第一种算法中本句法元素来计算POC值,在POC的第一种算法中是显式地传递POC的值,而其他两种算法是通过frame_num来映射POC的值。

关于什么是网站源码分享编码,网站源码分类的介绍到此结束,希望对大家有所帮助。

Published by

风君子

独自遨游何稽首 揭天掀地慰生平