x264 码率控制算法原理

x264 的码率控制是如何实现的 ?

本文介绍 x264 码率控制算法的数学原理,和基本框架。

 

1.理论假设

有助于往下阅读,可以暂时跳过。

设变量:      qscale = 拉格朗日常数 lamda = 0.85 * 2^((QP-12)/6)  ≈ 量化步长 Qstep = 2^((QP-4)/6)

设变量:      复杂度 complexity  ≈ 运动补偿后残差的satd

                    二次编码中: complexity  ≈  第一趟编码的实际比特数

设变量:      bits   =  实际编码后比特数

假设1:          qscale不变, bit 和 复杂度 成正比  

假设2:          复杂度不变, bit 和 qscale 成反比

也就是说:   complexity   ∝ qscale * bits  

       或者:    bits   正比于  complexity  / qscale 

x264 的码率控制的基础是基于这个假设。

虽然它不是很精确,但是工程上足够了。

 

2. 基本公式

有助于往下阅读,可以跳过

qscale = 0.85 * 2^((QP-12)/6)              ----- (1)

QP = 12 + 6 * log2(qscale/0.85)               ----- (2)

Qstep = 2^((QP-4)/6)                                ----- (3)

QP =  6 * log2(Qstep) + 4                    ----- (4)

 

3. 基础算法–帧间级

x264 码率控制分为帧间级 和 帧内级,这里先介绍帧间级码率控制算法。

帧级别码率控制需要为每一帧分配QP,  也就是分配每一帧的大小。

每一帧的大小主要取决于两个变量: 本帧的复杂度,码率预算。

复杂度更高的帧,需要更多的比特。但给多少码率,和复杂度不是正比的。

下面是x264模块控制模块的简化框图。包括了帧间级码率控制,和帧内级(宏块级)码率控制。

x264 码率控制算法原理_第1张图片

x264 中, 首先根据每帧的复杂度,在帧与帧之间分配码率比例。然后根据码率预算,将每帧缩放到合适大小。这个缩放系数称为 ratefactor 。

然后 qscale = complexity / ratefactor 

所谓 crf (constant ratefactor) 模式,就是固定ratefactor 参数, 码率分配由复杂度决定。(这里暂不考虑宏块级码率控制)

ABR 和 CBR 模式,都是通过实时调整 ratefactor 实现的。 

qscale 最后会再经过 vbv 模块调整,确保不会发生缓冲区下溢。vbv模块也是实现CBR模式的关键模块。

本文主要关注帧间级,它的主要框架如下:

x264 码率控制算法原理_第2张图片

下文会详细解释每个模块。

 

4. 模糊复杂度估计

码率控制的第一步是根据每一帧的复杂度,在帧与帧之间分配码率比例。

一趟编码中,由经过运动补偿后残差的satd代表一帧的复杂度。

X264中使用模糊复杂度作为码率分配的根据。相比使用单独一帧的复杂度,这样能避免QP的波动。

一趟编码中,模糊复杂度基于邻近已编码帧的复杂度加权得到:

其中,Cplxsum 和 Cplxcount 更新方法如下:

这一部分代码位于函数 rate_estimate_qscale():

rcc->last_satd = x264_rc_analyse_slice( h ); //得到 satd

rcc->short_term_cplxsum *= 0.5;

rcc->short_term_cplxcount *= 0.5;

rcc->short_term_cplxsum += rcc->last_satd / (CLIP_DURATION(h->fenc->f_duration) / BASE_FRAME_DURATION);

rcc->short_term_cplxcount ++;

rce.blurred_complexity = rcc->short_term_cplxsum / rcc->short_term_cplxcount;

 

4.1 感知编码优化

引用自x264官方文档:

“ You want the movie to be somewhere approaching constant quality. However, constant quality does not mean constant PSNR nor constant QP.

Details are less noticeable in high-complexity or high-motion scenes, so you can get away with somewhat higher QP for the same perceived quality”

恒定的质量并不代表恒定的QP, 对于高复杂度的场景,细节丢失比较难以发现,因此可以使用比较高的QP。

根据上述原理,x264对帧的复杂度进行了非线性”压缩”:

rceq 代表压缩后的“复杂度”,是码率分配的依据。

qcomp 一个外部可调的参数。

当qcomp = 1,各帧的比重(rceq)都一样,分配给平缓的帧和复杂的帧的比特是一样的。

当qcomp= 0,  各帧的比重和其复杂度成正比(各帧QP相等)相当于关闭了此项感知编码优化。

 

5. Ratefactor

5.1  简介

Ratefactor 参数用于控制总体码流的大小,它只用在一趟编码中。

ratefactor 和 复杂度rceq 一起,计算出 一帧的 qscale: 

                          --------------------------(5)

一趟编码中,由于未知未来帧的复杂度,所以ratefactor的选取是基于已编码帧进行的。

选取的依据是:假如选定的ratefactor被应用到所有已编码帧中,那么得到的是希望的码率。

“We don't know the complexities of future frames, so we can only scale based on the past.

The scaling factor is chosen to be the one that would have resulted in the desired bitrate if it had been applied to all frames so far.” 

--- 引自官方文档

这段话比较难以理解,下文会有详细解释。

我们先看看ratefactor的计算方法。

 

5.2 计算方法

Ratefactor 根据已编码帧计算:

           -------------------------------(6)

其中,wanted_bits_window 表示已编码帧的目标文件大小。Cplxr_sum 是一个系数。

两个变量的初始值为:

每编完一帧,更新这两个系数(ABR模式):

   x264 码率控制算法原理_第3张图片

其中,bitrate表示目标码率,fps表示帧率,bits表示实际编码得到的帧大小。

可以看到,wanted_bits_sum变量是累积的目标码率的大小。

Cpxr_sum 变量代表了bits*qscale/rceq的累积值。

 

5.3 原理解释

为什么ratefactor这么计算呢?

回忆第一章(理论假设),有:

complexity  正比于 qscale * bits

所以,可以设

A* complexity = qscale * bits              ------- (7)

A 是比例系数。

有: A = bits*qscale/complexity

这里,complexity = rceq

可以看到,cplr_sum 其实是A的累积值。

Bits 是实际得到的文件大小,跟目标大小(wanted_bits_window)可能不一样。

根据式(7),可以调整qscale,让码率“正确”:

用wanted_bits_window代替每帧大小 wanted_bits_per_frame; 用cplr_sum 代替A :

分母其实就是ratefactor(式6),带入上式子,得到:

这里,complexity 是历史帧的平均复杂度。

假设用这个qscale重新编码历史帧,可以得到目标文件大小 wanted_bits_window。

用当前帧的复杂度代替历史帧的平均复杂度:

 

就是式(5)qscale的求法。

总结而言,就是6.1节所说: “ratefactor的选取是基于已编码帧进行的。选取的依据是:假如选定的ratefactor被应用到所有已编码帧中,那么得到的是希望的码率”。

得到ratefactor之后,再结合当前帧的相对复杂度,就可以得到当前帧的qscale。

 

5.4 CBR 模式

CBR 模式需要保持码流在局部的稳定性。所以,ratefactor 不是根据所有已编码帧计算,而是根据局部已编码帧计算:

          

    -------------------------------(6)

通过给累积项添加一个衰减系数cbr_decay,让raterfactor主要由邻近的已编码帧决定。

Cbr_decay系数是一个由vbv-bufsize, vbv-maxrate, bitrate 决定的系数。

 

5.5 CRF 模式

CRF 模式也叫恒定质量模式。这种模式下编码的视频图像质量最好。

在CRF模式下,ratefactor是固定不变的。也就是说,没有全局的码率控制,视频的最终码率是不确定的。

ratefactor的计算基于 命令行参数 --crf 设置的质量参数(rf_const) 。计算方法如下:

 

上式中,base_cplx是根据经验得出的常数。

qp2qscale是从从qp变换到qscale的函数,公式可见第二章。

mbtree_offset是针对mbtree算法的偏置。

外部设置的crf 值和编码器内部使用的ratefactor是不一样的。

通过上述式子的变换,让外界输入的rf_const 跟最终编码得到的QP近似。

 

6. VBV模块

VBV 模块用于控制接收端缓存不上溢不下溢,它实质是对视频短时码率进行限制。主要用于CBR模式中。

关于VBV模型的更多基础信息,可以参见前一帖子: 视频码流控制类型和内涵。

VBV 控制流程主要位于函数clip_qscale()中。根据有没有lookahead, 处理流程不同。

clip_qscale()计算qscale,分配帧级码率预算。此外, x264 还有宏块行级别的码率控制,保证vbv码率预算的执行。

clip_qscale()函数的主要处理流程如下图 : 

x264 码率控制算法原理_第4张图片

6. 1 lookahead vbv调整

从lookahead 模块可以得到未来若干帧的复杂度。

vbv算法的原理是: 一样的qscale应用到lookahead中的所有帧中,

检测不会有帧让vbv缓存下溢,并且lookahead中所有帧编码结束后,缓存填充度在一个合理的范围内。

小步调整qscale,直到上述要求被满足。

 

6.2 实时 vbv 调整

如果没有lookahead, 未来帧的复杂度未知,只能根据当前帧的复杂度,控制缓存的充满程度。

算法主要流程是:

1,对于P帧和第一个I帧,让当前帧编码完成后,缓存区至少还有一半容量。

2,限制每帧大小不能超过当前换存量的一半。

3,限制每帧大小至少是buffer_rate 的一半。buffer_rate =vbv-maxrate/fps 。

4,限制qscale不能小于输入qscale。(有可能作废步骤3的更改)

 

6.3 minGOP vbv 调整。

B帧QP不直接被vbv调整,它由P帧加一个偏置得到。

这一步检查当前P帧和(编码顺序)到下一个P帧之前的B帧的复杂度。

适当调低qscale (调高码率预算), 使得本minGOP过后,缓存区没有上溢。

 

7. 总结

可以看到,x264的码率控制,是多个模块联合作用的结果。

并且对于不同的编码模式,所用到的模块有所不同 。

7.1 一趟ABR模式

通过复杂度和ratefactor控制总体码率大小。

vbv模块可以选择使用或者不使用,如果使用vbv, 会取得更加稳定的码率,同时降低图像质量。

7.2 一趟CBR模式

通过复杂度和ratefactor控制局部码率大小,得到可以比较合理的qscale,

然后通过vbv模块,确保码率恒定。

7.3 CRF模式

固定质量参数,只根据复杂度分配码率,最终码率未知。

一般不使用 vbv 模块。

 

8. ref 

  1. 源代码
  2. 源代码中文件: \doc\ratecontrol.txt
  3. 殷海兵.  数字视频编码-算法优化理论、方法和芯片实现[M].  北京. 电子工业出版社. 2015.
  4. https://onlivetest.wordpress.com/2015/02/20/x264-rate-control-part-one/

你可能感兴趣的:(x264,h264,算法)