FFmpeg coverage


Directory: ../../../ffmpeg/
File: src/libavcodec/dovi_rpudec.c
Date: 2026-09-24 20:08:24
Exec Total Coverage
Lines: 220 476 46.2%
Functions: 6 10 60.0%
Branches: 121 331 36.6%

Line Branch Exec Source
1 /*
2 * Dolby Vision RPU decoder
3 *
4 * Copyright (C) 2021 Jan Ekström
5 * Copyright (C) 2021-2024 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/mem.h"
25 #include "libavutil/crc.h"
26
27 #include "avcodec.h"
28 #include "dovi_rpu.h"
29 #include "golomb.h"
30 #include "get_bits.h"
31 #include "libavutil/refstruct.h"
32
33 10462 int ff_dovi_get_metadata(DOVIContext *s, AVDOVIMetadata **out_metadata)
34 {
35 AVDOVIMetadata *dovi;
36 size_t dovi_size;
37
38
3/4
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 10460 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 2 times.
10462 if (!s->mapping || !s->color)
39 10460 return 0; /* incomplete dovi metadata */
40
41 2 dovi = av_dovi_metadata_alloc(&dovi_size);
42
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 if (!dovi)
43 ✗ return AVERROR(ENOMEM);
44
45 /* Copy only the parts of these structs known to us at compiler-time. */
46 #define COPY(t, a, b, last) memcpy(a, b, offsetof(t, last) + sizeof((b)->last))
47 2 COPY(AVDOVIRpuDataHeader, av_dovi_get_header(dovi), &s->header, ext_mapping_idc_5_7);
48 2 COPY(AVDOVIDataMapping, av_dovi_get_mapping(dovi), s->mapping, nlq_pivots);
49 2 COPY(AVDOVIColorMetadata, av_dovi_get_color(dovi), s->color, source_diagonal);
50
51
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
2 if (s->ext_blocks) {
52 2 const DOVIExt *ext = s->ext_blocks;
53 2 size_t ext_sz = FFMIN(sizeof(AVDOVIDmData), dovi->ext_block_size);
54
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 for (int i = 0; i < ext->num_static; i++)
55 ✗ memcpy(av_dovi_get_ext(dovi, dovi->num_ext_blocks++), &ext->dm_static[i], ext_sz);
56
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 2 times.
4 for (int i = 0; i < ext->num_dynamic; i++)
57 2 memcpy(av_dovi_get_ext(dovi, dovi->num_ext_blocks++), &ext->dm_dynamic[i], ext_sz);
58 }
59
60 2 *out_metadata = dovi;
61 2 return dovi_size;
62 }
63
64 10462 int ff_dovi_attach_side_data(DOVIContext *s, AVFrame *frame)
65 {
66 AVFrameSideData *sd;
67 AVDOVIMetadata *dovi;
68 AVBufferRef *buf;
69 int size;
70
71 10462 size = ff_dovi_get_metadata(s, &dovi);
72
2/2
✓ Branch 0 taken 10460 times.
✓ Branch 1 taken 2 times.
10462 if (size <= 0)
73 10460 return size;
74
75 2 buf = av_buffer_create((uint8_t *) dovi, size, NULL, NULL, 0);
76
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 if (!buf) {
77 ✗ av_free(dovi);
78 ✗ return AVERROR(ENOMEM);
79 }
80
81 2 sd = av_frame_new_side_data_from_buf(frame, AV_FRAME_DATA_DOVI_METADATA, buf);
82
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 if (!sd) {
83 ✗ av_buffer_unref(&buf);
84 ✗ return AVERROR(ENOMEM);
85 }
86
87 2 return 0;
88 }
89
90 ✗ static inline uint64_t get_ue_coef(GetBitContext *gb, const AVDOVIRpuDataHeader *hdr)
91 {
92 uint64_t ipart;
93 union { uint32_t u32; float f32; } fpart;
94
95 ✗ switch (hdr->coef_data_type) {
96 ✗ case RPU_COEFF_FIXED:
97 ✗ ipart = get_ue_golomb_long(gb);
98 ✗ fpart.u32 = get_bits_long(gb, hdr->coef_log2_denom);
99 ✗ return (ipart << hdr->coef_log2_denom) | fpart.u32;
100
101 ✗ case RPU_COEFF_FLOAT:
102 ✗ fpart.u32 = get_bits_long(gb, 32);
103 ✗ return fpart.f32 * (1LL << hdr->coef_log2_denom);
104 }
105
106 ✗ return 0; /* unreachable */
107 }
108
109 136 static inline int64_t get_se_coef(GetBitContext *gb, const AVDOVIRpuDataHeader *hdr)
110 {
111 int64_t ipart;
112 union { uint32_t u32; float f32; } fpart;
113
114
1/3
✓ Branch 0 taken 136 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
136 switch (hdr->coef_data_type) {
115 136 case RPU_COEFF_FIXED:
116 136 ipart = get_se_golomb_long(gb);
117 136 fpart.u32 = get_bits_long(gb, hdr->coef_log2_denom);
118 136 return ipart * (1LL << hdr->coef_log2_denom) | fpart.u32;
119
120 ✗ case RPU_COEFF_FLOAT:
121 ✗ fpart.u32 = get_bits_long(gb, 32);
122 ✗ return fpart.f32 * (1LL << hdr->coef_log2_denom);
123 }
124
125 ✗ return 0; /* unreachable */
126 }
127
128 ✗ static inline unsigned get_variable_bits(GetBitContext *gb, int n)
129 {
130 ✗ unsigned int value = get_bits(gb, n);
131 ✗ int read_more = get_bits1(gb);
132 ✗ while (read_more) {
133 ✗ value = (value + 1) << n;
134 ✗ value |= get_bits(gb, n);
135 ✗ read_more = get_bits1(gb);
136 }
137 ✗ return value;
138 }
139
140 #define VALIDATE(VAR, MIN, MAX) \
141 do { \
142 if (VAR < MIN || VAR > MAX) { \
143 av_log(s->logctx, AV_LOG_ERROR, "RPU validation failed: " \
144 #MIN" <= "#VAR" = %d <= "#MAX"\n", (int) VAR); \
145 ff_dovi_ctx_unref(s); \
146 return AVERROR_INVALIDDATA; \
147 } \
148 } while (0)
149
150 2 static int parse_ext_v1(DOVIContext *s, GetBitContext *gb, AVDOVIDmData *dm)
151 {
152
1/7
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
2 switch (dm->level) {
153 2 case 1:
154 2 dm->l1.min_pq = get_bits(gb, 12);
155 2 dm->l1.max_pq = get_bits(gb, 12);
156 2 dm->l1.avg_pq = get_bits(gb, 12);
157 2 break;
158 ✗ case 2:
159 ✗ dm->l2.target_max_pq = get_bits(gb, 12);
160 ✗ dm->l2.trim_slope = get_bits(gb, 12);
161 ✗ dm->l2.trim_offset = get_bits(gb, 12);
162 ✗ dm->l2.trim_power = get_bits(gb, 12);
163 ✗ dm->l2.trim_chroma_weight = get_bits(gb, 12);
164 ✗ dm->l2.trim_saturation_gain = get_bits(gb, 12);
165 ✗ dm->l2.ms_weight = get_sbits(gb, 13);
166 ✗ VALIDATE(dm->l2.ms_weight, -1, 4095);
167 ✗ break;
168 ✗ case 4:
169 ✗ dm->l4.anchor_pq = get_bits(gb, 12);
170 ✗ dm->l4.anchor_power = get_bits(gb, 12);
171 ✗ break;
172 ✗ case 5:
173 ✗ dm->l5.left_offset = get_bits(gb, 13);
174 ✗ dm->l5.right_offset = get_bits(gb, 13);
175 ✗ dm->l5.top_offset = get_bits(gb, 13);
176 ✗ dm->l5.bottom_offset = get_bits(gb, 13);
177 ✗ break;
178 ✗ case 6:
179 ✗ dm->l6.max_luminance = get_bits(gb, 16);
180 ✗ dm->l6.min_luminance = get_bits(gb, 16);
181 ✗ dm->l6.max_cll = get_bits(gb, 16);
182 ✗ dm->l6.max_fall = get_bits(gb, 16);
183 ✗ break;
184 ✗ case 255:
185 ✗ dm->l255.dm_run_mode = get_bits(gb, 8);
186 ✗ dm->l255.dm_run_version = get_bits(gb, 8);
187 ✗ for (int i = 0; i < 4; i++)
188 ✗ dm->l255.dm_debug[i] = get_bits(gb, 8);
189 ✗ break;
190 ✗ default:
191 ✗ avpriv_request_sample(s->logctx, "Dolby Vision DM v1 level %u", dm->level);
192 }
193
194 2 return 0;
195 }
196
197 ✗ static AVCIExy get_cie_xy(GetBitContext *gb)
198 {
199 AVCIExy xy;
200 ✗ const int denom = 32767;
201 ✗ xy.x = av_make_q(get_sbits(gb, 16), denom);
202 ✗ xy.y = av_make_q(get_sbits(gb, 16), denom);
203 ✗ return xy;
204 }
205
206 ✗ static int parse_ext_v2(DOVIContext *s, GetBitContext *gb, AVDOVIDmData *dm,
207 int ext_block_length)
208 {
209 ✗ switch (dm->level) {
210 ✗ case 3:
211 ✗ dm->l3.min_pq_offset = get_bits(gb, 12);
212 ✗ dm->l3.max_pq_offset = get_bits(gb, 12);
213 ✗ dm->l3.avg_pq_offset = get_bits(gb, 12);
214 ✗ break;
215 ✗ case 8:
216 ✗ dm->l8.target_display_index = get_bits(gb, 8);
217 ✗ dm->l8.trim_slope = get_bits(gb, 12);
218 ✗ dm->l8.trim_offset = get_bits(gb, 12);
219 ✗ dm->l8.trim_power = get_bits(gb, 12);
220 ✗ dm->l8.trim_chroma_weight = get_bits(gb, 12);
221 ✗ dm->l8.trim_saturation_gain = get_bits(gb, 12);
222 ✗ dm->l8.ms_weight = get_bits(gb, 12);
223 ✗ if (ext_block_length < 12)
224 ✗ break;
225 ✗ dm->l8.target_mid_contrast = get_bits(gb, 12);
226 ✗ if (ext_block_length < 13)
227 ✗ break;
228 ✗ dm->l8.clip_trim = get_bits(gb, 12);
229 ✗ if (ext_block_length < 19)
230 ✗ break;
231 ✗ for (int i = 0; i < 6; i++)
232 ✗ dm->l8.saturation_vector_field[i] = get_bits(gb, 8);
233 ✗ if (ext_block_length < 25)
234 ✗ break;
235 ✗ for (int i = 0; i < 6; i++)
236 ✗ dm->l8.hue_vector_field[i] = get_bits(gb, 8);
237 ✗ break;
238 ✗ case 9:
239 ✗ dm->l9.source_primary_index = get_bits(gb, 8);
240 ✗ if (ext_block_length < 17)
241 ✗ break;
242 ✗ dm->l9.source_display_primaries.prim.r = get_cie_xy(gb);
243 ✗ dm->l9.source_display_primaries.prim.g = get_cie_xy(gb);
244 ✗ dm->l9.source_display_primaries.prim.b = get_cie_xy(gb);
245 ✗ dm->l9.source_display_primaries.wp = get_cie_xy(gb);
246 ✗ break;
247 ✗ case 10:
248 ✗ dm->l10.target_display_index = get_bits(gb, 8);
249 ✗ dm->l10.target_max_pq = get_bits(gb, 12);
250 ✗ dm->l10.target_min_pq = get_bits(gb, 12);
251 ✗ dm->l10.target_primary_index = get_bits(gb, 8);
252 ✗ if (ext_block_length < 21)
253 ✗ break;
254 ✗ dm->l10.target_display_primaries.prim.r = get_cie_xy(gb);
255 ✗ dm->l10.target_display_primaries.prim.g = get_cie_xy(gb);
256 ✗ dm->l10.target_display_primaries.prim.b = get_cie_xy(gb);
257 ✗ dm->l10.target_display_primaries.wp = get_cie_xy(gb);
258 ✗ break;
259 ✗ case 11:
260 ✗ dm->l11.content_type = get_bits(gb, 8);
261 ✗ dm->l11.whitepoint = get_bits(gb, 4);
262 ✗ dm->l11.reference_mode_flag = get_bits1(gb);
263 ✗ skip_bits(gb, 3); /* reserved */
264 ✗ skip_bits(gb, 8); /* reserved */
265 ✗ skip_bits(gb, 8); /* reserved */
266 ✗ break;
267 ✗ case 254:
268 ✗ dm->l254.dm_mode = get_bits(gb, 8);
269 ✗ dm->l254.dm_version_index = get_bits(gb, 8);
270 ✗ break;
271 ✗ default:
272 ✗ avpriv_request_sample(s->logctx, "Dolby Vision DM v2 level %u", dm->level);
273 }
274
275 ✗ return 0;
276 }
277
278 2 static int parse_ext_blocks(DOVIContext *s, GetBitContext *gb, int ver,
279 int compression, int err_recognition)
280 {
281 int num_ext_blocks, ext_block_length, start_pos, parsed_bits, ret;
282 2 DOVIExt *ext = s->ext_blocks;
283
284 2 num_ext_blocks = get_ue_golomb_31(gb);
285 2 align_get_bits(gb);
286
287
3/4
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✓ Branch 3 taken 1 times.
2 if (num_ext_blocks && !ext) {
288 1 ext = s->ext_blocks = av_refstruct_allocz(sizeof(*s->ext_blocks));
289
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (!ext)
290 ✗ return AVERROR(ENOMEM);
291 }
292
293
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 2 times.
4 while (num_ext_blocks--) {
294 AVDOVIDmData dummy;
295 AVDOVIDmData *dm;
296 uint8_t level;
297
298 2 ext_block_length = get_ue_golomb_31(gb);
299 2 level = get_bits(gb, 8);
300 2 start_pos = get_bits_count(gb);
301
302
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 2 times.
2 if (ff_dovi_rpu_extension_is_static(level)) {
303 ✗ if (compression) {
304 ✗ av_log(s->logctx, AV_LOG_WARNING, "Compressed DM RPU contains "
305 "static extension block level %d\n", level);
306 ✗ if (err_recognition & (AV_EF_AGGRESSIVE | AV_EF_EXPLODE))
307 ✗ return AVERROR_INVALIDDATA;
308 ✗ dm = &dummy;
309 } else {
310 ✗ if (ext->num_static >= FF_ARRAY_ELEMS(ext->dm_static))
311 ✗ return AVERROR_INVALIDDATA;
312 ✗ dm = &ext->dm_static[ext->num_static++];
313 }
314 } else {
315
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 if (ext->num_dynamic >= FF_ARRAY_ELEMS(ext->dm_dynamic))
316 ✗ return AVERROR_INVALIDDATA;
317 2 dm = &ext->dm_dynamic[ext->num_dynamic++];
318 }
319
320 2 memset(dm, 0, sizeof(*dm));
321 2 dm->level = level;
322
1/3
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
2 switch (ver) {
323 2 case 1: ret = parse_ext_v1(s, gb, dm); break;
324 ✗ case 2: ret = parse_ext_v2(s, gb, dm, ext_block_length); break;
325 ✗ default:
326 ✗ avpriv_request_sample(s->logctx, "Dolby Vision DM v%d", ver);
327 ✗ goto skip;
328 }
329
330
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 if (ret < 0)
331 ✗ return ret;
332
333 2 skip:
334 2 parsed_bits = get_bits_count(gb) - start_pos;
335
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 if (parsed_bits > ext_block_length * 8)
336 ✗ return AVERROR_INVALIDDATA;
337 2 skip_bits(gb, ext_block_length * 8 - parsed_bits);
338 }
339
340 2 return 0;
341 }
342
343 2 int ff_dovi_rpu_parse(DOVIContext *s, const uint8_t *rpu, size_t rpu_size,
344 int err_recognition)
345 {
346 2 AVDOVIRpuDataHeader *hdr = &s->header;
347 2 GetBitContext *gb = &(GetBitContext){0};
348 int ret;
349
350 uint8_t rpu_type;
351 uint8_t vdr_seq_info_present;
352 uint8_t vdr_dm_metadata_present;
353 2 uint8_t dm_compression = 0;
354 uint8_t use_prev_vdr_rpu;
355 uint8_t use_nlq;
356 uint8_t profile;
357
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
2 uint8_t compression = s->cfg.dv_profile ? s->cfg.dv_md_compression : 0;
358
359
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 if (rpu_size < 5)
360 ✗ return AVERROR_INVALIDDATA;
361
362 /* Container */
363
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 if (s->cfg.dv_profile == 10 /* dav1.10 */) {
364 /* DV inside AV1 reuses an EMDF container skeleton, but with fixed
365 * values - so we can effectively treat this as a magic byte sequence.
366 *
367 * The exact fields are, as follows:
368 * emdf_version : f(2) = 0
369 * key_id : f(3) = 6
370 * emdf_payload_id : f(5) = 31
371 * emdf_payload_id_ext : var(5) = 225
372 * smploffste : f(1) = 0
373 * duratione : f(1) = 0
374 * groupide : f(1) = 0
375 * codecdatae : f(1) = 0
376 * discard_unknown_payload : f(1) = 1
377 */
378 ✗ const unsigned header_magic = 0x01be6841u;
379 unsigned emdf_header, emdf_payload_size, emdf_protection;
380 ✗ if ((ret = init_get_bits8(gb, rpu, rpu_size)) < 0)
381 ✗ return ret;
382 ✗ emdf_header = get_bits_long(gb, 27);
383 ✗ VALIDATE(emdf_header, header_magic, header_magic);
384 ✗ emdf_payload_size = get_variable_bits(gb, 8);
385 ✗ VALIDATE(emdf_payload_size, 6, 512);
386 ✗ if (emdf_payload_size * 8 > get_bits_left(gb))
387 ✗ return AVERROR_INVALIDDATA;
388
389 /* The payload is not byte-aligned (off by *one* bit, curse Dolby),
390 * so copy into a fresh buffer to preserve byte alignment of the
391 * RPU struct */
392 ✗ av_fast_padded_malloc(&s->rpu_buf, &s->rpu_buf_sz, emdf_payload_size);
393 ✗ if (!s->rpu_buf)
394 ✗ return AVERROR(ENOMEM);
395 ✗ for (int i = 0; i < emdf_payload_size; i++)
396 ✗ s->rpu_buf[i] = get_bits(gb, 8);
397 ✗ rpu = s->rpu_buf;
398 ✗ rpu_size = emdf_payload_size;
399
400 /* Validate EMDF footer */
401 ✗ emdf_protection = get_bits(gb, 5 + 12);
402 ✗ VALIDATE(emdf_protection, 0x400, 0x400);
403 } else {
404 /* NAL unit with prefix and trailing zeroes */
405
2/4
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 2 times.
2 VALIDATE(rpu[0], 25, 25); /* NAL prefix */
406 2 rpu++;
407 2 rpu_size--;
408 /* Strip trailing padding zero bytes */
409
2/4
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 2 times.
2 while (rpu_size && !rpu[rpu_size - 1])
410 ✗ rpu_size--;
411 }
412
413
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 2 times.
2 if ((ret = init_get_bits8(gb, rpu, rpu_size)) < 0)
414 ✗ return ret;
415
416 /* RPU header */
417 2 rpu_type = get_bits(gb, 6);
418
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 if (rpu_type != 2) {
419 ✗ av_log(s->logctx, AV_LOG_WARNING, "Unrecognized RPU type "
420 "%"PRIu8", ignoring\n", rpu_type);
421 ✗ return 0;
422 }
423
424 2 hdr->rpu_type = rpu_type;
425 2 hdr->rpu_format = get_bits(gb, 11);
426
427 /* Values specific to RPU type 2 */
428 2 hdr->vdr_rpu_profile = get_bits(gb, 4);
429 2 hdr->vdr_rpu_level = get_bits(gb, 4);
430
431 2 vdr_seq_info_present = get_bits1(gb);
432
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
2 if (vdr_seq_info_present) {
433 2 hdr->chroma_resampling_explicit_filter_flag = get_bits1(gb);
434 2 hdr->coef_data_type = get_bits(gb, 2);
435
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 VALIDATE(hdr->coef_data_type, RPU_COEFF_FIXED, RPU_COEFF_FLOAT);
436
1/3
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
2 switch (hdr->coef_data_type) {
437 2 case RPU_COEFF_FIXED:
438 2 hdr->coef_log2_denom = get_ue_golomb(gb);
439
2/4
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 2 times.
2 VALIDATE(hdr->coef_log2_denom, 13, 32);
440 2 break;
441 ✗ case RPU_COEFF_FLOAT:
442 ✗ hdr->coef_log2_denom = 32; /* arbitrary, choose maximum precision */
443 ✗ break;
444 }
445
446 2 hdr->vdr_rpu_normalized_idc = get_bits(gb, 2);
447 2 hdr->bl_video_full_range_flag = get_bits1(gb);
448
449
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
2 if ((hdr->rpu_format & 0x700) == 0) {
450 2 int bl_bit_depth_minus8 = get_ue_golomb_31(gb);
451 2 int el_bit_depth_minus8 = get_ue_golomb_long(gb);
452 2 int vdr_bit_depth_minus8 = get_ue_golomb_31(gb);
453 /* ext_mapping_idc is in the upper 8 bits of el_bit_depth_minus8 */
454 2 int ext_mapping_idc = el_bit_depth_minus8 >> 8;
455 2 el_bit_depth_minus8 = el_bit_depth_minus8 & 0xFF;
456
2/4
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 2 times.
2 VALIDATE(bl_bit_depth_minus8, 0, 8);
457
2/4
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 2 times.
2 VALIDATE(el_bit_depth_minus8, 0, 8);
458
2/4
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 2 times.
2 VALIDATE(ext_mapping_idc, 0, 0xFF);
459
2/4
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 2 times.
2 VALIDATE(vdr_bit_depth_minus8, 0, 8);
460 2 hdr->bl_bit_depth = bl_bit_depth_minus8 + 8;
461 2 hdr->el_bit_depth = el_bit_depth_minus8 + 8;
462 2 hdr->ext_mapping_idc_0_4 = ext_mapping_idc & 0x1f; /* 5 bits */
463 2 hdr->ext_mapping_idc_5_7 = ext_mapping_idc >> 5;
464 2 hdr->vdr_bit_depth = vdr_bit_depth_minus8 + 8;
465 2 hdr->spatial_resampling_filter_flag = get_bits1(gb);
466 2 dm_compression = get_bits(gb, 3);
467 2 hdr->el_spatial_resampling_filter_flag = get_bits1(gb);
468 2 hdr->disable_residual_flag = get_bits1(gb);
469 } else {
470 ✗ avpriv_request_sample(s->logctx, "Unsupported RPU format 0x%x\n", hdr->rpu_format);
471 ✗ ff_dovi_ctx_unref(s);
472 ✗ return AVERROR_PATCHWELCOME;
473 }
474 } else {
475 /* lack of documentation/samples */
476 ✗ avpriv_request_sample(s->logctx, "Missing RPU VDR sequence info\n");
477 ✗ ff_dovi_ctx_unref(s);
478 ✗ return AVERROR_PATCHWELCOME;
479 }
480
481 2 vdr_dm_metadata_present = get_bits1(gb);
482
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 if (dm_compression > 1) {
483 /* It seems no device supports this */
484 ✗ av_log(s->logctx, AV_LOG_ERROR, "Dynamic metadata compression is not "
485 "yet implemented");
486 ✗ return AVERROR_PATCHWELCOME;
487
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
2 } else if (dm_compression && !vdr_dm_metadata_present) {
488 ✗ av_log(s->logctx, AV_LOG_ERROR, "Nonzero DM metadata compression method "
489 "but no DM metadata present");
490 ✗ return AVERROR_INVALIDDATA;
491 }
492
493 2 use_prev_vdr_rpu = get_bits1(gb);
494
2/4
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 2 times.
2 use_nlq = (hdr->rpu_format & 0x700) == 0 && !hdr->disable_residual_flag;
495
496
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
2 profile = s->cfg.dv_profile ? s->cfg.dv_profile : ff_dovi_guess_profile_hevc(hdr);
497
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
2 if (profile == 5 && use_nlq) {
498 ✗ av_log(s->logctx, AV_LOG_ERROR, "Profile 5 RPUs should not use NLQ\n");
499 ✗ ff_dovi_ctx_unref(s);
500 ✗ return AVERROR_INVALIDDATA;
501 }
502
503
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 if (err_recognition & (AV_EF_COMPLIANT | AV_EF_CAREFUL)) {
504 ✗ if (profile < 8 && compression) {
505 ✗ av_log(s->logctx, AV_LOG_ERROR, "Profile %d RPUs should not use "
506 "metadata compression.", profile);
507 ✗ return AVERROR_INVALIDDATA;
508 }
509
510 ✗ if (use_prev_vdr_rpu && !compression) {
511 ✗ av_log(s->logctx, AV_LOG_ERROR, "Uncompressed RPUs should not have "
512 "use_prev_vdr_rpu=1\n");
513 ✗ return AVERROR_INVALIDDATA;
514 }
515
516 ✗ if (dm_compression && !compression) {
517 ✗ av_log(s->logctx, AV_LOG_ERROR, "Uncompressed RPUs should not use "
518 "dm_compression=%d\n", dm_compression);
519 ✗ return AVERROR_INVALIDDATA;
520 }
521 }
522
523
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 if (use_prev_vdr_rpu) {
524 ✗ int prev_vdr_rpu_id = get_ue_golomb_31(gb);
525 ✗ VALIDATE(prev_vdr_rpu_id, 0, DOVI_MAX_DM_ID);
526 ✗ if (!s->vdr[prev_vdr_rpu_id])
527 ✗ prev_vdr_rpu_id = 0;
528 ✗ if (!s->vdr[prev_vdr_rpu_id]) {
529 /* FIXME: Technically, the spec says that in this case we should
530 * synthesize "neutral" vdr metadata, but easier to just error
531 * out as this corner case is not hit in practice */
532 ✗ av_log(s->logctx, AV_LOG_ERROR, "Unknown previous RPU ID: %u\n",
533 prev_vdr_rpu_id);
534 ✗ ff_dovi_ctx_unref(s);
535 ✗ return AVERROR_INVALIDDATA;
536 }
537 ✗ s->mapping = s->vdr[prev_vdr_rpu_id];
538 } else {
539 AVDOVIDataMapping *mapping;
540 2 int vdr_rpu_id = get_ue_golomb_31(gb);
541
2/4
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 2 times.
2 VALIDATE(vdr_rpu_id, 0, DOVI_MAX_DM_ID);
542
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 1 times.
2 if (!s->vdr[vdr_rpu_id]) {
543 1 s->vdr[vdr_rpu_id] = av_refstruct_allocz(sizeof(AVDOVIDataMapping));
544
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (!s->vdr[vdr_rpu_id]) {
545 ✗ ff_dovi_ctx_unref(s);
546 ✗ return AVERROR(ENOMEM);
547 }
548 }
549
550 2 s->mapping = mapping = s->vdr[vdr_rpu_id];
551 2 mapping->vdr_rpu_id = vdr_rpu_id;
552 2 mapping->mapping_color_space = get_ue_golomb_31(gb);
553 2 mapping->mapping_chroma_format_idc = get_ue_golomb_31(gb);
554
555
2/2
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 2 times.
8 for (int c = 0; c < 3; c++) {
556 6 AVDOVIReshapingCurve *curve = &mapping->curves[c];
557 6 int num_pivots_minus_2 = get_ue_golomb_31(gb);
558 6 int pivot = 0;
559
560
2/4
✓ Branch 0 taken 6 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 6 times.
6 VALIDATE(num_pivots_minus_2, 0, AV_DOVI_MAX_PIECES - 1);
561 6 curve->num_pivots = num_pivots_minus_2 + 2;
562
2/2
✓ Branch 0 taken 26 times.
✓ Branch 1 taken 6 times.
32 for (int i = 0; i < curve->num_pivots; i++) {
563 26 pivot += get_bits(gb, hdr->bl_bit_depth);
564 26 curve->pivots[i] = av_clip_uint16(pivot);
565 }
566 }
567
568
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 if (use_nlq) {
569 ✗ int nlq_pivot = 0;
570 ✗ mapping->nlq_method_idc = get_bits(gb, 3);
571
572 ✗ for (int i = 0; i < 2; i++) {
573 ✗ nlq_pivot += get_bits(gb, hdr->bl_bit_depth);
574 ✗ mapping->nlq_pivots[i] = av_clip_uint16(nlq_pivot);
575 }
576
577 /**
578 * The patent mentions another legal value, NLQ_MU_LAW, but it's
579 * not documented anywhere how to parse or apply that type of NLQ.
580 */
581 ✗ VALIDATE(mapping->nlq_method_idc, 0, AV_DOVI_NLQ_LINEAR_DZ);
582 } else {
583 2 mapping->nlq_method_idc = AV_DOVI_NLQ_NONE;
584 }
585
586 2 mapping->num_x_partitions = get_ue_golomb_long(gb) + 1;
587 2 mapping->num_y_partitions = get_ue_golomb_long(gb) + 1;
588
2/4
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 2 times.
2 VALIDATE(mapping->num_x_partitions, 1, 0xFFFF);
589
2/4
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 2 times.
2 VALIDATE(mapping->num_y_partitions, 1, 0xFFFF);
590 /* End of rpu_data_header(), start of vdr_rpu_data_payload() */
591
592
2/2
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 2 times.
8 for (int c = 0; c < 3; c++) {
593 6 AVDOVIReshapingCurve *curve = &mapping->curves[c];
594
2/2
✓ Branch 0 taken 20 times.
✓ Branch 1 taken 6 times.
26 for (int i = 0; i < curve->num_pivots - 1; i++) {
595 20 int mapping_idc = get_ue_golomb_31(gb);
596
2/4
✓ Branch 0 taken 20 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 20 times.
20 VALIDATE(mapping_idc, 0, 1);
597 20 curve->mapping_idc[i] = mapping_idc;
598
2/3
✓ Branch 0 taken 16 times.
✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
20 switch (mapping_idc) {
599 16 case AV_DOVI_MAPPING_POLYNOMIAL: {
600 16 int poly_order_minus1 = get_ue_golomb_31(gb);
601
2/4
✓ Branch 0 taken 16 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 16 times.
16 VALIDATE(poly_order_minus1, 0, 1);
602 16 curve->poly_order[i] = poly_order_minus1 + 1;
603
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 16 times.
16 if (poly_order_minus1 == 0) {
604 ✗ int linear_interp_flag = get_bits1(gb);
605 ✗ if (linear_interp_flag) {
606 /* lack of documentation/samples */
607 ✗ avpriv_request_sample(s->logctx, "Dolby Vision "
608 "linear interpolation");
609 ✗ ff_dovi_ctx_unref(s);
610 ✗ return AVERROR_PATCHWELCOME;
611 }
612 }
613
2/2
✓ Branch 0 taken 48 times.
✓ Branch 1 taken 16 times.
64 for (int k = 0; k <= curve->poly_order[i]; k++)
614 48 curve->poly_coef[i][k] = get_se_coef(gb, hdr);
615 16 break;
616 }
617 4 case AV_DOVI_MAPPING_MMR: {
618 4 int mmr_order_minus1 = get_bits(gb, 2);
619
2/4
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 4 times.
4 VALIDATE(mmr_order_minus1, 0, 2);
620 4 curve->mmr_order[i] = mmr_order_minus1 + 1;
621 4 curve->mmr_constant[i] = get_se_coef(gb, hdr);
622
2/2
✓ Branch 0 taken 12 times.
✓ Branch 1 taken 4 times.
16 for (int j = 0; j < curve->mmr_order[i]; j++) {
623
2/2
✓ Branch 0 taken 84 times.
✓ Branch 1 taken 12 times.
96 for (int k = 0; k < 7; k++)
624 84 curve->mmr_coef[i][j][k] = get_se_coef(gb, hdr);
625 }
626 4 break;
627 }
628 }
629 }
630 }
631
632
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 if (use_nlq) {
633 ✗ for (int c = 0; c < 3; c++) {
634 ✗ AVDOVINLQParams *nlq = &mapping->nlq[c];
635 ✗ nlq->nlq_offset = get_bits(gb, hdr->el_bit_depth);
636 ✗ nlq->vdr_in_max = get_ue_coef(gb, hdr);
637 ✗ switch (mapping->nlq_method_idc) {
638 ✗ case AV_DOVI_NLQ_LINEAR_DZ:
639 ✗ nlq->linear_deadzone_slope = get_ue_coef(gb, hdr);
640 ✗ nlq->linear_deadzone_threshold = get_ue_coef(gb, hdr);
641 ✗ break;
642 }
643 }
644 }
645 }
646
647
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
2 if (vdr_dm_metadata_present) {
648 AVDOVIColorMetadata *color;
649 2 int affected_dm_id = get_ue_golomb_31(gb);
650 2 int current_dm_id = get_ue_golomb_31(gb);
651
2/4
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 2 times.
2 VALIDATE(affected_dm_id, 0, DOVI_MAX_DM_ID);
652
2/4
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 2 times.
2 VALIDATE(current_dm_id, 0, DOVI_MAX_DM_ID);
653
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 if (affected_dm_id != current_dm_id) {
654 /* The spec does not explain these fields at all, and there is
655 * a lack of samples to understand how they're supposed to work,
656 * so just assert them being equal for now */
657 ✗ avpriv_request_sample(s->logctx, "affected/current_dm_metadata_id "
658 "mismatch? %u != %u\n", affected_dm_id, current_dm_id);
659 ✗ ff_dovi_ctx_unref(s);
660 ✗ return AVERROR_PATCHWELCOME;
661 }
662
663
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 1 times.
2 if (!s->dm) {
664 1 s->dm = av_refstruct_allocz(sizeof(AVDOVIColorMetadata));
665
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (!s->dm) {
666 ✗ ff_dovi_ctx_unref(s);
667 ✗ return AVERROR(ENOMEM);
668 }
669 }
670
671 2 s->color = color = s->dm;
672 2 color->dm_metadata_id = affected_dm_id;
673 2 color->scene_refresh_flag = get_ue_golomb_31(gb);
674
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
2 if (!dm_compression) {
675
2/2
✓ Branch 0 taken 18 times.
✓ Branch 1 taken 2 times.
20 for (int i = 0; i < 9; i++)
676 18 color->ycc_to_rgb_matrix[i] = av_make_q(get_sbits(gb, 16), 1 << 13);
677
2/2
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 2 times.
8 for (int i = 0; i < 3; i++) {
678
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 6 times.
6 int denom = profile == 4 ? (1 << 30) : (1 << 28);
679 6 unsigned offset = get_bits_long(gb, 32);
680
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 6 times.
6 if (offset > INT_MAX) {
681 /* Ensure the result fits inside AVRational */
682 ✗ offset >>= 1;
683 ✗ denom >>= 1;
684 }
685 6 color->ycc_to_rgb_offset[i] = av_make_q(offset, denom);
686 }
687
2/2
✓ Branch 0 taken 18 times.
✓ Branch 1 taken 2 times.
20 for (int i = 0; i < 9; i++)
688 18 color->rgb_to_lms_matrix[i] = av_make_q(get_sbits(gb, 16), 1 << 14);
689
690 2 color->signal_eotf = get_bits(gb, 16);
691 2 color->signal_eotf_param0 = get_bits(gb, 16);
692 2 color->signal_eotf_param1 = get_bits(gb, 16);
693 2 color->signal_eotf_param2 = get_bits_long(gb, 32);
694 2 color->signal_bit_depth = get_bits(gb, 5);
695
2/4
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 2 times.
2 VALIDATE(color->signal_bit_depth, 8, 16);
696 2 color->signal_color_space = get_bits(gb, 2);
697 2 color->signal_chroma_format = get_bits(gb, 2);
698 2 color->signal_full_range_flag = get_bits(gb, 2);
699 2 color->source_min_pq = get_bits(gb, 12);
700 2 color->source_max_pq = get_bits(gb, 12);
701 2 color->source_diagonal = get_bits(gb, 10);
702 }
703
704 /* Parse extension blocks */
705
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 1 times.
2 if (s->ext_blocks) {
706 1 DOVIExt *ext = s->ext_blocks;
707
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 if (!dm_compression)
708 1 ext->num_static = 0;
709 1 ext->num_dynamic = 0;
710 }
711
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 2 times.
2 if ((ret = parse_ext_blocks(s, gb, 1, dm_compression, err_recognition)) < 0) {
712 ✗ ff_dovi_ctx_unref(s);
713 ✗ return ret;
714 }
715
716 /* ue(1) + ue(0) + level + CRC32 + terminator */
717
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 2 times.
2 if (get_bits_left(gb) >= 3 + 1 + 8 + 32 + 8) {
718 ✗ if ((ret = parse_ext_blocks(s, gb, 2, dm_compression, err_recognition)) < 0) {
719 ✗ ff_dovi_ctx_unref(s);
720 ✗ return ret;
721 }
722 }
723 } else {
724 ✗ s->color = &ff_dovi_color_default;
725 ✗ av_refstruct_unref(&s->ext_blocks);
726 }
727
728 2 int aligned = get_bits_count(gb) % 8 == 0;
729 2 align_get_bits(gb);
730
731 /* The payload is followed by a CRC32 and a 0x80 terminator. Some encoders
732 * emit an extra zero padding byte before the CRC32 when the payload already
733 * ended on a byte boundary. A leading zero byte is then ambiguous: it may
734 * be that padding, or simply a CRC32 that starts with a zero byte. If only
735 * the padded position holds the terminator, assume the padding is real. If
736 * both do (the last CRC32 byte is 0x80 or there is stray 0x80 after real
737 * payload) the terminator position is ambiguous. Calculate CRC twice when
738 * AV_EF_CRCCHECK is requested. Note that this situation is extremely
739 * unlikely, there shouldn't be non-zero data after payload. */
740
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
2 int pad = aligned && show_bits(gb, 8) == 0;
741 2 skip_bits(gb, 32); /* CRC32 */
742 2 int term_unpadded = get_bits(gb, 8) == 0x80; /* terminator w/o padding */
743
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
2 int term_padded = pad && show_bits(gb, 8) == 0x80; /* terminator w/ padding */
744
745
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
2 if (!term_unpadded && !term_padded) {
746 ✗ avpriv_request_sample(s->logctx, "Unexpected RPU format");
747 ✗ ff_dovi_ctx_unref(s);
748 ✗ return AVERROR_PATCHWELCOME;
749 }
750
751
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 if (err_recognition & AV_EF_CRCCHECK) {
752 ✗ if (term_padded && !term_unpadded)
753 ✗ skip_bits(gb, 8); /* unambiguously padded */
754 ✗ rpu_size = get_bits_count(gb) / 8;
755 ✗ uint32_t crc = av_bswap32(av_crc(av_crc_get_table(AV_CRC_32_IEEE),
756 -1, rpu, rpu_size - 1)); /* exclude 0x80 */
757 ✗ if (crc && term_padded && term_unpadded) {
758 ✗ crc = av_bswap32(av_crc(av_crc_get_table(AV_CRC_32_IEEE),
759 -1, rpu, rpu_size)); /* include padding byte */
760 }
761 ✗ if (crc) {
762 ✗ av_log(s->logctx, AV_LOG_ERROR, "RPU CRC mismatch: %X\n", crc);
763 ✗ if (err_recognition & AV_EF_EXPLODE) {
764 ✗ ff_dovi_ctx_unref(s);
765 ✗ return AVERROR_INVALIDDATA;
766 }
767 }
768 }
769
770 2 return 0;
771 }
772