feat(engine): closed-GOP VP9 encoder args + concat-copy smoke test (#950)

## Description

PR 1 of 4 in the WebM (VP9) distributed-rendering series. A gating
experiment that proves closed-GOP libvpx-vp9 chunks survive
`ffmpeg -f concat -c copy` losslessly, so the rest of the stack can
ship Path A (concat-copy) rather than the slower
re-encode-in-assemble fallback.

Two changes:

1. **Closed-GOP VP9 encoder args.** `buildEncoderArgs` now lays
   `-g <chunkSize>`, `-keyint_min <chunkSize>`, `-auto-alt-ref 0`, and
   `-cpu-used 2` on libvpx-vp9 when `lockGopForChunkConcat=true`.
   Mirrors the existing libx264/libx265 branches. The alt-ref disable
   is load-bearing — libvpx-vp9's default non-displayable alt-ref
   frames can reach across chunk seams and break concat-copy.
   `-cpu-used 2` pins the speed/quality tradeoff so chunks encoded on
   workers with different libvpx-vp9 defaults produce visually
   consistent output across seams. Default (`lockGopForChunkConcat`
   unset) preserves the existing in-process VP9 path unchanged.

2. **Concat-copy smoke test** at
   `packages/producer/tests/distributed/_smoke/webm-concat-copy.test.ts`.
   Generates 60 PNGs via lavfi `testsrc2`, encodes them as 4 VP9 chunks
   of 15 frames using `buildEncoderArgs` with
   `lockGopForChunkConcat=true`, concat-copies via `ffmpeg -f concat -c
   copy`, then runs three independent verifications:
   `ffprobe -show_streams`, `ffmpeg -f null -` decode test, and
   `ffprobe -count_frames`. Each verification surfaces its failure
   fingerprint in the error message.

Smoke test passes 6/6 locally → Path A works; the rest of the stack
takes it.

Also exports `buildEncoderArgs` from `@hyperframes/engine` so
adapters / tests can construct args without re-implementing the
contract.

## Testing

- [x] `bunx vitest run --root packages/engine src/services/chunkEncoder.test.ts` — 62/62 pass (new VP9 closed-GOP tests included)
- [x] `bun test packages/producer/tests/distributed/_smoke/webm-concat-copy.test.ts` — passes
- [x] `bunx oxlint` + `bunx oxfmt --check` on all changed files — clean
- [x] `bunx tsc --noEmit -p packages/engine/tsconfig.json` — clean

🤖 Generated with [Claude Code](https://claude.com/claude-code)
This commit is contained in:
James Russo
2026-05-19 01:53:08 -04:00
committed by GitHub
parent efc5f0584b
commit 07de7e61ed
5 changed files with 446 additions and 5 deletions
+51 -1
View File
@@ -254,8 +254,58 @@ export function buildEncoderArgs(
args.push("-c:v", "libvpx-vp9", "-b:v", bitrate || "0", "-crf", String(quality));
args.push("-deadline", preset === "ultrafast" ? "realtime" : "good");
args.push("-row-mt", "1");
// Closed-GOP args for distributed chunk concat-copy. Mirrors the
// libx264/libx265 branch above: `lockGopForChunkConcat=true` lays a
// keyframe at every chunk boundary so `ffmpeg -f concat -c copy` can
// stitch sibling chunks losslessly.
//
// VP9-specific: `-auto-alt-ref 0` is mandatory. Alt-ref (a.k.a.
// "ARNR") frames are non-displayable references libvpx-vp9 inserts
// anywhere in the GOP for compression; they break concat-copy at
// chunk seams because the boundary frame is no longer the first
// displayable reference. The alpha branch below already disables
// alt-ref for an unrelated reason (alpha + alt-ref is unsupported);
// closed-GOP extends that to every pixel format.
//
// `-cpu-used 2` pins the libvpx-vp9 speed/quality tradeoff so chunks
// encoded on workers with different default cpu-used values still
// produce visually consistent output across seams. libvpx-vp9's
// default with `-deadline good` has drifted across versions
// historically — locking it makes the planHash round-trip
// deterministic.
const lockGopVp9 = options.lockGopForChunkConcat === true;
if (lockGopVp9) {
if (
typeof options.gopSize !== "number" ||
!Number.isFinite(options.gopSize) ||
options.gopSize <= 0
) {
throw new Error(
`[chunkEncoder] lockGopForChunkConcat=true requires a positive integer gopSize (received ${String(options.gopSize)})`,
);
}
const gop = Math.floor(options.gopSize);
args.push(
"-g",
String(gop),
"-keyint_min",
String(gop),
"-auto-alt-ref",
"0",
"-cpu-used",
"2",
);
}
if (pixelFormat === "yuva420p") {
args.push("-auto-alt-ref", "0");
// Alpha + alt-ref is unsupported by libvpx-vp9. The closed-GOP
// branch above already disables alt-ref; only push the flag for
// the non-locked alpha case to keep the args list clean (a second
// `-auto-alt-ref 0` is harmless but noisier in `ffmpeg -loglevel`
// diagnostics).
if (!lockGopVp9) {
args.push("-auto-alt-ref", "0");
}
args.push("-metadata:s:v:0", "alpha_mode=1");
}
} else if (codec === "prores") {