diff --git a/Cargo.lock b/Cargo.lock index 591208a..08a087c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -215,7 +215,7 @@ checksum = "fea41bba32d969b513997752735605054bc0dfa92b4c56bf1189f2e174be7a10" [[package]] name = "dolby_vision" -version = "1.6.5" +version = "1.6.6" dependencies = [ "anyhow", "bitvec", @@ -349,9 +349,9 @@ dependencies = [ [[package]] name = "globset" -version = "0.4.8" +version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "10463d9ff00a2a068db14231982f5132edebad0d7660cd956a1c30292dbcbfbd" +checksum = "0a1e17342619edbc21a964c2afbeb6c820c6a2560032872f397bb97ea127bd0a" dependencies = [ "aho-corasick", "bstr", @@ -980,9 +980,9 @@ checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" [[package]] name = "vergen" -version = "7.2.0" +version = "7.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "626fd028e124b3ee607632d92ba99b5a5a086cfd404ede4af6c19ecd9b75a02d" +checksum = "b1f44ef1afcf5979e34748c12595f9589f3dc4e34abf156fb6d95f9b835568dc" dependencies = [ "anyhow", "cfg-if", diff --git a/Cargo.toml b/Cargo.toml index 34f2aa3..9220aac 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -29,7 +29,7 @@ assert_fs = "1.0.7" predicates = "2.1.1" [build-dependencies] -vergen = { version = "7.2.0", default_features = false, features = ["git"] } +vergen = { version = "7.2.1", default_features = false, features = ["git"] } [[bin]] name = "dovi_tool" diff --git a/README.md b/README.md index acc3599..cb9d610 100644 --- a/README.md +++ b/README.md @@ -30,7 +30,7 @@ dovi_tool --help ## All options -- `--help`, `--version`, `--crop`, `--drop-hdr10plus`, `--mode`, `--edit-config` +- `--help`, `--version`, `--crop`, `--drop-hdr10plus`, `--mode`, `--edit-config`, `--start-code` ## All subcommands - Metadata utilities: **`info`**, **`generate`**, **`editor`**, **`export`** - HEVC parsing & handling: **`convert`**, **`demux`**, **`mux`**, **`extract-rpu`**, **`inject-rpu`** diff --git a/assets/editor_examples/convert_to_cmv4.json b/assets/editor_examples/l9_and_l11.json similarity index 67% rename from assets/editor_examples/convert_to_cmv4.json rename to assets/editor_examples/l9_and_l11.json index 2740e06..6d186a1 100644 --- a/assets/editor_examples/convert_to_cmv4.json +++ b/assets/editor_examples/l9_and_l11.json @@ -1,6 +1,5 @@ { - "convert_to_cmv4": true, - "level9": "DCIP3D65", + "level9": "BT.2020", "level11": { "content_type": 1, "whitepoint": 0, diff --git a/assets/editor_examples/remove_cmv4.json b/assets/editor_examples/remove_cmv4.json new file mode 100644 index 0000000..b72a0e8 --- /dev/null +++ b/assets/editor_examples/remove_cmv4.json @@ -0,0 +1,3 @@ +{ + "remove_cmv4": true +} diff --git a/docs/editor.md b/docs/editor.md index 210631e..73490b8 100644 --- a/docs/editor.md +++ b/docs/editor.md @@ -14,12 +14,10 @@ The editor expects a JSON config like the example below: // Mode to convert the RPU (refer to README) "mode": int, - // Adds CM v4.0 metadata with these defaults: - // L9 with DCI-P3 mastering display primaries - // L11 content type set to Cinema, D65 and Reference Mode - // - // Optional, defaults to false. - "convert_to_cmv4": boolean, + // Removes CM v4.0 from the RPU: + // - L3, L8, L9, L10 and L11 are removed + // - DM v2 metadata is removed, along with L254 + "remove_cmv4": boolean, // Whether to remove polynomial/MMR mapping coefficients from the metadata "remove_mapping": boolean, @@ -92,7 +90,7 @@ The editor expects a JSON config like the example below: // Level 9 Mastering Display Primaries // Optional, replaces existing L9. - // Implies converting to CM V4.0. + // The RPU must already be CM v4.0 for this to have any effect // // String value, must match enum. // Default: "DCIP3D65". @@ -100,7 +98,7 @@ The editor expects a JSON config like the example below: // Level 11 Content type metadata // Optional, replaces existing L11 - // Setting this implies converting to CM v4.0 + // The RPU must already be CM v4.0 for this to have any effect "level11": { // 1 = Cinema, 2 = Games, 3 = Sports, 4 = User generated content "content_type": int, diff --git a/dolby_vision/CHANGELOG.md b/dolby_vision/CHANGELOG.md index ad09d92..4c23b54 100644 --- a/dolby_vision/CHANGELOG.md +++ b/dolby_vision/CHANGELOG.md @@ -1,6 +1,12 @@ ## ?? +## 1.6.6 + - Add `ConversionMode` enum to use with `DoviRpu::convert_with_mode`. +- Added support to generate profile 5 RPUs. +- Added long play mode RPU generation. + - This sets `scene_refresh_flag` to `1` for every frame. +- Deprecated `DoviRpu::convert_to_cmv4` as it can lead to playback issues. ## 1.6.5 diff --git a/dolby_vision/Cargo.toml b/dolby_vision/Cargo.toml index aa72865..4c12660 100644 --- a/dolby_vision/Cargo.toml +++ b/dolby_vision/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "dolby_vision" -version = "1.6.5" +version = "1.6.6" authors = ["quietvoid"] edition = "2021" rust-version = "1.56.0" diff --git a/dolby_vision/src/rpu/dovi_rpu.rs b/dolby_vision/src/rpu/dovi_rpu.rs index 055a235..d228b5c 100644 --- a/dolby_vision/src/rpu/dovi_rpu.rs +++ b/dolby_vision/src/rpu/dovi_rpu.rs @@ -492,6 +492,10 @@ impl DoviRpu { .collect() } + #[deprecated( + since = "1.6.6", + note = "Causes issues in playback when L8 metadata is not present. Will be removed" + )] pub fn convert_to_cmv40(&mut self) -> Result<()> { if let Some(ref mut vdr_dm_data) = self.vdr_dm_data { if vdr_dm_data.cmv40_metadata.is_none() { @@ -530,4 +534,16 @@ impl DoviRpu { self.header = Profile84::rpu_data_header(); self.rpu_data_mapping = Some(Profile84::rpu_data_mapping()); } + + pub fn remove_cmv40_extension_metadata(&mut self) -> Result<()> { + if let Some(ref mut vdr_dm_data) = self.vdr_dm_data { + if vdr_dm_data.cmv40_metadata.is_some() { + self.modified = true; + + vdr_dm_data.cmv40_metadata = None; + } + } + + Ok(()) + } } diff --git a/dolby_vision/src/rpu/extension_metadata/primaries.rs b/dolby_vision/src/rpu/extension_metadata/primaries.rs index a693a69..1c6bcde 100644 --- a/dolby_vision/src/rpu/extension_metadata/primaries.rs +++ b/dolby_vision/src/rpu/extension_metadata/primaries.rs @@ -31,14 +31,22 @@ pub struct ColorPrimaries { #[derive(Debug, Clone, Copy)] #[cfg_attr(feature = "serde_feature", derive(Deserialize, Serialize))] pub enum MasteringDisplayPrimaries { + #[cfg_attr(feature = "serde_feature", serde(alias = "DCI-P3 D65"))] DCIP3D65 = 0, + #[cfg_attr(feature = "serde_feature", serde(alias = "BT.709"))] BT709, + #[cfg_attr(feature = "serde_feature", serde(alias = "BT.2020"))] BT2020, + #[cfg_attr(feature = "serde_feature", serde(alias = "SMPTE-C"))] SMPTEC, + #[cfg_attr(feature = "serde_feature", serde(alias = "BT.601"))] BT601, + #[cfg_attr(feature = "serde_feature", serde(alias = "DCI-P3"))] DCIP3, ACES, + #[cfg_attr(feature = "serde_feature", serde(alias = "S-Gamut"))] SGamut, + #[cfg_attr(feature = "serde_feature", serde(alias = "S-Gamut-3.Cine"))] SGamut3Cine, } diff --git a/src/dovi/editor.rs b/src/dovi/editor.rs index ec5e795..e143326 100644 --- a/src/dovi/editor.rs +++ b/src/dovi/editor.rs @@ -31,7 +31,7 @@ pub struct EditConfig { mode: u8, #[serde(default)] - convert_to_cmv4: bool, + remove_cmv4: bool, #[serde(default)] remove_mapping: bool, @@ -155,12 +155,7 @@ impl Editor { impl EditConfig { pub fn from_path(path: &PathBuf) -> Result { let json_file = File::open(path)?; - let mut config: EditConfig = serde_json::from_reader(&json_file)?; - - // Override to CM v4.0 - if !config.convert_to_cmv4 { - config.convert_to_cmv4 = config.level11.is_some() || config.level9.is_some(); - } + let config: EditConfig = serde_json::from_reader(&json_file)?; Ok(config) } @@ -171,8 +166,8 @@ impl EditConfig { self.remove_frames(ranges, rpus)?; } - if self.convert_to_cmv4 { - println!("Converting to CMv4.0..."); + if self.remove_cmv4 { + println!("Removing CMv4.0 metadata..."); } if self.mode > 0 { @@ -209,8 +204,8 @@ impl EditConfig { } pub fn execute_single_rpu(&self, rpu: &mut DoviRpu) -> Result<()> { - if self.convert_to_cmv4 { - rpu.convert_to_cmv40()?; + if self.remove_cmv4 { + rpu.remove_cmv40_extension_metadata()?; } if self.mode > 0 { @@ -349,8 +344,6 @@ impl EditConfig { ) -> Result<()> { let primary_index = *primaries as u8; - rpu.modified = true; - let level9 = ExtMetadataBlockLevel9 { length: 1, source_primary_index: primary_index, @@ -358,6 +351,8 @@ impl EditConfig { }; if let Some(ref mut vdr_dm_data) = rpu.vdr_dm_data { + rpu.modified = true; + vdr_dm_data.replace_metadata_block(ExtMetadataBlock::Level9(level9))?; } @@ -369,9 +364,8 @@ impl EditConfig { rpu: &mut DoviRpu, level11: &ExtMetadataBlockLevel11, ) -> Result<()> { - rpu.modified = true; - if let Some(ref mut vdr_dm_data) = rpu.vdr_dm_data { + rpu.modified = true; vdr_dm_data.replace_metadata_block(ExtMetadataBlock::Level11(level11.clone()))?; } diff --git a/tests/rpu/editor.rs b/tests/rpu/editor.rs index 6ad9f00..81870ea 100644 --- a/tests/rpu/editor.rs +++ b/tests/rpu/editor.rs @@ -5,7 +5,7 @@ use assert_cmd::Command; use assert_fs::prelude::*; use predicates::prelude::*; -use dolby_vision::rpu::extension_metadata::blocks::ExtMetadataBlock; +use dolby_vision::rpu::extension_metadata::{blocks::ExtMetadataBlock, MasteringDisplayPrimaries}; const SUBCOMMAND: &str = "editor"; @@ -56,12 +56,12 @@ fn mode() -> Result<()> { } #[test] -fn convert_to_cmv4() -> Result<()> { +fn remove_cmv4() -> Result<()> { let mut cmd = Command::cargo_bin(env!("CARGO_PKG_NAME"))?; let temp = assert_fs::TempDir::new().unwrap(); - let input_rpu = Path::new("assets/tests/fel_orig.bin"); - let edit_config = Path::new("assets/editor_examples/convert_to_cmv4.json"); + let input_rpu = Path::new("assets/tests/mel_variable_l8_length13.bin"); + let edit_config = Path::new("assets/editor_examples/remove_cmv4.json"); let output_rpu = temp.child("RPU.bin"); @@ -78,32 +78,25 @@ fn convert_to_cmv4() -> Result<()> { output_rpu.assert(predicate::path::is_file()); - let rpus = utilities_dovi::parse_rpu_file(output_rpu.as_ref())?.unwrap(); + // Original + let rpus = utilities_dovi::parse_rpu_file(input_rpu)?.unwrap(); assert_eq!(rpus.len(), 1); let rpu = &rpus[0]; assert_eq!(rpu.dovi_profile, 7); let vdr_dm_data = rpu.vdr_dm_data.as_ref().unwrap(); + assert!(vdr_dm_data.cmv40_metadata.is_some()); - // Only L9, L11 and L254 - assert_eq!(vdr_dm_data.metadata_blocks(3).unwrap().len(), 3); - - if let ExtMetadataBlock::Level9(level9) = vdr_dm_data.get_block(9).unwrap() { - assert_eq!(level9.length, 1); - assert_eq!(level9.source_primary_index, 0); - } + // Removed RPU + let rpus = utilities_dovi::parse_rpu_file(output_rpu.as_ref())?.unwrap(); + assert_eq!(rpus.len(), 1); - if let ExtMetadataBlock::Level11(level11) = vdr_dm_data.get_block(11).unwrap() { - assert_eq!(level11.content_type, 1); - assert_eq!(level11.whitepoint, 0); - assert!(level11.reference_mode_flag); - } + let rpu = &rpus[0]; + assert_eq!(rpu.dovi_profile, 7); - if let ExtMetadataBlock::Level254(level254) = vdr_dm_data.get_block(11).unwrap() { - assert_eq!(level254.dm_mode, 0); - assert_eq!(level254.dm_version_index, 2); - } + let vdr_dm_data = rpu.vdr_dm_data.as_ref().unwrap(); + assert!(vdr_dm_data.cmv40_metadata.is_none()); Ok(()) } @@ -160,3 +153,120 @@ fn active_area_specific() -> Result<()> { Ok(()) } + +#[test] +fn add_l9_l11_no_effect() -> Result<()> { + let mut cmd = Command::cargo_bin(env!("CARGO_PKG_NAME"))?; + let temp = assert_fs::TempDir::new().unwrap(); + + let input_rpu = Path::new("assets/tests/fel_orig.bin"); + let edit_config = Path::new("assets/editor_examples/l9_and_l11.json"); + + let output_rpu = temp.child("RPU.bin"); + + let assert = cmd + .arg(SUBCOMMAND) + .arg(input_rpu) + .arg("--json") + .arg(edit_config) + .arg("--rpu-out") + .arg(output_rpu.as_ref()) + .assert(); + + assert.success().stderr(predicate::str::is_empty()); + + output_rpu.assert(predicate::path::is_file()); + + // Original + let rpus = utilities_dovi::parse_rpu_file(input_rpu)?.unwrap(); + assert_eq!(rpus.len(), 1); + + let rpu = &rpus[0]; + assert_eq!(rpu.dovi_profile, 7); + + let vdr_dm_data = rpu.vdr_dm_data.as_ref().unwrap(); + assert!(vdr_dm_data.cmv40_metadata.is_none()); + + // No change + let rpus = utilities_dovi::parse_rpu_file(output_rpu.as_ref())?.unwrap(); + assert_eq!(rpus.len(), 1); + + let rpu = &rpus[0]; + assert_eq!(rpu.dovi_profile, 7); + + let vdr_dm_data = rpu.vdr_dm_data.as_ref().unwrap(); + assert!(vdr_dm_data.cmv40_metadata.is_none()); + + Ok(()) +} + +#[test] +fn add_l9_l11() -> Result<()> { + let mut cmd = Command::cargo_bin(env!("CARGO_PKG_NAME"))?; + let temp = assert_fs::TempDir::new().unwrap(); + + let input_rpu = Path::new("assets/tests/mel_variable_l8_length13.bin"); + let edit_config = Path::new("assets/editor_examples/l9_and_l11.json"); + + let output_rpu = temp.child("RPU.bin"); + + let assert = cmd + .arg(SUBCOMMAND) + .arg(input_rpu) + .arg("--json") + .arg(edit_config) + .arg("--rpu-out") + .arg(output_rpu.as_ref()) + .assert(); + + assert.success().stderr(predicate::str::is_empty()); + + output_rpu.assert(predicate::path::is_file()); + + // Original + let rpus = utilities_dovi::parse_rpu_file(input_rpu)?.unwrap(); + assert_eq!(rpus.len(), 1); + + let rpu = &rpus[0]; + assert_eq!(rpu.dovi_profile, 7); + + let vdr_dm_data = rpu.vdr_dm_data.as_ref().unwrap(); + assert!(vdr_dm_data.cmv40_metadata.is_some()); + + let orig_l9 = vdr_dm_data.get_block(9).unwrap(); + let orig_l11 = vdr_dm_data.get_block(11); + + if let ExtMetadataBlock::Level9(block) = orig_l9 { + assert_eq!( + block.source_primary_index, + MasteringDisplayPrimaries::DCIP3D65 as u8 + ) + } + assert!(orig_l11.is_none()); + + // Modifies the blocks + let rpus = utilities_dovi::parse_rpu_file(output_rpu.as_ref())?.unwrap(); + assert_eq!(rpus.len(), 1); + + let rpu = &rpus[0]; + assert_eq!(rpu.dovi_profile, 7); + + let vdr_dm_data = rpu.vdr_dm_data.as_ref().unwrap(); + assert!(vdr_dm_data.cmv40_metadata.is_some()); + + let orig_l9 = vdr_dm_data.get_block(9).unwrap(); + let orig_l11 = vdr_dm_data.get_block(11).unwrap(); + + if let ExtMetadataBlock::Level9(block) = orig_l9 { + assert_eq!( + block.source_primary_index, + MasteringDisplayPrimaries::BT2020 as u8 + ); + } + if let ExtMetadataBlock::Level11(block) = orig_l11 { + assert_eq!(block.content_type, 1); + assert!(block.reference_mode_flag); + } + + Ok(()) +}