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