Skip to content

fix(engine): add bt709 color space + range conversion to encoding#223

Merged
jrusso1020 merged 1 commit intomainfrom
fix/encoder-colorspace-bt709
Apr 7, 2026
Merged

fix(engine): add bt709 color space + range conversion to encoding#223
jrusso1020 merged 1 commit intomainfrom
fix/encoder-colorspace-bt709

Conversation

@jrusso1020
Copy link
Copy Markdown
Collaborator

Description

Adds proper BT.709 color space metadata and full→limited range conversion to H.264/H.265 encoding. Chrome captures frames in full-range sRGB (BT.709 primaries), but without explicit color tagging, players guess the wrong color space and range — causing color shifts across iOS/Android/desktop and crushed dark values that compound the gradient banding issue fixed in #222.

What changed:

Setting Before After
color_space bt470bg (guessed) bt709 (explicit)
color_primaries unknown bt709
color_transfer unknown bt709
color_range pc (full, wrong for H.264) tv (limited, correct)
time_base 1/15360 (varies by platform) 1/90000 (fixed)

Approach:

  • BT.709 VUI params embedded via x264-params/x265-params (colorprim=bt709:transfer=bt709:colormatrix=bt709) — ensures the bitstream itself carries color info
  • FFmpeg-level metadata flags (-colorspace:v bt709, etc.) — belt-and-suspenders
  • scale=in_range=pc:out_range=tv filter converts Chrome's full-range output to TV/limited range
  • VAAPI path chains the range filter with existing format=nv12,hwupload
  • -video_track_timescale 90000 for consistent cross-platform A/V timing (same as Remotion)
  • VP9 and ProRes encoding unaffected

Testing

  • Verified via ffprobe: all 5 color metadata fields now correct
  • Directly tested FFmpeg args produce expected output
  • 40 engine tests pass (8 new: color metadata h264/h265, range filter CPU, VAAPI filter chain, GPU skip, VP9 skip, timescale)
  • Builds cleanly, lint + format pass

…encoding

Chrome captures frames in full-range sRGB, which maps to BT.709. Without
explicit color metadata, players guess the wrong color space and range,
causing color shifts and crushed dark values across devices.

Changes:
- Embed bt709 color info in H.264/H.265 VUI via x264-params/x265-params
  (colorprim, transfer, colormatrix) and FFmpeg metadata flags
- Add scale filter (in_range=pc:out_range=tv) to convert full-range
  Chrome screenshots to limited/TV range expected by H.264 decoders
- Chain range conversion with VAAPI's existing hwupload filter
- Add -video_track_timescale 90000 for consistent A/V timing
- Skip color metadata and range conversion for VP9/ProRes
- 8 new regression tests for color space, range filter, VAAPI chain,
  GPU skip, VP9 skip, and timescale

Verified via ffprobe: color_space=bt709, color_transfer=bt709,
color_primaries=bt709, color_range=tv, time_base=1/90000.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Copy link
Copy Markdown
Collaborator Author

jrusso1020 commented Apr 7, 2026

Merge activity

  • Apr 7, 5:40 PM UTC: A user started a stack merge that includes this pull request via Graphite.
  • Apr 7, 5:40 PM UTC: @jrusso1020 merged this pull request with Graphite.

@jrusso1020 jrusso1020 merged commit b491679 into main Apr 7, 2026
20 checks passed
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.

2 participants