perf(engine): one-pass SDR-to-HDR extraction with cache-key transform (#1902)

* perf(engine): one-pass SDR-to-HDR extraction with cache-key transform

Mixed-HDR compositions converted each SDR source with a full libx264
re-encode (convertSdrToHdr) before extraction. The BT.709 to BT.2020
colorspace remap now runs as a filter inside the extraction pass
itself; convertSdrToHdr and the _hdr_normalized intermediate are
deleted. Same shape as the earlier one-pass VFR change.

Also fixes a cache-poisoning bug this exposed: the HDR preflight
rewrote entry.videoPath AFTER the cache-key snapshot, so a mixed-HDR
render cached converted frames under the plain source key and a later
SDR render of the same trim would have served HDR-tinted frames. The
cache key now carries an optional transform discriminator; keys
without a transform stay byte-compatible with existing entries.

* fix(engine): attribute SDR-to-HDR extract failures, pin filter-order intent

Review hardening for one-pass SDR-to-HDR:

- ffmpeg failures now carry an 'SDR→HDR conversion failed (colorspace
  filter in extract pass)' prefix when the remap is in the chain, so a
  filter-less ffmpeg build fails loudly with attribution instead of a
  generic extract error.
- Comments pin the fps-before-colorspace ordering intent and mark
  sdrToHdrTransfers as the canonical read for both the cache key and
  extraction options.
- Cross-render cache-poisoning regression test now compares frame
  BYTES across the cache boundary: mixed-HDR render then plain-SDR
  render of the same trim must produce different pixels, and a repeat
  plain render must hit the plain entry with byte-identical frames.
This commit is contained in:
Miguel Ángel
2026-07-03 15:08:51 -07:00
committed by GitHub
parent 397c3ba7a8
commit 48f158a0c2
4 changed files with 204 additions and 184 deletions
@@ -133,6 +133,20 @@ describe("computeCacheKey", () => {
expect(a).not.toBe(b);
});
it("changes when a source transform is applied", () => {
const plain = computeCacheKey(base(sourceFile));
const transformedInput = { ...base(sourceFile), transform: "sdr2hdr-pq" };
const transformed = computeCacheKey(transformedInput);
expect(transformed).not.toBe(plain);
});
it("keeps undefined transform byte-compatible with omitted transform", () => {
const omitted = computeCacheKey(base(sourceFile));
const input = { ...base(sourceFile), transform: undefined };
const explicitUndefined = computeCacheKey(input);
expect(explicitUndefined).toBe(omitted);
});
it("normalizes non-finite duration so Infinity doesn't produce unstable keys", () => {
const a = computeCacheKey({ ...base(sourceFile), duration: Infinity });
const b = computeCacheKey({ ...base(sourceFile), duration: Infinity });