diff --git a/Cargo.lock b/Cargo.lock index f2b975e..c044387 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -74,6 +74,12 @@ version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" +[[package]] +name = "bitstream-io" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d28070975aaf4ef1fd0bd1f29b739c06c2cdd9972e090617fb6dca3b2cb564e" + [[package]] name = "bitvec" version = "1.0.1" @@ -88,13 +94,11 @@ dependencies = [ [[package]] name = "bitvec_helpers" -version = "2.0.1" +version = "3.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f9d96095002ea2928bc8f8b361db4e41fbed1c86601918efc55a2d0840bc9bf4" +checksum = "3ef6883bd86b4112b56be19de3a1628de6c4063be7be6e641d484c83069efb4a" dependencies = [ - "anyhow", - "bitvec", - "funty", + "bitstream-io", ] [[package]] @@ -183,13 +187,13 @@ dependencies = [ [[package]] name = "clap" -version = "4.0.32" +version = "4.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7db700bc935f9e43e88d00b0850dae18a63773cfbec6d8e070fccf7fef89a39" +checksum = "4ec7a4128863c188deefe750ac1d1dfe66c236909f845af04beed823638dc1b2" dependencies = [ "bitflags", "clap_derive", - "clap_lex 0.3.0", + "clap_lex 0.3.1", "is-terminal", "once_cell", "strsim", @@ -199,9 +203,9 @@ dependencies = [ [[package]] name = "clap_derive" -version = "4.0.21" +version = "4.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0177313f9f02afc995627906bbd8967e2be069f5261954222dac78290c2b9014" +checksum = "684a277d672e91966334af371f1a7b5833f9aa00b07c84e92fbce95e00208ce8" dependencies = [ "heck", "proc-macro-error", @@ -221,18 +225,18 @@ dependencies = [ [[package]] name = "clap_lex" -version = "0.3.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d4198f73e42b4936b35b5bb248d81d2b595ecb170da0bac7655c54eedfa8da8" +checksum = "783fe232adfca04f90f56201b26d79682d4cd2625e0bc7290b95123afe558ade" dependencies = [ "os_str_bytes", ] [[package]] name = "console" -version = "0.15.4" +version = "0.15.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c9b6515d269224923b26b5febea2ed42b2d5f2ce37284a4dd670fedd6cb8347a" +checksum = "c3d79fbe8970a77e3e34151cc13d3b3e248aa0faaecb9f6091fa07ebefe5ad60" dependencies = [ "encode_unicode", "lazy_static", @@ -349,7 +353,7 @@ checksum = "fea41bba32d969b513997752735605054bc0dfa92b4c56bf1189f2e174be7a10" [[package]] name = "dolby_vision" -version = "2.1.0" +version = "3.0.0" dependencies = [ "anyhow", "bitvec", @@ -371,7 +375,7 @@ dependencies = [ "assert_fs", "bitvec", "bitvec_helpers", - "clap 4.0.32", + "clap 4.1.1", "dolby_vision", "hevc_parser", "indicatif", @@ -563,9 +567,9 @@ dependencies = [ [[package]] name = "hevc_parser" -version = "0.5.2" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab34f209a181c88c436af84050c1ca2c8d16c9909b5201c939ace9551d1d8d15" +checksum = "ce7a8f2b967679b2d12bf851b0c68f3cbc7a4dcf3b5ca063fd9b3efe37f8a5a4" dependencies = [ "anyhow", "bitvec_helpers", @@ -585,9 +589,9 @@ dependencies = [ [[package]] name = "ignore" -version = "0.4.19" +version = "0.4.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a05705bc64e0b66a806c3740bd6578ea66051b157ec42dc219c785cbf185aef3" +checksum = "dbe7873dab538a9a44ad79ede1faf5f30d49f9a5c883ddbab48bce81b64b7492" dependencies = [ "globset", "lazy_static", @@ -612,9 +616,9 @@ dependencies = [ [[package]] name = "indicatif" -version = "0.17.2" +version = "0.17.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4295cbb7573c16d310e99e713cf9e75101eb190ab31fccd35f2d2691b4352b19" +checksum = "cef509aa9bc73864d6756f0d34d35504af3cf0844373afe9b8669a5b8005a729" dependencies = [ "console", "number_prefix", @@ -633,9 +637,9 @@ dependencies = [ [[package]] name = "io-lifetimes" -version = "1.0.3" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46112a93252b123d31a119a8d1a1ac19deac4fac6e0e8b0df58f0d4e5870e63c" +checksum = "e7d6c6f8c91b4b9ed43484ad1a938e393caf35960fce7f82a040497207bd8e9e" dependencies = [ "libc", "windows-sys", @@ -770,9 +774,9 @@ checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" [[package]] name = "nom" -version = "7.1.2" +version = "7.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5507769c4919c998e69e49c839d9dc6e693ede4cc4290d6ad8b41d4f09c548c" +checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" dependencies = [ "memchr", "minimal-lexical", @@ -929,9 +933,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.49" +version = "1.0.50" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57a8eca9f9c4ffde41714334dee777596264c7825420f521abc92b5b5deb63a5" +checksum = "6ef7d57beacfaf2d8aee5937dab7b7f28de3cb8b1828479bb5de2a7106f2bae2" dependencies = [ "unicode-ident", ] @@ -1135,9 +1139,9 @@ dependencies = [ [[package]] name = "termcolor" -version = "1.1.3" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bab24d30b911b2376f3a13cc2cd443142f0c81dda04c118693e35b3835757755" +checksum = "be55cf8942feac5c765c2c993422806843c9a9a45d4d5c407ad6dd2ea95eb9b6" dependencies = [ "winapi-util", ] @@ -1443,45 +1447,45 @@ dependencies = [ [[package]] name = "windows_aarch64_gnullvm" -version = "0.42.0" +version = "0.42.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41d2aa71f6f0cbe00ae5167d90ef3cfe66527d6f613ca78ac8024c3ccab9a19e" +checksum = "8c9864e83243fdec7fc9c5444389dcbbfd258f745e7853198f365e3c4968a608" [[package]] name = "windows_aarch64_msvc" -version = "0.42.0" +version = "0.42.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd0f252f5a35cac83d6311b2e795981f5ee6e67eb1f9a7f64eb4500fbc4dcdb4" +checksum = "4c8b1b673ffc16c47a9ff48570a9d85e25d265735c503681332589af6253c6c7" [[package]] name = "windows_i686_gnu" -version = "0.42.0" +version = "0.42.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fbeae19f6716841636c28d695375df17562ca208b2b7d0dc47635a50ae6c5de7" +checksum = "de3887528ad530ba7bdbb1faa8275ec7a1155a45ffa57c37993960277145d640" [[package]] name = "windows_i686_msvc" -version = "0.42.0" +version = "0.42.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "84c12f65daa39dd2babe6e442988fc329d6243fdce47d7d2d155b8d874862246" +checksum = "bf4d1122317eddd6ff351aa852118a2418ad4214e6613a50e0191f7004372605" [[package]] name = "windows_x86_64_gnu" -version = "0.42.0" +version = "0.42.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf7b1b21b5362cbc318f686150e5bcea75ecedc74dd157d874d754a2ca44b0ed" +checksum = "c1040f221285e17ebccbc2591ffdc2d44ee1f9186324dd3e84e99ac68d699c45" [[package]] name = "windows_x86_64_gnullvm" -version = "0.42.0" +version = "0.42.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09d525d2ba30eeb3297665bd434a54297e4170c7f1a44cad4ef58095b4cd2028" +checksum = "628bfdf232daa22b0d64fdb62b09fcc36bb01f05a3939e20ab73aaf9470d0463" [[package]] name = "windows_x86_64_msvc" -version = "0.42.0" +version = "0.42.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f40009d85759725a34da6d89a94e63d7bdc50a862acf0dbc7c8e488f1edcb6f5" +checksum = "447660ad36a13288b1db4d4248e857b510e8c3a225c822ba4fb748c0aafecffd" [[package]] name = "wyz" diff --git a/Cargo.toml b/Cargo.toml index 6f4a19c..44eef98 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,19 +3,19 @@ name = "dovi_tool" version = "1.6.0" authors = ["quietvoid"] edition = "2021" -rust-version = "1.63.0" +rust-version = "1.64.0" license = "MIT" build = "build.rs" [dependencies] -bitvec_helpers = "2.0.1" -hevc_parser = { version = "0.5.2", features = ["hevc_io"] } +bitvec_helpers = { version = "3.1.2", default-features = false, features = ["bitstream-io"] } +hevc_parser = { version = "0.6.0", features = ["hevc_io"] } dolby_vision = { path = "dolby_vision", "features" = ["xml", "serde"] } madvr_parse = "1.0.1" anyhow = "1.0.68" -clap = { version = "4.0.32", features = ["derive", "wrap_help", "deprecated"] } -indicatif = "0.17.2" +clap = { version = "4.1.1", features = ["derive", "wrap_help", "deprecated"] } +indicatif = "0.17.3" regex = "1.7.1" bitvec = "1.0.1" serde = { version = "1.0.152", features = ["derive"] } diff --git a/README.md b/README.md index 67f8832..c9b49f5 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,7 @@ The **`dolby_vision`** crate is also hosted in this repo, see [README](dolby_vis ## **Building** ### **Toolchain** -The minimum Rust version to build **`dovi_tool`** is 1.63.0. +The minimum Rust version to build **`dovi_tool`** is 1.64.0. ### **Release binary** To build release binary in `target/release/dovi_tool` run: diff --git a/docs/profiles.md b/docs/profiles.md index 3688d18..37ecf00 100644 --- a/docs/profiles.md +++ b/docs/profiles.md @@ -1,6 +1,6 @@ Differentiating between Dolby Vision profiles. ##### Profile 4 -Possibly `vdr_bit_depth_minus_8` > 4 +Possibly `vdr_bit_depth_minus8` > 4 ##### Profile 5 `vdr_rpu_profile = 0` `bl_video_full_range_flag = 0` @@ -10,4 +10,4 @@ Possibly `vdr_bit_depth_minus_8` > 4 `disable_residual_flag = 0` ##### Profile 8 `vdr_rpu_profile = 1` -`el_spatial_resampling_filter_flag = 0` \ No newline at end of file +`el_spatial_resampling_filter_flag = 0` diff --git a/dolby_vision/CHANGELOG.md b/dolby_vision/CHANGELOG.md index 7aefb69..7875071 100644 --- a/dolby_vision/CHANGELOG.md +++ b/dolby_vision/CHANGELOG.md @@ -1,3 +1,26 @@ +## 3.0.0 +- Breaking changes from `RpuDataMapping` refactor. +- Renamed `serde_feature` to simply `serde`. + +- Moved some fields from `RpuDataHeader` into `RpuDataMapping. +- `RpuDataNlq` is now part of `RpuDataMapping`. + +- The mapping now has one curve per component, which is a `DoviReshapingCurve`. +- `DoviReshapingCurve` contains the pivots, mapping method and the respective curve params. + - Component 0 describes the polynomial params in `DoviPolynomialCurve`. + - Components 1 and 2 describe can be either polynomial or the MMR params in `DoviMMRCurve`. + - Polynomial interpolation fields were removed as there are no existing samples. + +- `RpuDataNlq` was changed to contain only one set of params, as there is no significant pivot. +- All `_minus_X` suffixed names were uniformized as `minusX`. + +The changes also affect the C API. + +C API: + - Added `dovi_rpu_set_active_area_offsets` function to edit L5 metadata. + - Added `dovi_rpu_remove_mapping` function. + + ## 2.1.0 - Made some parsing functions private, as they were always meant to be internal only. - Replaced `DoviRpu::trailing_bytes` by `trailing_zeroes`, which is only the count of the zero bytes. diff --git a/dolby_vision/Cargo.toml b/dolby_vision/Cargo.toml index 505d93e..376e696 100644 --- a/dolby_vision/Cargo.toml +++ b/dolby_vision/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "dolby_vision" -version = "2.1.0" +version = "3.0.0" authors = ["quietvoid"] edition = "2021" rust-version = "1.60.0" @@ -9,7 +9,7 @@ description = "Dolby Vision metadata parsing and writing" repository = "https://github.com/quietvoid/dovi_tool/tree/main/dolby_vision" [dependencies] -bitvec_helpers = "2.0.1" +bitvec_helpers = { version = "3.1.2", default-features = false, features = ["bitstream-io"] } anyhow = "1.0.68" bitvec = "1.0.1" crc = "3.0.0" diff --git a/dolby_vision/examples/capi_rpu_file.c b/dolby_vision/examples/capi_rpu_file.c index 6c77108..914f9dd 100644 --- a/dolby_vision/examples/capi_rpu_file.c +++ b/dolby_vision/examples/capi_rpu_file.c @@ -2,11 +2,7 @@ #include #include -#include - -int do_something(DoviRpuOpaque *rpu, const DoviRpuDataHeader *header); -int process_rpu_data_mapping(DoviRpuOpaque *rpu, const DoviRpuDataHeader *header); -int process_dm_metadata(DoviRpuOpaque *rpu, const DoviRpuDataHeader *header); +#include "helpers.h" int main(void) { char *path = "../../assets/hevc_tests/regular_rpu_mel.bin"; @@ -28,277 +24,13 @@ int main(void) { const DoviRpuDataHeader *header = dovi_rpu_get_header(rpu); // Process the RPU.. - ret = do_something(rpu, header); + ret = process_rpu_info(rpu, header); + if (ret < 0) { + const char *error = dovi_rpu_get_error(rpu); + printf("%s\n", error); + } // Free everything dovi_rpu_free_header(header); dovi_rpu_list_free(rpus); } - -int do_something(DoviRpuOpaque *rpu, const DoviRpuDataHeader *header) { - const char *error; - int ret; - - if (header->rpu_type != 2) - return 0; - - printf("Guessed profile: %i\n", header->guessed_profile); - - if (header->subprofile) { - printf("Profile 7 subprofile: %s\n", header->subprofile); - } - - // We have new rpu_data_mapping metadata - if (!header->use_prev_vdr_rpu_flag) { - ret = process_rpu_data_mapping(rpu, header); - } - - // We have display management metadata - if (header->vdr_dm_metadata_present_flag) { - ret = process_dm_metadata(rpu, header); - } - - if (header->guessed_profile == 7) { - // Convert FEL to MEL - ret = dovi_convert_rpu_with_mode(rpu, 1); - if (ret < 0) { - return -1; - } - } - - const DoviData *data = dovi_write_unspec62_nalu(rpu); - if (!data) { - return -1; - } - - // Do something with the encoded RPU.. - - // Free the encoded data when we're done - dovi_data_free(data); - - return 0; -} - -int process_rpu_data_mapping(DoviRpuOpaque *rpu, const DoviRpuDataHeader *header) { - const DoviRpuDataMapping *rpu_data_mapping = dovi_rpu_get_data_mapping(rpu); - if (!rpu_data_mapping) - return -1; - - printf("vdr_rpu_data_mapping()\n"); - - // Use the rpu_data_mapping metadata.. - for (int cmp = 0; cmp < DoviNUM_COMPONENTS; cmp++) { - printf(" cmp %d\n", cmp); - - // 1D buffer example - printf(" num_pivots: %d\n", header->num_pivots_minus_2[cmp] + 2); - printf(" values: [", cmp); - - const uint64_t *buf = header->pred_pivot_value[cmp].data; - for (int i= 0; i < header->pred_pivot_value[cmp].len; i++) { - printf(" %d", buf[i]); - } - printf(" ]\n"); - - // 2D buffer example - const DoviU64Data2D poly_coef = rpu_data_mapping->poly_coef[cmp]; - printf(" poly_coefs\n"); - - for (int i = 0; i < poly_coef.len; i++) { - const DoviU64Data *dovi_data = poly_coef.list[i]; - printf(" poly_coef[%d], len: %d, values: [", i, dovi_data->len); - - const uint64_t *buf = dovi_data->data; - for (int j = 0; j < dovi_data->len; j++) { - printf(" %d", buf[j]); - } - - printf(" ]\n"); - } - - // 3D buffer example - const DoviU64Data3D mmr_coef = rpu_data_mapping->mmr_coef[cmp]; - printf(" mmr_coefs, len: %d\n", mmr_coef.len); - - for (int i = 0; i < mmr_coef.len; i++) { - const DoviU64Data2D *dovi_data_2d = mmr_coef.list[i]; - printf(" mmr_coef[%d], len: %d, values: [\n", i, dovi_data_2d->len); - - for (int j = 0; j < dovi_data_2d->len; j++) { - const DoviU64Data *dovi_data = dovi_data_2d->list[j]; - printf(" mmr_coef[%d][%d], len: %d, values: [", i, j, dovi_data->len); - - const uint64_t *buf = dovi_data->data; - for (int k = 0; k < dovi_data->len; k++) { - printf(" %d", buf[k]); - } - - printf(" ]\n"); - } - - printf(" ]\n"); - } - } - - dovi_rpu_free_data_mapping(rpu_data_mapping); - - // We have NLQ metadata (and therefore an enhancement layer) - if (header->nlq_method_idc != -1) { - const DoviRpuDataNlq *rpu_data_nlq = dovi_rpu_get_data_nlq(rpu); - if (!rpu_data_nlq) - return -1; - - printf("vdr_rpu_data_nlq()\n"); - - // Do something with the NLQ data.. - const DoviU64Data2D vdr_in_max = rpu_data_nlq->vdr_in_max; - printf(" NLQ data\n vdr_in_max, len: %d\n", vdr_in_max.len); - - for (int i = 0; i < vdr_in_max.len; i++) { - // This buffer is always size 3 - const DoviU64Data *dovi_data = vdr_in_max.list[i]; - - printf(" vdr_in_max[%d], len: %d, values: [%d, %d, %d]\n", i, - dovi_data->len, dovi_data->data[0], dovi_data->data[1], dovi_data->data[2]); - } - - dovi_rpu_free_data_nlq(rpu_data_nlq); - } - - return 0; -} - -int process_dm_metadata(DoviRpuOpaque *rpu, const DoviRpuDataHeader *header) { - const DoviVdrDmData *vdr_dm_data = dovi_rpu_get_vdr_dm_data(rpu); - if (!vdr_dm_data) - return -1; - - printf("vdr_dm_data_payload()\n"); - - printf(" Num extension metadata blocks: %d\n", vdr_dm_data->dm_data.num_ext_blocks); - - // Do something with the DM metadata.. - printf(" Mastering display PQ codes: min %.6f max %.6f\n", vdr_dm_data->source_min_pq / 4095.0, - vdr_dm_data->source_max_pq / 4095.0); - - printf(" dm_data_payload(), CM v2.9 DM data\n"); - // We have the frame stats - if (vdr_dm_data->dm_data.level1) { - const DoviExtMetadataBlockLevel1 *meta = vdr_dm_data->dm_data.level1; - - // Values are PQ encoded in 12 bit, from 0 to 4095 - printf(" L1 Frame brightness: min %.6f, max %.6f, avg %.6f\n", meta->min_pq / 4095.0, - meta->max_pq / 4095.0, meta->avg_pq / 4095.0); - } - - // We have creative trims - if (vdr_dm_data->dm_data.level2.len > 0) { - const DoviLevel2BlockList blocks = vdr_dm_data->dm_data.level2; - printf(" L2 Creative trims, targets: %d\n", vdr_dm_data->dm_data.level2.len); - - for (int i = 0; i < vdr_dm_data->dm_data.level2.len; i++) { - const DoviExtMetadataBlockLevel2 *meta = blocks.list[i]; - - printf(" target display brightness PQ code: %.6f\n", meta->target_max_pq / 4095.0); - - // Trim values are from 0 to 4095 - printf(" trim_slope: %d, trim_offset: %d, trim_power: %d\n", meta->trim_slope, - meta->trim_offset, meta->trim_power); - printf(" trim_chroma_weight: %d, trim_saturation_gain: %d, ms_weight: %d\n", - meta->trim_chroma_weight, meta->trim_saturation_gain, meta->ms_weight); - } - } - - if (vdr_dm_data->dm_data.level4) { - const DoviExtMetadataBlockLevel4 *meta = vdr_dm_data->dm_data.level4; - - printf(" L4 anchor_pq: %d, anchor_power: %d\n", meta->anchor_pq, meta->anchor_power); - } - - // We have active area metadata - if (vdr_dm_data->dm_data.level5) { - const DoviExtMetadataBlockLevel5 *meta = vdr_dm_data->dm_data.level5; - - printf(" L5 Active area offsets: top %d, bottom %d, left %d, right %d\n", - meta->active_area_top_offset, meta->active_area_bottom_offset, - meta->active_area_left_offset, meta->active_area_right_offset); - } - - // We have fallback HDR10 metadata - if (vdr_dm_data->dm_data.level6) { - const DoviExtMetadataBlockLevel6 *meta = vdr_dm_data->dm_data.level6; - - printf(" L6 Mastering display: min %.4f, max %d\n", - meta->min_display_mastering_luminance / 10000.0, - meta->max_display_mastering_luminance); - - printf(" MaxCLL %d, MaxFALL %d\n", meta->max_content_light_level, - meta->max_frame_average_light_level); - } - - // CM v4.0, DM data version 2 - if (vdr_dm_data->dm_data.level254) { - printf(" dm_data_payload2(), CM v4.0 DM data\n"); - - if (vdr_dm_data->dm_data.level3) { - const DoviExtMetadataBlockLevel3 *meta = vdr_dm_data->dm_data.level3; - - printf(" L3 level 1 PQ offsets min: %d, max: %d, avg: %d\n", - meta->min_pq_offset, meta->max_pq_offset, meta->avg_pq_offset); - } - - // We have creative trims - if (vdr_dm_data->dm_data.level8.len > 0) { - const DoviLevel8BlockList blocks = vdr_dm_data->dm_data.level8; - printf(" L8 Creative trims, targets: %d\n", vdr_dm_data->dm_data.level8.len); - - for (int i = 0; i < vdr_dm_data->dm_data.level8.len; i++) { - const DoviExtMetadataBlockLevel8 *meta = blocks.list[i]; - - printf(" target display index: %d\n", meta->target_display_index); - - // Trim values are from 0 to 4095 - printf(" trim_slope: %d, trim_offset: %d, trim_power: %d\n", meta->trim_slope, - meta->trim_offset, meta->trim_power); - printf(" trim_chroma_weight: %d, trim_saturation_gain: %d, ms_weight: %d\n", - meta->trim_chroma_weight, meta->trim_saturation_gain, meta->ms_weight); - } - } - - if (vdr_dm_data->dm_data.level9) { - const DoviExtMetadataBlockLevel9 *meta = vdr_dm_data->dm_data.level9; - - printf(" L9 Source primary index: %d\n", meta->source_primary_index); - } - - // The L8 target definitions - if (vdr_dm_data->dm_data.level10.len > 0) { - const DoviLevel10BlockList blocks = vdr_dm_data->dm_data.level10; - printf(" L10 Custom display targets: %d\n", vdr_dm_data->dm_data.level10.len); - - for (int i = 0; i < vdr_dm_data->dm_data.level10.len; i++) { - const DoviExtMetadataBlockLevel10 *meta = blocks.list[i]; - - printf(" target display index: %d\n", meta->target_display_index); - - // Trim values are from 0 to 4095 - printf(" target_max_pq: %d, target_min_pq: %d, target_primary_index: %d \n", meta->target_max_pq, meta->target_min_pq, meta->target_primary_index); - } - } - - if (vdr_dm_data->dm_data.level11) { - const DoviExtMetadataBlockLevel11 *meta = vdr_dm_data->dm_data.level11; - - printf(" L11 Content type: %d, whitepoint: %d, reference_mode_flag: %d\n", - meta->content_type, (meta->whitepoint * 375) + 6504, meta->reference_mode_flag); - } - - if (vdr_dm_data->dm_data.level254) { - const DoviExtMetadataBlockLevel254 *meta = vdr_dm_data->dm_data.level254; - - printf(" L254 dm_mode: %d, dm_version_index: %d\n", meta->dm_mode, meta->dm_version_index); - } - } - - dovi_rpu_free_vdr_dm_data(vdr_dm_data); -} diff --git a/dolby_vision/examples/capi_single_rpu.c b/dolby_vision/examples/capi_single_rpu.c index 904f47f..fbaea6c 100644 --- a/dolby_vision/examples/capi_single_rpu.c +++ b/dolby_vision/examples/capi_single_rpu.c @@ -2,13 +2,7 @@ #include #include -#include - -int do_something(DoviRpuOpaque *rpu, const DoviRpuDataHeader *header); -int process_rpu_data_mapping(DoviRpuOpaque *rpu, const DoviRpuDataHeader *header); -int process_dm_metadata(DoviRpuOpaque *rpu, const DoviRpuDataHeader *header); - -const uint8_t* read_rpu_file(char *path, size_t *len); +#include "helpers.h" int main(void) { char *path = "../../assets/tests/cmv40_full_rpu.bin"; @@ -27,12 +21,11 @@ int main(void) { printf("%s\n", error); dovi_rpu_free(rpu); - return 0; + return 1; } // Process the RPU.. - ret = do_something(rpu, header); - + ret = process_rpu_info(rpu, header); if (ret < 0) { const char *error = dovi_rpu_get_error(rpu); printf("%s\n", error); @@ -42,289 +35,3 @@ int main(void) { dovi_rpu_free_header(header); dovi_rpu_free(rpu); } - -int do_something(DoviRpuOpaque *rpu, const DoviRpuDataHeader *header) { - const char *error; - int ret; - - if (header->rpu_type != 2) - return 0; - - printf("Guessed profile: %i\n", header->guessed_profile); - - if (header->subprofile) { - printf("Profile 7 subprofile: %s\n", header->subprofile); - } - - // We have new rpu_data_mapping metadata - if (!header->use_prev_vdr_rpu_flag) { - ret = process_rpu_data_mapping(rpu, header); - } - - // We have display management metadata - if (header->vdr_dm_metadata_present_flag) { - ret = process_dm_metadata(rpu, header); - } - - if (header->guessed_profile == 7) { - // Convert FEL to MEL - ret = dovi_convert_rpu_with_mode(rpu, 1); - if (ret < 0) { - return -1; - } - } - - const DoviData *data = dovi_write_unspec62_nalu(rpu); - if (!data) { - return -1; - } - - // Do something with the encoded RPU.. - - // Free the encoded data when we're done - dovi_data_free(data); - - return 0; -} - -int process_rpu_data_mapping(DoviRpuOpaque *rpu, const DoviRpuDataHeader *header) { - const DoviRpuDataMapping *rpu_data_mapping = dovi_rpu_get_data_mapping(rpu); - if (!rpu_data_mapping) - return -1; - - printf("vdr_rpu_data_mapping()\n"); - - // Use the rpu_data_mapping metadata.. - for (int cmp = 0; cmp < DoviNUM_COMPONENTS; cmp++) { - printf(" cmp %d\n", cmp); - - // 1D buffer example - printf(" num_pivots: %d\n", header->num_pivots_minus_2[cmp] + 2); - printf(" values: [", cmp); - - const uint64_t *buf = header->pred_pivot_value[cmp].data; - for (int i= 0; i < header->pred_pivot_value[cmp].len; i++) { - printf(" %d", buf[i]); - } - printf(" ]\n"); - - // 2D buffer example - const DoviU64Data2D poly_coef = rpu_data_mapping->poly_coef[cmp]; - printf(" poly_coefs\n"); - - for (int i = 0; i < poly_coef.len; i++) { - const DoviU64Data *dovi_data = poly_coef.list[i]; - printf(" poly_coef[%d], len: %d, values: [", i, dovi_data->len); - - const uint64_t *buf = dovi_data->data; - for (int j = 0; j < dovi_data->len; j++) { - printf(" %d", buf[j]); - } - - printf(" ]\n"); - } - - // 3D buffer example - const DoviU64Data3D mmr_coef = rpu_data_mapping->mmr_coef[cmp]; - printf(" mmr_coefs, len: %d\n", mmr_coef.len); - - for (int i = 0; i < mmr_coef.len; i++) { - const DoviU64Data2D *dovi_data_2d = mmr_coef.list[i]; - printf(" mmr_coef[%d], len: %d, values: [\n", i, dovi_data_2d->len); - - for (int j = 0; j < dovi_data_2d->len; j++) { - const DoviU64Data *dovi_data = dovi_data_2d->list[j]; - printf(" mmr_coef[%d][%d], len: %d, values: [", i, j, dovi_data->len); - - const uint64_t *buf = dovi_data->data; - for (int k = 0; k < dovi_data->len; k++) { - printf(" %d", buf[k]); - } - - printf(" ]\n"); - } - - printf(" ]\n"); - } - } - - dovi_rpu_free_data_mapping(rpu_data_mapping); - - // We have NLQ metadata (and therefore an enhancement layer) - if (header->nlq_method_idc != -1) { - const DoviRpuDataNlq *rpu_data_nlq = dovi_rpu_get_data_nlq(rpu); - if (!rpu_data_nlq) - return -1; - - printf("vdr_rpu_data_nlq()\n"); - - // Do something with the NLQ data.. - const DoviU64Data2D vdr_in_max = rpu_data_nlq->vdr_in_max; - printf(" NLQ data\n vdr_in_max, len: %d\n", vdr_in_max.len); - - for (int i = 0; i < vdr_in_max.len; i++) { - // This buffer is always size 3 - const DoviU64Data *dovi_data = vdr_in_max.list[i]; - - printf(" vdr_in_max[%d], len: %d, values: [%d, %d, %d]\n", i, - dovi_data->len, dovi_data->data[0], dovi_data->data[1], dovi_data->data[2]); - } - - dovi_rpu_free_data_nlq(rpu_data_nlq); - } - - return 0; -} - -int process_dm_metadata(DoviRpuOpaque *rpu, const DoviRpuDataHeader *header) { - const DoviVdrDmData *vdr_dm_data = dovi_rpu_get_vdr_dm_data(rpu); - if (!vdr_dm_data) - return -1; - - printf("vdr_dm_data_payload()\n"); - - printf(" Num extension metadata blocks: %d\n", vdr_dm_data->dm_data.num_ext_blocks); - - // Do something with the DM metadata.. - printf(" Mastering display PQ codes: min %.6f max %.6f\n", vdr_dm_data->source_min_pq / 4095.0, - vdr_dm_data->source_max_pq / 4095.0); - - printf(" dm_data_payload(), CM v2.9 DM data\n"); - // We have the frame stats - if (vdr_dm_data->dm_data.level1) { - const DoviExtMetadataBlockLevel1 *meta = vdr_dm_data->dm_data.level1; - - // Values are PQ encoded in 12 bit, from 0 to 4095 - printf(" L1 Frame brightness: min %.6f, max %.6f, avg %.6f\n", meta->min_pq / 4095.0, - meta->max_pq / 4095.0, meta->avg_pq / 4095.0); - } - - // We have creative trims - if (vdr_dm_data->dm_data.level2.len > 0) { - const DoviLevel2BlockList blocks = vdr_dm_data->dm_data.level2; - printf(" L2 Creative trims, targets: %d\n", vdr_dm_data->dm_data.level2.len); - - for (int i = 0; i < vdr_dm_data->dm_data.level2.len; i++) { - const DoviExtMetadataBlockLevel2 *meta = blocks.list[i]; - - printf(" target display brightness PQ code: %.6f\n", meta->target_max_pq / 4095.0); - - // Trim values are from 0 to 4095 - printf(" trim_slope: %d, trim_offset: %d, trim_power: %d\n", meta->trim_slope, - meta->trim_offset, meta->trim_power); - printf(" trim_chroma_weight: %d, trim_saturation_gain: %d, ms_weight: %d\n", - meta->trim_chroma_weight, meta->trim_saturation_gain, meta->ms_weight); - } - } - - if (vdr_dm_data->dm_data.level4) { - const DoviExtMetadataBlockLevel4 *meta = vdr_dm_data->dm_data.level4; - - printf(" L4 anchor_pq: %d, anchor_power: %d\n", meta->anchor_pq, meta->anchor_power); - } - - // We have active area metadata - if (vdr_dm_data->dm_data.level5) { - const DoviExtMetadataBlockLevel5 *meta = vdr_dm_data->dm_data.level5; - - printf(" L5 Active area offsets: top %d, bottom %d, left %d, right %d\n", - meta->active_area_top_offset, meta->active_area_bottom_offset, - meta->active_area_left_offset, meta->active_area_right_offset); - } - - // We have fallback HDR10 metadata - if (vdr_dm_data->dm_data.level6) { - const DoviExtMetadataBlockLevel6 *meta = vdr_dm_data->dm_data.level6; - - printf(" L6 Mastering display: min %.4f, max %d\n", - meta->min_display_mastering_luminance / 10000.0, - meta->max_display_mastering_luminance); - - printf(" MaxCLL %d, MaxFALL %d\n", meta->max_content_light_level, - meta->max_frame_average_light_level); - } - - // CM v4.0, DM data version 2 - if (vdr_dm_data->dm_data.level254) { - printf(" dm_data_payload2(), CM v4.0 DM data\n"); - - if (vdr_dm_data->dm_data.level3) { - const DoviExtMetadataBlockLevel3 *meta = vdr_dm_data->dm_data.level3; - - printf(" L3 level 1 PQ offsets min: %d, max: %d, avg: %d\n", - meta->min_pq_offset, meta->max_pq_offset, meta->avg_pq_offset); - } - - // We have creative trims - if (vdr_dm_data->dm_data.level8.len > 0) { - const DoviLevel8BlockList blocks = vdr_dm_data->dm_data.level8; - printf(" L8 Creative trims, targets: %d\n", vdr_dm_data->dm_data.level8.len); - - for (int i = 0; i < vdr_dm_data->dm_data.level8.len; i++) { - const DoviExtMetadataBlockLevel8 *meta = blocks.list[i]; - - printf(" target display index: %d\n", meta->target_display_index); - - // Trim values are from 0 to 4095 - printf(" trim_slope: %d, trim_offset: %d, trim_power: %d\n", meta->trim_slope, - meta->trim_offset, meta->trim_power); - printf(" trim_chroma_weight: %d, trim_saturation_gain: %d, ms_weight: %d\n", - meta->trim_chroma_weight, meta->trim_saturation_gain, meta->ms_weight); - } - } - - if (vdr_dm_data->dm_data.level9) { - const DoviExtMetadataBlockLevel9 *meta = vdr_dm_data->dm_data.level9; - - printf(" L9 Source primary index: %d\n", meta->source_primary_index); - } - - // The L8 target definitions - if (vdr_dm_data->dm_data.level10.len > 0) { - const DoviLevel10BlockList blocks = vdr_dm_data->dm_data.level10; - printf(" L10 Custom display targets: %d\n", vdr_dm_data->dm_data.level10.len); - - for (int i = 0; i < vdr_dm_data->dm_data.level10.len; i++) { - const DoviExtMetadataBlockLevel10 *meta = blocks.list[i]; - - printf(" target display index: %d\n", meta->target_display_index); - - // Trim values are from 0 to 4095 - printf(" target_max_pq: %d, target_min_pq: %d, target_primary_index: %d \n", meta->target_max_pq, meta->target_min_pq, meta->target_primary_index); - } - } - - if (vdr_dm_data->dm_data.level11) { - const DoviExtMetadataBlockLevel11 *meta = vdr_dm_data->dm_data.level11; - - printf(" L11 Content type: %d, whitepoint: %d, reference_mode_flag: %d\n", - meta->content_type, (meta->whitepoint * 375) + 6504, meta->reference_mode_flag); - } - - if (vdr_dm_data->dm_data.level254) { - const DoviExtMetadataBlockLevel254 *meta = vdr_dm_data->dm_data.level254; - - printf(" L254 dm_mode: %d, dm_version_index: %d\n", meta->dm_mode, meta->dm_version_index); - } - } - - dovi_rpu_free_vdr_dm_data(vdr_dm_data); -} - -const uint8_t* read_rpu_file(char *path, size_t *len) { - FILE *fileptr; - uint8_t *buffer; - - fileptr = fopen(path, "rb"); - fseek(fileptr, 0, SEEK_END); - *len = (size_t) ftell(fileptr); - rewind(fileptr); - - size_t size = *len * sizeof(uint8_t); - - buffer = (uint8_t *) malloc(size); - fread(buffer, *len, 1, fileptr); - fclose(fileptr); - - return buffer; -} diff --git a/dolby_vision/examples/helpers.h b/dolby_vision/examples/helpers.h new file mode 100644 index 0000000..e71a00b --- /dev/null +++ b/dolby_vision/examples/helpers.h @@ -0,0 +1,293 @@ +#include + +const uint8_t* read_rpu_file(char *path, size_t *len) { + FILE *fileptr; + uint8_t *buffer; + + fileptr = fopen(path, "rb"); + fseek(fileptr, 0, SEEK_END); + *len = (size_t) ftell(fileptr); + rewind(fileptr); + + size_t size = *len * sizeof(uint8_t); + + buffer = (uint8_t *) malloc(size); + fread(buffer, *len, 1, fileptr); + fclose(fileptr); + + return buffer; +} + +int process_rpu_data_mapping(DoviRpuOpaque *rpu, const DoviRpuDataHeader *header); +int process_dm_metadata(DoviRpuOpaque *rpu, const DoviRpuDataHeader *header); + +int process_rpu_info(DoviRpuOpaque *rpu, const DoviRpuDataHeader *header) { + const char *error; + int ret; + + if (header->rpu_type != 2) + return 0; + + printf("Guessed profile: %i\n", header->guessed_profile); + + if (header->el_type) { + printf("Profile 7 EL type: %s\n", header->el_type); + } + + // We have new rpu_data_mapping metadata + if (!header->use_prev_vdr_rpu_flag) { + ret = process_rpu_data_mapping(rpu, header); + } + + // We have display management metadata + if (header->vdr_dm_metadata_present_flag) { + ret = process_dm_metadata(rpu, header); + } + + if (header->guessed_profile == 7) { + // Convert FEL to MEL + ret = dovi_convert_rpu_with_mode(rpu, 1); + if (ret < 0) { + return -1; + } + } + + const DoviData *data = dovi_write_unspec62_nalu(rpu); + if (!data) { + return -1; + } + + // Do something with the encoded RPU.. + + // Free the encoded data when we're done + dovi_data_free(data); + + return 0; +} + +int process_rpu_data_mapping(DoviRpuOpaque *rpu, const DoviRpuDataHeader *header) { + const DoviRpuDataMapping *rpu_data_mapping = dovi_rpu_get_data_mapping(rpu); + if (!rpu_data_mapping) + return -1; + + printf("vdr_rpu_data_mapping()\n"); + + // Use the rpu_data_mapping metadata.. + for (int cmp = 0; cmp < DoviNUM_COMPONENTS; cmp++) { + printf(" cmp %d\n", cmp); + + const DoviReshapingCurve curve = rpu_data_mapping->curves[cmp]; + + // 1D buffer example + printf(" num_pivots: %d\n", curve.num_pivots_minus2 + 2); + printf(" values: [", cmp); + + const uint16_t *buf = curve.pivots.data; + for (int i= 0; i < curve.pivots.len; i++) { + printf(" %d", buf[i]); + } + printf(" ]\n"); + + if (curve.polynomial) { + printf(" Polynomial reshaping curve\n"); + const DoviPolynomialCurve *poly_curve = curve.polynomial; + + // 2D buffer example + const DoviU64Data2D poly_coef = poly_curve->poly_coef; + printf(" poly_coefs\n"); + + for (int i = 0; i < poly_coef.len; i++) { + const DoviU64Data *dovi_data = poly_coef.list[i]; + printf(" poly_coef[%d], len: %d, values: [", i, dovi_data->len); + + const uint64_t *buf = dovi_data->data; + for (int j = 0; j < dovi_data->len; j++) { + printf(" %d", buf[j]); + } + + printf(" ]\n"); + } + } else if (curve.mmr) { + printf(" MMR reshaping curve\n"); + const DoviMMRCurve *mmr_curve = curve.mmr; + + // 3D buffer example + const DoviU64Data3D mmr_coef = mmr_curve->mmr_coef; + printf(" mmr_coefs, len: %d\n", mmr_coef.len); + + for (int i = 0; i < mmr_coef.len; i++) { + const DoviU64Data2D *dovi_data_2d = mmr_coef.list[i]; + printf(" mmr_coef[%d], len: %d, values: [\n", i, dovi_data_2d->len); + + for (int j = 0; j < dovi_data_2d->len; j++) { + const DoviU64Data *dovi_data = dovi_data_2d->list[j]; + printf(" mmr_coef[%d][%d], len: %d, values: [", i, j, dovi_data->len); + + const uint64_t *buf = dovi_data->data; + for (int k = 0; k < dovi_data->len; k++) { + printf(" %d", buf[k]); + } + + printf(" ]\n"); + } + + printf(" ]\n"); + } + } + } + + // We have NLQ metadata (and therefore an enhancement layer) + if (rpu_data_mapping->nlq) { + const DoviRpuDataNlq *rpu_data_nlq = rpu_data_mapping->nlq; + printf("Non linear quantization (NLQ)\n"); + + // Do something with the NLQ data.. + printf(" nlq_offset: [%d, %d, %d]\n", + rpu_data_nlq->nlq_offset[0], rpu_data_nlq->nlq_offset[1], rpu_data_nlq->nlq_offset[2]); + printf(" vdr_in_max_int: [%d, %d, %d]\n", + rpu_data_nlq->vdr_in_max_int[0], rpu_data_nlq->vdr_in_max_int[1], rpu_data_nlq->vdr_in_max_int[2]); + printf(" vdr_in_max: [%d, %d, %d]\n", + rpu_data_nlq->vdr_in_max[0], rpu_data_nlq->vdr_in_max[1], rpu_data_nlq->vdr_in_max[2]); + } + + dovi_rpu_free_data_mapping(rpu_data_mapping); + + return 0; +} + +int process_dm_metadata(DoviRpuOpaque *rpu, const DoviRpuDataHeader *header) { + const DoviVdrDmData *vdr_dm_data = dovi_rpu_get_vdr_dm_data(rpu); + if (!vdr_dm_data) + return -1; + + printf("vdr_dm_data_payload()\n"); + + printf(" Num extension metadata blocks: %d\n", vdr_dm_data->dm_data.num_ext_blocks); + + // Do something with the DM metadata.. + printf(" Mastering display PQ codes: min %.6f max %.6f\n", vdr_dm_data->source_min_pq / 4095.0, + vdr_dm_data->source_max_pq / 4095.0); + + printf(" dm_data_payload(), CM v2.9 DM data\n"); + // We have the frame stats + if (vdr_dm_data->dm_data.level1) { + const DoviExtMetadataBlockLevel1 *meta = vdr_dm_data->dm_data.level1; + + // Values are PQ encoded in 12 bit, from 0 to 4095 + printf(" L1 Frame brightness: min %.6f, max %.6f, avg %.6f\n", meta->min_pq / 4095.0, + meta->max_pq / 4095.0, meta->avg_pq / 4095.0); + } + + // We have creative trims + if (vdr_dm_data->dm_data.level2.len > 0) { + const DoviLevel2BlockList blocks = vdr_dm_data->dm_data.level2; + printf(" L2 Creative trims, targets: %d\n", vdr_dm_data->dm_data.level2.len); + + for (int i = 0; i < vdr_dm_data->dm_data.level2.len; i++) { + const DoviExtMetadataBlockLevel2 *meta = blocks.list[i]; + + printf(" target display brightness PQ code: %.6f\n", meta->target_max_pq / 4095.0); + + // Trim values are from 0 to 4095 + printf(" trim_slope: %d, trim_offset: %d, trim_power: %d\n", meta->trim_slope, + meta->trim_offset, meta->trim_power); + printf(" trim_chroma_weight: %d, trim_saturation_gain: %d, ms_weight: %d\n", + meta->trim_chroma_weight, meta->trim_saturation_gain, meta->ms_weight); + } + } + + if (vdr_dm_data->dm_data.level4) { + const DoviExtMetadataBlockLevel4 *meta = vdr_dm_data->dm_data.level4; + + printf(" L4 anchor_pq: %d, anchor_power: %d\n", meta->anchor_pq, meta->anchor_power); + } + + // We have active area metadata + if (vdr_dm_data->dm_data.level5) { + const DoviExtMetadataBlockLevel5 *meta = vdr_dm_data->dm_data.level5; + + printf(" L5 Active area offsets: left %d, right %d, top %d, bottom %d\n", + meta->active_area_left_offset, meta->active_area_right_offset, + meta->active_area_top_offset, meta->active_area_bottom_offset); + } + + // We have fallback HDR10 metadata + if (vdr_dm_data->dm_data.level6) { + const DoviExtMetadataBlockLevel6 *meta = vdr_dm_data->dm_data.level6; + + printf(" L6 Mastering display: min %.4f, max %d\n", + meta->min_display_mastering_luminance / 10000.0, + meta->max_display_mastering_luminance); + + printf(" MaxCLL %d, MaxFALL %d\n", meta->max_content_light_level, + meta->max_frame_average_light_level); + } + + // CM v4.0, DM data version 2 + if (vdr_dm_data->dm_data.level254) { + printf(" dm_data_payload2(), CM v4.0 DM data\n"); + + if (vdr_dm_data->dm_data.level3) { + const DoviExtMetadataBlockLevel3 *meta = vdr_dm_data->dm_data.level3; + + printf(" L3 level 1 PQ offsets min: %d, max: %d, avg: %d\n", + meta->min_pq_offset, meta->max_pq_offset, meta->avg_pq_offset); + } + + // We have creative trims + if (vdr_dm_data->dm_data.level8.len > 0) { + const DoviLevel8BlockList blocks = vdr_dm_data->dm_data.level8; + printf(" L8 Creative trims, targets: %d\n", vdr_dm_data->dm_data.level8.len); + + for (int i = 0; i < vdr_dm_data->dm_data.level8.len; i++) { + const DoviExtMetadataBlockLevel8 *meta = blocks.list[i]; + + printf(" target display index: %d\n", meta->target_display_index); + + // Trim values are from 0 to 4095 + printf(" trim_slope: %d, trim_offset: %d, trim_power: %d\n", meta->trim_slope, + meta->trim_offset, meta->trim_power); + printf(" trim_chroma_weight: %d, trim_saturation_gain: %d, ms_weight: %d\n", + meta->trim_chroma_weight, meta->trim_saturation_gain, meta->ms_weight); + } + } + + if (vdr_dm_data->dm_data.level9) { + const DoviExtMetadataBlockLevel9 *meta = vdr_dm_data->dm_data.level9; + + printf(" L9 Source primary index: %d\n", meta->source_primary_index); + } + + // The L8 target definitions + if (vdr_dm_data->dm_data.level10.len > 0) { + const DoviLevel10BlockList blocks = vdr_dm_data->dm_data.level10; + printf(" L10 Custom display targets: %d\n", vdr_dm_data->dm_data.level10.len); + + for (int i = 0; i < vdr_dm_data->dm_data.level10.len; i++) { + const DoviExtMetadataBlockLevel10 *meta = blocks.list[i]; + + printf(" target display index: %d\n", meta->target_display_index); + + // Trim values are from 0 to 4095 + printf(" target_max_pq: %d, target_min_pq: %d, target_primary_index: %d \n", meta->target_max_pq, meta->target_min_pq, meta->target_primary_index); + } + } + + if (vdr_dm_data->dm_data.level11) { + const DoviExtMetadataBlockLevel11 *meta = vdr_dm_data->dm_data.level11; + + printf(" L11 Content type: %d, whitepoint: %d, reference_mode_flag: %d\n", + meta->content_type, (meta->whitepoint * 375) + 6504, meta->reference_mode_flag); + } + + if (vdr_dm_data->dm_data.level254) { + const DoviExtMetadataBlockLevel254 *meta = vdr_dm_data->dm_data.level254; + + printf(" L254 dm_mode: %d, dm_version_index: %d\n", meta->dm_mode, meta->dm_version_index); + } + } + + dovi_rpu_free_vdr_dm_data(vdr_dm_data); + + return 0; +} diff --git a/dolby_vision/examples/simple_edit.cpp b/dolby_vision/examples/simple_edit.cpp new file mode 100644 index 0000000..53be2ab --- /dev/null +++ b/dolby_vision/examples/simple_edit.cpp @@ -0,0 +1,44 @@ +#include +#include +#include +#include +#include + +extern "C" +{ +#include "helpers.h" +} + +int main(void) { + std::ifstream input("../../assets/tests/fel_orig.bin", std::ios::binary); + + const std::vector buf( + (std::istreambuf_iterator(input)), + (std::istreambuf_iterator())); + + input.close(); + + auto start = std::chrono::high_resolution_clock::now(); + + DoviRpuOpaque *rpu = dovi_parse_unspec62_nalu(buf.data(), buf.size()); + const DoviRpuDataHeader *header = dovi_rpu_get_header(rpu); + + if (header) { + int ret = dovi_convert_rpu_with_mode(rpu, 2); + ret = dovi_rpu_remove_mapping(rpu); + ret = dovi_rpu_set_active_area_offsets(rpu, 0, 0, 138, 138); + + const DoviData *rpu_payload = dovi_write_unspec62_nalu(rpu); + + // Do something with the edited payload + dovi_data_free(rpu_payload); + } + + if (header) + dovi_rpu_free_header(header); + + dovi_rpu_free(rpu); + + auto end = std::chrono::high_resolution_clock::now(); + std::cout << std::chrono::duration_cast(end - start).count() << " μs"; +} diff --git a/dolby_vision/src/c_structs/buffers.rs b/dolby_vision/src/c_structs/buffers.rs index 3a5ba95..c1f24c2 100644 --- a/dolby_vision/src/c_structs/buffers.rs +++ b/dolby_vision/src/c_structs/buffers.rs @@ -17,6 +17,15 @@ pub struct Data { pub len: size_t, } +/// Struct representing a data buffer +#[repr(C)] +pub struct U16Data { + /// Pointer to the data buffer. Can be null if length is zero. + pub data: *const u16, + /// Data buffer size + pub len: size_t, +} + /// Struct representing a data buffer #[repr(C)] pub struct U64Data { @@ -100,6 +109,15 @@ impl From> for Data { } } +impl From> for U16Data { + fn from(buf: Vec) -> Self { + U16Data { + len: buf.len(), + data: Box::into_raw(buf.into_boxed_slice()) as *const u16, + } + } +} + impl From<[bool; N]> for Data { fn from(array: [bool; N]) -> Self { let res: [u8; N] = array.map(|e| e as u8); @@ -129,26 +147,20 @@ impl From> for I64Data { } } -impl From<[u64; N]> for U64Data { - fn from(array: [u64; N]) -> Self { - U64Data { +impl From<[u16; N]> for U16Data { + fn from(array: [u16; N]) -> Self { + U16Data { len: array.len(), - data: Box::into_raw(Box::new(array)) as *const u64, + data: Box::into_raw(Box::new(array)) as *const u16, } } } -impl From<&Vec<[bool; N]>> for Data2D { - fn from(buf_2d: &Vec<[bool; N]>) -> Self { - let list: Vec<*const Data> = buf_2d - .clone() - .into_iter() - .map(|buf| Box::into_raw(Box::new(Data::from(buf))) as *const Data) - .collect(); - - Data2D { - len: list.len(), - list: Box::into_raw(list.into_boxed_slice()) as *const *const Data, +impl From<[u64; N]> for U64Data { + fn from(array: [u64; N]) -> Self { + U64Data { + len: array.len(), + data: Box::into_raw(Box::new(array)) as *const u64, } } } @@ -209,30 +221,23 @@ impl From>>> for I64Data3D { } } -impl From<&Vec<[u64; N]>> for U64Data2D { - fn from(buf_2d: &Vec<[u64; N]>) -> Self { - let list: Vec<*const U64Data> = buf_2d - .clone() - .into_iter() - .map(|buf| Box::into_raw(Box::new(U64Data::from(buf))) as *const U64Data) - .collect(); - - U64Data2D { - len: list.len(), - list: Box::into_raw(list.into_boxed_slice()) as *const *const U64Data, - } +impl From> for U16Data { + fn from(maybe_array: Option<[u16; N]>) -> Self { + maybe_array.map_or(U16Data::empty(), U16Data::from) } } -impl From> for U64Data { - fn from(maybe_array: Option<[u64; N]>) -> Self { - maybe_array.map_or(U64Data::empty(), U64Data::from) +impl Freeable for Data { + unsafe fn free(&self) { + Vec::from_raw_parts(self.data as *mut u8, self.len, self.len); } } -impl Freeable for Data { +impl Freeable for U16Data { unsafe fn free(&self) { - Vec::from_raw_parts(self.data as *mut u8, self.len, self.len); + if !self.data.is_null() { + Vec::from_raw_parts(self.data as *mut u16, self.len, self.len); + } } } @@ -305,7 +310,7 @@ impl Freeable for I64Data3D { } } -impl U64Data { +impl U16Data { fn empty() -> Self { Self { len: 0, diff --git a/dolby_vision/src/c_structs/mod.rs b/dolby_vision/src/c_structs/mod.rs index ca9e15d..70dead8 100644 --- a/dolby_vision/src/c_structs/mod.rs +++ b/dolby_vision/src/c_structs/mod.rs @@ -15,15 +15,3 @@ pub use rpu_data_header::RpuDataHeader; pub use rpu_data_mapping::RpuDataMapping; pub use rpu_data_nlq::RpuDataNlq; pub use vdr_dm_data::VdrDmData; - -fn components_to_cdata(cmps: &[T; NUM_COMPONENTS]) -> [R; NUM_COMPONENTS] -where - T: Clone, - R: From, -{ - [ - R::from(cmps[0].clone()), - R::from(cmps[1].clone()), - R::from(cmps[2].clone()), - ] -} diff --git a/dolby_vision/src/c_structs/rpu.rs b/dolby_vision/src/c_structs/rpu.rs index abdd115..7d13c1a 100644 --- a/dolby_vision/src/c_structs/rpu.rs +++ b/dolby_vision/src/c_structs/rpu.rs @@ -26,17 +26,20 @@ pub struct RpuOpaqueList { pub error: *const c_char, } +impl RpuOpaque { + pub(crate) fn new(rpu: Option, error: Option) -> Self { + Self { rpu, error } + } +} + impl From> for RpuOpaque { fn from(res: Result) -> Self { match res { - Ok(parsed_rpu) => Self { - rpu: Some(parsed_rpu), - error: None, - }, - Err(e) => Self { - rpu: None, - error: Some(CString::new(format!("Failed parsing RPU: {}", e)).unwrap()), - }, + Ok(rpu) => Self::new(Some(rpu), None), + Err(e) => Self::new( + None, + Some(CString::new(format!("Failed parsing RPU: {}", e)).unwrap()), + ), } } } diff --git a/dolby_vision/src/c_structs/rpu_data_header.rs b/dolby_vision/src/c_structs/rpu_data_header.rs index 81e3fe4..2fc96fe 100644 --- a/dolby_vision/src/c_structs/rpu_data_header.rs +++ b/dolby_vision/src/c_structs/rpu_data_header.rs @@ -3,63 +3,45 @@ use std::{ffi::CString, ptr::null}; use crate::rpu::rpu_data_header::RpuDataHeader as RuRpuDataHeader; -use super::{components_to_cdata, Freeable, U64Data, NUM_COMPONENTS}; - /// C struct for rpu_data_header() #[repr(C)] pub struct RpuDataHeader { /// Profile guessed from the values in the header - pub guessed_profile: u8, + guessed_profile: u8, - /// Subprofile (FEL or MEL) if the RPU is profile 7 + /// Enhancement layer type (FEL or MEL) if the RPU is profile 7 /// null pointer if not profile 7 - pub subprofile: *const c_char, + pub(crate) el_type: *const c_char, - pub rpu_nal_prefix: u8, - pub rpu_type: u8, - pub rpu_format: u16, - pub vdr_rpu_profile: u8, - pub vdr_rpu_level: u8, - pub vdr_seq_info_present_flag: bool, - pub chroma_resampling_explicit_filter_flag: bool, - pub coefficient_data_type: u8, - pub coefficient_log2_denom: u64, - pub vdr_rpu_normalized_idc: u8, - pub bl_video_full_range_flag: bool, - pub bl_bit_depth_minus8: u64, - pub el_bit_depth_minus8: u64, - pub vdr_bit_depth_minus_8: u64, - pub spatial_resampling_filter_flag: bool, - pub reserved_zero_3bits: u8, - pub el_spatial_resampling_filter_flag: bool, - pub disable_residual_flag: bool, - pub vdr_dm_metadata_present_flag: bool, - pub use_prev_vdr_rpu_flag: bool, - pub prev_vdr_rpu_id: u64, - pub vdr_rpu_id: u64, - pub mapping_color_space: u64, - pub mapping_chroma_format_idc: u64, - pub num_pivots_minus_2: [u64; NUM_COMPONENTS], - pub pred_pivot_value: [U64Data; NUM_COMPONENTS], - /// Set to -1 to represent Option::None - pub nlq_method_idc: i32, - /// Set to -1 to represent Option::None - pub nlq_num_pivots_minus2: i32, - /// Length of zero when not present. Only present in profile 4 and 7. - pub nlq_pred_pivot_value: U64Data, - pub num_x_partitions_minus1: u64, - pub num_y_partitions_minus1: u64, + rpu_nal_prefix: u8, + rpu_type: u8, + rpu_format: u16, + vdr_rpu_profile: u8, + vdr_rpu_level: u8, + vdr_seq_info_present_flag: bool, + chroma_resampling_explicit_filter_flag: bool, + coefficient_data_type: u8, + coefficient_log2_denom: u64, + vdr_rpu_normalized_idc: u8, + bl_video_full_range_flag: bool, + bl_bit_depth_minus8: u64, + el_bit_depth_minus8: u64, + vdr_bit_depth_minus8: u64, + spatial_resampling_filter_flag: bool, + reserved_zero_3bits: u8, + el_spatial_resampling_filter_flag: bool, + disable_residual_flag: bool, + vdr_dm_metadata_present_flag: bool, + use_prev_vdr_rpu_flag: bool, + prev_vdr_rpu_id: u64, } impl RpuDataHeader { /// # Safety /// The buffer pointers should be valid. pub unsafe fn free(&self) { - self.pred_pivot_value.iter().for_each(|data| data.free()); - self.nlq_pred_pivot_value.free(); - - if !self.subprofile.is_null() { - drop(CString::from_raw(self.subprofile as *mut c_char)); + if !self.el_type.is_null() { + drop(CString::from_raw(self.el_type as *mut c_char)); } } } @@ -68,7 +50,7 @@ impl From<&RuRpuDataHeader> for RpuDataHeader { fn from(header: &RuRpuDataHeader) -> Self { Self { guessed_profile: header.get_dovi_profile(), - subprofile: null(), + el_type: null(), rpu_nal_prefix: header.rpu_nal_prefix, rpu_type: header.rpu_type, rpu_format: header.rpu_format, @@ -82,7 +64,7 @@ impl From<&RuRpuDataHeader> for RpuDataHeader { bl_video_full_range_flag: header.bl_video_full_range_flag, bl_bit_depth_minus8: header.bl_bit_depth_minus8, el_bit_depth_minus8: header.el_bit_depth_minus8, - vdr_bit_depth_minus_8: header.vdr_bit_depth_minus_8, + vdr_bit_depth_minus8: header.vdr_bit_depth_minus8, spatial_resampling_filter_flag: header.spatial_resampling_filter_flag, reserved_zero_3bits: header.reserved_zero_3bits, el_spatial_resampling_filter_flag: header.el_spatial_resampling_filter_flag, @@ -90,16 +72,6 @@ impl From<&RuRpuDataHeader> for RpuDataHeader { vdr_dm_metadata_present_flag: header.vdr_dm_metadata_present_flag, use_prev_vdr_rpu_flag: header.use_prev_vdr_rpu_flag, prev_vdr_rpu_id: header.prev_vdr_rpu_id, - vdr_rpu_id: header.vdr_rpu_id, - mapping_color_space: header.mapping_color_space, - mapping_chroma_format_idc: header.mapping_chroma_format_idc, - num_pivots_minus_2: header.num_pivots_minus_2, - pred_pivot_value: components_to_cdata::, U64Data>(&header.pred_pivot_value), - nlq_method_idc: header.nlq_method_idc.map_or(-1, |e| e as i32), - nlq_num_pivots_minus2: header.nlq_num_pivots_minus2.map_or(-1, |e| e as i32), - nlq_pred_pivot_value: U64Data::from(header.nlq_pred_pivot_value), - num_x_partitions_minus1: header.num_x_partitions_minus1, - num_y_partitions_minus1: header.num_y_partitions_minus1, } } } diff --git a/dolby_vision/src/c_structs/rpu_data_mapping.rs b/dolby_vision/src/c_structs/rpu_data_mapping.rs index 6fdb63a..3dc36e5 100644 --- a/dolby_vision/src/c_structs/rpu_data_mapping.rs +++ b/dolby_vision/src/c_structs/rpu_data_mapping.rs @@ -1,89 +1,182 @@ -use crate::rpu::rpu_data_mapping::RpuDataMapping as RuRpuDataMapping; +use std::ptr::null_mut; -use super::{buffers::*, components_to_cdata, NUM_COMPONENTS}; +use crate::rpu::rpu_data_mapping::{ + DoviMMRCurve, DoviPolynomialCurve, DoviReshapingCurve, RpuDataMapping as RuRpuDataMapping, +}; + +use super::{buffers::*, RpuDataNlq, NUM_COMPONENTS}; /// C struct for rpu_data_mapping() #[repr(C)] pub struct RpuDataMapping { - mapping_idc: [U64Data; NUM_COMPONENTS], - mapping_param_pred_flag: [Data; NUM_COMPONENTS], - num_mapping_param_predictors: [U64Data; NUM_COMPONENTS], - diff_pred_part_idx_mapping_minus1: [U64Data; NUM_COMPONENTS], - poly_order_minus1: [U64Data; NUM_COMPONENTS], - linear_interp_flag: [Data; NUM_COMPONENTS], - pred_linear_interp_value_int: [U64Data; NUM_COMPONENTS], - pred_linear_interp_value: [U64Data; NUM_COMPONENTS], - poly_coef_int: [I64Data2D; NUM_COMPONENTS], - poly_coef: [U64Data2D; NUM_COMPONENTS], - mmr_order_minus1: [Data; NUM_COMPONENTS], - mmr_constant_int: [I64Data; NUM_COMPONENTS], - mmr_constant: [U64Data; NUM_COMPONENTS], - mmr_coef_int: [I64Data3D; NUM_COMPONENTS], - mmr_coef: [U64Data3D; NUM_COMPONENTS], + vdr_rpu_id: u64, + mapping_color_space: u64, + mapping_chroma_format_idc: u64, + num_x_partitions_minus1: u64, + num_y_partitions_minus1: u64, + + curves: [ReshapingCurve; NUM_COMPONENTS], + + /// Set to -1 to represent Option::None + nlq_method_idc: i32, + /// Set to -1 to represent Option::None + nlq_num_pivots_minus2: i32, + /// Length of zero when not present. Only present in profile 4 and 7. + nlq_pred_pivot_value: U16Data, + /// Pointer to `RpuDataNlq` struct, null if not dual layer profile + nlq: *const RpuDataNlq, +} + +#[repr(C)] +pub struct ReshapingCurve { + /// [2, 9] + pub num_pivots_minus2: u64, + pub pivots: U16Data, + + /// Consistent for a component + /// Luma (component 0): Polynomial = 0 + /// Chroma (components 1 and 2): MMR = 1 + pub mapping_idc: u8, + + /// mapping_idc = 0, null pointer otherwise + pub polynomial: *const PolynomialCurve, + + /// mapping_idc = 1, null pointer otherwise + pub mmr: *const MMRCurve, +} + +#[repr(C)] +pub struct PolynomialCurve { + poly_order_minus1: U64Data, + linear_interp_flag: Data, + poly_coef_int: I64Data2D, + poly_coef: U64Data2D, +} + +#[repr(C)] +pub struct MMRCurve { + mmr_order_minus1: Data, + mmr_constant_int: I64Data, + mmr_constant: U64Data, + mmr_coef_int: I64Data3D, + mmr_coef: U64Data3D, } impl RpuDataMapping { /// # Safety /// The buffer pointers should be valid. pub unsafe fn free(&self) { - self.mapping_idc.iter().for_each(|data| data.free()); - self.mapping_param_pred_flag - .iter() - .for_each(|data| data.free()); - self.num_mapping_param_predictors - .iter() - .for_each(|data| data.free()); - self.diff_pred_part_idx_mapping_minus1 - .iter() - .for_each(|data| data.free()); - self.poly_order_minus1.iter().for_each(|data| data.free()); - self.linear_interp_flag.iter().for_each(|data| data.free()); - self.pred_linear_interp_value_int - .iter() - .for_each(|data| data.free()); - self.pred_linear_interp_value - .iter() - .for_each(|data| data.free()); - self.poly_coef_int.iter().for_each(|data| data.free()); - self.poly_coef.iter().for_each(|data| data.free()); - self.mmr_order_minus1.iter().for_each(|data| data.free()); - self.mmr_constant_int.iter().for_each(|data| data.free()); - self.mmr_constant.iter().for_each(|data| data.free()); - self.mmr_coef_int.iter().for_each(|data| data.free()); - self.mmr_coef.iter().for_each(|data| data.free()); + self.curves.iter().for_each(|curve| curve.free()); + self.nlq_pred_pivot_value.free(); + + if !self.nlq.is_null() { + drop(Box::from_raw(self.nlq as *mut RpuDataNlq)) + } } } impl From<&RuRpuDataMapping> for RpuDataMapping { - fn from(data: &RuRpuDataMapping) -> Self { + fn from(mapping: &RuRpuDataMapping) -> Self { + let curves = [ + ReshapingCurve::from(&mapping.curves[0]), + ReshapingCurve::from(&mapping.curves[1]), + ReshapingCurve::from(&mapping.curves[2]), + ]; + Self { - mapping_idc: components_to_cdata::, U64Data>(&data.mapping_idc), - mapping_param_pred_flag: components_to_cdata::, Data>( - &data.mapping_param_pred_flag, - ), - num_mapping_param_predictors: components_to_cdata::, U64Data>( - &data.num_mapping_param_predictors, - ), - diff_pred_part_idx_mapping_minus1: components_to_cdata::, U64Data>( - &data.diff_pred_part_idx_mapping_minus1, - ), - poly_order_minus1: components_to_cdata::, U64Data>(&data.poly_order_minus1), - linear_interp_flag: components_to_cdata::, Data>( - &data.mapping_param_pred_flag, - ), - pred_linear_interp_value_int: components_to_cdata::, U64Data>( - &data.pred_linear_interp_value_int, - ), - pred_linear_interp_value: components_to_cdata::, U64Data>( - &data.pred_linear_interp_value, - ), - poly_coef_int: components_to_cdata::>, I64Data2D>(&data.poly_coef_int), - poly_coef: components_to_cdata::>, U64Data2D>(&data.poly_coef), - mmr_order_minus1: components_to_cdata::, Data>(&data.mmr_order_minus1), - mmr_constant_int: components_to_cdata::, I64Data>(&data.mmr_constant_int), - mmr_constant: components_to_cdata::, U64Data>(&data.mmr_constant), - mmr_coef_int: components_to_cdata::>>, I64Data3D>(&data.mmr_coef_int), - mmr_coef: components_to_cdata::>>, U64Data3D>(&data.mmr_coef), + vdr_rpu_id: mapping.vdr_rpu_id, + mapping_color_space: mapping.mapping_color_space, + mapping_chroma_format_idc: mapping.mapping_chroma_format_idc, + num_x_partitions_minus1: mapping.num_x_partitions_minus1, + num_y_partitions_minus1: mapping.num_y_partitions_minus1, + curves, + nlq_method_idc: mapping + .nlq_method_idc + .as_ref() + .map_or(-1, |e| (*e as u8) as i32), + nlq_num_pivots_minus2: mapping.nlq_num_pivots_minus2.map_or(-1, |e| e as i32), + nlq_pred_pivot_value: U16Data::from(mapping.nlq_pred_pivot_value), + nlq: mapping.nlq.as_ref().map_or(null_mut(), |nlq| { + Box::into_raw(Box::new(RpuDataNlq::from(nlq))) + }), + } + } +} + +impl ReshapingCurve { + /// # Safety + /// The buffer pointers should be valid. + pub unsafe fn free(&self) { + self.pivots.free(); + + if !self.polynomial.is_null() { + let poly_curve = Box::from_raw(self.polynomial as *mut PolynomialCurve); + poly_curve.free(); + } else if !self.mmr.is_null() { + let mmr_curve = Box::from_raw(self.mmr as *mut MMRCurve); + mmr_curve.free(); + } + } +} + +impl PolynomialCurve { + /// # Safety + /// The buffer pointers should be valid. + pub unsafe fn free(&self) { + self.poly_order_minus1.free(); + self.linear_interp_flag.free(); + self.poly_coef_int.free(); + self.poly_coef.free(); + } +} + +impl MMRCurve { + /// # Safety + /// The buffer pointers should be valid. + pub unsafe fn free(&self) { + self.mmr_order_minus1.free(); + self.mmr_constant_int.free(); + self.mmr_constant.free(); + self.mmr_coef_int.free(); + self.mmr_coef.free(); + } +} + +impl From<&DoviReshapingCurve> for ReshapingCurve { + fn from(curve: &DoviReshapingCurve) -> Self { + Self { + num_pivots_minus2: curve.num_pivots_minus2, + pivots: U16Data::from(curve.pivots.clone()), + mapping_idc: curve.mapping_idc as u8, + polynomial: curve.polynomial.as_ref().map_or(null_mut(), |poly_curve| { + Box::into_raw(Box::new(PolynomialCurve::from(poly_curve))) + }), + mmr: curve.mmr.as_ref().map_or(null_mut(), |mmr_curve| { + Box::into_raw(Box::new(MMRCurve::from(mmr_curve))) + }), + } + } +} + +impl From<&DoviPolynomialCurve> for PolynomialCurve { + fn from(poly_curve: &DoviPolynomialCurve) -> Self { + PolynomialCurve { + poly_order_minus1: U64Data::from(poly_curve.poly_order_minus1.clone()), + linear_interp_flag: Data::from(poly_curve.linear_interp_flag.clone()), + poly_coef_int: I64Data2D::from(poly_curve.poly_coef_int.clone()), + poly_coef: U64Data2D::from(poly_curve.poly_coef.clone()), + } + } +} + +impl From<&DoviMMRCurve> for MMRCurve { + fn from(mmr_curve: &DoviMMRCurve) -> Self { + MMRCurve { + mmr_order_minus1: Data::from(mmr_curve.mmr_order_minus1.clone()), + mmr_constant_int: I64Data::from(mmr_curve.mmr_constant_int.clone()), + mmr_constant: U64Data::from(mmr_curve.mmr_constant.clone()), + mmr_coef_int: I64Data3D::from(mmr_curve.mmr_coef_int.clone()), + mmr_coef: U64Data3D::from(mmr_curve.mmr_coef.clone()), } } } diff --git a/dolby_vision/src/c_structs/rpu_data_nlq.rs b/dolby_vision/src/c_structs/rpu_data_nlq.rs index ca26114..c094d75 100644 --- a/dolby_vision/src/c_structs/rpu_data_nlq.rs +++ b/dolby_vision/src/c_structs/rpu_data_nlq.rs @@ -1,55 +1,27 @@ -use crate::rpu::rpu_data_nlq::RpuDataNlq as RuRpuDataNlq; - -use super::buffers::*; +use crate::rpu::{rpu_data_nlq::RpuDataNlq as RuRpuDataNlq, NUM_COMPONENTS}; /// C struct for rpu_data_nlq() -/// -/// Here all the Data2D structs are of size N x 3. -/// Using dynamic buffers for convenience. #[repr(C)] pub struct RpuDataNlq { - num_nlq_param_predictors: U64Data2D, - nlq_param_pred_flag: Data2D, - diff_pred_part_idx_nlq_minus1: U64Data2D, - nlq_offset: U64Data2D, - vdr_in_max_int: U64Data2D, - vdr_in_max: U64Data2D, - linear_deadzone_slope_int: U64Data2D, - linear_deadzone_slope: U64Data2D, - linear_deadzone_threshold_int: U64Data2D, - linear_deadzone_threshold: U64Data2D, -} - -impl RpuDataNlq { - /// # Safety - /// The buffer pointers should be valid. - pub unsafe fn free(&self) { - self.num_nlq_param_predictors.free(); - self.nlq_param_pred_flag.free(); - self.diff_pred_part_idx_nlq_minus1.free(); - self.nlq_offset.free(); - self.vdr_in_max_int.free(); - self.vdr_in_max.free(); - self.linear_deadzone_slope_int.free(); - self.linear_deadzone_slope.free(); - self.linear_deadzone_threshold_int.free(); - self.linear_deadzone_threshold.free(); - } + nlq_offset: [u16; NUM_COMPONENTS], + vdr_in_max_int: [u64; NUM_COMPONENTS], + vdr_in_max: [u64; NUM_COMPONENTS], + linear_deadzone_slope_int: [u64; NUM_COMPONENTS], + linear_deadzone_slope: [u64; NUM_COMPONENTS], + linear_deadzone_threshold_int: [u64; NUM_COMPONENTS], + linear_deadzone_threshold: [u64; NUM_COMPONENTS], } impl From<&RuRpuDataNlq> for RpuDataNlq { fn from(data: &RuRpuDataNlq) -> Self { Self { - num_nlq_param_predictors: U64Data2D::from(&data.num_nlq_param_predictors), - nlq_param_pred_flag: Data2D::from(&data.nlq_param_pred_flag), - diff_pred_part_idx_nlq_minus1: U64Data2D::from(&data.diff_pred_part_idx_nlq_minus1), - nlq_offset: U64Data2D::from(&data.nlq_offset), - vdr_in_max_int: U64Data2D::from(&data.vdr_in_max_int), - vdr_in_max: U64Data2D::from(&data.vdr_in_max), - linear_deadzone_slope_int: U64Data2D::from(&data.linear_deadzone_slope_int), - linear_deadzone_slope: U64Data2D::from(&data.linear_deadzone_slope), - linear_deadzone_threshold_int: U64Data2D::from(&data.linear_deadzone_threshold_int), - linear_deadzone_threshold: U64Data2D::from(&data.linear_deadzone_threshold), + nlq_offset: data.nlq_offset, + vdr_in_max_int: data.vdr_in_max_int, + vdr_in_max: data.vdr_in_max, + linear_deadzone_slope_int: data.linear_deadzone_slope_int, + linear_deadzone_slope: data.linear_deadzone_slope, + linear_deadzone_threshold_int: data.linear_deadzone_threshold_int, + linear_deadzone_threshold: data.linear_deadzone_threshold, } } } diff --git a/dolby_vision/src/capi.rs b/dolby_vision/src/capi.rs index ae4db7c..d61eadb 100644 --- a/dolby_vision/src/capi.rs +++ b/dolby_vision/src/capi.rs @@ -88,7 +88,7 @@ pub unsafe extern "C" fn dovi_data_free(data: *const Data) { } /// # Safety -/// The struct pointer should be valid. +/// The struct pointer must be valid. /// /// Writes the encoded RPU as a byte buffer. /// If an error occurs in the writing, it is logged to RpuOpaque.error @@ -115,7 +115,7 @@ pub unsafe extern "C" fn dovi_write_rpu(ptr: *mut RpuOpaque) -> *const Data { } /// # Safety -/// The struct pointer should be valid. +/// The struct pointer must be valid. /// /// Writes the encoded RPU, escapes the bytes for HEVC and prepends the buffer with 0x7C01. /// If an error occurs in the writing, it is logged to RpuOpaque.error @@ -142,8 +142,8 @@ pub unsafe extern "C" fn dovi_write_unspec62_nalu(ptr: *mut RpuOpaque) -> *const } /// # Safety -/// The struct pointer should be valid. -/// The mode should be between 0 and 4. +/// The struct pointer must be valid. +/// The mode must be between 0 and 4. /// /// Converts the RPU to be compatible with a different Dolby Vision profile. /// Possible modes: @@ -195,8 +195,8 @@ pub unsafe extern "C" fn dovi_rpu_get_header(ptr: *const RpuOpaque) -> *const Rp if let Some(rpu) = &opaque.rpu { let mut header = RpuDataHeader::from(&rpu.header); - if let Some(subprofile) = rpu.subprofile { - header.subprofile = CString::new(subprofile).unwrap().into_raw(); + if let Some(el_type) = rpu.el_type.as_ref() { + header.el_type = CString::new(el_type.as_str()).unwrap().into_raw(); } Box::into_raw(Box::new(header)) @@ -252,41 +252,6 @@ pub unsafe extern "C" fn dovi_rpu_free_data_mapping(ptr: *const RpuDataMapping) } } -/// # Safety -/// The pointer to the opaque struct must be valid. -/// -/// Get the DoVi RpuDataNlq struct. -#[no_mangle] -pub unsafe extern "C" fn dovi_rpu_get_data_nlq(ptr: *const RpuOpaque) -> *const RpuDataNlq { - if ptr.is_null() { - return null_mut(); - } - - let opaque = &*ptr; - - if let Some(rpu) = &opaque.rpu { - if let Some(rpu_data_nlq) = &rpu.rpu_data_nlq { - Box::into_raw(Box::new(RpuDataNlq::from(rpu_data_nlq))) - } else { - null_mut() - } - } else { - null_mut() - } -} - -/// # Safety -/// The pointer to the struct must be valid. -/// -/// Frees the memory used by the RpuDataNlq struct. -#[no_mangle] -pub unsafe extern "C" fn dovi_rpu_free_data_nlq(ptr: *const RpuDataNlq) { - if !ptr.is_null() { - let rpu_data_nlq = Box::from_raw(ptr as *mut RpuDataNlq); - rpu_data_nlq.free(); - } -} - /// # Safety /// The pointer to the opaque struct must be valid. /// @@ -349,12 +314,7 @@ pub unsafe extern "C" fn dovi_parse_rpu_bin_file(path: *const c_char) -> *const let opaque_list: Vec<*mut RpuOpaque> = rpus .into_iter() - .map(|rpu| { - Box::into_raw(Box::new(RpuOpaque { - rpu: Some(rpu), - error: None, - })) - }) + .map(|rpu| Box::into_raw(Box::new(RpuOpaque::new(Some(rpu), None)))) .collect(); rpu_list.list = @@ -393,3 +353,58 @@ pub unsafe extern "C" fn dovi_rpu_list_free(ptr: *const RpuOpaqueList) { rpu_opaque_list.free(); } } + +/// # Safety +/// The struct pointer must be valid. +/// +/// Sets the L5 metadata active area offsets. +/// If there is no L5 block present, it is created with the offsets. +#[no_mangle] +pub unsafe extern "C" fn dovi_rpu_set_active_area_offsets( + ptr: *mut RpuOpaque, + left: u16, + right: u16, + top: u16, + bottom: u16, +) -> i32 { + if ptr.is_null() { + return -1; + } + + let opaque = &mut *ptr; + + if let Some(rpu) = &mut opaque.rpu { + match rpu.set_active_area_offsets(left, right, top, bottom) { + Ok(_) => 0, + Err(e) => { + opaque.error = Some( + CString::new(format!("Failed editing active area offsets: {}", e)).unwrap(), + ); + -1 + } + } + } else { + -1 + } +} + +/// # Safety +/// The struct pointer must be valid. +/// +/// Converts the existing reshaping/mapping to become no-op. +#[no_mangle] +pub unsafe extern "C" fn dovi_rpu_remove_mapping(ptr: *mut RpuOpaque) -> i32 { + if ptr.is_null() { + return -1; + } + + let opaque = &mut *ptr; + + if let Some(rpu) = &mut opaque.rpu { + rpu.remove_mapping(); + + 0 + } else { + -1 + } +} diff --git a/dolby_vision/src/rpu/dovi_rpu.rs b/dolby_vision/src/rpu/dovi_rpu.rs index 2baba9d..deb6184 100644 --- a/dolby_vision/src/rpu/dovi_rpu.rs +++ b/dolby_vision/src/rpu/dovi_rpu.rs @@ -1,24 +1,22 @@ -use anyhow::{bail, ensure, Result}; -use bitvec::prelude::*; -use bitvec_helpers::{bitslice_reader::BitSliceReader, bitvec_writer::BitVecWriter}; +use anyhow::{anyhow, bail, ensure, Result}; +use bitvec::prelude::{BitVec, Msb0}; +use bitvec_helpers::{ + bitstream_io_reader::BsIoSliceReader, bitstream_io_writer::BitstreamIoWriter, +}; #[cfg(feature = "serde")] use serde::Serialize; -use super::extension_metadata::blocks::{ - ExtMetadataBlock, ExtMetadataBlockLevel11, ExtMetadataBlockLevel5, ExtMetadataBlockLevel9, -}; -use super::extension_metadata::{CmV40DmData, DmData}; +use super::extension_metadata::blocks::{ExtMetadataBlock, ExtMetadataBlockLevel5}; use super::generate::GenerateConfig; use super::profiles::profile81::Profile81; use super::profiles::profile84::Profile84; -use super::rpu_data_header::{rpu_data_header, RpuDataHeader}; -use super::rpu_data_mapping::RpuDataMapping; -use super::rpu_data_nlq::RpuDataNlq; +use super::rpu_data_header::RpuDataHeader; +use super::rpu_data_mapping::{DoviNlqMethod, RpuDataMapping}; +use super::rpu_data_nlq::{DoviELType, RpuDataNlq}; use super::vdr_dm_data::VdrDmData; -use super::{compute_crc32, ConversionMode, FEL_STR, MEL_STR}; +use super::{compute_crc32, ConversionMode}; -use crate::rpu::rpu_data_mapping::vdr_rpu_data_payload; use crate::rpu::vdr_dm_data::vdr_dm_data_payload; use crate::utils::{ @@ -27,40 +25,37 @@ use crate::utils::{ const FINAL_BYTE: u8 = 0x80; -#[derive(Default, Debug, Clone)] +#[derive(Debug, Default, Clone)] #[cfg_attr(feature = "serde", derive(Serialize))] pub struct DoviRpu { pub dovi_profile: u8, #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))] - pub subprofile: Option<&'static str>, + pub el_type: Option, pub header: RpuDataHeader, #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))] pub rpu_data_mapping: Option, - #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))] - pub rpu_data_nlq: Option, - #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))] pub vdr_dm_data: Option, #[cfg_attr( feature = "serde", serde( - serialize_with = "crate::utils::bitvec_ser_bits", - skip_serializing_if = "BitVec::is_empty" + serialize_with = "crate::utils::opt_bitvec_ser_bits", + skip_serializing_if = "Option::is_none" ) )] - pub remaining: BitVec, + pub remaining: Option>, pub rpu_data_crc32: u32, #[cfg_attr(feature = "serde", serde(skip_serializing))] - trailing_zeroes: usize, + pub modified: bool, #[cfg_attr(feature = "serde", serde(skip_serializing))] - pub modified: bool, + trailing_zeroes: usize, #[cfg_attr(feature = "serde", serde(skip_serializing))] original_payload_size: usize, @@ -117,7 +112,7 @@ impl DoviRpu { bail!("Invalid RPU last byte: {}", last_byte); } - let mut dovi_rpu = DoviRpu::read_rpu_data(data, trailing_zeroes)?; + let dovi_rpu = DoviRpu::read_rpu_data(data, trailing_zeroes)?; if received_crc32 != dovi_rpu.rpu_data_crc32 { bail!( @@ -127,60 +122,76 @@ impl DoviRpu { ); } - dovi_rpu.dovi_profile = dovi_rpu.header.get_dovi_profile(); - dovi_rpu.subprofile = dovi_rpu.get_dovi_subprofile(); - Ok(dovi_rpu) } #[inline(always)] fn read_rpu_data(bytes: &[u8], trailing_zeroes: usize) -> Result { - let mut reader = BitSliceReader::new(bytes); - let mut dovi_rpu = DoviRpu { - trailing_zeroes, - original_payload_size: bytes.len(), - ..Default::default() - }; + let mut reader = BsIoSliceReader::from_slice(bytes); // CRC32 + 0x80 + trailing - let final_length = (8 * 4) + 8 + (trailing_zeroes * 8); + let final_length = (32 + 8 + (trailing_zeroes * 8)) as u64; - rpu_data_header(&mut dovi_rpu, &mut reader)?; + let header = RpuDataHeader::parse(&mut reader)?; // Preliminary header validation - dovi_rpu.dovi_profile = dovi_rpu.header.get_dovi_profile(); + let dovi_profile = header.get_dovi_profile(); + header.validate(dovi_profile)?; - dovi_rpu.header.validate(dovi_rpu.dovi_profile)?; + let rpu_data_mapping = if !header.use_prev_vdr_rpu_flag { + Some(RpuDataMapping::parse(&mut reader, &header)?) + } else { + None + }; - if dovi_rpu.header.rpu_type == 2 { - if !dovi_rpu.header.use_prev_vdr_rpu_flag { - vdr_rpu_data_payload(&mut dovi_rpu, &mut reader)?; - } + let el_type = rpu_data_mapping + .as_ref() + .map(|e| e.get_enhancement_layer_type()) + .unwrap_or(None); - if dovi_rpu.header.vdr_dm_metadata_present_flag { - vdr_dm_data_payload(&mut dovi_rpu, &mut reader, final_length)?; - } + let vdr_dm_data = if header.vdr_dm_metadata_present_flag { + Some(vdr_dm_data_payload(&mut reader, &header, final_length)?) + } else { + None + }; - // rpu_alignment_zero_bit - while !reader.is_aligned() { - ensure!(!reader.get()?, "rpu_alignment_zero_bit != 0"); - } + // rpu_alignment_zero_bit + while !reader.is_aligned() { + ensure!(!reader.get()?, "rpu_alignment_zero_bit != 0"); + } - // CRC32 is at the end, there can be more data in between - if reader.available() != final_length { - while reader.available() != final_length { - dovi_rpu.remaining.push(reader.get()?); - } + // CRC32 is at the end, there can be more data in between + + let remaining = if reader.available()? != final_length { + let mut remaining: BitVec = BitVec::new(); + + while reader.available()? != final_length { + remaining.push(reader.get()?); } - dovi_rpu.rpu_data_crc32 = reader.get_n(32)?; + Some(remaining) + } else { + None + }; - let last_byte: u8 = reader.get_n(8)?; - ensure!(last_byte == FINAL_BYTE, "last byte should be 0x80"); - } + let rpu_data_crc32 = reader.get_n(32)?; + let last_byte: u8 = reader.get_n(8)?; + ensure!(last_byte == FINAL_BYTE, "last byte should be 0x80"); + + let dovi_rpu = DoviRpu { + dovi_profile: header.get_dovi_profile(), + el_type, + header, + rpu_data_mapping, + vdr_dm_data, + remaining, + rpu_data_crc32, + modified: false, + trailing_zeroes, + original_payload_size: bytes.len(), + }; - // Update the profile and validate - dovi_rpu.dovi_profile = dovi_rpu.header.get_dovi_profile(); + // Validate dovi_rpu.validate()?; Ok(dovi_rpu) @@ -204,32 +215,40 @@ impl DoviRpu { #[inline(always)] fn write_rpu_data(&self) -> Result> { // Capacity is in bits - let mut writer = BitVecWriter::with_capacity(self.original_payload_size * 8); + let mut writer = BitstreamIoWriter::with_capacity(self.original_payload_size * 8); self.validate()?; let header = &self.header; - header.write_header(&mut writer); + header.write_header(&mut writer)?; if header.rpu_type == 2 { if !header.use_prev_vdr_rpu_flag { - self.write_vdr_rpu_data_payload(&mut writer)?; + if let Some(mapping) = &self.rpu_data_mapping { + mapping.write(&mut writer, &self.header)?; + } } if header.vdr_dm_metadata_present_flag { - self.write_vdr_dm_data_payload(&mut writer)?; + if let Some(vdr_dm_data) = &self.vdr_dm_data { + vdr_dm_data.write(&mut writer)?; + } } } - if !self.remaining.is_empty() { - self.remaining.iter().for_each(|b| writer.write(*b)); + if let Some(remaining) = &self.remaining { + for b in remaining { + writer.write(*b)?; + } } - while !writer.is_aligned() { - writer.write(false); - } + writer.byte_align()?; - let computed_crc32 = compute_crc32(&writer.as_slice()[1..]); + let computed_crc32 = compute_crc32( + &writer + .as_slice() + .ok_or_else(|| anyhow!("Unaligned bytes"))?[1..], + ); if !self.modified { // Validate the parsed crc32 is the same @@ -240,40 +259,29 @@ impl DoviRpu { } // Write crc32 - writer.write_n(&computed_crc32.to_be_bytes(), 32); - writer.write_n(&[0x80], 8); + writer.write_n(&computed_crc32, 32)?; + writer.write_n(&0x80_u8, 8)?; // Trailing bytes if self.trailing_zeroes > 0 { - (0..self.trailing_zeroes).for_each(|_| writer.write_n(&[0], 8)); - } - - Ok(writer.as_slice().to_owned()) - } - - fn write_vdr_rpu_data_payload(&self, writer: &mut BitVecWriter) -> Result<()> { - if let Some(ref rpu_data_mapping) = self.rpu_data_mapping { - rpu_data_mapping.write(writer, &self.header)?; - } - - if let Some(ref rpu_data_nlq) = self.rpu_data_nlq { - rpu_data_nlq.write(writer, &self.header)?; - } - - Ok(()) - } - - fn write_vdr_dm_data_payload(&self, writer: &mut BitVecWriter) -> Result<()> { - if let Some(ref vdr_dm_data) = self.vdr_dm_data { - vdr_dm_data.write(writer)?; + for _ in 0..self.trailing_zeroes { + writer.write_n(&0_u8, 8)?; + } } - Ok(()) + Ok(writer + .as_slice() + .ok_or_else(|| anyhow!("Unaligned bytes"))? + .to_owned()) } fn validate(&self) -> Result<()> { self.header.validate(self.dovi_profile)?; + if let Some(mapping) = self.rpu_data_mapping.as_ref() { + mapping.validate(self.dovi_profile)?; + } + if let Some(vdr_dm_data) = &self.vdr_dm_data { vdr_dm_data.validate()?; } @@ -281,16 +289,11 @@ impl DoviRpu { Ok(()) } - fn get_dovi_subprofile(&self) -> Option<&'static str> { - if self.dovi_profile == 7 { - if let Some(nlq) = &self.rpu_data_nlq { - let subprofile = if nlq.is_mel() { MEL_STR } else { FEL_STR }; - - return Some(subprofile); - } - } - - None + pub fn get_enhancement_layer_type(&self) -> Option { + self.rpu_data_mapping + .as_ref() + .map(|e| e.get_enhancement_layer_type()) + .unwrap_or(None) } /// Modes: @@ -341,7 +344,7 @@ impl DoviRpu { // Update profile value self.dovi_profile = self.header.get_dovi_profile(); - self.subprofile = self.get_dovi_subprofile(); + self.el_type = self.get_enhancement_layer_type(); Ok(()) } @@ -352,18 +355,20 @@ impl DoviRpu { header.el_spatial_resampling_filter_flag = true; header.disable_residual_flag = false; - header.nlq_method_idc = Some(0); - header.nlq_num_pivots_minus2 = Some(0); + if let Some(mapping) = self.rpu_data_mapping.as_mut() { + mapping.nlq_method_idc = Some(DoviNlqMethod::LinearDeadzone); + mapping.nlq_num_pivots_minus2 = Some(0); - // BL is always 10 bit in current spec - header.nlq_pred_pivot_value = Some([0, 1023]); + // BL is always 10 bit in current spec + mapping.nlq_pred_pivot_value = Some([0, 1023]); - if let Some(ref mut rpu_data_nlq) = self.rpu_data_nlq { - rpu_data_nlq.convert_to_mel(); - } else if self.dovi_profile == 8 { - self.rpu_data_nlq = Some(RpuDataNlq::mel_default()); - } else { - bail!("Not profile 7 or 8, cannot convert to MEL!"); + if let Some(ref mut nlq) = mapping.nlq.as_mut() { + nlq.convert_to_mel(); + } else if self.dovi_profile == 8 { + mapping.nlq = Some(RpuDataNlq::mel_default()); + } else { + bail!("Not profile 7 or 8, cannot convert to MEL!"); + } } Ok(()) @@ -378,14 +383,16 @@ impl DoviRpu { header.el_spatial_resampling_filter_flag = false; header.disable_residual_flag = true; - header.nlq_method_idc = None; - header.nlq_num_pivots_minus2 = None; - header.nlq_pred_pivot_value = None; + if let Some(mapping) = self.rpu_data_mapping.as_mut() { + mapping.nlq_method_idc = None; + mapping.nlq_num_pivots_minus2 = None; + mapping.nlq_pred_pivot_value = None; - header.num_x_partitions_minus1 = 0; - header.num_y_partitions_minus1 = 0; + mapping.num_x_partitions_minus1 = 0; + mapping.num_y_partitions_minus1 = 0; - self.rpu_data_nlq = None; + mapping.nlq = None; + } if let Some(ref mut vdr_dm_data) = self.vdr_dm_data { vdr_dm_data.set_p81_coeffs(); @@ -421,7 +428,6 @@ impl DoviRpu { modified: true, header: RpuDataHeader::p5_default(), rpu_data_mapping: Some(Profile81::rpu_data_mapping()), - rpu_data_nlq: None, vdr_dm_data: Some(VdrDmData::from_generate_config(config)?), ..Default::default() }) @@ -433,7 +439,6 @@ impl DoviRpu { modified: true, header: RpuDataHeader::p8_default(), rpu_data_mapping: Some(Profile81::rpu_data_mapping()), - rpu_data_nlq: None, vdr_dm_data: Some(VdrDmData::from_generate_config(config)?), ..Default::default() }) @@ -453,14 +458,35 @@ impl DoviRpu { Ok(()) } + pub fn set_active_area_offsets( + &mut self, + left: u16, + right: u16, + top: u16, + bottom: u16, + ) -> Result<()> { + self.modified = true; + + if let Some(ref mut vdr_dm_data) = self.vdr_dm_data { + vdr_dm_data.replace_metadata_block(ExtMetadataBlock::Level5( + ExtMetadataBlockLevel5::from_offsets(left, right, top, bottom), + ))?; + } + + Ok(()) + } + pub fn remove_mapping(&mut self) { self.modified = true; - self.header.num_pivots_minus_2 = [0, 0, 0]; - self.header.pred_pivot_value.iter_mut().for_each(|v| { - v.clear(); - v.extend([0, 1023]); - }); + if let Some(mapping) = self.rpu_data_mapping.as_mut() { + mapping.curves.iter_mut().for_each(|e| { + e.num_pivots_minus2 = 0; + + e.pivots.clear(); + e.pivots.extend([0, 1023]); + }); + } if let Some(ref mut rpu_data_mapping) = self.rpu_data_mapping { rpu_data_mapping.set_empty_p81_mapping(); @@ -474,37 +500,12 @@ 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() { - self.modified = true; - - vdr_dm_data.cmv40_metadata = Some(DmData::V40(CmV40DmData::new_with_l254_402())); - - // Defaults - vdr_dm_data.add_metadata_block(ExtMetadataBlock::Level9( - ExtMetadataBlockLevel9::default_dci_p3(), - ))?; - vdr_dm_data.add_metadata_block(ExtMetadataBlock::Level11( - ExtMetadataBlockLevel11::default_reference_cinema(), - ))?; - } - } - - Ok(()) - } - pub fn profile84_config(config: &GenerateConfig) -> Result { Ok(DoviRpu { dovi_profile: 8, modified: true, - header: Profile84::rpu_data_header(), + header: RpuDataHeader::p8_default(), rpu_data_mapping: Some(Profile84::rpu_data_mapping()), - rpu_data_nlq: None, vdr_dm_data: Some(VdrDmData::from_generate_config(config)?), ..Default::default() }) @@ -513,7 +514,7 @@ impl DoviRpu { fn convert_to_p84(&mut self) { self.convert_to_p81(); - self.header = Profile84::rpu_data_header(); + self.header = RpuDataHeader::p8_default(); self.rpu_data_mapping = Some(Profile84::rpu_data_mapping()); } diff --git a/dolby_vision/src/rpu/extension_metadata/blocks/level1.rs b/dolby_vision/src/rpu/extension_metadata/blocks/level1.rs index a71d69c..077e062 100644 --- a/dolby_vision/src/rpu/extension_metadata/blocks/level1.rs +++ b/dolby_vision/src/rpu/extension_metadata/blocks/level1.rs @@ -1,5 +1,7 @@ use anyhow::{ensure, Result}; -use bitvec_helpers::{bitslice_reader::BitSliceReader, bitvec_writer::BitVecWriter}; +use bitvec_helpers::{ + bitstream_io_reader::BsIoSliceReader, bitstream_io_writer::BitstreamIoWriter, +}; #[cfg(feature = "serde")] use serde::{Deserialize, Serialize}; @@ -16,6 +18,7 @@ pub const L1_MAX_PQ_MIN_VALUE: u16 = 2081; pub const L1_MAX_PQ_MAX_VALUE: u16 = 4095; /// cbindgen:ignore pub const L1_AVG_PQ_MIN_VALUE: u16 = 819; +/// cbindgen:ignore pub const L1_AVG_PQ_MIN_VALUE_CMV40: u16 = 1229; /// Statistical analysis of the frame: min, max, avg brightness. @@ -29,7 +32,7 @@ pub struct ExtMetadataBlockLevel1 { } impl ExtMetadataBlockLevel1 { - pub(crate) fn parse(reader: &mut BitSliceReader) -> Result { + pub(crate) fn parse(reader: &mut BsIoSliceReader) -> Result { Ok(ExtMetadataBlock::Level1(Self { min_pq: reader.get_n(12)?, max_pq: reader.get_n(12)?, @@ -37,12 +40,12 @@ impl ExtMetadataBlockLevel1 { })) } - pub fn write(&self, writer: &mut BitVecWriter) -> Result<()> { + pub fn write(&self, writer: &mut BitstreamIoWriter) -> Result<()> { self.validate()?; - writer.write_n(&self.min_pq.to_be_bytes(), 12); - writer.write_n(&self.max_pq.to_be_bytes(), 12); - writer.write_n(&self.avg_pq.to_be_bytes(), 12); + writer.write_n(&self.min_pq, 12)?; + writer.write_n(&self.max_pq, 12)?; + writer.write_n(&self.avg_pq, 12)?; Ok(()) } @@ -90,22 +93,6 @@ impl ExtMetadataBlockLevel1 { pub fn clamp_values_cm_version(&mut self, cm_version: CmVersion) { self.clamp_values_int(cm_version); } - - #[deprecated( - since = "1.7.1", - note = "Replaced by `from_stats_cm_version` with CmVersion::V29" - )] - pub fn from_stats(min_pq: u16, max_pq: u16, avg_pq: u16) -> ExtMetadataBlockLevel1 { - Self::from_stats_cm_version(min_pq, max_pq, avg_pq, CmVersion::V29) - } - - #[deprecated( - since = "1.7.1", - note = "Replaced by `clamp_values_cm_version` with CmVersion::V29" - )] - pub fn clamp_values(&mut self) { - self.clamp_values_int(CmVersion::V29); - } } impl ExtMetadataBlockInfo for ExtMetadataBlockLevel1 { diff --git a/dolby_vision/src/rpu/extension_metadata/blocks/level10.rs b/dolby_vision/src/rpu/extension_metadata/blocks/level10.rs index 78eecf2..6a67204 100644 --- a/dolby_vision/src/rpu/extension_metadata/blocks/level10.rs +++ b/dolby_vision/src/rpu/extension_metadata/blocks/level10.rs @@ -1,5 +1,7 @@ use anyhow::{ensure, Result}; -use bitvec_helpers::{bitslice_reader::BitSliceReader, bitvec_writer::BitVecWriter}; +use bitvec_helpers::{ + bitstream_io_reader::BsIoSliceReader, bitstream_io_writer::BitstreamIoWriter, +}; #[cfg(feature = "serde")] use serde::{ser::SerializeStruct, Deserialize, Serialize, Serializer}; @@ -37,7 +39,7 @@ pub struct ExtMetadataBlockLevel10 { } impl ExtMetadataBlockLevel10 { - pub(crate) fn parse(reader: &mut BitSliceReader, length: u64) -> Result { + pub(crate) fn parse(reader: &mut BsIoSliceReader, length: u64) -> Result { let mut block = Self { length, target_display_index: reader.get_n(8)?, @@ -61,23 +63,23 @@ impl ExtMetadataBlockLevel10 { Ok(ExtMetadataBlock::Level10(block)) } - pub fn write(&self, writer: &mut BitVecWriter) -> Result<()> { + pub fn write(&self, writer: &mut BitstreamIoWriter) -> Result<()> { self.validate()?; - writer.write_n(&self.target_display_index.to_be_bytes(), 8); - writer.write_n(&self.target_max_pq.to_be_bytes(), 12); - writer.write_n(&self.target_min_pq.to_be_bytes(), 12); - writer.write_n(&self.target_primary_index.to_be_bytes(), 8); + writer.write_n(&self.target_display_index, 8)?; + writer.write_n(&self.target_max_pq, 12)?; + writer.write_n(&self.target_min_pq, 12)?; + writer.write_n(&self.target_primary_index, 8)?; if self.length > 5 { - writer.write_n(&self.target_primary_red_x.to_be_bytes(), 16); - writer.write_n(&self.target_primary_red_y.to_be_bytes(), 16); - writer.write_n(&self.target_primary_green_x.to_be_bytes(), 16); - writer.write_n(&self.target_primary_green_y.to_be_bytes(), 16); - writer.write_n(&self.target_primary_blue_x.to_be_bytes(), 16); - writer.write_n(&self.target_primary_blue_y.to_be_bytes(), 16); - writer.write_n(&self.target_primary_white_x.to_be_bytes(), 16); - writer.write_n(&self.target_primary_white_y.to_be_bytes(), 16); + writer.write_n(&self.target_primary_red_x, 16)?; + writer.write_n(&self.target_primary_red_y, 16)?; + writer.write_n(&self.target_primary_green_x, 16)?; + writer.write_n(&self.target_primary_green_y, 16)?; + writer.write_n(&self.target_primary_blue_x, 16)?; + writer.write_n(&self.target_primary_blue_y, 16)?; + writer.write_n(&self.target_primary_white_x, 16)?; + writer.write_n(&self.target_primary_white_y, 16)?; } Ok(()) diff --git a/dolby_vision/src/rpu/extension_metadata/blocks/level11.rs b/dolby_vision/src/rpu/extension_metadata/blocks/level11.rs index 30d96e9..d104b0b 100644 --- a/dolby_vision/src/rpu/extension_metadata/blocks/level11.rs +++ b/dolby_vision/src/rpu/extension_metadata/blocks/level11.rs @@ -1,5 +1,7 @@ use anyhow::{ensure, Result}; -use bitvec_helpers::{bitslice_reader::BitSliceReader, bitvec_writer::BitVecWriter}; +use bitvec_helpers::{ + bitstream_io_reader::BsIoSliceReader, bitstream_io_writer::BitstreamIoWriter, +}; #[cfg(feature = "serde")] use serde::{Deserialize, Serialize}; @@ -24,7 +26,7 @@ pub struct ExtMetadataBlockLevel11 { } impl ExtMetadataBlockLevel11 { - pub(crate) fn parse(reader: &mut BitSliceReader) -> Result { + pub(crate) fn parse(reader: &mut BsIoSliceReader) -> Result { let mut l11 = Self { content_type: reader.get_n(8)?, whitepoint: reader.get_n(8)?, @@ -41,7 +43,7 @@ impl ExtMetadataBlockLevel11 { Ok(ExtMetadataBlock::Level11(l11)) } - pub fn write(&self, writer: &mut BitVecWriter) -> Result<()> { + pub fn write(&self, writer: &mut BitstreamIoWriter) -> Result<()> { self.validate()?; let mut wp = self.whitepoint; @@ -50,10 +52,10 @@ impl ExtMetadataBlockLevel11 { wp += MAX_WHITEPOINT_VALUE + 1 } - writer.write_n(&self.content_type.to_be_bytes(), 8); - writer.write_n(&wp.to_be_bytes(), 8); - writer.write_n(&self.reserved_byte2.to_be_bytes(), 8); - writer.write_n(&self.reserved_byte3.to_be_bytes(), 8); + writer.write_n(&self.content_type, 8)?; + writer.write_n(&wp, 8)?; + writer.write_n(&self.reserved_byte2, 8)?; + writer.write_n(&self.reserved_byte3, 8)?; Ok(()) } diff --git a/dolby_vision/src/rpu/extension_metadata/blocks/level2.rs b/dolby_vision/src/rpu/extension_metadata/blocks/level2.rs index 0afbd43..c886b02 100644 --- a/dolby_vision/src/rpu/extension_metadata/blocks/level2.rs +++ b/dolby_vision/src/rpu/extension_metadata/blocks/level2.rs @@ -1,5 +1,7 @@ use anyhow::{ensure, Result}; -use bitvec_helpers::{bitslice_reader::BitSliceReader, bitvec_writer::BitVecWriter}; +use bitvec_helpers::{ + bitstream_io_reader::BsIoSliceReader, bitstream_io_writer::BitstreamIoWriter, +}; #[cfg(feature = "serde")] use serde::{Deserialize, Serialize}; @@ -25,7 +27,7 @@ pub struct ExtMetadataBlockLevel2 { } impl ExtMetadataBlockLevel2 { - pub(crate) fn parse(reader: &mut BitSliceReader) -> Result { + pub(crate) fn parse(reader: &mut BsIoSliceReader) -> Result { let mut level2 = Self { target_max_pq: reader.get_n(12)?, trim_slope: reader.get_n(12)?, @@ -43,16 +45,16 @@ impl ExtMetadataBlockLevel2 { Ok(ExtMetadataBlock::Level2(level2)) } - pub fn write(&self, writer: &mut BitVecWriter) -> Result<()> { + pub fn write(&self, writer: &mut BitstreamIoWriter) -> Result<()> { self.validate()?; - writer.write_n(&self.target_max_pq.to_be_bytes(), 12); - writer.write_n(&self.trim_slope.to_be_bytes(), 12); - writer.write_n(&self.trim_offset.to_be_bytes(), 12); - writer.write_n(&self.trim_power.to_be_bytes(), 12); - writer.write_n(&self.trim_chroma_weight.to_be_bytes(), 12); - writer.write_n(&self.trim_saturation_gain.to_be_bytes(), 12); - writer.write_n(&self.ms_weight.to_be_bytes(), 13); + writer.write_n(&self.target_max_pq, 12)?; + writer.write_n(&self.trim_slope, 12)?; + writer.write_n(&self.trim_offset, 12)?; + writer.write_n(&self.trim_power, 12)?; + writer.write_n(&self.trim_chroma_weight, 12)?; + writer.write_n(&self.trim_saturation_gain, 12)?; + writer.write_signed_n(&self.ms_weight, 13)?; Ok(()) } diff --git a/dolby_vision/src/rpu/extension_metadata/blocks/level254.rs b/dolby_vision/src/rpu/extension_metadata/blocks/level254.rs index 0ef98e7..8cac4dd 100644 --- a/dolby_vision/src/rpu/extension_metadata/blocks/level254.rs +++ b/dolby_vision/src/rpu/extension_metadata/blocks/level254.rs @@ -1,5 +1,7 @@ use anyhow::Result; -use bitvec_helpers::{bitslice_reader::BitSliceReader, bitvec_writer::BitVecWriter}; +use bitvec_helpers::{ + bitstream_io_reader::BsIoSliceReader, bitstream_io_writer::BitstreamIoWriter, +}; #[cfg(feature = "serde")] use serde::{Deserialize, Serialize}; @@ -17,16 +19,16 @@ pub struct ExtMetadataBlockLevel254 { } impl ExtMetadataBlockLevel254 { - pub(crate) fn parse(reader: &mut BitSliceReader) -> Result { + pub(crate) fn parse(reader: &mut BsIoSliceReader) -> Result { Ok(ExtMetadataBlock::Level254(Self { dm_mode: reader.get_n(8)?, dm_version_index: reader.get_n(8)?, })) } - pub fn write(&self, writer: &mut BitVecWriter) -> Result<()> { - writer.write_n(&self.dm_mode.to_be_bytes(), 8); - writer.write_n(&self.dm_version_index.to_be_bytes(), 8); + pub fn write(&self, writer: &mut BitstreamIoWriter) -> Result<()> { + writer.write_n(&self.dm_mode, 8)?; + writer.write_n(&self.dm_version_index, 8)?; Ok(()) } diff --git a/dolby_vision/src/rpu/extension_metadata/blocks/level255.rs b/dolby_vision/src/rpu/extension_metadata/blocks/level255.rs index bef299d..7f50645 100644 --- a/dolby_vision/src/rpu/extension_metadata/blocks/level255.rs +++ b/dolby_vision/src/rpu/extension_metadata/blocks/level255.rs @@ -1,5 +1,7 @@ use anyhow::Result; -use bitvec_helpers::{bitslice_reader::BitSliceReader, bitvec_writer::BitVecWriter}; +use bitvec_helpers::{ + bitstream_io_reader::BsIoSliceReader, bitstream_io_writer::BitstreamIoWriter, +}; #[cfg(feature = "serde")] use serde::{Deserialize, Serialize}; @@ -22,7 +24,7 @@ pub struct ExtMetadataBlockLevel255 { } impl ExtMetadataBlockLevel255 { - pub(crate) fn parse(reader: &mut BitSliceReader) -> Result { + pub(crate) fn parse(reader: &mut BsIoSliceReader) -> Result { Ok(ExtMetadataBlock::Level255(Self { dm_run_mode: reader.get_n(8)?, dm_run_version: reader.get_n(8)?, @@ -33,13 +35,13 @@ impl ExtMetadataBlockLevel255 { })) } - pub fn write(&self, writer: &mut BitVecWriter) -> Result<()> { - writer.write_n(&self.dm_run_mode.to_be_bytes(), 8); - writer.write_n(&self.dm_run_version.to_be_bytes(), 8); - writer.write_n(&self.dm_debug0.to_be_bytes(), 8); - writer.write_n(&self.dm_debug1.to_be_bytes(), 8); - writer.write_n(&self.dm_debug2.to_be_bytes(), 8); - writer.write_n(&self.dm_debug3.to_be_bytes(), 8); + pub fn write(&self, writer: &mut BitstreamIoWriter) -> Result<()> { + writer.write_n(&self.dm_run_mode, 8)?; + writer.write_n(&self.dm_run_version, 8)?; + writer.write_n(&self.dm_debug0, 8)?; + writer.write_n(&self.dm_debug1, 8)?; + writer.write_n(&self.dm_debug2, 8)?; + writer.write_n(&self.dm_debug3, 8)?; Ok(()) } diff --git a/dolby_vision/src/rpu/extension_metadata/blocks/level3.rs b/dolby_vision/src/rpu/extension_metadata/blocks/level3.rs index a65f0fc..c96b249 100644 --- a/dolby_vision/src/rpu/extension_metadata/blocks/level3.rs +++ b/dolby_vision/src/rpu/extension_metadata/blocks/level3.rs @@ -1,5 +1,7 @@ use anyhow::{ensure, Result}; -use bitvec_helpers::{bitslice_reader::BitSliceReader, bitvec_writer::BitVecWriter}; +use bitvec_helpers::{ + bitstream_io_reader::BsIoSliceReader, bitstream_io_writer::BitstreamIoWriter, +}; #[cfg(feature = "serde")] use serde::{Deserialize, Serialize}; @@ -18,7 +20,7 @@ pub struct ExtMetadataBlockLevel3 { } impl ExtMetadataBlockLevel3 { - pub(crate) fn parse(reader: &mut BitSliceReader) -> Result { + pub(crate) fn parse(reader: &mut BsIoSliceReader) -> Result { Ok(ExtMetadataBlock::Level3(Self { min_pq_offset: reader.get_n(12)?, max_pq_offset: reader.get_n(12)?, @@ -26,12 +28,12 @@ impl ExtMetadataBlockLevel3 { })) } - pub fn write(&self, writer: &mut BitVecWriter) -> Result<()> { + pub fn write(&self, writer: &mut BitstreamIoWriter) -> Result<()> { self.validate()?; - writer.write_n(&self.min_pq_offset.to_be_bytes(), 12); - writer.write_n(&self.max_pq_offset.to_be_bytes(), 12); - writer.write_n(&self.avg_pq_offset.to_be_bytes(), 12); + writer.write_n(&self.min_pq_offset, 12)?; + writer.write_n(&self.max_pq_offset, 12)?; + writer.write_n(&self.avg_pq_offset, 12)?; Ok(()) } diff --git a/dolby_vision/src/rpu/extension_metadata/blocks/level4.rs b/dolby_vision/src/rpu/extension_metadata/blocks/level4.rs index 7e470ba..c2afd18 100644 --- a/dolby_vision/src/rpu/extension_metadata/blocks/level4.rs +++ b/dolby_vision/src/rpu/extension_metadata/blocks/level4.rs @@ -1,5 +1,7 @@ use anyhow::{ensure, Result}; -use bitvec_helpers::{bitslice_reader::BitSliceReader, bitvec_writer::BitVecWriter}; +use bitvec_helpers::{ + bitstream_io_reader::BsIoSliceReader, bitstream_io_writer::BitstreamIoWriter, +}; #[cfg(feature = "serde")] use serde::{Deserialize, Serialize}; @@ -16,18 +18,18 @@ pub struct ExtMetadataBlockLevel4 { } impl ExtMetadataBlockLevel4 { - pub(crate) fn parse(reader: &mut BitSliceReader) -> Result { + pub(crate) fn parse(reader: &mut BsIoSliceReader) -> Result { Ok(ExtMetadataBlock::Level4(Self { anchor_pq: reader.get_n(12)?, anchor_power: reader.get_n(12)?, })) } - pub fn write(&self, writer: &mut BitVecWriter) -> Result<()> { + pub fn write(&self, writer: &mut BitstreamIoWriter) -> Result<()> { self.validate()?; - writer.write_n(&self.anchor_pq.to_be_bytes(), 12); - writer.write_n(&self.anchor_power.to_be_bytes(), 12); + writer.write_n(&self.anchor_pq, 12)?; + writer.write_n(&self.anchor_power, 12)?; Ok(()) } diff --git a/dolby_vision/src/rpu/extension_metadata/blocks/level5.rs b/dolby_vision/src/rpu/extension_metadata/blocks/level5.rs index 2f62f9c..e815ac0 100644 --- a/dolby_vision/src/rpu/extension_metadata/blocks/level5.rs +++ b/dolby_vision/src/rpu/extension_metadata/blocks/level5.rs @@ -1,5 +1,7 @@ use anyhow::{ensure, Result}; -use bitvec_helpers::{bitslice_reader::BitSliceReader, bitvec_writer::BitVecWriter}; +use bitvec_helpers::{ + bitstream_io_reader::BsIoSliceReader, bitstream_io_writer::BitstreamIoWriter, +}; #[cfg(feature = "serde")] use serde::{Deserialize, Serialize}; @@ -20,7 +22,7 @@ pub struct ExtMetadataBlockLevel5 { } impl ExtMetadataBlockLevel5 { - pub(crate) fn parse(reader: &mut BitSliceReader) -> Result { + pub(crate) fn parse(reader: &mut BsIoSliceReader) -> Result { Ok(ExtMetadataBlock::Level5(Self { active_area_left_offset: reader.get_n(13)?, active_area_right_offset: reader.get_n(13)?, @@ -29,13 +31,13 @@ impl ExtMetadataBlockLevel5 { })) } - pub fn write(&self, writer: &mut BitVecWriter) -> Result<()> { + pub fn write(&self, writer: &mut BitstreamIoWriter) -> Result<()> { self.validate()?; - writer.write_n(&self.active_area_left_offset.to_be_bytes(), 13); - writer.write_n(&self.active_area_right_offset.to_be_bytes(), 13); - writer.write_n(&self.active_area_top_offset.to_be_bytes(), 13); - writer.write_n(&self.active_area_bottom_offset.to_be_bytes(), 13); + writer.write_n(&self.active_area_left_offset, 13)?; + writer.write_n(&self.active_area_right_offset, 13)?; + writer.write_n(&self.active_area_top_offset, 13)?; + writer.write_n(&self.active_area_bottom_offset, 13)?; Ok(()) } diff --git a/dolby_vision/src/rpu/extension_metadata/blocks/level6.rs b/dolby_vision/src/rpu/extension_metadata/blocks/level6.rs index 4f9f1cf..b75c97c 100644 --- a/dolby_vision/src/rpu/extension_metadata/blocks/level6.rs +++ b/dolby_vision/src/rpu/extension_metadata/blocks/level6.rs @@ -1,5 +1,7 @@ use anyhow::{ensure, Result}; -use bitvec_helpers::{bitslice_reader::BitSliceReader, bitvec_writer::BitVecWriter}; +use bitvec_helpers::{ + bitstream_io_reader::BsIoSliceReader, bitstream_io_writer::BitstreamIoWriter, +}; #[cfg(feature = "serde")] use serde::{Deserialize, Serialize}; @@ -21,7 +23,7 @@ pub struct ExtMetadataBlockLevel6 { } impl ExtMetadataBlockLevel6 { - pub(crate) fn parse(reader: &mut BitSliceReader) -> Result { + pub(crate) fn parse(reader: &mut BsIoSliceReader) -> Result { Ok(ExtMetadataBlock::Level6(Self { max_display_mastering_luminance: reader.get_n(16)?, min_display_mastering_luminance: reader.get_n(16)?, @@ -30,13 +32,13 @@ impl ExtMetadataBlockLevel6 { })) } - pub fn write(&self, writer: &mut BitVecWriter) -> Result<()> { + pub fn write(&self, writer: &mut BitstreamIoWriter) -> Result<()> { self.validate()?; - writer.write_n(&self.max_display_mastering_luminance.to_be_bytes(), 16); - writer.write_n(&self.min_display_mastering_luminance.to_be_bytes(), 16); - writer.write_n(&self.max_content_light_level.to_be_bytes(), 16); - writer.write_n(&self.max_frame_average_light_level.to_be_bytes(), 16); + writer.write_n(&self.max_display_mastering_luminance, 16)?; + writer.write_n(&self.min_display_mastering_luminance, 16)?; + writer.write_n(&self.max_content_light_level, 16)?; + writer.write_n(&self.max_frame_average_light_level, 16)?; Ok(()) } diff --git a/dolby_vision/src/rpu/extension_metadata/blocks/level8.rs b/dolby_vision/src/rpu/extension_metadata/blocks/level8.rs index 379ad16..07c799b 100644 --- a/dolby_vision/src/rpu/extension_metadata/blocks/level8.rs +++ b/dolby_vision/src/rpu/extension_metadata/blocks/level8.rs @@ -1,5 +1,7 @@ use anyhow::{ensure, Result}; -use bitvec_helpers::{bitslice_reader::BitSliceReader, bitvec_writer::BitVecWriter}; +use bitvec_helpers::{ + bitstream_io_reader::BsIoSliceReader, bitstream_io_writer::BitstreamIoWriter, +}; #[cfg(feature = "serde")] use serde::{ser::SerializeStruct, Deserialize, Serialize, Serializer}; @@ -51,7 +53,7 @@ pub struct ExtMetadataBlockLevel8 { } impl ExtMetadataBlockLevel8 { - pub(crate) fn parse(reader: &mut BitSliceReader, length: u64) -> Result { + pub(crate) fn parse(reader: &mut BsIoSliceReader, length: u64) -> Result { let mut block = Self { length, target_display_index: reader.get_n(8)?, @@ -93,42 +95,42 @@ impl ExtMetadataBlockLevel8 { Ok(ExtMetadataBlock::Level8(block)) } - pub fn write(&self, writer: &mut BitVecWriter) -> Result<()> { + pub fn write(&self, writer: &mut BitstreamIoWriter) -> Result<()> { self.validate()?; - writer.write_n(&self.target_display_index.to_be_bytes(), 8); - writer.write_n(&self.trim_slope.to_be_bytes(), 12); - writer.write_n(&self.trim_offset.to_be_bytes(), 12); - writer.write_n(&self.trim_power.to_be_bytes(), 12); - writer.write_n(&self.trim_chroma_weight.to_be_bytes(), 12); - writer.write_n(&self.trim_saturation_gain.to_be_bytes(), 12); - writer.write_n(&self.ms_weight.to_be_bytes(), 12); + writer.write_n(&self.target_display_index, 8)?; + writer.write_n(&self.trim_slope, 12)?; + writer.write_n(&self.trim_offset, 12)?; + writer.write_n(&self.trim_power, 12)?; + writer.write_n(&self.trim_chroma_weight, 12)?; + writer.write_n(&self.trim_saturation_gain, 12)?; + writer.write_n(&self.ms_weight, 12)?; // Write default values when the fields can not be omitted if self.length > 10 { - writer.write_n(&self.target_mid_contrast.to_be_bytes(), 12); + writer.write_n(&self.target_mid_contrast, 12)?; } if self.length > 12 { - writer.write_n(&self.clip_trim.to_be_bytes(), 12); + writer.write_n(&self.clip_trim, 12)?; } if self.length > 13 { - writer.write_n(&self.saturation_vector_field0.to_be_bytes(), 8); - writer.write_n(&self.saturation_vector_field1.to_be_bytes(), 8); - writer.write_n(&self.saturation_vector_field2.to_be_bytes(), 8); - writer.write_n(&self.saturation_vector_field3.to_be_bytes(), 8); - writer.write_n(&self.saturation_vector_field4.to_be_bytes(), 8); - writer.write_n(&self.saturation_vector_field5.to_be_bytes(), 8); + writer.write_n(&self.saturation_vector_field0, 8)?; + writer.write_n(&self.saturation_vector_field1, 8)?; + writer.write_n(&self.saturation_vector_field2, 8)?; + writer.write_n(&self.saturation_vector_field3, 8)?; + writer.write_n(&self.saturation_vector_field4, 8)?; + writer.write_n(&self.saturation_vector_field5, 8)?; } if self.length > 19 { - writer.write_n(&self.hue_vector_field0.to_be_bytes(), 8); - writer.write_n(&self.hue_vector_field1.to_be_bytes(), 8); - writer.write_n(&self.hue_vector_field2.to_be_bytes(), 8); - writer.write_n(&self.hue_vector_field3.to_be_bytes(), 8); - writer.write_n(&self.hue_vector_field4.to_be_bytes(), 8); - writer.write_n(&self.hue_vector_field5.to_be_bytes(), 8); + writer.write_n(&self.hue_vector_field0, 8)?; + writer.write_n(&self.hue_vector_field1, 8)?; + writer.write_n(&self.hue_vector_field2, 8)?; + writer.write_n(&self.hue_vector_field3, 8)?; + writer.write_n(&self.hue_vector_field4, 8)?; + writer.write_n(&self.hue_vector_field5, 8)?; } Ok(()) diff --git a/dolby_vision/src/rpu/extension_metadata/blocks/level9.rs b/dolby_vision/src/rpu/extension_metadata/blocks/level9.rs index c909061..b8c7a37 100644 --- a/dolby_vision/src/rpu/extension_metadata/blocks/level9.rs +++ b/dolby_vision/src/rpu/extension_metadata/blocks/level9.rs @@ -1,5 +1,7 @@ use anyhow::{ensure, Result}; -use bitvec_helpers::{bitslice_reader::BitSliceReader, bitvec_writer::BitVecWriter}; +use bitvec_helpers::{ + bitstream_io_reader::BsIoSliceReader, bitstream_io_writer::BitstreamIoWriter, +}; #[cfg(feature = "serde")] use serde::{ser::SerializeStruct, Deserialize, Serialize, Serializer}; @@ -55,7 +57,7 @@ pub struct ExtMetadataBlockLevel9 { } impl ExtMetadataBlockLevel9 { - pub(crate) fn parse(reader: &mut BitSliceReader, length: u64) -> Result { + pub(crate) fn parse(reader: &mut BsIoSliceReader, length: u64) -> Result { let mut block = Self { length, source_primary_index: reader.get_n(8)?, @@ -76,20 +78,20 @@ impl ExtMetadataBlockLevel9 { Ok(ExtMetadataBlock::Level9(block)) } - pub fn write(&self, writer: &mut BitVecWriter) -> Result<()> { + pub fn write(&self, writer: &mut BitstreamIoWriter) -> Result<()> { self.validate()?; - writer.write_n(&self.source_primary_index.to_be_bytes(), 8); + writer.write_n(&self.source_primary_index, 8)?; if self.length > 1 { - writer.write_n(&self.source_primary_red_x.to_be_bytes(), 16); - writer.write_n(&self.source_primary_red_y.to_be_bytes(), 16); - writer.write_n(&self.source_primary_green_x.to_be_bytes(), 16); - writer.write_n(&self.source_primary_green_y.to_be_bytes(), 16); - writer.write_n(&self.source_primary_blue_x.to_be_bytes(), 16); - writer.write_n(&self.source_primary_blue_y.to_be_bytes(), 16); - writer.write_n(&self.source_primary_white_x.to_be_bytes(), 16); - writer.write_n(&self.source_primary_white_y.to_be_bytes(), 16); + writer.write_n(&self.source_primary_red_x, 16)?; + writer.write_n(&self.source_primary_red_y, 16)?; + writer.write_n(&self.source_primary_green_x, 16)?; + writer.write_n(&self.source_primary_green_y, 16)?; + writer.write_n(&self.source_primary_blue_x, 16)?; + writer.write_n(&self.source_primary_blue_y, 16)?; + writer.write_n(&self.source_primary_white_x, 16)?; + writer.write_n(&self.source_primary_white_y, 16)?; } Ok(()) diff --git a/dolby_vision/src/rpu/extension_metadata/blocks/mod.rs b/dolby_vision/src/rpu/extension_metadata/blocks/mod.rs index 18b6d84..b91cc44 100644 --- a/dolby_vision/src/rpu/extension_metadata/blocks/mod.rs +++ b/dolby_vision/src/rpu/extension_metadata/blocks/mod.rs @@ -1,5 +1,7 @@ use anyhow::{ensure, Result}; -use bitvec_helpers::{bitslice_reader::BitSliceReader, bitvec_writer::BitVecWriter}; +use bitvec_helpers::{ + bitstream_io_reader::BsIoSliceReader, bitstream_io_writer::BitstreamIoWriter, +}; #[cfg(feature = "serde")] use serde::{Deserialize, Serialize}; @@ -160,7 +162,7 @@ impl ExtMetadataBlock { } } - pub fn write(&self, writer: &mut BitVecWriter) -> Result<()> { + pub fn write(&self, writer: &mut BitstreamIoWriter) -> Result<()> { match self { ExtMetadataBlock::Level1(b) => b.write(writer), ExtMetadataBlock::Level2(b) => b.write(writer), @@ -193,7 +195,7 @@ impl ExtMetadataBlock { pub(crate) fn validate_and_read_remaining( &self, - reader: &mut BitSliceReader, + reader: &mut BsIoSliceReader, block_length: u64, ) -> Result<()> { let level = self.level(); diff --git a/dolby_vision/src/rpu/extension_metadata/blocks/reserved.rs b/dolby_vision/src/rpu/extension_metadata/blocks/reserved.rs index 3aa3172..6d77363 100644 --- a/dolby_vision/src/rpu/extension_metadata/blocks/reserved.rs +++ b/dolby_vision/src/rpu/extension_metadata/blocks/reserved.rs @@ -1,7 +1,9 @@ use anyhow::{bail, Result}; use bitvec::{order::Msb0, prelude::BitVec}; -use bitvec_helpers::{bitslice_reader::BitSliceReader, bitvec_writer::BitVecWriter}; +use bitvec_helpers::{ + bitstream_io_reader::BsIoSliceReader, bitstream_io_writer::BitstreamIoWriter, +}; #[cfg(feature = "serde")] use serde::{Deserialize, Serialize}; @@ -25,7 +27,7 @@ impl ReservedExtMetadataBlock { pub(crate) fn parse( ext_block_length: u64, ext_block_level: u8, - reader: &mut BitSliceReader, + reader: &mut BsIoSliceReader, ) -> Result { let bits = 8 * ext_block_length; let mut data = BitVec::new(); @@ -41,9 +43,9 @@ impl ReservedExtMetadataBlock { })) } - pub fn write(&self, _writer: &mut BitVecWriter) -> Result<()> { + pub fn write(&self, _writer: &mut BitstreamIoWriter) -> Result<()> { bail!("Cannot write reserved block"); - // self.data.iter().for_each(|b| writer.write(*b)); + // self.data.iter().for_each(|b| writer.write(*b))?; } } diff --git a/dolby_vision/src/rpu/extension_metadata/cmv29.rs b/dolby_vision/src/rpu/extension_metadata/cmv29.rs index 5701048..4093a65 100644 --- a/dolby_vision/src/rpu/extension_metadata/cmv29.rs +++ b/dolby_vision/src/rpu/extension_metadata/cmv29.rs @@ -1,5 +1,5 @@ use anyhow::{bail, ensure, Result}; -use bitvec_helpers::bitslice_reader::BitSliceReader; +use bitvec_helpers::bitstream_io_reader::BsIoSliceReader; #[cfg(feature = "serde")] use serde::{Deserialize, Serialize}; @@ -41,7 +41,7 @@ impl WithExtMetadataBlocks for CmV29DmData { self.ext_metadata_blocks.as_mut() } - fn parse_block(&mut self, reader: &mut BitSliceReader) -> Result<()> { + fn parse_block(&mut self, reader: &mut BsIoSliceReader) -> Result<()> { let ext_block_length = reader.get_ue()?; let ext_block_level = reader.get_n(8)?; diff --git a/dolby_vision/src/rpu/extension_metadata/cmv40.rs b/dolby_vision/src/rpu/extension_metadata/cmv40.rs index cbefd80..b68600d 100644 --- a/dolby_vision/src/rpu/extension_metadata/cmv40.rs +++ b/dolby_vision/src/rpu/extension_metadata/cmv40.rs @@ -1,5 +1,5 @@ use anyhow::{bail, ensure, Result}; -use bitvec_helpers::bitslice_reader::BitSliceReader; +use bitvec_helpers::bitstream_io_reader::BsIoSliceReader; #[cfg(feature = "serde")] use serde::{Deserialize, Serialize}; @@ -41,7 +41,7 @@ impl WithExtMetadataBlocks for CmV40DmData { self.ext_metadata_blocks.as_mut() } - fn parse_block(&mut self, reader: &mut BitSliceReader) -> Result<()> { + fn parse_block(&mut self, reader: &mut BsIoSliceReader) -> Result<()> { let ext_block_length = reader.get_ue()?; let ext_block_level: u8 = reader.get_n(8)?; diff --git a/dolby_vision/src/rpu/extension_metadata/mod.rs b/dolby_vision/src/rpu/extension_metadata/mod.rs index 9d842f7..1a07685 100644 --- a/dolby_vision/src/rpu/extension_metadata/mod.rs +++ b/dolby_vision/src/rpu/extension_metadata/mod.rs @@ -1,5 +1,7 @@ use anyhow::{ensure, Result}; -use bitvec_helpers::{bitslice_reader::BitSliceReader, bitvec_writer::BitVecWriter}; +use bitvec_helpers::{ + bitstream_io_reader::BsIoSliceReader, bitstream_io_writer::BitstreamIoWriter, +}; #[cfg(feature = "serde")] use serde::{Deserialize, Serialize}; @@ -25,8 +27,8 @@ pub enum DmData { } pub trait ExtMetadata { - fn parse(&mut self, reader: &mut BitSliceReader) -> Result<()>; - fn write(&self, writer: &mut BitVecWriter); + fn parse(&mut self, reader: &mut BsIoSliceReader) -> Result<()>; + fn write(&self, writer: &mut BitstreamIoWriter); } pub trait WithExtMetadataBlocks { @@ -38,7 +40,7 @@ pub trait WithExtMetadataBlocks { fn set_num_ext_blocks(&mut self, num_ext_blocks: u64); fn num_ext_blocks(&self) -> u64; - fn parse_block(&mut self, reader: &mut BitSliceReader) -> Result<()>; + fn parse_block(&mut self, reader: &mut BsIoSliceReader) -> Result<()>; fn blocks_ref(&self) -> &Vec; fn blocks_mut(&mut self) -> &mut Vec; @@ -77,15 +79,13 @@ pub trait WithExtMetadataBlocks { self.update_extension_block_info(); } - fn write(&self, writer: &mut BitVecWriter) -> Result<()> { + fn write(&self, writer: &mut BitstreamIoWriter) -> Result<()> { let num_ext_blocks = self.num_ext_blocks(); - writer.write_ue(num_ext_blocks); + writer.write_ue(&num_ext_blocks)?; // dm_alignment_zero_bit - while !writer.is_aligned() { - writer.write(false); - } + writer.byte_align()?; let ext_metadata_blocks = self.blocks_ref(); @@ -93,13 +93,15 @@ pub trait WithExtMetadataBlocks { let remaining_bits = ext_metadata_block.length_bits() - ext_metadata_block.required_bits(); - writer.write_ue(ext_metadata_block.length_bytes()); - writer.write_n(&ext_metadata_block.level().to_be_bytes(), 8); + writer.write_ue(&ext_metadata_block.length_bytes())?; + writer.write_n(&ext_metadata_block.level(), 8)?; ext_metadata_block.write(writer)?; // ext_dm_alignment_zero_bit - (0..remaining_bits).for_each(|_| writer.write(false)); + for _ in 0..remaining_bits { + writer.write(false)?; + } } Ok(()) @@ -108,7 +110,7 @@ pub trait WithExtMetadataBlocks { impl DmData { pub(crate) fn parse( - reader: &mut BitSliceReader, + reader: &mut BsIoSliceReader, ) -> Result> { let num_ext_blocks = reader.get_ue()?; let mut meta = T::with_blocks_allocation(num_ext_blocks); @@ -129,7 +131,7 @@ impl DmData { Ok(Some(meta)) } - pub fn write(&self, writer: &mut BitVecWriter) -> Result<()> { + pub fn write(&self, writer: &mut BitstreamIoWriter) -> Result<()> { match self { DmData::V29(m) => m.write(writer), DmData::V40(m) => m.write(writer), diff --git a/dolby_vision/src/rpu/mod.rs b/dolby_vision/src/rpu/mod.rs index 4216b4f..b032f33 100644 --- a/dolby_vision/src/rpu/mod.rs +++ b/dolby_vision/src/rpu/mod.rs @@ -11,12 +11,12 @@ pub mod vdr_dm_data; pub mod utils; -pub const NUM_COMPONENTS: usize = 3; +static CRC32_INSTANCE: Crc = Crc::::new(&CRC_32_MPEG_2); -pub const FEL_STR: &str = "FEL"; -pub const MEL_STR: &str = "MEL"; +pub const NUM_COMPONENTS: usize = 3; -static CRC32_INSTANCE: Crc = Crc::::new(&CRC_32_MPEG_2); +pub(crate) const MMR_MAX_COEFFS: usize = 7; +pub(crate) const NLQ_NUM_PIVOTS: usize = 2; #[derive(Debug, Copy, Clone, PartialEq, Eq)] pub enum ConversionMode { diff --git a/dolby_vision/src/rpu/profiles/profile81.rs b/dolby_vision/src/rpu/profiles/profile81.rs index e7a6513..135f0a1 100644 --- a/dolby_vision/src/rpu/profiles/profile81.rs +++ b/dolby_vision/src/rpu/profiles/profile81.rs @@ -1,4 +1,9 @@ -use crate::rpu::rpu_data_mapping::RpuDataMapping; +use crate::rpu::{ + rpu_data_mapping::{ + DoviMappingMethod, DoviPolynomialCurve, DoviReshapingCurve, RpuDataMapping, + }, + NUM_COMPONENTS, +}; use super::{DoviProfile, VdrDmData}; @@ -35,22 +40,33 @@ impl DoviProfile for Profile81 { impl Profile81 { pub fn rpu_data_mapping() -> RpuDataMapping { + let curves: [DoviReshapingCurve; NUM_COMPONENTS] = [ + Self::dovi_reshaping_curve(), + Self::dovi_reshaping_curve(), + Self::dovi_reshaping_curve(), + ]; + RpuDataMapping { - mapping_idc: [vec![0], vec![0], vec![0]], - mapping_param_pred_flag: [vec![false], vec![false], vec![false]], - num_mapping_param_predictors: [vec![0], vec![0], vec![0]], - diff_pred_part_idx_mapping_minus1: [vec![], vec![], vec![]], - poly_order_minus1: [vec![0], vec![0], vec![0]], - linear_interp_flag: [vec![false], vec![false], vec![false]], - pred_linear_interp_value_int: [vec![], vec![], vec![]], - pred_linear_interp_value: [vec![], vec![], vec![]], - poly_coef_int: [vec![vec![0, 1]], vec![vec![0, 1]], vec![vec![0, 1]]], - poly_coef: [vec![vec![0, 0]], vec![vec![0, 0]], vec![vec![0, 0]]], - mmr_order_minus1: [vec![], vec![], vec![]], - mmr_constant_int: [vec![], vec![], vec![]], - mmr_constant: [vec![], vec![], vec![]], - mmr_coef_int: [vec![vec![]], vec![vec![]], vec![vec![]]], - mmr_coef: [vec![vec![]], vec![vec![]], vec![vec![]]], + vdr_rpu_id: 0, + mapping_color_space: 0, + mapping_chroma_format_idc: 0, + nlq_method_idc: None, + nlq_num_pivots_minus2: None, + nlq_pred_pivot_value: None, + num_x_partitions_minus1: 0, + num_y_partitions_minus1: 0, + curves, + nlq: None, + } + } + + pub fn dovi_reshaping_curve() -> DoviReshapingCurve { + DoviReshapingCurve { + num_pivots_minus2: 0, + pivots: vec![0, 1023], + mapping_idc: DoviMappingMethod::Polynomial, + polynomial: Some(DoviPolynomialCurve::p81_default()), + mmr: None, } } } diff --git a/dolby_vision/src/rpu/profiles/profile84.rs b/dolby_vision/src/rpu/profiles/profile84.rs index 1baafaa..b149522 100644 --- a/dolby_vision/src/rpu/profiles/profile84.rs +++ b/dolby_vision/src/rpu/profiles/profile84.rs @@ -1,4 +1,9 @@ -use crate::rpu::{rpu_data_header::RpuDataHeader, rpu_data_mapping::RpuDataMapping}; +use crate::rpu::{ + rpu_data_mapping::{ + DoviMMRCurve, DoviMappingMethod, DoviPolynomialCurve, DoviReshapingCurve, RpuDataMapping, + }, + NUM_COMPONENTS, +}; use super::{profile81::Profile81, DoviProfile, VdrDmData}; @@ -16,20 +21,9 @@ impl DoviProfile for Profile84 { // Based on iPhone 13 polynomials and MMR impl Profile84 { - pub fn rpu_data_header() -> RpuDataHeader { - RpuDataHeader { - num_pivots_minus_2: [7, 0, 0], - pred_pivot_value: [ - vec![63, 69, 230, 256, 256, 37, 16, 8, 7], - vec![0, 1023], - vec![0, 1023], - ], - ..RpuDataHeader::p8_default() - } - } - pub fn rpu_data_mapping() -> RpuDataMapping { - let poly_coef_int_cmp0 = vec![ + // Luma component + let poly_coef_int = vec![ vec![-1, 1, -3], vec![-1, 1, -2], vec![0, 0, -1], @@ -40,7 +34,7 @@ impl Profile84 { vec![28, -62, 34], ]; - let poly_coef_cmp0 = vec![ + let poly_coef = vec![ vec![7978928, 8332855, 4889184], vec![8269552, 5186604, 3909327], vec![1317527, 5338528, 7440486], @@ -51,52 +45,91 @@ impl Profile84 { vec![1947392, 1244640, 6094272], ]; + let poly_curve = DoviPolynomialCurve { + poly_order_minus1: vec![1; 8], + linear_interp_flag: vec![], + poly_coef_int, + poly_coef, + }; + let luma_reshaping_curve = DoviReshapingCurve { + num_pivots_minus2: 7, + pivots: vec![63, 69, 230, 256, 256, 37, 16, 8, 7], + mapping_idc: DoviMappingMethod::Polynomial, + polynomial: Some(poly_curve), + mmr: None, + }; + + // Chroma component 1 let mmr_coef_int_cmp1 = vec![vec![ - vec![0; 7], vec![-1, -2, -5, 2, 5, 9, -12], vec![-1, -1, 3, -1, -5, -12, 18], vec![-1, 0, -2, 0, 2, 7, -19], ]]; - - let mmr_coef_int_cmp2 = vec![vec![ - vec![0; 7], - vec![4, 0, 5, -2, -8, -1, 1], - vec![-4, -1, -6, 1, 12, 0, -4], - vec![1, 0, 2, -1, -8, -1, 4], - ]]; - let mmr_coef_cmp1 = vec![vec![ - vec![0; 7], vec![87355, 6228986, 642500, 1023296, 6569512, 5128216, 4317296], vec![ 8299905, 5819931, 2324124, 7273546, 1562484, 3679480, 6357360, ], vec![8172981, 3261951, 5970055, 927142, 3525840, 5110348, 6236848], ]]; + let mmr_curve1 = DoviMMRCurve { + mmr_order_minus1: vec![2], + mmr_constant_int: vec![1], + mmr_constant: vec![1150183], + mmr_coef_int: mmr_coef_int_cmp1, + mmr_coef: mmr_coef_cmp1, + }; + let chroma_reshaping_curve1 = DoviReshapingCurve { + num_pivots_minus2: 0, + pivots: vec![0, 1023], + mapping_idc: DoviMappingMethod::MMR, + polynomial: None, + mmr: Some(mmr_curve1), + }; + // Chroma component 2 + let mmr_coef_int_cmp2 = vec![vec![ + vec![4, 0, 5, -2, -8, -1, 1], + vec![-4, -1, -6, 1, 12, 0, -4], + vec![1, 0, 2, -1, -8, -1, 4], + ]]; let mmr_coef_cmp2 = vec![vec![ - vec![0; 7], vec![193104, 5369128, 2553116, 8009648, 2772020, 3122453, 2961581], vec![6769788, 2565605, 7864496, 4777288, 649616, 7036536, 1666406], vec![406265, 2901521, 2680224, 146340, 1008052, 4366810, 5080852], ]]; + let mmr_curve2 = DoviMMRCurve { + mmr_order_minus1: vec![2], + mmr_constant_int: vec![-2], + mmr_constant: vec![6266112], + mmr_coef_int: mmr_coef_int_cmp2, + mmr_coef: mmr_coef_cmp2, + }; + let chroma_reshaping_curve2 = DoviReshapingCurve { + num_pivots_minus2: 0, + pivots: vec![0, 1023], + mapping_idc: DoviMappingMethod::MMR, + polynomial: None, + mmr: Some(mmr_curve2), + }; + + let curves: [DoviReshapingCurve; NUM_COMPONENTS] = [ + luma_reshaping_curve, + chroma_reshaping_curve1, + chroma_reshaping_curve2, + ]; RpuDataMapping { - mapping_idc: [vec![0; 8], vec![1], vec![1]], - mapping_param_pred_flag: [vec![false; 8], vec![false], vec![false]], - num_mapping_param_predictors: [vec![0; 8], vec![0], vec![0]], - diff_pred_part_idx_mapping_minus1: [vec![], vec![], vec![]], - poly_order_minus1: [vec![1; 8], vec![], vec![]], - linear_interp_flag: [vec![], vec![], vec![]], - pred_linear_interp_value_int: [vec![], vec![], vec![]], - pred_linear_interp_value: [vec![], vec![], vec![]], - poly_coef_int: [poly_coef_int_cmp0, vec![], vec![]], - poly_coef: [poly_coef_cmp0, vec![], vec![]], - mmr_order_minus1: [vec![0], vec![2], vec![2]], - mmr_constant_int: [vec![0], vec![1], vec![-2]], - mmr_constant: [vec![0], vec![1150183], vec![6266112]], - mmr_coef_int: [vec![vec![]], mmr_coef_int_cmp1, mmr_coef_int_cmp2], - mmr_coef: [vec![vec![]], mmr_coef_cmp1, mmr_coef_cmp2], + vdr_rpu_id: 0, + mapping_color_space: 0, + mapping_chroma_format_idc: 0, + nlq_method_idc: None, + nlq_num_pivots_minus2: None, + nlq_pred_pivot_value: None, + num_x_partitions_minus1: 0, + num_y_partitions_minus1: 0, + curves, + nlq: None, } } } diff --git a/dolby_vision/src/rpu/rpu_data_header.rs b/dolby_vision/src/rpu/rpu_data_header.rs index 595f25e..837bf36 100644 --- a/dolby_vision/src/rpu/rpu_data_header.rs +++ b/dolby_vision/src/rpu/rpu_data_header.rs @@ -1,17 +1,17 @@ -use anyhow::{ensure, Result}; -use bitvec_helpers::{bitslice_reader::BitSliceReader, bitvec_writer::BitVecWriter}; +use anyhow::{bail, ensure, Result}; +use bitvec_helpers::{ + bitstream_io_reader::BsIoSliceReader, bitstream_io_writer::BitstreamIoWriter, +}; #[cfg(feature = "serde")] use serde::Serialize; -use super::{dovi_rpu::DoviRpu, NUM_COMPONENTS}; - -const NLQ_NUM_PIVOTS: usize = 2; - #[derive(Default, Debug, Clone)] #[cfg_attr(feature = "serde", derive(Serialize))] pub struct RpuDataHeader { + // Must be 25 pub rpu_nal_prefix: u8, + // Must be 2 pub rpu_type: u8, pub rpu_format: u16, pub vdr_rpu_profile: u8, @@ -20,121 +20,93 @@ pub struct RpuDataHeader { pub chroma_resampling_explicit_filter_flag: bool, pub coefficient_data_type: u8, pub coefficient_log2_denom: u64, + /// Calculated for `coefficient_data_type` + pub coefficient_log2_denom_length: u32, pub vdr_rpu_normalized_idc: u8, pub bl_video_full_range_flag: bool, + + // [8, 16] pub bl_bit_depth_minus8: u64, pub el_bit_depth_minus8: u64, - pub vdr_bit_depth_minus_8: u64, + pub vdr_bit_depth_minus8: u64, + pub spatial_resampling_filter_flag: bool, pub reserved_zero_3bits: u8, pub el_spatial_resampling_filter_flag: bool, pub disable_residual_flag: bool, pub vdr_dm_metadata_present_flag: bool, pub use_prev_vdr_rpu_flag: bool, - pub prev_vdr_rpu_id: u64, - pub vdr_rpu_id: u64, - pub mapping_color_space: u64, - pub mapping_chroma_format_idc: u64, - pub num_pivots_minus_2: [u64; NUM_COMPONENTS], - pub pred_pivot_value: [Vec; NUM_COMPONENTS], - pub nlq_method_idc: Option, - pub nlq_num_pivots_minus2: Option, - pub nlq_pred_pivot_value: Option<[u64; NLQ_NUM_PIVOTS]>, - pub num_x_partitions_minus1: u64, - pub num_y_partitions_minus1: u64, -} - -pub(crate) fn rpu_data_header(dovi_rpu: &mut DoviRpu, reader: &mut BitSliceReader) -> Result<()> { - dovi_rpu.header = RpuDataHeader::parse(reader)?; - Ok(()) + // [0, 15] + pub prev_vdr_rpu_id: u64, } impl RpuDataHeader { - pub(crate) fn parse(reader: &mut BitSliceReader) -> Result { - let mut rpu_nal = RpuDataHeader { - rpu_nal_prefix: reader.get_n(8)?, - ..Default::default() - }; - - if rpu_nal.rpu_nal_prefix == 25 { - rpu_nal.rpu_type = reader.get_n(6)?; - rpu_nal.rpu_format = reader.get_n(11)?; - - if rpu_nal.rpu_type == 2 { - rpu_nal.vdr_rpu_profile = reader.get_n(4)?; - - rpu_nal.vdr_rpu_level = reader.get_n(4)?; - rpu_nal.vdr_seq_info_present_flag = reader.get()?; + pub(crate) fn parse(reader: &mut BsIoSliceReader) -> Result { + let rpu_nal_prefix = reader.get_n(8)?; + ensure!(rpu_nal_prefix == 25); - if rpu_nal.vdr_seq_info_present_flag { - rpu_nal.chroma_resampling_explicit_filter_flag = reader.get()?; - rpu_nal.coefficient_data_type = reader.get_n(2)?; - - if rpu_nal.coefficient_data_type == 0 { - rpu_nal.coefficient_log2_denom = reader.get_ue()?; - } - - rpu_nal.vdr_rpu_normalized_idc = reader.get_n(2)?; - rpu_nal.bl_video_full_range_flag = reader.get()?; - - if rpu_nal.rpu_format & 0x700 == 0 { - rpu_nal.bl_bit_depth_minus8 = reader.get_ue()?; - rpu_nal.el_bit_depth_minus8 = reader.get_ue()?; - rpu_nal.vdr_bit_depth_minus_8 = reader.get_ue()?; - rpu_nal.spatial_resampling_filter_flag = reader.get()?; - rpu_nal.reserved_zero_3bits = reader.get_n(3)?; - rpu_nal.el_spatial_resampling_filter_flag = reader.get()?; - rpu_nal.disable_residual_flag = reader.get()?; - } - } + let rpu_type = reader.get_n(6)?; + ensure!(rpu_type == 2); - rpu_nal.vdr_dm_metadata_present_flag = reader.get()?; - rpu_nal.use_prev_vdr_rpu_flag = reader.get()?; + let rpu_format = reader.get_n(11)?; - if rpu_nal.use_prev_vdr_rpu_flag { - rpu_nal.prev_vdr_rpu_id = reader.get_ue()?; - } else { - rpu_nal.vdr_rpu_id = reader.get_ue()?; - rpu_nal.mapping_color_space = reader.get_ue()?; - rpu_nal.mapping_chroma_format_idc = reader.get_ue()?; + let vdr_rpu_profile = reader.get_n(4)?; + let vdr_rpu_level = reader.get_n(4)?; - let bl_bit_depth = (rpu_nal.bl_bit_depth_minus8 + 8) as usize; + let vdr_seq_info_present_flag = reader.get()?; - for cmp in 0..NUM_COMPONENTS { - rpu_nal.num_pivots_minus_2[cmp] = reader.get_ue()?; + let mut header = RpuDataHeader { + rpu_nal_prefix, + rpu_type, + rpu_format, + vdr_rpu_profile, + vdr_rpu_level, + vdr_seq_info_present_flag, + ..Default::default() + }; - let pivot_idx_count = (rpu_nal.num_pivots_minus_2[cmp] + 2) as usize; - rpu_nal.pred_pivot_value[cmp] = Vec::with_capacity(pivot_idx_count); - rpu_nal.pred_pivot_value[cmp] - .resize_with(pivot_idx_count, Default::default); + if vdr_seq_info_present_flag { + header.chroma_resampling_explicit_filter_flag = reader.get()?; + header.coefficient_data_type = reader.get_n(2)?; - for pivot_idx in 0..pivot_idx_count { - rpu_nal.pred_pivot_value[cmp][pivot_idx] = - reader.get_n(bl_bit_depth)?; - } - } + if header.coefficient_data_type == 0 { + header.coefficient_log2_denom = reader.get_ue()?; + } - // Profile 7 only - if rpu_nal.rpu_format & 0x700 == 0 && !rpu_nal.disable_residual_flag { - rpu_nal.nlq_method_idc = Some(reader.get_n(3)?); - rpu_nal.nlq_num_pivots_minus2 = Some(0); + header.vdr_rpu_normalized_idc = reader.get_n(2)?; + header.bl_video_full_range_flag = reader.get()?; + + if header.rpu_format & 0x700 == 0 { + header.bl_bit_depth_minus8 = reader.get_ue()?; + header.el_bit_depth_minus8 = reader.get_ue()?; + header.vdr_bit_depth_minus8 = reader.get_ue()?; + header.spatial_resampling_filter_flag = reader.get()?; + header.reserved_zero_3bits = reader.get_n(3)?; + header.el_spatial_resampling_filter_flag = reader.get()?; + header.disable_residual_flag = reader.get()?; + } - let mut nlq_pred_pivot_value = [0; NLQ_NUM_PIVOTS]; - for pv in &mut nlq_pred_pivot_value { - *pv = reader.get_n(bl_bit_depth)?; - } + header.coefficient_log2_denom_length = if header.coefficient_data_type == 0 { + header.coefficient_log2_denom as u32 + } else if header.coefficient_data_type == 1 { + 32 + } else { + bail!( + "Invalid coefficient_data_type value: {}", + header.coefficient_data_type + ); + }; + } - rpu_nal.nlq_pred_pivot_value = Some(nlq_pred_pivot_value); - } + header.vdr_dm_metadata_present_flag = reader.get()?; - rpu_nal.num_x_partitions_minus1 = reader.get_ue()?; - rpu_nal.num_y_partitions_minus1 = reader.get_ue()?; - } - } + header.use_prev_vdr_rpu_flag = reader.get()?; + if header.use_prev_vdr_rpu_flag { + header.prev_vdr_rpu_id = reader.get_ue()?; } - Ok(rpu_nal) + Ok(header) } pub fn validate(&self, profile: u8) -> Result<()> { @@ -150,53 +122,18 @@ impl RpuDataHeader { self.bl_video_full_range_flag, "profile 5: bl_video_full_range_flag should be true" ); - ensure!( - self.nlq_method_idc.is_none(), - "profile 5: nlq_method_idc should be undefined" - ); - ensure!( - self.nlq_num_pivots_minus2.is_none(), - "profile 5: nlq_num_pivots_minus2 should be undefined" - ); - ensure!( - self.nlq_pred_pivot_value.is_none(), - "profile 5: nlq_pred_pivot_value should be undefined" - ); } 7 => { ensure!( self.vdr_rpu_profile == 1, "profile 7: vdr_rpu_profile should be 1" ); - ensure!( - self.nlq_pred_pivot_value.is_some(), - "profile 7: nlq_pred_pivot_value should be defined" - ); - - if let Some(nlq_pred_pivot_value) = self.nlq_pred_pivot_value { - ensure!( - nlq_pred_pivot_value.iter().sum::() == 1023, - "profile 7: nlq_pred_pivot_value elements should add up to the BL bit depth" - ); - } } 8 => { ensure!( self.vdr_rpu_profile == 1, "profile 8: vdr_rpu_profile should be 1" ); - ensure!( - self.nlq_method_idc.is_none(), - "profile 8: nlq_method_idc should be undefined" - ); - ensure!( - self.nlq_num_pivots_minus2.is_none(), - "profile 8: nlq_num_pivots_minus2 should be undefined" - ); - ensure!( - self.nlq_pred_pivot_value.is_none(), - "profile 8: nlq_pred_pivot_value should be undefined" - ); } _ => (), }; @@ -211,16 +148,8 @@ impl RpuDataHeader { "el_bit_depth_minus8 should be 2" ); ensure!( - self.vdr_bit_depth_minus_8 <= 6, - "vdr_bit_depth_minus_8 should be <= 6" - ); - ensure!( - self.mapping_color_space == 0, - "mapping_color_space should be 0" - ); - ensure!( - self.mapping_chroma_format_idc == 0, - "mapping_chroma_format_idc should be 0" + self.vdr_bit_depth_minus8 <= 6, + "vdr_bit_depth_minus8 should be <= 6" ); ensure!( self.coefficient_log2_denom <= 23, @@ -243,7 +172,7 @@ impl RpuDataHeader { 1 => { // 4, 7 or 8 if self.el_spatial_resampling_filter_flag && !self.disable_residual_flag { - if self.vdr_bit_depth_minus_8 == 4 { + if self.vdr_bit_depth_minus8 == 4 { 7 } else { 4 @@ -256,83 +185,46 @@ impl RpuDataHeader { } } - pub fn write_header(&self, writer: &mut BitVecWriter) { - writer.write_n(&self.rpu_nal_prefix.to_be_bytes(), 8); + pub fn write_header(&self, writer: &mut BitstreamIoWriter) -> Result<()> { + writer.write_n(&self.rpu_nal_prefix, 8)?; - if self.rpu_nal_prefix == 25 { - writer.write_n(&self.rpu_type.to_be_bytes(), 6); - writer.write_n(&self.rpu_format.to_be_bytes(), 11); + writer.write_n(&self.rpu_type, 6)?; + writer.write_n(&self.rpu_format, 11)?; - if self.rpu_type == 2 { - writer.write_n(&self.vdr_rpu_profile.to_be_bytes(), 4); - writer.write_n(&self.vdr_rpu_level.to_be_bytes(), 4); - writer.write(self.vdr_seq_info_present_flag); + writer.write_n(&self.vdr_rpu_profile, 4)?; + writer.write_n(&self.vdr_rpu_level, 4)?; + writer.write(self.vdr_seq_info_present_flag)?; - if self.vdr_seq_info_present_flag { - writer.write(self.chroma_resampling_explicit_filter_flag); - writer.write_n(&self.coefficient_data_type.to_be_bytes(), 2); - - if self.coefficient_data_type == 0 { - writer.write_ue(self.coefficient_log2_denom); - } + if self.vdr_seq_info_present_flag { + writer.write(self.chroma_resampling_explicit_filter_flag)?; + writer.write_n(&self.coefficient_data_type, 2)?; - writer.write_n(&self.vdr_rpu_normalized_idc.to_be_bytes(), 2); - writer.write(self.bl_video_full_range_flag); - - if self.rpu_format & 0x700 == 0 { - writer.write_ue(self.bl_bit_depth_minus8); - writer.write_ue(self.el_bit_depth_minus8); - writer.write_ue(self.vdr_bit_depth_minus_8); - writer.write(self.spatial_resampling_filter_flag); - writer.write_n(&self.reserved_zero_3bits.to_be_bytes(), 3); - writer.write(self.el_spatial_resampling_filter_flag); - writer.write(self.disable_residual_flag); - } - } - - writer.write(self.vdr_dm_metadata_present_flag); - writer.write(self.use_prev_vdr_rpu_flag); - - if self.use_prev_vdr_rpu_flag { - writer.write_ue(self.prev_vdr_rpu_id); - } else { - writer.write_ue(self.vdr_rpu_id); - writer.write_ue(self.mapping_color_space); - writer.write_ue(self.mapping_chroma_format_idc); - - for cmp in 0..NUM_COMPONENTS { - writer.write_ue(self.num_pivots_minus_2[cmp]); - - let pivot_idx_count = (self.num_pivots_minus_2[cmp] + 2) as usize; + if self.coefficient_data_type == 0 { + writer.write_ue(&self.coefficient_log2_denom)?; + } - for pivot_idx in 0..pivot_idx_count { - writer.write_n( - &self.pred_pivot_value[cmp][pivot_idx].to_be_bytes(), - (self.bl_bit_depth_minus8 + 8) as usize, - ); - } - } + writer.write_n(&self.vdr_rpu_normalized_idc, 2)?; + writer.write(self.bl_video_full_range_flag)?; + + if self.rpu_format & 0x700 == 0 { + writer.write_ue(&self.bl_bit_depth_minus8)?; + writer.write_ue(&self.el_bit_depth_minus8)?; + writer.write_ue(&self.vdr_bit_depth_minus8)?; + writer.write(self.spatial_resampling_filter_flag)?; + writer.write_n(&self.reserved_zero_3bits, 3)?; + writer.write(self.el_spatial_resampling_filter_flag)?; + writer.write(self.disable_residual_flag)?; + } + } - if self.rpu_format & 0x700 == 0 && !self.disable_residual_flag { - if let Some(nlq_method_idc) = self.nlq_method_idc { - writer.write_n(&nlq_method_idc.to_be_bytes(), 3); - } - - if let Some(nlq_pred_pivot_value) = &self.nlq_pred_pivot_value { - nlq_pred_pivot_value.iter().for_each(|pv| { - writer.write_n( - &pv.to_be_bytes(), - (self.bl_bit_depth_minus8 + 8) as usize, - ) - }); - } - } + writer.write(self.vdr_dm_metadata_present_flag)?; + writer.write(self.use_prev_vdr_rpu_flag)?; - writer.write_ue(self.num_x_partitions_minus1); - writer.write_ue(self.num_y_partitions_minus1); - } - } + if self.use_prev_vdr_rpu_flag { + writer.write_ue(&self.prev_vdr_rpu_id)?; } + + Ok(()) } pub fn p5_default() -> RpuDataHeader { @@ -354,11 +246,12 @@ impl RpuDataHeader { chroma_resampling_explicit_filter_flag: false, coefficient_data_type: 0, coefficient_log2_denom: 23, + coefficient_log2_denom_length: 23, vdr_rpu_normalized_idc: 1, bl_video_full_range_flag: false, bl_bit_depth_minus8: 2, el_bit_depth_minus8: 2, - vdr_bit_depth_minus_8: 4, + vdr_bit_depth_minus8: 4, spatial_resampling_filter_flag: false, reserved_zero_3bits: 0, el_spatial_resampling_filter_flag: false, @@ -366,16 +259,6 @@ impl RpuDataHeader { vdr_dm_metadata_present_flag: true, use_prev_vdr_rpu_flag: false, prev_vdr_rpu_id: 0, - vdr_rpu_id: 0, - mapping_color_space: 0, - mapping_chroma_format_idc: 0, - num_pivots_minus_2: [0, 0, 0], - pred_pivot_value: [vec![0, 1023], vec![0, 1023], vec![0, 1023]], - nlq_method_idc: None, - nlq_num_pivots_minus2: None, - nlq_pred_pivot_value: None, - num_x_partitions_minus1: 0, - num_y_partitions_minus1: 0, } } } diff --git a/dolby_vision/src/rpu/rpu_data_mapping.rs b/dolby_vision/src/rpu/rpu_data_mapping.rs index f65732f..8f3081f 100644 --- a/dolby_vision/src/rpu/rpu_data_mapping.rs +++ b/dolby_vision/src/rpu/rpu_data_mapping.rs @@ -1,420 +1,545 @@ use anyhow::{bail, ensure, Result}; -use bitvec_helpers::{bitslice_reader::BitSliceReader, bitvec_writer::BitVecWriter}; +use bitvec_helpers::{ + bitstream_io_reader::BsIoSliceReader, bitstream_io_writer::BitstreamIoWriter, +}; #[cfg(feature = "serde")] use serde::Serialize; -use super::dovi_rpu::DoviRpu; -use super::profiles::profile81::Profile81; +use crate::rpu::MMR_MAX_COEFFS; + use super::rpu_data_header::RpuDataHeader; -use super::rpu_data_nlq::RpuDataNlq; +use super::rpu_data_nlq::{DoviELType, RpuDataNlq}; + +use super::{NLQ_NUM_PIVOTS, NUM_COMPONENTS}; + +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +#[cfg_attr(feature = "serde", derive(Serialize))] +pub enum DoviMappingMethod { + /// Not a valid value, placeholder for Default + Invalid = 255, + + Polynomial = 0, + MMR, +} -use super::NUM_COMPONENTS; +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +#[cfg_attr(feature = "serde", derive(Serialize))] +pub enum DoviNlqMethod { + LinearDeadzone = 0, +} #[derive(Debug, Default, Clone)] #[cfg_attr(feature = "serde", derive(Serialize))] pub struct RpuDataMapping { - pub mapping_idc: [Vec; NUM_COMPONENTS], - pub mapping_param_pred_flag: [Vec; NUM_COMPONENTS], - pub num_mapping_param_predictors: [Vec; NUM_COMPONENTS], - pub diff_pred_part_idx_mapping_minus1: [Vec; NUM_COMPONENTS], - pub poly_order_minus1: [Vec; NUM_COMPONENTS], - pub linear_interp_flag: [Vec; NUM_COMPONENTS], - pub pred_linear_interp_value_int: [Vec; NUM_COMPONENTS], - pub pred_linear_interp_value: [Vec; NUM_COMPONENTS], - pub poly_coef_int: [Vec>; NUM_COMPONENTS], - pub poly_coef: [Vec>; NUM_COMPONENTS], - pub mmr_order_minus1: [Vec; NUM_COMPONENTS], - pub mmr_constant_int: [Vec; NUM_COMPONENTS], - pub mmr_constant: [Vec; NUM_COMPONENTS], - pub mmr_coef_int: [Vec>>; NUM_COMPONENTS], - pub mmr_coef: [Vec>>; NUM_COMPONENTS], + // [0, 15] + pub vdr_rpu_id: u64, + pub mapping_color_space: u64, + pub mapping_chroma_format_idc: u64, + pub num_x_partitions_minus1: u64, + pub num_y_partitions_minus1: u64, + + pub curves: [DoviReshapingCurve; NUM_COMPONENTS], + + // NLQ params + #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))] + pub nlq_method_idc: Option, + #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))] + pub nlq_num_pivots_minus2: Option, + #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))] + pub nlq_pred_pivot_value: Option<[u16; NLQ_NUM_PIVOTS]>, + + #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))] + pub nlq: Option, } -pub(crate) fn vdr_rpu_data_payload( - dovi_rpu: &mut DoviRpu, - reader: &mut BitSliceReader, -) -> Result<()> { - dovi_rpu.rpu_data_mapping = Some(RpuDataMapping::parse(reader, &mut dovi_rpu.header)?); +#[derive(Debug, Default, Clone)] +#[cfg_attr(feature = "serde", derive(Serialize))] +pub struct DoviReshapingCurve { + // [2, 9] + pub num_pivots_minus2: u64, + pub pivots: Vec, + + // Consistent for a component + // Luma (component 0): Polynomial + // Chroma (components 1 and 2): MMR + pub mapping_idc: DoviMappingMethod, + + /// DoviMappingMethod::Polynomial + #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))] + #[cfg_attr(feature = "serde", serde(flatten))] + pub polynomial: Option, + + /// DoviMappingMethod::MMR + #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))] + #[cfg_attr(feature = "serde", serde(flatten))] + pub mmr: Option, +} - if dovi_rpu.header.nlq_method_idc.is_some() { - dovi_rpu.rpu_data_nlq = Some(RpuDataNlq::parse(reader, &mut dovi_rpu.header)?); - } +#[derive(Debug, Default, Clone)] +#[cfg_attr(feature = "serde", derive(Serialize))] +pub struct DoviPolynomialCurve { + pub poly_order_minus1: Vec, + pub linear_interp_flag: Vec, + pub poly_coef_int: Vec>, + pub poly_coef: Vec>, +} - Ok(()) +#[derive(Debug, Default, Clone)] +#[cfg_attr(feature = "serde", derive(Serialize))] +pub struct DoviMMRCurve { + pub mmr_order_minus1: Vec, + pub mmr_constant_int: Vec, + pub mmr_constant: Vec, + pub mmr_coef_int: Vec>>, + pub mmr_coef: Vec>>, } impl RpuDataMapping { pub(crate) fn parse( - reader: &mut BitSliceReader, - header: &mut RpuDataHeader, + reader: &mut BsIoSliceReader, + header: &RpuDataHeader, ) -> Result { - let mut data = RpuDataMapping::default(); - - let coefficient_log2_denom_length = if header.coefficient_data_type == 0 { - header.coefficient_log2_denom as usize - } else if header.coefficient_data_type == 1 { - 32 - } else { - bail!("Invalid coefficient_data_type value!"); + let mut mapping = RpuDataMapping { + vdr_rpu_id: reader.get_ue()?, + mapping_color_space: reader.get_ue()?, + mapping_chroma_format_idc: reader.get_ue()?, + ..Default::default() }; - // rpu_data_mapping_param + let bl_bit_depth = (header.bl_bit_depth_minus8 + 8) as u32; for cmp in 0..NUM_COMPONENTS { - let pivot_idx_count = (header.num_pivots_minus_2[cmp] + 1) as usize; + let mut curve = &mut mapping.curves[cmp]; - data.mapping_idc[cmp] = Vec::with_capacity(pivot_idx_count); - data.mapping_idc[cmp].resize_with(pivot_idx_count, Default::default); + curve.num_pivots_minus2 = reader.get_ue()?; + let num_pivots = (curve.num_pivots_minus2 + 2) as usize; - data.num_mapping_param_predictors[cmp] = Vec::with_capacity(pivot_idx_count); - data.num_mapping_param_predictors[cmp].resize_with(pivot_idx_count, Default::default); + curve.pivots = Vec::with_capacity(num_pivots); + curve.pivots.resize(num_pivots, Default::default()); - data.mapping_param_pred_flag[cmp] = Vec::with_capacity(pivot_idx_count); - data.mapping_param_pred_flag[cmp].resize_with(pivot_idx_count, Default::default); - - for pivot_idx in 0..pivot_idx_count { - data.mapping_idc[cmp][pivot_idx] = reader.get_ue()?; + for i in 0..num_pivots { + curve.pivots[i] = reader.get_n(bl_bit_depth)?; + } + } - if data.num_mapping_param_predictors[cmp][pivot_idx] > 0 { - data.mapping_param_pred_flag[cmp][pivot_idx] = reader.get()?; - } else { - data.mapping_param_pred_flag[cmp][pivot_idx] = false; - } + // Profile 7 only + if header.rpu_format & 0x700 == 0 && !header.disable_residual_flag { + let nlq_method_idc = reader.get_n::(3)?; + ensure!(nlq_method_idc == 0); - // == 0 - if !data.mapping_param_pred_flag[cmp][pivot_idx] { - // rpu_data_mapping_param() + mapping.nlq_method_idc = Some(DoviNlqMethod::from(nlq_method_idc)); + mapping.nlq_num_pivots_minus2 = Some(0); - // MAPPING_POLYNOMIAL - if data.mapping_idc[cmp][pivot_idx] == 0 { - if data.poly_order_minus1[cmp].is_empty() { - data.poly_order_minus1[cmp] = Vec::with_capacity(pivot_idx_count); - data.poly_order_minus1[cmp] - .resize_with(pivot_idx_count, Default::default); - } + let mut nlq_pred_pivot_value = [0; NLQ_NUM_PIVOTS]; + for pv in &mut nlq_pred_pivot_value { + *pv = reader.get_n(bl_bit_depth)?; + } - data.poly_order_minus1[cmp][pivot_idx] = reader.get_ue()?; + mapping.nlq_pred_pivot_value = Some(nlq_pred_pivot_value); + } - if data.poly_order_minus1[cmp][pivot_idx] == 0 { - if data.linear_interp_flag[cmp].is_empty() { - data.linear_interp_flag[cmp] = Vec::with_capacity(pivot_idx_count); - data.linear_interp_flag[cmp] - .resize_with(pivot_idx_count, Default::default); - } + mapping.num_x_partitions_minus1 = reader.get_ue()?; + mapping.num_y_partitions_minus1 = reader.get_ue()?; - data.linear_interp_flag[cmp][pivot_idx] = reader.get()?; - } + // rpu_data_mapping_param - // Linear interpolation - if data.poly_order_minus1[cmp][pivot_idx] == 0 - && data.linear_interp_flag[cmp][pivot_idx] - { - if data.pred_linear_interp_value[cmp].is_empty() { - data.pred_linear_interp_value_int[cmp] = - Vec::with_capacity(pivot_idx_count); - data.pred_linear_interp_value_int[cmp] - .resize_with(pivot_idx_count, Default::default); - - data.pred_linear_interp_value[cmp] = - Vec::with_capacity(pivot_idx_count); - data.pred_linear_interp_value[cmp] - .resize_with(pivot_idx_count, Default::default); - } + for cmp in 0..NUM_COMPONENTS { + let curve = &mut mapping.curves[cmp]; + let num_pieces = (curve.num_pivots_minus2 + 1) as usize; + + for _ in 0..num_pieces { + let mapping_idc = DoviMappingMethod::from(reader.get_ue()?); + curve.mapping_idc = mapping_idc; + + // MAPPING_POLYNOMIAL + if mapping_idc == DoviMappingMethod::Polynomial { + let poly_curve = curve + .polynomial + .get_or_insert_with(|| DoviPolynomialCurve::new(num_pieces)); + + poly_curve.parse(reader, header)?; + } else if mapping_idc == DoviMappingMethod::MMR { + let mmr_curve = curve + .mmr + .get_or_insert_with(|| DoviMMRCurve::new(num_pieces)); + + mmr_curve.parse(reader, header)?; + } + } + } - if header.coefficient_data_type == 0 { - data.pred_linear_interp_value_int[cmp][pivot_idx] = - reader.get_ue()?; - } + if mapping.nlq_method_idc.is_some() { + mapping.nlq = Some(RpuDataNlq::parse(reader, header, &mapping)?); + } - data.pred_linear_interp_value[cmp][pivot_idx] = - reader.get_n(coefficient_log2_denom_length)?; + Ok(mapping) + } - if pivot_idx as u64 == header.num_pivots_minus_2[cmp] { - if header.coefficient_data_type == 0 { - data.pred_linear_interp_value_int[cmp][pivot_idx + 1] = - reader.get_ue()?; - } + pub fn write(&self, writer: &mut BitstreamIoWriter, header: &RpuDataHeader) -> Result<()> { + let coefficient_log2_denom_length = header.coefficient_log2_denom_length; - data.pred_linear_interp_value[cmp][pivot_idx + 1] = - reader.get_n(coefficient_log2_denom_length)?; - } - } else { - if data.poly_coef_int[cmp].is_empty() { - data.poly_coef_int[cmp] = Vec::with_capacity(pivot_idx_count); - data.poly_coef_int[cmp] - .resize_with(pivot_idx_count, Default::default); - - data.poly_coef[cmp] = Vec::with_capacity(pivot_idx_count); - data.poly_coef[cmp].resize_with(pivot_idx_count, Default::default); - } + let bl_bit_depth = (header.bl_bit_depth_minus8 + 8) as u32; - let poly_coef_count = - data.poly_order_minus1[cmp][pivot_idx] as usize + 1; + writer.write_ue(&self.vdr_rpu_id)?; + writer.write_ue(&self.mapping_color_space)?; + writer.write_ue(&self.mapping_chroma_format_idc)?; - data.poly_coef_int[cmp][pivot_idx] = vec![0; poly_coef_count + 1]; - data.poly_coef[cmp][pivot_idx] = vec![0; poly_coef_count + 1]; + for cmp in 0..NUM_COMPONENTS { + let curve = &self.curves[cmp]; + writer.write_ue(&curve.num_pivots_minus2)?; - for i in 0..=poly_coef_count { - if header.coefficient_data_type == 0 { - data.poly_coef_int[cmp][pivot_idx][i] = reader.get_se()?; - } + for p in &curve.pivots { + writer.write_n(p, bl_bit_depth)?; + } + } - data.poly_coef[cmp][pivot_idx][i] = - reader.get_n(coefficient_log2_denom_length)?; - } - } - } else if data.mapping_idc[cmp][pivot_idx] == 1 { - // MAPPING_MMR - if data.mmr_order_minus1[cmp].is_empty() { - data.mmr_order_minus1[cmp] = Vec::with_capacity(pivot_idx_count); - data.mmr_order_minus1[cmp] - .resize_with(pivot_idx_count, Default::default); + if header.rpu_format & 0x700 == 0 && !header.disable_residual_flag { + if let Some(nlq_method_idc) = self.nlq_method_idc { + writer.write_n(&(nlq_method_idc as u8), 3)?; + } - data.mmr_constant_int[cmp] = Vec::with_capacity(pivot_idx_count); - data.mmr_constant_int[cmp] - .resize_with(pivot_idx_count, Default::default); + if let Some(nlq_pred_pivot_value) = &self.nlq_pred_pivot_value { + for pv in nlq_pred_pivot_value { + writer.write_n(pv, bl_bit_depth)?; + } + } + } - data.mmr_constant[cmp] = Vec::with_capacity(pivot_idx_count); - data.mmr_constant[cmp].resize_with(pivot_idx_count, Default::default); + writer.write_ue(&self.num_x_partitions_minus1)?; + writer.write_ue(&self.num_y_partitions_minus1)?; - data.mmr_coef_int[cmp] = Vec::with_capacity(pivot_idx_count); - data.mmr_coef_int[cmp].resize_with(pivot_idx_count, Default::default); + for cmp in 0..NUM_COMPONENTS { + let curve = &self.curves[cmp]; + let num_pieces = (curve.num_pivots_minus2 + 1) as usize; - data.mmr_coef[cmp] = Vec::with_capacity(pivot_idx_count); - data.mmr_coef[cmp].resize_with(pivot_idx_count, Default::default); - } + for i in 0..num_pieces { + writer.write_ue(&(curve.mapping_idc as u64))?; - data.mmr_order_minus1[cmp][pivot_idx] = reader.get_n(2)?; + // MAPPING_POLYNOMIAL + if let Some(poly_curve) = &curve.polynomial { + writer.write_ue(&poly_curve.poly_order_minus1[i])?; - ensure!(data.mmr_order_minus1[cmp][pivot_idx] <= 2); + let poly_order_minus1 = poly_curve.poly_order_minus1[i]; + if poly_order_minus1 == 0 { + writer.write(poly_curve.linear_interp_flag[i])?; + } - data.mmr_coef_int[cmp][pivot_idx] = - vec![vec![0; 7]; data.mmr_order_minus1[cmp][pivot_idx] as usize + 2]; - data.mmr_coef[cmp][pivot_idx] = - vec![vec![0; 7]; data.mmr_order_minus1[cmp][pivot_idx] as usize + 2]; + if poly_order_minus1 == 0 && poly_curve.linear_interp_flag[i] { + unimplemented!("write: Polynomial interpolation: please open an issue"); + /* if header.coefficient_data_type == 0 { - data.mmr_constant_int[cmp][pivot_idx] = reader.get_se()?; + writer.write_ue( + self.pred_linear_interp_value_int[cmp_idx][pivot_idx], + ); } - data.mmr_constant[cmp][pivot_idx] = - reader.get_n(coefficient_log2_denom_length)?; + writer.write_n( + &self.pred_linear_interp_value[cmp_idx][pivot_idx].to_be_bytes(), + coefficient_log2_denom_length, + ); + + if pivot_idx as u64 == header.num_pivots_minus2[cmp_idx] { + if header.coefficient_data_type == 0 { + writer.write_ue( + self.pred_linear_interp_value_int[cmp_idx][pivot_idx + 1], + ); + } - for i in 1..=data.mmr_order_minus1[cmp][pivot_idx] as usize + 1 { - for j in 0..7_usize { - if header.coefficient_data_type == 0 { - data.mmr_coef_int[cmp][pivot_idx][i][j] = reader.get_se()?; - } + writer.write_n( + &self.pred_linear_interp_value[cmp_idx][pivot_idx + 1] + .to_be_bytes(), + coefficient_log2_denom_length, + ); + } + */ + } else { + let poly_coef_count = poly_order_minus1 as usize + 1; - data.mmr_coef[cmp][pivot_idx][i][j] = - reader.get_n(coefficient_log2_denom_length)?; + for j in 0..=poly_coef_count { + if header.coefficient_data_type == 0 { + writer.write_se(&poly_curve.poly_coef_int[i][j])?; } + + writer.write_n( + &poly_curve.poly_coef[i][j], + coefficient_log2_denom_length, + )?; } } - } else if data.num_mapping_param_predictors[cmp][pivot_idx] > 1 { - if data.diff_pred_part_idx_mapping_minus1[cmp].is_empty() { - data.diff_pred_part_idx_mapping_minus1[cmp] = - Vec::with_capacity(pivot_idx_count); - data.diff_pred_part_idx_mapping_minus1[cmp] - .resize_with(pivot_idx_count, Default::default); + } else if let Some(mmr_curve) = &curve.mmr { + // MAPPING_MMR + writer.write_n(&mmr_curve.mmr_order_minus1[i], 2)?; + + if header.coefficient_data_type == 0 { + writer.write_se(&mmr_curve.mmr_constant_int[i])?; } - data.diff_pred_part_idx_mapping_minus1[cmp][pivot_idx] = reader.get_ue()?; + writer.write_n(&mmr_curve.mmr_constant[i], coefficient_log2_denom_length)?; + + for j in 0..mmr_curve.mmr_order_minus1[i] as usize + 1 { + for k in 0..MMR_MAX_COEFFS { + if header.coefficient_data_type == 0 { + writer.write_se(&mmr_curve.mmr_coef_int[i][j][k])?; + } + + writer.write_n( + &mmr_curve.mmr_coef[i][j][k], + coefficient_log2_denom_length, + )?; + } + } + } else { + bail!("Missing mapping method"); } } } - Ok(data) + if let Some(nlq) = self.nlq.as_ref() { + nlq.write(writer, header, self)?; + } + + Ok(()) } - pub fn write(&self, writer: &mut BitVecWriter, header: &RpuDataHeader) -> Result<()> { - let coefficient_log2_denom_length = if header.coefficient_data_type == 0 { - header.coefficient_log2_denom as usize - } else if header.coefficient_data_type == 1 { - 32 - } else { - bail!( - "Invalid coefficient_data_type value: {}", - header.coefficient_data_type - ); + pub fn validate(&self, profile: u8) -> Result<()> { + match profile { + 5 => { + ensure!( + self.nlq_method_idc.is_none(), + "profile 5: nlq_method_idc should be undefined" + ); + ensure!( + self.nlq_num_pivots_minus2.is_none(), + "profile 5: nlq_num_pivots_minus2 should be undefined" + ); + ensure!( + self.nlq_pred_pivot_value.is_none(), + "profile 5: nlq_pred_pivot_value should be undefined" + ); + } + 7 => { + ensure!( + self.nlq_pred_pivot_value.is_some(), + "profile 7: nlq_pred_pivot_value should be defined" + ); + + if let Some(nlq_pred_pivot_value) = self.nlq_pred_pivot_value { + ensure!( + nlq_pred_pivot_value.iter().sum::() == 1023, + "profile 7: nlq_pred_pivot_value elements should add up to the BL bit depth" + ); + } + } + 8 => { + ensure!( + self.nlq_method_idc.is_none(), + "profile 8: nlq_method_idc should be undefined" + ); + ensure!( + self.nlq_num_pivots_minus2.is_none(), + "profile 8: nlq_num_pivots_minus2 should be undefined" + ); + ensure!( + self.nlq_pred_pivot_value.is_none(), + "profile 8: nlq_pred_pivot_value should be undefined" + ); + } + _ => (), }; - // rpu_data_mapping_param + ensure!( + self.mapping_color_space == 0, + "mapping_color_space should be 0" + ); + ensure!( + self.mapping_chroma_format_idc == 0, + "mapping_chroma_format_idc should be 0" + ); - self.mapping_idc - .iter() - .enumerate() - .for_each(|(cmp_idx, mapping_idc)| { - let pivot_idx_count = (header.num_pivots_minus_2[cmp_idx] + 1) as usize; + Ok(()) + } - for (pivot_idx, mapping_idc_value) in - mapping_idc.iter().enumerate().take(pivot_idx_count) - { - writer.write_ue(*mapping_idc_value); + pub fn set_empty_p81_mapping(&mut self) { + self.curves.iter_mut().for_each(|curve| { + curve.mapping_idc = DoviMappingMethod::Polynomial; + curve.mmr = None; + + if let Some(poly_curve) = curve.polynomial.as_mut() { + poly_curve.set_p81_params(); + } else { + curve.polynomial = Some(DoviPolynomialCurve::p81_default()); + } + }); + } - if self.num_mapping_param_predictors[cmp_idx][pivot_idx] > 0 { - writer.write(self.mapping_param_pred_flag[cmp_idx][pivot_idx]); - } + pub fn get_enhancement_layer_type(&self) -> Option { + self.nlq.as_ref().map(|nlq| nlq.el_type()) + } +} + +impl DoviPolynomialCurve { + fn new(num_pieces: usize) -> Self { + DoviPolynomialCurve { + poly_order_minus1: Vec::with_capacity(num_pieces), + linear_interp_flag: Vec::with_capacity(num_pieces), + poly_coef_int: Vec::with_capacity(num_pieces), + poly_coef: Vec::with_capacity(num_pieces), + } + } - // == 0 - if !self.mapping_param_pred_flag[cmp_idx][pivot_idx] { - // rpu_data_mapping_param() + fn parse(&mut self, reader: &mut BsIoSliceReader, header: &RpuDataHeader) -> Result<()> { + let coefficient_log2_denom_length = header.coefficient_log2_denom_length; - // MAPPING_POLYNOMIAL - if mapping_idc[pivot_idx] == 0 { - writer.write_ue(self.poly_order_minus1[cmp_idx][pivot_idx]); + let poly_order_minus1 = reader.get_ue()?; + ensure!(poly_order_minus1 <= 1); - if self.poly_order_minus1[cmp_idx][pivot_idx] == 0 { - writer.write(self.linear_interp_flag[cmp_idx][pivot_idx]); - } + self.poly_order_minus1.push(poly_order_minus1); - // Linear interpolation - if self.poly_order_minus1[cmp_idx][pivot_idx] == 0 - && self.linear_interp_flag[cmp_idx][pivot_idx] - { - if header.coefficient_data_type == 0 { - writer.write_ue( - self.pred_linear_interp_value_int[cmp_idx][pivot_idx], - ); - } - - writer.write_n( - &self.pred_linear_interp_value[cmp_idx][pivot_idx] - .to_be_bytes(), - coefficient_log2_denom_length, - ); + let linear_interp_flag = if poly_order_minus1 == 0 { + reader.get()? + } else { + false + }; + self.linear_interp_flag.push(linear_interp_flag); - if pivot_idx as u64 == header.num_pivots_minus_2[cmp_idx] { - if header.coefficient_data_type == 0 { - writer.write_ue( - self.pred_linear_interp_value_int[cmp_idx] - [pivot_idx + 1], - ); - } - - writer.write_n( - &self.pred_linear_interp_value[cmp_idx][pivot_idx + 1] - .to_be_bytes(), - coefficient_log2_denom_length, - ); - } - } else { - for i in 0..=self.poly_order_minus1[cmp_idx][pivot_idx] as usize + 1 - { - if header.coefficient_data_type == 0 { - writer.write_se(self.poly_coef_int[cmp_idx][pivot_idx][i]); - } - - writer.write_n( - &self.poly_coef[cmp_idx][pivot_idx][i].to_be_bytes(), - coefficient_log2_denom_length, - ); - } - } - } else if mapping_idc[pivot_idx] == 1 { - // MAPPING_MMR - writer.write_n( - &self.mmr_order_minus1[cmp_idx][pivot_idx].to_be_bytes(), - 2, - ); + if poly_order_minus1 == 0 && linear_interp_flag { + // Linear interpolation + unimplemented!("parse: Polynomial interpolation: please open an issue"); - if header.coefficient_data_type == 0 { - writer.write_se(self.mmr_constant_int[cmp_idx][pivot_idx]); - } + /*if header.coefficient_data_type == 0 { + self.pred_linear_interp_value_int[i] = reader.get_ue()?; + } - writer.write_n( - &self.mmr_constant[cmp_idx][pivot_idx].to_be_bytes(), - coefficient_log2_denom_length, - ); + self.pred_linear_interp_value[i] = + reader.get_n(coefficient_log2_denom_length)?; - for i in 1..=self.mmr_order_minus1[cmp_idx][pivot_idx] as usize + 1 { - for j in 0..7_usize { - if header.coefficient_data_type == 0 { - writer - .write_se(self.mmr_coef_int[cmp_idx][pivot_idx][i][j]); - } - - writer.write_n( - &self.mmr_coef[cmp_idx][pivot_idx][i][j].to_be_bytes(), - coefficient_log2_denom_length, - ); - } - } - } - } else if self.num_mapping_param_predictors[cmp_idx][pivot_idx] > 1 { - writer.write_ue(self.diff_pred_part_idx_mapping_minus1[cmp_idx][pivot_idx]); - } + if pivot_idx as u64 == header.num_pivots_minus2[cmp] { + if header.coefficient_data_type == 0 { + self.pred_linear_interp_value_int[cmp][pivot_idx + 1] = + reader.get_ue()?; + } + + self.pred_linear_interp_value[cmp][pivot_idx + 1] = + reader.get_n(coefficient_log2_denom_length)?; + }*/ + } else { + let poly_coef_count = poly_order_minus1 as usize + 2; + let mut poly_coef_int = Vec::with_capacity(poly_coef_count); + let mut poly_coef = Vec::with_capacity(poly_coef_count); + + for _j in 0..poly_coef_count { + if header.coefficient_data_type == 0 { + poly_coef_int.push(reader.get_se()?); } - }); + + poly_coef.push(reader.get_n(coefficient_log2_denom_length)?); + } + + self.poly_coef_int.push(poly_coef_int); + self.poly_coef.push(poly_coef); + } Ok(()) } - pub fn set_empty_p81_mapping(&mut self) { - self.mapping_idc.iter_mut().for_each(|v| { - v.clear(); - v.push(0); - }); + pub fn p81_default() -> Self { + let mut poly_curve = Self::new(1); + poly_curve.set_p81_params(); - self.mapping_param_pred_flag.iter_mut().for_each(|v| { - v.clear(); - v.push(false); - }); + poly_curve + } - self.num_mapping_param_predictors.iter_mut().for_each(|v| { - v.clear(); - v.push(0); - }); + pub fn set_p81_params(&mut self) { + self.poly_order_minus1.clear(); + self.poly_order_minus1.push(0); - self.diff_pred_part_idx_mapping_minus1 - .iter_mut() - .for_each(|v| { - v.clear(); - v.push(0); - }); + self.linear_interp_flag.clear(); + self.linear_interp_flag.push(false); - self.poly_order_minus1.iter_mut().for_each(|v| { - v.clear(); - v.push(0); - }); + self.poly_coef_int.clear(); + self.poly_coef_int.push(vec![0, 1]); - self.linear_interp_flag.iter_mut().for_each(|v| { - v.clear(); - v.push(false); - }); + self.poly_coef.clear(); + self.poly_coef.push(vec![0, 0]); + } +} - self.pred_linear_interp_value_int - .iter_mut() - .for_each(|v| v.clear()); +impl DoviMMRCurve { + fn new(num_pieces: usize) -> Self { + DoviMMRCurve { + mmr_order_minus1: Vec::with_capacity(num_pieces), + mmr_constant_int: Vec::with_capacity(num_pieces), + mmr_constant: Vec::with_capacity(num_pieces), + mmr_coef_int: Vec::with_capacity(num_pieces), + mmr_coef: Vec::with_capacity(num_pieces), + } + } - self.pred_linear_interp_value - .iter_mut() - .for_each(|v| v.clear()); + fn parse(&mut self, reader: &mut BsIoSliceReader, header: &RpuDataHeader) -> Result<()> { + let coefficient_log2_denom_length = header.coefficient_log2_denom_length; - self.poly_coef_int.iter_mut().for_each(|v| { - v.clear(); - v.push(vec![0, 1]); - }); + let mmr_order_minus1 = reader.get_n(2)?; + ensure!(mmr_order_minus1 <= 2); - self.poly_coef.iter_mut().for_each(|v| { - v.clear(); - v.push(vec![0, 0]); - }); + self.mmr_order_minus1.push(mmr_order_minus1); + + let mmr_orders_count = mmr_order_minus1 as usize + 1; + + if header.coefficient_data_type == 0 { + self.mmr_constant_int.push(reader.get_se()?); + } + self.mmr_constant + .push(reader.get_n(coefficient_log2_denom_length)?); + + let mut mmr_coef_int = Vec::with_capacity(mmr_orders_count); + let mut mmr_coef = Vec::with_capacity(mmr_orders_count); - self.mmr_order_minus1.iter_mut().for_each(|v| v.clear()); + for _j in 0..mmr_orders_count { + let mut mmr_coef_int2 = Vec::with_capacity(MMR_MAX_COEFFS); + let mut mmr_coef2 = Vec::with_capacity(MMR_MAX_COEFFS); - self.mmr_constant_int.iter_mut().for_each(|v| v.clear()); + for _k in 0..MMR_MAX_COEFFS { + if header.coefficient_data_type == 0 { + mmr_coef_int2.push(reader.get_se()?); + } - self.mmr_constant.iter_mut().for_each(|v| v.clear()); + mmr_coef2.push(reader.get_n(coefficient_log2_denom_length)?); + } - self.mmr_coef_int.iter_mut().for_each(|v| v.clear()); + mmr_coef_int.push(mmr_coef_int2); + mmr_coef.push(mmr_coef2); + } - self.mmr_coef.iter_mut().for_each(|v| v.clear()); + self.mmr_coef_int.push(mmr_coef_int); + self.mmr_coef.push(mmr_coef); + + Ok(()) } +} - #[deprecated(since = "1.6.5", note = "Replaced by Profile81::rpu_data_mapping")] - pub fn p8_default() -> RpuDataMapping { - Profile81::rpu_data_mapping() +impl Default for DoviMappingMethod { + fn default() -> Self { + Self::Invalid + } +} + +impl From for DoviMappingMethod { + fn from(value: u64) -> Self { + match value { + 0 => Self::Polynomial, + 1 => Self::MMR, + _ => unreachable!(), + } + } +} + +impl From for DoviNlqMethod { + fn from(value: u8) -> Self { + match value { + 0 => Self::LinearDeadzone, + _ => unreachable!(), + } } } diff --git a/dolby_vision/src/rpu/rpu_data_nlq.rs b/dolby_vision/src/rpu/rpu_data_nlq.rs index bdbc900..149b615 100644 --- a/dolby_vision/src/rpu/rpu_data_nlq.rs +++ b/dolby_vision/src/rpu/rpu_data_nlq.rs @@ -1,148 +1,86 @@ -use anyhow::{bail, Result}; -use bitvec_helpers::{bitslice_reader::BitSliceReader, bitvec_writer::BitVecWriter}; +use std::fmt::Display; + +use anyhow::{ensure, Result}; +use bitvec_helpers::{ + bitstream_io_reader::BsIoSliceReader, bitstream_io_writer::BitstreamIoWriter, +}; #[cfg(feature = "serde")] use serde::Serialize; use super::rpu_data_header::RpuDataHeader; +use super::rpu_data_mapping::{DoviNlqMethod, RpuDataMapping}; use super::NUM_COMPONENTS; +const FEL_STR: &str = "FEL"; +const MEL_STR: &str = "MEL"; + +#[derive(Debug, Clone, PartialEq, Eq)] +#[cfg_attr(feature = "serde", derive(Serialize))] +pub enum DoviELType { + MEL, + FEL, +} + #[derive(Debug, Default, Clone)] #[cfg_attr(feature = "serde", derive(Serialize))] pub struct RpuDataNlq { - pub num_nlq_param_predictors: Vec<[u64; NUM_COMPONENTS]>, - pub nlq_param_pred_flag: Vec<[bool; NUM_COMPONENTS]>, - pub diff_pred_part_idx_nlq_minus1: Vec<[u64; NUM_COMPONENTS]>, - pub nlq_offset: Vec<[u64; NUM_COMPONENTS]>, - pub vdr_in_max_int: Vec<[u64; NUM_COMPONENTS]>, - pub vdr_in_max: Vec<[u64; NUM_COMPONENTS]>, - pub linear_deadzone_slope_int: Vec<[u64; NUM_COMPONENTS]>, - pub linear_deadzone_slope: Vec<[u64; NUM_COMPONENTS]>, - pub linear_deadzone_threshold_int: Vec<[u64; NUM_COMPONENTS]>, - pub linear_deadzone_threshold: Vec<[u64; NUM_COMPONENTS]>, + // [0, 512] + pub nlq_offset: [u16; NUM_COMPONENTS], + pub vdr_in_max_int: [u64; NUM_COMPONENTS], + pub vdr_in_max: [u64; NUM_COMPONENTS], + pub linear_deadzone_slope_int: [u64; NUM_COMPONENTS], + pub linear_deadzone_slope: [u64; NUM_COMPONENTS], + pub linear_deadzone_threshold_int: [u64; NUM_COMPONENTS], + pub linear_deadzone_threshold: [u64; NUM_COMPONENTS], } impl RpuDataNlq { - fn with_allocated_vecs(pivot_count: usize) -> Self { - Self { - num_nlq_param_predictors: Vec::with_capacity(pivot_count), - nlq_param_pred_flag: Vec::with_capacity(pivot_count), - diff_pred_part_idx_nlq_minus1: Vec::with_capacity(pivot_count), - nlq_offset: Vec::with_capacity(pivot_count), - vdr_in_max_int: Vec::with_capacity(pivot_count), - vdr_in_max: Vec::with_capacity(pivot_count), - linear_deadzone_slope_int: Vec::with_capacity(pivot_count), - linear_deadzone_slope: Vec::with_capacity(pivot_count), - linear_deadzone_threshold_int: Vec::with_capacity(pivot_count), - linear_deadzone_threshold: Vec::with_capacity(pivot_count), - } - } - pub(crate) fn parse( - reader: &mut BitSliceReader, - header: &mut RpuDataHeader, + reader: &mut BsIoSliceReader, + header: &RpuDataHeader, + mapping: &RpuDataMapping, ) -> Result { - let pivot_idx_count = if let Some(nlq_num_pivots_minus2) = header.nlq_num_pivots_minus2 { - nlq_num_pivots_minus2 as usize + 1 - } else { - bail!("Shouldn't be in NLQ if not profile 7!"); - }; + ensure!( + mapping.nlq_num_pivots_minus2.is_some(), + "Shouldn't be in NLQ if not profile 7!" + ); - let mut data = RpuDataNlq::with_allocated_vecs(pivot_idx_count); + let num_pivots = mapping.nlq_num_pivots_minus2.unwrap() as usize + 1; + ensure!(num_pivots == 1, "NLQ should only have 1 significant pivot"); - let coefficient_log2_denom_length = if header.coefficient_data_type == 0 { - header.coefficient_log2_denom as usize - } else if header.coefficient_data_type == 1 { - 32 - } else { - bail!( - "Invalid coefficient_data_type value: {}", - header.coefficient_data_type - ); - }; - - data.num_nlq_param_predictors - .resize_with(pivot_idx_count, Default::default); - data.nlq_param_pred_flag - .resize_with(pivot_idx_count, Default::default); - - for pivot_idx in 0..pivot_idx_count { - for cmp in 0..NUM_COMPONENTS { - if data.num_nlq_param_predictors[pivot_idx][cmp] > 0 { - data.nlq_param_pred_flag[pivot_idx][cmp] = reader.get()?; - } else { - data.nlq_param_pred_flag[pivot_idx][cmp] = false; - } + let mut data = RpuDataNlq::default(); - if !data.nlq_param_pred_flag[pivot_idx][cmp] { - // rpu_data_nlq_param + let coefficient_log2_denom_length = header.coefficient_log2_denom_length; - if data.nlq_offset.is_empty() { - data.nlq_offset - .resize_with(pivot_idx_count, Default::default); - data.vdr_in_max - .resize_with(pivot_idx_count, Default::default); - } + for cmp in 0..NUM_COMPONENTS { + // rpu_data_nlq_param - data.nlq_offset[pivot_idx][cmp] = - reader.get_n((header.el_bit_depth_minus8 + 8) as usize)?; + data.nlq_offset[cmp] = reader.get_n((header.el_bit_depth_minus8 + 8) as u32)?; - if header.coefficient_data_type == 0 { - if data.vdr_in_max_int.is_empty() { - data.vdr_in_max_int - .resize_with(pivot_idx_count, Default::default); - } + if header.coefficient_data_type == 0 { + data.vdr_in_max_int[cmp] = reader.get_ue()?; + } - data.vdr_in_max_int[pivot_idx][cmp] = reader.get_ue()?; + data.vdr_in_max[cmp] = reader.get_n(coefficient_log2_denom_length)?; + + // NLQ_LINEAR_DZ + if let Some(nlq_method_idc) = mapping.nlq_method_idc { + if nlq_method_idc == DoviNlqMethod::LinearDeadzone { + if header.coefficient_data_type == 0 { + data.linear_deadzone_slope_int[cmp] = reader.get_ue()?; } - data.vdr_in_max[pivot_idx][cmp] = + data.linear_deadzone_slope[cmp] = reader.get_n(coefficient_log2_denom_length)?; - // NLQ_LINEAR_DZ - if let Some(nlq_method_idc) = header.nlq_method_idc { - if nlq_method_idc == 0 { - if data.linear_deadzone_slope.is_empty() { - data.linear_deadzone_slope - .resize_with(pivot_idx_count, Default::default); - data.linear_deadzone_threshold - .resize_with(pivot_idx_count, Default::default); - } - - if header.coefficient_data_type == 0 { - if data.linear_deadzone_slope_int.is_empty() { - data.linear_deadzone_slope_int - .resize_with(pivot_idx_count, Default::default); - } - - data.linear_deadzone_slope_int[pivot_idx][cmp] = reader.get_ue()?; - } - - data.linear_deadzone_slope[pivot_idx][cmp] = - reader.get_n(coefficient_log2_denom_length)?; - - if header.coefficient_data_type == 0 { - if data.linear_deadzone_threshold_int.is_empty() { - data.linear_deadzone_threshold_int - .resize_with(pivot_idx_count, Default::default); - } - - data.linear_deadzone_threshold_int[pivot_idx][cmp] = - reader.get_ue()?; - } - - data.linear_deadzone_threshold[pivot_idx][cmp] = - reader.get_n(coefficient_log2_denom_length)?; - } - } - } else if data.num_nlq_param_predictors[pivot_idx][cmp] > 1 { - if data.diff_pred_part_idx_nlq_minus1.is_empty() { - data.diff_pred_part_idx_nlq_minus1 - .resize_with(pivot_idx_count, Default::default); + if header.coefficient_data_type == 0 { + data.linear_deadzone_threshold_int[cmp] = reader.get_ue()?; } - data.diff_pred_part_idx_nlq_minus1[pivot_idx][cmp] = reader.get_ue()?; + data.linear_deadzone_threshold[cmp] = + reader.get_n(coefficient_log2_denom_length)?; } } } @@ -152,101 +90,60 @@ impl RpuDataNlq { pub fn convert_to_mel(&mut self) { // Set to 0 - self.nlq_offset.iter_mut().for_each(|v| { - v.iter_mut().for_each(|v2| *v2 = 0); - }); - + self.nlq_offset.fill(0); // Set to 1 - self.vdr_in_max_int.iter_mut().for_each(|v| { - v.iter_mut().for_each(|v2| *v2 = 1); - }); - + self.vdr_in_max_int.fill(1); // Set to 0 - self.vdr_in_max.iter_mut().for_each(|v| { - v.iter_mut().for_each(|v2| *v2 = 0); - }); + self.vdr_in_max.fill(0); - self.linear_deadzone_slope_int.iter_mut().for_each(|v| { - v.iter_mut().for_each(|v2| *v2 = 0); - }); + self.linear_deadzone_slope_int.fill(0); + self.linear_deadzone_slope.fill(0); + self.linear_deadzone_threshold_int.fill(0); + self.linear_deadzone_threshold.fill(0); + } - self.linear_deadzone_slope.iter_mut().for_each(|v| { - v.iter_mut().for_each(|v2| *v2 = 0); - }); + pub fn write( + &self, + writer: &mut BitstreamIoWriter, + header: &RpuDataHeader, + mapping: &RpuDataMapping, + ) -> Result<()> { + let coefficient_log2_denom_length = header.coefficient_log2_denom_length; - self.linear_deadzone_threshold_int.iter_mut().for_each(|v| { - v.iter_mut().for_each(|v2| *v2 = 0); - }); + for cmp in 0..NUM_COMPONENTS { + // rpu_data_nlq_param - self.linear_deadzone_threshold.iter_mut().for_each(|v| { - v.iter_mut().for_each(|v2| *v2 = 0); - }); - } + writer.write_n( + &self.nlq_offset[cmp], + (header.el_bit_depth_minus8 + 8) as u32, + )?; - pub fn write(&self, writer: &mut BitVecWriter, header: &RpuDataHeader) -> Result<()> { - let pivot_idx_count = if let Some(nlq_num_pivots_minus2) = header.nlq_num_pivots_minus2 { - nlq_num_pivots_minus2 as usize + 1 - } else { - bail!("Shouldn't be in NLQ if not profile 7!"); - }; - let coefficient_log2_denom_length = if header.coefficient_data_type == 0 { - header.coefficient_log2_denom as usize - } else if header.coefficient_data_type == 1 { - 32 - } else { - bail!( - "Invalid coefficient_data_type value: {}", - header.coefficient_data_type - ); - }; - - for pivot_idx in 0..pivot_idx_count { - for cmp in 0..NUM_COMPONENTS { - if self.num_nlq_param_predictors[pivot_idx][cmp] > 0 { - writer.write(self.nlq_param_pred_flag[pivot_idx][cmp]); - } + if header.coefficient_data_type == 0 { + writer.write_ue(&self.vdr_in_max_int[cmp])?; + } + + writer.write_n(&self.vdr_in_max[cmp], coefficient_log2_denom_length)?; - if !self.nlq_param_pred_flag[pivot_idx][cmp] { - // rpu_data_nlq_param + if let Some(nlq_method_idc) = mapping.nlq_method_idc { + if nlq_method_idc == DoviNlqMethod::LinearDeadzone { + // NLQ_LINEAR_DZ + if header.coefficient_data_type == 0 { + writer.write_ue(&self.linear_deadzone_slope_int[cmp])?; + } writer.write_n( - &self.nlq_offset[pivot_idx][cmp].to_be_bytes(), - (header.el_bit_depth_minus8 + 8) as usize, - ); + &self.linear_deadzone_slope[cmp], + coefficient_log2_denom_length, + )?; if header.coefficient_data_type == 0 { - writer.write_ue(self.vdr_in_max_int[pivot_idx][cmp]); + writer.write_ue(&self.linear_deadzone_slope_int[cmp])?; } writer.write_n( - &self.vdr_in_max[pivot_idx][cmp].to_be_bytes(), + &self.linear_deadzone_threshold[cmp], coefficient_log2_denom_length, - ); - - if let Some(nlq_method_idc) = header.nlq_method_idc { - if nlq_method_idc == 0 { - // NLQ_LINEAR_DZ - if header.coefficient_data_type == 0 { - writer.write_ue(self.linear_deadzone_slope_int[pivot_idx][cmp]); - } - - writer.write_n( - &self.linear_deadzone_slope[pivot_idx][cmp].to_be_bytes(), - coefficient_log2_denom_length, - ); - - if header.coefficient_data_type == 0 { - writer.write_ue(self.linear_deadzone_slope_int[pivot_idx][cmp]); - } - - writer.write_n( - &self.linear_deadzone_threshold[pivot_idx][cmp].to_be_bytes(), - coefficient_log2_denom_length, - ); - } - } - } else if self.num_nlq_param_predictors[pivot_idx][cmp] > 1 { - writer.write_ue(self.diff_pred_part_idx_nlq_minus1[pivot_idx][cmp]); + )?; } } } @@ -255,43 +152,28 @@ impl RpuDataNlq { } pub fn mel_default() -> Self { + let zeroed_cmps = [0_u64; NUM_COMPONENTS]; + let vdr_in_max_int = [1; NUM_COMPONENTS]; + Self { - num_nlq_param_predictors: vec![[0; NUM_COMPONENTS]], - nlq_param_pred_flag: vec![[false; NUM_COMPONENTS]], - diff_pred_part_idx_nlq_minus1: vec![[0; NUM_COMPONENTS]], - nlq_offset: vec![[0; NUM_COMPONENTS]], - vdr_in_max_int: vec![[1; NUM_COMPONENTS]], - vdr_in_max: vec![[0; NUM_COMPONENTS]], - linear_deadzone_slope_int: vec![[0; NUM_COMPONENTS]], - linear_deadzone_slope: vec![[0; NUM_COMPONENTS]], - linear_deadzone_threshold_int: vec![[0; NUM_COMPONENTS]], - linear_deadzone_threshold: vec![[0; NUM_COMPONENTS]], + nlq_offset: [0_u16; NUM_COMPONENTS], + vdr_in_max_int, + vdr_in_max: zeroed_cmps, + linear_deadzone_slope_int: zeroed_cmps, + linear_deadzone_slope: zeroed_cmps, + linear_deadzone_threshold_int: zeroed_cmps, + linear_deadzone_threshold: zeroed_cmps, } } pub fn is_mel(&self) -> bool { - let zero_nlq_offset = self.nlq_offset.iter().all(|v| v.iter().all(|e| *e == 0)); - let one_vdr_in_max_int = self - .vdr_in_max_int - .iter() - .all(|v| v.iter().all(|e| *e == 1)); - let one_vdr_in_max = self.vdr_in_max.iter().all(|v| v.iter().all(|e| *e == 0)); - let zero_dz_slope_int = self - .linear_deadzone_slope_int - .iter() - .all(|v| v.iter().all(|e| *e == 0)); - let zero_dz_slope = self - .linear_deadzone_slope - .iter() - .all(|v| v.iter().all(|e| *e == 0)); - let zero_dz_threshold_int = self - .linear_deadzone_threshold_int - .iter() - .all(|v| v.iter().all(|e| *e == 0)); - let zero_dz_threshold = self - .linear_deadzone_threshold - .iter() - .all(|v| v.iter().all(|e| *e == 0)); + let zero_nlq_offset = self.nlq_offset.iter().all(|e| *e == 0); + let one_vdr_in_max_int = self.vdr_in_max_int.iter().all(|e| *e == 1); + let one_vdr_in_max = self.vdr_in_max.iter().all(|e| *e == 0); + let zero_dz_slope_int = self.linear_deadzone_slope_int.iter().all(|e| *e == 0); + let zero_dz_slope = self.linear_deadzone_slope.iter().all(|e| *e == 0); + let zero_dz_threshold_int = self.linear_deadzone_threshold_int.iter().all(|e| *e == 0); + let zero_dz_threshold = self.linear_deadzone_threshold.iter().all(|e| *e == 0); zero_nlq_offset && one_vdr_in_max_int @@ -301,4 +183,27 @@ impl RpuDataNlq { && zero_dz_threshold_int && zero_dz_threshold } + + pub fn el_type(&self) -> DoviELType { + if self.is_mel() { + DoviELType::MEL + } else { + DoviELType::FEL + } + } +} + +impl DoviELType { + pub const fn as_str(&self) -> &'static str { + match self { + DoviELType::MEL => MEL_STR, + DoviELType::FEL => FEL_STR, + } + } +} + +impl Display for DoviELType { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.write_str(self.as_str()) + } } diff --git a/dolby_vision/src/rpu/vdr_dm_data.rs b/dolby_vision/src/rpu/vdr_dm_data.rs index ae8a3ba..8ab0be2 100644 --- a/dolby_vision/src/rpu/vdr_dm_data.rs +++ b/dolby_vision/src/rpu/vdr_dm_data.rs @@ -1,10 +1,11 @@ use anyhow::{bail, ensure, Result}; -use bitvec_helpers::{bitslice_reader::BitSliceReader, bitvec_writer::BitVecWriter}; +use bitvec_helpers::{ + bitstream_io_reader::BsIoSliceReader, bitstream_io_writer::BitstreamIoWriter, +}; #[cfg(feature = "serde")] use serde::{Deserialize, Serialize}; -use super::dovi_rpu::DoviRpu; use super::extension_metadata::blocks::{ ExtMetadataBlock, ExtMetadataBlockLevel11, ExtMetadataBlockLevel9, }; @@ -16,6 +17,7 @@ use super::profiles::profile84::Profile84; use super::profiles::DoviProfile; use super::extension_metadata::WithExtMetadataBlocks; +use super::rpu_data_header::RpuDataHeader; #[derive(Debug, Default, Clone)] #[cfg_attr(feature = "serde", derive(Deserialize, Serialize))] @@ -73,11 +75,11 @@ pub enum CmVersion { } pub(crate) fn vdr_dm_data_payload( - dovi_rpu: &mut DoviRpu, - reader: &mut BitSliceReader, - final_length: usize, -) -> Result<()> { - let compressed_dm_data = dovi_rpu.header.reserved_zero_3bits == 1; + reader: &mut BsIoSliceReader, + header: &RpuDataHeader, + final_length: u64, +) -> Result { + let compressed_dm_data = header.reserved_zero_3bits == 1; let mut vdr_dm_data = if compressed_dm_data { VdrDmData { @@ -97,19 +99,17 @@ pub(crate) fn vdr_dm_data_payload( } // 16 bits min for required level 254 - if reader.available() >= final_length + 16 { + if reader.available()? >= final_length + 16 { if let Some(cmv40_dm_data) = DmData::parse::(reader)? { vdr_dm_data.cmv40_metadata = Some(DmData::V40(cmv40_dm_data)); } } - dovi_rpu.vdr_dm_data = Some(vdr_dm_data); - - Ok(()) + Ok(vdr_dm_data) } impl VdrDmData { - pub(crate) fn parse(reader: &mut BitSliceReader) -> Result { + pub(crate) fn parse(reader: &mut BsIoSliceReader) -> Result { let data = VdrDmData { affected_dm_metadata_id: reader.get_ue()?, current_dm_metadata_id: reader.get_ue()?, @@ -187,49 +187,49 @@ impl VdrDmData { Ok(()) } - pub fn write(&self, writer: &mut BitVecWriter) -> Result<()> { - writer.write_ue(self.affected_dm_metadata_id); - writer.write_ue(self.current_dm_metadata_id); - writer.write_ue(self.scene_refresh_flag); + pub fn write(&self, writer: &mut BitstreamIoWriter) -> Result<()> { + writer.write_ue(&self.affected_dm_metadata_id)?; + writer.write_ue(&self.current_dm_metadata_id)?; + writer.write_ue(&self.scene_refresh_flag)?; if !self.compressed { - writer.write_n(&self.ycc_to_rgb_coef0.to_be_bytes(), 16); - writer.write_n(&self.ycc_to_rgb_coef1.to_be_bytes(), 16); - writer.write_n(&self.ycc_to_rgb_coef2.to_be_bytes(), 16); - writer.write_n(&self.ycc_to_rgb_coef3.to_be_bytes(), 16); - writer.write_n(&self.ycc_to_rgb_coef4.to_be_bytes(), 16); - writer.write_n(&self.ycc_to_rgb_coef5.to_be_bytes(), 16); - writer.write_n(&self.ycc_to_rgb_coef6.to_be_bytes(), 16); - writer.write_n(&self.ycc_to_rgb_coef7.to_be_bytes(), 16); - writer.write_n(&self.ycc_to_rgb_coef8.to_be_bytes(), 16); - - writer.write_n(&self.ycc_to_rgb_offset0.to_be_bytes(), 32); - writer.write_n(&self.ycc_to_rgb_offset1.to_be_bytes(), 32); - writer.write_n(&self.ycc_to_rgb_offset2.to_be_bytes(), 32); - - writer.write_n(&self.rgb_to_lms_coef0.to_be_bytes(), 16); - writer.write_n(&self.rgb_to_lms_coef1.to_be_bytes(), 16); - writer.write_n(&self.rgb_to_lms_coef2.to_be_bytes(), 16); - writer.write_n(&self.rgb_to_lms_coef3.to_be_bytes(), 16); - writer.write_n(&self.rgb_to_lms_coef4.to_be_bytes(), 16); - writer.write_n(&self.rgb_to_lms_coef5.to_be_bytes(), 16); - writer.write_n(&self.rgb_to_lms_coef6.to_be_bytes(), 16); - writer.write_n(&self.rgb_to_lms_coef7.to_be_bytes(), 16); - writer.write_n(&self.rgb_to_lms_coef8.to_be_bytes(), 16); - - writer.write_n(&self.signal_eotf.to_be_bytes(), 16); - writer.write_n(&self.signal_eotf_param0.to_be_bytes(), 16); - writer.write_n(&self.signal_eotf_param1.to_be_bytes(), 16); - writer.write_n(&self.signal_eotf_param2.to_be_bytes(), 32); - - writer.write_n(&self.signal_bit_depth.to_be_bytes(), 5); - writer.write_n(&self.signal_color_space.to_be_bytes(), 2); - writer.write_n(&self.signal_chroma_format.to_be_bytes(), 2); - writer.write_n(&self.signal_full_range_flag.to_be_bytes(), 2); - - writer.write_n(&self.source_min_pq.to_be_bytes(), 12); - writer.write_n(&self.source_max_pq.to_be_bytes(), 12); - writer.write_n(&self.source_diagonal.to_be_bytes(), 10); + writer.write_signed_n(&self.ycc_to_rgb_coef0, 16)?; + writer.write_signed_n(&self.ycc_to_rgb_coef1, 16)?; + writer.write_signed_n(&self.ycc_to_rgb_coef2, 16)?; + writer.write_signed_n(&self.ycc_to_rgb_coef3, 16)?; + writer.write_signed_n(&self.ycc_to_rgb_coef4, 16)?; + writer.write_signed_n(&self.ycc_to_rgb_coef5, 16)?; + writer.write_signed_n(&self.ycc_to_rgb_coef6, 16)?; + writer.write_signed_n(&self.ycc_to_rgb_coef7, 16)?; + writer.write_signed_n(&self.ycc_to_rgb_coef8, 16)?; + + writer.write_n(&self.ycc_to_rgb_offset0, 32)?; + writer.write_n(&self.ycc_to_rgb_offset1, 32)?; + writer.write_n(&self.ycc_to_rgb_offset2, 32)?; + + writer.write_signed_n(&self.rgb_to_lms_coef0, 16)?; + writer.write_signed_n(&self.rgb_to_lms_coef1, 16)?; + writer.write_signed_n(&self.rgb_to_lms_coef2, 16)?; + writer.write_signed_n(&self.rgb_to_lms_coef3, 16)?; + writer.write_signed_n(&self.rgb_to_lms_coef4, 16)?; + writer.write_signed_n(&self.rgb_to_lms_coef5, 16)?; + writer.write_signed_n(&self.rgb_to_lms_coef6, 16)?; + writer.write_signed_n(&self.rgb_to_lms_coef7, 16)?; + writer.write_signed_n(&self.rgb_to_lms_coef8, 16)?; + + writer.write_n(&self.signal_eotf, 16)?; + writer.write_n(&self.signal_eotf_param0, 16)?; + writer.write_n(&self.signal_eotf_param1, 16)?; + writer.write_n(&self.signal_eotf_param2, 32)?; + + writer.write_n(&self.signal_bit_depth, 5)?; + writer.write_n(&self.signal_color_space, 2)?; + writer.write_n(&self.signal_chroma_format, 2)?; + writer.write_n(&self.signal_full_range_flag, 2)?; + + writer.write_n(&self.source_min_pq, 12)?; + writer.write_n(&self.source_max_pq, 12)?; + writer.write_n(&self.source_diagonal, 10)?; } if let Some(cmv29) = &self.cmv29_metadata { diff --git a/dolby_vision/src/st2094_10/itu_t35/cm_data.rs b/dolby_vision/src/st2094_10/itu_t35/cm_data.rs index 2939adc..31bf1e0 100644 --- a/dolby_vision/src/st2094_10/itu_t35/cm_data.rs +++ b/dolby_vision/src/st2094_10/itu_t35/cm_data.rs @@ -1,5 +1,5 @@ use anyhow::Result; -use bitvec_helpers::bitslice_reader::BitSliceReader; +use bitvec_helpers::bitstream_io_reader::BsIoSliceReader; use super::UserDataTypeStruct; @@ -38,7 +38,7 @@ pub struct ST2094_10CmData { } impl ST2094_10CmData { - pub(crate) fn parse(reader: &mut BitSliceReader) -> Result { + pub(crate) fn parse(reader: &mut BsIoSliceReader) -> Result { let mut meta = ST2094_10CmData { ccm_profile: reader.get_n(4)?, ccm_level: reader.get_n(4)?, @@ -50,7 +50,7 @@ impl ST2094_10CmData { ..Default::default() }; - let coefficient_log2_denom_length = meta.coefficient_log2_denom as usize; + let coefficient_log2_denom_length = meta.coefficient_log2_denom as u32; for cmp in 0..NUM_COMPONENTS { meta.num_pivots_minus2[cmp] = reader.get_ue()?; @@ -60,7 +60,7 @@ impl ST2094_10CmData { for pivot_idx in 0..(meta.num_pivots_minus2[cmp] as usize) + 2 { meta.pred_pivot_value[cmp][pivot_idx] = - reader.get_n((meta.el_bit_depth_minus8 as usize) + 8)?; + reader.get_n((meta.el_bit_depth_minus8 as u32) + 8)?; } } @@ -139,7 +139,7 @@ impl ST2094_10CmData { if !meta.disable_residual_flag { for cmp in 0..NUM_COMPONENTS { - meta.nlq_offset[cmp] = reader.get_n((meta.el_bit_depth_minus8 as usize) + 8)?; + meta.nlq_offset[cmp] = reader.get_n((meta.el_bit_depth_minus8 as u32) + 8)?; meta.hdr_in_max_int[cmp] = reader.get_ue()?; meta.hdr_in_max[cmp] = reader.get_n(coefficient_log2_denom_length)?; meta.linear_deadzone_slope_int[cmp] = reader.get_ue()?; diff --git a/dolby_vision/src/st2094_10/itu_t35/dm_data.rs b/dolby_vision/src/st2094_10/itu_t35/dm_data.rs index 05200db..d0a32e6 100644 --- a/dolby_vision/src/st2094_10/itu_t35/dm_data.rs +++ b/dolby_vision/src/st2094_10/itu_t35/dm_data.rs @@ -1,5 +1,5 @@ use anyhow::Result; -use bitvec_helpers::bitslice_reader::BitSliceReader; +use bitvec_helpers::bitstream_io_reader::BsIoSliceReader; use crate::rpu::extension_metadata::{CmV29DmData, DmData}; @@ -14,7 +14,7 @@ pub struct ST2094_10DmData { } impl ST2094_10DmData { - pub(crate) fn parse(reader: &mut BitSliceReader) -> Result { + pub(crate) fn parse(reader: &mut BsIoSliceReader) -> Result { let mut meta = ST2094_10DmData { app_identifier: reader.get_ue()?, app_version: reader.get_ue()?, diff --git a/dolby_vision/src/st2094_10/itu_t35/mod.rs b/dolby_vision/src/st2094_10/itu_t35/mod.rs index 4539163..1f686ac 100644 --- a/dolby_vision/src/st2094_10/itu_t35/mod.rs +++ b/dolby_vision/src/st2094_10/itu_t35/mod.rs @@ -1,5 +1,5 @@ use anyhow::{bail, ensure, Result}; -use bitvec_helpers::bitslice_reader::BitSliceReader; +use bitvec_helpers::bitstream_io_reader::BsIoSliceReader; use crate::utils::clear_start_code_emulation_prevention_3_byte; @@ -27,7 +27,7 @@ impl ST2094_10ItuT35 { let trimmed_data = Self::validated_trimmed_data(data)?; let bytes = clear_start_code_emulation_prevention_3_byte(trimmed_data); - let mut reader = BitSliceReader::new(&bytes); + let mut reader = BsIoSliceReader::from_slice(&bytes); let itu_t_t35_country_code: u8 = reader.get_n(8)?; let itu_t_t35_provider_code: u16 = reader.get_n(16)?; diff --git a/dolby_vision/src/utils.rs b/dolby_vision/src/utils.rs index 3d63a6b..6c723f6 100644 --- a/dolby_vision/src/utils.rs +++ b/dolby_vision/src/utils.rs @@ -24,23 +24,23 @@ pub fn nits_to_pq(nits: f64) -> f64 { /// Unescapes a byte slice from annexb. /// Allocates a new Vec. pub fn clear_start_code_emulation_prevention_3_byte(data: &[u8]) -> Vec { - let mut unescaped_bytes: Vec = Vec::with_capacity(data.len()); + let len = data.len(); - let unescaped_bytes_iter = data.iter().enumerate().filter_map(|(index, value)| { - if index > 2 - && index < data.len() - 2 - && data[index - 2] == 0 - && data[index - 1] == 0 - && data[index] == 3 - { - None - } else { - Some(*value) + if len > 2 { + let mut unescaped_bytes: Vec = Vec::with_capacity(len); + unescaped_bytes.push(data[0]); + unescaped_bytes.push(data[1]); + + for i in 2..len { + if !(data[i - 2] == 0 && data[i - 1] == 0 && data[i] == 3) { + unescaped_bytes.push(data[i]); + } } - }); - unescaped_bytes.extend(unescaped_bytes_iter); - unescaped_bytes + unescaped_bytes + } else { + data.to_owned() + } } /// Escapes the vec to annexb to avoid emulating a start code by accident @@ -67,3 +67,17 @@ pub(crate) fn bitvec_ser_bits( let bits: Vec = bitvec.iter().map(|b| *b as u8).collect(); bits.serialize(s) } + +/// Serializing an optional bitvec as a vec of bits +#[cfg(feature = "serde")] +pub(crate) fn opt_bitvec_ser_bits( + bitvec: &Option>, + s: S, +) -> Result { + let bits: Vec = if let Some(vec) = bitvec { + vec.iter().map(|b| *b as u8).collect() + } else { + Vec::new() + }; + bits.serialize(s) +} diff --git a/src/dovi/hdr10plus_utils.rs b/src/dovi/hdr10plus_utils.rs index 305297f..08b7754 100644 --- a/src/dovi/hdr10plus_utils.rs +++ b/src/dovi/hdr10plus_utils.rs @@ -1,6 +1,6 @@ use anyhow::Result; -use bitvec_helpers::bitslice_reader::BitSliceReader; +use bitvec_helpers::bitstream_io_reader::BsIoSliceReader; use hevc_parser::hevc::{NALUnit, SeiMessage, NAL_SEI_PREFIX, USER_DATA_REGISTERED_ITU_T_35}; use hevc_parser::utils::{ add_start_code_emulation_prevention_3_byte, clear_start_code_emulation_prevention_3_byte, @@ -17,7 +17,8 @@ pub fn st2094_40_sei_msg(sei_payload: &[u8]) -> Result> { let start = msg.payload_offset; let end = start + msg.payload_size; - let mut reader = BitSliceReader::new(&sei_payload[start..end]); + let bytes = &sei_payload[start..end]; + let mut reader = BsIoSliceReader::from_slice(bytes); let itu_t_t35_country_code = reader.get_n::(8).unwrap(); let itu_t_t35_terminal_provider_code = reader.get_n::(16).unwrap(); diff --git a/src/dovi/muxer.rs b/src/dovi/muxer.rs index a6e19fc..8d0b645 100644 --- a/src/dovi/muxer.rs +++ b/src/dovi/muxer.rs @@ -198,7 +198,7 @@ impl IoProcessor for Muxer { NalBuffer { nal_type: NAL_AUD, start_code: NALUStartCode::Length4, - data: hevc_parser::utils::aud_for_frame(previous_frame, None), + data: hevc_parser::utils::aud_for_frame(previous_frame, None)?, }, ); } @@ -275,7 +275,7 @@ impl IoProcessor for Muxer { NalBuffer { nal_type: NAL_AUD, start_code: NALUStartCode::Length4, - data: hevc_parser::utils::aud_for_frame(last_frame, None), + data: hevc_parser::utils::aud_for_frame(last_frame, None)?, }, ); } diff --git a/src/dovi/rpu_info.rs b/src/dovi/rpu_info.rs index 062c49d..8ad3ea1 100644 --- a/src/dovi/rpu_info.rs +++ b/src/dovi/rpu_info.rs @@ -109,7 +109,7 @@ impl RpuInfo { if profiles.contains('7') { let subprofiles = rpus .iter() - .filter_map(|rpu| rpu.subprofile.as_ref()) + .filter_map(|rpu| rpu.el_type.as_ref().map(|e| e.to_string())) .unique() .sorted() .join(", "); diff --git a/src/dovi/rpu_injector.rs b/src/dovi/rpu_injector.rs index f7df413..3e12a67 100644 --- a/src/dovi/rpu_injector.rs +++ b/src/dovi/rpu_injector.rs @@ -272,7 +272,7 @@ impl IoProcessor for RpuInjector { NalBuffer { nal_type: NAL_AUD, start_code: NALUStartCode::Length4, - data: hevc_parser::utils::aud_for_frame(buffered_frame, None), + data: hevc_parser::utils::aud_for_frame(buffered_frame, None)?, }, ); } @@ -361,7 +361,7 @@ impl IoProcessor for RpuInjector { NalBuffer { nal_type: NAL_AUD, start_code: NALUStartCode::Length4, - data: hevc_parser::utils::aud_for_frame(last_frame, None), + data: hevc_parser::utils::aud_for_frame(last_frame, None)?, }, ); } diff --git a/src/tests/rpu.rs b/src/tests/rpu.rs index 92633ee..f728494 100644 --- a/src/tests/rpu.rs +++ b/src/tests/rpu.rs @@ -7,7 +7,8 @@ use dolby_vision::rpu::dovi_rpu::DoviRpu; use dolby_vision::rpu::extension_metadata::blocks::ExtMetadataBlock; use dolby_vision::rpu::extension_metadata::{ColorPrimaries, MasteringDisplayPrimaries}; use dolby_vision::rpu::generate::GenerateConfig; -use dolby_vision::rpu::{ConversionMode, FEL_STR, MEL_STR}; +use dolby_vision::rpu::rpu_data_nlq::DoviELType; +use dolby_vision::rpu::ConversionMode; use hevc_parser::hevc::{NALUnit, NAL_UNSPEC62}; use crate::commands::GenerateArgs; @@ -93,7 +94,7 @@ fn profile8() -> Result<()> { fn fel() -> Result<()> { let (original_data, dovi_rpu) = _parse_file(PathBuf::from("./assets/tests/fel_rpu.bin"))?; assert_eq!(dovi_rpu.dovi_profile, 7); - assert_eq!(dovi_rpu.subprofile.unwrap(), FEL_STR); + assert_eq!(dovi_rpu.el_type.as_ref().unwrap(), &DoviELType::FEL); let parsed_data = dovi_rpu.write_hevc_unspec62_nalu()?; @@ -106,7 +107,7 @@ fn fel() -> Result<()> { fn mel() -> Result<()> { let (original_data, dovi_rpu) = _parse_file(PathBuf::from("./assets/tests/mel_rpu.bin"))?; assert_eq!(dovi_rpu.dovi_profile, 7); - assert_eq!(dovi_rpu.subprofile.unwrap(), MEL_STR); + assert_eq!(dovi_rpu.el_type.as_ref().unwrap(), &DoviELType::MEL); let parsed_data = dovi_rpu.write_hevc_unspec62_nalu()?; @@ -1038,7 +1039,9 @@ fn p81_to_p84() -> Result<()> { assert_eq!(&p81_data[4..], &parsed_data[2..]); assert_eq!(dovi_rpu.dovi_profile, 8); - assert_eq!(dovi_rpu.header.num_pivots_minus_2[0], 7); + + let num_pivots = dovi_rpu.rpu_data_mapping.unwrap().curves[0].num_pivots_minus2; + assert_eq!(num_pivots, 7); Ok(()) } @@ -1060,7 +1063,8 @@ fn profile5_to_p84() -> Result<()> { assert_eq!(&p81_data[4..], &parsed_data[2..]); assert_eq!(dovi_rpu.dovi_profile, 8); - assert_eq!(dovi_rpu.header.num_pivots_minus_2[0], 7); + let num_pivots = dovi_rpu.rpu_data_mapping.unwrap().curves[0].num_pivots_minus2; + assert_eq!(num_pivots, 7); Ok(()) }