1cabdff1aSopenharmony_ci/*
2cabdff1aSopenharmony_ci * Dolby Vision RPU decoder
3cabdff1aSopenharmony_ci *
4cabdff1aSopenharmony_ci * Copyright (C) 2021 Jan Ekström
5cabdff1aSopenharmony_ci * Copyright (C) 2021 Niklas Haas
6cabdff1aSopenharmony_ci *
7cabdff1aSopenharmony_ci * This file is part of FFmpeg.
8cabdff1aSopenharmony_ci *
9cabdff1aSopenharmony_ci * FFmpeg is free software; you can redistribute it and/or
10cabdff1aSopenharmony_ci * modify it under the terms of the GNU Lesser General Public
11cabdff1aSopenharmony_ci * License as published by the Free Software Foundation; either
12cabdff1aSopenharmony_ci * version 2.1 of the License, or (at your option) any later version.
13cabdff1aSopenharmony_ci *
14cabdff1aSopenharmony_ci * FFmpeg is distributed in the hope that it will be useful,
15cabdff1aSopenharmony_ci * but WITHOUT ANY WARRANTY; without even the implied warranty of
16cabdff1aSopenharmony_ci * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17cabdff1aSopenharmony_ci * Lesser General Public License for more details.
18cabdff1aSopenharmony_ci *
19cabdff1aSopenharmony_ci * You should have received a copy of the GNU Lesser General Public
20cabdff1aSopenharmony_ci * License along with FFmpeg; if not, write to the Free Software
21cabdff1aSopenharmony_ci * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
22cabdff1aSopenharmony_ci */
23cabdff1aSopenharmony_ci
24cabdff1aSopenharmony_ci#include "libavutil/buffer.h"
25cabdff1aSopenharmony_ci
26cabdff1aSopenharmony_ci#include "dovi_rpu.h"
27cabdff1aSopenharmony_ci#include "golomb.h"
28cabdff1aSopenharmony_ci#include "get_bits.h"
29cabdff1aSopenharmony_ci
30cabdff1aSopenharmony_cienum {
31cabdff1aSopenharmony_ci    RPU_COEFF_FIXED = 0,
32cabdff1aSopenharmony_ci    RPU_COEFF_FLOAT = 1,
33cabdff1aSopenharmony_ci};
34cabdff1aSopenharmony_ci
35cabdff1aSopenharmony_ci/**
36cabdff1aSopenharmony_ci * Private contents of vdr_ref.
37cabdff1aSopenharmony_ci */
38cabdff1aSopenharmony_citypedef struct DOVIVdrRef {
39cabdff1aSopenharmony_ci    AVDOVIDataMapping mapping;
40cabdff1aSopenharmony_ci    AVDOVIColorMetadata color;
41cabdff1aSopenharmony_ci} DOVIVdrRef;
42cabdff1aSopenharmony_ci
43cabdff1aSopenharmony_civoid ff_dovi_ctx_unref(DOVIContext *s)
44cabdff1aSopenharmony_ci{
45cabdff1aSopenharmony_ci    for (int i = 0; i < FF_ARRAY_ELEMS(s->vdr_ref); i++)
46cabdff1aSopenharmony_ci        av_buffer_unref(&s->vdr_ref[i]);
47cabdff1aSopenharmony_ci
48cabdff1aSopenharmony_ci    *s = (DOVIContext) {
49cabdff1aSopenharmony_ci        .logctx = s->logctx,
50cabdff1aSopenharmony_ci    };
51cabdff1aSopenharmony_ci}
52cabdff1aSopenharmony_ci
53cabdff1aSopenharmony_civoid ff_dovi_ctx_flush(DOVIContext *s)
54cabdff1aSopenharmony_ci{
55cabdff1aSopenharmony_ci    for (int i = 0; i < FF_ARRAY_ELEMS(s->vdr_ref); i++)
56cabdff1aSopenharmony_ci        av_buffer_unref(&s->vdr_ref[i]);
57cabdff1aSopenharmony_ci
58cabdff1aSopenharmony_ci    *s = (DOVIContext) {
59cabdff1aSopenharmony_ci        .logctx = s->logctx,
60cabdff1aSopenharmony_ci        .dv_profile = s->dv_profile,
61cabdff1aSopenharmony_ci    };
62cabdff1aSopenharmony_ci}
63cabdff1aSopenharmony_ci
64cabdff1aSopenharmony_ciint ff_dovi_ctx_replace(DOVIContext *s, const DOVIContext *s0)
65cabdff1aSopenharmony_ci{
66cabdff1aSopenharmony_ci    int ret;
67cabdff1aSopenharmony_ci    s->logctx = s0->logctx;
68cabdff1aSopenharmony_ci    s->mapping = s0->mapping;
69cabdff1aSopenharmony_ci    s->color = s0->color;
70cabdff1aSopenharmony_ci    s->dv_profile = s0->dv_profile;
71cabdff1aSopenharmony_ci    for (int i = 0; i < DOVI_MAX_DM_ID; i++) {
72cabdff1aSopenharmony_ci        if ((ret = av_buffer_replace(&s->vdr_ref[i], s0->vdr_ref[i])) < 0)
73cabdff1aSopenharmony_ci            goto fail;
74cabdff1aSopenharmony_ci    }
75cabdff1aSopenharmony_ci
76cabdff1aSopenharmony_ci    return 0;
77cabdff1aSopenharmony_ci
78cabdff1aSopenharmony_cifail:
79cabdff1aSopenharmony_ci    ff_dovi_ctx_unref(s);
80cabdff1aSopenharmony_ci    return ret;
81cabdff1aSopenharmony_ci}
82cabdff1aSopenharmony_ci
83cabdff1aSopenharmony_civoid ff_dovi_update_cfg(DOVIContext *s, const AVDOVIDecoderConfigurationRecord *cfg)
84cabdff1aSopenharmony_ci{
85cabdff1aSopenharmony_ci    if (!cfg)
86cabdff1aSopenharmony_ci        return;
87cabdff1aSopenharmony_ci
88cabdff1aSopenharmony_ci    s->dv_profile = cfg->dv_profile;
89cabdff1aSopenharmony_ci}
90cabdff1aSopenharmony_ci
91cabdff1aSopenharmony_ciint ff_dovi_attach_side_data(DOVIContext *s, AVFrame *frame)
92cabdff1aSopenharmony_ci{
93cabdff1aSopenharmony_ci    AVFrameSideData *sd;
94cabdff1aSopenharmony_ci    AVBufferRef *buf;
95cabdff1aSopenharmony_ci    AVDOVIMetadata *dovi;
96cabdff1aSopenharmony_ci    size_t dovi_size;
97cabdff1aSopenharmony_ci
98cabdff1aSopenharmony_ci    if (!s->mapping || !s->color)
99cabdff1aSopenharmony_ci        return 0; /* incomplete dovi metadata */
100cabdff1aSopenharmony_ci
101cabdff1aSopenharmony_ci    dovi = av_dovi_metadata_alloc(&dovi_size);
102cabdff1aSopenharmony_ci    if (!dovi)
103cabdff1aSopenharmony_ci        return AVERROR(ENOMEM);
104cabdff1aSopenharmony_ci
105cabdff1aSopenharmony_ci    buf = av_buffer_create((uint8_t *) dovi, dovi_size, NULL, NULL, 0);
106cabdff1aSopenharmony_ci    if (!buf) {
107cabdff1aSopenharmony_ci        av_free(dovi);
108cabdff1aSopenharmony_ci        return AVERROR(ENOMEM);
109cabdff1aSopenharmony_ci    }
110cabdff1aSopenharmony_ci
111cabdff1aSopenharmony_ci    sd = av_frame_new_side_data_from_buf(frame, AV_FRAME_DATA_DOVI_METADATA, buf);
112cabdff1aSopenharmony_ci    if (!sd) {
113cabdff1aSopenharmony_ci        av_buffer_unref(&buf);
114cabdff1aSopenharmony_ci        return AVERROR(ENOMEM);
115cabdff1aSopenharmony_ci    }
116cabdff1aSopenharmony_ci
117cabdff1aSopenharmony_ci    /* Copy only the parts of these structs known to us at compiler-time. */
118cabdff1aSopenharmony_ci#define COPY(t, a, b, last) memcpy(a, b, offsetof(t, last) + sizeof((b)->last))
119cabdff1aSopenharmony_ci    COPY(AVDOVIRpuDataHeader, av_dovi_get_header(dovi), &s->header, disable_residual_flag);
120cabdff1aSopenharmony_ci    COPY(AVDOVIDataMapping, av_dovi_get_mapping(dovi), s->mapping, nlq[2].linear_deadzone_threshold);
121cabdff1aSopenharmony_ci    COPY(AVDOVIColorMetadata, av_dovi_get_color(dovi), s->color, source_diagonal);
122cabdff1aSopenharmony_ci    return 0;
123cabdff1aSopenharmony_ci}
124cabdff1aSopenharmony_ci
125cabdff1aSopenharmony_cistatic int guess_profile(const AVDOVIRpuDataHeader *hdr)
126cabdff1aSopenharmony_ci{
127cabdff1aSopenharmony_ci    switch (hdr->vdr_rpu_profile) {
128cabdff1aSopenharmony_ci    case 0:
129cabdff1aSopenharmony_ci        if (hdr->bl_video_full_range_flag)
130cabdff1aSopenharmony_ci            return 5;
131cabdff1aSopenharmony_ci        break;
132cabdff1aSopenharmony_ci    case 1:
133cabdff1aSopenharmony_ci        if (hdr->el_spatial_resampling_filter_flag && !hdr->disable_residual_flag) {
134cabdff1aSopenharmony_ci            if (hdr->vdr_bit_depth == 12) {
135cabdff1aSopenharmony_ci                return 7;
136cabdff1aSopenharmony_ci            } else {
137cabdff1aSopenharmony_ci                return 4;
138cabdff1aSopenharmony_ci            }
139cabdff1aSopenharmony_ci        } else {
140cabdff1aSopenharmony_ci            return 8;
141cabdff1aSopenharmony_ci        }
142cabdff1aSopenharmony_ci    }
143cabdff1aSopenharmony_ci
144cabdff1aSopenharmony_ci    return 0; /* unknown */
145cabdff1aSopenharmony_ci}
146cabdff1aSopenharmony_ci
147cabdff1aSopenharmony_cistatic inline uint64_t get_ue_coef(GetBitContext *gb, const AVDOVIRpuDataHeader *hdr)
148cabdff1aSopenharmony_ci{
149cabdff1aSopenharmony_ci    uint64_t ipart;
150cabdff1aSopenharmony_ci    union { uint32_t u32; float f32; } fpart;
151cabdff1aSopenharmony_ci
152cabdff1aSopenharmony_ci    switch (hdr->coef_data_type) {
153cabdff1aSopenharmony_ci    case RPU_COEFF_FIXED:
154cabdff1aSopenharmony_ci        ipart = get_ue_golomb_long(gb);
155cabdff1aSopenharmony_ci        fpart.u32 = get_bits_long(gb, hdr->coef_log2_denom);
156cabdff1aSopenharmony_ci        return (ipart << hdr->coef_log2_denom) + fpart.u32;
157cabdff1aSopenharmony_ci
158cabdff1aSopenharmony_ci    case RPU_COEFF_FLOAT:
159cabdff1aSopenharmony_ci        fpart.u32 = get_bits_long(gb, 32);
160cabdff1aSopenharmony_ci        return fpart.f32 * (1LL << hdr->coef_log2_denom);
161cabdff1aSopenharmony_ci    }
162cabdff1aSopenharmony_ci
163cabdff1aSopenharmony_ci    return 0; /* unreachable */
164cabdff1aSopenharmony_ci}
165cabdff1aSopenharmony_ci
166cabdff1aSopenharmony_cistatic inline int64_t get_se_coef(GetBitContext *gb, const AVDOVIRpuDataHeader *hdr)
167cabdff1aSopenharmony_ci{
168cabdff1aSopenharmony_ci    int64_t ipart;
169cabdff1aSopenharmony_ci    union { uint32_t u32; float f32; } fpart;
170cabdff1aSopenharmony_ci
171cabdff1aSopenharmony_ci    switch (hdr->coef_data_type) {
172cabdff1aSopenharmony_ci    case RPU_COEFF_FIXED:
173cabdff1aSopenharmony_ci        ipart = get_se_golomb_long(gb);
174cabdff1aSopenharmony_ci        fpart.u32 = get_bits_long(gb, hdr->coef_log2_denom);
175cabdff1aSopenharmony_ci        return ipart * (1LL << hdr->coef_log2_denom) + fpart.u32;
176cabdff1aSopenharmony_ci
177cabdff1aSopenharmony_ci    case RPU_COEFF_FLOAT:
178cabdff1aSopenharmony_ci        fpart.u32 = get_bits_long(gb, 32);
179cabdff1aSopenharmony_ci        return fpart.f32 * (1LL << hdr->coef_log2_denom);
180cabdff1aSopenharmony_ci    }
181cabdff1aSopenharmony_ci
182cabdff1aSopenharmony_ci    return 0; /* unreachable */
183cabdff1aSopenharmony_ci}
184cabdff1aSopenharmony_ci
185cabdff1aSopenharmony_ci#define VALIDATE(VAR, MIN, MAX)                                                 \
186cabdff1aSopenharmony_ci    do {                                                                        \
187cabdff1aSopenharmony_ci        if (VAR < MIN || VAR > MAX) {                                           \
188cabdff1aSopenharmony_ci            av_log(s->logctx, AV_LOG_ERROR, "RPU validation failed: "           \
189cabdff1aSopenharmony_ci                   #MIN" <= "#VAR" = %d <= "#MAX"\n", (int) VAR);               \
190cabdff1aSopenharmony_ci            goto fail;                                                          \
191cabdff1aSopenharmony_ci        }                                                                       \
192cabdff1aSopenharmony_ci    } while (0)
193cabdff1aSopenharmony_ci
194cabdff1aSopenharmony_ciint ff_dovi_rpu_parse(DOVIContext *s, const uint8_t *rpu, size_t rpu_size)
195cabdff1aSopenharmony_ci{
196cabdff1aSopenharmony_ci    AVDOVIRpuDataHeader *hdr = &s->header;
197cabdff1aSopenharmony_ci    GetBitContext *gb = &(GetBitContext){0};
198cabdff1aSopenharmony_ci    DOVIVdrRef *vdr;
199cabdff1aSopenharmony_ci    int ret;
200cabdff1aSopenharmony_ci
201cabdff1aSopenharmony_ci    uint8_t nal_prefix;
202cabdff1aSopenharmony_ci    uint8_t rpu_type;
203cabdff1aSopenharmony_ci    uint8_t vdr_seq_info_present;
204cabdff1aSopenharmony_ci    uint8_t vdr_dm_metadata_present;
205cabdff1aSopenharmony_ci    uint8_t use_prev_vdr_rpu;
206cabdff1aSopenharmony_ci    uint8_t use_nlq;
207cabdff1aSopenharmony_ci    uint8_t profile;
208cabdff1aSopenharmony_ci    if ((ret = init_get_bits8(gb, rpu, rpu_size)) < 0)
209cabdff1aSopenharmony_ci        return ret;
210cabdff1aSopenharmony_ci
211cabdff1aSopenharmony_ci    /* RPU header, common values */
212cabdff1aSopenharmony_ci    nal_prefix = get_bits(gb, 8);
213cabdff1aSopenharmony_ci    VALIDATE(nal_prefix, 25, 25);
214cabdff1aSopenharmony_ci    rpu_type = get_bits(gb, 6);
215cabdff1aSopenharmony_ci    if (rpu_type != 2) {
216cabdff1aSopenharmony_ci        av_log(s->logctx, AV_LOG_WARNING, "Unrecognized RPU type "
217cabdff1aSopenharmony_ci               "%"PRIu8", ignoring\n", rpu_type);
218cabdff1aSopenharmony_ci        return 0;
219cabdff1aSopenharmony_ci    }
220cabdff1aSopenharmony_ci
221cabdff1aSopenharmony_ci    hdr->rpu_type = rpu_type;
222cabdff1aSopenharmony_ci    hdr->rpu_format = get_bits(gb, 11);
223cabdff1aSopenharmony_ci
224cabdff1aSopenharmony_ci    /* Values specific to RPU type 2 */
225cabdff1aSopenharmony_ci    hdr->vdr_rpu_profile = get_bits(gb, 4);
226cabdff1aSopenharmony_ci    hdr->vdr_rpu_level = get_bits(gb, 4);
227cabdff1aSopenharmony_ci
228cabdff1aSopenharmony_ci    vdr_seq_info_present = get_bits1(gb);
229cabdff1aSopenharmony_ci    if (vdr_seq_info_present) {
230cabdff1aSopenharmony_ci        hdr->chroma_resampling_explicit_filter_flag = get_bits1(gb);
231cabdff1aSopenharmony_ci        hdr->coef_data_type = get_bits(gb, 2);
232cabdff1aSopenharmony_ci        VALIDATE(hdr->coef_data_type, RPU_COEFF_FIXED, RPU_COEFF_FLOAT);
233cabdff1aSopenharmony_ci        switch (hdr->coef_data_type) {
234cabdff1aSopenharmony_ci        case RPU_COEFF_FIXED:
235cabdff1aSopenharmony_ci            hdr->coef_log2_denom = get_ue_golomb(gb);
236cabdff1aSopenharmony_ci            VALIDATE(hdr->coef_log2_denom, 13, 32);
237cabdff1aSopenharmony_ci            break;
238cabdff1aSopenharmony_ci        case RPU_COEFF_FLOAT:
239cabdff1aSopenharmony_ci            hdr->coef_log2_denom = 32; /* arbitrary, choose maximum precision */
240cabdff1aSopenharmony_ci            break;
241cabdff1aSopenharmony_ci        }
242cabdff1aSopenharmony_ci
243cabdff1aSopenharmony_ci        hdr->vdr_rpu_normalized_idc = get_bits(gb, 2);
244cabdff1aSopenharmony_ci        hdr->bl_video_full_range_flag = get_bits1(gb);
245cabdff1aSopenharmony_ci
246cabdff1aSopenharmony_ci        if ((hdr->rpu_format & 0x700) == 0) {
247cabdff1aSopenharmony_ci            int bl_bit_depth_minus8 = get_ue_golomb_31(gb);
248cabdff1aSopenharmony_ci            int el_bit_depth_minus8 = get_ue_golomb_31(gb);
249cabdff1aSopenharmony_ci            int vdr_bit_depth_minus8 = get_ue_golomb_31(gb);
250cabdff1aSopenharmony_ci            VALIDATE(bl_bit_depth_minus8, 0, 8);
251cabdff1aSopenharmony_ci            VALIDATE(el_bit_depth_minus8, 0, 8);
252cabdff1aSopenharmony_ci            VALIDATE(vdr_bit_depth_minus8, 0, 8);
253cabdff1aSopenharmony_ci            hdr->bl_bit_depth = bl_bit_depth_minus8 + 8;
254cabdff1aSopenharmony_ci            hdr->el_bit_depth = el_bit_depth_minus8 + 8;
255cabdff1aSopenharmony_ci            hdr->vdr_bit_depth = vdr_bit_depth_minus8 + 8;
256cabdff1aSopenharmony_ci            hdr->spatial_resampling_filter_flag = get_bits1(gb);
257cabdff1aSopenharmony_ci            skip_bits(gb, 3); /* reserved_zero_3bits */
258cabdff1aSopenharmony_ci            hdr->el_spatial_resampling_filter_flag = get_bits1(gb);
259cabdff1aSopenharmony_ci            hdr->disable_residual_flag = get_bits1(gb);
260cabdff1aSopenharmony_ci        }
261cabdff1aSopenharmony_ci    }
262cabdff1aSopenharmony_ci
263cabdff1aSopenharmony_ci    if (!hdr->bl_bit_depth) {
264cabdff1aSopenharmony_ci        av_log(s->logctx, AV_LOG_ERROR, "Missing RPU VDR sequence info?\n");
265cabdff1aSopenharmony_ci        goto fail;
266cabdff1aSopenharmony_ci    }
267cabdff1aSopenharmony_ci
268cabdff1aSopenharmony_ci    vdr_dm_metadata_present = get_bits1(gb);
269cabdff1aSopenharmony_ci    use_prev_vdr_rpu = get_bits1(gb);
270cabdff1aSopenharmony_ci    use_nlq = (hdr->rpu_format & 0x700) == 0 && !hdr->disable_residual_flag;
271cabdff1aSopenharmony_ci
272cabdff1aSopenharmony_ci    profile = s->dv_profile ? s->dv_profile : guess_profile(hdr);
273cabdff1aSopenharmony_ci    if (profile == 5 && use_nlq) {
274cabdff1aSopenharmony_ci        av_log(s->logctx, AV_LOG_ERROR, "Profile 5 RPUs should not use NLQ\n");
275cabdff1aSopenharmony_ci        goto fail;
276cabdff1aSopenharmony_ci    }
277cabdff1aSopenharmony_ci
278cabdff1aSopenharmony_ci    if (use_prev_vdr_rpu) {
279cabdff1aSopenharmony_ci        int prev_vdr_rpu_id = get_ue_golomb_31(gb);
280cabdff1aSopenharmony_ci        VALIDATE(prev_vdr_rpu_id, 0, DOVI_MAX_DM_ID);
281cabdff1aSopenharmony_ci        if (!s->vdr_ref[prev_vdr_rpu_id]) {
282cabdff1aSopenharmony_ci            av_log(s->logctx, AV_LOG_ERROR, "Unknown previous RPU ID: %u\n",
283cabdff1aSopenharmony_ci                   prev_vdr_rpu_id);
284cabdff1aSopenharmony_ci            goto fail;
285cabdff1aSopenharmony_ci        }
286cabdff1aSopenharmony_ci        vdr = (DOVIVdrRef *) s->vdr_ref[prev_vdr_rpu_id]->data;
287cabdff1aSopenharmony_ci        s->mapping = &vdr->mapping;
288cabdff1aSopenharmony_ci    } else {
289cabdff1aSopenharmony_ci        int vdr_rpu_id = get_ue_golomb_31(gb);
290cabdff1aSopenharmony_ci        VALIDATE(vdr_rpu_id, 0, DOVI_MAX_DM_ID);
291cabdff1aSopenharmony_ci        if (!s->vdr_ref[vdr_rpu_id]) {
292cabdff1aSopenharmony_ci            s->vdr_ref[vdr_rpu_id] = av_buffer_allocz(sizeof(DOVIVdrRef));
293cabdff1aSopenharmony_ci            if (!s->vdr_ref[vdr_rpu_id])
294cabdff1aSopenharmony_ci                return AVERROR(ENOMEM);
295cabdff1aSopenharmony_ci        }
296cabdff1aSopenharmony_ci
297cabdff1aSopenharmony_ci        vdr = (DOVIVdrRef *) s->vdr_ref[vdr_rpu_id]->data;
298cabdff1aSopenharmony_ci        s->mapping = &vdr->mapping;
299cabdff1aSopenharmony_ci
300cabdff1aSopenharmony_ci        vdr->mapping.vdr_rpu_id = vdr_rpu_id;
301cabdff1aSopenharmony_ci        vdr->mapping.mapping_color_space = get_ue_golomb_31(gb);
302cabdff1aSopenharmony_ci        vdr->mapping.mapping_chroma_format_idc = get_ue_golomb_31(gb);
303cabdff1aSopenharmony_ci
304cabdff1aSopenharmony_ci        for (int c = 0; c < 3; c++) {
305cabdff1aSopenharmony_ci            AVDOVIReshapingCurve *curve = &vdr->mapping.curves[c];
306cabdff1aSopenharmony_ci            int num_pivots_minus_2 = get_ue_golomb_31(gb);
307cabdff1aSopenharmony_ci            int pivot = 0;
308cabdff1aSopenharmony_ci
309cabdff1aSopenharmony_ci            VALIDATE(num_pivots_minus_2, 0, AV_DOVI_MAX_PIECES - 1);
310cabdff1aSopenharmony_ci            curve->num_pivots = num_pivots_minus_2 + 2;
311cabdff1aSopenharmony_ci            for (int i = 0; i < curve->num_pivots; i++) {
312cabdff1aSopenharmony_ci                pivot += get_bits(gb, hdr->bl_bit_depth);
313cabdff1aSopenharmony_ci                curve->pivots[i] = av_clip_uint16(pivot);
314cabdff1aSopenharmony_ci            }
315cabdff1aSopenharmony_ci        }
316cabdff1aSopenharmony_ci
317cabdff1aSopenharmony_ci        if (use_nlq) {
318cabdff1aSopenharmony_ci            vdr->mapping.nlq_method_idc = get_bits(gb, 3);
319cabdff1aSopenharmony_ci            /**
320cabdff1aSopenharmony_ci             * The patent mentions another legal value, NLQ_MU_LAW, but it's
321cabdff1aSopenharmony_ci             * not documented anywhere how to parse or apply that type of NLQ.
322cabdff1aSopenharmony_ci             */
323cabdff1aSopenharmony_ci            VALIDATE(vdr->mapping.nlq_method_idc, 0, AV_DOVI_NLQ_LINEAR_DZ);
324cabdff1aSopenharmony_ci        } else {
325cabdff1aSopenharmony_ci            vdr->mapping.nlq_method_idc = AV_DOVI_NLQ_NONE;
326cabdff1aSopenharmony_ci        }
327cabdff1aSopenharmony_ci
328cabdff1aSopenharmony_ci        vdr->mapping.num_x_partitions = get_ue_golomb_long(gb) + 1;
329cabdff1aSopenharmony_ci        vdr->mapping.num_y_partitions = get_ue_golomb_long(gb) + 1;
330cabdff1aSopenharmony_ci        /* End of rpu_data_header(), start of vdr_rpu_data_payload() */
331cabdff1aSopenharmony_ci
332cabdff1aSopenharmony_ci        for (int c = 0; c < 3; c++) {
333cabdff1aSopenharmony_ci            AVDOVIReshapingCurve *curve = &vdr->mapping.curves[c];
334cabdff1aSopenharmony_ci            for (int i = 0; i < curve->num_pivots - 1; i++) {
335cabdff1aSopenharmony_ci                int mapping_idc = get_ue_golomb_31(gb);
336cabdff1aSopenharmony_ci                VALIDATE(mapping_idc, 0, 1);
337cabdff1aSopenharmony_ci                curve->mapping_idc[i] = mapping_idc;
338cabdff1aSopenharmony_ci                switch (mapping_idc) {
339cabdff1aSopenharmony_ci                case AV_DOVI_MAPPING_POLYNOMIAL: {
340cabdff1aSopenharmony_ci                    int poly_order_minus1 = get_ue_golomb_31(gb);
341cabdff1aSopenharmony_ci                    VALIDATE(poly_order_minus1, 0, 1);
342cabdff1aSopenharmony_ci                    curve->poly_order[i] = poly_order_minus1 + 1;
343cabdff1aSopenharmony_ci                    if (poly_order_minus1 == 0) {
344cabdff1aSopenharmony_ci                        int linear_interp_flag = get_bits1(gb);
345cabdff1aSopenharmony_ci                        if (linear_interp_flag) {
346cabdff1aSopenharmony_ci                            /* lack of documentation/samples */
347cabdff1aSopenharmony_ci                            avpriv_request_sample(s->logctx, "Dolby Vision "
348cabdff1aSopenharmony_ci                                                  "linear interpolation");
349cabdff1aSopenharmony_ci                            ff_dovi_ctx_unref(s);
350cabdff1aSopenharmony_ci                            return AVERROR_PATCHWELCOME;
351cabdff1aSopenharmony_ci                        }
352cabdff1aSopenharmony_ci                    }
353cabdff1aSopenharmony_ci                    for (int k = 0; k <= curve->poly_order[i]; k++)
354cabdff1aSopenharmony_ci                        curve->poly_coef[i][k] = get_se_coef(gb, hdr);
355cabdff1aSopenharmony_ci                    break;
356cabdff1aSopenharmony_ci                }
357cabdff1aSopenharmony_ci                case AV_DOVI_MAPPING_MMR: {
358cabdff1aSopenharmony_ci                    int mmr_order_minus1 = get_bits(gb, 2);
359cabdff1aSopenharmony_ci                    VALIDATE(mmr_order_minus1, 0, 2);
360cabdff1aSopenharmony_ci                    curve->mmr_order[i] = mmr_order_minus1 + 1;
361cabdff1aSopenharmony_ci                    curve->mmr_constant[i] = get_se_coef(gb, hdr);
362cabdff1aSopenharmony_ci                    for (int j = 0; j < curve->mmr_order[i]; j++) {
363cabdff1aSopenharmony_ci                        for (int k = 0; k < 7; k++)
364cabdff1aSopenharmony_ci                            curve->mmr_coef[i][j][k] = get_se_coef(gb, hdr);
365cabdff1aSopenharmony_ci                    }
366cabdff1aSopenharmony_ci                    break;
367cabdff1aSopenharmony_ci                }
368cabdff1aSopenharmony_ci                }
369cabdff1aSopenharmony_ci            }
370cabdff1aSopenharmony_ci        }
371cabdff1aSopenharmony_ci
372cabdff1aSopenharmony_ci        if (use_nlq) {
373cabdff1aSopenharmony_ci            for (int c = 0; c < 3; c++) {
374cabdff1aSopenharmony_ci                AVDOVINLQParams *nlq = &vdr->mapping.nlq[c];
375cabdff1aSopenharmony_ci                nlq->nlq_offset = get_bits(gb, hdr->el_bit_depth);
376cabdff1aSopenharmony_ci                nlq->vdr_in_max = get_ue_coef(gb, hdr);
377cabdff1aSopenharmony_ci                switch (vdr->mapping.nlq_method_idc) {
378cabdff1aSopenharmony_ci                case AV_DOVI_NLQ_LINEAR_DZ:
379cabdff1aSopenharmony_ci                    nlq->linear_deadzone_slope = get_ue_coef(gb, hdr);
380cabdff1aSopenharmony_ci                    nlq->linear_deadzone_threshold = get_ue_coef(gb, hdr);
381cabdff1aSopenharmony_ci                    break;
382cabdff1aSopenharmony_ci                }
383cabdff1aSopenharmony_ci            }
384cabdff1aSopenharmony_ci        }
385cabdff1aSopenharmony_ci    }
386cabdff1aSopenharmony_ci
387cabdff1aSopenharmony_ci    if (vdr_dm_metadata_present) {
388cabdff1aSopenharmony_ci        AVDOVIColorMetadata *color;
389cabdff1aSopenharmony_ci        int affected_dm_id = get_ue_golomb_31(gb);
390cabdff1aSopenharmony_ci        int current_dm_id = get_ue_golomb_31(gb);
391cabdff1aSopenharmony_ci        VALIDATE(affected_dm_id, 0, DOVI_MAX_DM_ID);
392cabdff1aSopenharmony_ci        VALIDATE(current_dm_id, 0, DOVI_MAX_DM_ID);
393cabdff1aSopenharmony_ci        if (!s->vdr_ref[affected_dm_id]) {
394cabdff1aSopenharmony_ci            s->vdr_ref[affected_dm_id] = av_buffer_allocz(sizeof(DOVIVdrRef));
395cabdff1aSopenharmony_ci            if (!s->vdr_ref[affected_dm_id])
396cabdff1aSopenharmony_ci                return AVERROR(ENOMEM);
397cabdff1aSopenharmony_ci        }
398cabdff1aSopenharmony_ci
399cabdff1aSopenharmony_ci        if (!s->vdr_ref[current_dm_id]) {
400cabdff1aSopenharmony_ci            av_log(s->logctx, AV_LOG_ERROR, "Unknown previous RPU DM ID: %u\n",
401cabdff1aSopenharmony_ci                   current_dm_id);
402cabdff1aSopenharmony_ci            goto fail;
403cabdff1aSopenharmony_ci        }
404cabdff1aSopenharmony_ci
405cabdff1aSopenharmony_ci        /* Update current pointer based on current_dm_id */
406cabdff1aSopenharmony_ci        vdr = (DOVIVdrRef *) s->vdr_ref[current_dm_id]->data;
407cabdff1aSopenharmony_ci        s->color = &vdr->color;
408cabdff1aSopenharmony_ci
409cabdff1aSopenharmony_ci        /* Update values of affected_dm_id */
410cabdff1aSopenharmony_ci        vdr = (DOVIVdrRef *) s->vdr_ref[affected_dm_id]->data;
411cabdff1aSopenharmony_ci        color = &vdr->color;
412cabdff1aSopenharmony_ci        color->dm_metadata_id = affected_dm_id;
413cabdff1aSopenharmony_ci        color->scene_refresh_flag = get_ue_golomb_31(gb);
414cabdff1aSopenharmony_ci        for (int i = 0; i < 9; i++)
415cabdff1aSopenharmony_ci            color->ycc_to_rgb_matrix[i] = av_make_q(get_sbits(gb, 16), 1 << 13);
416cabdff1aSopenharmony_ci        for (int i = 0; i < 3; i++) {
417cabdff1aSopenharmony_ci            int denom = profile == 4 ? (1 << 30) : (1 << 28);
418cabdff1aSopenharmony_ci            unsigned offset = get_bits_long(gb, 32);
419cabdff1aSopenharmony_ci            if (offset > INT_MAX) {
420cabdff1aSopenharmony_ci                /* Ensure the result fits inside AVRational */
421cabdff1aSopenharmony_ci                offset >>= 1;
422cabdff1aSopenharmony_ci                denom >>= 1;
423cabdff1aSopenharmony_ci            }
424cabdff1aSopenharmony_ci            color->ycc_to_rgb_offset[i] = av_make_q(offset, denom);
425cabdff1aSopenharmony_ci        }
426cabdff1aSopenharmony_ci        for (int i = 0; i < 9; i++)
427cabdff1aSopenharmony_ci            color->rgb_to_lms_matrix[i] = av_make_q(get_sbits(gb, 16), 1 << 14);
428cabdff1aSopenharmony_ci
429cabdff1aSopenharmony_ci        color->signal_eotf = get_bits(gb, 16);
430cabdff1aSopenharmony_ci        color->signal_eotf_param0 = get_bits(gb, 16);
431cabdff1aSopenharmony_ci        color->signal_eotf_param1 = get_bits(gb, 16);
432cabdff1aSopenharmony_ci        color->signal_eotf_param2 = get_bits_long(gb, 32);
433cabdff1aSopenharmony_ci        color->signal_bit_depth = get_bits(gb, 5);
434cabdff1aSopenharmony_ci        VALIDATE(color->signal_bit_depth, 8, 16);
435cabdff1aSopenharmony_ci        color->signal_color_space = get_bits(gb, 2);
436cabdff1aSopenharmony_ci        color->signal_chroma_format = get_bits(gb, 2);
437cabdff1aSopenharmony_ci        color->signal_full_range_flag = get_bits(gb, 2);
438cabdff1aSopenharmony_ci        color->source_min_pq = get_bits(gb, 12);
439cabdff1aSopenharmony_ci        color->source_max_pq = get_bits(gb, 12);
440cabdff1aSopenharmony_ci        color->source_diagonal = get_bits(gb, 10);
441cabdff1aSopenharmony_ci    }
442cabdff1aSopenharmony_ci
443cabdff1aSopenharmony_ci    /* FIXME: verify CRC32, requires implementation of AV_CRC_32_MPEG_2 */
444cabdff1aSopenharmony_ci    return 0;
445cabdff1aSopenharmony_ci
446cabdff1aSopenharmony_cifail:
447cabdff1aSopenharmony_ci    ff_dovi_ctx_unref(s); /* don't leak potentially invalid state */
448cabdff1aSopenharmony_ci    return AVERROR(EINVAL);
449cabdff1aSopenharmony_ci}
450