vmaf-tune hardware encoders: NVENC, QSV, AMF and VideoToolbox¶
Use a hardware adapter when you need a large corpus quickly or when the production pipeline is GPU-encoded. The adapters share the software adapters' contract, so the search loop is identical; only the FFmpeg argv differs. This page covers the four hardware families. The registry of all adapters is vmaf-tune-codec-adapters.md.
Quick start¶
vmaf-tune corpus \
--encoder h264_nvenc \
--source ref.yuv --width 1920 --height 1080 --pix-fmt yuv420p \
--framerate 24 --duration 10 \
--preset slow --preset medium --preset fast \
--crf 23 --crf 28 --crf 34 \
--output corpus_nvenc.jsonl
Swap --encoder for any adapter below. Requirements are the same for every family: an FFmpeg build with the vendor encoder compiled in, the matching GPU, and its driver or runtime.
The four families¶
| Family | Adapters | Quality knob (accepted range) | Default |
|---|---|---|---|
| NVIDIA NVENC | h264_nvenc, hevc_nvenc, av1_nvenc | -cq (0..51) | 23 |
| Intel QSV | h264_qsv, hevc_qsv, av1_qsv | -global_quality (1..51) | 23 |
| AMD AMF | h264_amf, hevc_amf, av1_amf | -qp_i / -qp_p (15..40) | 23 |
| Apple VideoToolbox | h264_videotoolbox, hevc_videotoolbox, prores_videotoolbox, av1_videotoolbox | -q:v (0..100), -profile:v tier (0..5) | 50, tier 3 |
Common behaviour:
- The adapters do not check the GPU generation. A missing encoder or an unsupported GPU makes the encode exit non-zero, and the harness records the failure and skips scoring, so a partial corpus from a mixed fleet is still well-formed.
- Every hardware encoder (NVENC, QSV, AMF and VideoToolbox) is probed with a one-frame dummy encode before it is used.
compareskips a failing one with a recorded reason;corpus, liverecommendandladderstop before the first encode with exit status 2 and the probe's reason. - None of the hardware adapters supports the two-invocation
--two-passdriver. NVENC, QSV and AMF offer an in-encoder look-ahead instead; see multi-pass encoding. - Hardware encoders write no parseable first-pass stats file.
NVIDIA NVENC¶
| Adapter | FFmpeg encoder | Hardware |
|---|---|---|
h264_nvenc | h264_nvenc | NVIDIA Kepler or newer |
hevc_nvenc | hevc_nvenc | NVIDIA Maxwell 2nd generation or newer (GTX 960 and up) |
av1_nvenc | av1_nvenc | NVIDIA Ada Lovelace or newer (RTX 40 series, L40, L4) |
The FFmpeg build needs --enable-nvenc. The quality knob is -cq, the closest analogue of libx264 CRF. The hardware accepts 0..51; the informative grid window is 15..40, the same as libx264.
NVENC has seven preset levels, p1 (fastest) to p7 (slowest). The CLI takes the shared mnemonic names and maps them:
| Mnemonic | NVENC preset |
|---|---|
ultrafast, superfast, veryfast | p1 |
faster | p2 |
fast | p3 |
medium (default) | p4 |
slow | p5 |
slower | p6 |
slowest, placebo | p7 |
Forced keyframes (--force-keyframes) add -forced-idr 1, because NVENC honours -force_key_frames only with it.
Intel QSV¶
| Adapter | FFmpeg encoder | Hardware |
|---|---|---|
h264_qsv | h264_qsv | Intel iGPU 7th generation (Kaby Lake) or newer, or Arc / Battlemage |
hevc_qsv | hevc_qsv | Intel iGPU 7th generation or newer (10-bit needs 11th generation), or Arc / Battlemage |
av1_qsv | av1_qsv | Intel iGPU 12th generation or newer, or Arc / Battlemage |
QSV uses ICQ rate control through -global_quality (1..51). Its seven presets are veryslow, slower, slow, medium, fast, faster and veryfast, passed through unchanged. QSV has no ultrafast or superfast; those names are rejected.
vmaf-tune validates the (preset, global_quality) pair before spawning FFmpeg. Every QSV encode (corpus, ladder, compare's bisect, fast, tune-per-shot) carries a VA-API / QSV device chain before the input and the upload filter at the end of its -vf chain, from the same adapter code the availability probe uses:
ffmpeg \
-init_hw_device vaapi=va:/dev/dri/renderD129 \
-init_hw_device qsv=qsv_dev@va \
-filter_hw_device qsv_dev \
-i src.mkv \
-vf format=nv12,hwupload=extra_hw_frames=64 \
-c:v h264_qsv -preset medium -global_quality 23 -an out.mkv
Without the chain every QSV encode fails with -22 Invalid argument. The filter device is the QSV device (qsv_dev): with the VA-API device there, as ADR-0601 first recorded it, hwupload produces vaapi frames and the filter graph fails before the encoder opens. A ladder rung's scale=W:H and the upload share one -vf chain (scale=W:H,format=nv12,hwupload=extra_hw_frames=64), because FFmpeg keeps only the last -vf.
Not proven on QSV hardware
The argv and the probe wiring are covered by tests with fakes. On the one Intel GPU the project has measured (Arc A380, Linux xe driver, iHD 26.3.5, oneVPL 2.17), the chain gets past device and filter initialisation, but every QSV encoder then fails inside the runtime with Invalid FrameType:0, with or without vmaf-tune's chain. A LIBVA_DRIVER_NAME that names another vendor's driver also breaks the device chain ("Failed to get device id from the driver").
The VA-API render node defaults to auto: vmaf-tune walks /dev/dri/by-path and /sys/class/drm/renderD*/device/vendor, picks the first Intel vendor node (0x8086), and falls back to /dev/dri/renderD128 only when none is found. Pin a node with vmaf-tune compare --vaapi-device /dev/dri/renderD129 or with the VMAFTUNE_VAAPI_DEVICE environment variable (the flag wins). The environment variable applies to every subcommand that encodes; the flag exists on compare and holds for its probe and every encode of the run.
AMD AMF¶
| Adapter | Hardware | FFmpeg flag |
|---|---|---|
h264_amf | any AMD GPU with AMF | -c:v h264_amf |
hevc_amf | any AMD GPU with AMF | -c:v hevc_amf |
av1_amf | RDNA 3 or newer (RX 7000 series) | -c:v av1_amf |
Requirements: FFmpeg built with --enable-amf, and the AMF runtime (Adrenalin on Windows; the Mesa AMF stack or the AMD Pro driver on Linux). ensure_amf_available() raises a RuntimeError when the encoder is missing, but corpus does not call it. Rate control is constant-QP: -rc cqp with matched -qp_i and -qp_p, range 15..40 (the hardware allows 0..51).
AMF exposes three -quality rungs where the other families expose seven or more presets, so the adapter compresses the names:
| Preset names | AMF -quality |
|---|---|
placebo, slowest, slower, slow | quality |
medium (default) | balanced |
fast, faster, veryfast, superfast, ultrafast | speed |
Callers that need finer control pin -qp_i / -qp_p instead of the preset.
vmaf-tune corpus \
--encoder h264_amf \
--source ref.yuv --width 1920 --height 1080 \
--preset slow --preset medium --preset fast \
--crf 23 --crf 28 --crf 34 \
--output corpus_amf.jsonl
The FFmpeg invocation for one cell:
Each of -quality, -rc, -qp_i and -qp_p appears once. Until 2026-10-04 the AMF adapters returned the same block from extra_params() as well, so every AMF command line carried it twice.
Some APUs have no AMF encoder
The gfx1036 iGPU in AMD Raphael and Phoenix APUs (Ryzen 7000 integrated graphics) is decode-only. h264_amf, hevc_amf and av1_amf fail there with AMF_NOT_SUPPORTED even with the AMF runtime installed. compare reports it as hardware encoder not available: ... dummy encode failed and carries on.
Apple VideoToolbox¶
| Adapter | FFmpeg encoder | Quality knob | Hardware |
|---|---|---|---|
h264_videotoolbox | h264_videotoolbox | -q:v 0..100, higher is better | Apple Silicon, or Intel Mac with T2 |
hevc_videotoolbox | hevc_videotoolbox | -q:v 0..100, higher is better | Apple Silicon, or Intel Mac with T2 |
prores_videotoolbox | prores_videotoolbox | -profile:v tier 0..5 | Apple Silicon M1 Pro / Max / Ultra or later |
av1_videotoolbox | av1_videotoolbox | -q:v 0..100 (placeholder) | none shipped yet |
The adapters validate the (preset, quality) pair (-q:v for H.264 and HEVC, the integer tier id for ProRes). The host is probed like every hardware encoder's: on a machine without VideoToolbox, for example Linux, the one-frame dummy encode fails and compare records the row as unavailable, while corpus and ladder stop with exit status 2.
VideoToolbox has no multi-valued preset, only a boolean -realtime flag, so the nine accepted names collapse onto it:
| Preset names | -realtime |
|---|---|
ultrafast, superfast, veryfast, faster, fast | 1 (low latency) |
medium, slow, slower, veryslow | 0 (offline, quality first) |
The mapping is lossy by design. placebo and slowest are not accepted here. The FFmpeg invocations:
ffmpeg -i src.mkv -c:v h264_videotoolbox -realtime 0 -q:v 60 -an out.mkv
ffmpeg -i src.mkv -c:v hevc_videotoolbox -realtime 0 -q:v 60 -an out.mkv
ffmpeg -i src.mkv -c:v prores_videotoolbox -realtime 0 -profile:v hq -an out.mov
AV1 placeholder¶
av1_videotoolbox is registered but inactive. validate() raises Av1VideoToolboxUnavailableError until a probe (ffmpeg -h encoder=av1_videotoolbox) shows that the local FFmpeg exposes the encoder, at which point it activates with no code change (ADR-0339). Until then use libaom-av1 or libsvtav1; see AV1 encoders.
ProRes tiers¶
ProRes is a fixed-rate intermediate codec with no CRF or QP scalar. The tier selects quality, and the bitrate follows from tier, resolution and frame rate. --crf carries the integer tier id (FFmpeg's profile:v value); the adapter writes the FFmpeg alias on the argv.
--crf value | FFmpeg alias | Marketing name | Typical use |
|---|---|---|---|
| 0 | proxy | ProRes 422 Proxy | Offline editing, dailies |
| 1 | lt | ProRes 422 LT | Broadcast acquisition |
| 2 | standard | ProRes 422 | Mainline broadcast master |
| 3 | hq | ProRes 422 HQ | High-end broadcast or film master (default) |
| 4 | 4444 | ProRes 4444 | Graphics, alpha, colour grading |
| 5 | xq | ProRes 4444 XQ | High dynamic range or wide gamut master |
The tiers come from FFmpeg's libavcodec/videotoolboxenc.c prores_options table in the supported FFmpeg n9.0.2 baseline. ProRes is intra-only, so --keyint and --force-keyframes are accepted but have no rate-distortion effect; the harness still emits them so the muxer's seek-table density stays predictable across codecs.
Hardware or software¶
Hardware encoders are much faster than the software encoders at the cost of quality. Typical figures, not a contract:
- NVENC is roughly 10x to 100x faster.
h264_nvencatmediumtypically loses 3 to 5 VMAF points againstlibx264 mediumat the same bitrate, depending on content.
The Pareto frontier is genuinely different, which is why the harness treats NVENC as separate codec entries instead of a flag on libx264. Use hardware for a large corpus quickly or a GPU-encoded production pipeline, and software when you need the best encode at a given bitrate.
Troubleshooting¶
Encoder h264_nvenc not found (or a sibling encoder) means the FFmpeg build lacks the encoder or the GPU lacks the generation. The harness records exit_status != 0 and skips scoring for that cell. List the encoders your build has:
See also¶
vmaf-tune.md: the base tool.vmaf-tune-codec-adapters.md: the adapter registry and contract.vmaf-tune-multipass.md: the in-encoder look-ahead flags per hardware family.vmaf-tune-hdr-and-sampling.md: the 10-bit HDR flags per hardware encoder.- ADRs: 0290 NVENC, 0281 QSV, 0282 AMF, 0283 VideoToolbox, 0601 and 0641 device init and probes.