Repository navigation
fix(core): long cut-list edits play every cut without dropped or late frames - #6785
Conversation
Edit accuracy: accurate 271 (base branch 271), smooth 127 of thoseThe gate passes. Quarantined, measured but not gated (0) Unstable (1)
|
There was a problem hiding this comment.
Verdict: APPROVE · 0 blocker · 1 important · 0 nits · c3a36ff2
Both new tests fail with main's media.ts, as the fails without the fix: line says, and the 176 tests in media.test.ts pass at this head. CI is green; the PR is blocked only on review. The one finding, posted inline, doesn't block: it can expose at most about one frame, and only when the browser drops the last frame before the cut.
Scope
Read all of media.ts and media.test.ts. Checked every place the hold is cleared: hard, strict and force sync, evictMediaSyncState from both mediaProxy evict sites and the inactive branch. A transport pause while held keeps the out-point hold, and resuming stays paused, which is correct. A seek back over 0.5 s clears it (offsetJumped). Probed a dropped-frame case locally; details are in the inline comment.
…ad of stopping a frame early
…-point's frame show
…very clip each frame
c3a36ff to
1fa435d
Compare
…eygen-com#7271) 537b88d ported heygen-com#7271, whose muteCallbacks list spreads sceneTimelines(), but that helper arrives upstream with heygen-com#6785, which this fork has not taken. The default tsconfig excludes the runtime, so only CI's `tsc -p tsconfig.runtime.json` caught the missing name (TS2552 at init.ts:3982). Add upstream's helper as is: registered timelines other than the root composition's. Co-authored-by: Vance Ingalls <vance@heygen.com> Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The 1a9bb89 port of heygen-com#7402 and heygen-com#7378 brought in releaseMedia(el, flag) with window.__HF_RELEASE_PASSED_MEDIA (heygen-com#6929, needs the heygen-com#6785 buffered guard) and registerSeekCompletion(landed, "image-decode") (the SeekHold union from heygen-com#6596). Both upstream commits stay deferred, so typecheck:runtime failed in the Windows render verification workflow. The fork's releaseMedia already frees every clip, so the call keeps its behaviour. Add tsconfig.runtime.json to the pre-commit typecheck, which only covered tsconfig.json (it excludes src/runtime), and pin it in the hook test. Co-Authored-By: Claude Sonnet <noreply@anthropic.com>
What
Three fixes for Studio preview on a long edit cut from one source file:
Why
currentTimeis within one sync tolerance (40 ms) of its out-point. On 23.976 fps footage a frame lasts 41.7 ms, so whenever the last frame before the out-point starts inside those 40 ms, the video is paused before that frame is presented and the picture holds the frame before it until the cut.firstRenderedisnull,null?.size !== 0is true, and it took the fallback that shows every hidden clip: on a 140-clip edit, about 33,000 attribute writes a second, each a style invalidation on a video.srcand reloads it) so it stops holding one of the browser's six connections. A clip armed by the clip before it loads early; when that clip ends the arm lapses and the clip is released even when its whole file had arrived and it held no connection. It then fetched again just before its start, queued behind long video downloads on the same host, for 1.2-3.2 s.Related work
Refines #6438 (the out-point hold). #6033 bounds open-ended media ranges in the file servers, which shortens how long long downloads hold connections; fix 3 is independent of it. The reveal check in fix 2 was last changed by #6189 and #5690. Folds in #6786 and #6787.
How
requestVideoFrameCallback, a playing video within 0.5 s of its out-point is watched frame by frame. When a presented frame plus the longest frame step seen in this watch would land on or past the out-point, the video is paused there. The tick check holds a watched video once it reaches the predicted last frame before the out-point (on the shortest step seen), so a dropped frame cannot carry it onto the out-point's own frame; a paused video shows the frame at itscurrentTime, so that last frame still shows. A seek between two frames is not taken as a frame step. The 40 ms rule stays where the browser cannot report frames. The hold is dropped wherever the existing lead hold is dropped.releaseMediareturns early when the element's buffered range covers the whole file. Its only caller is the look-ahead window ininit.ts.Test plan
media.test.ts(a video inside the old 40 ms margin plays to its last frame and holds; a seek back from the hold plays again; a dropped last frame still holds before the out-point's frame; an uneven cadence holds before a long step crosses the out-point),init.test.ts(seeks on a page with no timeline never query[data-hf-clip-hidden]),preloadMedia.test.ts(a whole-file clip is kept; clips with 0-15 s or 430-2642 s of a long file buffered are released).plays a video in step with the playhead up to the last frame before its out-point; dropping the reset on a seek failsplays a held video again once a seek takes the playhead back; holding at the out-point instead of the predicted last frame failsholds a video on its last frame before the out-point when that frame is dropped; predicting from the last step instead of the longest failsholds a video with an uneven cadence before a long step crosses the out-point.leaves hidden clips alone on every jump when the page has no timeline.keeps a clip whose whole file is buffered; treating any range that reaches the end as whole failsreleases a clip with only 430-2642 s of a long file buffered; treating any range from the start as whole failsreleases a clip with only 0-15 s of a long file buffered.vitest run src/runtimeinpackages/core: 108 files, 2653 tests passed. oxfmt, oxlint, coretscand the comment ratchet clean.mainagainst each fix, 3 runs per side. Media is one synthetic 44-minute 1080p H.264 file at 23.976 fps with long keyframe gaps (7 s apart on average, up to 10.4 s). The measured builds predate the dropped-frame guard in fix 1, which is covered by its unit test only.A 149-clip edit of that file (graded clips, b-roll overlays), 60 s per run, picture timed from each video's
requestVideoFrameCallback:main→ all three fixesFix 1 alone on 64-124 s: longest picture hold 121, 108, 99 → 84, 83, 84 ms. Fix 3 alone on 0-60 s: latest first frame after the playhead reached a clip 1211, 1215, 1280 → 33, 29, 26 ms.
The same 140 clips with no GSAP timeline, 30 s runs, fix 2 alone:
main→ fix 2data-hf-clip-hiddenwrites a secondOn this machine the time fix 2 frees does not show up as smoother frames; the work goes away.
For scale: one plain
<video>playing the same file on the same machine drops 1-2 frames a minute.