mirror of
https://github.com/heygen-com/hyperframes.git
synced 2026-10-30 17:27:09 +00:00
fix(core): a seek during playback lands a playing video on the new time (#4467)
* fix(core): a seek during playback lands a playing video on the new time * fix(core): the seek fix passes the comment ratchet without a new comment * fix(core): keep why a pausing seek still lands, folded into the paragraph above --------- Co-authored-by: Miguel Angel Simon Sierra <miguelangelsisi098@gmail.com>
This commit is contained in:
co-authored by
Miguel Angel Simon Sierra
parent
93df8a814d
commit
2455a3d0ee
@@ -1628,6 +1628,25 @@ describe("syncRuntimeMedia", () => {
|
||||
expect(clip.el.currentTime).toBe(5);
|
||||
});
|
||||
|
||||
// A seek while playing pauses and syncs in one pass, before the video element has paused.
|
||||
it("a seek that pauses mid-playback lands a lagging playing video on the new time", () => {
|
||||
const clip = createMockClip({ start: 3.85, end: 5.6, duration: 1.75 });
|
||||
Object.defineProperty(clip.el, "paused", { value: false, writable: true });
|
||||
Object.defineProperty(clip.el, "currentTime", { value: 0.031, writable: true });
|
||||
syncRuntimeMedia({ clips: [clip], timeSeconds: 4.109, playing: true, playbackRate: 1 });
|
||||
expect(clip.el.currentTime).toBe(0.031);
|
||||
|
||||
syncRuntimeMedia({
|
||||
clips: [clip],
|
||||
timeSeconds: 4.4,
|
||||
playing: false,
|
||||
playbackRate: 1,
|
||||
forceSync: true,
|
||||
});
|
||||
expect(clip.el.currentTime).toBeCloseTo(0.55, 5);
|
||||
expect(clip.el.pause).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("mutes when either outputMuted OR userMuted is true (OR invariant)", () => {
|
||||
// Explicit validation of the combined-flag contract: setting one to
|
||||
// false while the other is true must keep the element muted.
|
||||
|
||||
@@ -461,13 +461,12 @@ export function syncRuntimeMedia(params: {
|
||||
(el.ended && canSeekEndedMediaBackward && drift > 0.001) ||
|
||||
staleAudioOnFirstTick ||
|
||||
(drift > 0.5 && (firstTickOfClip || offsetJumped || catastrophicDrift));
|
||||
// Playing video elements use the browser's native decoder pipeline for
|
||||
// timing. Seeking a playing video resets the decoder, causing a ~150ms
|
||||
// freeze while it re-buffers — during which the monotonic clock advances,
|
||||
// creating a perpetual seek→freeze→drift→seek stutter loop. Skip strict
|
||||
// and force sync for playing videos; only hard sync (>0.5s) warrants
|
||||
// the decoder-reset cost.
|
||||
const isPlayingVideo = el.tagName === "VIDEO" && !el.paused;
|
||||
// Playing videos use the browser's decoder for timing. Seeking one resets the decoder: a
|
||||
// ~150ms freeze while it re-buffers, as the monotonic clock advances, which loops into a
|
||||
// seek→freeze→drift→seek stutter. So a playing video skips strict and force sync; only hard
|
||||
// sync (>0.5s) warrants the decoder-reset cost. A paused transport pauses this video below,
|
||||
// so a seek that pauses mid-playback still lands it.
|
||||
const isPlayingVideo = el.tagName === "VIDEO" && !el.paused && params.playing;
|
||||
// Only apply strict sync when offset has stabilized (not growing).
|
||||
// During initial buffering, offset grows ~16ms/tick as the timeline
|
||||
// advances while media stays at 0. Accumulated drift from pause/play
|
||||
|
||||
Reference in New Issue
Block a user