Skip to content

feat(producer): render long videos with windowed source frames behind a flag - #7242

Merged
vanceingalls merged 9 commits into
mainfrom
feat/video-frame-window
Nov 5, 2026
Merged

vanceingalls merged 9 commits into
mainfrom
feat/video-frame-window

Conversation

@vanceingalls

@vanceingalls vanceingalls commented Nov 5, 2026 •

Copy link
Copy Markdown
Collaborator

What

HF_VIDEO_FRAME_WINDOW=true makes a segmented render hold a few segments' worth of source frames on disk instead of every frame. Opt-in, off by default, and only on the sdr_segmented plan. With the flag off, every path behaves as today.

Why

A 46-minute three-camera podcast asks HyperFrames Desktop for 27 to 89 GB free, almost all of it extracted source frames. This brings the "extract per chunk" move from distributed renders to local ones.

How

  • shouldWindowVideoFrames decides early, since the capture plan isn't final until the extract stage has probed.
  • Every exit from the segmented route fills all frames first: a plan that isn't segmented, and the spawn-failure fallback to streaming. A drawElement retry stays segmented.
  • VFR, HDR-transformed, final-frame-only and motion-blur renders stay fully extracted. Blur samples at a clip's edge can need a frame the segment never extracted.
  • Calibration sees real frames. It extracts just its sample frames before timing starts, because blank videos under-measure cost and would pick too many workers.
  • Segment delete after the join honours --keep-segments, and is best-effort: a leftover file must not throw away a finished render.

Measured on real footage

Worktree-built CLI, HF_SEGMENTED_CAPTURE=true, --quality standard --sdr --workers auto, drawElement capture, 3 workers.

Run Flag Wall Frames Output
Long-GOP AV1, 5:00 off 292 s 8,992 222,475,668 B
Same on 298 s 8,992 222,475,668 B
Podcast, flattened, first 5:00 off 275 s 7,193 191,310,453 B
Same on 275 s 7,193 191,310,453 B
Whole podcast, 46:03 on 3123 s 66,244 1,757,647,871 B
  • Frames: framemd5 identical on both pairs, and the files are byte-identical with cmp. The whole podcast has no flag-off pair, because that doesn't fit on the disk.
  • Time: ratios 1.02 and 1.00, against a 1.15 gate.
  • Disk, whole podcast: 10.57 GB held at the lowest 10 s sample, about 10.9 GB reconstructed at the moment the join finishes. This narrowly misses the plan's 10 GB gate. The gate came from an estimate that put the flattened file at 4.1 GB. It's 6.39 GB. The render's own share is about 4.2 GB.
  • HF_SEGMENT_FRAMES=1500 halves frames on disk and ran 5% faster, but doesn't move the peak, which is at the join.

Known gaps

  • No automated guard for windowed drawElement parity. windowedFrames.integration.test.ts pins browserGpuMode: "software", so it takes the screenshot path. drawElement output parity was measured by hand in the runs above. A drawElement test exists as a draft that passes on macOS, but it can't run on CI today: self-verify's screenshot times out on a GPU-less Linux headless shell.
  • The segmented route doesn't check self-verify samples. captureSegmentedStage arms ground-truth samples and never compares drawElement frames with them; only captureStreamingStage and the disk-sample path do. This predates this stack, and it means the parity results above are the evidence for these renders, not self-verify. Raised with the stage's owner.
  • A windowed render gets no extraction-cache hits for its deferred videos, and two clips of one source and range are extracted separately.

CI on a stacked PR

CI's integration lane and Build run only on PRs against main, so they don't appear on this PR while it's stacked. I dispatched CI on this branch instead. Run 42039548198 at this head passed in full, including Producer: integration tests and Build. All 11 cases of windowedFrames.integration.test.ts passed on Linux in the previous run, whose only failure was the size budget below.

Package size budget: @hyperframes/producer packed goes from 845,000 to 857,000 bytes. This branch measured 854,006. The growth is the new segment frame modules, with no tests or source maps added to the tarball.

Test plan

  • windowedFrames.integration.test.ts renders each case flag on and flag off and compares framemd5: mid-clip start, NTSC, loop across a slice edge, rate ramp, held tail, two clips of one file, VFR beside CFR, 2 workers, calibrating workers, motion blur. Plus a disk bound. Fails without the fix: a one-frame shift in segmentVideoFrames.ts first differs at output frame 60.
  • renderOrchestrator.videoFrameWindow.test.ts, videoFrameWindow.test.ts, stage tests.
  • Manual testing on real footage, above.
  • Documentation updated: docs/packages/cli.mdx.
  • Comments follow CONTRIBUTING.md.

🤖 Generated with Claude Code

heygengenesis[bot]
heygengenesis Bot previously approved these changes Nov 5, 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 blocker · 0 important · 0 nits · 1768fb8

Every exit from the segmented route is covered. The only replanAfterFailure calls on an sdr_segmented plan are the drawElement retry, which stays segmented, and the spawn fallback, which fills the frames first. A plan that resolves to anything other than sdr_segmented fills them at line 4747. The FrameLookupTable holds the ExtractedFrames objects and reads framePaths lazily, so the in-place fill reaches createRenderVideoFrameInjector. The one finding, a missing transient retry that also affects the calibration forFrames, is posted inline on #7241, where the code lives.

Scope

Read the orchestrator diff, videoFrameWindow.ts, deferredVideoFrames.ts (extractAllDeferredFrames), videoFrameCoverage.ts and the calibration wiring end to end. I skimmed the integration and orchestrator tests. Run 42039548198, dispatched at 1768fb8, is green, including the integration lane and Build. Comments is green.

— Genesis (Claude)

@vanceingalls
vanceingalls force-pushed the feat/segment-video-frames branch from 79072a0 to 920661f Compare November 5, 2026 17:45
Base automatically changed from feat/segment-video-frames to main November 5, 2026 18:53
@vanceingalls
vanceingalls dismissed heygengenesis[bot]’s stale review November 5, 2026 18:53

The base branch was changed.

@mintlify

mintlify Bot commented Nov 5, 2026 •

Copy link
Copy Markdown

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

Project Status Preview Updated
hyperframes 🟢 Ready View Preview Nov 5, 2026, 6:56 PM

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

vanceingalls and others added 8 commits November 5, 2026 10:53
…d HF_VIDEO_FRAME_WINDOW

Co-Authored-By: Claude Opus 5.5 <[email protected]>
…ll extraction reports itself

Co-Authored-By: Claude Opus 5.5 <[email protected]>
Renders ten compositions twice on the segmented route, with and without
HF_VIDEO_FRAME_WINDOW, and compares FFmpeg framemd5 of the two outputs: a
mid-clip start, NTSC 24000/1001, a loop crossing a slice edge, a playback-rate
ramp, a held tail, two clips of one file, a VFR source beside a CFR one, two
workers, auto workers (capture calibration runs), and motion blur, which
declines the window. Each pair must also report the same capture mode.

A separate test samples segment-frames during a four-segment, two-worker
render and holds its peak under workers x segment frames x the largest frame
file x 1.2, and checks the folder is empty once the render closes.

Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
…ts what it claims

Find the render's work dir with resolveRenderWorkDirPrefix and the owner record's pid, so
the peak test works on Linux and never samples or deletes a concurrent render's dir.
Assert the measured worker count, calibration, and the exact up-front frame count of the
VFR video. Retry a dead Chrome only with HF_TEST_RETRY_BROWSER_STALL=1, and fail a case
whose windowed render alone needed it.

Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
The new segment frame modules add about 9 KB to the packed producer package.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
@vanceingalls
vanceingalls force-pushed the feat/video-frame-window branch from 1768fb8 to a21885c Compare November 5, 2026 18:54
@github-actions

github-actions Bot commented Nov 5, 2026

Copy link
Copy Markdown
Contributor

Edit accuracy: accurate 267 (base branch 267), smooth 108 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 (8)

@vanceingalls
vanceingalls merged commit 968f97f into main Nov 5, 2026
93 checks passed
@vanceingalls
vanceingalls deleted the feat/video-frame-window branch November 5, 2026 19:27
@vanceingalls vanceingalls mentioned this pull request Nov 5, 2026
2 of 3 tasks
SanHsien added a commit to SanHsien/hyperframes that referenced this pull request Nov 6, 2026
…view

Adopts heygen-com#7240 (deferred frame extraction leaves the distributed code),
heygen-com#7241 (a capture segment extracts and deletes its own frames), heygen-com#7255
(segmented and sequential capture check drawElement samples), heygen-com#7254
(normalize-audio --reference stops short of the peak ceiling), heygen-com#7256
(engine names a failed script's origin first), heygen-com#7258 (SVG gradient text
judged by its stops) and heygen-com#7242 (windowed source frames, off unless
HF_VIDEO_FRAME_WINDOW=true). Release v0.9.17 is not synced wholesale;
the producer packed budget takes upstream's +12000 from this range.
Watermark advances to 32eb4c3.

Co-authored-by: Vance Ingalls <[email protected]>
Co-authored-by: Miguel Ángel <[email protected]>
Co-Authored-By: Claude Opus 5.5 <[email protected]>
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