Skip to content

fix(runtime): prepare trimmed video frames before preview cuts - #5633

Merged
miguel-heygen merged 1 commit into
mainfrom
fix/5631-preseek-trimmed-video
Oct 15, 2026
Merged

miguel-heygen merged 1 commit into
mainfrom
fix/5631-preseek-trimmed-video

Conversation

@miguel-heygen

Copy link
Copy Markdown
Collaborator

What

Prepare incoming trimmed video frames while they are still hidden, so continuous preview playback can show the in-point immediately at a cut.

Why

An incoming clip currently becomes visible before its activation seek has decoded a frame. This causes the cut flash reported in #5631. Preloading bytes alone does not put the video at its trimmed in-point.

Related work

Fixes #5631. No overlapping open PR found when starting this fix.

How

  • During playback, the runtime's indexed media pass also visits videos starting within 1.5 wall seconds. The query remains bounded by start time; paused seeks and audio visitation retain their existing behavior.
  • An inactive paused video with metadata seeks to its activation source position, respecting clipped origin, rate integration, loops and source-duration clamps. Preparation neither plays the video nor registers a visible-frame seek barrier. It skips outstanding seeks, already prepared positions, export capture and injected-frame videos.
  • Existing fetch ownership and drift correction remain in place. Preparation is best effort: a source that cannot load/decode within the lead time can still stall; jumps without lead time use the existing active seek path.

Test plan

  • 2,056 runtime tests pass (76 suites/1,707 tests, then six initially uncollected GSAP suites/349 tests after linking an existing compatible GSAP dependency; no dependency install).
  • New direct sync and real init/index regressions cover entering the lookahead after playback starts, delayed metadata, no duplicate activation seek, clipped origins, loops, source clamps, rate scaling, paused/audio/export/injected-frame isolation.
  • Removing the pre-seek fails the direct regression; removing the index lookahead fails the real init regression.
  • Core build, core and runtime TypeScript checks, preview-guard lint, changed-file oxlint/oxfmt and fallow changed-file audit pass. Independent code review found no blocker and independently ran 21 focused cases.
  • Actual built player + runtime in Chrome 152: same 12 trimmed clips from one H.264 source, 11 cuts, 55 ms source-response latency. Before: all incoming clips first visible at readyState 1/seeking, 18–241 ms until a decoded current frame. After: all 11 first visible at readyState 4/not seeking, 0 ms measured wait. This browser shows a stale source-start frame during the before gap, rather than black. Side-by-side recording and raw per-frame traces retained; recording shared for review.
  • Comments explain the cut/seek invariant and hidden barrier constraint.

The monorepo pre-commit typecheck proceeds past core but cannot complete Studio with the borrowed dependency environment (missing dockview-react/html2canvas/@mcp-b/global and older Tailwind). That local hook alone was excluded after recording the failures; hosted clean-environment Typecheck remains a mandatory merge gate. No dependency manifests or lockfiles changed.

No real-user footage, native Desktop launch, Safari/mobile run, or production release is claimed. Hosted required checks and exact-head approval must pass before normal queue merge.

@jrusso1020 jrusso1020 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Reviewed at 6a054640. I found no blocker in the code. This is a COMMENT and not an approval for two reasons: the approval needs a go from a maintainer, and CI is still running on this head.

Checked

  • Target matches activation. prepareUpcomingVideo computes mediaStart + sourceTimeAt(rate, start - origin). That is the same relTime the active branch of syncRuntimeMedia computes at t = start. The loop wrap and the non-loop sourceDuration clamp mirror the active path's loop wrap and held-tail clamp. At the cut, drift is about one tick, well under MEDIA_HARD_SYNC_SECONDS, and evictMediaSyncState makes it a first tick. So the activation takes the play path, not a second seek. The PR's claim holds.
  • Units. dueIn is in timeline seconds, and VIDEO_PRESEEK_SECONDS * playbackRate converts the 1.5 s wall-clock lead into timeline seconds. The index lookahead in init.ts uses the same bound, so the two cannot disagree about which clips are in range.
  • Index. The extra pass runs only while playing and not during export, and it only adds VIDEO elements. Audio and paused seeks visit exactly what they did before. Visiting an upcoming clip early runs only the inactive branch (evict, pause, prepare), which is a no-op for an already-paused hidden element.
  • Export and render. The pass is skipped under __HF_EXPORT_RENDER_SEEK_CONFIG (in both places) and when an injected render frame exists. playing is false during capture anyway. holdSeekBarrierUntilVideoLands is not called, so a hidden seek cannot hold up a visible frame.

Non-blocking

  • The activation path clamps with Math.max(0, min(t, end) - origin); the pre-seek path uses start - origin without the clamp. The two differ only if origin > start. In that case the target < 0 guard or the existing hard sync at activation covers it, so this falls back to today's behavior rather than regressing it.
  • The <= 0.001 "already prepared" check assumes the engine reads back currentTime as written. If an engine snaps it to a frame boundary, the clip would be seeked again on every tick inside the lead window, once the previous seek settles. Chrome is measured; Safari and Firefox are not, as the PR says.

I read the source and the two new test files. I did not run the suite locally or do a live player run.

— Rames

@github-actions

Copy link
Copy Markdown
Contributor

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

@jrusso1020 jrusso1020 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Approving at 6a054640, on Miguel's explicit go. CI on this head is fully green: 69 pass, 8 skipped, and all required checks pass. My earlier review found no blocker. Its two non-blocking edges still stand (the pre-seek is not clamped when origin > start, and the <= 0.001 re-seek check can loop if an engine quantizes currentTime).

— Rames

@miguel-heygen
miguel-heygen added this pull request to the merge queue Oct 15, 2026
Merged via the queue into main with commit 8f8f91c Oct 15, 2026
77 checks passed
@miguel-heygen
miguel-heygen deleted the fix/5631-preseek-trimmed-video branch October 15, 2026 03:51
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.

Preview: a trimmed video clip is shown before it has a frame at its in-point (black at every cut)

2 participants