ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

FFmpeg Intel QSV 硬编解码:零拷贝与码率控制实战

FFmpeg Intel QSV 硬编解码:零拷贝与码率控制实战 三年前接过一个把两百多个 4K H.264 素材统一转成 HEVC 的活当时手上只有一台八核的机器用 libx265 的 medium 档跑到最后是 0.3x 左右的速度——也就是一小时的素材要跑三个多小时。两百多个文件排下来光转码就得排到第二天。后来把同一批素材丢给 ffmpeg 的 hevc_qsv速度直接拉到 6x 上下画质肉眼看不出差别功耗还低了一截。从那时候起Intel QSV 就成了我处理批量转码任务时的默认选项。这篇文章想聊的就是 ffmpeg 硬编解码里 Intel QSV 这条线它到底靠什么在跑、驱动和编译参数上有哪些容易踩的雷、解码编码怎么拼才是真正的零拷贝、码率控制怎么调才不浪费硬件、以及那些报错和花屏到底该怎么排。不管你是刚听说-hwaccel qsv想试试水还是已经在用但总觉得好像没跑对下面的内容应该都能对得上号。1. 为什么我最后选了 QSV一次多路转码的选型复盘1.1 从 0.3x 到 6x被逼到硬编的那次任务先还原一下当时的场景。素材是 3840x2160 的 H.264码率 60Mbps 左右目标是把它们压成 1080p 的 HEVC码率控制在 8Mbps。用 libx265 的话1080p 输出配 medium 档单路实际速度只有 0.3x 到 0.4xCPU 八个核心全部跑满风扇直接起飞。我试着降档到 ultrafast速度是上来了但码率控制完全失控同码率下画质崩得厉害。换成 QSV 之后链路变成核显硬解 H.264 VPP 缩放 核显硬编 HEVC速度稳定在 5.5x 到 6.5xCPU 占用从 800% 掉到 200% 以下剩下那点占用主要是 demux、容器封装和音频转码。这里的关键不在编码器快多少倍而在于整条链路里最耗 CPU 的两件事——解码和缩放——都被挪到了核显的固定功能单元上。如果你只做纯编码不做缩放QSV 的收益会小一些但依然明显。原因在于 QSV 用的是 ASIC 专用电路它不像 CPU 那样需要在通用核心上跑复杂的运动估计搜索而是把 SAD、变换量化这些运算固化在硅片里。代价是搜索策略不如 x265 那么聪明同码率下的画质通常会差一点点具体差多少后面第 6 节有实测数据。1.2 QSV、NVENC、VAAPI 三条路的真实差别很多人第一次接触硬编看到-hwaccel qsv、-hwaccel cuda、-hwaccel vaapi会懵觉得都是用显卡加速。实际上这三条路的定位完全不同。方案依赖硬件典型编码器特点QSVIntel 核显 / Intel Arc 独显h264_qsv、hevc_qsv、av1_qsv走 oneVPL(原 Media SDK) 接口跨 Windows/Linux滤镜链完整NVENCNVIDIA 显卡h264_nvenc、hevc_nvenc、av1_nvenc消费卡有并发 session 数限制画质调优空间大VAAPILinux 上的多种 GPUh264_vaapi、hevc_vaapi是 Linux 图形栈的通用抽象层Intel/AMD 都能用QSV 和 VAAPI 在 Intel 平台上有很大重叠——底层其实都可能调用同一套 iHD 驱动但 QSV 这一侧多了一层 VPL 接口好处是滤镜支持更完整vpp_qsv能做的色彩空间转换、缩放、去隔行在纯 VAAPI 通路里往往要拆成好几个 filter 才能拼出来。而 VAAPI 的优势是更通用同一份脚本换到 AMD 的机器上稍作修改就能跑。我的实际选择逻辑是这样的如果是 Intel 平台且需要做完整的解码—处理—编码链路优先 QSV如果是 Intel 平台只想单纯硬编、不做显卡上的处理VAAPI 也够用如果是 NVIDIA 平台那就 NVENC 没得选。这个判断在我后面的项目里基本没出过错。1.3 QSV 的硬件门槛你的 CPU 到底支不支持QSV 不是所有 Intel CPU 都有。核心门槛是 CPU 里要带 Intel 核显且代号在 Gen9Skylake2015 年之后。这条线的划分大概是这样的Gen9 到 Gen9.5Skylake / Kaby Lake / Coffee Lake支持 H.264 和 HEVC 8bit 的编解码解码能到 4K编码到 4K 也勉强10bit HEVC 只有解码支持编码不支持。Gen11Ice LakeHEVC 10bit 编码开始支持VP9 编码加入。Gen12Tiger Lake / Alder Lake / Rocket LakeHEVC 编解码能力和吞吐都上一档低功耗 VDENC 通路更成熟。Arc 独显 / Meteor Lake 之后的核显AV1 硬件编解码加入这是目前最值得关注的一档。有几个坑必须提前说。第一个是带 F 后缀的 CPU 没有核显比如 i5-12400F、i7-9700F这些型号装完 ffmpeg 你会发现在-hwaccels里根本看不到 qsv不是配置问题是硬件压根没有。第二个是服务器平台Xeon E3/E5 大多不带核显部分带 P 后缀的除外拿这类机器做 QSV 是白费功夫。第三个是主板 BIOS 里核显被禁用尤其是插了独显之后很多主板默认把 iGPU 关掉需要在 BIOS 里找到 iGPU Multi-Monitor 之类的选项打开。提示判断有没有核显最直接的办法是看设备节点。Linux 下ls /dev/dri/如果能看到renderD128说明至少有一个能用于计算的 GPU 节点如果同时有renderD128和renderD129那可能是核显加独显都在。2. 让 FFmpeg 真正识别 QSV驱动、编译参数与自检清单2.1 Linux 下 iHD 驱动的选择以及 i965 这个老名字Linux 上跑 QSV第一道坎是 libva 驱动。Intel 的 VA-API 驱动有两个老的i965-va-driver和新的intel-media-va-driver内部代号 iHD。分界线同样是 Gen9Gen9 及以后用iHDintel-media-va-driverGen8 及以前Haswell、Broadwell只能用i965Skylake 是个尴尬的交叉点两个驱动理论上都支持但 i965 在新内核上已经很久没人维护实际用起来问题多。我一般直接上 iHD。安装命令Debian/Ubuntu 系sudo apt update sudo apt install -y libva2 libva-drm2 libva-x11-2 vainfo # Gen9 及以后 sudo apt install -y intel-media-va-driver-non-free # 如果是 Gen8 及以前 # sudo apt install -y i965-va-driver注意那个-non-free后缀不是笔误。Intel 把一部分编解码固件单独放进了 non-free 包不装的话vainfo能过但 HEVC 编码这类功能会莫名其妙地初始化失败报一堆unsupported parameter。装完之后要显式指定驱动别指望系统自动挑export LIBVA_DRIVER_NAMEiHD vainfovainfo的输出里重点看两件事Driver version是不是 iHD 而不是 i965以及VAProfileHEVCMain、VAProfileH264High这些 Profile 后面的VAEntrypointEncSlice编码入口在不在。如果只有VAEntrypointVLD解码说明你这个平台只支持硬解不支持硬编只能拿来做解码加速。2.2 Windows 侧核显驱动版本比什么都重要Windows 上的 QSV 走的是 D3D11 路径不需要单独装 libva 那一套但核显驱动版本是硬门槛。Windows 自带的微软基本显示适配器驱动是没有媒体能力的必须装 Intel 官方驱动。我有一次在虚拟机里测试核显直通之后 ffmpeg 能列出 qsv 编码器但一跑就报Error initializing an internal MFX session: unsupported (-3)。排查了半天发现是驱动版本太旧更新到当年的正式版驱动之后直接就好了。判断驱动够不够新的方法很简单在设备管理器里看显示适配器右键属性看驱动日期一般建议不要低于两年的版本。另外 Windows 上还有一个容易忽略的点——如果用完整的 FFmpeg 静态包最好选带 qsv 支持的构建。有些精简版构建把 QSV 裁掉了-encoders里看不到h264_qsv这时候不是驱动的问题是二进制的问题换一个 full 或 gpl 版本即可。2.3 自己编译 FFmpeg 时--enable-libvpl 和 --enable-libmfx 的区别如果你用的是发行版自带的 ffmpeg大概率已经带了 QSV 支持用ffmpeg -encoders | grep qsv确认一下就行。但如果要自己编译这里有个版本分水岭FFmpeg 6.0 之前用--enable-libmfx依赖 Intel Media SDK。FFmpeg 6.0 及之后用--enable-libvpl依赖 oneVPL。libmfx 被标记为废弃。依赖装的命令大概是sudo apt install -y libvpl-dev libva-dev libdrm-dev编译时的关键参数./configure --prefix/usr/local/ffmpeg \ --enable-libvpl \ --enable-gpl \ --enable-libx264 --enable-libx265 \ --disable-doc这里有个很常见的失败场景configure 跑完输出里libvpl那一行显示no但前面几步都没报错。原因通常是libvpl-dev装了但 pkg-config 路径不对或者系统里同时存在新旧两套头文件造成了冲突。检查办法是pkg-config --modversion vpl能输出版本号才说明环境是通的。2.4 三条命令确认 QSV 是否真的可用环境配完别急着上大文件按下面三步走能省掉大量试错时间。第一步看硬件加速后端列表ffmpeg -hwaccels | grep qsv有输出说明 ffmpeg 支持 qsv 这个后端。第二步看编解码器是否注册ffmpeg -hide_banner -encoders | grep qsv ffmpeg -hide_banner -decoders | grep qsv正常应该能看到h264_qsv、hevc_qsv、mpeg2_qsv、vp9_qsv这些新平台还会有av1_qsv。第三步也是最关键的一步做一次真实的初始化和编码测试别只看列表ffmpeg -hide_banner \ -init_hw_device qsvhw:/dev/dri/renderD128 \ -filter_hw_device hw \ -f lavfi -i testsrcsize1280x720:rate30 \ -vf hwuploadextra_hw_frames64,formatqsv \ -c:v h264_qsv -t 2 -f null -这条命令在显存里生成测试图、上传成 QSV 帧、再走一遍编码器跑通不报错才算环境真的没问题。如果这一步失败前面的列表再好看也没用。3. 解码、编码、零拷贝QSV 流水线的三种拼法3.1 -hwaccel qsv 与 -hwaccel_output_format qsv 的分水岭这是 QSV 用得对不对的核心分界线也是最多人搞混的地方。-hwaccel qsv只告诉 ffmpeg解码用 QSV但解出来的帧默认会被拷回系统内存也就是普通的内存 buffer。如果你后面接的是 CPU 滤镜比如scale这个行为是正确的。但如果你后面接的是 QSV 编码器那帧就得再传回显存白白多了一次 PCIe 往返拷贝。-hwaccel_output_format qsv才是让解码结果留在显存里的开关。加上它之后帧的类型变成 QSV 帧可以直接喂给vpp_qsv或者*_qsv编码器不用下载。两条命令的差别# 方式 A解码留在显存外帧会下载到内存 ffmpeg -hwaccel qsv -i input.mp4 -c:v h264_qsv output.mp4 # 方式 B解码留在显存后续可以直接接 vpp_qsv / *_qsv ffmpeg -hwaccel qsv -hwaccel_output_format qsv -i input.mp4 \ -c:v h264_qsv output.mp4看着只多了一个参数实际吞吐能差 20% 到 40%具体取决于分辨率。4K 素材差异尤其明显因为一帧 NV12 的 4K 数据差不多 12MB来回拷几次带宽就吃满了。一个很实用的判断技巧如果你写完了链路发现 CPU 占用里有一大块是 sys 而不是 usr八成就是在做多余的内存拷贝。3.2 只做硬编CPU 帧怎么正确地喂给 qsv 编码器有些场景只需要编码加速解码还是想用 CPU 的软解比如源是 HEVC 10bit 而平台解码能力不够。这时候链路是CPU 解码 CPU 滤镜 QSV 编码。这种情况下不需要-hwaccel_output_format qsv反而是需要hwuploadffmpeg -i input.mp4 \ -vf scale1920:1080,formatnv12,hwuploadextra_hw_frames64 \ -c:v hevc_qsv -global_quality 24 \ -c:a aac -b:a 128k \ output.mp4这里formatnv12这一步容易被忽略。QSV 编码器接受的像素格式有限主要是 NV128bit和 P01010bit。如果前一步 scale 出来的是 yuv420p虽然名字上看着像但内存布局不同hwupload 之后可能报格式不支持。显式加一句formatnv12能省掉一堆排查时间。extra_hw_frames64这个参数也值得说一下。它的作用是给硬件帧池预留额外的帧数。默认值在某些高延迟场景下会不够用导致滤镜链卡住或者报Cannot allocate memory。64 是个比较保险的值代价是多占一点显存。3.3 全程零拷贝用 vpp_qsv 替掉 scale 和 format完整链路想要做到零拷贝核心就是把所有涉及像素处理的滤镜都换成 QSV 版本。scale换成vpp_qsv老版本里叫scale_qsvformat也交给vpp_qsv的format参数处理。一条我常用的完整命令把 4K H.264 转成 1080p HEVCffmpeg -hide_banner \ -init_hw_device qsvhw:/dev/dri/renderD128 \ -filter_hw_device hw \ -hwaccel qsv -hwaccel_output_format qsv \ -i input_4k.mp4 \ -vf vpp_qsvw1920:h1080:formatnv12 \ -c:v hevc_qsv -preset medium -global_quality 24 -look_ahead 1 \ -c:a aac -b:a 128k \ -movflags faststart \ output_1080p.mp4vpp_qsv除了缩放还能做这些事deinterlace1去隔行处理老素材很有用denoise20轻量降噪数值越大越强副作用是细节会损失detail10锐化/细节增强和 denoise 一起用要小心过度处理formatp010le输出 10bit 格式需要注意的是vpp_qsv的能力依赖平台代数。Gen9 上的 VPP 功能比较基础只有缩放和简单的色彩转换Gen12 之后 denoise、detail 这些才比较可用。如果某个参数加了之后报unsupported先别怀疑命令写错多半是这个平台不支持。3.4 硬解加 CPU 滤镜加硬编什么时候值得多花那一次拷贝零拷贝不是万能的。有些处理 QSV 的 VPP 确实做不了比如复杂的色彩 LUT、自定义的锐化算法、需要接入第三方库的画面分析。这时候只能老老实实走硬解 → 下载到内存 → CPU 滤镜 → 上传 → 硬编这条混合链路。ffmpeg -hwaccel qsv -hwaccel_output_format qsv -i input.mp4 \ -vf hwdownload,formatnv12,你的CPU滤镜,formatnv12,hwuploadextra_hw_frames64 \ -c:v h264_qsv -global_quality 23 \ output.mp4这条链路里hwdownload和hwupload就是那两次额外拷贝。我在 1080p 素材上实测过加上这两次拷贝之后整体速度大概下降 15% 到 25%4K 上会掉 30% 左右。所以判断标准很简单CPU 滤镜带来的收益能不能抵掉这 20% 的性能损失。如果只是一个简单的色调调整能改用vpp_qsv实现那就别走混合链路。另外一个容易忽略的点混用 CPU 滤镜和 GPU 滤镜时format那一步必须显式写清楚。因为 ffmpeg 的自动协商在跨硬件边界时经常选错格式最后表现为画面颜色不对或者直接报错。4. 码率控制决定画质上限ICQ、CQP、VBR、CBR、LA_ICQ 怎么选4.1 -global_quality 和 -q:v 这两个看似等价的参数QSV 编码器的码率控制模式不是靠一个显式参数指定的而是通过你给了哪些参数推导出来的。这一点和 libx264 那种显式-crf的风格完全不同所以经常有人搞不清自己在用哪个模式。大概的对应关系是你给的参数实际生效的模式特点只给-q:v NCQP固定 QP码率波动最大画质最可控但体积不好预估只给-global_quality NICQ智能恒定质量QSV 里最接近 CRF 的东西推荐日常使用-global_quality N-look_ahead 1LA_ICQ画质进一步提升代价是延迟和显存-b:v NVBR码率可控适合有带宽预算的场景-b:v N -maxrate N -minrate NCBR恒定码率适合直播推流这里有个坑必须点出来-q:v和-global_quality不是一回事虽然在 8bit 场景下数值看起来能对上。我见过不少人抄了别人的-q:v 25当 CRF 用结果发现码率飘得厉害。原因就是-q:v走的是 CQP每个帧、每个宏块都用同一个 QP不做任何基于内容复杂度的调整。风景镜头和高速运动镜头出来的码率可能差好几倍。日常转码我的默认选择是-global_quality 23到-global_quality 26这个区间在 1080p 上对应的观感和 x264 的 CRF 21 到 24 差不多。4K 素材我会往上调一到两档因为高分辨率下同样的质量数值实际码率会低一些。4.2 五种码率控制模式在真实素材上的表现拿一段 3 分钟的 1080p 测试素材混合了静态访谈、手持跟拍、快速运动三个片段分别跑五种模式目标都是观感接近的档位结果大致是这样的模式参数输出大小主观画质适用场景CQP-q:v 24340MB运动段有轻微块效应对体积不敏感、追求速度ICQ-global_quality 24210MB整体稳定通用转码首选LA_ICQ-global_quality 24 -look_ahead 1195MB暗部和高动态段更好归档、点播VBR-b:v 8M -maxrate 12M -bufsize 16M183MB码率高的段画质好低的段掉有明确带宽上限CBR-b:v 8M -maxrate 8M -minrate 8M -bufsize 8M184MB静态段浪费码率直播推流从这张表能看出来一个反直觉的结论CQP 在体积上并不占优反而更大。因为它不做内容自适应静态画面也在用同样的 QP 编码等于白白浪费码率。所以在存储空间敏感的场合CQP 其实是性价比最差的选择。CBR 的问题在另一个方向。它对静态画面浪费码率对高速运动段又给不够码率——因为码率是锁死的。如果你做的是直播推流CBR 是必须的因为网络传输需要稳定的码率但如果是本地归档用 CBR 纯粹是自虐。4.3 look_ahead 和 extbrc画质增益背后的延迟与显存代价-look_ahead 1打开的是前瞻功能编码器会先缓冲一定数量的帧分析未来帧的内容来分配码率。效果在场景切换的时候特别明显——没有前瞻的话场景切换后的头几帧会因为码率没跟上而糊掉有前瞻的话就平滑很多。代价有两个。第一是延迟需要缓冲帧所以从输入到输出的延迟会增加直播场景基本不能用。第二是显存占用缓冲的帧要占显存多路并发的时候这个开销会累积。extbrc扩展码率控制是另一个值得开的选项-c:v hevc_qsv -global_quality 24 -look_ahead 1 -extbrc 1 -async_depth 8extbrc让编码器在码率控制上有更多的调整自由度代价是编码速度会下降一些。我在归档类任务上会开实时性要求高的场景不开。-async_depth这个参数控制编码器的异步深度也就是同时在处理的帧数。默认值是 4加大到 8 或 16 能提升吞吐尤其是在多核机器上跑多路并发的时候。但它和 look_ahead 一样会占显存需要按实际的并发路数核算——经验值是 1080p 每路给 100MB 到 200MB 显存余量4K 要翻三到四倍。4.4 一份可以直接抄的参数配置表下面这几种场景是我实际用过的配置可以直接拿去改路径用。通用归档画质优先ffmpeg -hwaccel qsv -hwaccel_output_format qsv -i in.mp4 \ -vf vpp_qsvformatnv12 \ -c:v hevc_qsv -preset slow -global_quality 23 -look_ahead 1 -extbrc 1 -async_depth 8 \ -c:a aac -b:a 192k -movflags faststart out.mp4批量转码速度优先ffmpeg -hwaccel qsv -hwaccel_output_format qsv -i in.mp4 \ -vf vpp_qsvw1920:h1080:formatnv12 \ -c:v hevc_qsv -preset veryfast -global_quality 26 -async_depth 16 \ -c:a aac -b:a 128k out.mp4限码率分发ffmpeg -hwaccel qsv -hwaccel_output_format qsv -i in.mp4 \ -vf vpp_qsvw1920:h1080:formatnv12 \ -c:v h264_qsv -preset medium -b:v 4M -maxrate 6M -bufsize 8M \ -c:a aac -b:a 128k out.mp4关于-presetQSV 的可选值从快到慢是veryfast、faster、fast、medium、slow、slower、veryslow。实际测试下来从 veryfast 到 medium 速度差大约 15%画质提升在 0.3dB PSNR 左右从 medium 到 slow 速度再降 20%画质提升不到 0.2dB。所以性价比拐点大概在 medium再往上调就很亏了。5. 那些让我排查半天的 QSV 报错与画面异常5.1 Device creation failed 和 unsupported(-3) 的完整排查链路这两个报错我遇到过至少四五次每次原因都不一样所以值得把排查路径完整写一遍。第一步确认是不是权限问题。/dev/dri/renderD128这个节点的属组通常是render如果你的用户不在这个组里ffmpeg 就打不开设备。现象是vainfo用 root 跑正常普通用户跑就失败。ls -l /dev/dri/ sudo usermod -aG render,video $USER # 重新登录后生效第二步确认驱动环境变量。系统里同时装了 i965 和 iHD 的时候libva 可能挑错。显式指定export LIBVA_DRIVER_NAMEiHD第三步确认设备节点选对了。双显卡机器上renderD128不一定是 Intel 核显。用lspci加上设备名过滤确认一下lspci -nn | grep -i -E vga|display然后在 ffmpeg 命令里显式指定正确的节点-init_hw_device qsvhw:/dev/dri/renderD129第四步如果还是 unsupported(-3)基本就是驱动能力不够。这个错误码的含义是指定的配置不被支持最常见的原因是 HEVC 编码在 Gen9 上要用特定的 profile或者你请求了平台不支持的 10bit 编码。这时候换个编码器试试比如把hevc_qsv换成h264_qsv如果 h264 能跑通那就是平台对 HEVC 的支持不完整。5.2 绿屏、花屏、颜色发灰多半是像素格式在捣鬼画面异常的情况比报错更难查因为它不报错你只看到结果不对。我遇到过的几种整体绿屏。典型原因是硬件帧和软件帧搞混了。比如解码用了-hwaccel_output_format qsv但后面接了一个只认软件帧的滤镜ffmpeg 在某些版本里不会报错而是把显存指针当成内存指针读结果就是一屏绿色。解决办法是补上hwdownload或者把滤镜换成 QSV 版本。颜色发灰、对比度不对。这多半是色彩范围color range出了问题。视频有 full range 和 limited range 两种NV12 默认按 limited 处理如果源是 full range 而没标清楚转换一次就会发灰。在vpp_qsv里显式指定-vf vpp_qsvformatnv12:color_rangepc或者干脆在输出上加-color_range pc。画面下半部分是花屏。这种情况我遇到过一次原因是vpp_qsv的输出尺寸设成了奇数。NV12 是 4:2:0 采样宽高都必须是偶数给个 1921 就会出问题。加个-2或者手动取整就好-vf vpp_qsvw1920:h-2:formatnv125.3 10bit HEVC 和 P010为什么加了 -pix_fmt 反而报错想输出 10bit HEVC 的时候很多人会下意识加-pix_fmt yuv420p10le因为这是软编 x265 的写法。但在 QSV 上这样写会报错因为 QSV 编码器不接受yuv420p10le这种软件格式它只认p010le或者叫 P010。正确写法是两步走先让vpp_qsv输出 p010 格式再告诉编码器用 main10 profileffmpeg -hwaccel qsv -hwaccel_output_format qsv -i in.mp4 \ -vf vpp_qsvformatp010le \ -c:v hevc_qsv -profile:v main10 -global_quality 24 \ -c:a copy out.mp4还有一个前提平台得支持 10bit 编码。Gen9 只有 10bit 解码编码要 Gen11Ice Lake之后。在不支持的平台上跑这个命令会得到类似Error initializing the encoder: unsupported parameter的报错。判断方法是在vainfo里找VAProfileHEVCMain10后面有没有VAEntrypointEncSlice。顺便说一句h264_qsv 是不支持 10bit 的因为 H.264 的 High 10 Profile 在 QSV 里压根没实现。想输出 10bit 只能走 HEVC 或 AV1如果平台支持。5.4 多路并发时 async_depth 和显存占用的平衡单路跑得好好的一开并发就出问题这是很常见的现象。表现通常是速度不升反降或者跑着跑着某一程报内存分配失败。原因在于核显的显存是和系统内存共享的默认分配的显存额度可能只有几百兆。多路 4K 转码每路都要占几百兆很容易打满。Linux 下可以在 BIOS 里调大 DVMT/共享显存或者监控一下实际占用sudo intel_gpu_top这个工具能看到 GPU 的各个引擎Video、Render、Blitter的占用率。如果 Video 引擎跑到了 90% 以上说明硬件编码单元已经饱和再加并发只会互相拖慢这时候应该减少并发数而不是增加。并发数的经验公式大概是每 8 到 12 路 1080p 转码对应 1 个核显的编码单元具体看平台代数新平台能多一些-async_depth设成 4 到 8 就够了。设得太大反而因为显存压力导致性能抖动。我一般先用单路测出基准速度再逐步加并发直到总吞吐不再增长为止那个点就是最优并发数。5.5 老平台为什么连 vainfo 都过不去Haswell 和 Broadwell 这两代Gen7.5 和 Gen8经常是拿来当廉价转码机的但它们有很多限制。首先是驱动只能用 i965而 i965 在新内核上编译越来越麻烦很多发行版已经不再提供。其次是这两代对 HEVC 的支持很有限——Haswell 基本不支持 HEVCBroadwell 只有部分解码能力编码完全没有。我实际测试的结果是Broadwell 上vainfo能列出的 Profile 很少VAEntrypointEncSlice几乎看不到。这种平台就别硬上 QSV 了用 CPU 软编反而更省心。真想用老平台做转码机Skylake 是最低线而且要注意 Skylake 早期的步进版本有 HEVC 编码的已知问题需要较新的内核和驱动。6. 实测数据QSV 在速度、画质、功耗上的真实表现6.1 速度对比不是所有环节都能被加速拿一台 i7-12700 的机器处理一段 10 分钟的 4K H.264 转 1080p HEVC几种配置的耗时对比大致如下配置耗时CPU 平均占用备注全 CPUx265 medium32 分钟750%风扇满转硬解 CPU 缩放 硬编6 分 20 秒380%多了一次拷贝硬解 vpp_qsv 硬编4 分 10 秒180%零拷贝硬解 vpp_qsv 硬编 look_ahead5 分 05 秒190%画质换速度可以看到两个关键点。第一零拷贝相比混合链路提升了 34%这是把拷贝开销省下来的直接结果。第二look_ahead 让速度慢了约 22%但画质提升是实打实的值不值得看你自己的场景。intel_gpu_top显示在零拷贝模式下Video 引擎占用 85% 左右Render 引擎几乎不动Blitter 有一小部分占用负责帧的搬运。这个占用分布说明瓶颈已经落在硬件编码单元上再优化空间不大了。6.2 同码率画质对比QSV 和 x265 差在哪用同一段素材分别用 x265 medium 和 hevc_qsv medium 编码到 8Mbps跑 VMAF 对比编码器VMAF主观感受x265 medium94.2细节保留好暗部干净hevc_qsv medium92.6整体接近高速运动段有轻微涂抹hevc_qsv slow93.1差距缩小但速度慢 20%hevc_qsv medium look_ahead93.4场景切换处明显改善差距大概是 1.5 到 2 个 VMAF 点。这个差距在手机屏幕、笔记本屏幕上看不出来在大屏电视上仔细对比才能发现。我的判断是如果是给客户交付的成片用 x265如果是内部归档、监控存储、素材预处理QSV 完全够用。另外值得说的是QSV 在低码率段4Mbps 以下的劣势会更明显一些因为它的码率分配策略相对保守遇到复杂场景容易整体降质量。如果目标码率很低建议把-preset调到 slow 并且开 look_ahead。6.3 什么情况下不该用 QSV用了几年下来我总结出几个明确不该上 QSV 的场景。第一是超高质量要求的成片输出。影视级的交付VMAF 要求 96 以上QSV 很难达到还是老老实实上 x265 的 slow 甚至 veryslow。第二是极低码率的压缩。比如要把视频压到 500kbps 以内QSV 的码率控制精度不如 x265 的 CRF容易出现画面崩坏。第三是源的格式 QSV 不支持解码的。比如某些专业摄像机格式、ProRes、DNxHD 这些QSV 解码器不认只能软解。虽然可以软解之后硬编但收益就只剩一半了。第四是已经有大量并发的 CPU 任务在跑的机器。如果这台机器的 CPU 已经被别的服务吃满了再加一个核显转码任务虽然编码本身不占 CPU但 demux、封装、音频转码这些还是要 CPU 的可能会互相影响。6.4 批量转码脚本里的两个小经验最后分享两个我在批量脚本里踩过的坑。第一个是进度判断不能只看退出码。QSV 在某些异常情况下比如某帧数据损坏会打印警告但退出码是 0输出文件其实是坏的。我的做法是在脚本里加一道检查对比输出文件的时长ffprobe -v error -show_entries formatduration -of csvp0 output.mp4如果时长和源文件差超过 1 秒就标记出来人工检查。第二个是并发路数要留余量。我一开始按 8 路并发跑前 20 个文件都很稳跑到第 30 个左右开始出现速度骤降。后来用intel_gpu_top观察发现是显存碎片化导致的。解决办法是每跑完 50 个文件就把整个批次停一下或者干脆把并发数降到 6 路。这种问题在文档里完全查不到只能靠实际跑批发现。另外提醒一句批量脚本里最好给每个 ffmpeg 进程加上-nostdin否则多个进程抢标准输入会出各种奇怪的问题这个坑我在不同项目里见过至少三次。
返回列表