diff --git a/packages/core/src/runtime/colorGrading.test.ts b/packages/core/src/runtime/colorGrading.test.ts index 014eb8826d..dd27d64a16 100644 --- a/packages/core/src/runtime/colorGrading.test.ts +++ b/packages/core/src/runtime/colorGrading.test.ts @@ -1041,6 +1041,29 @@ describe("createColorGradingRuntime", () => { expect(document.querySelector("[data-hf-color-grading-canvas]")).not.toBeNull(); }); + it("grades a clip's first producer frame when the DOM observer runs before it decodes", () => { + const video = makeDrawableVideo(); + video.style.visibility = "hidden"; + document.body.appendChild(video); + runtime = createColorGradingRuntime(); + + let decoded = false; + const frame = document.createElement("img"); + frame.id = "__render_frame_hero-video__"; + frame.className = "__render_frame__"; + frame.style.visibility = "visible"; + Object.defineProperty(frame, "complete", { get: () => decoded, configurable: true }); + Object.defineProperty(frame, "naturalWidth", { get: () => (decoded ? 640 : 0) }); + Object.defineProperty(frame, "naturalHeight", { get: () => (decoded ? 360 : 0) }); + video.after(frame); + runtime.refresh(); + decoded = true; + + expect(runtime.redraw()).toBe(1); + expect(texImage2DCalls.at(-1)).toContain(frame); + expect(frame.nextSibling).toBe(document.querySelector("[data-hf-color-grading-canvas]")); + }); + it("does not recreate an inactive clip from its hidden producer frame", () => { const { video, canvas } = startRuntimeWithVideo(); const frame = document.createElement("img"); diff --git a/packages/core/src/runtime/colorGrading.ts b/packages/core/src/runtime/colorGrading.ts index f0c5ae4a32..f72079ba96 100644 --- a/packages/core/src/runtime/colorGrading.ts +++ b/packages/core/src/runtime/colorGrading.ts @@ -2494,7 +2494,7 @@ function findRenderFrameImage(video: HTMLVideoElement): HTMLImageElement | null function hasInjectedRenderFrame(element: ColorGradingMediaElement): boolean { if (!isVideoElement(element)) return false; - const frame = findRenderFrameImage(element); + const frame = findInjectedRenderFrame(element); if (!frame) return false; const style = window.getComputedStyle(frame); return style.display !== "none" && style.visibility !== "hidden"; diff --git a/packages/producer/scripts/test-classification.mjs b/packages/producer/scripts/test-classification.mjs index 2aa4f1673b..0bf0e4f5d2 100644 --- a/packages/producer/scripts/test-classification.mjs +++ b/packages/producer/scripts/test-classification.mjs @@ -33,6 +33,7 @@ const INTEGRATION_TEST_FILES = new Set([ "src/services/render/hlsRender.integration.test.ts", "src/services/render/motionBlurVideo.integration.test.ts", "src/services/render/renderCancel.integration.test.ts", + "src/services/render/segmentedColorGrading.integration.test.ts", "src/services/render/stages/compileStage.mediaType.test.ts", "src/services/render/stages/gifEncodeArgs.test.ts", "src/services/videoFrameBorderClip.test.ts", diff --git a/packages/producer/src/services/render/segmentedColorGrading.integration.test.ts b/packages/producer/src/services/render/segmentedColorGrading.integration.test.ts new file mode 100644 index 0000000000..57356f9698 --- /dev/null +++ b/packages/producer/src/services/render/segmentedColorGrading.integration.test.ts @@ -0,0 +1,195 @@ +// A graded