ADR-0730: vmafx-tune Go port — Stage 2 (ladder subcommand)¶
- Status: Accepted
- Date: 2026-05-28
- Deciders: lusoris
- Tags:
go,vmafx-tune,language-modernization,cli,phase4,fork-local
Context¶
ADR-0705 shipped Stage 1 of the vmafx-tune Go port: the compare subcommand as a standalone vmafx-tune-go binary, with all other subcommands as stubs redirecting to the Python binary.
Stage 2 is the next porting milestone. The ladder subcommand is the most impactful choice: it implements per-title ABR bitrate-ladder generation (the Netflix per-title encoding paper's central idea), is the second most commonly invoked subcommand after compare, and has the highest leverage ratio — it composes the already-ported compare bisect engine (pkg/bisect) without requiring new subprocess plumbing.
The Python ladder.py Phase E algorithm is well-specified:
- Sample the
(resolution × VMAF-target)plane via the Phase B bisect. - Compute the upper convex hull (Pareto frontier) of the
(bitrate, vmaf)cloud. - Select a small set of "knee" renditions using curvature-based inflection detection (Kneedle-style perpendicular-distance recursion).
- Emit a JSON payload compatible with the existing HLS/DASH manifest renderer.
Decision¶
We ship pkg/ladder/ as the Stage-2 core package and cmd/vmafx-tune/cmd/ladder.go as the cobra subcommand.
New package:
pkg/ladder/—Build(src, encoder, Params)function, convex hull (upperConvexHull), knee selection (selectRenditions), min-bitrate-gap filter (applyBitrateGap), and theSamplerFnseam. TheSamplerFnmirrors the Pythonladder.pytype alias so tests can inject stub samplers without running ffmpeg or vmaf.
The ladder CLI subcommand wires bisect.Run as the production sampler, composing the Stage-1 bisect engine for each (resolution, target) cell. Hardware encoders from pkg/encoder/hardware.go (NVENC, QSV, AMF, SVT-AV1) are supported via encoder.NewExtended.
Resolution-aware scaling (downscale the source before encoding each rendition) is Stage-3 scope; Stage 2 bisects at the native source resolution and tags points with the requested rendition resolution for hull and rendition tracking. This matches the Python ladder.py behaviour when the source resolution equals the target rendition resolution, which is the common case for single-clip ladder sweeps.
The JSON output schema (schema_version: 1) is a superset of the Python ladder output — it adds target_vmaf per cloud point as an optional field but never removes existing fields (ADR-0705 schema-forward invariant).
Alternatives considered¶
| Option | Pros | Cons | Why not chosen |
|---|---|---|---|
Port tune-per-shot (Stage 2) | Exercises per-shot state machine early | Complex state machine, no shared infra with compare; slower to test | ladder composes the already-ported bisect engine directly |
Port report (Stage 2) | No subprocess calls; pure render | Lower leverage — already usable via Python | Does not add a new user-facing encode path |
Port fast (Stage 2) | Popular for NR-proxy accelerated workflows | Depends on NR-proxy model loading not yet in Go | Premature without an ONNX Runtime Go binding |
| Concurrent grid sampling | Lower wall time for large grids | Race-condition risk; Stage 1 lesson: correctness first | Stage 3 will add --workers once sequential path is validated |
| Full resolution-scaling in Stage 2 | True per-title ladder | Requires ffmpeg scale filter injection + ffprobe source dimension extraction; 2× scope | Deferred to Stage 3; Stage 2 validates hull + knee selection first |
Consequences¶
- Positive:
vmafx-tune-go ladderis immediately usable for ABR ladder generation at native source resolution, with libx264/libx265 and all Stage-2 hardware encoders. - Positive:
pkg/ladder/is fully unit-testable viaSamplerFninjection; no ffmpeg or vmaf binary required forgo test ./pkg/ladder/. - Positive: JSON output is schema-compatible with the Python ladder renderer.
- Negative: Resolution-aware scaling deferred to Stage 3 — callers that need a true multi-resolution sweep must downscale sources externally or use the Python
vmaf-tune ladder. - Neutral:
cmd/vmafx-tune/cmd/root.goupdated to Stage-2 messaging;ladderremoved from the stub list.
References¶
- req: "Implement vmafx-tune Go Stage 2 (next subcommand after Stage 1's
compare)" (user directive 2026-05-28) - ADR-0705: vmafx-tune Go Stage 1 —
comparesubcommand - ADR-0702: vmafx Phase 4 language-modernization umbrella
tools/vmaf-tune/src/vmaftune/ladder.pyPhase E algorithmdocs/adr/0295-vmaf-tune-phase-e-bitrate-ladder.md(Python ladder design rationale)docs/adr/0307-vmaf-tune-ladder-default-sampler.md(default sampler canonical CRF sweep)