mirror of
https://github.com/heygen-com/hyperframes.git
synced 2026-10-30 17:27:09 +00:00
fix(core): the picture keeps playing while a just-started take's audio catches up
This commit is contained in:
@@ -353,6 +353,32 @@ describe("TransportClock", () => {
|
||||
expect(clock.now()).toBeCloseTo(6.37, 5);
|
||||
});
|
||||
|
||||
it("runs the playhead past a just-started element frozen 300 ms behind it, as iOS leaves it", () => {
|
||||
const { clock, advance } = createClock({ duration: 30 });
|
||||
clock.play();
|
||||
advancePolled(clock, advance, 6300);
|
||||
const take = createMockAudioEl(0, false); // playing, its time frozen at its start
|
||||
clock.attachAudioSource({ el: take, compositionStart: 6, mediaStart: 0 });
|
||||
let previous = clock.now();
|
||||
for (let i = 0; i < 20; i++) {
|
||||
advance(16);
|
||||
const now = clock.now();
|
||||
expect(now).toBeGreaterThan(previous); // never held, though the element has not moved
|
||||
previous = now;
|
||||
}
|
||||
expect(previous).toBeCloseTo(6.3 + 0.32, 1);
|
||||
});
|
||||
|
||||
it("holds for an element no more than the steer limit behind, so a small lag is not seeked", () => {
|
||||
const { clock, advance } = createClock({ duration: 30 });
|
||||
clock.play();
|
||||
advancePolled(clock, advance, 6300);
|
||||
const take = createMockAudioEl(0, false); // 50 ms behind the playhead
|
||||
clock.attachAudioSource({ el: take, compositionStart: 6.25, mediaStart: 0 });
|
||||
advance(100);
|
||||
expect(clock.now()).toBeCloseTo(6.3, 5);
|
||||
});
|
||||
|
||||
it("holds the playhead while its element seeks, even to a time ahead", () => {
|
||||
const { clock, advance } = createClock({ duration: 30 });
|
||||
clock.play();
|
||||
@@ -400,7 +426,7 @@ describe("TransportClock", () => {
|
||||
clock.play();
|
||||
advancePolled(clock, advance, 2000);
|
||||
const bed = Object.assign(createMockAudioEl(0, false), { loop: true, duration: 8 });
|
||||
clock.attachAudioSource({ el: bed, compositionStart: 1.9, mediaStart: 0 });
|
||||
clock.attachAudioSource({ el: bed, compositionStart: 1.95, mediaStart: 0 });
|
||||
advance(16);
|
||||
expect(clock.now()).toBeCloseTo(2, 5);
|
||||
});
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { timeAtSourceTime, type RateSpec } from "../speedRamp.js";
|
||||
import { MEDIA_HARD_SYNC_SECONDS } from "./mediaSteer.js";
|
||||
import { AUDIO_STEER_LIMIT_SECONDS } from "./mediaSteer.js";
|
||||
import { playRangeHoldTime } from "./protocol.js";
|
||||
|
||||
export type TransportClockSnapshot = {
|
||||
@@ -99,7 +99,8 @@ export class TransportClock {
|
||||
private _heldTimeWhenBehind(el: HTMLMediaElement, behind: number, spec: RateSpec): number | null {
|
||||
if (el.loop && behind > timeAtSourceTime(spec, el.duration) / 2) return null;
|
||||
const buffering = el.readyState < HAVE_FUTURE_DATA;
|
||||
return buffering || behind <= MEDIA_HARD_SYNC_SECONDS ? this._lastNow : null;
|
||||
// Past the steer limit the sync seeks the audio onto the playhead, so the picture does not wait for it.
|
||||
return buffering || behind <= AUDIO_STEER_LIMIT_SECONDS ? this._lastNow : null;
|
||||
}
|
||||
|
||||
/** Folds a >500ms gap since the last read into `_playStartMs` so it's never reported as played time. */
|
||||
|
||||
@@ -11,7 +11,7 @@ const MEDIA_STEER_STEPS: readonly (readonly [lag: number, steer: number])[] = [
|
||||
const MEDIA_STEER_RELEASE_SECONDS = 0.01;
|
||||
// Audio never steers past 10%, where speech changes tempo; a lag held past 100 ms is seeked instead.
|
||||
const AUDIO_MAX_STEER = 0.1;
|
||||
const AUDIO_STEER_LIMIT_SECONDS = 0.1;
|
||||
export const AUDIO_STEER_LIMIT_SECONDS = 0.1;
|
||||
|
||||
/** Direction (+1 fast, -1 slow) an element is being steered in; absent when it plays at its authored rate. */
|
||||
const steering = new WeakMap<HTMLMediaElement, number>();
|
||||
|
||||
Reference in New Issue
Block a user