fix(producer): forward GPU preference to HDR streaming encoder (#5030)

This commit is contained in:
Monster-GM
2026-10-24 08:15:42 +00:00
committed by GitHub
parent e3881bc589
commit 6c5ad56e46
2 changed files with 97 additions and 0 deletions
@@ -267,6 +267,7 @@ export async function runCaptureHdrStage(
width,
height,
codec: preset.codec,
useGpu: job.config.useGpu,
preset: preset.preset,
quality: effectiveQuality,
bitrate: effectiveBitrate,
@@ -9,6 +9,7 @@ const framesDir = join(fixtureRoot, "frames");
afterAll(() => rmSync(fixtureRoot, { recursive: true, force: true }));
import { getCaptureStageBrowserConsole } from "../captureStageError.js";
import { createCapturePlan } from "../capturePlan.js";
import type { CaptureHdrStageInput } from "./captureHdrStage.js";
type MinimalEngineConfig = {
forceScreenshot: boolean;
@@ -72,6 +73,7 @@ mock.module("@hyperframes/engine", () => ({
return { buffer: Buffer.from("frame"), captureTimeMs: 1 };
},
closeCaptureSession,
cloneCaptureWarnings: () => [],
completeDeferredDrawElementInit: async () => {},
createCaptureSession: async () => ({
isInitialized: false,
@@ -79,6 +81,12 @@ mock.module("@hyperframes/engine", () => ({
options: { captureBeyondViewport: false },
workerEncodeEnabled: sessionWorkerEncodeEnabled,
captureMode: captureSessionMode,
page: {
evaluate: mock(async (): Promise<unknown> => [])
.mockResolvedValueOnce([])
.mockResolvedValueOnce({}),
},
warnings: [],
}),
createFrameReorderBuffer: () => ({
waitForFrame: async () => {},
@@ -753,6 +761,94 @@ describe("runCaptureStage", () => {
});
describe("runCaptureHdrStage", () => {
function createHdrInput(useGpu: boolean | undefined): CaptureHdrStageInput {
const plan = createCapturePlan({
workerCount: 1,
forceScreenshot: true,
useStreamingEncode: false,
useLayeredComposite: true,
usePageSideCompositing: false,
hasHdrContent: true,
needsAlpha: false,
});
if (plan.kind !== "hdr_layered") throw new Error("Expected an HDR layered capture plan");
return {
job: {
id: "capture-hdr-stage-test",
config: { fps: { num: 30, den: 1 }, quality: "draft", useGpu },
status: "queued",
progress: 0,
currentStage: "HDR Capture",
createdAt: new Date(0),
duration: 1,
},
cfg: { forceScreenshot: true },
plan,
log: { error: () => {}, warn: () => {}, info: () => {}, debug: () => {} },
projectDir: fixtureRoot,
compiledDir: fixtureRoot,
framesDir,
videoOnlyPath: join(fixtureRoot, "video-only.mp4"),
width: 64,
height: 64,
totalFrames: 1,
composition: { duration: 1, videos: [], audios: [], images: [], width: 64, height: 64 },
hasHdrContent: true,
effectiveHdr: { transfer: "pq" },
nativeHdrVideoIds: new Set<string>(),
nativeHdrImageIds: new Set<string>(),
videoTransfers: new Map(),
imageTransfers: new Map(),
hdrImageSrcPaths: new Map(),
preset: {
preset: "ultrafast",
quality: 28,
codec: "h265",
pixelFormat: "yuv420p10le",
hdr: { transfer: "pq" },
},
effectiveQuality: 28,
effectiveBitrate: undefined,
fileServer: {
url: "http://127.0.0.1:4173",
port: 4173,
close: () => {},
addPreHeadScript: () => {},
},
buildCaptureOptions: () => ({}),
createRenderVideoFrameInjector: () => null,
hdrDiagnostics: { videoExtractionFailures: 0, imageDecodeFailures: 0 },
abortSignal: undefined,
assertNotAborted: () => {},
};
}
it.each([true, false, undefined])("forwards useGpu=%s to the HDR encoder", async (useGpu) => {
failCaptureFrameToBuffer = false;
failInitializeSession = false;
spawnStreamingEncoder.mockClear();
closeEncoder.mockClear();
const { runCaptureHdrStage } = await import("./captureHdrStage.js");
const input = createHdrInput(useGpu);
const result = await runCaptureHdrStage(input);
expect(spawnStreamingEncoder).toHaveBeenCalledTimes(1);
expect(spawnStreamingEncoder).toHaveBeenCalledWith(
input.videoOnlyPath,
expect.objectContaining({
useGpu,
codec: "h265",
hdr: { transfer: "pq" },
rawInputFormat: "rgb48le",
}),
undefined,
{ ffmpegStreamingTimeout: 3_600_000 },
);
expect(closeEncoder).toHaveBeenCalledTimes(1);
expect(result.encodeMs).toBe(123);
});
it("wraps HDR capture failures with the browser console buffer", async () => {
failCaptureFrameToBuffer = false;
failInitializeSession = true;