校准 Intel Quick Sync HEVC:28 与 30 之间的悬崖,和一个毫无作用的 preset

· 8 分钟 · tutorials ffmpeghevcqsvtranscodingvideo

一个我回答不了的问题:到底该出货哪个数字?

我的转码队列按文件逐个决定用哪个编码器:源文件本来就高效就用 copy、GPU 走 NVENC、CPU 走 x265——现在又多了一个 Intel Quick Sync。每一档都有两个决定质量的旋钮,除了 QSV,其他每一档我都有在自己片库上量过的数字。

QSV 一个都没有。而代码拒绝猜:出货默认值是 qsv_quality = 0 / qsv_preset = "",含义是「没人标定过这一档」,引擎把它当成硬排除——于是 QSV 可以存在于代码里,却在任何机器上都不会被选中,包括真的有这块显卡的那台。

所以任务不是「把 QSV 打开」,而是「为两个旋钮产出量过的数字,让这个编码器能被诚实地选中」。

问题一:硬件不在跑这个程序的机器里

队列跑在 Windows 11 虚拟机里。那台 VM 直通了一张 RTX 3060,没有任何 Intel 核显——这正是一直没量过 QSV 的原因:最顺手的主意就是在本地跑一次、把数字读出来。

核显在宿主上(unRaid,i9-13900K),而且空闲、无人占用:

00:02.0 VGA compatible controller: Intel Raptor Lake-S GT1 [UHD Graphics 770]
  driver: i915            refcount: 0
/dev/dri/card0  /dev/dri/renderD128        (存在,全局可读)
没有任何运行中的容器声明 dri 设备
virsh dumpxml Win11: hostdev = 01:00.0, 01:00.1(3060 + 它的 HDMI 音频)

两个结论都很有用。VM 里那句 Error creating a MFX session 是预期行为,不是一个要去查的 bug——那台 VM 永远看不到这块核显。而量 QSV 的唯一办法,是在宿主上、在容器里、用 Linux 侧去量。

用量尺的那个人自己的闸门来量,而不是用 ffmpeg

这是让上面这些数字真正有价值的部分。我没有自己写 ffmpeg 命令、自己算 SSIM——整场标定跑的是将来真正干活的那个引擎本体,带着它自己的校验和自己的判收闸门:

python3 library_run.py --work-dir /work --tier qsv --force-tier qsv \
  --qsv-quality Q --qsv-preset P --path <真原件> --keep-local --overwrite

--keep-local 的意思是:编码、校验、过闸门,但不落盘——我收集的是判决,不是一批新文件。素材通过已有的 SMB 挂载只读进入,而且每个源档在编码前后都做了哈希核对:未变。

镜像是 linuxserver/ffmpeg(ffmpeg 9.0.2、iHD 26.3.5、oneVPL 2.17)加 python3,再加 numpy——后者不是可选项。没有它闸门会打 flat-frame scan skipped: No module named 'numpy',悄悄改变近平坦帧的判定方式,也就改变了判决。

-global_quality 在 hevc_qsv 上对应 RateControlMethod: ICQ,这就是被标定的旋钮。

质量阶梯:在瘦源上,低 ICQ 会把文件变大

第一轮,源是一支真实的 1080p25、951 kbps(bpp 0.0184——已经很瘦、榨过一遍的那种):

ICQ引擎判决mean SSIM视频 = 源
18FAIL size — 176.8%——
20FAIL size — 141.8%——
22FAIL size — 107.6%——
24ok0.996781.1%
26ok0.995961.0%
28ok0.995146.4%
30FAIL quality0.9942—
32FAIL quality0.9942—

这张表掉出三个结论。

第一个反直觉的:低 ICQ 不等于安全。ICQ 18–22 产出的文件比源还大,于是被引擎的体积闸门(源视频码率的 100%)拒掉。这不是 QSV 独有的毛病——NVENC 对瘦输入用低 cq 也是同一个形状——但这里的边界更靠上:可用区间从 24 起,不是 18。

第二个,28 与 30 之间有一道悬崖。30 和 32 都质量失败,而且失败在首次尝试。这句话很重要,因为引擎在质量失败时会用 global_quality - 2 重试一次——ICQ 更低、质量更高——并把重试写进日志。一行写着「ok at quality+2」的日志其实是首次尝试失败,不是通过。我第一遍在自己的笔记里把这条读反了;规矩是读首次尝试,不读救济结果。

第三个,地板决定取舍:在 0.995 的 mean SSIM 下限下,q28 的余量是 0.0001,q26 的余量是 0.0010。

完整文件确认了这张阶梯——而且有一个捷径站得住

摘录(180 秒无损切片)是「不用跑十次完整编码就能排出候选顺序」的办法。它只有在与完整文件结果一致时才成立,所以我把真原件端到端跑了一遍:

源ICQpreset视频 = 源文件SSIM min / mean编码
254 MB,2023 秒,872 kbps26medium61.0%168,435,266 B(66%)0.9938 / 0.9960360 秒 ≈ 5.6× 实时
同一支28medium46.3%54%0.9927 / 0.9952330 秒
315 MB,3280 秒,546 kbps26medium50.4%60%0.9965 / 0.9977600 秒 ≈ 5.5× 实时

第一支的 180 秒摘录量到 mean 0.9959,完整文件量到 0.9960。这个接近程度足以让人相信「摘录用来排质量档」这条路——而且现在这是有证据的,不是靠希望。

那个毫无作用的 preset

摘录那一层给出的故事很干净:preset 只影响速度——veryfast 30 秒 / 61.6%、fast 30 秒 / 61.0%、medium 60 秒 / 61.0%、slow 与 veryslow 60 秒。结论似乎就是选 veryfast,每支文件省 30 秒。

然后完整文件否掉了它。同一支源、同一个 ICQ 26,一次用 fast、一次用 medium:

fast:    168,435,266 B   编码 360 秒   mean SSIM 0.9960
medium:  168,435,266 B   编码 360 秒   mean SSIM 0.9960

逐字节同大小、同编码时间、同质量。摘录层的 30 秒对 60 秒,是每一轮固定开销造成的差别,不是编码速度——这正是那种「如果只走到一步就停」会写进配置文件、再写进一篇博客的数字。

所以:摘录只能排质量,永远不能排速度。 速度必须在完整文件上量,固定开销在那里才会消失。

最后落了什么

qsv_quality = 26、qsv_preset = medium——源视频码率的 61.0%,mean SSIM 0.9960(min 0.9938),约 5.6× 实时;在片库里最瘦的一支上是 50.4%、mean 0.9977。q28 相对下限的余量只有 0.0002,而一次质量失败的代价是两次整片编码再加一次搁置,所以那多出来的 15 个百分点不值得换这条尾部风险。

另外两条值得记下来的量测,它们回答的是「到底是 QSV 的问题还是文件的问题」:

四个坑,每个都花掉一轮

  1. 永远不要把共享片库挂到容器的 /lib。 我把 SMB 共享挂进了容器的 /lib——而那是容器自己的动态加载器目录。八个作业全在 1 秒内死掉,报 exec /bin/bash: no such file or directory,读起来像镜像坏了,而不是挂载点错了。挂 /library 就正常。
  2. 合成源会给出与事实相反的 ICQ 结论。 hevc_qsv 的码率控制对 rgb24 输入会被驱动拒绝(Selected ratecontrol mode is unsupported),所以用 testsrc/mandelbrot 自检会得出相反的结论。要么用真文件,要么加 format=nv12。
  3. 已经是 HEVC 的源根本没法标定。 闸门会回 SKIP already HEVC hvc1,--overwrite 也不解除。这个片库里肥档 1080p H.264 原件早就转完了,剩下还在肥档的都是 HEVC——所以那一档已经没有可量的素材了。这是缺输入,不是漏做任务。
  4. 读首次尝试,不读重试(见上面那道悬崖)。

如果重来一次

  1. 速度要在你真正会跑的那个产物上量,不要在它的一片切片上量。整件事里我唯一报错的数字,就是信了摘录的墙钟。
  2. 先让机器证明它有没有这块硬件,再排测试计划。 六分钟的 lspci 加 virsh dumpxml,就把「为什么 QSV 在 VM 里失败」这个悬了几周的问题结掉了。
  3. 把闸门留在回路里。 因为每一次运行都由引擎自己的校验判收,上面这些表格是决策,不是观点——将来在生产里会拒掉坏文件的,正是这个把我的 ICQ 30 拒掉的闸门。
  4. 把诚实的适用范围写进文档。 这些数字是 UHD 770 上 Linux iHD 26.3.5 + oneVPL 2.17 的数;生产目标是同一块硅片上的 Windows MSDK 路径,我不能声称两者一致。这里站得住的是:命令在真硬件上跑得通、质量↔体积曲线的位置、以及锁的行为——留给将来的,只是「在 Windows+Intel 上按同一张表复测一次」,而不是又一个下午。

结果

QSV 从「存在于代码里、永远选不到」变成了一个带公开数字的已标定档,安全规则也跟着改了:原来那条钉住「出货默认值必须代表未标定」的测试,现在钉的是「出货默认值必须是量过的值」,同时保留「空值 = 硬排除」的全部断言。在没装 Intel 显卡的机器上,doctor 那行也从信息级的「等硬件」变成了警告——这本身是另一个小教训:一个已标定的编码器自检失败该长什么样,以及为什么机器上不存在的硬件应该进一行汇总,而不是进警告。

Lee Teong Hoe

Lee Teong Hoe · Mr Hoelee

Full-stack developer & DevOps engineer based in Malaysia. I run my own production infrastructure and build web systems for small businesses — this blog is the living record.

Hiring? Profile & stack · Malaysian SME? Website design & development ↗ · All posts