Skip to content

feat(render): add CRF/bitrate controls and improve default quality#292

Merged
jrusso1020 merged 1 commit intomainfrom
feat/encoding-quality-controls
Apr 16, 2026
Merged

feat(render): add CRF/bitrate controls and improve default quality#292
jrusso1020 merged 1 commit intomainfrom
feat/encoding-quality-controls

Conversation

@jrusso1020
Copy link
Copy Markdown
Collaborator

@jrusso1020 jrusso1020 commented Apr 16, 2026

What

Add encoding quality controls and raise default MP4 quality to visually lossless at 1080p.

Why

a 40MB input video rendered to a 2.8MB MP4 with visible quality loss. The previous default CRF 23 was too aggressive for video-heavy compositions.

Screen.Recording.2026-04-16.at.1.30.33.AM.mov

How

Default quality bump (packages/engine):

  • standard: CRF 23 → 18 (visually lossless at 1080p, matches Remotion)
  • high: CRF 18 → 15 (near-lossless for final delivery)
  • draft: unchanged at CRF 28

CLI power-user flags (packages/cli):

  • --crf <0-51> — override CRF directly
  • --video-bitrate <e.g. 10M, 1.5M> — target bitrate encoding
  • Mutual exclusion enforced with validation

RenderConfig (packages/producer):

  • Added optional crf and videoBitrate fields
  • Extracted shared baseEncoderOpts to deduplicate streaming/chunk encoder paths

Studio UI (packages/studio):

  • Added quality dropdown: Draft / Standard / High Quality
  • Hidden for MOV (ProRes is fixed quality — no CRF control)
  • Format info tooltip now opens downward to avoid clipping
  • Typed quality param in useRenderQueue.startRender

Docs:

  • New "Quality and Encoding" section in rendering guide
  • Updated flag tables in both cli.mdx and rendering.mdx

Test plan

  • bun test — 35/35 encoder + config tests pass
  • Lint (oxlint), format (oxfmt), typecheck all pass via pre-commit hooks
  • Studio preview verified — quality dropdown shows for MP4/WebM, hides for MOV
  • Tooltip renders correctly (opens downward)
  • Manual render test with --crf 15 and --video-bitrate 10M
  • Compare file sizes: standard preset before (CRF 23) vs after (CRF 18)

🤖 Generated with Claude Code

Raise default encoding quality to visually lossless at 1080p (CRF 18)
and expose fine-grained encoding controls for power users.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@mintlify
Copy link
Copy Markdown

mintlify Bot commented Apr 16, 2026

Preview deployment for your docs. Learn more about Mintlify Previews.

Project Status Preview Updated (UTC)
hyperframes 🟢 Ready View Preview Apr 16, 2026, 8:37 AM

💡 Tip: Enable Workflows to automatically generate PRs for you.

@jrusso1020 jrusso1020 merged commit ebc12f7 into main Apr 16, 2026
21 checks passed
@jrusso1020 jrusso1020 deleted the feat/encoding-quality-controls branch April 16, 2026 19:02
vanceingalls added a commit that referenced this pull request Apr 22, 2026
…ders

The CLI flags `--crf` and `--video-bitrate` were defined and parsed in
`packages/cli/src/commands/render.ts`, validated for mutual exclusivity,
and threaded into `RenderConfig.crf`/`RenderConfig.videoBitrate`, but
the values were silently dropped at the encoder spawn sites in
`renderOrchestrator.ts`. 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.

This change re-introduces the override at the three encoder spawn
sites:

  1. HDR streaming encoder (rgb48le path)
  2. SDR streaming encoder (jpeg/png path)
  3. Disk-based encode (encodeFramesFromDir / encodeFramesChunkedConcat)

At each site, `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 do not need to re-check it here.

Also fixes 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`.
vanceingalls added a commit that referenced this pull request Apr 22, 2026
…ders

The CLI flags `--crf` and `--video-bitrate` were defined and parsed in
`packages/cli/src/commands/render.ts`, validated for mutual exclusivity,
and threaded into `RenderConfig.crf`/`RenderConfig.videoBitrate`, but
the values were silently dropped at the encoder spawn sites in
`renderOrchestrator.ts`. 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.

This change re-introduces the override at the three encoder spawn
sites:

  1. HDR streaming encoder (rgb48le path)
  2. SDR streaming encoder (jpeg/png path)
  3. Disk-based encode (encodeFramesFromDir / encodeFramesChunkedConcat)

At each site, `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 do not need to re-check it here.

Also fixes 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`.
vanceingalls added a commit that referenced this pull request Apr 23, 2026
…ders

The CLI flags `--crf` and `--video-bitrate` were defined and parsed in
`packages/cli/src/commands/render.ts`, validated for mutual exclusivity,
and threaded into `RenderConfig.crf`/`RenderConfig.videoBitrate`, but
the values were silently dropped at the encoder spawn sites in
`renderOrchestrator.ts`. 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.

This change re-introduces the override at the three encoder spawn
sites:

  1. HDR streaming encoder (rgb48le path)
  2. SDR streaming encoder (jpeg/png path)
  3. Disk-based encode (encodeFramesFromDir / encodeFramesChunkedConcat)

At each site, `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 do not need to re-check it here.

Also fixes 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`.
vanceingalls added a commit that referenced this pull request Apr 23, 2026
…ders

The CLI flags `--crf` and `--video-bitrate` were defined and parsed in
`packages/cli/src/commands/render.ts`, validated for mutual exclusivity,
and threaded into `RenderConfig.crf`/`RenderConfig.videoBitrate`, but
the values were silently dropped at the encoder spawn sites in
`renderOrchestrator.ts`. 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.

This change re-introduces the override at the three encoder spawn
sites:

  1. HDR streaming encoder (rgb48le path)
  2. SDR streaming encoder (jpeg/png path)
  3. Disk-based encode (encodeFramesFromDir / encodeFramesChunkedConcat)

At each site, `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 do not need to re-check it here.

Also fixes 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`.
vanceingalls added a commit that referenced this pull request Apr 23, 2026
…ders

The CLI flags `--crf` and `--video-bitrate` were defined and parsed in
`packages/cli/src/commands/render.ts`, validated for mutual exclusivity,
and threaded into `RenderConfig.crf`/`RenderConfig.videoBitrate`, but
the values were silently dropped at the encoder spawn sites in
`renderOrchestrator.ts`. 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.

This change re-introduces the override at the three encoder spawn
sites:

  1. HDR streaming encoder (rgb48le path)
  2. SDR streaming encoder (jpeg/png path)
  3. Disk-based encode (encodeFramesFromDir / encodeFramesChunkedConcat)

At each site, `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 do not need to re-check it here.

Also fixes 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`.
vanceingalls added a commit that referenced this pull request Apr 23, 2026
…ders

The CLI flags `--crf` and `--video-bitrate` were defined and parsed in
`packages/cli/src/commands/render.ts`, validated for mutual exclusivity,
and threaded into `RenderConfig.crf`/`RenderConfig.videoBitrate`, but
the values were silently dropped at the encoder spawn sites in
`renderOrchestrator.ts`. 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.

This change re-introduces the override at the three encoder spawn
sites:

  1. HDR streaming encoder (rgb48le path)
  2. SDR streaming encoder (jpeg/png path)
  3. Disk-based encode (encodeFramesFromDir / encodeFramesChunkedConcat)

At each site, `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 do not need to re-check it here.

Also fixes 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`.
vanceingalls added a commit that referenced this pull request Apr 23, 2026
…ders

The CLI flags `--crf` and `--video-bitrate` were defined and parsed in
`packages/cli/src/commands/render.ts`, validated for mutual exclusivity,
and threaded into `RenderConfig.crf`/`RenderConfig.videoBitrate`, but
the values were silently dropped at the encoder spawn sites in
`renderOrchestrator.ts`. 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.

This change re-introduces the override at the three encoder spawn
sites:

  1. HDR streaming encoder (rgb48le path)
  2. SDR streaming encoder (jpeg/png path)
  3. Disk-based encode (encodeFramesFromDir / encodeFramesChunkedConcat)

At each site, `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 do not need to re-check it here.

Also fixes 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`.
vanceingalls added a commit that referenced this pull request Apr 23, 2026
…ders

The CLI flags `--crf` and `--video-bitrate` were defined and parsed in
`packages/cli/src/commands/render.ts`, validated for mutual exclusivity,
and threaded into `RenderConfig.crf`/`RenderConfig.videoBitrate`, but
the values were silently dropped at the encoder spawn sites in
`renderOrchestrator.ts`. 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.

This change re-introduces the override at the three encoder spawn
sites:

  1. HDR streaming encoder (rgb48le path)
  2. SDR streaming encoder (jpeg/png path)
  3. Disk-based encode (encodeFramesFromDir / encodeFramesChunkedConcat)

At each site, `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 do not need to re-check it here.

Also fixes 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`.
vanceingalls added a commit that referenced this pull request Apr 23, 2026
…ders (#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.
vanceingalls added a commit that referenced this pull request Apr 23, 2026
…ders

The CLI flags `--crf` and `--video-bitrate` were defined and parsed in
`packages/cli/src/commands/render.ts`, validated for mutual exclusivity,
and threaded into `RenderConfig.crf`/`RenderConfig.videoBitrate`, but
the values were silently dropped at the encoder spawn sites in
`renderOrchestrator.ts`. 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.

This change re-introduces the override at the three encoder spawn
sites:

  1. HDR streaming encoder (rgb48le path)
  2. SDR streaming encoder (jpeg/png path)
  3. Disk-based encode (encodeFramesFromDir / encodeFramesChunkedConcat)

At each site, `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 do not need to re-check it here.

Also fixes 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`.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants