理解 x264、x265、vpx 中的码率控制模式
原文: https://slhck.info/video/2017/03/01/rate-control.html
原文时间:2017.03.01
翻译时间:2019.01.30
注:原文可能还在持续更新(最近一次是2018年8月),如有疑问请先查看原文和变动
什么是“码率控制”?它指的是视频编码器如何决定用多少比特数据来编码一帧画面。(有损)视频编码的目的就是在尽量保证源视频质量的同时,尽可能减少输出文件的大小。在权衡文件大小和视频质量的过程中,码率控制是至关重要的一步。
码率控制体现在多种形式上——你将会了解到 "1-pass(一次编码)" 和 "2-pass(二次编码)"、“CBR(固定码率)” 和 “VBR(动态码率)”,或许你还能知道 “VBV编码(视频缓存检验器)” 或者 “CRF(固定码率因子)”
为什么需要关心这些?我们已经看够那些用了错码率控制模式或者错误码率的视频编码命令。这篇文章是一份不同码率控制模式的简明指导,会为最终用户解释它们的适用场景。注意这不是一份码率失真优化的详细说明书。
序言:动态 vs. 固定 码率
很多人可能对音频编码器的码率控制更加熟悉,特别是那些像我一样伴随着MP3成长起来的人。为了翻录CD,我们一度使用固定码率(CBR)进行编码,直到后来动态码率编码(VBR)的出现。动态的码率确保在给定条件限制(VBR质量等级)的情况下,能得到尽可能小的文件大小和尽可能高的视频质量。
简单来说,VBR能让编码器在视频容易被压缩的部分节省数据空间,留给那些 “难以编码的内容”。怎么区分压缩的难易度呢?比如视频中大多运动部分,编码时都需要更多数据空间,因为连续视频帧之间的内容差别会更大。高空间细节和复杂纹理也一样难以编码。
编码场景是什么?
选择哪种码率控制模式和你的实际用例是强关联的。以下列举的不同场景都会影响你在设计编码管线时做出的选择:
- 归档用 —— 被压缩的文件用于在外部设备或网络空间中存储归档。输出文件应当以尽量小的文件大小保持尽可能最高的质量,但通常此时你不会在乎文件的实际大小。
- 流媒体 —— 用于通过网络在典型的视频点播服务(VoD)流媒体方案中传输文件,如HTTP渐进式下载或HTTP自适应流。你需要确保传输文件不能超过某个特定码率,或者需要为同一个文件的不同播放形式提供不同的码率设置(自适应流)。
- 直播流媒体 —— 和 2 类似,但编码过程应当越快越好,而且直播场景下你不可能提前知道媒体内容的任何信息。
- 为终端设备编码 —— 用于在DVD、蓝光机等其他终端设备上播放媒体文件。你可能会想要输出文件有一个比较固定的大小。
总之,了解实际场景会帮助你选择合适的码率控制模式。
码率控制模式
现在让我们深入了解一些不同的模式。我会围绕 x264、x265、libvpx(ffmpeg
中可用)这些当下流行的 H.264 和 H.265 编码器进行阐述。你可以在这个链接 (ffmpeg libx264, libx264rgb )中了解更多这些编码器所支持的参数。
对于 x265,编译 ffmpeg 时需要加上 --enable-libx265
,并不是所有的参数都能直接传入 x265,需要配置 --x265-params
选项。对于 libvpx,编译 ffmpeg 时需要加上 --enable-libvpx
。
提醒一句:像 x264 这样的编码器在默认情况下不会死板地将视频帧填充到不必要的数据量,这意味着如果你的编码内容很简单,最终输出的码率大小可能会小于指定给编码器的数值。别担心,记住一点就行——在有浪费情况时,为了达到目标码率而进行编码时毫无意义的。
Constant QP(CQP)
量化参数(Quantization Parameter)控制了视频帧中每一个宏区块(Macroblock)的压缩量。较大的数值,量化值更高,意味着更多的压缩,更低的质量,较小的数值代表相反的含义。H.264 的 QP 范围是 0 到 51,在 x264 和 x265 中,可以简单地为整个编码过程设置一个固定的 QP 值。注意:libvpx 不能设置固定 QP。
ffmpeg -i <input> -c:v libx264 -qp 23 <output>
ffmpeg -i <input> -c:v libx265 -x265-params qp=23 <output>
可以阅读这篇教程 以了解QP背后的原理(如果你不惧怕那些数学公式的话。。)
除非你明确知道需要 QP,否则不要使用该模式! 设置某个固定 QP 意味着最终码率会随着每个内容场景的复杂度而剧烈变化,并且会导致输入视频的编码效率低下。这可能会引起数据空间浪费,你也不能对输出文件的实际码率有所把握。
适用于: 视频编码研究
不适用于:几乎所有其他场景
Netflex 为了使每个内容场景分类都能达到最优编码效果,提出使用固定QP编码 来优化它的 per-shot 编码技术(这篇文章里 From chunks to shots 那一段解释了什么是 "shot")。但这需要大量的计算能力,并且要很小心地区分每个进行独立编码的镜头,所以固定QP不是一种“一刀切”的方法,在此之前,你需要实现一整套框架来支持它。
自适应码率(ABR)
这里我们给编码器设置一个目标码率,然后希望它能知道怎么去达到这个码率:
ffmpeg -i <input> -c:v libx264 -b:v 1M <output>
ffmpeg -i <input> -c:v libx265 -b:v 1M <output>
ffmpeg -i <input> -c:v libvpx-vp9 -b:v 1M <output>
别用这种模式! x264 的主要开发成员之一说过一定不要使用它 。为什么呢?编码器不能准确知道在后面的时间是什么内容,所以它也必须靠猜来达到目标码率。这意味着码率本身会有波动性,特别是在一段视频的开始阶段,然后在某一时刻达到目标。特别是对于HAS-type(这是什么?)流媒体,会引发短小分段之间巨大的质量变动。
这不是一种固定码率模式!尽管 ABR 从技术上来说是一种 VBR 模式,但它并不比指定某个固定码率好多少,因为输出稳定画质的可靠性实在不行。
适用于: 应急方案
不适用于:几乎所有场景。。
固定码率(CBR)
如果是用例需求,可以通过开启nal-hrd
选项强制编码器一直使用某个特定码率:
ffmpeg -i <input> -c:v libx264 -x264-params "nal-hrd=cbr:force-cfr=1" -b:v 1M -minrate 1M -maxrate 1M -bufsize 2M <output>
这种方式的输出文件必须是.ts
格式(MPEG-2 TS),因为 MP4 不支持 NAL 填充。注意,如果输入文件易于编码,这种模式会引起带宽浪费,但能保证码率在整个流媒体中保持不变。可以这篇 文章中查看更多要点。CBR 模式或许在某些应用中行得通,但在流媒体中,通常需要让码率尽可能低下来。
VP9 中的使用方法:
ffmpeg -i <input> -c:v libvpx-vp9 -b:v 1M -maxrate 1M -minrate 1M <output>
适用于: 单纯为了保持固定码率(额。。好吧~);视频流媒体(例如Twitch)
不适用于:归档;需要有效利用带宽的场景
二次编码自适应码率(2-Pass ABR)
二次(或更多次)编码让编码器对视频内容进行预估成为了可能。第一次编码会计算出编码每一帧画面的开销,然后在第二次编码中更有效地利用数据空间。这确保了在特定的码率限制下,输出画质能达到最好。
ffmpeg -i <input> -c:v libx264 -b:v 1M -pass 1 -f mp4 /dev/null
ffmpeg -i <input> -c:v libx264 -b:v 1M -pass 2 <output>.mp4
对于 x265,要将 libx264
替换成 libx265
,然后在对应的参数中设置编码次数:
ffmpeg -i <input> -c:v libx264 -b:v 1M -x265-params pass=1 -f mp4 /dev/null
ffmpeg -i <input> -c:v libx264 -b:v 1M -x265-params pass=2 <output>.mp4
VP9 和 x264 的使用方式类似:
ffmpeg -i <input> -c:v libvpx-vp9 -b:v 1M -pass 1 -f webm /dev/null
ffmpeg -i <input> -c:v libvpx-vp9 -b:v 1M -pass 2 <output>.webm
在流媒体场景中,这是编码一个视频文件最简单的方式了。有两点需要注意:你不知道最终视频的质量如何,所以需要做一些测试来确保所设置的码率对那些复杂内容是够用的;另外该模式下的负面影响是码率可能会出现局部峰值,导致客户端接受流媒体时在峰值区间会有压力或能力不足。对于码率数值的选择,Youtube 给出了一些建议 ,为了让用户上传尽可能高质量的视频,这些数值经过了优化,在实际使用中你也可以选择低一些的码率。
适用于: 输出一个特定目标码率的文件;为终端设备编码
不适用于:对编码速度有较高要求的场景(例如 直播流)
固定质量(CQ)/ 固定码率因子(CRF)
我曾在另一篇文章中详细介绍过 固定码率因子(Constant Rate Factor) 。总的来说它会在整个编码过程中给你提供稳定的质量。这是中“一劳永逸”的方法——你只需要指定 CRF 值,剩下的交给编码器就行了。
ffmpeg -i <input> -c:v libx264 -crf 23 <output>
ffmpeg -i <input> -c:v libx265 -crf 28 <output>
ffmpeg -i <input> -c:v libvpx-vp9 -crf 30 -b:v 0 <output>
在 H.264 和 H.265 中,CRF 的范围是 0 到 51 (和 QP 一样)。x264 的默认值是 23,x265 的默认值是 28。设置 18 (或对 x265 设置成 24)的效果会比较明显,比这更低的值很可能会造成空间浪费。数值 +6 会使码率减一半,-6 会使码率增一倍。VP9 的 CRF 范围是 0 到 63。CRF 的推荐范围是 15 到 35。
CRF 模式的唯一负面影响是不知道输出文件的大小,也不知道码率的波动情况如何。
2-pass 编码和 CRF 编码在输出相同码率的情况下,视频质量是相同的。主要的区别是在 2-pass 中,如有需要你可以直接控制输出文件的大小,相对地,CRF 的出发点在于设置所需质量的级别。
适用于: 归档;保存条件允许的最高质量
不适用于:流媒体;输出特定码率/文件大小
受限编码(VBV)
视频缓存检验器(Video Buffering Verifier) 提供了一种实现控制最大码率的方式。这对流媒体是很有用的,因为你能确定对某一段时间内(如码率高峰)的内容,终端设备接收的数据量不会超出的所认为的范围。 VBV 在 2-pass VBR(两次编码中都用 VBR),或 CRF 编码中都能使用——它能被“添加”到上述那些码率控制模式中。后面那种使用方式也被称为 “受限 CRF(Constrainted/Capped CRF)”。
通过 -maxrate
设置最大码率,-bufsize
设置期望端缓存大小 以开启 VBV:
ffmpeg -i <input> -c:v libx264 -crf 23 -maxrate 1M -bufsize 2M <output>
ffmpeg -i <input> -c:v libx265 -crf 28 -x265-params vbv-maxrate=1000:vbv-bufsize=2000 <output>
VP9 中有一个类似的模式,虽然不叫 VBV,但效果是一样的:
ffmpeg -i <input> -c:v libvpx-vp9 -crf 30 -b:v 2M <output>
注意:如果你在直播流媒体应用中开启了 VBV,在 x264
和 x265
中可以添加 -tune zerolatency
和 -preset ultrafast
来提高编码速度。这两个设置会为了达到特定码率(压缩效率)降低输出视频的质量,但会显著提高编码速度。对 libvpx-vp9
,需要设置 -quality realtime
和 -speed 5
。更多信息可以查看 H.264 和 VP9 的指导文档。
使用 Constrained ABR-VBV 编码:
ffmpeg -i <input> -c:v libx264 -b:v 1M -maxrate 1M -bufsize 2M -pass 1 -f mp4 /dev/null
ffmpeg -i <input> -c:v libx264 -b:v 1M -maxrate 1M -bufsize 2M -pass 2 <output>
对 x265:
ffmpeg -i <input> -c:v libx265 -b:v 1M -x265-params pass=1:vbv-maxrate=1000:vbv-bufsize=2000 -f mp4 /dev/null
ffmpeg -i <input> -c:v libx265 -b:v 1M -x265-params pass=2:vbv-maxrate=1000:vbv-bufsize=2000 <output>
对 VP9:
ffmpeg -i <input> -c:v libvpx-vp9 -b:v 1M -maxrate 1M -bufsize 2M -pass 1 -f webm /dev/null
ffmpeg -i <input> -c:v libvpx-vp9 -b:v 1M -maxrate 1M -bufsize 2M -pass 2 <output>
注意:根据 x264 开发者的说法,这里可以使用 1-pass,能和 2-pass 效果一样 ,但压缩效率可能比不上。
该怎么设置 bufsize?这取决于你能接受多大的码率波动性。简单做法是把缓存大小设置成最大码率的2倍,但这个建议会随流媒体的其他配置而变动。如果客户端的缓冲区比较小(比如只有大约几秒钟), bufsize 应该设置成差不多和最大码率相同大小。如果你想限制流媒体的码率,尝试把 bufsize 设置成最大码率的一半或更小。
当使用 VBV 或 CRF 编码时,一个小技巧是找到某个 CRF 值,测试下来输出的视频码率大概不会超出你想要的最大码率。如果在你的编码方案下,输出文件经常会“完全超出”想要的最大码率,那其中的 CRF 设置可能太低了,这种情况下,编码器会尝试使用未被分配给它的数据空间。另一方面,如果设置的 CRF 较高,输出文件经常达不到最大码率,那还可以继续降低 CRF 来获得更高的质量。举个例子:某个编码方案没有开启VBV,设置 CRF = 18,平均的输出文件码率为 3.0 Mbit/s,但想通过添加 VBV 设置限制到 1.5 Mbit/s,这时需要把 CRF 降低到大约 24 以得到只有一半的码率。
适用于: 带宽受限的流媒体;直播流媒体(1-pass,CRF);VoD流媒体(给定码率,2-pass)
不适用于:简单随意的编码尝试;归档用
对比举例
下面是对不同码率控制模式之间的简单对比。我从免费的 Big Buck Bunny 和 Tears of Steel 视频序列中分别选择了三个不同分段(时长30s)。这些视频序列是未经压缩的原始录制版本,之后将视频分段用 libx264
按默认配置编码,对比中唯一的不同是码率控制方式。我分别设置了不同的目标码率(750, 1500, 3000, 7500 kbit/s)、最大码率(VBV)、QP/CRF(17, 23, 29, 35)。可以在 Github 上查看这些对比脚本 。
注意,这份对比并不很详尽或有完全的代表性。通常,你要尝试许多不同类型的视频序列和不同的编码器进行搭配测试。我计划在不久之后对这些测试进行更新,但现在,以下的这些测试也能给你关于不同模式的一些建议。
下图中,左边一列为 3000 kbit/s,右边是 7500 kbit/s。我只保留了这两个码率是因为其他两个目标码率并未在表中显示出多大的差别,编码器在面对一个已经如此低的码率时,并没有太多数据分配上的选择。每一行是 Big Buck Bunny(BBB) 和 Tears of Steel(ToS)中的一个片段。表中的线条显示的是每一帧的大小经过 LOESS 平滑后的结果曲线——这表示了码率如何随视频分段的时间变化。
image.png你会发现——特别是前四个分段——ABR(青绿色)和 ABR+VBV(紫色)错误地估计了视频分段的复杂度。实际上,BBB 视频序列的开头是一段渐入,平滑的渐变,没有多少运动画面,这意味着为了使其压缩到一个足够好的质量,并不需要太多数据空间。2-pass 的方法正确地从一个较低的码率开始,节省了带宽。倒数第三个分段包含了大量的空间细节,2-pass 模式得以将开头省下来的数据空间用在这些细节上。
对于 BBB 的第二个视频分段,各种编码模式的表现实际上比预期要好,尽管可以再次看到 2-pass 模式比其他模式引起了更强烈的码率波动。
当然,也有一些易于编码(或者说在内容复杂度上几乎没有什么波动)的视频分段。对这些分段,不同的码率控制模式没有太多区别。
那些以视频质量为主的模式(CQP 和 CRF),我只给出 CRF/QP 17 和 23 的结果,这个范围差不多就对标的是一个“较好”的质量范围(就像 3000 ~ 7500 kbit/s 对 Full HD 视频是个“较好”的码率范围)。这里的曲线顺序就反过来了——CRF越低质量越好。
image.png在结合了 2-pass 后能发现相同的趋势:码率随着内容复杂度变化。在使用CRF时,为了在不是很必要的地方节省出数据空间,码率的选择会更加受限。最有趣的是左下角的例子:CRF 像其他例子里一样总体上比 Constant QP 更节省码率,但这里从头到尾都保持了一个大致稳定的差距。我只能去猜想为什么会这样——可能在这篇文章之后的某次更新中我会对其进行分析吧…
通常,只要我们能提前确定输出的平均码率是多少,会发现 CRF 模式在内容适应上做的很好…下面是 CRF+VBV 的表现:
image.png为一个给定的 CRF 值选择一个合适正确的目标码率/最大码率总是一件需要靠猜的事情,并且还依赖于源视频的内容。不管怎么说,当这个合适正确的值被找到时,你不会过于限制视频质量到一个很极限的程度(就像 3000 kbit/s 和 CRF 17 的那个例子)。你也不会希望码率发生很大的波动,CRF 23 是 libx264
的默认值,你会发现当设置了一个合适的目标码率(如 7500 kbit/s)后,这种编码方式能让码率的波动性对复杂内容中的不同有足够的适应能力,同时还保留了结合 VBV 模型的空间。
总结
要理解清楚不同的码率控制模式并非易事。不幸的是,虽然完全不推荐使用最简单的方案(仅指定一个固定码率值),但整个互联网还在不断流传使用这种方式的代码样例。
总的来说,你可以根据自己的使用场景,做出以下选择:
- 归档用 —— CRF模式会让你得到想要的视频质量.
- 流媒体 —— 2-Pass CRF 或 ABR 搭配 VBV
- 直播流媒体 —— 1-Pass 或 ABR 搭配 VBV,或 CBR(如果能接受带宽冗余)
- 为终端设备编码 —— 2-Pass ABR
更多资料:
- Handbrake Wiki: Constant Quality vs Average Bit Rate
- FFmpeg H.264 Encoding Guide
- x264-devel Mailing List: Making sense out of x264 rate control modes
- Video Encoding Settings for H.264 Excellence
- A qualitative overview of x264’s ratecontrol methods
- Google: VP9 Bitrate Modes in Detail
- Streaming Learning Center: Saving on Encoding and Streaming: Deploy Capped CRF
更新:
- August 2018 – 小细节更新,添加链接
- March 2018 – 添加 per-scene/per-shot 编码的相关链接
- November 2017 - 添加对 libvpx/VP9 的说明
- November 2017 – 修改了对 x265 2-pass 的错误举例,添加有关 bufsize 的说明
- June 2017 – 添加对 x265 默认 CRF 的说明
- April 2017 – 修改了对 libx265 的 2-pass 选项的错误描述