Skip to content

Clear reserved colour tags before decodes that convert the pixel format - #161

Draft
StuartCameronCode wants to merge 3 commits into
mainfrom
agentbob/5-sources-tagged-with-reserved-colour-prim
Draft

StuartCameronCode wants to merge 3 commits into
mainfrom
agentbob/5-sources-tagged-with-reserved-colour-prim

Conversation

@StuartCameronCode

Copy link
Copy Markdown
Owner

Problem

ProRes files tagged color_primaries=reserved / color_transfer=reserved (ProRes writes code 0 for "unknown", which FFmpeg reads as ISO 23091's reserved) break every decode that converts the pixel format. FFmpeg 9's scaler refuses the frame:

[swscaler] Unsupported input (Operation not supported): fmt:yuv422p10le csp:unknown prim:reserved trc:reserved -> …

Reported on the videohelp forum (thread 419911, posts #123/#140/#150) with a ProRes 422 capture. Symptoms:

  • the Original side of the preview showed "No preview"
  • timeline thumbnails were missing
  • Detect borders failed with "No frames could be decoded"

Descale Detect and a ProRes 4444-with-alpha encode, where the decoder converts to the pipe format, failed the same way.

Fix

Every decode of the source that may convert the pixel format clears only the invalid tags, each one separately, with a setparams placed first in its single -vf. Clearing a valid matrix would change pixels: yuv → gray16le moves by up to 28 levels.

  • Worker: color_metadata::decode_tag_neutraliser / VideoJob::decode_tag_neutraliser. It reaches the encode, preview and timeline decoders through source_decode::decoder_filter, and every detector through it too (crop_detect::decode_samples, so noise/halo as well, and native_res). When the tags are usable, the args are byte-identical to before.
  • App: new ColorTags, a byte-identical twin of the worker's lists and neutraliser. A test parses the Rust source to keep the two in step. PreviewGenerator.beforeFrameArgs / thumbnailArgs apply it ahead of the HDR tone-map and scale.
  • colorTag keeps forwarding ffprobe's raw reserved, as it always did, because the worker needs it to know the decoder must clear it. ColorMetadata::from_raw still keeps it off the encoder's flags.

Docs

  • Report: docs/reports/2026-11-reserved-colour-tags/, with the probe scripts that produced its numbers.
  • An ENGINEERING_NOTES entry and a reports-index row.
  • A rule under CLAUDE.md's "Colour metadata" section.

Tests

  • Fixtures: reserved_colour_tags.mov (ProRes 422, laid out like the reported clip) and reserved_colour_tags_4444a.mov, 2.3 MB together. generate_reserved_colour_tags_test.sh regenerates them.
  • Unit: the Rust and Dart case tables match; no accepted value is ever cleared; the decoder args keep one -vf with the fix first; and the before-frame and thumbnail args compose with the tone-map.
  • Heavy: integration_reserved_colour_tags_test.dart passes 8/8 on macOS arm64 with deps 1.16.0. Before frame, thumbnail, --detect-crop, --detect-native, --detect-noise-halo, preview (422 and 4444a) and a 4444a encode each match a validly-tagged twin exactly.
  • Rust suite (cargo test): passes. subtitle_integration_test fails only on a machine without the whisper add-on.
  • Real clip: the before frame fails with exit 211 without the fix and succeeds with it; the thumbnail succeeds with it. Detect borders, Descale Detect, noise/halo, preview and a trimmed encode all succeed.

Merge note

#157 (seek alignment) also changes source_decode::decoder_filter, adding a seeks parameter. Whichever of the two merges second needs a small manual merge: the chain is setparams → setpts → size guard, and the call sites need updating too.

Follow-up, not in this PR: Descale Detect's luma depends on the source's matrix tag (tracked separately).

🤖 Generated with Claude Code

StuartCameronCode and others added 3 commits November 10, 2026 23:24
…format

ProRes that writes colour as "unknown" the Apple way (code 0) is read by
FFmpeg as `reserved`, and FFmpeg 9's scaler refuses to convert such a frame.
The app's before frame and thumbnails, --detect-crop and --detect-native all
failed on it, and so did the encode/preview of a ProRes 4444 with alpha,
whose decoder converts to the pipe format.

Every decode now puts a setparams that clears only the invalid tags first in
its one -vf: ColorMetadata's decode_tag_neutraliser in the worker (all three
source_decode builders, hence every detector), ColorTags.decodeNeutraliser
in the app (PreviewGenerator.beforeFrameArgs / thumbnailArgs). A valid tag
is never touched: clearing a valid matrix changes yuv->gray pixels.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
docs/reports/2026-11-reserved-colour-tags with its probe scripts (the
per-call-site table on the reported clip, the per-tag and valid-matrix
measurements, the 4444-alpha encode), an ENGINEERING_NOTES entry, a
reports-index row, and the rule under CLAUDE.md's "Colour metadata".

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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