Skip to content

Commit e266ad5

Browse files
fix(core): a frame capture keeps waiting for a video between its range requests (#4490)
1 parent dd88b5f commit e266ad5

2 files changed

Lines changed: 12 additions & 5 deletions

File tree

‎packages/core/src/runtime/media.test.ts‎

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -819,10 +819,17 @@ describe("syncRuntimeMedia", () => {
819819
}
820820
});
821821

822-
it("releases a loading video that stops fetching before it has data", async () => {
822+
it("keeps holding a loading video through a suspend between range requests", async () => {
823823
const clip = seekLoadingVideo();
824824
const barrier = waitForSeekCompletion();
825825
clip.el.dispatchEvent(new Event("suspend"));
826+
expect(await barrierSettled(barrier)).toBe(false);
827+
Object.defineProperty(clip.el, "seeking", { value: true, configurable: true });
828+
clip.el.dispatchEvent(new Event("loadedmetadata"));
829+
clip.el.dispatchEvent(new Event("seeking"));
830+
expect(await barrierSettled(barrier)).toBe(false);
831+
Object.defineProperty(clip.el, "seeking", { value: false, configurable: true });
832+
clip.el.dispatchEvent(new Event("seeked"));
826833
expect(await barrierSettled(barrier)).toBe(true);
827834
});
828835

‎packages/core/src/runtime/media.ts‎

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,13 +19,13 @@ export function readElementPlaybackStart(el: Element): number {
1919
return readMediaStart(el);
2020
}
2121

22-
const HOLD_END_EVENTS = ["seeked", "loadeddata", "suspend", "error", "emptied", "abort"] as const;
22+
const HOLD_END_EVENTS = ["seeked", "loadeddata", "error", "emptied", "abort"] as const;
2323
const HOLD_CAP_MS = 5000;
2424
const releaseHeldVideo = new WeakMap<HTMLMediaElement, () => void>();
2525

2626
// A seeking video still paints its previous frame, and one still fetching its first data paints none (its seek
27-
// waits for metadata without setting `seeking`); frame captures wait on the seek barrier until it lands. Each seek
28-
// re-arms a 5 s cap, and listeners run in the capture phase so a failing <source> child releases the hold.
27+
// waits for metadata without setting `seeking`); frame captures wait on the barrier until it lands. The 5 s cap is
28+
// the only end for a stalled source (`suspend` also fires between range requests); capture phase catches <source>.
2929
function holdSeekBarrierUntilVideoLands(el: HTMLMediaElement): void {
3030
const loading =
3131
el.readyState < el.HAVE_CURRENT_DATA &&
@@ -36,7 +36,7 @@ function holdSeekBarrierUntilVideoLands(el: HTMLMediaElement): void {
3636
registerSeekCompletion(
3737
new Promise<void>((resolve) => {
3838
const done = (event?: Event) => {
39-
if ((event?.type === "loadeddata" || event?.type === "suspend") && el.seeking) return;
39+
if (event?.type === "loadeddata" && el.seeking) return;
4040
clearTimeout(cap);
4141
for (const type of HOLD_END_EVENTS) el.removeEventListener(type, done, true);
4242
if (releaseHeldVideo.get(el) === done) releaseHeldVideo.delete(el);

0 commit comments

Comments
 (0)