前言
好久没碰过带英伟达独显的电脑了,最近入手一台,于是想试试用 FFMpeg 搭配 GPU 硬编码器做视频编码。
对“硬件编码”的第一印象,快!之前我租用过一台 GPU 云主机,因为我注意到录屏的视频文件往往码率虚高,而自己的电脑压制很慢,于是我尝试用它的硬编码器压制视频;调试完后,看到 speed 那一栏的 11x,简直爽到飞起。相比之下,在这台云主机上用 CPU 软编码视频大概速度能到 1x,而在我可怜的过时 PC 上软编码速度甚至会跌下 0.1x。
但是硬编码也有它的一些劣势,比如说普遍认为的编码质量差(其实这个说法有待商榷,下文会提到)。这篇文章我会介绍在 Windows 平台、搭载英伟达显卡的环境下使用硬件编码器(NVENC)的过程和参数调整方法,以更好地利用硬件编码的优势。算是自己的摸索记录,整理后给大家作为参考。
“硬件加速”不等于“硬件编码”
首先让我们来明确一下这两个概念吧,虽然说不分清也不妨碍使用硬编码器压制视频。查找资料的过程中,我发现自己混淆了这两个概念,因此记录一下。
“硬件编码”是在 GPU 内完成的。调用硬编码器 NVENC 来做视频编码时,其实使用的是 GPU 中的视频编码专用电路,而非流处理器这样的通用电路,因此这个过程是不占用 CUDA 核心的。用 CUDA 核心做视频编码的话,目前应用不广泛,似乎是涉及到算法的问题。专用电路无法通过软件的升级来修改,因此视频硬编解码器是随着显卡硬件的更新换代而更新的,新推出的显卡会支持更多编码格式,编码质量也越好。例如,AV1 的硬编码只有较新的显卡(如 GeForce RTX 40 系列)才开始支持。
顾名思义,“硬件加速”是 GPU 和 CPU 共同完成的。x264 似乎是有一个 -opencl 选项能够启用硬件加速,可以调用 GPU 的 OpenCL 功能加速编解码,据说速度增加 20%;但是我试验时没有成功,也就不细究了。
何时选择硬件编码?
早期硬件编码给人们留下的印象是画质渣,但现在 NVENC 的编码质量也不错了。甚至,据说现在 NVENC 在同等情况下(花费同等时间)编码质量要好于 x264。(等我找到这个说法的出处,届时会补充在参考中。)但是,前者的参数调整不如后者灵活,这大概也是字幕组更偏爱使用软编码器的原因。毕竟他们需要的是慢工出细活,追求将原始风味压缩在最小体积中。但是像我之前的需求,压制游戏录制的视频,对保留画质没那么多高要求,这就挺适合使用硬编码的。
此外,硬件编码的高速度也足以应付实时的转码需求,这对于需要直播推流的游戏主播等用户来说一定有很大帮助。
综上所述,如果你有一个足够新的显卡、并且对于编码质量的要求并非追求极致的话,那不妨试试硬件编码吧。如果还有速度要求的话,那就选它了,基本没错。
准备
我们先准备一下使用硬件编码的环境吧。这篇文章介绍的是 NVENC,因此你得有一个英伟达显卡,是新款的那再好不过。其他的硬编码器,例如英特尔的 QSV、AMD 的 AMF,就不在本篇涉及范畴内了。
安装 CUDA 驱动
硬件编码需要这个支持。安装 CUDA 驱动之前,显卡驱动自然得先装好,这里不多赘述。从英伟达官网下载安装包。下载哪个版本?可以在终端执行 nvidia-smi,查看“CUDA Version”一栏就是最高支持的 CUDA 版本了。可能 3 GB 左右的安装包比较吓人;我下载的是网络安装版本,实测国内下载也挺快的(N卡网速快是吧)。之后就是跟着提示一步一步来即可。可以在终端执行 nvcc -V 检查是否已经安装好了。
安装支持硬件编码的 FFMpeg
如果已经安装好了 FFMpeg,在终端执行 ffmpeg -hwaccels (“hwaccel”指的便是 Hardware acceleration),检查一下是否支持硬件编码。输出支持列表中包含 cuda 便证明支持英伟达显卡编码。
我不太推荐自己编译 FFMpeg,除非你对从源码自行编译这件事非常执着。嗯,上面提到我曾租用一台云主机压制视频,但首次尝试硬编码的我,发现使用包管理器安装的 FFMpeg 根本没有硬件编码支持后,根据网上教程选择了自行编译。下载源码、一顿 make install加上其中解决一堆错误,反正这过程对于初探者还是挺困难的。最后总算是搞定了,但是折腾了近一个晚上,虽然最后大功告成很欣喜(然而压制视频质量惨不忍睹)。再者,在 Windows 上编译坑点更多,某篇教程好像提到用 WSL2 是不行的。这篇文章就不负责自行编译的引路了。
因此,为图方便,我还是选择乖乖下载现成的二进制包。FFMpeg 官网 提供了适用于 Windows 的二进制文件。这里我选择了列表中第一个,gyan.dev 提供的版本。Gyan 是 FFMpeg 的维护者之一,应该也算是可信来源。下载完整的最新发行版(ffmpeg-release-full.7z),解压到合适位置,将 bin 目录添加进环境变量即可。
使用
环境配置完毕后,我们便可以开始了。硬件编解码器的名字还算好记,只不过 CUVID 是 NVDEC 的旧称。可以执行 ffmpeg -codecs 查看所有支持的编解码器。以下列举一些常用的:
| 编码类型 | 编码器 | 解码器 |
|---|---|---|
| H.264(AVC) | h264_nvenc | h264_cuvid |
| H.265(HEVC) | hevc_nvenc | hevc_cuvid |
| VP8 | 无 | vp8_cuvid |
| VP9 | 无 | vp9_cuvid |
| AV1 | av1_nvenc | av1_cuvid |
使用硬件编码的命令也和普通方式差不多,比如说 H.265 硬件编码,你可以不额外声明使用硬编码器(我看到网上很多教程都使用了 -hwaccel cuvid),这样操作即可:
ffmpeg -i ./origin_h264.mp4 -c:v hevc_nvenc output_h265.mp4
可以再加上点参数(下文解释),如下。
ffmpeg -i ./origin_h264.mp4 \
-c:v hevc_nvenc \
-preset slow \
-cq 38 \
output_h265.mp4
参数调整
显卡压制的速度很快,但初始效果很可能不尽人意。我们可以通过调整参数来改善输出视频的质量,找到编码速度与输出视频质量之间的平衡。
要详细查看硬编码器接受的参数的话,比如说要查看 h264_nvenc 接受的参数,可以执行以下命令:
ffmpeg -h encoder=h264_nvenc
下面我结合自己的理解,解释主要的一些参数。之后我可能会补充。
preset
用于平衡编码速度和编码质量。从 p1 到 p7 共七个级别,p1 为最快速度,p7 为最高编码质量(但这并不意味着输出视频质量一定高);默认为 p4。而 hq、bd 等值已弃用,不建议在 preset 中使用。
另外,它也可以接受 slow、medium、fast 这三种值。其中 slow 选项将启用 2-pass 编码模式。
tune
接受 lossless、hq、ll、ull 四种选项,默认 hq。后两种选项对实时编码要求的/低延迟做了优化。x264 的 tune 主要是针对特殊场景优化,我理解为情景模式;但这里的 tune 我还没搞清楚它和 preset 有什么区别。
cq
类似于 x264 中的 crf,用于控制输出视频的质量,1 最高,51 最低(自然也最快),0 为自动。
有一个与 cq 相似的参数,qp,但是大多数情况下不建议使用。cq 对应的是 CRF 模式,qp 对应的是 CQP 模式。前者是按需分配,更加动态灵活,针对每个画面会用不同的 qp 编码;而后者则是平均分配,每个画面分到的码率基本相同,这就导致有些画面码率浪费,有些画面码率不足而出现马赛克色块。
要设置合适的 cq 值的话,可以自己主观感受一下,或者也可以通过 PSNR、SSIM 等指标量化。我的方法是加上 -t 30 输出一段时长 30 s 的视频以缩短试验时间,然后使用不同的 cq 输出,看看在输出的短片中是否存在轻易可见的色块等现象,以衡量不同 cq 的表现。