Coverage Report

Created: 2026-10-28 08:13

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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
}