From 49a06c1588d85531f9835a0ea04f76467b7e3d01 Mon Sep 17 00:00:00 2001 From: Rainbaby Date: Wed, 2 Nov 2022 13:38:54 +0800 Subject: [PATCH] Fix CM v2.9 Level 2 parsing & support more custom display cases --- README.md | 4 +- src/metadata/display/characteristics.rs | 64 +++++++++++++++++++++---- src/metadata/display/mod.rs | 6 +-- src/metadata/levels/level2.rs | 16 ++++--- 4 files changed, 69 insertions(+), 21 deletions(-) diff --git a/README.md b/README.md index b5df80f..a9d4ffc 100644 --- a/README.md +++ b/README.md @@ -62,9 +62,9 @@ Currently, the only available subcommand is **`convert`** ```console dovi_meta convert RPU.bin metadata.xml --skip 24 --rate 30000/1001 --size 1920x1080 ``` - The default color space of mastering display is **BT.2020**, the default EOTF is **PQ**. + The default color encoding is **BT.2020 PQ 16-bit RGB Full Range**. - The default color space of target display (except the anchor target) is **P3 D65** for CM v2.9 XML, also for CM v4.0 XML when it can't be determined by input. + The default color space of mastering display and target displays (except the anchor target) is **P3 D65** for CM v2.9 XML, also for CM v4.0 XML when it can't be determined by input. ## **Notes** diff --git a/src/metadata/display/characteristics.rs b/src/metadata/display/characteristics.rs index 8176620..cb29c2c 100644 --- a/src/metadata/display/characteristics.rs +++ b/src/metadata/display/characteristics.rs @@ -109,10 +109,10 @@ impl Characteristics { } } - fn get_primary_target(block: &ExtMetadataBlockLevel2, primary: Primaries) -> Option { + fn get_primary_target(block: &ExtMetadataBlockLevel2, primary: Primaries) -> Self { let max_luminance = Self::max_u16_from_rpu_pq_u12(block.target_max_pq); - let primary = if let Some(primary) = primary.get_index() { + let primary_index = if let Some(primary) = primary.get_index() { if max_luminance == 100 { 1 } else { @@ -122,7 +122,24 @@ impl Characteristics { 0 }; - Self::get_display(PREDEFINED_TARGET_DISPLAYS, max_luminance, primary) + if let Some(target) = + Self::get_display(PREDEFINED_TARGET_DISPLAYS, max_luminance, primary_index) + { + target + } else { + let mut target = Self { + id: block.target_max_pq as usize, + primary_index, + primaries: primary, + peak_brightness: max_luminance, + minimum_brightness: 0.0, + eotf: Eotf::Pq, + ..Default::default() + }; + + target.update_name(); + target + } } fn get_target(block: &ExtMetadataBlockLevel8) -> Option { @@ -156,9 +173,7 @@ impl Characteristics { vdr.level_blocks_iter(2).for_each(|b| { if let ExtMetadataBlock::Level2(b) = b { - if let Some(d) = Self::get_primary_target(b, primary) { - targets.push(d) - } + targets.push(Self::get_primary_target(b, primary)) } }); @@ -181,7 +196,8 @@ impl Characteristics { } pub fn get_source_or_default(vdr: &VdrDmData) -> Self { - let primary = Primaries::from(vdr).get_index().unwrap_or(0); + let primary = Primaries::from(vdr); + let primary_index = primary.get_index().unwrap_or(0); // Prefer level 6 metadata let max_luminance = match vdr.get_block(6) { @@ -189,8 +205,38 @@ impl Characteristics { _ => Characteristics::max_u16_from_rpu_pq_u12(vdr.source_max_pq), }; - Self::get_display(PREDEFINED_MASTERING_DISPLAYS, max_luminance, primary) - .unwrap_or_else(Self::default_source) + if let Some(source) = + Self::get_display(PREDEFINED_MASTERING_DISPLAYS, max_luminance, primary_index) + { + source + } else { + let mut source = Self::default_source(); + + if vdr.get_block(254).is_some() { + // Custom mastering display for CM v4.0 + // For convenience, use source_max_pq as custom mastering display id + source.id = vdr.source_max_pq as usize; + source.primaries = primary; + + source.primary_index = if primary.get_index().is_none() { + // Random invalid value + 255 + } else { + primary_index + }; + + // BT.709 BT.1886 + if primary_index == 1 { + source.eotf = Eotf::GammaBT1886; + source.peak_brightness = 100; + // Default source (4000-nit) min_brightness is 0.005-nit + } + + source.update_name(); + } + + source + } } /*pub fn update_luminance_range_with_l6_block(&mut self, block: &ExtMetadataBlockLevel6) { diff --git a/src/metadata/display/mod.rs b/src/metadata/display/mod.rs index 1177aa0..eb3e39b 100644 --- a/src/metadata/display/mod.rs +++ b/src/metadata/display/mod.rs @@ -58,7 +58,7 @@ pub const PREDEFINED_TARGET_DISPLAYS: &[[usize; 6]] = &[ [ 38, 2, 2000, 0, 0, 0], [ 48, 0, 1000, 0, 0, 0], [ 49, 2, 1000, 0, 0, 0], - [9003, 1, 600, 7, 2, 0], // BETA + // [9003, 1, 600, 7, 2, 0], // BETA ]; // pub const CMV29_TARGET_DISPLAYS_LIST: &[u8] = &[1, 27, 28, 37, 38, 48, 49]; @@ -78,8 +78,8 @@ pub fn pq2l(pq: f32) -> f32 { y * ST2084_Y_MAX } -pub fn find_target_id(max: usize, primary: usize) -> usize { - get_display_id(PREDEFINED_TARGET_DISPLAYS, max, primary).unwrap_or(0) +pub fn find_target_id(max: usize, primary: usize) -> Option { + get_display_id(PREDEFINED_TARGET_DISPLAYS, max, primary) } fn get_display_id(list: &[[usize; 6]], max_luminance: usize, primary: usize) -> Option { diff --git a/src/metadata/levels/level2.rs b/src/metadata/levels/level2.rs index 6f6b148..c26c126 100644 --- a/src/metadata/levels/level2.rs +++ b/src/metadata/levels/level2.rs @@ -13,10 +13,8 @@ use super::TrimSixField; #[derive(Debug, Clone, PartialEq, Default)] pub struct Level2 { pub level: u8, - // #[serde(rename = "$unflatten=TID")] - pub tid: u8, + pub tid: usize, // Format: 0 0 0 f32 f32 f32 f32 f32 f32 - // #[serde(rename = "$unflatten=Trim")] pub trim: MDFType, } @@ -55,7 +53,7 @@ impl IntoCMV29 for Level2 { impl Level2 { pub fn with_primary_index(block: &ExtMetadataBlockLevel2, primary: Option) -> Self { - // Actually the only possible value is -1 + // identical definition for all negative values, use -1 for v2.0.5+ let ms_weight = if block.ms_weight < 0 { -1.0 } else { @@ -66,9 +64,13 @@ impl Level2 { let primary = if luminance == 100 { 1 } else { - primary.unwrap_or(2) + // P3 D65 + primary.unwrap_or(0) }; + // For convenience, use target_max_pq as Level2 custom target display id + let tid = find_target_id(luminance, primary).unwrap_or(block.target_max_pq as usize); + let mut trim = TrimSixField([ f32_from_rpu_u12_with_bias(block.trim_slope), f32_from_rpu_u12_with_bias(block.trim_offset), @@ -82,7 +84,7 @@ impl Level2 { Self { level: 2, - tid: find_target_id(luminance, primary) as u8, + tid, trim: CMV40(trim), } } @@ -90,7 +92,7 @@ impl Level2 { impl From<&ExtMetadataBlockLevel2> for Level2 { fn from(block: &ExtMetadataBlockLevel2) -> Self { - // BT.2020 + // P3 D65 Self::with_primary_index(block, None) } }