FFmpeg coverage


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