Skip to content

feat(producer): a capture segment extracts the frames it shows and deletes them - #7241

Merged
vanceingalls merged 3 commits into
mainfrom
feat/segment-video-frames
Nov 5, 2026
Merged

vanceingalls merged 3 commits into
mainfrom
feat/segment-video-frames

Conversation

@vanceingalls

@vanceingalls vanceingalls commented Nov 5, 2026 •

Copy link
Copy Markdown
Collaborator

What

An optional stage input, segmentVideoFrames. When it's present, each capture segment extracts only the source frames it shows into <workDir>/segment-frames/segment_NNNNN/, deletes them when it closes, and the stage deletes segment files after a successful join. Nothing sets it yet: the routing is the next PR, so no render changes here.

Why

Part 2 of 3. See the last PR for the whole picture.

How

  • One stable hook per worker, surviving a recycle. prepare swaps in an injector over that segment's frames, and release removes the folder on success, failure and abort.
  • Outside a prepared window, the hook extracts on demand. The drawElement self-verify runs at session open and at each recycle, and stores ground-truth screenshots at samples across the whole composition. A no-op hook would have bypassed its guard at frameCapture.ts and stored black video boxes. Note that captureSegmentedStage does not itself compare drawElement frames with that ground truth, which predates this stack, so what this protects today is the arming path and any later check. So the hook extracts each sampled frame, injects it and deletes it, and carries fresh() for the isolated truth page. frameCapture.ts is unchanged.
  • The stall timer starts after prepare, and progress says Reading video for segment N/M, so slow seeks neither trip it nor look frozen.

Test plan

  • segmentVideoFrames.test.ts, with a stubbed extractor.
  • Stage tests: order of prepare and release, retry, resume, abort mid-capture and mid-extraction, recycle, stall budget, segment delete after a join.
  • Resume after segments were deleted recaptures rather than failing, pinned beside validateCompletedSegments.
  • Comments follow CONTRIBUTING.md.

🤖 Generated with Claude Code

@vanceingalls
vanceingalls force-pushed the feat/segment-video-frames branch from 2c08d8a to 79072a0 Compare November 5, 2026 13:41
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 · 1 important · 1 nit · 79072a0

The on-demand hook holds up. fresh() is the only one the engine calls (frameCapture.ts:5629, the self-verify truth page). Recycles only happen between segments, at cadence or on retry, while injector is null. Static dedup turns itself off whenever a hook is set. So each worker's self-verify truth frames come from frames it has actually extracted.

  • nit: removeJoinedSegments (captureSegmentedStage.ts:382) doesn't lower the peak. The orchestrator already runs rmSync(segmentDir) on success under the same keepSegments check, right after this stage returns. The peak happens during the concat, which both deletes come after.
Scope

Read segmentVideoFrames.ts and the stage diff end to end, plus the tests. I also traced the engine's BeforeCaptureHook callers: self-verify, prepareFrameForCapture, motion-blur accumulation and deferred drawElement init. CI doesn't run on stacked PRs. The dispatched run 42039548198 on the top branch, 1768fb8, includes this diff and is green.

— Genesis (Claude)


/** Extracts the given output frames into `dir` and returns an injector over them plus the fully extracted videos. */
const injectorFor = async (frames: number[], dir: string): Promise<BeforeCaptureHook | null> => {
const sliced = await extractFrames({

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.

important: Windowed extraction has no transient retry. Flag off, every ffmpeg extraction runs inside runVideoExtractionWithRetry, and so does extractAllDeferredFrames in #7242. These per-segment and on-demand calls don't. A long render makes about one call per video per segment, plus one per self-verify sample on every session open, all while the workers' Chromes are running. One EAGAIN/EMFILE (ffmpeg_transient) or download_transient on any of them propagates: retryAfterTargetLoss rethrows anything that isn't a target loss, so the render fails at segment N. The calibration forFrames call in #7242 also runs before its try, so the same error there kills a render that calibration would otherwise only downgrade to a conservative worker budget.

Fix: wrap this call in runVideoExtractionWithRetry({ signal, maxTransientRetries, onRetry: () => { rmSync(dir, …); mkdirSync(dir, …) } }), passing maxTransientRetries in from the orchestrator as resolveVideoExtractionPolicy().maxTransientRetries. That covers prepare, on-demand and forFrames together. Add one stage test where the stubbed extractor throws a retryable VideoSourceExtractionError once.

Base automatically changed from refactor/deferred-video-frames to main November 5, 2026 17:45
@vanceingalls
vanceingalls dismissed heygengenesis[bot]’s stale review November 5, 2026 17:45

The base branch was changed.

vanceingalls and others added 3 commits November 5, 2026 09:45
…letes them when it closes

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… prepared window

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…dded

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@vanceingalls
vanceingalls force-pushed the feat/segment-video-frames branch from 79072a0 to 920661f Compare November 5, 2026 17:45
@github-actions

github-actions Bot commented Nov 5, 2026

Copy link
Copy Markdown
Contributor

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

Unstable (1)

  • seqrotatenudge-none-pct-r0-root-z100: tracking 0.03, pressJump 0, drop 0, reload 3.03, render 0.04, renderKey -, undo false, teleport true / tracking 0.03, pressJump 0, drop 0, reload 0, render 0.08, renderKey -, undo true, teleport true / tracking 0.03, pressJump 0, drop 0, reload 0, render 0.08, renderKey -, undo true, teleport true

@vanceingalls
vanceingalls merged commit 599eaad into main Nov 5, 2026
144 of 146 checks passed
@vanceingalls
vanceingalls deleted the feat/segment-video-frames branch November 5, 2026 18:53
@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 <vance@heygen.com>
Co-authored-by: Miguel Ángel <miguel.sierra@heygen.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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