Skip to content

feat(engine): render sets a keyframe interval and a peak bitrate cap - #6444

Merged
miguel-heygen merged 12 commits into
mainfrom
feat/encoder-delivery-options
Oct 27, 2026
Merged

miguel-heygen merged 12 commits into
mainfrom
feat/encoder-delivery-options

Conversation

@miguel-heygen

@miguel-heygen miguel-heygen commented Oct 26, 2026 •

Copy link
Copy Markdown
Collaborator

hyperframes render and hyperframes lambda render (and render-batch) now take --keyframe-interval <seconds>, --max-bitrate <rate> and --buffer-size <size>, and Studio's render route accepts the same options. They reach the final MP4 encode, so delivery files need no second ffmpeg pass. Without the flags nothing changes.

What changed

  • Engine: appendRateCapArgs is the one place that writes -maxrate and -bufsize; both encoder builders call it. keyframeIntervalToGopSize turns seconds into frames at the render's fps.
  • Producer: render/deliveryEncoder.ts validates the options up front and resolves them once for all four encoder paths. The segment-plan hash includes them only when set, so a cached segment is never reused across a different cap.
  • Lambda: the options are locked plan fields; each chunk's GOP is the shorter of the chunk and the interval; the CFR re-encode keeps the cap.
  • CLI: one shared flag definition and parser (utils/deliveryFlags.ts) for local render, Docker re-run, lambda render and lambda render-batch. They apply to MP4 only and fail fast on other formats.
  • Studio: POST /projects/:id/render forwards the options to the job through one helper used by both the Vite and CLI adapters.
  • Docs: CLI help, docs/packages/cli.mdx, docs/guides/rendering.mdx, packages/cli/src/docs/rendering.md and the two CLI skill references.

What I measured

  • Real CLI render on a noisy 6 s source, 320x180 draft. Without the flags: keyframe at 0 s only, 1,469,149 bytes. With --keyframe-interval 2 --max-bitrate 300k: keyframes at 0, 2 and 4 s, 269,793 bytes. --format webm --max-bitrate 1M is refused with a usage error before any frame is captured.
  • Engine real ffmpeg + ffprobe tests: the cap holds the VBV bound and a plain encode exceeds it; a 2 s interval puts keyframes at 0, 2, 4, 6 s. A producer render test (deliveryRender.integration.test.ts) checks the same on a full render.
  • Removing the rate-cap call fails the cap tests; removing the locked-GOP call fails the keyframe test.
  • CLI plan, flag parser, Docker args and both Lambda config builders: 113 tests across those files pass, each asserting the option reaches its target. Studio-server route test asserts the options reach the render job (102 pass in that package).

What I did NOT exercise

  • A deployed Lambda render. The wire config and chunk planning are covered by unit tests, and chunk joins by reasoning (see below), not by a run on AWS.
  • GPU encoders and HDR output.
  • The Studio browser UI; only the HTTP route and the adapters.

Design calls

  • Keyframe interval is in seconds, rounded to the nearest whole frame, and reuses the closed-GOP path HLS already uses.
  • The cap is -maxrate plus -bufsize alongside CRF, so quality still sets the rate below the cap. The buffer defaults to twice the cap.
  • On Lambda the interval is a maximum (a chunk join adds a keyframe) and the cap holds per chunk, so a join can burst up to one buffer. Both limits are stated in the docs.
  • VP9 and ProRes fail with a clear error instead of ignoring the cap, since a silently uncapped delivery file would only show up downstream.

No visible change

packages/studio/vite.adapter.ts only forwards the render options to the job; no Studio screen, style or component changes.

@heygengenesis heygengenesis Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Verdict: REQUEST CHANGES · 1 blocker · 1 important · 0 nits · head 6d4ac59c

The engine layer is correct. -maxrate/-bufsize come after every -i in both builders, nothing else writes them, and the 195 engine encoder tests pass locally, including the real-encode VBV and keyframe tests. The blocker is the comment ratchet. ↓ inline

  • important (scope): the body says the CLI flags, producer/Lambda config, docs and Lambda chunk handling "follow in this same PR". The merge loop fires on the first approval, so approving this head merges only the engine half, under a title ("renders can set…") that no render can use yet. Either push the wiring before the next approval, or retitle this as the engine layer and open the rest as its own PR.

— Claude

@miguel-heygen
miguel-heygen marked this pull request as draft October 26, 2026 16:47
@miguel-heygen
miguel-heygen marked this pull request as ready for review October 26, 2026 16:48
@github-actions

github-actions Bot commented Oct 26, 2026 •

Copy link
Copy Markdown
Contributor

Edit accuracy: accurate 271 (base branch 271), smooth 118 of those

The gate passes.
Smoothness is reported in the artifact, not gated. A case fails only if it fails 2 of 3 runs.

Quarantined, measured but not gated (0)

Unstable (1)

  • seqplainkeys-none-pct-r0-root-z100: tracking 0.06, pressJump 0, drop 0, reload 0, render 0.03, renderKey -, undo false, teleport true / tracking 0.06, pressJump 0, drop 0, reload 0, render 0.03, renderKey -, undo true, teleport true / tracking 0.06, pressJump 0, drop 0, reload 0, render 0.03, renderKey -, undo true, teleport true

@heygengenesis heygengenesis Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Verdict: REQUEST CHANGES — 1 resolved, 1 open, 2 new · 6d4ac59 → 34febe1

The producer wiring is correct, and deliveryRender.integration.test.ts passes locally with Chrome and ffmpeg. Open: the scope item from last round. This head adds no CLI flags and no renderRequest/server, Lambda or distributed fields. The body still says "not wired yet… this head should not merge on its own", and that text would land in the squash commit. Either push that wiring or narrow the body and title to engine + producer RenderConfig. ↓ 2 inline

— Claude

@miguel-heygen
miguel-heygen force-pushed the feat/encoder-delivery-options branch from a739952 to b85e7f8 Compare October 26, 2026 17:50

@heygengenesis heygengenesis Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Verdict: REQUEST CHANGES (held) — 2 resolved, 1 open, 0 new · 34febe1 → b85e7f8. Still open: request/server/Lambda/CLI wiring, which per the author lands on this PR before merge. The earlier changes-requested review stays until that head is posted.

@miguel-heygen
miguel-heygen force-pushed the feat/encoder-delivery-options branch from b85e7f8 to bf04b76 Compare October 26, 2026 18:07

@heygengenesis heygengenesis Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Verdict: REQUEST CHANGES (held) — 0 resolved, 1 open, 0 new · b85e7f8 → bf04b76 (a rebase: same patch apart from the re-pinned classification sha256, which matches the file). Still open: request/server/Lambda/CLI wiring.

@heygengenesis heygengenesis Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Verdict: REQUEST CHANGES — 0 resolved, 1 open, 2 new · bf04b76 → b426131. Still open: CLI flags and docs, which per the author land on this PR. The request, producer-server, Lambda-plan, chunk and CFR wiring is correct as far as I traced it. encoder.json feeds planHash, so the new locked fields re-key plans without a separate pin. ↓ 2 inline

— Claude

SanHsien added a commit to SanHsien/hyperframes that referenced this pull request Oct 26, 2026
…ing, pin reviewed PR heads

- The ad4d3fa entry said --strict "returned to green"; its own scope note
  (and the live checker) say it is red because upstream moved on. Say so.
- Two entries were both titled "2026-10-27(續)"; the CI flake entry is now
  "2026-10-27(CI 紅燈)" and UPSTREAM.md names the entry it points to.
- The open-PR table records the head SHA each PR had at review time
  (criterion 8 step 1), reconstructed from PR timeline force-push events,
  committer times and the diff stats the entry already recorded. heygen-com#6440 and
  heygen-com#6444 were force-pushed after review.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
@miguel-heygen
miguel-heygen force-pushed the feat/encoder-delivery-options branch from b426131 to 74dceba Compare October 26, 2026 20:26
@mintlify

mintlify Bot commented Oct 26, 2026 •

Copy link
Copy Markdown

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

Project Status Preview Updated
hyperframes 🟢 Ready View Preview Oct 26, 2026, 11:57 PM

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

@heygengenesis heygengenesis Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Verdict: REQUEST CHANGES — 3 resolved, 0 open, 1 new · b426131 → 0e4f809. The code is ready, and CI and the 215 CLI + studio-server tests I ran locally pass. One new blocker, a body edit with no code change: the required Studio and player captures check fails ("the body has no Before/After heading") because packages/studio/vite.adapter.ts changed. There is nothing to capture (6 lines under packages/studio, no .tsx/.css/.html), so add a ## No visible change section saying the adapter only forwards render options. Then re-run the check.

— Claude

@heygengenesis heygengenesis Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Verdict: APPROVE — 1 resolved, 0 open, 0 new · 0e4f809 (no code change; the body now has ## No visible change and Studio and player captures passes. The earlier Windows engine-cli failure was the known chromeOrphans flake, and the re-run passes. All CI is green.)

@heygengenesis heygengenesis Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Verdict: APPROVE — 0 resolved, 0 open, 0 new · 0e4f809 → 1cb1548 (a rebase: the only patch change is the ...deliveryEncoder spread re-indented inside the refactored segmented call. The segment plan hash and segmentEncoderOptions still read it. All CI is green.)

@miguel-heygen
miguel-heygen added this pull request to the merge queue Oct 26, 2026
@github-merge-queue
github-merge-queue Bot removed this pull request from the merge queue due to a conflict with the base branch Oct 26, 2026
@miguel-heygen
miguel-heygen added this pull request to the merge queue Oct 26, 2026
@github-merge-queue
github-merge-queue Bot removed this pull request from the merge queue due to a conflict with the base branch Oct 26, 2026

@heygengenesis heygengenesis Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Verdict: APPROVE — 0 resolved, 0 open, 0 new · 1cb1548 → 495e8fb (a rebase: the PR patch is identical. Since the last base, main changed only integration-tests.json (the #6441 entry), and both entries are kept. All CI is green.)

@miguel-heygen
miguel-heygen added this pull request to the merge queue Oct 27, 2026
Merged via the queue into main with commit 54d0af5 Oct 27, 2026
81 checks passed
@miguel-heygen
miguel-heygen deleted the feat/encoder-delivery-options branch October 27, 2026 00:50

This branch was successfully deployed

1 active deployment
staging - docs — 495e8fbe Deployed Oct 26, 2026 by mintlify[bot]
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.

1 participant