mirror of
https://github.com/heygen-com/hyperframes.git
synced 2026-10-30 09:16:55 +00:00
fix(engine): drawElement self-verify waits for colour-grading LUTs before its truth screenshot
This commit is contained in:
@@ -5,6 +5,7 @@ import type { CaptureSession } from "./frameCapture.js";
|
||||
import type { SubTimelineWaitMemo } from "../types.js";
|
||||
import {
|
||||
buildZeroDurationDiagnostic,
|
||||
captureDeVerificationFrames,
|
||||
pollHfReady,
|
||||
captureFramesBatchPipelined,
|
||||
captureFrameToBufferPipelined,
|
||||
@@ -726,3 +727,38 @@ describe("HF_READY_DIAGNOSTIC_EXPR (evaluated as real JS, not via a fake fixture
|
||||
expect(result.rejectedBuildReadyKeys).toEqual([]);
|
||||
});
|
||||
});
|
||||
|
||||
describe("captureDeVerificationFrames", () => {
|
||||
it("waits for the colour-grading LUTs after injecting video frames, before every truth screenshot", async () => {
|
||||
const log: string[] = [];
|
||||
const cdp = {
|
||||
send: vi.fn(async () => {
|
||||
log.push("screenshot");
|
||||
return { data: "" };
|
||||
}),
|
||||
};
|
||||
const page = {
|
||||
createCDPSession: async () => cdp,
|
||||
evaluate: vi.fn(async (fn: unknown) => {
|
||||
const src = String(fn);
|
||||
if (src.includes("waitForActiveLuts")) log.push("luts");
|
||||
else if (src.includes("data-start")) return [];
|
||||
return undefined;
|
||||
}),
|
||||
};
|
||||
const session = {
|
||||
options: { fps: 30, width: 8, height: 8, format: "jpeg", compositionDurationSeconds: 10 },
|
||||
onBeforeCapture: async () => {
|
||||
log.push("inject");
|
||||
},
|
||||
} as unknown as CaptureSession;
|
||||
|
||||
await captureDeVerificationFrames(session, page as never, () => {});
|
||||
|
||||
expect(session.deVerifyFrames?.size).toBeGreaterThan(0);
|
||||
log.forEach((entry, i) => {
|
||||
if (entry === "inject") expect(log.slice(i + 1, i + 3)).toEqual(["luts", "screenshot"]);
|
||||
});
|
||||
expect(log).toContain("inject");
|
||||
});
|
||||
});
|
||||
|
||||
@@ -3269,14 +3269,7 @@ async function prepareFrameForCapture(
|
||||
session.captureStep = "waiting for page seek handlers";
|
||||
await waitForPendingSeekCompletion(page);
|
||||
session.captureStep = "loading color-grading LUTs";
|
||||
await page.evaluate(async () => {
|
||||
const runtime = (
|
||||
window as Window & {
|
||||
__hf?: { colorGrading?: { waitForActiveLuts?: () => Promise<number> } };
|
||||
}
|
||||
).__hf?.colorGrading;
|
||||
await runtime?.waitForActiveLuts?.();
|
||||
});
|
||||
await waitForColorGradingLuts(page);
|
||||
const beforeCaptureMs = Date.now() - beforeCaptureStart;
|
||||
|
||||
// Page-side compositing three-phase protocol:
|
||||
@@ -3346,6 +3339,18 @@ export function computeAuthoredClipBoundaryFrames(
|
||||
return frames;
|
||||
}
|
||||
|
||||
/** A graded frame paints ungraded until the LUT has loaded, so wait before every screenshot. */
|
||||
async function waitForColorGradingLuts(page: Page): Promise<void> {
|
||||
await page.evaluate(async () => {
|
||||
const runtime = (
|
||||
window as Window & {
|
||||
__hf?: { colorGrading?: { waitForActiveLuts?: () => Promise<number> } };
|
||||
}
|
||||
).__hf?.colorGrading;
|
||||
await runtime?.waitForActiveLuts?.();
|
||||
});
|
||||
}
|
||||
|
||||
async function computeClipBoundaryFrames(page: Page, fps: number): Promise<Set<number>> {
|
||||
const schedule = await page.evaluate(() =>
|
||||
Array.from(document.querySelectorAll("[data-start]")).map((el) => ({
|
||||
@@ -5408,7 +5413,7 @@ export async function seekDeVerifyTruth(
|
||||
);
|
||||
}
|
||||
|
||||
async function captureDeVerificationFrames(
|
||||
export async function captureDeVerificationFrames(
|
||||
session: CaptureSession,
|
||||
page: Page,
|
||||
logInitPhase: (phase: string) => void,
|
||||
@@ -5493,6 +5498,7 @@ async function captureDeVerificationFrames(
|
||||
// it, <video> elements screenshot black and every video comp would
|
||||
// false-positive into the screenshot fallback.
|
||||
await injectFrames?.(truthPage, t);
|
||||
await waitForColorGradingLuts(truthPage);
|
||||
// The first screenshot forces a frame, which runs rAF-driven callbacks (a count-up lands
|
||||
// a tick after seek()); waiting on rAF via evaluate deadlocks, headless fires it only
|
||||
// when a frame is produced.
|
||||
|
||||
Reference in New Issue
Block a user