ADR-0595: Real two-pass argv for all 14 codec adapters¶
- Status: Accepted
- Date: 2026-05-18
- Deciders: lusoris, Claude
- Tags: vmaf-tune, codec, encode, ffmpeg
Context¶
Phase F (ADR-0333) added the two_pass_args(pass_number, stats_path) method to the codec-adapter contract and wired the run_two_pass_encode driver to invoke it. Only three adapters (libx264, libx265, libvpx-vp9) supplied real implementations; the remaining 14 codec adapters (libsvtav1, libaom-av1, libvvenc, the three NVENC, three QSV, three AMF, three VideoToolbox encoders, plus the ProRes VideoToolbox adapter) inherited the protocol-default body that raised NotImplementedError. Calls to two_pass_args against any of those 14 codecs therefore crashed at the contract surface rather than returning meaningful argv or a documented refusal.
Two-pass encoding maps to four distinct vendor models that the harness must preserve without per-codec branches in the search loop:
- Software encoders with a true two-invocation 2-pass pipeline.
libx264,libx265,libvpx-vp9,libaom-av1,libvvencall run the sameffmpeg <pass1> && ffmpeg <pass2>shape with an on-disk stats sidecar. The harness's existing two-invocation driver applies as-is. - Software encoder with a CRF-mode multi-pass prohibition.
libsvtav1enforces "CRF does not support multi-pass" at runtime (verified against SVT-AV1 v4.1.0); the adapter must surface that constraint instead of silently returning argv ffmpeg will reject. - Hardware encoders with single-invocation in-encoder analysis. NVENC (
-multipass fullres), QSV (-extbrc 1 -look_ahead_depth 40), and AMF (-preanalysis true) all expose 2-pass-equivalent quality through a single ffmpeg invocation rather than two. Running the two-invocation driver against them would either fail outright or just re-encode from scratch twice. - API-limited refusal. Apple VideoToolbox's
VTCompressionSessionC API has no multi-pass interface; the only honest behaviour is to raise a clearly-typed error pointing users at the software fallback.
Decision¶
Implement two_pass_args for every adapter so the contract is met:
- Software true 2-pass (
libaom-av1,libvvenc): setsupports_two_pass = Trueand emit the generic ffmpeg-pass N -passlogfile <prefix>pair the existing driver already handles forlibx264/libvpx-vp9. - SVT-AV1: set
supports_two_pass = False(the harness pins CRF mode, which SvtAv1 forbids for multi-pass), but still return the VBR-mode argv (-pass N -passlogfile <prefix>) fromtwo_pass_argsso callers that explicitly override into VBR mode viaextra_paramscan use it. - NVENC / QSV / AMF: set
supports_two_pass = Falseand return the vendor's single-invocation analysis flag set fromtwo_pass_args(1, _),()fromtwo_pass_args(2, _). The driver falls back to single-pass; callers can splice the pass-1 return value intoextra_paramsfor a quality-boosted single-pass encode. - VideoToolbox (all four adapters): raise
VideoToolboxTwoPassUnsupportedError(aNotImplementedErrorsubclass) with a message that names the encoder and points users atlibx264 / libx265 / libsvtav1 / libaom-av1 / libvvencas the software fallback set.
The supports_two_pass flag remains the single source of truth for whether the two-invocation driver runs; the new two_pass_args implementations are the source of truth for what argv a 2-pass invocation would emit, even when the driver chooses to short-circuit to single-pass.
Alternatives considered¶
| Option | Pros | Cons | Why not chosen |
|---|---|---|---|
Leave NotImplementedError for hardware encoders | No new code | Crashes the contract surface; callers can't introspect what argv 2-pass would emit; no path for hardware quality-boost flags to reach extra_params | Fails the acceptance gate ("two_pass_args(1, p) returns a real list of ffmpeg args") |
Set supports_two_pass=True for hardware encoders and run two invocations | Reuses the existing driver | Wastes one full encode cycle (the "second pass" is just a separate single-pass run), confuses cache keys (ADR-0298), and contradicts vendor documentation | Hardware analysis is in-encoder; two invocations don't accumulate state |
Use SVT-AV1 -svtav1-params passes=2:pass=N:stats= for SvtAv1 | Mirrors the standalone SvtAv1EncApp invocation | The FFmpeg libsvtav1 wrapper doesn't expose those keys (verified against FFmpeg n8.1.1 + SVT-AV1 v4.1.0); the encoder also forbids multi-pass in CRF mode regardless | Falls foul of both the wrapper surface and the encoder's CRF prohibition |
Raise NotImplementedError for VideoToolbox without a custom subclass | Smaller diff | Callers can't disambiguate "API limitation" from "we forgot to implement" without parsing the message | The custom subclass adds one symbol and removes ambiguity |
Consequences¶
- Positive: every adapter now returns either real argv or a documented refusal. The
run_two_pass_encodedriver's fallback path (warn + single-pass) activates uniformly across hardware encoders. Callers that introspect the adapter to composeextra_paramsget the hardware quality-boost flags without per-codec branches. - Positive: the SVT-AV1 CRF-mode multi-pass prohibition is documented in code (with the verbatim encoder error message) instead of being rediscovered each time a user enables
--two-pass. - Negative: the contract is now slightly asymmetric — software adapters with
supports_two_pass=Truereturn per-pass argv, while hardware adapters withsupports_two_pass=Falsereturn pass-1 argv that lives in a single ffmpeg invocation. The asymmetry is documented per-adapter. - Neutral / follow-ups: a future PR can add a
hardware_quality_boostmethod on the protocol that surfaces the same NVENC / QSV / AMF flags more directly; today'stwo_pass_args(1, _)doubles as that surface.
References¶
- ADR-0237 — codec-adapter contract.
- ADR-0294 — codec-agnostic dispatcher.
- ADR-0298 — adapter-version cache key.
- ADR-0333 — Phase F: 2-pass encoding for libx264 / libx265.
- SVT-AV1 v4.1.0 runtime error verified against FFmpeg n8.1.1 + SVT-AV1 v4.1.0 (
Svt[error]: CRF does not support multi-pass. Use single pass.). - FFmpeg
libavcodec/nvenc.c-multipassAVOption (single-invocation, encoder-internal full-resolution analysis). - FFmpeg
libavcodec/qsvenc.c-extbrc+-look_ahead_depthAVOptions (single-invocation, in-encoder look-ahead window). - FFmpeg
libavcodec/amfenc_*.c-preanalysisAVOption. - Apple
VTCompressionSessionC API — no multi-pass interface. - Source:
req("Implement 2-pass encoding for the 14 codec adapters in VMAFx/vmafx that currently raiseNotImplementedError").