美文网首页
音视频流媒体开发【十八】H264编码原理

音视频流媒体开发【十八】H264编码原理

作者: AlanGe | 来源:发表于2023-03-04 08:20 被阅读0次

    音视频流媒体开发-目录

    重点:

    为什么存在编码延迟
    什么影响编码效率

    H264压缩技术

    1. 帧内预测压缩,解决的是空域数据冗余问题。什么是空域数据,就是这幅图⾥数据在宽⾼空间内包含了很多颜⾊,光亮。⼈的⾁眼很难察觉的数据。 对于这些数据,我们可以认作冗余。直接压缩掉的。
    2. 帧间预测压缩,解决的是时域数据冗余问题。在我们之前举例说明过,摄像头在⼀段时间内所捕捉的数据没有较⼤的变化,我们针对这⼀时间内的相同的数据压缩掉。这叫时域数据压缩。
    3. 整数离散余弦变换(DCT),将空间上的相关性变为频域上⽆关的数据然后进⾏量化。这个⽐较抽象。这个跟数学是紧密联系在⼀起的。如果对傅⾥叶变换理解的⽐较好的。对这个会理解的⽐较快。如果对傅⾥叶变换不了解的。可能有稍许困难。傅⾥叶变换可以把⼀个复杂波形图变换成许多的正弦波。只是他们之间的频率不⼀样。以及振幅也不⼀样。如果它们在频率上没有⼀致性那么我们就可以对他进⾏压缩处理。
    4. CABAC压缩:⽆损压缩。

    H.264 两层编码体系

    H.264 的码流结构,相⽐于之前的视频标准,虽然都采⽤了基于块匹配的混合编 码⽅式,但在层次划分上有着较⼤的改进。从 H.261 到 H.263 视频编码标准中,将码流的结构划分为四个层次[19],按照从上到下的顺序依次为:图像层(picture layer)、块组层(GOB layer)、宏块层(macroblock layer)和块层(block layer)。从层次的划分上可以看出没有针对⽹络传输部分进⾏层级的定义和专⻔设定,H.264 则针对视频数据经由⽹络传输的需求,从功能上划分为两层,分别是视频编码层(VCL)以及⽹络抽象层(NAL),如图 2.1 所示。

    VCL 层即是视频编码层[20],进⾏视频数据的压缩和编码,它包含着以块为基本单位的运动补偿等混合编码技术。经过编码后的数据按照 NAL 规定的格式封装成NAL 单元,实现了在⽹络中传输的数据格式的统⼀,⽅便和⽹络协议进⾏对接。NAL单元由两部分组成,NAL 头部和包含着编码后视频数据的载荷(RBSP:Raw Byte Sequence Payload)[21]。NAL 使⽤下层⽹络的分段⽅式来对数据进⾏封装,其中包含如何组成⼀帧数据、信道的信令和如何利⽤定时信息时间戳等。

    H.264 编解码基本框架

    作为视频编解码标准 H.264,它对编码后的视频⽐特流的句法和如何对⽐特流进⾏解释作出了规定,但并没有具体地规定如何去实现⼀个编解码器。在⼯程中使⽤的编解码器建⽴在相同的语法和框架之下,但对于如何实现并没有做具体的要求,这样给⼯程实现提供着更⼤的发展空间,有利于针对不同硬件设备进⾏具体实现,有着较⼤的灵活性

    编码器框图

    H.264 的编码器框图如图 2.2 所示,可以看出由图中实现和虚线分为两部分,前向部分和后向部分,后向表示的是重建分⽀。

    • 编码器(前向路径):编码器在输⼊的视频帧中以 16x16 的像素宏块为单位做帧内或者帧间处理,随后⽣成预测宏块 P,对原始块和预测宏块做残差,随后对残差数据进⾏变换和量化等操作。对于帧内编码,其预测值通过正在被编码的当前帧先前被编码数据,在进⾏重建宏块像素后求得预测宏块 P;对于帧间编码由对参考帧进⾏运动估计后求得运动⽮量,并在进⾏运动补偿后求得预测值。
    图 2.2 H.264 编码框架图
    • 编码器(重建路径):解码器相对于编码器⽽⾔,对视频数据进⾏解码操作和 反量化、反变换等等。在解码过程中通过⼀系列操作重建图像后⽤于对下⼀帧图像进⾏预测。经过变换和量化后的宏块得出数据进⾏反量化和⽅变换等操作得出差分宏块,由于在反量化和反变换过程为了⽅便计算,结合不同的量化步⻓对计算系数进⾏处理,所以得到的重建后宏块和原始块并不是完全相同。经过对预测宏块与残差宏块相加等操作后得出重建参考帧。

    关键技术点:

    • 帧内预测压缩,解决的是空域数据冗余问题。
    • 帧间预测压缩(运动估计与补偿),解决的是时域数据冗徐问题。
    • 整数离散余弦变换(DCT),将空间上的相关性变为频域上⽆关的数据然后进⾏量化。
    • CABAC压缩。

    解码器框图

    H.264 的解码器框图如图 2.3 所示。由于 H.264 中加⼊了视频流在⽹络传输的⽀持,解码器从后侧输⼊的数据包格式为 NAL,码流⽅向从右⾄左。解码器在⽹络抽象层 NAL 中获取到压缩后的码流,然后从码流中提取⼀帧数据,经过熵编码、重新排序、反量化和反变换后作为加法器的输⼊,其中包含着重建图像所需要的残差数据和运动⽮量等进⾏解码操作所必需的信息。接下来,解码器通过在码流中解码所获得的头部信息创建出⼀个预测块 P,将之前求得的残差信息 D 和预测块 P 求和,求得图像块数据 uF’n,然后经过去块滤波,SAO 等操作获得重建图像的解码块 F。

    图 2.3 H.264 解码框架图

    H.264 的关键技术

    H.264 相⽐于之前的视频编码标准增加了较多的新技术,以适应视频发展的需求,提供了⼀种更加⾼效的视频编码标准,并有着较好的⽹络适⽤性。标准中使⽤到的主要技术包括:运动估计、运动补偿、变换、量化、滤波、SAO 和熵编码。视频信息中包含最多的冗余信息是空间冗余信息和时间冗余信息,编码中采⽤帧内编码和帧间编码的⽅式较⼤程度的减少了冗余信息。算法复杂度更为复杂,在计算⽅式上更加多样,可以更好的针对不同图像的特点进⾏分析使⽤,为视频压缩提供更好的压缩性能和⽅案选择,同时提升图像压缩质量。但是⼤量复杂的算法也增加了编码需要的时间,从⽽加⼤了软件和硬件的实现难度。

    下⾯对 H.264 的关键技术进⾏介绍。

    帧内预测

    为什么要有帧内预测?因为⼀般来说,对于⼀幅图像,相邻的两个像素的亮度和⾊度值之间经常是⽐较接近的,也就是颜⾊是逐渐变化的,不会⼀下⼦突变成完全不⼀样的颜⾊。⽽进⾏视频编码,⽬的就是利⽤这个相关性,来进⾏压缩。

    很好理解,存储⼀个像素的亮度值可能需要8个bit,但是如果相邻的两个像素变化不⼤,我存储⼀个像素的原始值,以及第⼆个像素相对第⼀个像素的变化值,那么第⼆个值我可能⽤2个bit就够了,这就节约了很多的空间。⽽节约存储消耗的bit数,也就是节约码率,贯穿了H.264编码器的所有过程,不管是帧内预测、帧间预测、变换、量化、熵编码,⼀切的⼀切都是为这个⽬的服务的,明⽩了这⼀点,我们就能轻易的理解H.264编码器,所有看上去复杂难懂的地⽅,都是为了⼀个⽬的——节约码率。

    说回到帧内预测,帧内预测的流程⻅下图

    image.png

    ⾸先,是上图中蓝⾊的部分,假设现在我需要对⼀个像素X进⾏编码,在编码这个像素之前,先假设我已经有⼀个参考像素X'了,这个参考像素与同⼀帧的临近像素有关,根据参考像素X'的值,我得到了⼀个预测值Xp。

    然后,是上图中红⾊的部分,我⽤编码的像素X减去预测值Xp,得到了残差d,这个残差d代替原始值X被编码进最终的图像,起到了节省码率的作⽤

    最后,是上图中⿊⾊的部分,残差d和预测值Xp相加,得到了X',⽤于下⼀个像素的预测。

    总结起来就是三步:

    1. 以同⼀帧图像内的临近像素作为参考,计算预测值Xp
    2. 原始值X和预测值Xp的差值d,被传递到解码端
    3. 解码端接收到差值d,将其与预测值Xp相加,就得到了“原始值”X',X'=Xp+d,传输的是Xp值和d。

    步骤很简单,但是⾥⾯有⼏个问题要明确,⾸先要知道的是,我们固然可以按像素来进⾏预测,但是这样太费事了,要计算很多次,⽽且由于帧内预测的特性,你要预测当前的像素,能参考的像素只能是它的临近像素,也就是前⾯已经编码完成的,它后⾯的像素还没有编到,所以⾃然是不能⽤来编码当前像素的,因此我们只能⼀个像素⼀个像素进⾏编码,编完了⼀个才能编下⼀个,这样显然会很慢。

    于是H.264标准中提出按块进⾏计算,⼀个宏块是16x16像素,然后它可以分成⼦块,最⼩是4x4的(这个⼤⼩是对于亮度编码⽽⾔,⾄于⾊度编码,4:2:0格式的⾊度宏块的⻓和宽都是亮度宏块的⼀半),这样也能⼤⼤提⾼计算速度。因此下⾯提到的“值”可以代表“像素”也可以代表“块”,从原理上来说是⼀样的,⽽实际采⽤的是“块”,因此我也就统⼀⽤块这个词了。

    我们从上⾯三个步骤从头开始⼀点点找问题

    1. 这个参考值X'是怎么来的?答案是在第三步⾥,使⽤Xp和传递过来的残差d相加得到的,这个X'⽤于后⾯的块编码时的参考。
    2. 预测值Xp是怎么从X'获得的?答案是根据X',通过某个公式计算得到的。⽽这个X'并不是只有⼀个块,⽽是有左侧、左上、正上、右上⼀共四个块作为参考。
    3. 上⾯提到的这个某个公式是什么?根据⽩⽪书,Intra(帧内预测)有两种,⼀种是4x4⼤⼩的亮度块,⼀种是16x16⼤⼩的亮度块。

    对于4x4⼤⼩的亮度块,我们有9种预测模式,如下图所示

    • 对于预测模式0(vertical),当前块的⼗六个像素值,完全由其上⽅块最后⼀⾏的那四个像素值决定,
    • 第⼀列所有的Xp值都等于A,第⼆排都等于B,以此类推。预测模式1(horizontal)也是⼀样,完全由图中IJKL四个像素值决定。
    • 对于预测模式2(DC),则⼗六个像素值完全相等,等于ABCDIJKL这⼋个像素的平均值。
    • 对于预测模式3-8,倾斜⽅向的,各个像素是由A到L像素通过权重不等的公式加权计算的

    ⽐如对于模式3(diagnal down-left)来说,a=(A+2B+C+2)/4,这⾥+2代表四舍五⼊,b和e=(B+2C+D)/4,cfi=(C+2D+E+2)/4,dgjm=(D+2E+F+2)/4,hkn=(E+2F+G+2)/4,lo=(F+2G+H+2)/4,p=(G+2H+H+2)/4=(G+3H+2)/4

    对于模式4(diag down-right),加权系数也是(1,2,1)/4,afkp⽤IMA三个像素计算,以此类推

    对于模式5(vertical right),aj=(M+A+1)/2,同理bk是AB均值,cl是BC均值,d是CD均值(因为这⼏个像素延⻓线都不在预测⽤的13个像素⾥⾯)。en在M的延⻓线上,所以等于(I+2M+A+2)/4,同理fo\gp\h\i\m都可以计算出来

    同理模式6、7、8也基本跟5⼀样,注意对于模式8来说,klmnop四个像素都等于L,因为其延⻓线在L的下⾯和前⾯,没有可以平均的像素,于是只好⽤L⼀个值代替了。

    还有⼀点要注意的是,这⾥⾯A到L的像素有的没有怎么办?对于模式2(DC),有什么⽤什么,反正是求均值就对了。其余的模式就必须⽤到的那⼏个像素都存在才⾏(EFGH四个像素可以不存在,此时认为都等于D)

    最终,我们要选择哪种模式进⾏预测?为了在后⾯节约码率考虑,当然是预测的越准越好,也就是选择Xp和X'差距最⼩为好。⽽评判这个差距其实也有好⼏种算法,⽐如SAD、SATD等,等到了变换那⾥再详细说。总之我们⽤⼀个公式把上⾯9种模式的预测都评价了⼀番,选出⾥⾯最好的⼀种,作为4x4帧内预测的选择。

    即是有损在于预测模型的误差。

    我们注意到这⾥有9种模式,之后要进⾏编码的话,我们除了把残差编进去,总得知道我预测的时候⽤了哪种模式吧,9这个数就尴尬了,因为刚好三个⽐特可以表示8种,四个⽐特可以表示16种,所以3bit不够4bit⼜浪费了。怎么办呢?有个⽅法就⽐较巧妙,我有1bit⽤来表示我当前⽤的模式和前⾯的是不是⼀样的,因为经常有这样的情况,我前⾯块⽤的预测⽅向和现在这个块⽤的预测⽅向⼀样(⽐如物体边缘是⼀条直线,那么对应的那⼏个块⽤的预测⽅向很可能都是⼀样的),如果⼀样,我只⽤1bit就⾜够存储了,如果不⼀样,我再⽤⽤4个bit存储,也就达到了节约bit的⽬的。

    说完了4x4的亮度块,我们看看16x16⼤⼩的亮度块。16x16亮度块有四种模式,如下图

    前⾯三种很好理解了,重点是第四种plane的计算⽅式,从图上看⼤概能理解,不过具体算法我还不是很理解。

    我们假设左上⻆起,上⽅那⼀⾏是17个像素是a1 b2 c3 d4 e5 f6 g7 h8 i9 j8 k7 l6 m5 n4 o3 p2 q1,⽤这17个像素计算⼀个H值。

    我在上⾯标了191的数字,有数字相同的8对像素,后⾯计算的时候,都是⼀对对的计算的。i9;j8 - h8;k7 - g7;l6 - f6;m5 - e5;n4 - d4;o3 - c3;p2 - b2;q1 - a1,这九对分别乘以权重0到8(也就是i9这个像素没有⽤到),⽽最左边和最右边两个像素权重最⼤。

    同理V值,也是⼀样的算法,从上到下像素记作带'的(当然a1' = a1)。

    然后计算⼀个A值,它等于右上⻆(q1)和左下⻆(V值计算时候对应的那个q1')的和乘以16计算⼀个B值,它等于(5H+32)/64,计算⼀个C值,C=(5V+32)/64,这种后⾯加了32⼜除以64的,其实都是⽤来四舍五⼊的。

    然后就能计算我们的预测值了。
    i00 = A - 7 * B - 7 * C + 16
    pix[0][0] = i00 / 32(超出0到255范围的要截断成0或者255)

    然后计算第⼀⾏⼗六个像素分别是i00 + B到i00 + 15*B,然后除以32(然后截断到0~255)

    第⼆⾏是在第⼀⾏基础上加了⼀个C,⼀直到第16⾏,加了15个C,于是这256个像素都算出来了。

    这就是plane⽅式的算法。算出来结果就跟下图⼀样。

    对于⾊度块是亮度块的四分之⼀,也就是8x8的,那就只有⼀种了,预测模式也跟亮度16x16块的类似,有四种,只不过具体的序号不⼀样⽽已。是0代表DC,1代表horizontal,2代表vertical,3代表plane。plane的算法和上⾯16x16的类似,只不过系数变了,⽽且两个⾊度块⽤的⽅式⼀定都是⼀样的。好了,我们回到前⾯的帧内预测的步骤,还有⼏个问题没有解决。

    4、重建值X'和原始值是不是⼀样的?答案是不⼀样,因为d在传到解码端的过程中经过了量化、变换、反变换和反量化,有了精度的损失,因此Xp+d得到的X'跟原始的X是不⼀样的。这也是为什么要⽤重建值X'来得到Xp⽽不是⽤原始值X。因为解码器那边在解码图像的时候,⽤到的是有损的X'获得Xp,如果编码器⽤⽆损的X来得到Xp的话,那么得到的残差d在编码侧和解码侧就不⼀致了,这个误差扩散到了下⼀个块⾥。

    好了,这样上⾯的步骤就组成了⼀个循环,可以⼀直编码下去了,当然最开始没有参考的那个块,其预测值Xp⼜是怎么来的呢?可以有⼀种⽅式叫做DC128,也就是认为这个块的每个像素都是0x80,然后依据此来计算。

    实际上对于⼀个16x16的宏块,上⾯这个16x16的四种模式和它16个⼦块(⼤⼩4x4)的9种模式都会算⼀遍,然后⽤16个⼦块的SATD最⼩值和16x16的四种模式的SATD最⼩值⽐较,选择更⼩的那个,所以这⾥⾯计算量还是很⼤的。x264源代码⾥有很多节约计算量的设计,不过这些就得看代码了,⽩⽪书⾥⾯是没有的,以后再做研究。

    另外x264⾥⾯还有Intra8x8亮度块的模式,这种我认为应该也是为了找到⼀个预测最准的模式⽽加进去的,⽽且也不常⽤,所以也就不多介绍了。

    回过头来看最开始的那个步骤图,跟⽩⽪书⾥给的编码器流程图,是不是正是编码器的⼀部分呢?

    进⾏帧内预测,⾸先确定预测块和当前块,对于当前块⽽⾔,和它⽽⾔相邻的宏块作为预测块。将帧内预测的计算放在变换编码之前,可以提升帧内编码的效率。对于不同的图像信息,有着不同的特点:

    1. 图像中有些区域较为平坦,这类区域附近的颜⾊变化很⼩,像素值变化幅度很低,可以⽤相邻像素块的值来表示当前像素块的值;
    2. 有些区域在⼀定⽅向上变化较⼩,就可以利⽤这⼀特点,使⽤同⼀⽅向上的像素块进⾏预测。

    像素点由亮度信号和⾊度信息表示:

    • 对于亮度信号,将块的⼤⼩划分为 4x4和 16x16 两种形式,以此为基本单位进⾏帧内预测。不同的块划分形式对应着不同的预测模式,有 9 种预测模式对应于 4x4 ⽅式,有 4 种可以选择的⽅式对应于16x16⽅式。
    • 对于⾊度信号⽽⾔,划分为 8x8 块,有 4 中模式可以选择。

    以上是对图像数据进⾏预测操作然后输出,I_PCM 模式能够使帧内预测模块不对图像数据进⾏预测操作⽽直接输出,⽐如当图像信息杂乱⽆章,相关性很⼩的情形下,不进⾏帧内预测操作会⽐使⽤不同的预测⽅式更加简便⽽且有效,同时在此模式下对图像的质量不造成影响。

    帧内预测操作主要是:⾸先使⽤⼀些预测⽅式,对图像进⾏预测,接着进⾏判断,选择出最优的预测⽅式,最后根据确定出的预测⽅式对宏块进⾏预测。

    1. 亮度块预测 4x4

    对于 4x4 亮度块,有沿着不同⽅向的 8 种预测⽅式以及 DC 帧内预测⽅式。如图 2.4 中表示了 8 个不同的⽅向,箭头的指向代表着 8 种预测⽅向。可以看出都是沿着不同的⽅向通过相邻块进⾏预测。在H.264 中通过语法元素的值来表示采⽤哪种帧内预测⽅式,以便于解码端使⽤。

    图 2.4 4x4 亮度块预测⽅向

    由图 2.5 中可以看出每⼀像素点都可以使⽤与当前像素块临近的 17 的像素进⾏ 计算,如对每个参与运算的像素点使⽤不同的权重,然后像素点值相加。其中 a、b⾄ p 表示等待被预测的像素点,A、B ⾄M 表示已经经过编码的像素点,选择⼀种⽅向进⾏预测,就意味着使⽤不同⽅向的像素点并使⽤相应的权值进⾏计算,例如 m的值可以通过(J+ZK+L+2)/4 来进⾏预测,也可以使⽤ DC 预测模式,经由(A+B+C+D+I+J+K+L)/8 进⾏预测。

    图 2.5 4x4 块相邻像素
    2. 亮度块预测

    16x16 当对 16x16 亮度块进⾏预测时,有 4 中不同的预测⽅向可以选择,分别是垂直⽅向、⽔平⽅向、平⾯⽅向和直流⽅向。4x4 亮度块附近有 17 个像素点,16x16 亮度块附近有 33 个像素点,可以⽤于预测计算。为了标志 33 个点,对其划分坐标系,横坐标为-1 时,纵坐标从-1 到 15 共 17 个点,当纵坐标为-1 时,横坐标范围从 0 到 15 共16 个点。使⽤这 33 个像素点的前提是这些相邻的像素点都可⽤,如果被预测的宏块在图像的边缘区域或者相邻的宏块和当前宏块处在不同的 slice 中时,则不能使⽤。

    帧间预测

    视频帧分为 I、P 和 B 帧三种帧类型[23],I 帧为信息帧,只使⽤帧内预测,解码 时不依赖与其他帧,P帧为单向预测帧,预测时需要使⽤ I 帧或者 P 帧信息,B 帧为双向预测帧,可以使⽤ I 帧、P 帧和 B 帧进⾏预测,表示⼀帧图像所⽤的数据量最⼩,压缩率最⾼,但是解码时运算量较⼤。通常来说,I 帧的压缩⽐为 7,与 JPG 格式的图⽚压缩率基本⼀致,P 帧压缩率为 20 左右,⽽ B 帧可以达到 50。由此可以看出帧间预测可以很⼤程度的对视频⽂件进⾏压缩。

    由于运动是连续的,所以在时间上连续的相邻帧中的⼤部分元素是相同的,具有时间相关性,利⽤帧与帧之间的时间相关性就可以对图像进⾏压缩。物体运动时,通常是由图像中的⼀个位置移动到另外⼀个位置,运动的主体不变,在移动范围较⼩的前提下,运动背景也具有⾼度相似性,利⽤这⼀特性,选定参考帧后,设定坐标系,⽐较参考帧和被预测帧,通过运动物体在参考帧中的坐标和被预测帧中的坐标计算出运动⽮量,使⽤运动⽮量结合参考帧中的信息就可以表示出当前图像。

    在帧内预测时,将⼀副图像划分成⼏种类型的预测块,对于 16x16 的分块,可以 划分为 1 个 16x16 的块,或者 2 个 16x8 块,或者 4 个 8x8 块;⽽对于 8x8 的分块可以划分为 2 个 8x4 块或者、2 个 4x8块或者 4 个 4x4 块。H.264 中可以划分出多种分块模式,其中最⼩的分块为 4x4,更加准确的表达出⼩于 4x4 块的物体的运动情况的同时(即计算出的运动⽮量更加精确),减少了编码数据量。

    帧间预测中,按照分块标准将⼀帧图像划分成不同⼤⼩的分块,通过参考帧中⼤⼩相同的宏块进⾏预测得出编码的宏块,其中计算产⽣运动⽮量即两个宏块之间的偏移距离。然后使⽤参考宏块和预测块计算出残差数据,选⽤更⼩的像素为基准进⾏运动估计,求得的运动⽮量更加准确,运动⽮量精度的提⾼能够减少残差,如此能够使得在提⾼了压缩率的同时,更好的保证了视频质量。H.264 中通过使⽤块匹配算法来进⾏运动⽮量的估计,通常对块以整像素为单位进⾏运动估计,也可以以亮度分量的1/4 像素为单位或者⾊度分量的 1/8 像素为单位进⾏运动估计。这些⾮整像素是通过插值计算的⽅式求得的,对图像进⾏了细化。在选择匹配块时,需要计算参考宏块和预测块之间的相似度,判断其相似度使⽤归⼀化的⼆维相关函数,两个块之间亮度像素点的均⽅差 MSE,或者计算两个块之间的绝对值的平均值 MAD 等。由于 MAD的计算过程只有加减和求绝对值运算,没有乘法和除法操作,计算⽤时少,⽅便简单,因此多使⽤ MAD 的计算⽅式。其公式如 2-1 所示:

    帧间预测⾸先计算运动⽮量和参考索引值。在 skip 模式下参考索引的值为 0, ⽽其他模式下编码模式所使⽤的参考索引都是通过计算由编码码流获得。运动⽮量的运动搜索算法很多,主要有全局搜索算法(Full Search Method,FS)、三步法(Three Step Search,TSS),四步法(Four Step Search,FSS)、⼆维对数法(Two Dimensional Logarithmic,TDL)、交叉法(Cross Serach Algorithm,CSA)、新三步法(New Three Step Search,NTSS)、新四步法(New Four Step Search,NFSS)和菱形法(Diamond Search,DS)等。

    全搜索算法是对采⽤此种预测算法的块,在预测帧中使⽤ SAD 对帧内所有预测块进⾏计算,求出SAD 最⼩的块,作为算法作⽤块的预测块,随后根据选定的不同像素标准,如全像素或者亚像素,求出两个块之间的坐标偏移量即运动⽮量,再求得两个块之间的残差。全搜索算法的优点是可以从预测帧中选择出最准确的预测块,为全局最优结果,预测精度很⾼。但是这种算法每确定⼀个块的预测块都需要对预测帧中的所有块做运算,算法复杂度⾼,搜索范围太⼴,⼤⼤增加了视频压缩时间。

    三步搜索算法是快速搜索算法中较为常⽤的算法。它的计算规模相⽐于全搜索算法,是后者的 1/10 左右。此算法的思想如下:⾸先选定开始进⾏搜索的中⼼坐标,然后确定进⾏搜索的范围,搜索步⻓初始值设定为最⼤搜索范围的⼀半。在以起始点 为中⼼的正⽅形上对待预测块计算匹配误差,如果在中⼼点处,计算的 SAD 值最⼩,则认为待预测块没有运动,预测块所处的位置即为中⼼点;若在正⽅形上的其余 8个点上计算的 SAD 最⼩,则以最⼩点所处位置为中⼼重新确定正⽅形,此时搜索步⻓为上⼀次的⼆分之⼀,将第三次搜索得出的结果作为最终结果。此种搜索算法的计算复杂度较⼩,但是容易由于计算出的点是局部最优,⽽不是全局最优导致匹配误差较⼤。

    菱形搜索算法为使⽤度最⾼的搜索算法,可以选⽤⼤菱形搜索算法(LDSP)或者⼩菱形搜索算法(SDSP)如图 2.6 所示:

    图 2.6 菱形搜索示意图

    ⼤菱形搜索算法中采⽤ 9 个点进⾏搜索,中⼼点和按照菱形顶点确定的 8 个像素 点,⼩菱形搜索算法中使⽤ 5 个点进⾏搜索,中⼼点以及垂直和⽔平⽅向上的 4 个像素点。具体算法思想是,⾸先和三步搜索算法相同,确定搜索的中⼼点和搜索步⻓,然后加⼊以此为中⼼的菱形 8 个顶点(⼤菱形搜索算法)或者 5个顶点(⼩菱形搜索算法),对这些点计算 SAD 值,从中得出 SAD 值最⼩的点,如果最⼩值的点为中⼼点则判定没有运动,算法结束,继续计算该待预测块的运动⽮量和运动残差,如果最⼩值点位于菱形顶点,则继续以此点为中⼼点,搜索步⻓和初始搜索步⻓相同,确定出新的菱形顶点,并继续计算,直到最⼩值点位于菱形中⼼点则算法结束。采⽤此算法时,搜索点数最⼩为 13,但是不断⽤菱形搜索算法持续计算,可能会导致搜索点数很多,⽽且选⽤了固定⻓度的搜索步⻓,很有可能使算法陷⼊局部最优,⽽不是全局最优甚⾄有可能导致计算发⽣错误。

    因为运动物体的⼤⼩和运动情况很是复杂,很难⽤⼀种较为简单的算法计算出精确度较⾼的运动⽮量,所以通常将⼏种搜索算法结合起来进⾏运动估计,可以结合图像上不同的运动情况,使⽤相应的算法进⾏预测,从⽽提⾼预测的有效性、精确度和鲁棒性。

    变换和量化

    视频中的⼤部分图像在表达信息的时候有⼀个相同的特点,平坦的区域和像素值 变化较为缓慢的区域在⼀帧图像中占据着很⼤的⽐例,细化的空间和内容剧烈变化的空间占据的⽐例较⼩。从数字信号处理的⻆度⽽⾔,图像在频域上包含着⼤量的直流和低频成分,⽽⾼频分量较少。因此,对图像进⾏时域到频域的变换,如使⽤ DCT变换,会使能量集中在图像的左上⻆区域,使得经过变换后的图像左上⻆的值较⼤,⽽其他区域的值较⼩,有利于进⾏熵编码,对数据进⾏压缩。

    H.264 中采⽤以 4x4 块为运算单元的整数变换算法,此算法在进⾏变换计算的过程中只有简单的整数系数,这是和之前其他标准所不同的地⽅。在处理使⽤帧内编码的图像时进⾏了分块操作,划分块的最⼩尺度为 4x4;同时对于帧间编码,在进⾏运动补偿时对图像进⾏了分割操作,分割后最⼩块⼤⼩为 4x4,因此整数变换算法采⽤4x4 块为单元可以准确的对经过帧内核帧间预测后的图像进⾏变换操作。此变换算法是 Nokia 和 Microsoft 共同提出,使⽤了新的计算公式。假设输⼊矩阵为 X,对 4x4块进⾏变换时⽅法如下所示:

    在上式中:

    如果使⽤公式 2-2 直接进⾏运算,则运算系数为⽆理数,增加了计算难度,且使⽤软件实现算法时⽆法使得块经过变换和反变换的结果⼀致,会降低压缩的准确性。

    因此对变换矩阵进⾏处理以⽅便计算,对矩阵系数进⾏四舍五⼊和部分列进⾏放⼤后得出新的变换矩阵 C

    新的变换矩阵中所有的系数均为整数且值为 1 和 2,对于系数为 2 的运算可以⽤ 移位操作替代乘法操作,从⽽使得整个变换计算过程没有乘法和除法操作,简化了计算过程,提升了计算速度。在解码的过程中,使⽤对应的反变换矩阵,同样的反变换操作也没有包含乘法和除法操作,且在运算中没有⽆理数,整个反变换过程也更加简单和快速。

    经过变换后的数据值较⼤,通过量化的⽅式,可以缩⼩变换后数据的值范围,⽅ 便随后的编码操作。量化的⽬的是在保证图像质量的前提下缩⼩值范围,从⽽减少图像的编码⻓度,提⾼压缩率。H.264 编码标准中采⽤的是标量的量化⽅式,量化器的量化公式如 2-4 所示:

    公式中,y 为输⼊样本点的编码值,QP 是量化步⻓,round 函数是取整数值的函数, FQ 为量化结果。可以看出 QP 的值选取的越⼤,FQ 的取值范围越⼩,此时经过反量化后能够恢复出的像素细节较少;QP取值越⼩,FQ 的取值范围就会越⼤,此时经过反量化后能够恢复出较多的像素细节,但是在进⾏后续的编码操作时,编码需要的位数就越多,压缩率相⽐于前者降低。因此需要对量化步⻓和图⽚质量之间进⾏权衡,以达到更优秀的编码性能。

    去块滤波

    H.264 算法中的操作是以块为单位进⾏计算的,这样的⽅式对于块内数据⽽⾔, 不会造成影响,但是对于块与块之间的边界处理不够精确,从⽽引⼊块间数据的误差,使得经过编解码器反变换和量化之后,可以看到图像上有块状边界,即⽅块效应。产⽣⽅块效应的原因主要有两个,其中最为主要的原因是对于帧内数据经过预测后产⽣的残差进⾏ DCT 变换时是基于块进⾏操作的,帧间预测时对残差数据的处理也是如此。对于变换系数进⾏量化操作的过程较为粗糙,因此在进⾏反量化操作时,恢复出的原始数据不够准确,从⽽导致⾁眼看图像时有较为明显的分块边界。另外⼀个原因是运动补偿,由于运动补偿使⽤的块在匹配精度上不够准确,当块与块之间有错位时,边界会出现不连续的情况,参考帧中边界的不连续状态也被引⼊到进⾏运动补偿操作的图像中。变换操作是基于 4x4 块的,尺度较⼩,在⼀定程度上减轻了⽅块效应,但是仍然需要专⻔的算法策略以消除⽅块效应。

    去块滤波[27]的⽅法主要有两种:⼀种是添加后置滤波器,另外⼀种是使⽤环路滤波器。两者对数据的处理范围有区别,后置滤波器的数据处理范围局限于编码环路之外的位于显示缓冲器中的图像数据,也因此它在标准中只是可以选择的⼀种选项。环路滤波器⽤于处理位于编码环路中的数据。在编码器中,经过滤波操作的图像数据被⽤于作为其他帧进⾏运动补偿的参考帧;在解码器中,经过滤波操作的图像数据被显示出来。

    在视觉上的块效应,不仅是由算法中的误差引起的,当 DCT 变换块的边界,刚好位于图像上的边界,⽐如器具的边缘等,如果不对此种情形进⾏特殊处理⽽认定为是⽅块效应,就会引⼊新的处理错误,影响视频质量。所以在对⽅块效应进⾏处理的时候,⾸先要确定边界是否是由算法引⼊的即假边界还是真实的图像边界,对于真实 边界则不进⾏处理,对于假边界需要根据残差块的特性和编码模式⽽使⽤不同的强度进⾏滤波。

    滤波过程主要分为两⼤步骤:
    1. 通过计算得出残差块边界在⽔平⽅向和垂直⽅向上的滤波强度 Bs 如图 2.7。滤波强度 Bs 的取值范围为0-4,对于亮度块的边界,4x4 块的边界强度值和边界⾃身的特性有关联,如下图所示。对于⾊度块的边界,它的滤波强度 Bs 不需要单独进⾏计算,直接使⽤对应的亮度块滤波强度 Bs 即可。
    图 2.7 滤波垂直边界示意图
    1. 为了判断边界是真实边界还是伪边界,确定边界上两边的像素值,根据 1 中确 定的边界强度使⽤相对应的公式进⾏滤波操作。由于去块滤波的过程中不对真实边界进⾏滤波,对由于 DCT 等操作形成的伪边界进⾏滤波。为了保证图像的视觉效果,应该在尽量去除⽅块效应的同时保证真实边界不进⾏滤波。针对这两种情况,分析要进⾏滤波操作的边界两侧的像素值,即采⽤⾃适应样点级滤波。滤波⽅式主要分为两种:特定的滤波⽅式⽤于对滤波强度为 4 的边界进⾏强滤波;滤波强度为 1、2、3的普通滤波适⽤于其他情形。
    图 2.8 边界强度判断流程图

    熵编码

    熵编码[28]是利⽤数据之间的相关性进⾏数据压缩的⼀种编码⽅式,这种⽆损压缩 编码⽅法,分为变⻓编码和算术编码两⼤类。由于熵编码的⽆损特点,可以在消除视频数据的统计冗余信息后,再通过编码的⽅式没有失真地还原出之前的码流信息,能够很好地提升压缩效率,⽽不影响视频质量。在 H.264 中采⽤的熵编码⽅式主要有两种,⼀种是基于上下⽂⾃适应变⻓编码(CAVLC)和基于上下⽂的⾃适应⼆进制算术编码(CABAC)。由于两种算法的复杂度和特性不同,H.264 的基本档次和扩展档次只⽀持 CAVLC,⽽在主要档次中可以使⽤ CABAC 和 CAVLC 的编码⽅式。

    变⻓编码的⽅式是⾸先统计符号出现的概率,对于出现概率⼤的字符,使⽤较短的⼆进制表示,对于出现概率⼩的字符,使⽤较⻓的⼆进制表示,使⽤此种⽅法⼤⼤较少了原始信息的数据量,起到了压缩信息的作⽤。算术编码的⽅式不同于前者,是使⽤⼀个浮点数来代替原始数据中的⼀连串数据,在计算之后,输出的是⼀个在 0-1区间内的浮点数。

    H.264 中,使⽤ CABAC 带来的压缩⽐要⾼于使⽤ CAVLC,这⼀优势随着量化步⻓ QP 的增加继续扩⼤,但是压缩⽐的提升是以算法复杂度的增加为代价的,算法计算所需要的时间⽐ CAVLC 算法要⻓,因⽽具体选⽤哪种算法进⾏计算,需要权衡。

    H.264有四种画质级别

    分别是baseline, extended, main, high:
    1、Baseline Profile:基本画质。⽀持I/P 帧,只⽀持⽆交错(Progressive)和CAVLC;
    2、Extended profile:进阶画质。⽀持I/P/B/SP/SI 帧,只⽀持⽆交错(Progressive)和CAVLC;(⽤的少)
    3、Main profile:主流画质。提供I/P/B 帧,⽀持⽆交错(Progressive)和交错(Interlaced),也⽀持CAVLC 和CABAC 的⽀持;
    4、High profile:⾼级画质。在main Profile 的基础上增加了8x8内部预测、⾃定义量化、 ⽆损视频编码和更多的YUV 格式;

    H.264 Baseline profile、Extended profile和Main profile都是针对8位样本数据、4:2:0格式(YUV)的视频序列。在相同配置情况下,High profile(HP)可以⽐Main profile(MP)降低10%的码率。 根据应⽤领域的不同,Baseline profile多应⽤于实时通信领域,Main profile多应⽤于流媒体领域,High profile则多应⽤于⼴电和存储领域。

    ⼀些解释:

    1. I-帧:也成为关键帧,I-帧完全⾃我使⽤的,并且不使⽤任何其他帧的信息。它在三种帧中占最⼤的⽐例,并且具有最⾼的质量,但是压缩效率是最低的。
      P -帧:P -帧是所谓的“预示”帧。当创建了⼀个P-帧时,编码器可以向后查看I-帧或者P-帧中冗余的图⽚信息。P-帧⽐I-帧效率⾼,但是没有B-帧的效率⾼。
      B-帧:B-帧是双向预测帧,这意味着当我们创建B-帧,编码器可以同时向前和向后查找冗余的图⽚信息。这使得B-帧在三种帧中具备最佳的效率。注意,B-帧在使⽤Baseline⽅式⽣产视频的时候是不可⽤的。
    2. 虽然结果是取决于源⽂件质量的,但通常来说CABAC被认为⽐CAVLC效率⾼5%-15%。 这意味着,CABAC应该在码率低5-15%,的情况下,提供同等的,或者更⾼的视频质量。
    3. ⾄于Baseline@L x.x、Main@L x.x、High@L x.x形式则是在不同级别下的码流级别,数值越⼤码流就越⼤,更耗费资源。所以就码流⽽⾔High@L3.0<High@L4.0<High@n L5.1。
    4. 压缩率来说就是BP>HP>MP, ⽐例⼤概是100的原始数据, 压缩后BP=15,HP=45,MP=50;在相同配置情况下,High profile(HP)可以⽐Main profile(MP)节省10%的码流量,⽐MPEG-2 MP节省60%的码流量,具有更好的编码性能。根据应⽤领域的不同,Baseline profile多应⽤于实时通信领域,Main profile多应⽤于流媒体领域,High profile则多应⽤于⼴电和存储领域。

    下图清楚的给出不同的profile&level的性能区别。

    profile 主要参数:

    Level 主要参数:

    从压缩⽐例来说,baseline< main < high ,对于带宽⽐较局限的在线视频,可能会选择high,但有些时候,做个⼩视频,希望所有的设备基本都能解码(有些低端设备或早期的设备只能解码baseline),那就牺牲⽂件⼤⼩吧,⽤baseline。⾃⼰取舍吧!

    苹果的设备对不同profile的⽀持。

    preset

    • --bframes <最多连续放多少B帧>
      B帧是压缩程度最⾼的帧, 放的越多愈能降低码率!
      ⽐如设置--bframes 16 (最⼤值16)就等于⼀组最多可以连续放⼊16个.

    注意: 此参数在不同⽤途的视频的分歧很⼤.
    注意: 此参数会严重影响⽹络视频的观看效果.

    B帧本身需要读取参考前⾯的帧的信息, 调的越⾼, ⼀个GOP中就有更⼤可能被放⼊更多B帧从⽽达到压缩效果.

    ⼤量B帧能达到压缩效果, 加快⽹络缓冲, 但上传⽤视频占⽤太多CPU会导致⽹络播放器从轻微到严重的磕磕绊绊断断续续的播放状态, 这种情况不要设⾼!

    • --b-adapt <应该放B帧还是P帧>
      x264⽤来决定放P帧还是B帧更好, ⼀共3个选项:
      0 全部设B帧 (可能会造成播放器解码困难, 不推荐)
      1 加速算法 (bframes开的越⼤, 速度愈快, 但效果不如2)
      2 完整算法 (bframes开的越⼤, 速度愈慢, 但压缩率更好)
      推荐2, 但码率允许的情况下也推荐1

    • --direct <根据空间还是时间来判断物件移动>
      直接 --direct auto ⾃动判断 (有的参数确实没啥好说的)

    • --me <位移预测⽅式>
      速度从低到⾼有:
      dia菱形(快, 效果差),
      hex六边形(快, 效果⼀般, 纯单向移动推荐),
      umh可变六边形(适中,效果好, 推荐),
      esa全向(慢, 效果好点⼉)
      tesa超全向(⽐上⼀个慢点⼉, 效果好⼀丢丢).
      ⼤多情况推荐 --me umh
      模拟钢琴, midi轨道演示类的视频⼤多都是单向移动, 所以可以选 --me hex, 但⽂件码率会增⼤⼀点(1080p, 60帧下约100kbps).
      tesa只⽐esa慢⼀⼩点. 除⾮码率只差⼀点(10kbps)就合格了再使⽤, 当然更简单粗暴不拖时间的⽅式还是调 --vf resize ⾥的 sar~

    • --merange <位移预测范围⼤⼩>
      多⼤都可以. 在最极端的 --preset placebo 参数中这个值也只是24.
      普通情况就设24即可, 毕竟现在电脑的配置不是⼏年前了.
      模拟钢琴类的视频由于⼏乎所有的物件都往⼀个⽅向移动, 不会很⼤的拖慢判断, 所以推荐32.

    • --partitions <宏块能分成多少份>
      ⽤来精确的还原线条. --presetplacebo下的是 --partitions all 所以视频的宽度和⾼度都会被分成16块,⻅图(数⼀下图中横向和纵向的块的数量就明⽩了)
      推荐:
      ⾼清⽚源+等于/⾼于1280720: --partitions all
      低清⽚源/低于1280
      720情况下: --partitions b8x8,p8x8,i8x8,i4x4 (IPB帧被分成8x8的⼩块, I帧还可以被分成4x4的⼩块.)
      当⽚源⾮常不清晰时: --partitions b4x4,p4x4,i4x4.

    • --ref <能参考多少帧> 最⼤值16
      这个参数最终会以命令的形式告诉播放器每⼀个B帧和P帧能⽤多少个前⾯的帧来获取参考信息. 能参考更多帧, 标志着⾃身可以储存更少信息, 增强压缩效果.
      注意: 此参数会严重影响⽹络视频的观看效果.
      注意: 此参数在不同⽤途的视频的分歧很⼤.
      ⽆变化,不⽤参考,设为1左右。
      变化不⼤,容易参考,适合开⼤,设为10以上。
      变化太⼤,难以参考,适合缩⼩,设为5以下。

    • --trellis <要帮忙吗>
      基于拉普拉斯算⼦(什么⻤)通过分格量化将编码数据微调的参数. 好吧, 拉普拉斯算⼦的优点是能发现并增强微⼩的细节, 我就知道这么多~
      设定的值会影响trellis在计算部分的参与程度. ⼀般是越多越好.
      0: 关闭.
      1: 仅编码后期量化计算.
      2: 编码后期量化, 运动精度, 分割宏块等计算.
      所有情况推荐 --trellis 2

    • --rc-lookahead <扫描多少帧>
      扫描往后的帧并把变化数据交给量化⼯具.
      建议设置为视频原帧数的三倍, 个位数除以2, ⼩数去尾.
      对于分辨率很低(240p,360p, 480p等)可以考虑设为视频源帧数的5倍(极限值为 250)
      并降低qcomp来保证最⼤的扫描距离和量化压缩强度哦(qcomp后⾯会讲到, 现在可以⽆视)

    • --subme <设置亚像素估计的复杂度>
      值设在0-11之间, 设的越⼤提升越强.
      推荐最⾼的值: 11 (有的x264版本不⽀持, 这种情况下⽤10)
      默认:7
      可使⽤的值如下:
      0 —— fullpel only
      1 —— QPel SAD 1 iteration
      2 —— QPel SATD 2 iterations
      3 —— HPel on MB then QPel
      4 —— Always QPel
      5 —— Multi QPel + bi-directional motion estimation
      6 —— RD on I/P frames
      7 —— RD on all frames
      8 —— RD refinement on I/P frames
      9 —— RD refinement on all frames
      10 —— QP-RD (requires –trellis=2, –aq-mode > 0)

    preset跟profile,level是否冲突?
    就个⼈理解,profile,是特性限定,level是数量限定,preset是具体执⾏的步骤限定,在⼀起应该不冲突。

    X264的preset和tune

    鉴于x264的参数众多,各种参数的配合复杂,为了使⽤者⽅便,x264建议如⽆特别需要可使⽤preset和tune设置。这套开发者推荐的参数较为合理,可在此基础上在调整⼀些具体参数以符合⾃⼰需要,⼿动设定的参数会覆盖preset和tune⾥的参数。

    --preset的参数主要调节编码速度和质量的平衡,有ultrafast、superfast、veryfast、faster、fast、medium、slow、slower、veryslow、placebo这10个选项,从快到慢。
    --tune的参数主要配合视频类型和视觉优化的参数,或特别的情况。如果视频的内容符合其中⼀个可⽤的调整值⼜或者有其中需要,则可以使⽤此选项,否则建议不使⽤(如tune grain是为⾼⽐特率的编码⽽设计的)。

    tune的值有:
    film: 电影、真⼈类型;
    animation: 动画;
    grain: 需要保留⼤量的grain时⽤;
    stillimage: 静态图像编码时使⽤;
    psnr: 为提⾼psnr做了优化的参数;
    ssim: 为提⾼ssim做了优化的参数;
    fastdecode: 可以快速解码的参数;
    zerolatency:零延迟,⽤在需要⾮常低的延迟的情况下,⽐如电视电话会议的编码。

    4.⼀些编码建议
    编码延时

    降低x264的延时是可能的,但是会降低质量。若需零延时,设置--tune zerolatency。若你可以接受⼀点⼉⼩延时(如⼩于1秒),最好还是允许延时。下列步骤可以降低延迟,当延迟⾜够⼩时,就别再进⾏后续步骤了:
    1.从初始值开始
    2.关闭sync-lookahead(设置⽤于线程预测的帧缓存⼤⼩。最⼤值是250.在第⼆遍及更多遍编码或基于分⽚线程时⾃动关闭)
    3.降低rc-lookahead,但别⼩于10(设定mb-tree位元率控制和vbv-lookahead使⽤的帧数)
    4.降低threads(⽐如从12降到6)
    5.使⽤切⽚线程(sliced threads)
    6.禁⽤rc-lookahead
    7.禁⽤b-frames
    8.实在不⾏,就⽤--tune zerolatency

    param->rc.i_lookahead = 0;param->i_sync_lookahead = 0; param->i_bframe = 0; param->b_sliced_threads = 1; param->b_vfr_input = 0;param->rc.b_mb_tree = 0;(使⽤宏块树位元率控制会改善整体压缩率)

    x264线程

    x264起多少个线程⽐较好 ?
    建议线程数:
    1、2、4、8

    测试结论:

    1、更多的线程会消耗更多总CPU时间⽚,因此在⻓期满载的机器上不宜使⽤多线程。
    2、获得的时间收益随线程增多呈递减趋势,8线程以后尤为明显。
    3、PNSR下降随线程数增加呈抛物递增趋势,16线程增加到24线程PSNR时下降了0.6之巨。
    4、设置threads=auto时,线程数为逻辑CPU个数的1.5倍。

    编码器的算法可定制模块

    H.264 作为⼯业界⼴泛使⽤的视频编码标准,采⽤了⼤量编码新技术,使得其具有以下特点:提⾼了编码效率,与 H.263 等相⽐,在压缩码率⽅⾯提⾼了约50%,具有较⾼的压缩性能;能够在⼀定码率约束下,编码后获取⾼质量的图像,并且提⾼传输过程的效率。数据压缩⽐的提升⽆疑是技术改进最重要的⽬标,但同时也伴随着较⾼的编码复杂度。与其他视频编码标准相同,H.264 是由不同编码技术联合作⽤,共同完成⾼效的编码过程,包括场景切换检测、调整图像组、去噪预处理、变换、量化、逆量化、逆变换、环路滤波、运动预测、运动补偿等⼀系列编码技术。⼀些技术已在视频编码标准中制定了具体实现⽅法,但是为了体现视频编码技术的灵活性,只描述了部分编码技术传输的数据流语法内容与编解码流程,允许使⽤者根据实时编码需求为特定编码技术选取最佳的模式,以满⾜不同的应⽤需求[3]。编码参数的不同选项正是编码技术不同模式的体现,根据编码参数在编码流程中的作⽤,视频编码技术可以划分为不同的算法可定制模块,如图中⻩⾊标记块所示。

    编码器端接收原始数字视频图像后,经过对视频复杂度、编码效率与主观视频质量的分析,进⾏视频图像的处理,这部分即为视频预处理模块;

    为编码量化模块提供量化步⻓,控制输出码流的码率,在⼀定允许条件下,使编码率失真性能达到最优,极⼤程度的提升视频压缩质量是码率控制模块的主要作⽤[3];

    在视频编码技术中,考虑到视频信号在时间域与空间域的相关性,运动预测技术尤为重要。运动⽮量预测的准确程度直接决定了运动预测的效率,这正是运动估计模块需要解决的问题;

    H.264、AVS 等编码标准中,帧内与帧间预测过程采⽤了众多编码模式,不同模式的选择影响着编码结果。基于不同视频序列的特征进⾏模式的选择,是提⾼编码效率的关键所在,这些⼯作在模式选择模块开展。

    视频编码过程中,视频预处理、码率控制、运动估计、模式选择四个算法可定制模块起着决定性作⽤,协调它们之间的⼯作,以达到率失真性能、编码复杂度最优化的视频编码算法定制。

    (1)视频预处理

    视频预处理技术可以根据需要确定是否采⽤,⼀般预处理包括下⾯两种情况:⼀种是分析视频序列,根据视频内容调整图像组(GOP)结构和编码帧类型,以优化视频编码算法效率;另⼀种是以提⾼编码效率和主观视频质量为⽬标的图像序列去噪处理,以降低视频信号空时域相关性[3]。随着视频编码中多参考帧技术的产⽣,固定 GOP 结构⼤⼩的模式已显逊⾊,相⽐之下⾃适应的 GOP 结构更加灵活,减少了不必要的冗余,虽然增加了编解码器的复杂度,但是可以显著改进预测的准确度。图 2.3 为多参考帧运动预测示例图,H.264 对不同时间的参考帧引⼊权重,使得在选择参考宏块时扩展到空域和时域两⽅⾯,空域上控制得更精准。

    图 2.3 多参考帧运动预测示例图

    (2)码率控制

    码率控制是视频编码中最复杂的模块之⼀,任何标准都需要在⼀定的码率控制约束下执⾏。码率控制算法是在⼀定码率约束的情况下,⾃适应地调节编码器量化参数值,使编码达到优化。MPEG-2 的 TM5、H.263 的 TMN8、H.264、AVC的 G012 等⼏种码率控制算法已经被推荐作为参考⽅案。新⼀代的编码标准HEVC,在码率控制⽅⾯也有很⼤的改进。图 2.4 给出了码率控制模块以及基本原理。

    图 2.4 码率控制模块以及基本原理图

    (3)运动估计

    运动估计模块是视频编码中最为复杂的模块,它的根本⽬的在于利⽤视频帧之间存在的相关性,在邻近的多参考帧中进⾏运动⽮量预测,压缩视频信号加以传输。运动估计⽅法最常⽤的是块匹配法,将⼀帧视频图像划分为不同⼤⼩的块,加以搜索寻找最优的匹配块。在 H.264 中采⽤ 16x16 固定⼤⼩的块,在HEVC中则引⼊编码单元(CU)这⼀概念,可以对⼀个 CU 进⾏不同模式的分割,最⼤为 64x64,最⼩可⽀持 8x8。匹配搜素算法的好坏很⼤程度地影响运动估计的精度与实现复杂度,为了降低复杂度,许多学者提出了快速搜索算法,包括:三步搜索算法、共轭搜索法、交叉搜索法、新三步搜索法、四步搜索法、基于块的梯度下降搜索法、菱形搜索法、分层搜索法等,但是快速搜索算法不能保证得到全局最优点。整像素运动估计往往精度不够,因此引⽤了分像素运动估计。H.264采⽤的运动补偿精度最少为 1/4像素精度,也可以选择 1/8 像素精度,HEVC 则采⽤基于 8 阶 DCT 变换的 1/4 亮度插值和 4 阶 DCT变换的 1/8 ⾊度插值。使⽤分像素运动估计与整像素运动估计相⽐,可减少消耗的⽐特数[4]

    (4)模式选择

    H.264 编码标准与其他视频编码标准相⽐获得较⾼的编码效率,主要的原因在于模式选择模块的技术改进。帧间运动预测、帧内预测、熵编码技术等等都是模式选择模块引⽤的先进技术,⾃适应的模式选择更是提⾼编码效率的关键所在。在模式选择过程中不仅要考虑视频图像序列⾃身的特性,也要考虑场景变更过程中存在的空时域相关性,通过视频的预分析去除⼀些不需要的模式,这样可以⼤⼤降低模式选择过程带来的复杂度。基于率失真优化的模式选择是⽐较合适的模式选择⽅法,它的⽬的是达到率失真性能最优化。同样⾮率失真优化模式选择的⽬的是码率、失真、复杂度的折中。

    算法可定制模块之间具有⼀定的相关性。视频序列通过预处理分析,在编码中运动估计与码率控制部分起到作⽤,码率控制基于视频的预分析获取复杂度分布信息。运动估计模块与模式选择模块也相互影响着进⾏编码过程的处理,通过模式选择部分不同模式的代价计算,为运动估计提供最优的预测⽮量。视频编码中的率失真优化是基于拉格朗⽇乘⼦完成的,同时拉格朗⽇乘⼦的确定是根据量化参数制定的,因此模式选择与码率控制之间也存在⼀定的相关性。各个模块间的关系如图 2.5 所示。

    图 2.5 视频编码中模块间相关性示意图

    X264编码参数

    x264 开源项⽬作为最优秀的 H.264 编码器,实现了标准中的主要编码算法,其 中包括对不同编码档次的划分、帧内预测的 9 中⽅向,帧间预测的典型算法如全搜索算法和菱形搜索算法等,对于 4x4 块的 DCT变换,量化,反变换,反量化以及熵编码等。由于 H.264 中定义的算法太多,x264 ⼯程在保证编码性能不受较⼤影响的情况下省略了对⼀些算法的实现。

    X264 作为⽬前⼯业界⼴泛使⽤的 H.264/AVC 编码器,主要进⾏视频的压缩⼯作,它的⼴泛使⽤基于它可以完成⾼效率的编码过程。X264 视频编码器的开发到成熟,经历了 10 年左右的时间,这 10 年时间的不断更新使得 X264 的编码性能与效率远远优于其他版本的编码器。与它同期的开源项⽬ Xvid 采⽤的早期MPEG-4 标准,X264 压缩的视频⽂件,在相同的质量情况下⼩于 Xvid 的压缩⽂件[6]。

    X264 编码器涵盖了 H.264 编码标准中⼤部分编码技术,是最新的 AVC 编码格式之⼀。Core 128 是⽬前最⾼级的 X264 版本,已经应⽤于 ffdshow、fmpeg 和MEncoder 等。HM 是新⼀代视频编码标准HEVC 的官⽅标准测试模型,现已进⾏发布,虽然 HM 采⽤了 HEVC 中⾼效率的编码新技术,但是使⽤仍未得到推⼴,⽽是 X264 作为当今优秀的视频编码器在各个应⽤领域使⽤。X264 能够提供于视频编码技术研究使⽤,并且具有很⼤的利⽤空间。

    编码参数简介

    算法可定制模块为了实现先进的编码技术,采⽤了⼤量编码参数对编码过程进⾏控制,包括控制:
    全局变量参数
    开关控制参数
    算法执⾏参数

    这些编码参数共同作⽤完成了视频图像的⾼效压缩编码。⼀些编码参数的设置是编码过程所必需的,对视频编码起着决定性作⽤;还有部分编码参数,可以根据实际要求⼈为决定采⽤与否。针对 X264 视频编码器,算法可定制模块包含的编码参数达⼏百个之多,主要编码参数如下:

    (1)视频预处理模块

    bframes

    bframes 参数设定 X264 可以使⽤的最⼤并⾏ B 帧数量,即设置最多允许⼏个连续的 P 帧被 B 帧取代。在视频编码过程中,I 帧采⽤帧内预测⽅式,P 帧与B 帧类似,采⽤帧内和帧间预测⽅式,均可以参考其他帧进⾏预测,区别在于 P帧只能参考前⾯的帧进⾏前向预测,⽽ B 帧允许以后⾯的帧作为参考帧进⾏双向运动预测,如图 2.6 所示,其提⾼了视频编码压缩效率。Bframes 参数选项范围为 1-16 帧,X264 编码器以 3 帧作为默认选项。

    图 2.6 B 帧双向预测结构图
    b-adapt

    b-adapt 参数设定⾃适应 B 帧位置决策算法,可以⾃动调节 P 帧与 B 帧的位置,进⾏⾃适应编码优化。在多步编码模式中,此参数仅在第⼀步中起作⽤,在第⼀步结束时编码帧类型已经确定。B-adapt 的选项包括:“0”表示停⽤,将决策帧全部设定为 B 帧;“1”表示快速决策算法;“2”表示最佳决策算法,默认选项为“1”。

    b-bias

    b-bias 参数是 B 帧决策的衡量值,B-bias 越⼤,则决策为 B 帧的可能性越⼤,它与b-adapt 参数协作,完成⾃适⽤ B 帧决策。选项范围是-100~100 之间有效整数,但设置为 100 或-100 时,并不保证全部设置为 B 帧,这由算法本身特性决定。“0”为默认选项。

    b-pyramid

    b-pyramid 参数决定编码过程是否允许 B 帧做为参考帧,关闭此参数,只有I 帧、P 帧可以为参考帧。可以作为参考帧的 B 帧是介于 P 帧和普通 B 帧之间特定的 B 帧,若开启此参数,需保证 Bframes 参数选项⼤于等于 2,否则将⽆法⽣效。选项包括:“none”不允许 B 帧作为参照帧;“strict”表示在⼀个 GOP结构中只允许存在⼀个 B 帧作为参照帧;“normal”表示可以任意使⽤ B 帧作为参照帧,其中“strict”选项为蓝光光盘编码强制设定选项,默认选项为“normal”。

    ref

    ref 参数控制解码图像缓存区⼤⼩,选项范围 1-16 帧,默认选项为 3 帧。此值的设定决定 P 帧可以使⽤参考帧的数量,B 帧需要判断是否允许为参考帧。参考帧数量的增多可以提⾼运动预测的精度,同时会增加视频编码过程的复杂度。⼀般情况下,当参考帧数达到⼀定数量时,预测精确度并未有显著提升,此参数选项根据实际情况实时分析,参考帧数的增加与性能的提升并⾮满⾜线性递增关系。H.264 编码标准限制了不同等级(level)参考帧数⽬,针对 level4.1 来说,分辨率为 1280x720 的序列(720P)最⼤参考帧数⽬为 4,分辨率为 1920x1080的序列(1080P)允许最多参考帧为 9 帧[7]。

    keyint

    keyint 参数设定 X264 输出⽂件的最⼤关键帧间隔。关键帧(IDR)是视频流的“分隔符号”,视频帧⽆法参考 IDR 帧之前其他视频帧。在 HEVC 中,Keyint参数的设置⼏乎由 GOP 结构代替,关键帧的设定分为⼏种情况,包括 GOP 的开启、GOP 关闭等。GOP 关闭时与 H.264 相同,视频帧⽆法参考前 GOP 中视频帧。

    ⽽ GOP 开启时,HEVC 标准中允许当前 GOP 结构中视频帧可以参考其他 GOP中视频帧,同时 GOP 的开启和关闭中增添了多种视频帧设置⽅式,这⽅⾯也是HEVC 编码技术提升的表现之⼀。X264 中 Keyint的默认选项为 250,并且在⼀般情况下,使⽤者均采⽤此选项进⾏编码[7]。

    lookahead

    Lookahead 参数设定宏块树(Mb-tree)码率控制使⽤的视频帧数,默认为40 帧。对于 Mb-tree ⽽⾔,使⽤视频帧数越多可以得到更准确地结果,但同时编码复杂度更⾼。Mb-tree 使⽤的最⼤帧数是Lookahead 参数和 Keyint 参数中较⼩的值,使⽤视频帧数的增加能够获得更稳定和精确的码率控制过程。

    (2)码率控制模块

    qp、bitrate、crf
    Qp、Bitrate、Crf 参数,三种码率控制⽅法。
    • Qp 设定 X264 以固定量化值进⾏编码视频,把某个量化值作为⽬标,编码后⽂件⼤⼩未知。Qp 固定的编码⽅式,直接控制着编码图像的失真,失真的⼤⼩以及失真的分布特性直接决定着编码视频的主观感知质量;
    • Bitrate 以⽬标码率模式进⾏编码视频,编码后⽂件⼤⼩已知,但视频压缩质量是未知的。⽬标码率编码⽅式,在编码开始时会根据设定的⽬标码率进⾏码率分配,依次分为 GOP 级码率分配、帧级码率分配、单元级码率分配,确保复杂度较⾼的区域分配充⾜的码率,复杂度较低的区域获取适合的码率,以保证编码失真的均匀分布[8];
    • Crf 是以固定码率系数进⾏编码,将压缩的视频“质量”作为⽬标。三种编码⽅式是互斥的,每个编码过程只能对应⼀种编码⽅式。

    Aq-mode 参数设定视频编码⾃适应量化模式。图像由不同空间域矩阵变换时,不进⾏任何处理其数据是⽆法减少的[3]。为了视频压缩码率,采⽤了数据的量化与编码技术,⾃适应量化是量化技术的提⾼,动态调节量化步⻓,优化编码质量。选项“0”表示不启⽤⾃适应量化功能。“1”代表编码过程中允许对视频帧以及帧内宏块进⾏⽐特数调整,以确保为细节较少的区域分配充⾜的⽐特数,为默认选项。“2”可以⾃动变化 Aq-mode 参数模式,并且可以调整⾃适应量化的强度,使得⾃适应的量化编码具有更⾼灵活性,适⽤于实验性测试。

    Mb-tree 参数控制是否开启宏块树进⾏码率控制。基于分块预测的视频编码如果出现明显的帧间失真波动,会形成所谓"蚊⼦效应",这种时域失真的波动⼈眼⽐较容易觉察。因为帧间预测和不同类型帧存在,较⼩的时域失真波动对于提⾼预测效率是必须和难免的,但需要控制在⼀定的范围内,不⾜以引起⼈眼明显感知。I 帧和靠近 GOP 头部的 P 帧更多地被后续帧作为预测参考。这些被更多地参考的帧或区域应该有更⼩的失真,这样可避免失真在 GOP 内累积传播,最⼤化 GOP 内各帧视频质量。 从这个⻆度理解,为提⾼运动预测的效率并有效控制失真波动,需要分析 GOP 中 I、P 帧内各个宏块被参考的频度,在帧内宏块级码率分配中,给被参考频度⾼的宏块分配相对多⼀些⽐特,让有限的⽐特分配给更重要的区域,提⾼预测编码效率。X264 采⽤ Mb-tree 宏块级量化控制算法,基于⼈类感知特性来动态调节量化参数,改善失真分布情况。Mb-tree 算法主要从以下因素考虑:(1) 作为被参考的宏块,若相邻帧的宏块频繁地参考其进⾏预测,则应该降低当前宏块的量化步⻓以减⼩失真;(2) 由于参考过程中导致失真在传递过程中累加,Mb-tree 宏块树考虑到间接传递产⽣的影响;(3) 除了客观存在的因素外,考虑到⼈类感知系统特性,具有复杂场景的区域,即使存在⼀定失真可能⼈眼对此并不敏感,⽽是对平坦区域关注⼒⽐较强,因此应降低这部分失真[9][10]。

    对于当前视频帧中所有宏块,分别在滑动窗内后续邻近帧中进⾏运动预测,预测帧的数量由 Lookahead 和 Keyint 两个参数的较⼩值决定,先分析 B 帧参考和代价传递情况,接着分析 P 帧参考和代价传递情况,依次以树状结构向前扩展,树状结构如图 2.7 所示。Lookahead 分析滑动窗如图 2.8 所示,在编码每⼀帧之前滑动窗更新⼀次,得到当前编码帧内每个宏块传递代价。使⽤ Mb-tree 码率控制会改善整体压缩率,借由追踪跨帧的时域相关性并相应地加权。

    图 2.7 Mb-tree 树状分析结构图 图 2.8 Mb-tree 滑动窗及树状分析结构图

    (3)运动估计模块

    Me 参数设定全像素运动估计模式,包含五个选项:⼩菱形搜索(DIA)、正六边形搜索(HEX)、⾮正六边形搜索(UMH)、全局搜索(ESA)和变换全局搜索(TESA)。DIA 表示最简单的搜寻⽅法,检查上、左、下、右⽅⼀个像素的运动⽮量,如图 2.9 所示。重复搜索各点,直⾄找到最佳匹配点位置,完成搜索;HEX 表示使⽤周围 6 个范围为 2 的点进⾏搜寻,因此称为正六边形搜索;此搜索⽅法效率较⾼,为默认 Me 参数选项,搜索示意图为 2.10。UMH 表示复杂的多六边形搜索,搜索半径由 Merange 参数控制;ESA 表示⼀种⾼度最佳化智慧搜寻⽅式,搜索范围仍由 Merange 参数限定;

    图 2.9 DIA 搜索模版 图 2.10 HEX 搜索模版

    Merange 参数控制运动⽮量搜索的最⼤范围,单位是像素。设置范围越⼤,运动估计的复杂度越⾼。此参数基于运动估计的不同模式进⾏不同设置,对于运动估计的 DIA、HEX 选项,当设置搜索范围⼤于 16时,最⼤搜索范围仍为 16。

    UMH、ESA、TESA 选项允许更⼤的搜索范围,但加⼤搜索范围,会⼤幅度提⾼编码复杂度。默认选项为16。

    Mvrange 参数设置垂直运动⽮量的最⼤像素值,根据层级的不同,此参数默认选项不同:Level 1/1b64,Level 1.1-2.0-128,Level 2.1-3.0-256,Level 3.1+-512。

    Subme 参数设定分像素运动估计模式。X264 中分像素运动估计分为 11 个层级,层级 1~5 控制分像素细分强度,层级越⾼,细分强度越⼤,导致编码过程越复杂。层级 6 为 I、P 帧的模式决策启⽤率失真优化(RDO)。层级 7 为全部视频帧启⽤ RDO,是此参数的默认选项。层级 8 为 I、P 帧的运动⽮量和内部预测模式启⽤ RDO。层级 9 为全部视频帧的运动⽮量和内部预测模式启⽤ RDO。

    层级 10 为 Qp 量化开启 RDO,此层级的启⽤,对⾃适应量化与 Trellis 参数有⼀定要求。层级 11 开启所有 RDO,与层级 10 ⼀样需要其他参数⽀撑。

    (4)模式选择模块

    Partitions 参数设置视频帧允许的分割模式。H.264 将视频帧在压缩过程中划分为 16x16 的宏块,这些区块可以进⼀步划分为更⼩的宏块,完成精确的运动预测。对于 I、P、B 不同类型的视频帧,具有不同划分深度。I 帧可以划分为 8x8、4x4 的区块,P 帧⽀持 8x8 与 4x4 的划分,B 帧只允许划分成 8x8 的区块。此参数默认选项为 P 帧 8x8,B 帧 8x8,I 帧 4x4。若设定 I、P 帧允许划分 4x4 区块,则表明同样允许划分为 16x16、8x8 区块,B 帧若设定最⼩可进⾏ 8x8 划分,则仍可进⾏ 16x16 划分。

    Trellis 参 数 执 ⾏ ⽹ 格 编 码 量 化 来 提 ⾼ 编 码 效 率 , 包 括 三 个 选 项 :“0”停⽤此项功能;“1”只在⼀个宏块的最终编码上启⽤此功能,提供了速度和效率间良好的平衡;“2”在所有模式决策上启⽤此功能,默认选项为“1”。

    候选测试编码参数

    基于本⽂提出的显性编码参数标准,对算法可定制模块中重要编码参数进⾏测评。考虑到编码参数各⾃的特点属性,候选测试参数以及选项如表 2.1 所示。

    Subme 参数的选项“10”与“11”需要其他参数的特定选项⽀撑,并且应⽤范围有限,要求较⾼,并不在本实验考虑范围内。Bframes 与 Ref 参数可⽀持最⼤选项为 16 帧,测试选项选定为 1 ⾄ 16 的整数。Me 参数的 5 个选项展示了不同的运动搜索⽅式,均在测试范围内。Keyint、Merange、Lookahead 参数选项范围较⼤,因此选项选取间隔式测试。B-adapt、Aq-mode、Trellis 参数是编码过程⾃适应技术的体现,需要在编码过程中开启,不使⽤“0”选项。

    对于模式选择模块的 Partitions 参数,考虑到针对不同帧类型具有不同分割模式,本⽂引⽤新组合参数Sub-partitions 来表示。不同帧类型分割模式的组合,形成了 Sub-partitions 参数的选项“0”“1”“2”“3”,如图 2.12 所示。

    图 2.12 新组合参数 Sub-partitions

    表 2.1 候选测试编码参数及其测试选项

    x264 ⼯程分析

    x264 项⽬的实现版本主要由 linux 环境下的实现版本和 windows 平台下的实现版 本。本⽂在 windows环境下对 x264 进⾏优化和移植。使⽤ VS2013 集成开发环境对x264 ⼯程进⾏分析。使⽤此⼯具打开x264 ⼯程,有两个⼯程⽂件,⼀个是 libx264⼯程,另外⼀个是 x264 ⼯程,libx264 ⼯程的配置类型为静态库(.lib),不包含 main

    ()函数等程序启动⼊⼝,在 x264 ⼯程中引⽤了 lib264 ⼯程。libx264 ⼯程中有三个⽂件夹如图 3.1所示:

    可以看出 libx264 包含四个⽂件夹 Core、Enc、extras 和外部依赖项,外部依赖项中存放着⼯程所需要的头⽂件,⽐如 pixel.h,dct.h 等,Core ⽂件包含编码所需要的核⼼⽂件⽐如 dct.c,quant.c(⽤于实现 DCT 变换和量化操作)等,Enc ⽂件夹下存放着和编码操作密切相关的⽂件⽐如 cavlc.c 和 ratecontrol.c(熵编码和码率控制),extras ⽂件夹下包含着处理输⼊参数的⽂件 getopt.h 和⼤量的宏定义 stdint.h。x264 ⼯程中的⽂件较少,包含控制整体编码流程的 x264.c,其中包含函数的⼊⼝点 main()函数以及 Encode()函数。

    编码流程

    x264 项⽬的执⾏是从程序⼊⼝点 main()开始执⾏的,下图中为 x264 ⼯程从 main 函数开始的执⾏流程图:

    图 3.2 x264 编码流程图

    x264 项⽬中实现了 H.264 标准中的⼤量算法,对于选⽤哪种算法进⾏运算和算 法中的关键参数都需要进⾏设置,这样才能使项⽬更加具有灵活性和适应性,针对不同的应⽤场景和应⽤需求选择不同的参数如码率、量化参数、参考帧数量和快速优化算法等。x264 中定义了数据结构 Struct x264_param_t,其中定义了和编码器控制相关的⼤量参数,⽐如 struct analyse、struct rc(码率控制)、struct vui 以及i_fame_refence(可 以使⽤的参考帧最⼤数量)、i_bfame(两副图像之间可以使⽤最⼤的 B 帧数量)、i_threads(编码中并⾏处理使⽤的线程数)、i_width(图像的宽度,以像素为单位)、i_height(图像的⾼度,以像素为单位)、i_fame_total(视频中需要编码的帧数量)和 i_fps_num(帧率)。x264 在进⾏编码之前,通过设置命令⾏参数,向程序中传⼊了部分所需要的编码器控制参数。⽐如:要进⾏编码的⽂件位置和经过编码操作后压缩⽂件的存放位置通过-o 选项设定。⾸先对编码器控制参数x264_param_t 进⾏初始化,使⽤x264_param_default(&param)函数对控制参数进⾏默认赋值。随后使⽤ Parse(argc, argv, &param, &opt) 函 数处 理 从 命 令 ⾏ 中 传 ⼊ 的 参 数 , 具 体 的 提 取 参数 操 作 是 由getopt_long(argc, argv, short_options, long_options, NULL)函数完成的,其中 argc为从命令⾏输⼊的参数个数,argv 类型是 char**,为输⼊的参数的引⽤。经过此函数处理后将处理后的参数放⼊ optarg 中并分别于编码器已经设定好的控制参数进⾏匹配,如果相等则设置为对应的参数值。通过以上操作完成了对编码器的参数设定。

    设置完参数后,开始进⼊编码函数 ret = Encode(&param, &opt),编码函数的返回值 ret ⽤来表示编码是否成功。在此函数中⾸先根据输⼊的参数对编码器进⾏初始化x264_encoder_open(param) , 并 对要 编 码 帧 的 数 量 进 ⾏ 了 计 算 。 使 ⽤x264_picture_alloc(&pic,x264_CSP_I420,param->i_width,param->i_height) 函数创建 ⼀副图像⽤于存放视频⽂件的 yuv 数据,其数据格式为 YUV420。在完成这些初始化操作后,确定了需要编码的总帧数 i_frame_total,以此为控制条件,在被编码帧序号⼩于 i_frame_total 的状态下,对每⼀帧原始数据格式图像进⾏编码。具体⽽⾔,先调⽤p_read_frame(&pic, opt->hin, i_frame + opt->i_seek) 函数从视频⽂件中读⼊⼀个视频帧的数据,放⼊ pic 指针对应的内存中。然后调⽤Encode_frame(h,opt->hout, &pic)对⼀帧图像 pic 进⾏编码。编码器对⼀帧图像进⾏编码的具体操作是在此函数中完成的。

    在 Encode_frame 中的核⼼函数为 x264_encoder_encode(h, &nal, &i_nal, pic, &pic_out),对图像的压缩编码实际上是由此函数完成的,除了编码操作外,还对编码后的数据使⽤ NAL 包格式进⾏了封装,以便于在⽹络中传输。x264_encoder_encode的流程图如图 3.3 所示。在此函数中⾸先对程序使⽤的线程数量进⾏判断,如果使⽤的线程数⼤于 1 ,则记录当前线程,下⼀个将被使⽤的线程和较早被使⽤的⼀个线程,随后对线程运⾏的环境进⾏初始化,为线程的运⾏做准备,并在当前线程和下⼀个将运⾏的线程不同时,执⾏参数和资源拷⻉指令。然后更新图像帧所需要使⽤的参考集合。

    图 3.3 图像帧级别编码流程

    在对帧类型进⾏设定之后,将等待被编码的图像加⼊编码队列中。如果编码队列 的⻓度为 0 就需要重新设定参考集合,当队列⻓度达到 GOP ⻓度时,也需要采⽤同样的操作。由于在编码的过程中会使⽤到 B帧,所以需要将编码后的数据也保存起来,为随后进⾏编码的图像提供参考,x264 在实现⼯程中设置了两个队列,⼀个为next 队列按照图像在时间上连续的顺序进⾏存储,另外⼀个 current 队列是按照已经编码的图像帧的顺序存储。如果按照编码器的设定,在 P 帧之间有 3 个 B 帧,则⾸先将这些图像帧放⼊ next队列中,如果此队列中保存了 4 个图像,便对帧的类型进⾏判断,通过函数判断出这四个帧的类型为 B、B、B 和 P,对应于代码中的SLICE_TYPE_I、SLICE_TYPE_P 和 SLICE_TYPE_B。由于 P 帧在编码时不能参考 B 帧,⽽ B 帧的编码需要依赖于 I 帧、P 帧或者 B 帧,并且通过对这四帧图像在 next队列中的位置关系,判定出先对 P 帧进⾏编码,随后按照时间上连续的顺序将其余三帧加⼊ current 队列中。如图3.4 所示:

    图 3.4 图像帧队列

    其中使⽤到参考帧,由于在 x264 可以选⽤的参考帧数量较多,需要使⽤数据结 构进⾏维护,于是根据H.264 协议中的规定,设置两个参考队列⽤于存放参考帧,分别是 list0 和 list1。第⼀个队列 list0 ⽤来存放被⽤于其他帧进⾏预测参考的前向帧,后⼀个队列 list1 被⽤来存放被⽤于其他帧进⾏预测参考的后向帧。前向帧还是后向帧的判断依据是 POC 的⼤⼩。在 x264 中为了避免在两个参考队列中出现相同参考帧的情形,⽽导致对同⼀帧数据进⾏重复预测的状况,在 list1 中⼀般只存放⼀个后向参考帧。

    有了之前参考帧的设置和更新⽅法,就可以对带编码的图像进⾏帧间预测等操作。在对图像帧进⾏编码时,对图像进⾏了不同层次更⼩单元的划分,⾸先划分为 slice,划分后调⽤ x264_slice_init(h,i_nal_type,i_global_qp)对条带进⾏初始化操作,核⼼功能是使⽤ x264_slice_write()函数进⾏操作的。这⼀过程中包含了对宏块的分析、对宏块进⾏编码、熵编码以及码率控制和去⽅块滤波。

    整个 x264 项⽬中最耗时的部分就是对宏块进⾏操作。帧间预测占据⼯程的预测部分的主要部分,⽽运动估计确定带编码块的匹配块,确定运动⽮量,以及最后的⽣成残差操作是其主要内容。使⽤ x264_macroblock_analyse(h)函数宏块进⾏运动估计,在使⽤从众多的运动预测算法中选出合适的算法进⾏预测后⽣成运动⽮量 MV,接下来使⽤ x264_macroblock_encode(h)计算出残差数据,使⽤这个函数时应当注意,在需要的时候它可以改变宏块的类型⾄ P_SKIP。由于得出的残差数据仍然有着较强的相关性以及数值范围较⼤不利于直接进⾏编码压缩,所以对残差数据的变换块进⾏变换,将时域上的数据转换到频域上,转化之后,数据的分布⼤多集中在图像的左上⻆区域,有利于进⾏编码操作,然后对变换后的数据进⾏量化,根据开发者对编码 QP 设置的要求,采⽤适当的 QP 将值范围缩⼩到⼀定区域,再使⽤ zig-zag ⾏程编码的⽅式组织数据,最后进⾏重建等操作。

    对于运动搜索部分,为了能够得到正确的匹配块和运动⽮量,x264 使⽤了亚像素细化,提供了多种可供选择的运动搜索算法并且可以设置搜索步⻓等相关参数,具有较强的灵活性和适应性,运动搜索的性能对图像质量以及编码器的编码速度都有着很⼤的影响,本⽂在下⾯的优化部分针对不同的搜索算法进⾏了研究和测试。

    X264优化

    由于 x264 强⼤的功能和适应性,它有着⼴泛的应⽤,⽐如 VLC、FFMPEG 等软件使⽤了 x264 编码器,在视频监控领域和视频会议等需要进⾏实时编码的场景中,x264 ⽆疑是当前使⽤最多的编码器,⼚商们通过对 x264 进⾏裁剪,将其应⽤到不同的环境中。

    本⽂设计的系统采⽤ x264 作为编码器,在对系统进⾏优化时,主要是对 x264编码器进⾏优化,以提⾼帧率,增强实时性。经过对 x264 ⼯程的介绍,可以了解到x264 有着强⼤的功能以及灵活的控制参数设置,以适⽤于不同的应⽤场景和不同⽤户的需求。在编码器中衡量系统性能的重要指标有 PSNR(视频质量)、码率(反应编码后视频的数据量)和 fps(帧率)等。通过选取不同的控制参数可以使编码器的性能在不同指标上⾯有所提升,但是这种提升是有代价的,⽐如通过选⽤不同的运动估计算法以及不同的编码器控制参数使 PSNR 提升,可能会导致码率的增加。也就是在⼀个指标上的性能提升可能会导致其他指标上性能的下降,这需要我们进⾏权衡,⽽在实际需求中,恰好对某⼀性能的要求较⾼,就可以通过此种⽅式达到优化的⽬的。当前市场上的⼀些硬件设备专⻔对图像处理做了优化,将 x264 算法移植到选定的硬件平台上,就可以达到计算性能的提升,⽐如专⽤的图像处理硬件平台 DSP 处理芯⽚等。本⽂主要根据⾃身的需求通过选定合适的编码器参数和使⽤多线程技术对x264 进⾏优化,以提升整体性能。

    参数优化

    在 x264 编码器中如果不在命令⾏中对参数进⾏设置,则会使⽤默认的参数集合, 对应于程序中的x264_param_default( &param )。由于本⽂设计的系统应⽤需求为实时编码,对实时性要求较⾼,同时压缩的原始视频要求是 720P,因此采⽤的档次为 Main Profile,由于 720P 的视频每帧数据量很⼤,为提⾼压缩⽐率使⽤ I、P、B 帧。在此基础上分析不同参数对系统性能的影响,并最终确定配置参数。运动估计是 x264 中最为耗时的部分,提供的运动预测算法也较多,⽐如菱形搜索、正六边形搜索、可变半径六边形搜索和全搜索等,主要对这⼀部分进⾏优化。下⾯对量化参数 QP、参考 帧数量、搜索⽅式以及动态预测和分区⽅式的控制参数进⾏研究测试,以确定恰当的控制参数来在保证图像质量的情况下提升系统的性能。

    进⾏优化研究的硬件平台和测试序列等软硬件环境如表 3.1 所示,为了显示PSNR 和码率帧率等统计信息,需要加⼊命令⾏参数--psnr。

    1. 量化参数研究。在量化过程中实际⽤于计算的参数是 Qstep 量化步⻓,测试 使⽤的是从命令⾏中传⼊的控制参数为 QP 量化参数,两者之间有着对应关系,它们都有 52 个值,取值范围为 0-51,取 0 时表示⽆损输出,QP 的值增加 1 相应的量化步⻓增⻓了 12.5%。以下对 QP 设置不同的值,通过 PSNR、码率和编码速度三个指标来查看对系统性能的影响。设置参数的格式为:-q 或者-qp。设置不同 QP 的测试结果⻅表格 3.2。

    以下图像组3.5中五张图像的先后顺序对应于QP分别设置不同参数值时的编码图像。

    由表格中统计出的数据可以得出,对于此视频序列,随着 QP 的增⼤,PSNR 值 逐步减⼩,码率相应的降低,但编码速度有相应的提升。但是当 QP 取值在 30 以上时,虽然帧率有较⼤幅度的提升,QP 为 30时相对于 20 提升了 1.38,QP 为 40 时相对于 20 提升了 2.32,但是视频的质量下降很明显,。由此得出对 QP 参数的设置对于本⽂主题实时性有较⼤的影响,在保证视频质量的前提下可以通过选取适当的QP,提升编码速度。本⽂中设置 QP 为 25。

    1. 最⼤参考帧数量研究。在进⾏帧间预测时,H.264 设定了两个参考帧队列, ⽤来存放⽤于进⾏预测的已经解码过的帧,当使⽤较少的参考帧时通常意味较少的计算量,但是预测的准确性有所下降,并且可能造成码率的增加。在 x264 中这⼀选项的参数格式为-r 或者--ref,默认值为 3,取值范围为 0-16。对于它的测试结果如表格3.3 所示:

    以下图像组 3.6 中五张图像的先后顺序对应于 ref 设置不同参数值时的编码图像。

    图 3.6 测试 ref 时编码图像

    由表格中的数据可以看出当最⼤参考帧的数量逐渐增加时,码率和帧率会下降,但是当 ref 值超过⼀定值时,效果就会显得不明显,同时对 PSNR 的影响⼏乎没有。

    理论上,不同分辨率的视频能够有效使⽤的参考帧数量是有限的,也和这⼀测试的结果符合。不同 ref 对应的编码图像如上图组所示,本⽂中设置 ref 为 3。

    1. 运动估计是 x264 算法中很重要的⼀部分,通过 x264 命令⾏可以控制选⽤不 同的搜索算法。使⽤不同的搜索算法时会影响运动估计的计算规模,从⽽会对压缩的准确性和速度造成影响。在 x264 中设置的格式为--me,可以设置的参数值有表格中的四种,分别是菱形搜索、正六边形搜索、可变半径六边形搜索和全局搜索。其中默认值为 hex,即采⽤正六边形搜索。测试结果如表格 3.4 所示:
    image.png

    以下图像组 3.7 中四张图像的先后顺序对应于不同搜索算法时的编码图像。

    图 3.7 测试搜索算法时编码图像

    由表格中的数据可以看出,对于此测试序列,当选⽤不同的搜索算法时,帧率会 发⽣变化,表格从左到右依次增加。但是对码率和 PSNR 的影响较⼩,使⽤全局搜索算法时运动估计时计算量最⼤帧率最⼩,采⽤菱形搜索时帧率最⾼,后者⽐前者提升了 1.48 倍。因此本⽂在系统设计时,使⽤菱形搜索算法,以获得较⾼的帧率,从⽽满⾜实时性要求。

    1. 在确定需要使⽤的搜索算法后,搜索的范围也会影响到算法的计算量和效率, 搜索的精度⽐如是采⽤整像素进⾏搜索或者 1/2 像素,1/4 像素等会影响块匹配的准确性,算法复杂度也不相同,这些会对系统性能造成影响。下⾯选取不同动态预测和分区⽅式,查看对编码性能造成的影响。在 x264 中参数选项为m,⼀共有 7 个选项:
      1、⽤全像素块进⾏动态搜索,对每个块再使⽤快速模式进⾏四分之⼀精确搜索;
      2、⽤半像素块进⾏动态搜索,对每个块再使⽤快速模式进⾏四分之⼀精确搜索;
      3、⽤半像素块进⾏动态搜索,对每个块再使⽤质量模式进⾏四分之⼀精确搜索;
      4、⽤快速模式进⾏四分之⼀像素块精确搜索;
      5、⽤质量模式进⾏四分之⼀像素块精确搜索;
      6、进⾏ I、P 帧像素块的速率失真最优化;
      7、进⾏ I、P 帧运动⽮量及块内部的速率失真最优化。

    默认的模式值为 5,表示⽤质量模式进⾏四分之⼀像素块精度搜索。

    在此对⽅式 1、3、4、5、6 和 7 这六个选项进⾏测试,测试结果如表 3.5 所示。

    image.png

    以下图像组 3.8 中六张图像的先后顺序对应于动态预测与分区⽅式设置不同参数 值时的编码图像。

    图 3.8 动态预测与分区编码图像

    由表格中数据可以看出,选取不同的动态预测与分区⽅式,对帧率的影响最⼤, 对于码率和 PSNR 的影响较⼩。从图 3.8 中的图像效果也可以看出视频的视觉效果相差很⼩,与 PSNR 的结果相符合。本⽂中设置 m 为 1。

    上述测试结果表明通过设置量化参数、最⼤参考帧数量、运动搜素算法和动态预测与分区⽅式均会对编码速度造成较⼤幅度的影响;量化参数对 psnr 的影响较⼤,但是,在实际应⽤中,不能单纯的追求图像的客观质量,需要根据图像主观上的质量对其进⾏设定,因此可以通过设置 qp 来提升编码速率;本⽂主要的设计⽬的是在保证视频质量的前提下提升编码速率,码率⽅⾯不做过⾼要求。综合使⽤不同参数对系统性能造成的影响,根据本⽂的需求进⾏恰当的设定,可以达到提升编码速率的⽬的。

    并⾏优化

    对于⼀些计算耗时的任务,可以通过使⽤多线程等并⾏处理⽅式来减少计算时间。x264 项⽬中⽀持 slice层级的并⾏编码操作,并且⽀持使⽤ pthread 来创建多线程[29],创建新线程来以 slice 为单元进⾏压缩编码,最⼤程度上可以同时对 128 个分⽚进⾏编码操作。当多线程数⽬的设置 i_threads ⼤于 1 时,使⽤ x264_pthread_create 创建新的分⽚进⾏编码。具体的,在主线程中对⽬前开启的线程数⽬进⾏判断,如果⼩于i_threads,thread_oldest 指向新线程,h->b_thread_active 被设置为 0,此时不能执⾏x264_encoder_frame_end()函数的相关代码段,主线程继续循环创建 x264_slices_write线程,当被创建的线程数⽬达到 i_threads 时,thread_oldest 被指向 i_threads 个线程中被 系 统 判 断 返 回 最 快 的 , h_b_thread_active 也 被 设 置 为 1 , 此 时 将 执 ⾏x264_encoder_frame_end()函数,在此函数中调⽤ x264_pthread_join(),从⽽阻塞主线程,直到 thread_oldest 完成后才重新创建新线程,通过此种⽅式,保证对编码部分进⾏多线程处理并且运⾏的编码线程数量等于通过 i_threads 设置的线程数⽬。通过以下操作⼤⼤减少了编码时间,提升了编码效率。对系统进⾏ pthread 多线程优化的主要操作如下:

    1. 从⽹络上下载 win64 环境下 pthread 相关的库⽂件和头⽂件。
    2. 将 pthreadVC2.lib 库的位置加⼊ vc++⽬录下⼦条⽬引⽤⽬录中,并将引⽤到的头⽂件加⼊包含⽬录。
    3. 项⽬上点击右键,选择 c/c++项⽬的⼦条⽬预处理器,在预处理定义中加⼊HAVE_PTHREAD 和 SYS_MINGW 两个参数。
    4. 在项⽬命令参数中加⼊--threads 线程数⽬,或者直接修改 x264 编码参数中的i_threads,设定为要使⽤的线程数量。

    经过上述步骤后,就可以使⽤多线程进⾏编码操作。在本⽂所涉及的系统中通过配置 threads 参数以及配置进⾏多线程并⾏分块编码。以下对本论⽂所使⽤的编码程序进⾏了并⾏优化效果测试,测试序列为 FourPeople_1280x720_60.yuv,设置参数为QP 为 25,ref 为 3,采⽤半像素精度进⾏动态搜索,并对每个块再基于 1/4 像素精度使⽤快速模式搜索。对使⽤ 1、2、4 和 8 个线程进⾏测试,测试结果如表格 3.6 所示:

    通过表格中的数据可以看出在本⽂使⽤的硬件平台上,当使⽤ 4 个线程进⾏并⾏ 操作时,帧率相对与不进⾏并⾏处理提升了 188%。由于使⽤的 CPU 是双核四线程,在使⽤ 4 线程时帧率提升幅度最⼤,充分利⽤了多线程的优势。⽤这种⽅法编码性能的提升容易受硬件条件的影响,与 CPU 的核⼼数和逻辑核⼼数有关。本系统使⽤双核四线程的 i3,使⽤ 4 线程同时进⾏编码时,实现了系统性能的优化。

    参考

    基于x264的实时流媒体传输系统设计与实现_常良⽟.caj
    作者:yang_4e3c链接:https://www.jianshu.com/p/d5d92421186a来源:简书著作权归作者所有。
    商业转载请联系作者获得授权,⾮商业转载请注明出处。

    相关文章

      网友评论

          本文标题:音视频流媒体开发【十八】H264编码原理

          本文链接:https://www.haomeiwen.com/subject/sxvsldtx.html