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

然后 qscale = complexity / ratefactor

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

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

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

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

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

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 = 0,qscale 与复杂度成正比,分配给平缓的帧和复杂的帧的比特是一样的,行为接近CBR.

当qcomp= 1,   qscale 与复杂度无关(各帧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模式):

其中,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()函数的主要处理流程如下图 :

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 码率控制算法原理相关推荐

  1. X264码率控制流程分析

    码率控制的理论知识: 码率控制的目的和意义: 图像通信中码率控制的目的:通过调节编码参数,控制单位时间内的编码视频流的数据量,以使产生的比特流符合各种应用的需求.视频压缩的效率和视频内容有很大的关系, ...

  2. X264码率控制流程分析 (转)

    二.编码器机能20钟头前 码率节制的意见常识: 码率节制的目的以及意义: 图象通讯中码率节制的目的:路程经过过程调治编码参量,节制单元时间内的编码视频文件流的数值量,以使 ... 二.编码器机能20钟 ...

  3. H.264的码率控制算法

    H.264的码率控制算法采用了多种技术,其中包括自适应基本单元层(Adaptive Basic Unit Layer).流量往返模型(Fluid Traffic Model).线性MAD模型.二次率失 ...

  4. X264码率控制总结1——ABR,CQP,CRF

    lakeone X264码率控制总结1--ABR,CQP,CRF 1. X264显式支持的一趟码率控制方法有:ABR, CQP, CRF. 缺省方法是CRF.这三种方式的优先级是ABR > CQ ...

  5. 【Codecs系列】X264码率控制总结1——ABR,CQP,CRF

    X264码率控制总结1--ABR,CQP,CRF https://blog.csdn.net/chinabinlang/article/details/88837615

  6. JM编码器码率控制算法笔记:以JVT-G012r1为例

    应用率失真优化需要三个算法/模型 根据信源输入及选择的编码框架,建立合适的R-D模型(通常是R-Q.D-Q关系): 根据给定的码率R以及缓冲区状态,为每个编码单元分配比特并带入R-D模型求解Q,并在编 ...

  7. H.264码率控制算法研究及JM相应代码分析(二)

    在前一篇文章的基础上,现在先看一下MPEG4 编码标准中应用的码率控制算法,总结起来,各大算法都是在解决两个问题:RD 率失真的优化以及避免缓冲区的上溢下溢. MPEG-4 VM8 码率控制算法 在这 ...

  8. HEVC码率控制算法1TEncRateCtrl

    TEncRateCtrl类是码率控制的总类,后续的序列,GOP,帧都是基于该基础 码率控制的总流程为 1.在TEncTop::create中,定义了TEncRateCtrl的码率控制初始化,其中序列级 ...

  9. PID控制算法原理通俗讲解

    一文读懂PID控制算法(抛弃公式,从原理上真正理解PID控制) PID控制应该算是应用非常广泛的控制算法了.小到控制一个元件的温度,大到控制无人机的飞行姿态和飞行速度等等,都可以使用PID控制.这里我 ...

最新文章

  1. OpenCV——简易图形画法:画奥运五环
  2. 为什么我的子线程更新了 UI 没报错?借此,纠正一些Android 程序员的一个知识误区...
  3. 【xinfanqie】五方面快速解决Win8蓝屏问题
  4. [Qt] 利用QtWebKit完成JavaScript访问C++对象
  5. js在一个指定元素前添加内容_WebAR开发指南(1)---使用AR.js实现第一个WebAR demo...
  6. jfinal html5,Jfinal框架整合webSocket技术功能实现
  7. php 请求远程链接
  8. python写爬虫4-多线程爬虫(采集58出租房信息)_python实现多线程爬虫
  9. pci串行端口找不到驱动程序_科普:PCI-E插槽都有哪些样子?
  10. struts2 模型驱动封装
  11. SharePoint 2010 中型场(Farm)——性能研究(来自DeLL技术中心博客)
  12. RPM包安装卸载命令
  13. matlab实现单极性,怎样用matlab画单极性非归零码传输码型示意图?请各位大虾赐教...
  14. 在windows 10上编译bitcoin源码
  15. Python项目实战:抓取全网王者荣耀皮肤,收藏最好看的
  16. 如何用python来打印一个三角形
  17. MFC Windows 程序设计[192]之六只眼八卦图按钮组(附源码)
  18. 【WPS表格】创建表格,汇总行
  19. 【电机控制】Arduino mega 2560控制42步进电机接线
  20. ps字体安装快捷安装方法

热门文章

  1. 百度2023届暑期实习生面经-产品运营岗
  2. 基于JAVA毕业设计的客户关系系统的设计与实现
  3. 【学习路线】——JAVA工程师成神之路
  4. 怎样把电脑上的歌曲格式转换成MP3格式
  5. 刘汝佳 语言篇(一)
  6. 电子制造企业如何做好商机管理,促成更多交易
  7. 网络虚拟化之virtio-net和vhost
  8. Oracle 動態取周次
  9. 个人成长 | 总结2018--展望2019.
  10. journalctl -xe命令(系统日志查询)的使用