mirror of
https://github.com/heygen-com/hyperframes.git
synced 2026-10-30 09:16:55 +00:00
fix(core): preserve in-flight video preloads during runtime binding (#4562)
This commit is contained in:
@@ -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();
|
||||
});
|
||||
});
|
||||
@@ -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();
|
||||
}
|
||||
Reference in New Issue
Block a user