fix(core): preserve in-flight video preloads during runtime binding (#4562)

This commit is contained in:
James Russo
2026-09-27 02:16:19 +00:00
committed by GitHub
parent 91b21ae06f
commit cc439b0e59
3 changed files with 49 additions and 9 deletions
+2 -9
View File
@@ -1,4 +1,5 @@
// fallow-ignore-file code-duplication complexity
import { preloadMedia } from "./preloadMedia";
import { installRuntimeControlBridge, postRuntimeMessage, setRuntimeProtocolFps } from "./bridge";
import { instantTolerance } from "../clipFacts";
import { isInClipWindow } from "./clipWindow";
@@ -2383,15 +2384,7 @@ export function initSandboxRuntimeModular(): void {
// mode, for <audio>, or when the codec map is absent.
maybeProxyProactively(mediaEl);
// Eagerly preload media data so audio/video is buffered before the user
// clicks play. Without this, the first play() call fires on un-fetched
// media, producing silence or choppy audio until the browser caches it.
if (mediaEl.preload !== "auto") {
mediaEl.preload = "auto";
}
if (mediaEl.readyState < HTMLMediaElement.HAVE_FUTURE_DATA) {
mediaEl.load();
}
preloadMedia(mediaEl);
// Probe volume automation from the GSAP timeline — same approach as the
// renderer (see discoverAudioVolumeAutomationFromTimeline / audioMixer).
@@ -0,0 +1,36 @@
import { describe, expect, it, vi } from "vitest";
import { preloadMedia } from "./preloadMedia";
function media(tagName = "VIDEO", networkState = 2, readyState = 0) {
return { tagName, networkState, readyState, preload: "metadata", load: vi.fn() };
}
describe("initial media preload", () => {
it.each([0, 1, 2])(
"keeps an active video fetch at readyState %s while enabling eager preload",
(readyState) => {
const video = media("VIDEO", 2, readyState);
preloadMedia(video);
expect(video.preload).toBe("auto");
expect(video.load).not.toHaveBeenCalled();
},
);
it.each([0, 1, 3])("starts or retries incomplete video at networkState %s", (networkState) => {
const video = media("VIDEO", networkState);
preloadMedia(video);
expect(video.preload).toBe("auto");
expect(video.load).toHaveBeenCalledOnce();
});
it("preserves audio loading while a streaming source is still being fetched", () => {
const audio = media("AUDIO");
preloadMedia(audio);
expect(audio.preload).toBe("auto");
expect(audio.load).toHaveBeenCalledOnce();
});
it.each(["VIDEO", "AUDIO"])("does not reset ready %s media", (tagName) => {
const element = media(tagName, 1, 3);
preloadMedia(element);
expect(element.preload).toBe("auto");
expect(element.load).not.toHaveBeenCalled();
});
});
+11
View File
@@ -0,0 +1,11 @@
type PreloadableMedia = Pick<
HTMLMediaElement,
"tagName" | "preload" | "readyState" | "networkState" | "load"
>;
export function preloadMedia(media: PreloadableMedia): void {
if (media.preload !== "auto") media.preload = "auto";
// load() resets an in-flight video fetch, discarding its selected resource and buffered data.
const videoAlreadyLoading = media.tagName === "VIDEO" && media.networkState === 2;
if (media.readyState < 3 && !videoAlreadyLoading) media.load();
}