FFmpeg coverage


Directory: ../../../ffmpeg/
File: src/libavcodec/dovi_rpuenc.c
Date: 2024-10-27 21:33:06
Exec Total Coverage
Lines: 0 601 0.0%
Functions: 0 11 0.0%
Branches: 0 404 0.0%

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