Skip to content

fix(core): a playing preview clip keeps its source until it pauses before a new copy size - #7243

Closed
miguel-heygen wants to merge 2 commits into
mainfrom
drenderq/defer-playing-swap
Closed

miguel-heygen wants to merge 2 commits into
mainfrom
drenderq/defer-playing-swap

Conversation

@miguel-heygen

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

Copy link
Copy Markdown
Collaborator

What

A preview clip that is playing at the playhead no longer has its source swapped for another size or quality of its lighter copy. The swap waits until the clip pauses, seeks, ends or leaves its window.

Why

Setting a video element's src empties it: it loads again from the start of the new file and shows "waiting" until it can play. The preview does this when the shown size of a clip changes (a larger copy to take, a light copy no longer needed), so a clip that is on screen and playing can stutter or freeze for a moment each time its size class changes. People on Windows reported choppy preview playback; this is one cause, a swap under a playing clip.

Related work

Refs the choppy-preview reports. Not covered here: the swap to a decodable copy after a player error (the desktop app's own ?hf-proxy=auto retry), which has to show something else while it loads; and a clip that fails to load for other reasons.

How

packages/core/src/runtime/mediaProxy.ts: whenNotPlayingHere(el, wanted, apply) runs apply now when the clip is not playing at the playhead, else on its next pause, seeking, ended or emptied or a 250 ms check, and does nothing if wanted() turned false meanwhile. requestBox uses it before loadProxy, and keepSized uses it before restoreOriginal. A pending upgrade stays marked until the swap runs, so no second request is made for the same size.

Size

Small on purpose: one helper in one file and its test, a lone fix no other open change carries.

Test plan

  • fails without the fix: packages/core/src/runtime/mediaProxy.test.ts › a clip playing at the playhead › keeps its copy when the video shows larger, and takes the larger one once it pauses (and › takes the larger copy when it seeks, and drops the wish when the copy it waited for is replaced). Both fail on the unfixed file (expected '2436x4096' to be '1218x2048') and pass with it; the whole file passes (76 tests), and oxfmt --check, oxlint and core's tsc --noEmit are clean.
  • A real Chromium run in the desktop app (Linux, Xvfb) with two 1080p clips the browser cannot decode, so each is swapped to a copy, played 20 s: source changes per run were 6, 6, 6 without the fix and 6, 7, 8 with it, and "waiting" events 3, 1, 2 without and 2, 5, 1 with it, so the totals do not show an improvement. In the one pair of runs whose timelines I read, the unfixed runtime changed the source of the playing clip at 6.6 s into it and the player waited at once; the fixed runtime changed no source of a clip while it played. One run each, on a loaded machine, so that is a pointer, not a measurement.

What I did not do

  • No before and after number from real users: it needs a shipped build.
  • The swap for a decodable copy after an error is not changed.
  • A project with many long HEVC clips (the case where the browser resets connections) is not tested.

@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 · 1 nit · head 385b0719

Scope

Read the full diff, plus every keepSized/requestBox caller and the pendingUpgrades lifecycle. A wish that is superseded or dropped while waiting (dropUnneededCopy, retryPreviewCopies, a src change) turns wanted() false, and the waiter cleans up without swapping. Locally mediaProxy.test.ts passes 76/76. With main's mediaProxy.ts both new tests fail (expected '2436x4096' to be '1218x2048'), so the fails without the fix: line holds. Two swaps stay out of scope: the error-retry swap (the body says so) and a user-set quality change through applyPreviewQuality. Comments is green; the rest of CI was still running.

— Genesis V2 (Lane B)

Comment thread packages/core/src/runtime/mediaProxy.ts Resolved
@github-actions

github-actions Bot commented Nov 5, 2026 •

Copy link
Copy Markdown
Contributor

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

Copy link
Copy Markdown
Collaborator Author

Took the nit (4abccc7): the put-back branch of keepSized now waits once per clip (a WeakSet of clips with a restore pending), so a panel drag during playback no longer stacks waiters. fails without the fix: mediaProxy.test.ts › waits once for a playing clip's light copy to be put back, however often its size is asked (3 pause listeners instead of 1 with the guard removed). Whole file: 77 pass.

@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 · 385b0719 → 4abccc72

— Genesis V2 (Lane B)

@miguel-heygen
miguel-heygen added this pull request to the merge queue Nov 5, 2026
@miguel-heygen
miguel-heygen removed this pull request from the merge queue due to a manual request Nov 5, 2026
@miguel-heygen

Copy link
Copy Markdown
Collaborator Author

Closing: a trigger run of 20 plays (size and quality changes mid-play, 5 each, with and without this change) produced no in-play source swaps either way, and waiting while playing did not differ. The Windows choppiness report stays open and gets a new fix if it recurs with a trigger we can reproduce.

SanHsien added a commit to SanHsien/hyperframes that referenced this pull request Nov 6, 2026
…view to heygen-com#7250

Adopts heygen-com#7310 (MPEG-4 Part 2 / MJPEG get browser-playable copies), heygen-com#7316
(check_report counts the copies a check waited for) and heygen-com#7317 (render reads
a composition src like the preview and refuses paths outside the project).
Defers heygen-com#7309 (needs heygen-com#6912); heygen-com#7314 does not apply. PR axis advances to heygen-com#7250
(heygen-com#7243 closed unmerged, heygen-com#7249 open and pinned as monitored; monitored 96->97).

Co-authored-by: Miguel Ángel <miguel.sierra@heygen.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.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