diff --git a/mp4parse/src/lib.rs b/mp4parse/src/lib.rs index 955e902f..6a404556 100644 --- a/mp4parse/src/lib.rs +++ b/mp4parse/src/lib.rs @@ -5772,9 +5772,13 @@ fn read_video_sample_entry( } BoxType::ColourInformationBox => { if colour_info.is_some() { + // ISO/IEC 14496-12:2015 § 12.1.5.1 permits one or more colr boxes + // in a VisualSampleEntry and assigns them no normative behaviour; + // a reader may keep the first (most accurate) and ignore the rest. + // Only reject under Strict. warn!("Multiple colr boxes in video sample entry, keeping first"); fail_with_status_if( - strictness != ParseStrictness::Permissive, + strictness == ParseStrictness::Strict, Status::ColrBadQuantityBMFF, )?; skip_box_content(&mut b)?; diff --git a/mp4parse_capi/tests/test_colour_info.rs b/mp4parse_capi/tests/test_colour_info.rs index 3d0312d1..eaed00e7 100644 --- a/mp4parse_capi/tests/test_colour_info.rs +++ b/mp4parse_capi/tests/test_colour_info.rs @@ -135,6 +135,32 @@ fn video_colr_nclx_rgb_identity_matrix() { } } +/// Two adjacent nclx colr boxes in one video sample entry (the backward-compatible +/// HLG signaling convention). ISO/IEC 14496-12:2015 § 12.1.5.1 permits "one or more" +/// ColourInformationBoxes and assigns them no normative behaviour; the reader keeps the +/// first (most accurate) box. The parse must succeed and surface the first box (HLG, +/// transfer=18), not the second (transfer=14). +#[test] +fn video_colr_nclx_two_colr() { + unsafe { + let parser = open_parser("tests/video_colr_nclx_two_colr.mp4"); + + let mut video = Mp4parseTrackVideoInfo::default(); + let rv = mp4parse_get_track_video_info(parser, 0, &mut video); + assert_eq!(rv, Mp4parseStatus::Ok); + + assert_eq!(video.sample_info_count, 1); + let sample = &*video.sample_info; + assert!(sample.has_colour_info); + assert_eq!(sample.colour_primaries, 9); + assert_eq!(sample.transfer_characteristics, 18); // first box (HLG) wins + assert_eq!(sample.matrix_coefficients, 9); + assert!(!sample.full_range_flag); + + mp4parse_free(parser); + } +} + /// No colr box: has_colour_info=false, CICP fields are zero #[test] fn video_no_colr_box() { diff --git a/mp4parse_capi/tests/video_colr_nclx_two_colr.mp4 b/mp4parse_capi/tests/video_colr_nclx_two_colr.mp4 new file mode 100644 index 00000000..23d1f715 Binary files /dev/null and b/mp4parse_capi/tests/video_colr_nclx_two_colr.mp4 differ