Skip to content

fix(producer): auto renders with color-graded media stay SDR instead of promoting to HDR - #6671

Merged
miguel-heygen merged 2 commits into
mainfrom
fix/hdr-graded-footage
Oct 31, 2026
Merged

miguel-heygen merged 2 commits into
mainfrom
fix/hdr-graded-footage

Conversation

@miguel-heygen

@miguel-heygen miguel-heygen commented Oct 28, 2026 •

Copy link
Copy Markdown
Collaborator

What

A render in auto HDR mode that has any color-graded media (data-color-grading on a <video>, <img> or <canvas>) now renders SDR, as --sdr does: HDR sources are tone-mapped to SDR before the grade, and the render logs why. Before, one HDR clip (an iPhone HLG video, for example) promoted the whole render to HDR HEVC, and every graded element was graded in SDR and then placed in the HDR output, with no warning.

Why

Color grading is SDR-only. The media-treatment contract says so ("The realtime treatment path is Rec.709/SDR. Do not silently process HDR, HLG, PQ, or camera LOG sources through it."), but auto mode did exactly that. Reproduced on 0.8.212 with generated clips (a 1080x1920 HEVC Main10 HLG clip tagged bt2020/arib-std-b67, and an SDR clip with the clean-studio preset):

Composition 0.8.212 output This PR
HLG clip graded HEVC HLG; the graded clip about 25% darker (mean luma 97 vs 128 ungraded) and dull H.264 BT.709; tone-mapped then graded (mean luma 118)
HLG clip, then a graded SDR clip HEVC HLG; the graded clip in SDR placed in HDR H.264 BT.709
HLG clip, nothing graded HEVC HLG unchanged: HEVC HLG

check reported the same auto-promotion, so nothing flagged the combination.

Related work

#5728 changes extractVideosStage.ts and renderOrchestrator.ts too, in different hunks. Whichever lands second rebases.

How

  • parseVideoElements and parseImageElements (engine) mark an element that carries data-color-grading as graded; hasGradedCanvas (engine) does the same for a graded <canvas>, which the media collector carries as gradedCanvas through compile into the render.
  • gradingKeepsSdr(hdrMode, media) (engine) is the one rule: auto mode plus any graded media means SDR. It's read by:
    • rendersSdrOnly, so extraction tone-maps HDR sources as it does for --sdr;
    • the HDR color-space probes in extractVideosStage, which are skipped as they are for --sdr;
    • resolveEffectiveHdrMode, which logs: HDR source detected, but color grading is SDR-only, so rendering SDR ... Use --hdr to force HDR output (grades stay SDR).;
    • check's HDR promotion report, which no longer reports a promotion the render won't make.
  • --hdr with graded media still renders HDR, and now warns that the graded elements are graded in SDR.
  • snapshot needs no change: its stills already tone-map HDR the way an SDR export does, so a graded composition's still now matches its render.

Test plan

  • Unit tests added: hdrMode.test.ts (auto with graded media stays SDR and says why; --hdr with graded media stays HDR and warns; rendersSdrOnly with graded media), hdr.test.ts (gradingKeepsSdr, including a graded canvas), videoFrameExtractor.test.ts (graded video and image are marked, others are not; hasGradedCanvas), hdrPromotion.test.ts (no promotion and no probe when graded media keeps the render SDR).
  • Fails without the fix: hdrMode.test.ts > resolveEffectiveHdrMode > keeps an auto render with graded media SDR when an HDR source would promote it, and says why (expected { transfer: 'pq' } to be undefined) and > rendersSdrOnly > is true for an auto render with graded media..., with the rule removed from the producer.
  • Real renders with the CLI built from this branch, on Linux: the three compositions above (output tags and mean luma as in the table). The same repro on 0.8.212 on an Apple Silicon Mac matched the Linux numbers.
  • check.test.ts 150 passed; renderMediaCollector.test.ts and htmlCompiler.test.ts 189 passed; engine, producer and CLI typecheck clean; oxfmt, oxlint and fallow audit clean (the repo's commit hooks, run on the push).
  • Documentation updated (not applicable: the contract already says grading is SDR-only)
  • Comments follow CONTRIBUTING.md "Comments"

@github-actions

github-actions Bot commented Oct 28, 2026 •

Copy link
Copy Markdown
Contributor

Edit accuracy: accurate 271 (base branch 271), smooth 125 of those

The gate passes.
Smoothness is reported in the artifact, not gated. A case fails only if it fails 2 of 3 runs.

Quarantined, measured but not gated (0)

Unstable (1)

  • seqplainkeys-none-center-r0-nested-z100: tracking 0.06, pressJump 0, drop 0, reload 0, render 0.03, renderKey -, undo false, teleport true / tracking 0.06, pressJump 0, drop 0, reload 0, render 0.03, renderKey -, undo true, teleport true / tracking 0.06, pressJump 0, drop 0, reload 0, render 0.03, renderKey -, undo true, teleport true

@heygengenesis heygengenesis Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Verdict: REQUEST CHANGES · 1 blocker · 1 important · 0 nits · head 5085f495

The Comments check is red. Every required check is green. The SDR-on-grade logic is correct at every hdrMode/rendersSdrOnly site, and collectRenderMedia spreads graded through to both render and check. ↓ inline

Scope

Read the full diff. I audited all "force-sdr" branches and the callers of rendersSdrOnly and resolveEffectiveHdrMode. Distributed mode only allows auto/force-sdr and reaches the extract stage through the same rendersSdrOnly. The fails without the fix: line names hdrMode.test.ts > resolveEffectiveHdrMode > keeps an auto render with graded media SDR…. I did not run any renders.

Comment thread packages/engine/src/utils/hdr.ts Resolved

@heygengenesis heygengenesis Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Verdict: APPROVE — 2 resolved, 0 open, 0 new · 5085f495 → 919df04f

CI is green. The first Windows run failed on the known flaky Chrome-orphans cleanup test in the cli package; the second run on this head passed.

@miguel-heygen
miguel-heygen added this pull request to the merge queue Oct 31, 2026
Merged via the queue into main with commit 60f7116 Oct 31, 2026
127 of 129 checks passed
@miguel-heygen
miguel-heygen deleted the fix/hdr-graded-footage branch October 31, 2026 16:41
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant