Skip to content

fix(cli): snapshot stops Chrome decoding videos it already replaces with extracted frames - #6949

Merged
miguel-heygen merged 3 commits into
mainfrom
fix/snapshot-holds-video-decoders
Nov 3, 2026
Merged

miguel-heygen merged 3 commits into
mainfrom
fix/snapshot-holds-video-decoders

Conversation

@miguel-heygen

@miguel-heygen miguel-heygen commented Nov 1, 2026 •

Copy link
Copy Markdown
Collaborator

What

hyperframes snapshot no longer crashes Chrome on a project with many clips of one 4K source. Chrome stops decoding the project's own video files, because snapshot already replaces every active video with a frame it extracted with FFmpeg.

Why

Three reports (about 23, 36 and 48 clips of one 4K 10-bit 4:2:2 H.264 file with a rotation flag, and check passing on the same project) end in video frame injection failed, then Attempted to use detached Frame or Target closed. The page process runs out of memory: each active <video> gets its own decoder, about 0.5 GB for this source. Marking the <video> tags data-hidden made the snapshot work, which matches.

Render already avoids this: a video it replaces with extracted frames is held at preload="none" from parse (holdRenderFrameVideos). Snapshot replaced its videos the same way but never held them.

Change

  • Snapshot holds every video that plays a project file (localVideoRenderIds) with the same engine function. Remote clips are unchanged: Chrome still loads them, and snapshot still downloads a local copy for FFmpeg.
  • A held video has no duration in the page, and snapshot reads the source duration for clips without data-duration, loops, and the end-of-source clamp. Those durations now come from FFprobe (probeHeldVideo, once per file), together with the size.
  • A held video also gets the intrinsic-size poster render gives it (applyVideoMetadataHints, run before the readiness wait through the page-loaded hook), from the same FFprobe read with a rotation swap. Without it a width:100%;height:auto clip with no poster lays out at 300x150 and the injected frame lands in a squashed box.
  • The local-file lookup the extraction step already did inline is now projectLocalFile, used by both.
  • holdRenderFrameVideos and applyVideoMetadataHints are exported from the engine.

Measured

Linux x64, 4K 10-bit 4:2:2 H.264 with a 90 degree rotation flag, every clip active at once, Chrome process memory at its peak:

clips published 0.8.227 this change
6 5230 MB 500 MB
24 not run 498 MB
48 not run 529 MB

The still for 6 clips is byte-identical to the published CLI's. The remaining process-tree memory (about 5.2 GB) is the four FFmpeg decodes snapshot already caps.

fails without the fix: packages/cli/src/commands/snapshotHeldVideos.test.ts > "gives a held height:auto clip its intrinsic size, as render does, and skips what has none"; packages/cli/src/commands/snapshot.test.ts > "holds the project's local videos out of Chrome and reads their durations and sizes from FFprobe"

@github-actions

github-actions Bot commented Nov 1, 2026 •

Copy link
Copy Markdown
Contributor

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

@miguel-heygen
miguel-heygen force-pushed the fix/snapshot-holds-video-decoders branch from 8eab027 to 6811e31 Compare November 2, 2026 00:14

@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 · 0 important · 0 nits · head 6811e316

The memory fix is the right move, but snapshot holds these videos without the intrinsic-size hints that render applies alongside the same hold, so a local video with height:auto now lays out at the wrong size. ↓ inline

Scope
  • Read the full diff (5 files), plus renderFrameVideoHold.ts, isRenderFrameVideo in the runtime, render's hold and hint pairing (extractVideosStage.ts, frameCapture.ts installMediaPageHooks / applyVideoMetadataHints), and captureCompositionFrame.ts beforeNavigate.
  • The ids match on both sides: localVideoRenderIds uses data-hf-render-id || id, the same as holdRenderFrameVideos and __hfMediaId.
  • The body's fails without the fix: line names snapshot.test.ts › "holds the project's local videos out of Chrome and reads their durations from FFprobe". That test greps the source, so it does fail without the change.
  • CI on this head was still running (CI, CodeQL, regression, Windows, preview-regression) when I reviewed.

Comment thread packages/cli/src/commands/snapshot.ts Resolved
@miguel-heygen
miguel-heygen force-pushed the fix/snapshot-holds-video-decoders branch from 6811e31 to cc48b47 Compare November 3, 2026 00:16
@github-actions

github-actions Bot commented Nov 3, 2026

Copy link
Copy Markdown
Contributor

Fallow audit report

Quality gate failed

Found 1 finding.

Health (1)
Severity Rule Location Description
major fallow/high-crap-score packages/cli/src/commands/snapshotHeldVideos.ts:60 'parseProbe' has CRAP score 71.3 (threshold: 30.0, cyclomatic 16)

Generated by fallow.

Gate outcomes: failed audit-verdict.

@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 · 6811e316 → cc48b474

CI note

The optional Comments check fails because the new comment line at captureCompositionFrame.ts:129 raises that file's comment share from 6.9% to 7.0%. It isn't a required check, so it doesn't block. Deleting that line clears it. The required checks were still running when I approved.

@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 · cc48b474 → 9478dc16 (one comment reworded in snapshotHeldVideos.ts; the earlier commits are unchanged)

CI note

The optional Comments check still fails on this head. The ratchet is flagging captureCompositionFrame.ts (the added // Runs after navigation and before the render-ready wait. line, 6.9% → 7.0%), not snapshotHeldVideos.ts. Removing that one line clears it. It isn't a required check.

@miguel-heygen
miguel-heygen added this pull request to the merge queue Nov 3, 2026
Merged via the queue into main with commit 81b93cf Nov 3, 2026
91 of 93 checks passed
@miguel-heygen
miguel-heygen deleted the fix/snapshot-holds-video-decoders branch November 3, 2026 01:29
@jrusso1020 jrusso1020 mentioned this pull request Nov 3, 2026
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