1 /** DOVI metadata helper functions
2
3 mkvmerge -- utility for splicing together matroska files
4 from component media subtypes
5
6 Distributed under the GPL v2
7 see the file COPYING for details
8 or visit https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
9
10 \file
11
12 */
13
14 #include "common/bit_reader.h"
15 #include "common/bit_writer.h"
16 #include "common/mm_mem_io.h"
17 #include "common/codec.h"
18
19 #include "common/dovi_meta.h"
20
21 namespace mtx::dovi {
22
23 void
dump()24 dovi_rpu_data_header_t::dump() {
25 mxinfo(fmt::format("dovi_rpu_data_header\n"
26 " rpu_nal_prefix: {0}\n"
27 " rpu_type: {1}\n"
28 " rpu_format: {2}\n"
29 " vdr_rpu_profile: {3}\n"
30 " vdr_rpu_level: {4}\n"
31 " vdr_seq_info_present_flag: {5}\n"
32 " chroma_resampling_explicit_filter_flag: {6}\n"
33 " coefficient_data_type: {7}\n"
34 " coefficient_log2_denom: {8}\n"
35 " vdr_rpu_normalized_idc: {9}\n"
36 " bl_video_full_range_flag: {10}\n"
37 " bl_bit_depth_minus8: {11}\n"
38 " el_bit_depth_minus8: {12}\n"
39 " vdr_bit_depth_minus_8: {13}\n"
40 " spatial_resampling_filter_flag: {14}\n"
41 " reserved_zero_3bits: {15}\n"
42 " el_spatial_resampling_filter_flag: {16}\n"
43 " disable_residual_flag: {17}\n"
44 " vdr_dm_metadata_present_flag: {18}\n"
45 " use_prev_vdr_rpu_flag: {19}\n"
46 " prev_vdr_rpu_id: {20}\n"
47 " vdr_rpu_id: {21}\n"
48 " mapping_color_space: {22}\n"
49 " mapping_chroma_format_idc: {23}\n"
50 " num_x_partitions_minus1: {24}\n"
51 " num_y_partitions_minus1: {25}\n",
52 rpu_nal_prefix,
53 rpu_type,
54 rpu_format,
55 vdr_rpu_profile,
56 vdr_rpu_level,
57 vdr_seq_info_present_flag,
58 chroma_resampling_explicit_filter_flag,
59 coefficient_data_type,
60 coefficient_log2_denom,
61 vdr_rpu_normalized_idc,
62 bl_video_full_range_flag,
63 bl_bit_depth_minus8,
64 el_bit_depth_minus8,
65 vdr_bit_depth_minus_8,
66 spatial_resampling_filter_flag,
67 reserved_zero_3bits,
68 el_spatial_resampling_filter_flag,
69 disable_residual_flag,
70 vdr_dm_metadata_present_flag,
71 use_prev_vdr_rpu_flag,
72 prev_vdr_rpu_id,
73 vdr_rpu_id,
74 mapping_color_space,
75 mapping_chroma_format_idc,
76 num_x_partitions_minus1,
77 num_y_partitions_minus1
78 ));
79 }
80
81 void
dump()82 dovi_decoder_configuration_record_t::dump() {
83 mxinfo(fmt::format("dovi_decoder_configuration_record\n"
84 " dv_version_major: {0}\n"
85 " dv_version_minor: {1}\n"
86 " dv_profile: {2}\n"
87 " dv_level: {3}\n"
88 " rpu_present_flag: {4}\n"
89 " el_present_flag: {5}\n"
90 " bl_present_flag: {6}\n"
91 " dv_bl_signal_compatibility_id: {7}\n",
92 dv_version_major,
93 dv_version_minor,
94 dv_profile,
95 dv_level,
96 rpu_present_flag,
97 el_present_flag,
98 bl_present_flag,
99 dv_bl_signal_compatibility_id
100 ));
101 }
102
103 dovi_decoder_configuration_record_t
create_dovi_configuration_record(dovi_rpu_data_header_t const & hdr,unsigned int width,unsigned int height,mtx::hevc::vui_info_t const & vui,uint64_t duration)104 create_dovi_configuration_record(dovi_rpu_data_header_t const &hdr,
105 unsigned int width,
106 unsigned int height,
107 mtx::hevc::vui_info_t const &vui,
108 uint64_t duration) {
109 dovi_decoder_configuration_record_t conf{};
110
111 bool has_el = hdr.el_spatial_resampling_filter_flag && !hdr.disable_residual_flag;
112 conf.dv_version_major = 1;
113 conf.dv_version_minor = 0;
114
115 if (hdr.rpu_nal_prefix == 25) {
116 if ((hdr.vdr_rpu_profile == 0) && hdr.bl_video_full_range_flag)
117 conf.dv_profile = 5;
118
119 else if (has_el) {
120 // Profile 7 is max 12 bits, both MEL & FEL
121 if (hdr.vdr_bit_depth_minus_8 == 4)
122 conf.dv_profile = 7;
123 else
124 conf.dv_profile = 4;
125
126 } else
127 conf.dv_profile = 8;
128 }
129
130 conf.dv_level = calculate_dovi_level(width, height, duration);
131
132 // In all single PID cases, these are set to 1
133 conf.rpu_present_flag = 1;
134 conf.bl_present_flag = 1;
135
136 conf.el_present_flag = 0;
137
138 if (conf.dv_profile == 4)
139 conf.dv_bl_signal_compatibility_id = 2;
140
141 else if (conf.dv_profile == 5)
142 conf.dv_bl_signal_compatibility_id = 0;
143
144 else if (conf.dv_profile == 8) {
145 auto trc = vui.transfer_characteristics;
146
147 // WCG
148 if (vui.colour_primaries == 9 && vui.matrix_coefficients == 9) {
149 if (trc == 16) // ST2084, HDR10 base layer
150 conf.dv_bl_signal_compatibility_id = 1;
151 else if ((trc == 14) || (trc == 18)) // ARIB STD-B67, HLG base layer
152 conf.dv_bl_signal_compatibility_id = 4;
153 else // undefined
154 conf.dv_bl_signal_compatibility_id = 0;
155
156 } else // BT.709, BT.1886, SDR
157 conf.dv_bl_signal_compatibility_id = 2;
158
159 } else if (conf.dv_profile == 7)
160 conf.dv_bl_signal_compatibility_id = 6;
161
162 else if (conf.dv_profile == 9)
163 conf.dv_bl_signal_compatibility_id = 2;
164
165 // Profile 4 is necessarily SDR with an enhancement-layer
166 // It's possible that the first guess was wrong, so correct it
167 if (has_el && (conf.dv_bl_signal_compatibility_id == 2) && (conf.dv_profile != 4))
168 conf.dv_profile = 4;
169
170 // Set EL present for profile 4 and 7
171 if ((conf.dv_profile == 4) || (conf.dv_profile == 7)) {
172 conf.el_present_flag = 1;
173 }
174
175 return conf;
176 }
177
178 uint8_t
calculate_dovi_level(unsigned int width,unsigned int height,uint64_t duration)179 calculate_dovi_level(unsigned int width,
180 unsigned int height,
181 uint64_t duration) {
182 constexpr auto level1 = 22'118'400ull;
183 constexpr auto level2 = 27'648'000ull;
184 constexpr auto level3 = 49'766'400ull;
185 constexpr auto level4 = 62'208'000ull;
186 constexpr auto level5 = 124'416'000ull;
187 constexpr auto level6 = 199'065'600ull;
188 constexpr auto level7 = 248'832'000ull;
189 constexpr auto level8 = 398'131'200ull;
190 constexpr auto level9 = 497'664'000ull;
191 constexpr auto level10_11 = 995'328'000ull;
192 constexpr auto level12 = 1'990'656'000ull;
193 constexpr auto level13 = 3'981'312'000ull;
194
195 auto frame_rate = 1'000'000'000ull / duration;
196 auto pps = frame_rate * (width * height);
197
198 uint8_t level = pps <= level1 ? 1
199 : pps <= level2 ? 2
200 : pps <= level3 ? 3
201 : pps <= level4 ? 4
202 : pps <= level5 ? 5
203 : pps <= level6 ? 6
204 : pps <= level7 ? 7
205 : pps <= level8 ? 8
206 : pps <= level9 ? 9
207 : (pps <= level10_11) && (width <= 3840) ? 10
208 : pps <= level10_11 ? 11
209 : pps <= level12 ? 12
210 : pps <= level13 ? 13
211 : 0;
212
213 return level;
214 }
215
216 block_addition_mapping_t
create_dovi_block_addition_mapping(dovi_decoder_configuration_record_t const & dovi_conf)217 create_dovi_block_addition_mapping(dovi_decoder_configuration_record_t const &dovi_conf) {
218 block_addition_mapping_t mapping;
219
220 mapping.id_name = "Dolby Vision configuration";
221 mapping.id_type = dovi_conf.dv_profile > 7 ? fourcc_c{"dvvC"}.value() : fourcc_c{"dvcC"}.value();
222
223 mtx::bits::writer_c w{};
224
225 w.put_bits(8, dovi_conf.dv_version_major);
226 w.put_bits(8, dovi_conf.dv_version_minor);
227 w.put_bits(7, dovi_conf.dv_profile);
228 w.put_bits(6, dovi_conf.dv_level);
229 w.put_bit(dovi_conf.rpu_present_flag);
230 w.put_bit(dovi_conf.el_present_flag);
231 w.put_bit(dovi_conf.bl_present_flag);
232 w.put_bits(4, dovi_conf.dv_bl_signal_compatibility_id);
233
234 w.put_bits(28, 0); // reserved
235 w.put_bits(32, 0); // reserved
236 w.put_bits(32, 0);
237 w.put_bits(32, 0);
238 w.put_bits(32, 0);
239
240 w.byte_align();
241
242 mapping.id_extra_data = w.get_buffer();
243
244 return mapping;
245 }
246
247 } // namespace mtx::dovi
248