/src/ffmpeg/libavcodec/dovi_rpuenc.c
Line | Count | Source |
1 | | /* |
2 | | * Dolby Vision RPU encoder |
3 | | * |
4 | | * Copyright (C) 2024 Niklas Haas |
5 | | * |
6 | | * This file is part of FFmpeg. |
7 | | * |
8 | | * FFmpeg is free software; you can redistribute it and/or |
9 | | * modify it under the terms of the GNU Lesser General Public |
10 | | * License as published by the Free Software Foundation; either |
11 | | * version 2.1 of the License, or (at your option) any later version. |
12 | | * |
13 | | * FFmpeg is distributed in the hope that it will be useful, |
14 | | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
15 | | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
16 | | * Lesser General Public License for more details. |
17 | | * |
18 | | * You should have received a copy of the GNU Lesser General Public |
19 | | * License along with FFmpeg; if not, write to the Free Software |
20 | | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
21 | | */ |
22 | | |
23 | | #include "libavutil/attributes.h" |
24 | | #include "libavutil/avassert.h" |
25 | | #include "libavutil/crc.h" |
26 | | #include "libavutil/mem.h" |
27 | | #include "libavutil/refstruct.h" |
28 | | |
29 | | #include "avcodec.h" |
30 | | #include "dovi_rpu.h" |
31 | | #include "itut35.h" |
32 | | #include "put_bits.h" |
33 | | #include "put_golomb.h" |
34 | | |
35 | | static const struct { |
36 | | uint64_t pps; // maximum pixels per second |
37 | | int width; // maximum width |
38 | | int main; // maximum bitrate in main tier |
39 | | int high; // maximum bitrate in high tier |
40 | | } dv_levels[] = { |
41 | | [1] = {1280*720*24, 1280, 20, 50}, |
42 | | [2] = {1280*720*30, 1280, 20, 50}, |
43 | | [3] = {1920*1080*24, 1920, 20, 70}, |
44 | | [4] = {1920*1080*30, 2560, 20, 70}, |
45 | | [5] = {1920*1080*60, 3840, 20, 70}, |
46 | | [6] = {3840*2160*24, 3840, 25, 130}, |
47 | | [7] = {3840*2160*30, 3840, 25, 130}, |
48 | | [8] = {3840*2160*48, 3840, 40, 130}, |
49 | | [9] = {3840*2160*60, 3840, 40, 130}, |
50 | | [10] = {3840*2160*120, 3840, 60, 240}, |
51 | | [11] = {3840*2160*120, 7680, 60, 240}, |
52 | | [12] = {7680*4320*60, 7680, 120, 450}, |
53 | | [13] = {7680*4320*120u, 7680, 240, 800}, |
54 | | }; |
55 | | |
56 | | static av_cold int dovi_configure_ext(DOVIContext *s, enum AVCodecID codec_id, |
57 | | const AVDOVIMetadata *metadata, |
58 | | enum AVDOVICompression compression, |
59 | | int strict_std_compliance, |
60 | | int width, int height, |
61 | | AVRational framerate, |
62 | | enum AVPixelFormat pix_format, |
63 | | enum AVColorSpace color_space, |
64 | | enum AVColorPrimaries color_primaries, |
65 | | enum AVColorTransferCharacteristic color_trc, |
66 | | AVPacketSideData **coded_side_data, |
67 | | int *nb_coded_side_data) |
68 | 170 | { |
69 | 170 | AVDOVIDecoderConfigurationRecord *cfg; |
70 | 170 | const AVDOVIRpuDataHeader *hdr = NULL; |
71 | 170 | int dv_profile, dv_level, bl_compat_id = -1; |
72 | 170 | size_t cfg_size; |
73 | 170 | uint64_t pps; |
74 | | |
75 | 170 | if (!s->enable) |
76 | 0 | goto skip; |
77 | | |
78 | 170 | if (metadata) |
79 | 0 | hdr = av_dovi_get_header(metadata); |
80 | | |
81 | 170 | if (s->enable == FF_DOVI_AUTOMATIC && !hdr) |
82 | 0 | goto skip; |
83 | | |
84 | 170 | if (compression == AV_DOVI_COMPRESSION_RESERVED || |
85 | 170 | compression > AV_DOVI_COMPRESSION_EXTENDED) |
86 | 0 | return AVERROR(EINVAL); |
87 | | |
88 | 170 | switch (codec_id) { |
89 | 73 | case AV_CODEC_ID_AV1: dv_profile = 10; break; |
90 | 0 | case AV_CODEC_ID_H264: dv_profile = 9; break; |
91 | 97 | case AV_CODEC_ID_HEVC: |
92 | 97 | if (hdr) { |
93 | 0 | dv_profile = ff_dovi_guess_profile_hevc(hdr); |
94 | 0 | break; |
95 | 0 | } |
96 | | |
97 | | /* This is likely to be proprietary IPTPQc2 */ |
98 | 97 | if (color_space == AVCOL_SPC_IPT_C2 || |
99 | 97 | (color_space == AVCOL_SPC_UNSPECIFIED && |
100 | 97 | color_trc == AVCOL_TRC_UNSPECIFIED)) |
101 | 97 | dv_profile = 5; |
102 | 0 | else |
103 | 0 | dv_profile = 8; |
104 | 97 | break; |
105 | 0 | default: |
106 | 0 | av_unreachable("ff_dovi_configure only used with AV1, H.264 and HEVC"); |
107 | 170 | } |
108 | | |
109 | 170 | if (strict_std_compliance > FF_COMPLIANCE_UNOFFICIAL) { |
110 | 170 | if (dv_profile == 9) { |
111 | 0 | if (pix_format != AV_PIX_FMT_YUV420P) |
112 | 0 | dv_profile = 0; |
113 | 170 | } else { |
114 | 170 | if (pix_format != AV_PIX_FMT_YUV420P10) |
115 | 170 | dv_profile = 0; |
116 | 170 | } |
117 | 170 | } |
118 | | |
119 | 170 | switch (dv_profile) { |
120 | 0 | case 4: /* HEVC with enhancement layer */ |
121 | 0 | case 7: |
122 | 0 | if (s->enable > 0) { |
123 | 0 | av_log(s->logctx, AV_LOG_ERROR, "Coding of Dolby Vision enhancement " |
124 | 0 | "layers is currently unsupported."); |
125 | 0 | return AVERROR_PATCHWELCOME; |
126 | 0 | } else { |
127 | 0 | goto skip; |
128 | 0 | } |
129 | 0 | case 5: /* HEVC with proprietary IPTPQc2 */ |
130 | 0 | bl_compat_id = 0; |
131 | 0 | break; |
132 | 0 | case 10: |
133 | | /* FIXME: check for proper H.273 tags once those are added */ |
134 | 0 | if (hdr && hdr->bl_video_full_range_flag) { |
135 | | /* AV1 with proprietary IPTPQc2 */ |
136 | 0 | bl_compat_id = 0; |
137 | 0 | break; |
138 | 0 | } |
139 | 0 | av_fallthrough; |
140 | 0 | case 8: /* HEVC (or AV1) with BL compatibility */ |
141 | 0 | if (color_space == AVCOL_SPC_BT2020_NCL && |
142 | 0 | color_primaries == AVCOL_PRI_BT2020 && |
143 | 0 | color_trc == AVCOL_TRC_SMPTE2084) { |
144 | 0 | bl_compat_id = 1; |
145 | 0 | } else if (color_space == AVCOL_SPC_BT2020_NCL && |
146 | 0 | color_primaries == AVCOL_PRI_BT2020 && |
147 | 0 | color_trc == AVCOL_TRC_ARIB_STD_B67) { |
148 | 0 | bl_compat_id = 4; |
149 | 0 | } else if (color_space == AVCOL_SPC_BT709 && |
150 | 0 | color_primaries == AVCOL_PRI_BT709 && |
151 | 0 | color_trc == AVCOL_TRC_BT709) { |
152 | 0 | bl_compat_id = 2; |
153 | 0 | } |
154 | 170 | } |
155 | | |
156 | 170 | if (!dv_profile || bl_compat_id < 0) { |
157 | 170 | if (s->enable > 0) { |
158 | 170 | av_log(s->logctx, AV_LOG_ERROR, "Dolby Vision enabled, but could " |
159 | 170 | "not determine profile and compatibility mode. Double-check " |
160 | 170 | "colorspace and format settings for compatibility?\n"); |
161 | 170 | return AVERROR(EINVAL); |
162 | 170 | } |
163 | 0 | goto skip; |
164 | 170 | } |
165 | | |
166 | 0 | if (compression != AV_DOVI_COMPRESSION_NONE) { |
167 | 0 | if (dv_profile < 8 && strict_std_compliance > FF_COMPLIANCE_UNOFFICIAL) { |
168 | 0 | av_log(s->logctx, AV_LOG_ERROR, "Dolby Vision metadata compression " |
169 | 0 | "is not permitted for profiles 7 and earlier. (dv_profile: %d, " |
170 | 0 | "compression: %d)\n", dv_profile, compression); |
171 | 0 | return AVERROR(EINVAL); |
172 | 0 | } else if (compression == AV_DOVI_COMPRESSION_EXTENDED && |
173 | 0 | strict_std_compliance > FF_COMPLIANCE_EXPERIMENTAL) { |
174 | 0 | av_log(s->logctx, AV_LOG_ERROR, "Dolby Vision extended metadata " |
175 | 0 | "compression is experimental and not supported by " |
176 | 0 | "devices."); |
177 | 0 | return AVERROR(EINVAL); |
178 | 0 | } else if (dv_profile == 8) { |
179 | 0 | av_log(s->logctx, AV_LOG_WARNING, "Dolby Vision metadata compression " |
180 | 0 | "for profile 8 is known to be unsupported by many devices, " |
181 | 0 | "use with caution.\n"); |
182 | 0 | } |
183 | 0 | } |
184 | | |
185 | 0 | pps = width * height; |
186 | 0 | if (framerate.num) { |
187 | 0 | pps = pps * framerate.num / framerate.den; |
188 | 0 | } else { |
189 | 0 | pps *= 25; /* sanity fallback */ |
190 | 0 | } |
191 | |
|
192 | 0 | dv_level = 0; |
193 | 0 | for (int i = 1; i < FF_ARRAY_ELEMS(dv_levels); i++) { |
194 | 0 | if (pps > dv_levels[i].pps) |
195 | 0 | continue; |
196 | 0 | if (width > dv_levels[i].width) |
197 | 0 | continue; |
198 | | /* In theory, we should also test the bitrate when known, and |
199 | | * distinguish between main and high tier. In practice, just ignore |
200 | | * the bitrate constraints and hope they work out. This would ideally |
201 | | * be handled by either the encoder or muxer directly. */ |
202 | 0 | dv_level = i; |
203 | 0 | break; |
204 | 0 | } |
205 | |
|
206 | 0 | if (!dv_level) { |
207 | 0 | if (strict_std_compliance >= FF_COMPLIANCE_STRICT) { |
208 | 0 | av_log(s->logctx, AV_LOG_ERROR, "Coded PPS (%"PRIu64") and width (%d) " |
209 | 0 | "exceed Dolby Vision limitations\n", pps, width); |
210 | 0 | return AVERROR(EINVAL); |
211 | 0 | } else { |
212 | 0 | av_log(s->logctx, AV_LOG_WARNING, "Coded PPS (%"PRIu64") and width (%d) " |
213 | 0 | "exceed Dolby Vision limitations. Ignoring, resulting file " |
214 | 0 | "may be non-conforming.\n", pps, width); |
215 | 0 | dv_level = FF_ARRAY_ELEMS(dv_levels) - 1; |
216 | 0 | } |
217 | 0 | } |
218 | | |
219 | 0 | cfg = av_dovi_alloc(&cfg_size); |
220 | 0 | if (!cfg) |
221 | 0 | return AVERROR(ENOMEM); |
222 | | |
223 | 0 | if (!av_packet_side_data_add(coded_side_data, |
224 | 0 | nb_coded_side_data, |
225 | 0 | AV_PKT_DATA_DOVI_CONF, cfg, cfg_size, 0)) { |
226 | 0 | av_free(cfg); |
227 | 0 | return AVERROR(ENOMEM); |
228 | 0 | } |
229 | | |
230 | 0 | cfg->dv_version_major = 1; |
231 | 0 | cfg->dv_version_minor = 0; |
232 | 0 | cfg->dv_profile = dv_profile; |
233 | 0 | cfg->dv_level = dv_level; |
234 | 0 | cfg->rpu_present_flag = 1; |
235 | 0 | cfg->el_present_flag = 0; |
236 | 0 | cfg->bl_present_flag = 1; |
237 | 0 | cfg->dv_bl_signal_compatibility_id = bl_compat_id; |
238 | 0 | cfg->dv_md_compression = compression; |
239 | |
|
240 | 0 | s->cfg = *cfg; |
241 | 0 | return 0; |
242 | | |
243 | 0 | skip: |
244 | 0 | s->cfg = (AVDOVIDecoderConfigurationRecord) {0}; |
245 | 0 | return 0; |
246 | 0 | } |
247 | | |
248 | | av_cold int ff_dovi_configure_from_codedpar(DOVIContext *s, AVCodecParameters *par, |
249 | | const AVDOVIMetadata *metadata, |
250 | | enum AVDOVICompression compression, |
251 | | int strict_std_compliance) |
252 | 170 | { |
253 | 170 | return dovi_configure_ext(s, par->codec_id, metadata, compression, |
254 | 170 | strict_std_compliance, par->width, par->height, |
255 | 170 | par->framerate, par->format, par->color_space, |
256 | 170 | par->color_primaries, par->color_trc, |
257 | 170 | &par->coded_side_data, &par->nb_coded_side_data); |
258 | 170 | } |
259 | | |
260 | | av_cold int ff_dovi_configure(DOVIContext *s, AVCodecContext *avctx) |
261 | 0 | { |
262 | 0 | const AVDOVIMetadata *metadata = NULL; |
263 | 0 | const AVFrameSideData *sd; |
264 | 0 | sd = av_frame_side_data_get(avctx->decoded_side_data, |
265 | 0 | avctx->nb_decoded_side_data, |
266 | 0 | AV_FRAME_DATA_DOVI_METADATA); |
267 | 0 | if (sd) |
268 | 0 | metadata = (const AVDOVIMetadata *) sd->data; |
269 | | |
270 | | /* Current encoders cannot handle metadata compression during encoding */ |
271 | 0 | return dovi_configure_ext(s, avctx->codec_id, metadata, AV_DOVI_COMPRESSION_NONE, |
272 | 0 | avctx->strict_std_compliance, avctx->width, |
273 | 0 | avctx->height, avctx->framerate, avctx->pix_fmt, |
274 | 0 | avctx->colorspace, avctx->color_primaries, avctx->color_trc, |
275 | 0 | &avctx->coded_side_data, &avctx->nb_coded_side_data); |
276 | 0 | } |
277 | | |
278 | | /* Compares only the static DM metadata parts of AVDOVIColorMetadata (excluding |
279 | | * dm_metadata_id and scene_refresh_flag) */ |
280 | | static int cmp_dm_level0(const AVDOVIColorMetadata *dm1, |
281 | | const AVDOVIColorMetadata *dm2) |
282 | 0 | { |
283 | 0 | int ret; |
284 | 0 | for (int i = 0; i < FF_ARRAY_ELEMS(dm1->ycc_to_rgb_matrix); i++) { |
285 | 0 | if ((ret = av_cmp_q(dm1->ycc_to_rgb_matrix[i], dm2->ycc_to_rgb_matrix[i]))) |
286 | 0 | return ret; |
287 | 0 | } |
288 | | |
289 | 0 | for (int i = 0; i < FF_ARRAY_ELEMS(dm1->ycc_to_rgb_offset); i++) { |
290 | 0 | if ((ret = av_cmp_q(dm1->ycc_to_rgb_offset[i], dm2->ycc_to_rgb_offset[i]))) |
291 | 0 | return ret; |
292 | 0 | } |
293 | | |
294 | 0 | for (int i = 0; i < FF_ARRAY_ELEMS(dm1->rgb_to_lms_matrix); i++) { |
295 | 0 | if ((ret = av_cmp_q(dm1->rgb_to_lms_matrix[i], dm2->rgb_to_lms_matrix[i]))) |
296 | 0 | return ret; |
297 | 0 | } |
298 | | |
299 | 0 | return memcmp(&dm1->signal_eotf, &dm2->signal_eotf, |
300 | 0 | sizeof(AVDOVIColorMetadata) -offsetof(AVDOVIColorMetadata, signal_eotf)); |
301 | 0 | } |
302 | | |
303 | | /* Tries to reuse the static ext blocks. May reorder `ext->dm_static` */ |
304 | | static int try_reuse_ext(DOVIExt *ext, const AVDOVIMetadata *metadata) |
305 | 0 | { |
306 | 0 | int i, j, idx = 0; |
307 | |
|
308 | 0 | for (i = 0; i < metadata->num_ext_blocks; i++) { |
309 | 0 | const AVDOVIDmData *dm = av_dovi_get_ext(metadata, i); |
310 | 0 | if (!ff_dovi_rpu_extension_is_static(dm->level)) |
311 | 0 | continue; |
312 | | |
313 | | /* Find the first matching ext block and move it to [idx] */ |
314 | 0 | for (j = idx; j < ext->num_static; j++) { |
315 | 0 | if (!memcmp(&ext->dm_static[j], dm, sizeof(*dm))) { |
316 | 0 | if (j != idx) |
317 | 0 | FFSWAP(AVDOVIDmData, ext->dm_static[j], ext->dm_static[idx]); |
318 | 0 | idx++; |
319 | 0 | break; |
320 | 0 | } |
321 | 0 | } |
322 | |
|
323 | 0 | if (j == ext->num_static) { |
324 | | /* Found no matching ext block */ |
325 | 0 | return 0; |
326 | 0 | } |
327 | 0 | } |
328 | | |
329 | | /* If idx is less than ext->num_static, then there are extra unmatched |
330 | | * ext blocks inside ext->dm_static */ |
331 | 0 | return idx == ext->num_static; |
332 | 0 | } |
333 | | |
334 | | static inline void put_ue_coef(PutBitContext *pb, const AVDOVIRpuDataHeader *hdr, |
335 | | uint64_t coef) |
336 | 0 | { |
337 | 0 | union { uint32_t u32; float f32; } fpart; |
338 | |
|
339 | 0 | switch (hdr->coef_data_type) { |
340 | 0 | case RPU_COEFF_FIXED: |
341 | 0 | set_ue_golomb(pb, coef >> hdr->coef_log2_denom); |
342 | 0 | put_bits63(pb, hdr->coef_log2_denom, |
343 | 0 | coef & ((1LL << hdr->coef_log2_denom) - 1)); |
344 | 0 | break; |
345 | 0 | case RPU_COEFF_FLOAT: |
346 | 0 | fpart.f32 = coef / (float) (1LL << hdr->coef_log2_denom); |
347 | 0 | put_bits63(pb, hdr->coef_log2_denom, fpart.u32); |
348 | 0 | break; |
349 | 0 | } |
350 | 0 | } |
351 | | |
352 | | static inline void put_se_coef(PutBitContext *pb, const AVDOVIRpuDataHeader *hdr, |
353 | | uint64_t coef) |
354 | 0 | { |
355 | 0 | union { uint32_t u32; float f32; } fpart; |
356 | |
|
357 | 0 | switch (hdr->coef_data_type) { |
358 | 0 | case RPU_COEFF_FIXED: |
359 | 0 | set_se_golomb(pb, coef >> hdr->coef_log2_denom); |
360 | 0 | put_bits63(pb, hdr->coef_log2_denom, |
361 | 0 | coef & ((1LL << hdr->coef_log2_denom) - 1)); |
362 | 0 | break; |
363 | 0 | case RPU_COEFF_FLOAT: |
364 | 0 | fpart.f32 = coef / (float) (1LL << hdr->coef_log2_denom); |
365 | 0 | put_bits63(pb, hdr->coef_log2_denom, fpart.u32); |
366 | 0 | break; |
367 | 0 | } |
368 | 0 | } |
369 | | |
370 | | static int validate_ue_golomb_value(uint64_t value) |
371 | 0 | { |
372 | 0 | return value <= 0xFFFE; |
373 | 0 | } |
374 | | |
375 | | static int validate_se_golomb_value(int64_t value) |
376 | 0 | { |
377 | 0 | return value >= -0x7FFF && value <= 0x7FFF; |
378 | 0 | } |
379 | | |
380 | | static int validate_ue_coef(const AVDOVIRpuDataHeader *hdr, uint64_t coef) |
381 | 0 | { |
382 | 0 | if (hdr->coef_log2_denom >= 63) |
383 | 0 | return 0; |
384 | 0 | return validate_ue_golomb_value(coef >> hdr->coef_log2_denom); |
385 | 0 | } |
386 | | |
387 | | static int validate_se_coef(const AVDOVIRpuDataHeader *hdr, int64_t coef) |
388 | 0 | { |
389 | 0 | if (hdr->coef_log2_denom >= 63) |
390 | 0 | return 0; |
391 | 0 | return validate_se_golomb_value(coef >> hdr->coef_log2_denom); |
392 | 0 | } |
393 | | |
394 | | static int validate_mapping_for_generation(const AVDOVIRpuDataHeader *hdr, |
395 | | const AVDOVIDataMapping *mapping) |
396 | 0 | { |
397 | 0 | if (!mapping->num_x_partitions || mapping->num_x_partitions > 0xFFFF || |
398 | 0 | !mapping->num_y_partitions || mapping->num_y_partitions > 0xFFFF) |
399 | 0 | return 0; |
400 | | |
401 | 0 | for (int c = 0; c < 3; c++) { |
402 | 0 | const AVDOVIReshapingCurve *curve = &mapping->curves[c]; |
403 | |
|
404 | 0 | if (curve->num_pivots < 2 || curve->num_pivots > AV_DOVI_MAX_PIECES + 1) |
405 | 0 | return 0; |
406 | | |
407 | 0 | for (int i = 1; i < curve->num_pivots; i++) |
408 | 0 | if (curve->pivots[i] < curve->pivots[i - 1]) |
409 | 0 | return 0; |
410 | | |
411 | 0 | for (int i = 0; i < curve->num_pivots - 1; i++) { |
412 | 0 | switch (curve->mapping_idc[i]) { |
413 | 0 | case AV_DOVI_MAPPING_POLYNOMIAL: |
414 | 0 | if (curve->poly_order[i] < 1 || curve->poly_order[i] > 2) |
415 | 0 | return 0; |
416 | 0 | for (int k = 0; k <= curve->poly_order[i]; k++) |
417 | 0 | if (!validate_se_coef(hdr, curve->poly_coef[i][k])) |
418 | 0 | return 0; |
419 | 0 | break; |
420 | 0 | case AV_DOVI_MAPPING_MMR: |
421 | 0 | if (curve->mmr_order[i] < 1 || curve->mmr_order[i] > 3) |
422 | 0 | return 0; |
423 | 0 | if (!validate_se_coef(hdr, curve->mmr_constant[i])) |
424 | 0 | return 0; |
425 | 0 | for (int j = 0; j < curve->mmr_order[i]; j++) |
426 | 0 | for (int k = 0; k < 7; k++) |
427 | 0 | if (!validate_se_coef(hdr, curve->mmr_coef[i][j][k])) |
428 | 0 | return 0; |
429 | 0 | break; |
430 | 0 | default: |
431 | 0 | return 0; |
432 | 0 | } |
433 | 0 | } |
434 | 0 | } |
435 | | |
436 | 0 | if (mapping->nlq_method_idc != AV_DOVI_NLQ_NONE) { |
437 | 0 | if (mapping->nlq_method_idc != AV_DOVI_NLQ_LINEAR_DZ) |
438 | 0 | return 0; |
439 | 0 | for (int c = 0; c < 3; c++) { |
440 | 0 | const AVDOVINLQParams *nlq = &mapping->nlq[c]; |
441 | 0 | if (!validate_ue_coef(hdr, nlq->vdr_in_max) || |
442 | 0 | !validate_ue_coef(hdr, nlq->linear_deadzone_slope) || |
443 | 0 | !validate_ue_coef(hdr, nlq->linear_deadzone_threshold)) |
444 | 0 | return 0; |
445 | 0 | } |
446 | 0 | } |
447 | | |
448 | 0 | return 1; |
449 | 0 | } |
450 | | |
451 | | static int av_q2den(AVRational q, int den) |
452 | 0 | { |
453 | 0 | if (!q.den || q.den == den) |
454 | 0 | return q.num; |
455 | 0 | q = av_mul_q(q, av_make_q(den, 1)); |
456 | 0 | return (q.num + (q.den >> 1)) / q.den; |
457 | 0 | } |
458 | | |
459 | | static void generate_ext_v1(PutBitContext *pb, const AVDOVIDmData *dm) |
460 | 0 | { |
461 | 0 | int ext_block_length, start_pos, pad_bits; |
462 | |
|
463 | 0 | switch (dm->level) { |
464 | 0 | case 1: ext_block_length = 5; break; |
465 | 0 | case 2: ext_block_length = 11; break; |
466 | 0 | case 4: ext_block_length = 3; break; |
467 | 0 | case 5: ext_block_length = 7; break; |
468 | 0 | case 6: ext_block_length = 8; break; |
469 | 0 | case 255: ext_block_length = 6; break; |
470 | 0 | default: return; |
471 | 0 | } |
472 | | |
473 | 0 | set_ue_golomb(pb, ext_block_length); |
474 | 0 | put_bits(pb, 8, dm->level); |
475 | 0 | start_pos = put_bits_count(pb); |
476 | |
|
477 | 0 | switch (dm->level) { |
478 | 0 | case 1: |
479 | 0 | put_bits(pb, 12, dm->l1.min_pq); |
480 | 0 | put_bits(pb, 12, dm->l1.max_pq); |
481 | 0 | put_bits(pb, 12, dm->l1.avg_pq); |
482 | 0 | break; |
483 | 0 | case 2: |
484 | 0 | put_bits(pb, 12, dm->l2.target_max_pq); |
485 | 0 | put_bits(pb, 12, dm->l2.trim_slope); |
486 | 0 | put_bits(pb, 12, dm->l2.trim_offset); |
487 | 0 | put_bits(pb, 12, dm->l2.trim_power); |
488 | 0 | put_bits(pb, 12, dm->l2.trim_chroma_weight); |
489 | 0 | put_bits(pb, 12, dm->l2.trim_saturation_gain); |
490 | 0 | put_sbits(pb, 13, dm->l2.ms_weight); |
491 | 0 | break; |
492 | 0 | case 4: |
493 | 0 | put_bits(pb, 12, dm->l4.anchor_pq); |
494 | 0 | put_bits(pb, 12, dm->l4.anchor_power); |
495 | 0 | break; |
496 | 0 | case 5: |
497 | 0 | put_bits(pb, 13, dm->l5.left_offset); |
498 | 0 | put_bits(pb, 13, dm->l5.right_offset); |
499 | 0 | put_bits(pb, 13, dm->l5.top_offset); |
500 | 0 | put_bits(pb, 13, dm->l5.bottom_offset); |
501 | 0 | break; |
502 | 0 | case 6: |
503 | 0 | put_bits(pb, 16, dm->l6.max_luminance); |
504 | 0 | put_bits(pb, 16, dm->l6.min_luminance); |
505 | 0 | put_bits(pb, 16, dm->l6.max_cll); |
506 | 0 | put_bits(pb, 16, dm->l6.max_fall); |
507 | 0 | break; |
508 | 0 | case 255: |
509 | 0 | put_bits(pb, 8, dm->l255.dm_run_mode); |
510 | 0 | put_bits(pb, 8, dm->l255.dm_run_version); |
511 | 0 | for (int i = 0; i < 4; i++) |
512 | 0 | put_bits(pb, 8, dm->l255.dm_debug[i]); |
513 | 0 | break; |
514 | 0 | } |
515 | | |
516 | 0 | pad_bits = ext_block_length * 8 - (put_bits_count(pb) - start_pos); |
517 | 0 | av_assert1(pad_bits >= 0); |
518 | 0 | put_bits(pb, pad_bits, 0); |
519 | 0 | } |
520 | | |
521 | | static void put_cie_xy(PutBitContext *pb, AVCIExy xy) |
522 | 0 | { |
523 | 0 | const int denom = 32767; |
524 | 0 | put_sbits(pb, 16, av_q2den(xy.x, denom)); |
525 | 0 | put_sbits(pb, 16, av_q2den(xy.y, denom)); |
526 | 0 | } |
527 | | |
528 | 0 | #define ANY6(arr) (arr[0] || arr[1] || arr[2] || arr[3] || arr[4] || arr[5]) |
529 | 0 | #define ANY_XY(xy) (xy.x.num || xy.y.num) |
530 | 0 | #define ANY_CSP(csp) (ANY_XY(csp.prim.r) || ANY_XY(csp.prim.g) || \ |
531 | 0 | ANY_XY(csp.prim.b) || ANY_XY(csp.wp)) |
532 | | |
533 | | static void generate_ext_v2(PutBitContext *pb, const AVDOVIDmData *dm) |
534 | 0 | { |
535 | 0 | int ext_block_length, start_pos, pad_bits; |
536 | |
|
537 | 0 | switch (dm->level) { |
538 | 0 | case 3: ext_block_length = 5; break; |
539 | 0 | case 8: |
540 | 0 | if (ANY6(dm->l8.hue_vector_field)) { |
541 | 0 | ext_block_length = 25; |
542 | 0 | } else if (ANY6(dm->l8.saturation_vector_field)) { |
543 | 0 | ext_block_length = 19; |
544 | 0 | } else if (dm->l8.clip_trim) { |
545 | 0 | ext_block_length = 13; |
546 | 0 | } else if (dm->l8.target_mid_contrast) { |
547 | 0 | ext_block_length = 12; |
548 | 0 | } else { |
549 | 0 | ext_block_length = 10; |
550 | 0 | } |
551 | 0 | break; |
552 | 0 | case 9: |
553 | 0 | if (ANY_CSP(dm->l9.source_display_primaries)) { |
554 | 0 | ext_block_length = 17; |
555 | 0 | } else { |
556 | 0 | ext_block_length = 1; |
557 | 0 | } |
558 | 0 | break; |
559 | 0 | case 10: |
560 | 0 | if (ANY_CSP(dm->l10.target_display_primaries)) { |
561 | 0 | ext_block_length = 21; |
562 | 0 | } else { |
563 | 0 | ext_block_length = 5; |
564 | 0 | } |
565 | 0 | break; |
566 | 0 | case 11: ext_block_length = 4; break; |
567 | 0 | case 254: ext_block_length = 2; break; |
568 | 0 | default: return; |
569 | 0 | } |
570 | | |
571 | 0 | set_ue_golomb(pb, ext_block_length); |
572 | 0 | put_bits(pb, 8, dm->level); |
573 | 0 | start_pos = put_bits_count(pb); |
574 | |
|
575 | 0 | switch (dm->level) { |
576 | 0 | case 3: |
577 | 0 | put_bits(pb, 12, dm->l3.min_pq_offset); |
578 | 0 | put_bits(pb, 12, dm->l3.max_pq_offset); |
579 | 0 | put_bits(pb, 12, dm->l3.avg_pq_offset); |
580 | 0 | break; |
581 | 0 | case 8: |
582 | 0 | put_bits(pb, 8, dm->l8.target_display_index); |
583 | 0 | put_bits(pb, 12, dm->l8.trim_slope); |
584 | 0 | put_bits(pb, 12, dm->l8.trim_offset); |
585 | 0 | put_bits(pb, 12, dm->l8.trim_power); |
586 | 0 | put_bits(pb, 12, dm->l8.trim_chroma_weight); |
587 | 0 | put_bits(pb, 12, dm->l8.trim_saturation_gain); |
588 | 0 | put_bits(pb, 12, dm->l8.ms_weight); |
589 | 0 | if (ext_block_length < 12) |
590 | 0 | break; |
591 | 0 | put_bits(pb, 12, dm->l8.target_mid_contrast); |
592 | 0 | if (ext_block_length < 13) |
593 | 0 | break; |
594 | 0 | put_bits(pb, 12, dm->l8.clip_trim); |
595 | 0 | if (ext_block_length < 19) |
596 | 0 | break; |
597 | 0 | for (int i = 0; i < 6; i++) |
598 | 0 | put_bits(pb, 8, dm->l8.saturation_vector_field[i]); |
599 | 0 | if (ext_block_length < 25) |
600 | 0 | break; |
601 | 0 | for (int i = 0; i < 6; i++) |
602 | 0 | put_bits(pb, 8, dm->l8.hue_vector_field[i]); |
603 | 0 | break; |
604 | 0 | case 9: |
605 | 0 | put_bits(pb, 8, dm->l9.source_primary_index); |
606 | 0 | if (ext_block_length < 17) |
607 | 0 | break; |
608 | 0 | put_cie_xy(pb, dm->l9.source_display_primaries.prim.r); |
609 | 0 | put_cie_xy(pb, dm->l9.source_display_primaries.prim.g); |
610 | 0 | put_cie_xy(pb, dm->l9.source_display_primaries.prim.b); |
611 | 0 | put_cie_xy(pb, dm->l9.source_display_primaries.wp); |
612 | 0 | break; |
613 | 0 | case 10: |
614 | 0 | put_bits(pb, 8, dm->l10.target_display_index); |
615 | 0 | put_bits(pb, 12, dm->l10.target_max_pq); |
616 | 0 | put_bits(pb, 12, dm->l10.target_min_pq); |
617 | 0 | put_bits(pb, 8, dm->l10.target_primary_index); |
618 | 0 | if (ext_block_length < 21) |
619 | 0 | break; |
620 | 0 | put_cie_xy(pb, dm->l10.target_display_primaries.prim.r); |
621 | 0 | put_cie_xy(pb, dm->l10.target_display_primaries.prim.g); |
622 | 0 | put_cie_xy(pb, dm->l10.target_display_primaries.prim.b); |
623 | 0 | put_cie_xy(pb, dm->l10.target_display_primaries.wp); |
624 | 0 | break; |
625 | 0 | case 11: |
626 | 0 | put_bits(pb, 8, dm->l11.content_type); |
627 | 0 | put_bits(pb, 4, dm->l11.whitepoint); |
628 | 0 | put_bits(pb, 1, dm->l11.reference_mode_flag); |
629 | 0 | put_bits(pb, 3, 0); /* reserved */ |
630 | 0 | put_bits(pb, 8, 0); /* reserved */ |
631 | 0 | put_bits(pb, 8, 0); /* reserved */ |
632 | 0 | break; |
633 | 0 | case 254: |
634 | 0 | put_bits(pb, 8, dm->l254.dm_mode); |
635 | 0 | put_bits(pb, 8, dm->l254.dm_version_index); |
636 | 0 | break; |
637 | 0 | } |
638 | | |
639 | 0 | pad_bits = ext_block_length * 8 - (put_bits_count(pb) - start_pos); |
640 | 0 | av_assert1(pad_bits >= 0); |
641 | 0 | put_bits(pb, pad_bits, 0); |
642 | 0 | } |
643 | | |
644 | | int ff_dovi_rpu_generate(DOVIContext *s, const AVDOVIMetadata *metadata, |
645 | | int flags, uint8_t **out_rpu, int *out_size) |
646 | 0 | { |
647 | 0 | PutBitContext *pb = &(PutBitContext){0}; |
648 | 0 | const AVDOVIRpuDataHeader *hdr; |
649 | 0 | const AVDOVIDataMapping *mapping; |
650 | 0 | const AVDOVIColorMetadata *color; |
651 | 0 | int vdr_dm_metadata_present, vdr_rpu_id, use_prev_vdr_rpu, profile, |
652 | 0 | buffer_size, rpu_size, pad, zero_run, dm_compression; |
653 | 0 | int num_ext_blocks_v1, num_ext_blocks_v2; |
654 | 0 | int dv_md_compression = s->cfg.dv_md_compression; |
655 | 0 | uint32_t crc; |
656 | 0 | uint8_t *dst; |
657 | 0 | if (!metadata) { |
658 | 0 | *out_rpu = NULL; |
659 | 0 | *out_size = 0; |
660 | 0 | return 0; |
661 | 0 | } |
662 | | |
663 | 0 | hdr = av_dovi_get_header(metadata); |
664 | 0 | mapping = av_dovi_get_mapping(metadata); |
665 | 0 | color = av_dovi_get_color(metadata); |
666 | 0 | av_assert0(s->cfg.dv_profile); |
667 | | |
668 | 0 | if (!(flags & FF_DOVI_COMPRESS_RPU)) |
669 | 0 | dv_md_compression = AV_DOVI_COMPRESSION_NONE; |
670 | 0 | else if (dv_md_compression == AV_DOVI_COMPRESSION_RESERVED) |
671 | 0 | return AVERROR(EINVAL); |
672 | | |
673 | 0 | if (hdr->rpu_type != 2) { |
674 | 0 | av_log(s->logctx, AV_LOG_ERROR, "Unhandled RPU type %"PRIu8"\n", |
675 | 0 | hdr->rpu_type); |
676 | 0 | return AVERROR_INVALIDDATA; |
677 | 0 | } |
678 | | |
679 | 0 | if (!validate_mapping_for_generation(hdr, mapping)) { |
680 | 0 | av_log(s->logctx, AV_LOG_ERROR, "Coefficient out of range for RPU\n"); |
681 | 0 | return AVERROR_INVALIDDATA; |
682 | 0 | } |
683 | | |
684 | 0 | if (!(flags & FF_DOVI_COMPRESS_RPU)) |
685 | 0 | dv_md_compression = AV_DOVI_COMPRESSION_NONE; |
686 | |
|
687 | 0 | vdr_rpu_id = mapping->vdr_rpu_id; |
688 | 0 | if (vdr_rpu_id < 0 || vdr_rpu_id > DOVI_MAX_DM_ID) { |
689 | 0 | av_log(s->logctx, AV_LOG_ERROR, "Invalid VDR RPU id %d\n", vdr_rpu_id); |
690 | 0 | return AVERROR_INVALIDDATA; |
691 | 0 | } |
692 | 0 | use_prev_vdr_rpu = 0; |
693 | |
|
694 | 0 | if (!s->vdr[vdr_rpu_id]) { |
695 | 0 | s->vdr[vdr_rpu_id] = av_refstruct_allocz(sizeof(AVDOVIDataMapping)); |
696 | 0 | if (!s->vdr[vdr_rpu_id]) |
697 | 0 | return AVERROR(ENOMEM); |
698 | 0 | } |
699 | | |
700 | 0 | switch (dv_md_compression) { |
701 | 0 | case AV_DOVI_COMPRESSION_LIMITED: |
702 | | /* Limited metadata compression requires vdr_rpi_id == 0 */ |
703 | 0 | if (vdr_rpu_id != 0) |
704 | 0 | break; |
705 | 0 | av_fallthrough; |
706 | 0 | case AV_DOVI_COMPRESSION_EXTENDED: |
707 | 0 | if (s->vdr[vdr_rpu_id]) |
708 | 0 | use_prev_vdr_rpu = !memcmp(s->vdr[vdr_rpu_id], mapping, sizeof(*mapping)); |
709 | 0 | break; |
710 | 0 | case AV_DOVI_COMPRESSION_RESERVED: |
711 | 0 | return AVERROR(EINVAL); |
712 | 0 | } |
713 | | |
714 | 0 | if (s->cfg.dv_md_compression != AV_DOVI_COMPRESSION_EXTENDED) { |
715 | | /* Flush VDRs to avoid leaking old state; maintaining multiple VDR |
716 | | * references requires extended compression */ |
717 | 0 | for (int i = 0; i <= DOVI_MAX_DM_ID; i++) { |
718 | 0 | if (i != vdr_rpu_id) |
719 | 0 | av_refstruct_unref(&s->vdr[i]); |
720 | 0 | } |
721 | 0 | } |
722 | |
|
723 | 0 | if (metadata->num_ext_blocks && !s->ext_blocks) { |
724 | 0 | s->ext_blocks = av_refstruct_allocz(sizeof(*s->ext_blocks)); |
725 | 0 | if (!s->ext_blocks) |
726 | 0 | return AVERROR(ENOMEM); |
727 | 0 | } |
728 | | |
729 | 0 | vdr_dm_metadata_present = !!memcmp(color, &ff_dovi_color_default, sizeof(*color)); |
730 | 0 | if (metadata->num_ext_blocks) |
731 | 0 | vdr_dm_metadata_present = 1; |
732 | |
|
733 | 0 | if (vdr_dm_metadata_present && !s->dm) { |
734 | 0 | s->dm = av_refstruct_allocz(sizeof(AVDOVIColorMetadata)); |
735 | 0 | if (!s->dm) |
736 | 0 | return AVERROR(ENOMEM); |
737 | 0 | } |
738 | | |
739 | 0 | dm_compression = 0; |
740 | 0 | if (dv_md_compression != AV_DOVI_COMPRESSION_NONE) { |
741 | 0 | if (s->dm && !cmp_dm_level0(s->dm, color) && try_reuse_ext(s->ext_blocks, metadata)) |
742 | 0 | dm_compression = 1; |
743 | 0 | } |
744 | |
|
745 | 0 | num_ext_blocks_v1 = num_ext_blocks_v2 = 0; |
746 | 0 | for (int i = 0; i < metadata->num_ext_blocks; i++) { |
747 | 0 | const AVDOVIDmData *dm = av_dovi_get_ext(metadata, i); |
748 | 0 | if (dm_compression && ff_dovi_rpu_extension_is_static(dm->level)) |
749 | 0 | continue; |
750 | | |
751 | 0 | switch (dm->level) { |
752 | 0 | case 1: |
753 | 0 | case 2: |
754 | 0 | case 4: |
755 | 0 | case 5: |
756 | 0 | case 6: |
757 | 0 | case 255: |
758 | 0 | num_ext_blocks_v1++; |
759 | 0 | break; |
760 | 0 | case 3: |
761 | 0 | case 8: |
762 | 0 | case 9: |
763 | 0 | case 10: |
764 | 0 | case 11: |
765 | 0 | case 254: |
766 | 0 | num_ext_blocks_v2++; |
767 | 0 | break; |
768 | 0 | default: |
769 | 0 | av_log(s->logctx, AV_LOG_ERROR, "Invalid ext block level %d\n", |
770 | 0 | dm->level); |
771 | 0 | return AVERROR_INVALIDDATA; |
772 | 0 | } |
773 | 0 | } |
774 | | |
775 | 0 | buffer_size = 12 /* vdr seq info */ + 5 /* CRC32 + terminator */; |
776 | 0 | buffer_size += num_ext_blocks_v1 * 13; |
777 | 0 | buffer_size += num_ext_blocks_v2 * 28; |
778 | 0 | if (!use_prev_vdr_rpu) { |
779 | 0 | buffer_size += 160; |
780 | 0 | for (int c = 0; c < 3; c++) { |
781 | 0 | for (int i = 0; i < mapping->curves[c].num_pivots - 1; i++) { |
782 | 0 | switch (mapping->curves[c].mapping_idc[i]) { |
783 | 0 | case AV_DOVI_MAPPING_POLYNOMIAL: buffer_size += 26; break; |
784 | 0 | case AV_DOVI_MAPPING_MMR: buffer_size += 177; break; |
785 | 0 | } |
786 | 0 | } |
787 | 0 | } |
788 | 0 | } |
789 | 0 | if (vdr_dm_metadata_present) |
790 | 0 | buffer_size += 67; |
791 | |
|
792 | 0 | av_fast_padded_malloc(&s->rpu_buf, &s->rpu_buf_sz, buffer_size); |
793 | 0 | if (!s->rpu_buf) |
794 | 0 | return AVERROR(ENOMEM); |
795 | 0 | init_put_bits(pb, s->rpu_buf, s->rpu_buf_sz); |
796 | | |
797 | | /* RPU header */ |
798 | 0 | put_bits(pb, 6, hdr->rpu_type); |
799 | 0 | put_bits(pb, 11, hdr->rpu_format); |
800 | 0 | put_bits(pb, 4, hdr->vdr_rpu_profile); |
801 | 0 | put_bits(pb, 4, hdr->vdr_rpu_level); |
802 | 0 | put_bits(pb, 1, 1); /* vdr_seq_info_present */ |
803 | 0 | put_bits(pb, 1, hdr->chroma_resampling_explicit_filter_flag); |
804 | 0 | put_bits(pb, 2, hdr->coef_data_type); |
805 | 0 | if (hdr->coef_data_type == RPU_COEFF_FIXED) |
806 | 0 | set_ue_golomb(pb, hdr->coef_log2_denom); |
807 | 0 | put_bits(pb, 2, hdr->vdr_rpu_normalized_idc); |
808 | 0 | put_bits(pb, 1, hdr->bl_video_full_range_flag); |
809 | 0 | if ((hdr->rpu_format & 0x700) == 0) { |
810 | 0 | int ext_mapping_idc = (hdr->ext_mapping_idc_5_7 << 5) | hdr->ext_mapping_idc_0_4; |
811 | 0 | set_ue_golomb(pb, hdr->bl_bit_depth - 8); |
812 | 0 | set_ue_golomb(pb, (ext_mapping_idc << 8) | (hdr->el_bit_depth - 8)); |
813 | 0 | set_ue_golomb(pb, hdr->vdr_bit_depth - 8); |
814 | 0 | put_bits(pb, 1, hdr->spatial_resampling_filter_flag); |
815 | 0 | put_bits(pb, 3, dm_compression); |
816 | 0 | put_bits(pb, 1, hdr->el_spatial_resampling_filter_flag); |
817 | 0 | put_bits(pb, 1, hdr->disable_residual_flag); |
818 | 0 | } |
819 | 0 | s->header = *hdr; |
820 | |
|
821 | 0 | put_bits(pb, 1, vdr_dm_metadata_present); |
822 | 0 | put_bits(pb, 1, use_prev_vdr_rpu); |
823 | 0 | set_ue_golomb(pb, vdr_rpu_id); |
824 | 0 | s->mapping = s->vdr[vdr_rpu_id]; |
825 | |
|
826 | 0 | profile = s->cfg.dv_profile ? s->cfg.dv_profile : ff_dovi_guess_profile_hevc(hdr); |
827 | |
|
828 | 0 | if (!use_prev_vdr_rpu) { |
829 | 0 | set_ue_golomb(pb, mapping->mapping_color_space); |
830 | 0 | set_ue_golomb(pb, mapping->mapping_chroma_format_idc); |
831 | 0 | for (int c = 0; c < 3; c++) { |
832 | 0 | const AVDOVIReshapingCurve *curve = &mapping->curves[c]; |
833 | 0 | int prev = 0; |
834 | 0 | set_ue_golomb(pb, curve->num_pivots - 2); |
835 | 0 | for (int i = 0; i < curve->num_pivots; i++) { |
836 | 0 | put_bits(pb, hdr->bl_bit_depth, curve->pivots[i] - prev); |
837 | 0 | prev = curve->pivots[i]; |
838 | 0 | } |
839 | 0 | } |
840 | |
|
841 | 0 | if (mapping->nlq_method_idc != AV_DOVI_NLQ_NONE) { |
842 | 0 | put_bits(pb, 3, mapping->nlq_method_idc); |
843 | 0 | put_bits(pb, hdr->bl_bit_depth, mapping->nlq_pivots[0]); |
844 | 0 | put_bits(pb, hdr->bl_bit_depth, mapping->nlq_pivots[1] - mapping->nlq_pivots[0]); |
845 | 0 | } |
846 | |
|
847 | 0 | set_ue_golomb(pb, mapping->num_x_partitions - 1); |
848 | 0 | set_ue_golomb(pb, mapping->num_y_partitions - 1); |
849 | |
|
850 | 0 | for (int c = 0; c < 3; c++) { |
851 | 0 | const AVDOVIReshapingCurve *curve = &mapping->curves[c]; |
852 | 0 | for (int i = 0; i < curve->num_pivots - 1; i++) { |
853 | 0 | set_ue_golomb(pb, curve->mapping_idc[i]); |
854 | 0 | switch (curve->mapping_idc[i]) { |
855 | 0 | case AV_DOVI_MAPPING_POLYNOMIAL: { |
856 | 0 | set_ue_golomb(pb, curve->poly_order[i] - 1); |
857 | 0 | if (curve->poly_order[i] == 1) |
858 | 0 | put_bits(pb, 1, 0); /* linear_interp_flag */ |
859 | 0 | for (int k = 0; k <= curve->poly_order[i]; k++) |
860 | 0 | put_se_coef(pb, hdr, curve->poly_coef[i][k]); |
861 | 0 | break; |
862 | 0 | } |
863 | 0 | case AV_DOVI_MAPPING_MMR: { |
864 | 0 | put_bits(pb, 2, curve->mmr_order[i] - 1); |
865 | 0 | put_se_coef(pb, hdr, curve->mmr_constant[i]); |
866 | 0 | for (int j = 0; j < curve->mmr_order[i]; j++) { |
867 | 0 | for (int k = 0; k < 7; k++) |
868 | 0 | put_se_coef(pb, hdr, curve->mmr_coef[i][j][k]); |
869 | 0 | } |
870 | 0 | break; |
871 | 0 | } |
872 | 0 | } |
873 | 0 | } |
874 | 0 | } |
875 | | |
876 | 0 | if (mapping->nlq_method_idc != AV_DOVI_NLQ_NONE) { |
877 | 0 | for (int c = 0; c < 3; c++) { |
878 | 0 | const AVDOVINLQParams *nlq = &mapping->nlq[c]; |
879 | 0 | put_bits(pb, hdr->el_bit_depth, nlq->nlq_offset); |
880 | 0 | put_ue_coef(pb, hdr, nlq->vdr_in_max); |
881 | 0 | switch (mapping->nlq_method_idc) { |
882 | 0 | case AV_DOVI_NLQ_LINEAR_DZ: |
883 | 0 | put_ue_coef(pb, hdr, nlq->linear_deadzone_slope); |
884 | 0 | put_ue_coef(pb, hdr, nlq->linear_deadzone_threshold); |
885 | 0 | break; |
886 | 0 | } |
887 | 0 | } |
888 | 0 | } |
889 | | |
890 | 0 | memcpy(s->vdr[vdr_rpu_id], mapping, sizeof(*mapping)); |
891 | 0 | } |
892 | | |
893 | 0 | if (vdr_dm_metadata_present) { |
894 | 0 | DOVIExt *ext = s->ext_blocks; |
895 | 0 | const int denom = profile == 4 ? (1 << 30) : (1 << 28); |
896 | 0 | set_ue_golomb(pb, color->dm_metadata_id); /* affected_dm_id */ |
897 | 0 | set_ue_golomb(pb, color->dm_metadata_id); /* current_dm_id */ |
898 | 0 | set_ue_golomb(pb, color->scene_refresh_flag); |
899 | 0 | if (!dm_compression) { |
900 | 0 | for (int i = 0; i < 9; i++) |
901 | 0 | put_sbits(pb, 16, av_q2den(color->ycc_to_rgb_matrix[i], 1 << 13)); |
902 | 0 | for (int i = 0; i < 3; i++) |
903 | 0 | put_bits32(pb, av_q2den(color->ycc_to_rgb_offset[i], denom)); |
904 | 0 | for (int i = 0; i < 9; i++) |
905 | 0 | put_sbits(pb, 16, av_q2den(color->rgb_to_lms_matrix[i], 1 << 14)); |
906 | 0 | put_bits(pb, 16, color->signal_eotf); |
907 | 0 | put_bits(pb, 16, color->signal_eotf_param0); |
908 | 0 | put_bits(pb, 16, color->signal_eotf_param1); |
909 | 0 | put_bits32(pb, color->signal_eotf_param2); |
910 | 0 | put_bits(pb, 5, color->signal_bit_depth); |
911 | 0 | put_bits(pb, 2, color->signal_color_space); |
912 | 0 | put_bits(pb, 2, color->signal_chroma_format); |
913 | 0 | put_bits(pb, 2, color->signal_full_range_flag); |
914 | 0 | put_bits(pb, 12, color->source_min_pq); |
915 | 0 | put_bits(pb, 12, color->source_max_pq); |
916 | 0 | put_bits(pb, 10, color->source_diagonal); |
917 | 0 | } |
918 | |
|
919 | 0 | memcpy(s->dm, color, sizeof(*color)); |
920 | 0 | s->color = s->dm; |
921 | | |
922 | | /* Extension blocks */ |
923 | 0 | set_ue_golomb(pb, num_ext_blocks_v1); |
924 | 0 | align_put_bits(pb); |
925 | 0 | for (int i = 0; i < metadata->num_ext_blocks; i++) { |
926 | 0 | const AVDOVIDmData *dm = av_dovi_get_ext(metadata, i); |
927 | 0 | if (dm_compression && ff_dovi_rpu_extension_is_static(dm->level)) |
928 | 0 | continue; |
929 | 0 | generate_ext_v1(pb, dm); |
930 | 0 | } |
931 | |
|
932 | 0 | if (num_ext_blocks_v2) { |
933 | 0 | set_ue_golomb(pb, num_ext_blocks_v2); |
934 | 0 | align_put_bits(pb); |
935 | 0 | for (int i = 0; i < metadata->num_ext_blocks; i++) { |
936 | 0 | const AVDOVIDmData *dm = av_dovi_get_ext(metadata, i); |
937 | 0 | if (dm_compression && ff_dovi_rpu_extension_is_static(dm->level)) |
938 | 0 | continue; |
939 | 0 | generate_ext_v2(pb, av_dovi_get_ext(metadata, i)); |
940 | 0 | } |
941 | 0 | } |
942 | |
|
943 | 0 | if (ext) { |
944 | 0 | size_t ext_sz = FFMIN(sizeof(AVDOVIDmData), metadata->ext_block_size); |
945 | 0 | ext->num_dynamic = 0; |
946 | 0 | if (!dm_compression) |
947 | 0 | ext->num_static = 0; |
948 | 0 | for (int i = 0; i < metadata->num_ext_blocks; i++) { |
949 | 0 | const AVDOVIDmData *dm = av_dovi_get_ext(metadata, i); |
950 | 0 | if (!ff_dovi_rpu_extension_is_static(dm->level)) |
951 | 0 | memcpy(&ext->dm_dynamic[ext->num_dynamic++], dm, ext_sz); |
952 | 0 | else if (!dm_compression) |
953 | 0 | memcpy(&ext->dm_static[ext->num_static++], dm, ext_sz); |
954 | 0 | } |
955 | 0 | } |
956 | 0 | } else { |
957 | 0 | s->color = &ff_dovi_color_default; |
958 | 0 | av_refstruct_unref(&s->ext_blocks); |
959 | 0 | } |
960 | |
|
961 | 0 | flush_put_bits(pb); |
962 | 0 | crc = av_bswap32(av_crc(av_crc_get_table(AV_CRC_32_IEEE), -1, |
963 | 0 | s->rpu_buf, put_bytes_output(pb))); |
964 | 0 | put_bits32(pb, crc); |
965 | 0 | put_bits(pb, 8, 0x80); /* terminator */ |
966 | 0 | flush_put_bits(pb); |
967 | |
|
968 | 0 | rpu_size = put_bytes_output(pb); |
969 | 0 | if (flags & FF_DOVI_WRAP_T35) { |
970 | 0 | *out_rpu = av_malloc(rpu_size + 15); |
971 | 0 | if (!*out_rpu) |
972 | 0 | return AVERROR(ENOMEM); |
973 | 0 | init_put_bits(pb, *out_rpu, rpu_size + 15); |
974 | 0 | put_bits(pb, 8, ITU_T_T35_COUNTRY_CODE_US); |
975 | 0 | put_bits(pb, 16, ITU_T_T35_PROVIDER_CODE_DOLBY); |
976 | 0 | put_bits32(pb, 0x800); /* provider_oriented_code */ |
977 | 0 | put_bits(pb, 27, 0x01be6841u); /* fixed EMDF header, see above */ |
978 | 0 | if (rpu_size > 0xFF) { |
979 | 0 | av_assert2(rpu_size <= 0x10000); |
980 | 0 | put_bits(pb, 8, (rpu_size >> 8) - 1); |
981 | 0 | put_bits(pb, 1, 1); /* read_more */ |
982 | 0 | put_bits(pb, 8, rpu_size & 0xFF); |
983 | 0 | put_bits(pb, 1, 0); |
984 | 0 | } else { |
985 | 0 | put_bits(pb, 8, rpu_size); |
986 | 0 | put_bits(pb, 1, 0); |
987 | 0 | } |
988 | 0 | ff_copy_bits(pb, s->rpu_buf, rpu_size * 8); |
989 | 0 | put_bits(pb, 17, 0x400); /* emdf payload id + emdf_protection */ |
990 | |
|
991 | 0 | pad = pb->bit_left & 7; |
992 | 0 | put_bits(pb, pad, (1 << pad) - 1); /* pad to next byte with 1 bits */ |
993 | 0 | flush_put_bits(pb); |
994 | 0 | *out_size = put_bytes_output(pb); |
995 | 0 | return 0; |
996 | 0 | } else if (flags & FF_DOVI_WRAP_NAL) { |
997 | 0 | *out_rpu = dst = av_malloc(4 + rpu_size * 3 / 2); /* worst case */ |
998 | 0 | if (!*out_rpu) |
999 | 0 | return AVERROR(ENOMEM); |
1000 | 0 | *dst++ = 25; /* NAL prefix */ |
1001 | 0 | zero_run = 0; |
1002 | 0 | for (int i = 0; i < rpu_size; i++) { |
1003 | 0 | if (zero_run < 2) { |
1004 | 0 | if (s->rpu_buf[i] == 0) { |
1005 | 0 | zero_run++; |
1006 | 0 | } else { |
1007 | 0 | zero_run = 0; |
1008 | 0 | } |
1009 | 0 | } else { |
1010 | 0 | if ((s->rpu_buf[i] & ~3) == 0) { |
1011 | | /* emulation prevention */ |
1012 | 0 | *dst++ = 3; |
1013 | 0 | } |
1014 | 0 | zero_run = s->rpu_buf[i] == 0; |
1015 | 0 | } |
1016 | 0 | *dst++ = s->rpu_buf[i]; |
1017 | 0 | } |
1018 | 0 | *out_size = dst - *out_rpu; |
1019 | 0 | return 0; |
1020 | 0 | } else { |
1021 | | /* Return intermediate buffer directly */ |
1022 | 0 | *out_rpu = s->rpu_buf; |
1023 | 0 | *out_size = rpu_size; |
1024 | | s->rpu_buf = NULL; |
1025 | 0 | s->rpu_buf_sz = 0; |
1026 | 0 | return 0; |
1027 | 0 | } |
1028 | 0 | } |