Stop video seek loop when returning from background
This commit is contained in:
@@ -2704,6 +2704,24 @@ const Player = {
|
|||||||
SOFT_SYNC_THRESHOLD: 0.08,
|
SOFT_SYNC_THRESHOLD: 0.08,
|
||||||
HARD_SYNC_THRESHOLD: 0.3,
|
HARD_SYNC_THRESHOLD: 0.3,
|
||||||
SLEW_RATE: 0.04, // playbackRate offset applied while easing back into sync
|
SLEW_RATE: 0.04, // playbackRate offset applied while easing back into sync
|
||||||
|
SNAP_COOLDOWN_MS: 3000, // after a hard snap, only slew for this long
|
||||||
|
_seekLag: 0.25, // seconds the last video seek took (measured), used as the snap lead
|
||||||
|
|
||||||
|
// Seek the muted video to where the sounding clock WILL be once the seek lands,
|
||||||
|
// measuring how long seeks take on this device so the lead adapts.
|
||||||
|
snapVideoTo(V, clock) {
|
||||||
|
const rate = (clock && clock.playbackRate) || 1;
|
||||||
|
const lead = clock && !clock.paused ? Math.min(1.5, this._seekLag) * rate : 0;
|
||||||
|
const target = ((clock && clock.currentTime) || 0) + lead;
|
||||||
|
const started = performance.now();
|
||||||
|
this._internalSeek = true;
|
||||||
|
this._lastSnapAt = Date.now();
|
||||||
|
V.addEventListener('seeked', () => {
|
||||||
|
const took = (performance.now() - started) / 1000;
|
||||||
|
this._seekLag = Math.max(0.05, Math.min(1.5, this._seekLag * 0.5 + took * 0.5));
|
||||||
|
}, { once: true });
|
||||||
|
V.currentTime = target;
|
||||||
|
},
|
||||||
|
|
||||||
// Shared by the 1s watchdog tick and the master 'playing' handler so both
|
// Shared by the 1s watchdog tick and the master 'playing' handler so both
|
||||||
// paths get the same no-pop behavior.
|
// paths get the same no-pop behavior.
|
||||||
@@ -2719,12 +2737,18 @@ const Player = {
|
|||||||
correctDrift() {
|
correctDrift() {
|
||||||
if (this.mode !== 'dual' || !this.secondary || this.master.paused) return;
|
if (this.mode !== 'dual' || !this.secondary || this.master.paused) return;
|
||||||
if (this.secondary.paused) return; // resume paths own this case
|
if (this.secondary.paused) return; // resume paths own this case
|
||||||
|
// A seek still in flight is not drift: re-snapping now restarts the seek and,
|
||||||
|
// on iPhone, loops "seek → land behind → seek again" (stutter after resuming).
|
||||||
|
if (this.master.seeking) return;
|
||||||
const drift = this.secondary.currentTime - this.master.currentTime;
|
const drift = this.secondary.currentTime - this.master.currentTime;
|
||||||
const abs = Math.abs(drift);
|
const abs = Math.abs(drift);
|
||||||
if (abs >= this.HARD_SYNC_THRESHOLD) {
|
if (abs >= this.HARD_SYNC_THRESHOLD && Date.now() - (this._lastSnapAt || 0) < this.SNAP_COOLDOWN_MS) {
|
||||||
|
// Just snapped: ease in instead of snapping again.
|
||||||
|
const base = parseFloat(els.speed.value) || 1;
|
||||||
|
this.master.playbackRate = drift > 0 ? base + this.SLEW_RATE * 2 : base - this.SLEW_RATE * 2;
|
||||||
|
} else if (abs >= this.HARD_SYNC_THRESHOLD) {
|
||||||
// Snap the muted video onto the audio clock (inaudible).
|
// Snap the muted video onto the audio clock (inaudible).
|
||||||
this._internalSeek = true;
|
this.snapVideoTo(this.master, this.secondary);
|
||||||
this.master.currentTime = this.secondary.currentTime;
|
|
||||||
this._resyncSpeed();
|
this._resyncSpeed();
|
||||||
} else if (abs >= this.SOFT_SYNC_THRESHOLD) {
|
} else if (abs >= this.SOFT_SYNC_THRESHOLD) {
|
||||||
// Video behind audio -> speed the video up; ahead -> slow it down.
|
// Video behind audio -> speed the video up; ahead -> slow it down.
|
||||||
@@ -2841,7 +2865,7 @@ const Player = {
|
|||||||
this.master = V;
|
this.master = V;
|
||||||
this.secondary = A;
|
this.secondary = A;
|
||||||
try {
|
try {
|
||||||
if (AudioContinuity.needsAlignment(V.currentTime, t, 0.12)) { this._internalSeek = true; V.currentTime = t; }
|
if (AudioContinuity.needsAlignment(V.currentTime, t, 0.12)) this.snapVideoTo(V, A);
|
||||||
if (this._wantsPlaying) audioContinuity.play(V);
|
if (this._wantsPlaying) audioContinuity.play(V);
|
||||||
} catch { /* the watchdog picks up whatever is left */ }
|
} catch { /* the watchdog picks up whatever is left */ }
|
||||||
return;
|
return;
|
||||||
@@ -2853,7 +2877,7 @@ const Player = {
|
|||||||
this.mode = 'dual';
|
this.mode = 'dual';
|
||||||
this.secondary = A;
|
this.secondary = A;
|
||||||
V.muted = true;
|
V.muted = true;
|
||||||
if (AudioContinuity.needsAlignment(V.currentTime, t, 0.12)) { this._internalSeek = true; V.currentTime = t; }
|
if (AudioContinuity.needsAlignment(V.currentTime, t, 0.12)) this.snapVideoTo(V, A);
|
||||||
if (this._wantsPlaying) audioContinuity.play(V);
|
if (this._wantsPlaying) audioContinuity.play(V);
|
||||||
} catch { /* the watchdog picks up whatever is left */ }
|
} catch { /* the watchdog picks up whatever is left */ }
|
||||||
},
|
},
|
||||||
|
|||||||
Reference in New Issue
Block a user