mirror of
https://github.com/storytold/filmcraft.git
synced 2026-11-09 01:23:40 +00:00
fix(export): tag SDR exports with a colr box (#2142)
ColorSignal::apply_to wrote the sample entry's colr box only for HDR, so SDR ProRes, DNxHR and APV MOVs carried no colour that Apple's media stack reads (it takes their colour only from colr, not from the codecs' frame headers): QuickTime Player and Final Cut saw no primaries, transfer or matrix. SDR exports now get colr with the export's colour too (nclc in MOV, nclx in MP4); HDR metadata stays PQ-only. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
8309f21a6c
commit
678fe73221
@@ -199,3 +199,17 @@ fn the_software_h264_sei_is_the_static_metadata_laid_out_as_messages() {
|
||||
want.push(0x80);
|
||||
assert_eq!(sei, want);
|
||||
}
|
||||
|
||||
/// #2142: SDR gets its `colr` box as well (nclc in MOV, nclx in MP4), and no HDR metadata.
|
||||
#[test]
|
||||
fn sdr_gets_a_colr_box_and_no_hdr_metadata() {
|
||||
let mut e = filmcraft_isobmff::SampleEntry::prores(filmcraft_isobmff::FourCc(*b"apch"), 64, 64);
|
||||
ColorSignal::default().apply_to(&mut e, true);
|
||||
let v = e.video.as_ref().unwrap();
|
||||
assert_eq!(v.color, Some(filmcraft_isobmff::ColorInfo::Nclc { primaries: 1, transfer: 1, matrix: 1 }));
|
||||
assert!(v.mastering_display.is_none() && v.content_light.is_none());
|
||||
let mut e = filmcraft_isobmff::SampleEntry::hevc(Default::default(), 64, 64);
|
||||
ColorSignal::default().apply_to(&mut e, false);
|
||||
let v = e.video.as_ref().unwrap();
|
||||
assert_eq!(v.color, Some(filmcraft_isobmff::ColorInfo::Nclx { primaries: 1, transfer: 1, matrix: 1, full_range: false }));
|
||||
}
|
||||
|
||||
@@ -520,11 +520,10 @@ impl ColorSignal {
|
||||
if self.matrix == 9 { (0.2627, 0.0593) } else { (0.2126, 0.0722) }
|
||||
}
|
||||
/// Sample-entry boxes: `colr` (nclx for MP4, nclc for MOV) plus `mdcv`/`clli` for PQ
|
||||
/// (mastering display BT.2020/D65, 1000/0.0001 cd/m²; MaxCLL/MaxFALL 0 = unknown).
|
||||
/// (mastering display BT.2020/D65, 1000/0.0001 cd/m²; MaxCLL/MaxFALL 0 = unknown). SDR gets
|
||||
/// its `colr` too: Apple's media stack (QuickTime Player, Final Cut Pro) reads the colour of
|
||||
/// ProRes, DNxHR and APV only from this box, not from their frame headers (#2142).
|
||||
pub fn apply_to(&self, e: &mut SampleEntry, mov: bool) {
|
||||
if !self.is_hdr() {
|
||||
return;
|
||||
}
|
||||
if let Some(v) = e.video.as_mut() {
|
||||
let (p, t, m) = (self.primaries as u16, self.transfer as u16, self.matrix as u16);
|
||||
v.color = Some(if mov {
|
||||
|
||||
@@ -126,6 +126,31 @@ fn prores_export_roundtrip() {
|
||||
assert!(f[0] > 240 && f[1] < 15 && f[2] < 15, "{:?}", &f[..4]);
|
||||
}
|
||||
|
||||
/// #2142: SDR ProRes, DNxHR and APV (MOV) and H.264 (MP4) exports carry a `colr` box with the
|
||||
/// export's BT.709 colour: Apple's media stack reads the colour of the MOV codecs only from there.
|
||||
#[test]
|
||||
fn sdr_exports_carry_a_colr_box() {
|
||||
use filmcraft_isobmff::ColorInfo;
|
||||
let (p, seq, m) = project();
|
||||
let nclc = ColorInfo::Nclc { primaries: 1, transfer: 1, matrix: 1 };
|
||||
let nclx = ColorInfo::Nclx { primaries: 1, transfer: 1, matrix: 1, full_range: false };
|
||||
for (format, name, want) in [
|
||||
(Format::ProRes, "colr-prores.mov", nclc.clone()),
|
||||
(Format::DnxHr, "colr-dnxhr.mov", nclc.clone()),
|
||||
(Format::Apv, "colr-apv.mov", nclc.clone()),
|
||||
(Format::H264, "colr-h264.mp4", nclx),
|
||||
] {
|
||||
let path = tmp(name);
|
||||
export(&p, seq, &ExportSettings { format, path: path.clone(), include_audio: false, ..Default::default() }, &m, &Progress::default()).unwrap();
|
||||
let bytes = std::fs::read(&path).unwrap();
|
||||
let file = filmcraft_isobmff::open(bytes).unwrap();
|
||||
let t = file.track_of_kind(filmcraft_isobmff::TrackKind::Video).unwrap();
|
||||
let v = file.tracks[t].entries[0].video.as_ref().unwrap();
|
||||
assert_eq!(v.color.as_ref(), Some(&want), "{name}");
|
||||
assert!(v.mastering_display.is_none() && v.content_light.is_none(), "{name}: no HDR metadata on SDR");
|
||||
}
|
||||
}
|
||||
|
||||
/// ProRes 4444 / 4444 XQ export (#342): 4:4:4 frames under the `ap4h` / `ap4x` codes, and with
|
||||
/// Include Alpha Channel the picture's alpha survives instead of being flattened over black.
|
||||
#[test]
|
||||
|
||||
Reference in New Issue
Block a user