fix(producer): wire --crf and --video-bitrate CLI overrides into encoders (#372)

## Summary

Re-wire the `--crf` and `--video-bitrate` CLI flags through the three encoder spawn sites in `renderOrchestrator.ts`. They were defined and parsed in the CLI but silently dropped before reaching ffmpeg.

## Why

`Chunk 10` of `plans/hdr-followups.md`. PR #292 originally wired these through with a `baseEncoderOpts` object using `effectiveQuality`/`effectiveBitrate`; PR #268 rewrote the encode paths and reverted to `preset.quality` only, accidentally dropping the override. This is a user-facing regression — `hyperframes render --crf 18` was being silently ignored.

## What changed

- At the three encoder spawn sites (HDR streaming, SDR streaming, disk-based encode), `quality` defaults to `preset.quality` but is overridden by `job.config.crf` when set, and `bitrate` is set from `job.config.videoBitrate`. Mutual exclusivity is enforced upstream in the CLI, so we don't re-check it here.
- Fix the contradictory note in `docs/packages/cli.mdx` that claimed CRF/bitrate were now driven only by `--quality`. The flags table now lists `--crf` and `--video-bitrate` consistent with `docs/guides/rendering.mdx`.

## Test plan

- [x] `hyperframes render --crf 18 ...` now respects the CRF override (verified via ffprobe of the encoded output).
- [x] `hyperframes render --hdr ...` still works (no behavior change at the default path).
- [x] `hyperframes render --help` shows all flags consistent with the docs.

## Stack

Chunk 10 of `plans/hdr-followups.md`. Independent of all other chunks.
This commit is contained in:
Vance Ingalls
2026-04-22 22:05:48 -07:00
committed by GitHub
parent 6fd99109c9
commit 53e1aeaadc
2 changed files with 29 additions and 4 deletions
@@ -1186,6 +1186,26 @@ export async function executeRenderJob(
const encoderHdr = hasHdrContent ? effectiveHdr : undefined;
const preset = getEncoderPreset(job.config.quality, outputFormat, encoderHdr);
// CLI overrides (--crf, --video-bitrate) flow through job.config and must
// win over the preset-derived defaults. The CLI enforces mutual exclusivity
// upstream, but we still resolve them defensively. Without this, the flags
// are silently ignored at the encoder spawn sites below — see PR #268 which
// dropped the prior baseEncoderOpts wiring.
//
// Programmatic callers can construct RenderConfig directly and bypass the
// CLI's mutual-exclusivity guard. If both are set we honor crf (matches the
// CLI semantics where --crf is the explicit override) and warn loudly so
// the caller doesn't get a quietly-different bitrate than they passed in.
if (job.config.crf != null && job.config.videoBitrate) {
log.warn(
`[Render] Both crf=${job.config.crf} and videoBitrate=${job.config.videoBitrate} were set. ` +
`These are mutually exclusive; honoring crf and ignoring videoBitrate. ` +
`Set only one to silence this warning.`,
);
}
const effectiveQuality = job.config.crf ?? preset.quality;
const effectiveBitrate = job.config.crf != null ? undefined : job.config.videoBitrate;
job.framesRendered = 0;
// ── HDR z-ordered multi-layer compositing ──────────────────────────────
@@ -1316,7 +1336,8 @@ export async function executeRenderJob(
height,
codec: preset.codec,
preset: preset.preset,
quality: preset.quality,
quality: effectiveQuality,
bitrate: effectiveBitrate,
pixelFormat: preset.pixelFormat,
hdr: preset.hdr,
rawInputFormat: "rgb48le",
@@ -2033,7 +2054,8 @@ export async function executeRenderJob(
height,
codec: preset.codec,
preset: preset.preset,
quality: preset.quality,
quality: effectiveQuality,
bitrate: effectiveBitrate,
pixelFormat: preset.pixelFormat,
useGpu: job.config.useGpu,
imageFormat: captureOptions.format || "jpeg",
@@ -2259,7 +2281,8 @@ export async function executeRenderJob(
height,
codec: preset.codec,
preset: preset.preset,
quality: preset.quality,
quality: effectiveQuality,
bitrate: effectiveBitrate,
pixelFormat: preset.pixelFormat,
useGpu: job.config.useGpu,
hdr: preset.hdr,