FFmpeg
Loading...
Searching...
No Matches
hdr_dynamic_metadata.c
Go to the documentation of this file.
1/*
2 * This file is part of FFmpeg.
3 *
4 * FFmpeg is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2.1 of the License, or (at your option) any later version.
8 *
9 * FFmpeg is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
13 *
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with FFmpeg; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
17 */
18
19#include <limits.h>
20#include <stdio.h>
21#include <string.h>
22
23#include "libavutil/error.h"
24#include "libavutil/frame.h"
26#include "libavutil/mem.h"
27#include "libavutil/rational.h"
28
29/* Denominators av_dynamic_hdr_plus_to_t35() quantises to. The fixtures below
30 * express every rational in these terms, so a correct round trip reproduces
31 * the input exactly and the decoded values printed here can be compared
32 * against the fixture directly. */
33#define LUMINANCE_DEN 1
34#define PEAK_LUMINANCE_DEN 15
35#define RGB_DEN 100000
36#define FRACTION_PIXEL_DEN 1000
37#define KNEE_POINT_DEN 4095
38#define BEZIER_ANCHOR_DEN 1023
39#define SATURATION_DEN 8
40
41/*
42 * Printers. Every field a fixture populates is printed, so that a serializer
43 * or parser which drops or corrupts a value changes the FATE reference
44 * instead of silently passing a flag-only check.
45 */
46
47static void print_peak_matrix(const char *label, int rows, int cols,
48 const AVRational m[25][25])
49{
50 printf(" %s %dx%d:", label, rows, cols);
51 for (int i = 0; i < rows; i++)
52 for (int j = 0; j < cols; j++)
53 printf(" %d/%d", m[i][j].num, m[i][j].den);
54 printf("\n");
55}
56
58 int geometry)
59{
60 printf(" window %d:\n", w);
61 /* Window 0 covers the whole frame; its geometry is not serialized. */
62 if (geometry) {
63 printf(" corners: ul=%d/%d,%d/%d lr=%d/%d,%d/%d\n",
64 p->window_upper_left_corner_x.num,
65 p->window_upper_left_corner_x.den,
66 p->window_upper_left_corner_y.num,
67 p->window_upper_left_corner_y.den,
68 p->window_lower_right_corner_x.num,
69 p->window_lower_right_corner_x.den,
70 p->window_lower_right_corner_y.num,
71 p->window_lower_right_corner_y.den);
72 printf(" ellipse: center=%u,%u rotation=%u semimajor_int=%u"
73 " semimajor_ext=%u semiminor_ext=%u overlap=%u\n",
74 p->center_of_ellipse_x, p->center_of_ellipse_y,
75 p->rotation_angle, p->semimajor_axis_internal_ellipse,
76 p->semimajor_axis_external_ellipse,
77 p->semiminor_axis_external_ellipse,
78 p->overlap_process_option);
79 }
80 printf(" maxscl: %d/%d %d/%d %d/%d average_maxrgb=%d/%d\n",
81 p->maxscl[0].num, p->maxscl[0].den,
82 p->maxscl[1].num, p->maxscl[1].den,
83 p->maxscl[2].num, p->maxscl[2].den,
84 p->average_maxrgb.num, p->average_maxrgb.den);
85 printf(" distribution_maxrgb: n=%u",
86 p->num_distribution_maxrgb_percentiles);
87 for (int i = 0; i < p->num_distribution_maxrgb_percentiles; i++)
88 printf(" %u%%=%d/%d", p->distribution_maxrgb[i].percentage,
89 p->distribution_maxrgb[i].percentile.num,
90 p->distribution_maxrgb[i].percentile.den);
91 printf("\n fraction_bright_pixels=%d/%d\n",
92 p->fraction_bright_pixels.num, p->fraction_bright_pixels.den);
93 printf(" tone_mapping_flag=%u", p->tone_mapping_flag);
94 if (p->tone_mapping_flag) {
95 printf(" knee=%d/%d,%d/%d anchors=%u:",
96 p->knee_point_x.num, p->knee_point_x.den,
97 p->knee_point_y.num, p->knee_point_y.den,
98 p->num_bezier_curve_anchors);
99 for (int i = 0; i < p->num_bezier_curve_anchors; i++)
100 printf(" %d/%d", p->bezier_curve_anchors[i].num,
101 p->bezier_curve_anchors[i].den);
102 }
103 printf("\n color_saturation_mapping_flag=%u",
104 p->color_saturation_mapping_flag);
105 if (p->color_saturation_mapping_flag)
106 printf(" weight=%d/%d", p->color_saturation_weight.num,
107 p->color_saturation_weight.den);
108 printf("\n");
109}
110
111static void print_hdr_plus(const char *label, const AVDynamicHDRPlus *s)
112{
113 printf("%s:\n", label);
114 printf(" application_version=%u num_windows=%u"
115 " targeted_system_display_maximum_luminance=%d/%d\n",
116 s->application_version, s->num_windows,
117 s->targeted_system_display_maximum_luminance.num,
118 s->targeted_system_display_maximum_luminance.den);
119 printf(" targeted_system_display_actual_peak_luminance_flag=%u\n",
120 s->targeted_system_display_actual_peak_luminance_flag);
121 if (s->targeted_system_display_actual_peak_luminance_flag)
122 print_peak_matrix("targeted_peak",
123 s->num_rows_targeted_system_display_actual_peak_luminance,
124 s->num_cols_targeted_system_display_actual_peak_luminance,
125 s->targeted_system_display_actual_peak_luminance);
126 printf(" mastering_display_actual_peak_luminance_flag=%u\n",
127 s->mastering_display_actual_peak_luminance_flag);
128 if (s->mastering_display_actual_peak_luminance_flag)
129 print_peak_matrix("mastering_peak",
130 s->num_rows_mastering_display_actual_peak_luminance,
131 s->num_cols_mastering_display_actual_peak_luminance,
132 s->mastering_display_actual_peak_luminance);
133 for (int w = 0; w < s->num_windows; w++)
134 print_window(w, &s->params[w], w > 0);
135}
136
137static void print_app5(const char *label, const AVDynamicHDRSmpte2094App5 *s)
138{
139 printf("%s:\n", label);
140 printf(" application_version=%u minimum_application_version=%u\n",
141 s->application_version, s->minimum_application_version);
142 printf(" has_custom_hdr_reference_white_flag=%u",
143 s->has_custom_hdr_reference_white_flag);
144 if (s->has_custom_hdr_reference_white_flag)
145 printf(" hdr_reference_white=0x%04x", s->hdr_reference_white);
146 printf("\n has_adaptive_tone_map_flag=%u\n",
147 s->has_adaptive_tone_map_flag);
148 if (!s->has_adaptive_tone_map_flag)
149 return;
150
151 printf(" baseline_hdr_headroom=0x%04x"
152 " use_reference_white_tone_mapping_flag=%u\n",
153 s->baseline_hdr_headroom, s->use_reference_white_tone_mapping_flag);
154 if (s->use_reference_white_tone_mapping_flag)
155 return;
156
157 printf(" num_alternate_images=%u chromaticities_flag=%u"
158 " has_common_component_mix_params_flag=%u"
159 " has_common_curve_params_flag=%u\n",
160 s->num_alternate_images,
161 s->gain_application_space_chromaticities_flag,
162 s->has_common_component_mix_params_flag,
163 s->has_common_curve_params_flag);
164 if (s->gain_application_space_chromaticities_flag == 3) {
165 printf(" chromaticities:");
166 for (int r = 0; r < 8; r++)
167 printf(" 0x%04x", s->gain_application_space_chromaticities[r]);
168 printf("\n");
169 }
170
171 for (int a = 0; a < s->num_alternate_images; a++) {
172 int n = s->gain_curve_num_control_points_minus_1[a] + 1;
173 printf(" alternate %d: hdr_headroom=0x%04x component_mixing_type=%u\n",
174 a, s->alternate_hdr_headrooms[a], s->component_mixing_type[a]);
175 if (s->component_mixing_type[a] == 3) {
176 printf(" mixing flag:coefficient:");
177 for (int k = 0; k < 6; k++)
178 printf(" %u:0x%04x",
179 s->has_component_mixing_coefficient_flag[a][k],
180 s->component_mixing_coefficient[a][k]);
181 printf("\n");
182 }
183 printf(" curve: num_control_points=%d use_pchip_slope=%u\n", n,
184 s->gain_curve_use_pchip_slope_flag[a]);
185 printf(" x:");
186 for (int c = 0; c < n; c++)
187 printf(" 0x%04x", s->gain_curve_control_points_x[a][c]);
188 printf("\n y:");
189 for (int c = 0; c < n; c++)
190 printf(" 0x%04x", s->gain_curve_control_points_y[a][c]);
191 printf("\n");
192 /* theta is only serialized when the pchip slope is not used. */
193 if (!s->gain_curve_use_pchip_slope_flag[a]) {
194 printf(" theta:");
195 for (int c = 0; c < n; c++)
196 printf(" 0x%04x", s->gain_curve_control_points_theta[a][c]);
197 printf("\n");
198 }
199 }
200}
201
202/*
203 * Round-trip helpers: serialize, then parse back into a zeroed struct.
204 */
205
207{
208 uint8_t *buf = NULL;
209 size_t size = 0;
210 int ret;
211
212 ret = av_dynamic_hdr_plus_to_t35(in, &buf, &size);
213 if (ret < 0)
214 return ret;
215
216 memset(out, 0, sizeof(*out));
218 av_free(buf);
219 return ret;
220}
221
224{
225 uint8_t *buf = NULL;
226 size_t size = 0;
227 int ret;
228
230 if (ret < 0)
231 return ret;
232
233 memset(out, 0, sizeof(*out));
235 av_free(buf);
236 return ret;
237}
238
239/*
240 * Fixtures.
241 */
242
243/* A conforming ST 2094-40:2020 ApplicationVersion 1 payload: exactly one
244 * processing window, neither actual-peak-luminance item and no
245 * ColorSaturationWeight (§9.4 excludes all three for Version 1), and the nine
246 * distribution maxrgb percentiles §8.5.4 requires for that version.
247 * CTA-861-H fixes application_version at 1 for HDR10+ carried over T.35.
248 *
249 * The entries for 5% and 10% are not CFD percentile samples: §8.5.4 reserves
250 * every value other than 0.00000 and 0.00255 for that pair. No other entry may
251 * exceed the largest MaxSCL component either, since §8.3 takes MaxSCL over each
252 * RGB component separately while §8.5.2 draws the distribution from the
253 * per-pixel maximum component. */
255{
256 static const uint8_t percentages[9] = { 1, 5, 10, 25, 50, 75, 90, 95, 99 };
257 static const int percentiles[9] = {
258 1000, 0, 255, 25000, 50000, 75000, 90000, 95000, 99000,
259 };
260
261 memset(s, 0, sizeof(*s));
262
263 s->application_version = 1;
264 s->num_windows = 1;
265 s->targeted_system_display_maximum_luminance =
266 (AVRational){ 400, LUMINANCE_DEN };
267 s->targeted_system_display_actual_peak_luminance_flag = 0;
268 s->mastering_display_actual_peak_luminance_flag = 0;
269
270 s->params[0].maxscl[0] = (AVRational){ 50000, RGB_DEN };
271 s->params[0].maxscl[1] = (AVRational){ 60000, RGB_DEN };
272 s->params[0].maxscl[2] = (AVRational){ 99000, RGB_DEN };
273 s->params[0].average_maxrgb = (AVRational){ 40000, RGB_DEN };
274
275 s->params[0].num_distribution_maxrgb_percentiles = 9;
276 for (int i = 0; i < 9; i++) {
277 s->params[0].distribution_maxrgb[i].percentage = percentages[i];
278 s->params[0].distribution_maxrgb[i].percentile =
279 (AVRational){ percentiles[i], RGB_DEN };
280 }
281 s->params[0].fraction_bright_pixels =
283
284 s->params[0].tone_mapping_flag = 1;
285 s->params[0].knee_point_x = (AVRational){ 512, KNEE_POINT_DEN };
286 s->params[0].knee_point_y = (AVRational){ 1024, KNEE_POINT_DEN };
287 s->params[0].num_bezier_curve_anchors = 9;
288 for (int i = 0; i < 9; i++)
289 s->params[0].bezier_curve_anchors[i] =
290 (AVRational){ 100 * (i + 1), BEZIER_ANCHOR_DEN };
291
292 s->params[0].color_saturation_mapping_flag = 0;
293}
294
295/* Deliberately NOT a conforming Version 1 payload: it combines a second
296 * processing window, both actual-peak-luminance matrices and
297 * ColorSaturationWeight, all of which §9.4 excludes for Version 1. It exists
298 * only to drive the optional serializer/parser branches those fields guard.
299 *
300 * tone_mapping_flag stays set for every window because the serializer writes
301 * color_saturation_mapping_flag inside the tone-mapping block while the parser
302 * reads it outside; only tone_mapping_flag=1 round-trips today. */
304{
305 memset(s, 0, sizeof(*s));
306
307 s->application_version = 1;
308 s->num_windows = 2;
309 s->targeted_system_display_maximum_luminance =
310 (AVRational){ 10000, LUMINANCE_DEN };
311
312 s->params[1].window_upper_left_corner_x = (AVRational){ 100, 1 };
313 s->params[1].window_upper_left_corner_y = (AVRational){ 120, 1 };
314 s->params[1].window_lower_right_corner_x = (AVRational){ 500, 1 };
315 s->params[1].window_lower_right_corner_y = (AVRational){ 520, 1 };
316 s->params[1].center_of_ellipse_x = 300;
317 s->params[1].center_of_ellipse_y = 320;
318 s->params[1].rotation_angle = 45;
319 s->params[1].semimajor_axis_internal_ellipse = 50;
320 s->params[1].semimajor_axis_external_ellipse = 100;
321 s->params[1].semiminor_axis_external_ellipse = 80;
322 s->params[1].overlap_process_option = 1;
323
324 s->params[0].maxscl[0] = (AVRational){ 50000, RGB_DEN };
325 s->params[0].maxscl[1] = (AVRational){ 60000, RGB_DEN };
326 s->params[0].maxscl[2] = (AVRational){ 70000, RGB_DEN };
327 s->params[0].average_maxrgb = (AVRational){ 40000, RGB_DEN };
328 s->params[0].num_distribution_maxrgb_percentiles = 2;
329 s->params[0].distribution_maxrgb[0].percentage = 50;
330 s->params[0].distribution_maxrgb[0].percentile =
331 (AVRational){ 30000, RGB_DEN };
332 s->params[0].distribution_maxrgb[1].percentage = 99;
333 s->params[0].distribution_maxrgb[1].percentile =
334 (AVRational){ 90000, RGB_DEN };
335 s->params[0].fraction_bright_pixels =
337
338 s->params[1].maxscl[0] = (AVRational){ 20000, RGB_DEN };
339 s->params[1].maxscl[1] = (AVRational){ 30000, RGB_DEN };
340 s->params[1].maxscl[2] = (AVRational){ 40000, RGB_DEN };
341 s->params[1].average_maxrgb = (AVRational){ 25000, RGB_DEN };
342 s->params[1].num_distribution_maxrgb_percentiles = 1;
343 s->params[1].distribution_maxrgb[0].percentage = 25;
344 s->params[1].distribution_maxrgb[0].percentile =
345 (AVRational){ 12500, RGB_DEN };
346 s->params[1].fraction_bright_pixels =
348
349 s->targeted_system_display_actual_peak_luminance_flag = 1;
350 s->num_rows_targeted_system_display_actual_peak_luminance = 2;
351 s->num_cols_targeted_system_display_actual_peak_luminance = 3;
352 for (int i = 0; i < 2; i++)
353 for (int j = 0; j < 3; j++)
354 s->targeted_system_display_actual_peak_luminance[i][j] =
355 (AVRational){ i * 3 + j, PEAK_LUMINANCE_DEN };
356
357 s->mastering_display_actual_peak_luminance_flag = 1;
358 s->num_rows_mastering_display_actual_peak_luminance = 3;
359 s->num_cols_mastering_display_actual_peak_luminance = 2;
360 for (int i = 0; i < 3; i++)
361 for (int j = 0; j < 2; j++)
362 s->mastering_display_actual_peak_luminance[i][j] =
363 (AVRational){ 15 - (i * 2 + j), PEAK_LUMINANCE_DEN };
364
365 for (int w = 0; w < 2; w++) {
366 s->params[w].tone_mapping_flag = 1;
367 s->params[w].knee_point_x = (AVRational){ 500 + w, KNEE_POINT_DEN };
368 s->params[w].knee_point_y = (AVRational){ 800 + w, KNEE_POINT_DEN };
369 s->params[w].num_bezier_curve_anchors = 3;
370 s->params[w].bezier_curve_anchors[0] =
371 (AVRational){ 100 + w, BEZIER_ANCHOR_DEN };
372 s->params[w].bezier_curve_anchors[1] =
373 (AVRational){ 500 + w, BEZIER_ANCHOR_DEN };
374 s->params[w].bezier_curve_anchors[2] =
375 (AVRational){ 900 + w, BEZIER_ANCHOR_DEN };
376 s->params[w].color_saturation_mapping_flag = 1;
377 s->params[w].color_saturation_weight =
378 (AVRational){ 8 + w, SATURATION_DEN };
379 }
380}
381
382static void test_hdr_plus(void)
383{
384 AVDynamicHDRPlus *hdr, *hdr_rt;
385 AVFrame *frame;
386 size_t size = 0, required = 0;
387 uint8_t *buf = NULL;
388 int ret;
389
390 printf("=== AVDynamicHDRPlus ===\n");
391
392 printf("Testing av_dynamic_hdr_plus_alloc()\n");
394 printf("alloc: %s size>0=%s\n", hdr ? "OK" : "FAIL",
395 size > 0 ? "yes" : "no");
396 av_freep(&hdr);
397
399 printf("alloc (no size): %s\n", hdr ? "OK" : "FAIL");
400 av_freep(&hdr);
401
402 printf("\nTesting av_dynamic_hdr_plus_create_side_data()\n");
404 if (frame) {
406 printf("create_side_data: %s\n", hdr ? "OK" : "FAIL");
408 }
409
412 if (!hdr || !hdr_rt) {
413 av_freep(&hdr);
414 av_freep(&hdr_rt);
415 return;
416 }
418
419 /* Size-query mode: data=NULL, size receives the required byte count. */
420 printf("\nTesting av_dynamic_hdr_plus_to_t35() size query\n");
421 required = 0;
422 ret = av_dynamic_hdr_plus_to_t35(hdr, NULL, &required);
423 printf("size query: ret=%d required>0=%s\n", ret,
424 required > 0 ? "yes" : "no");
425
426 /* Allocation mode: *data=NULL, the function allocates the buffer. */
427 printf("\nTesting av_dynamic_hdr_plus_to_t35() allocation\n");
428 buf = NULL;
429 size = 0;
430 ret = av_dynamic_hdr_plus_to_t35(hdr, &buf, &size);
431 printf("alloc mode: ret=%d size_match=%s\n", ret,
432 size == required ? "yes" : "no");
433 av_freep(&buf);
434
435 /* Existing-buffer mode: a caller-owned buffer is filled in place. */
436 printf("\nTesting av_dynamic_hdr_plus_to_t35() existing buffer\n");
437 buf = av_malloc(required);
438 if (buf) {
439 uint8_t *orig = buf;
440 size = required;
441 ret = av_dynamic_hdr_plus_to_t35(hdr, &buf, &size);
442 printf("existing buf: ret=%d same_pointer=%s size_match=%s\n", ret,
443 buf == orig ? "yes" : "no",
444 size == required ? "yes" : "no");
445 av_freep(&buf);
446 }
447
448 /* Buffer-too-small: caller-owned buffer shorter than required. */
449 printf("\nTesting av_dynamic_hdr_plus_to_t35() buffer too small\n");
450 if (required > 1) {
451 buf = av_malloc(required - 1);
452 if (buf) {
453 size = required - 1;
454 ret = av_dynamic_hdr_plus_to_t35(hdr, &buf, &size);
455 printf("too small: ret==AVERROR_BUFFER_TOO_SMALL=%s\n",
456 ret == AVERROR_BUFFER_TOO_SMALL ? "yes" : "no");
457 av_freep(&buf);
458 }
459 }
460
461 printf("\nTesting round trip, conforming ST 2094-40 Version 1 payload\n");
462 ret = hdr_plus_round_trip(hdr, hdr_rt);
463 printf("round trip: ret=%d\n", ret);
464 if (ret >= 0)
465 print_hdr_plus("decoded", hdr_rt);
466
467 /* to_t35() ignores application_version and always writes the CTA-861-H
468 * value, so the parsed field does not echo an out-of-range input. */
469 printf("\nTesting that to_t35() overrides application_version\n");
470 hdr->application_version = 7;
471 ret = hdr_plus_round_trip(hdr, hdr_rt);
472 printf("input=7 ret=%d decoded application_version=%u\n", ret,
473 hdr_rt->application_version);
474 hdr->application_version = 1;
475
476 printf("\nTesting round trip, synthetic payload (branch coverage only)\n");
478 ret = hdr_plus_round_trip(hdr, hdr_rt);
479 printf("round trip: ret=%d\n", ret);
480 if (ret >= 0)
481 print_hdr_plus("decoded", hdr_rt);
482
483 printf("\nTesting error paths\n");
484 buf = NULL;
485 size = 0;
486 ret = av_dynamic_hdr_plus_to_t35(NULL, &buf, &size);
487 printf("to_t35 NULL s: ret==AVERROR(EINVAL)=%s\n",
488 ret == AVERROR(EINVAL) ? "yes" : "no");
489
491 printf("to_t35 no data no size: ret==AVERROR(EINVAL)=%s\n",
492 ret == AVERROR(EINVAL) ? "yes" : "no");
493
494 ret = av_dynamic_hdr_plus_from_t35(NULL, (const uint8_t *)"", 0);
495 printf("from_t35 NULL s: ret==AVERROR(EINVAL)=%s\n",
496 ret == AVERROR(EINVAL) ? "yes" : "no");
497
498 {
499 /* Oversized input is rejected before parsing. */
500 size_t big = AV_HDR_PLUS_MAX_PAYLOAD_SIZE + 1;
501 uint8_t *oversized = av_mallocz(big);
502 if (oversized) {
503 ret = av_dynamic_hdr_plus_from_t35(hdr_rt, oversized, big);
504 printf("from_t35 oversized: ret==AVERROR(EINVAL)=%s\n",
505 ret == AVERROR(EINVAL) ? "yes" : "no");
506 av_free(oversized);
507 }
508 }
509
510 av_freep(&hdr);
511 av_freep(&hdr_rt);
512
513 printf("\nTesting OOM path\n");
514 av_max_alloc(1);
516 printf("alloc OOM: %s\n", hdr ? "FAIL" : "OK");
517 av_max_alloc(INT_MAX);
518 av_freep(&hdr);
519}
520
521/* Table C.2 early-out: no adaptive tone map, custom reference white only. */
523{
524 memset(s, 0, sizeof(*s));
525 s->application_version = 1;
526 s->minimum_application_version = 0;
527 s->has_custom_hdr_reference_white_flag = 1;
528 s->hdr_reference_white = 0x0203;
529 s->has_adaptive_tone_map_flag = 0;
530}
531
532/* Table C.3 early-out: reference-white tone mapping, no alternate images. */
534{
535 memset(s, 0, sizeof(*s));
536 s->application_version = 2;
537 s->has_adaptive_tone_map_flag = 1;
538 s->baseline_hdr_headroom = 0x0abc;
539 s->use_reference_white_tone_mapping_flag = 1;
540}
541
542/* Per-image parameters: explicit gain application space chromaticities,
543 * component mixing type 3 with a mix of present and absent coefficients, and
544 * explicit control point slopes (theta). */
546{
547 memset(s, 0, sizeof(*s));
548 s->application_version = 1;
549 s->has_adaptive_tone_map_flag = 1;
550 s->baseline_hdr_headroom = 0x0abc;
551 s->num_alternate_images = 2;
552 s->gain_application_space_chromaticities_flag = 3;
553 for (int r = 0; r < 8; r++)
554 s->gain_application_space_chromaticities[r] = 100 * (r + 1);
555 s->has_common_component_mix_params_flag = 0;
556 s->has_common_curve_params_flag = 0;
557 for (int a = 0; a < 2; a++) {
558 s->alternate_hdr_headrooms[a] = 0x0100 + a;
559 s->component_mixing_type[a] = 3;
560 for (int k = 0; k < 6; k++) {
561 s->has_component_mixing_coefficient_flag[a][k] = k & 1;
562 s->component_mixing_coefficient[a][k] = 0x1000 + 0x10 * a + k;
563 }
564 s->gain_curve_num_control_points_minus_1[a] = 1;
565 s->gain_curve_use_pchip_slope_flag[a] = 0;
566 for (int c = 0; c < 2; c++) {
567 s->gain_curve_control_points_x[a][c] = 0x2000 + 0x10 * a + c;
568 s->gain_curve_control_points_y[a][c] = 0x3000 + 0x10 * a + c;
569 s->gain_curve_control_points_theta[a][c] = 0x4000 + 0x10 * a + c;
570 }
571 }
572}
573
574/* The complementary selectors: no explicit chromaticities, a component mixing
575 * type other than 3, mixing and curve parameters shared from alternate 0, and
576 * the pchip slope, which omits theta from the payload.
577 *
578 * The common_* flags make the parser copy alternate 0's parameters to the
579 * other alternates, so the fixture sets them identically everywhere; only
580 * alternate_hdr_headrooms and the y control points stay per-image. */
582{
583 memset(s, 0, sizeof(*s));
584 s->application_version = 1;
585 s->has_adaptive_tone_map_flag = 1;
586 s->baseline_hdr_headroom = 0x0def;
587 s->num_alternate_images = 3;
588 s->gain_application_space_chromaticities_flag = 0;
589 s->has_common_component_mix_params_flag = 1;
590 s->has_common_curve_params_flag = 1;
591 for (int a = 0; a < 3; a++) {
592 s->alternate_hdr_headrooms[a] = 0x0200 + a;
593 s->component_mixing_type[a] = 1;
594 s->gain_curve_num_control_points_minus_1[a] = 2;
595 s->gain_curve_use_pchip_slope_flag[a] = 1;
596 for (int c = 0; c < 3; c++) {
597 s->gain_curve_control_points_x[a][c] = 0x5000 + c;
598 s->gain_curve_control_points_y[a][c] = 0x6000 + 0x10 * a + c;
599 }
600 }
601}
602
603static void test_app5(void)
604{
605 AVDynamicHDRSmpte2094App5 *app5, *app5_rt;
606 AVFrame *frame;
607 size_t size = 0, required = 0;
608 uint8_t *buf = NULL;
609 int ret;
610
611 printf("\n=== AVDynamicHDRSmpte2094App5 ===\n");
612 printf("Testing av_dynamic_hdr_smpte2094_app5_alloc()\n");
614 printf("alloc: %s size>0=%s\n", app5 ? "OK" : "FAIL",
615 size > 0 ? "yes" : "no");
616 av_freep(&app5);
617
618 printf("\nTesting av_dynamic_hdr_smpte2094_app5_create_side_data()\n");
620 if (frame) {
622 printf("create_side_data: %s\n", app5 ? "OK" : "FAIL");
624 }
625
628 if (!app5 || !app5_rt) {
629 av_freep(&app5);
630 av_freep(&app5_rt);
631 return;
632 }
633 fill_app5_minimal(app5);
634
635 printf("\nTesting av_dynamic_hdr_smpte2094_app5_to_t35()\n");
636 required = 0;
637 ret = av_dynamic_hdr_smpte2094_app5_to_t35(app5, NULL, &required);
638 printf("size query: ret=%d required>0=%s\n", ret,
639 required > 0 ? "yes" : "no");
640
641 buf = NULL;
642 size = 0;
643 ret = av_dynamic_hdr_smpte2094_app5_to_t35(app5, &buf, &size);
644 printf("alloc mode: ret=%d size_match=%s\n", ret,
645 size == required ? "yes" : "no");
646 av_freep(&buf);
647
648 printf("\nTesting round trip, no adaptive tone map\n");
649 ret = app5_round_trip(app5, app5_rt);
650 printf("round trip: ret=%d\n", ret);
651 if (ret >= 0)
652 print_app5("decoded", app5_rt);
653
654 printf("\nTesting round trip, reference-white tone mapping\n");
656 ret = app5_round_trip(app5, app5_rt);
657 printf("round trip: ret=%d\n", ret);
658 if (ret >= 0)
659 print_app5("decoded", app5_rt);
660
661 printf("\nTesting round trip, per-image mixing and curve parameters\n");
663 ret = app5_round_trip(app5, app5_rt);
664 printf("round trip: ret=%d\n", ret);
665 if (ret >= 0)
666 print_app5("decoded", app5_rt);
667
668 printf("\nTesting round trip, common mixing and curve parameters\n");
669 fill_app5_common(app5);
670 ret = app5_round_trip(app5, app5_rt);
671 printf("round trip: ret=%d\n", ret);
672 if (ret >= 0)
673 print_app5("decoded", app5_rt);
674
675 printf("\nTesting error paths\n");
676 buf = NULL;
677 size = 0;
679 printf("to_t35 NULL s: ret==AVERROR(EINVAL)=%s\n",
680 ret == AVERROR(EINVAL) ? "yes" : "no");
681
682 ret = av_dynamic_hdr_smpte2094_app5_from_t35(NULL, (const uint8_t *)"", 0);
683 printf("from_t35 NULL s: ret==AVERROR(EINVAL)=%s\n",
684 ret == AVERROR(EINVAL) ? "yes" : "no");
685
686 /* application_version does not fit the 3-bit field. */
687 app5->application_version = 8;
689 printf("to_t35 invalid application_version: ret==AVERROR_INVALIDDATA=%s\n",
690 ret == AVERROR_INVALIDDATA ? "yes" : "no");
691 app5->application_version = 1;
692
693 /* minimum_application_version above the documented maximum. */
696 printf("to_t35 invalid minimum_application_version:"
697 " ret==AVERROR_INVALIDDATA=%s\n",
698 ret == AVERROR_INVALIDDATA ? "yes" : "no");
700
701 /* More alternate images than the 3-bit count admits. */
702 app5->num_alternate_images = 5;
704 printf("to_t35 too many alternate images: ret==AVERROR_INVALIDDATA=%s\n",
705 ret == AVERROR_INVALIDDATA ? "yes" : "no");
706
707 av_freep(&app5);
708 av_freep(&app5_rt);
709
710 printf("\nTesting OOM path\n");
711 av_max_alloc(1);
713 printf("alloc OOM: %s\n", app5 ? "FAIL" : "OK");
714 av_max_alloc(INT_MAX);
715 av_freep(&app5);
716}
717
718int main(void)
719{
721 test_app5();
722 return 0;
723}
static FILE * out
#define i(width, name, range_min, range_max)
Definition cbs_h264.c:63
#define s(width, name)
Definition cbs_vp9.c:198
#define NULL
Definition coverity.c:32
__device__ int printf(const char *,...)
static AVFrame * frame
error code definitions
reference-counted frame API
#define AVERROR_BUFFER_TOO_SMALL
Buffer too small.
Definition error.h:53
#define AVERROR_INVALIDDATA
Invalid data found when processing input.
Definition error.h:61
#define AVERROR(e)
Definition error.h:45
void av_frame_free(AVFrame **frame)
Free the frame and any dynamically allocated objects in it, e.g.
Definition frame.c:64
AVFrame * av_frame_alloc(void)
Allocate an AVFrame and set its fields to default values.
Definition frame.c:52
void av_max_alloc(size_t max)
Set the maximum size that may be allocated in one block.
Definition mem.c:76
int a
AVDynamicHDRPlus * av_dynamic_hdr_plus_alloc(size_t *size)
Allocate an AVDynamicHDRPlus structure and set its fields to default values.
AVDynamicHDRPlus * av_dynamic_hdr_plus_create_side_data(AVFrame *frame)
Allocate a complete AVDynamicHDRPlus and add it to the frame.
AVDynamicHDRSmpte2094App5 * av_dynamic_hdr_smpte2094_app5_alloc(size_t *size)
Allocate an AVDynamicHDRSmpte2094App5 structure and set its fields to default values.
int av_dynamic_hdr_smpte2094_app5_to_t35(const AVDynamicHDRSmpte2094App5 *s, uint8_t **data, size_t *size)
Serialize dynamic SMPTE-2094-50 metadata to a ITU-T T.35 message.
int av_dynamic_hdr_plus_from_t35(AVDynamicHDRPlus *s, const uint8_t *data, size_t size)
Parse the user data registered ITU-T T.35 to AVbuffer (AVDynamicHDRPlus).
int av_dynamic_hdr_plus_to_t35(const AVDynamicHDRPlus *s, uint8_t **data, size_t *size)
Serialize dynamic HDR10+ metadata to a user data registered ITU-T T.35 buffer, excluding the first 48...
int av_dynamic_hdr_smpte2094_app5_from_t35(AVDynamicHDRSmpte2094App5 *s, const uint8_t *data, size_t size)
Parse the user data formatted as ITU-T T.35 message to AVDynamicHDRSmpte2094App5.
AVDynamicHDRSmpte2094App5 * av_dynamic_hdr_smpte2094_app5_create_side_data(AVFrame *frame)
Allocate a complete AVDynamicHDRSmpte2094App5 and add it to the frame.
#define AV_HDR_PLUS_MAX_PAYLOAD_SIZE
#define r
Definition input.c:42
uint8_t w
Definition llvidencdsp.c:39
Memory handling functions.
#define av_malloc(s)
Definition ops_static.c:52
Utilities for rational number calculation.
This struct represents dynamic metadata for color volume transform - application 4 of SMPTE 2094-40:2...
uint8_t application_version
Application version in the application defining document in ST-2094 suite.
This struct represents dynamic metadata for color volume transform as specified in the SMPTE 2094-50 ...
uint8_t application_version
Section C.2.1.
This structure describes decoded (raw) audio or video data.
Definition frame.h:472
Color transform parameters at a processing window in a dynamic metadata for SMPTE 2094-40.
Rational number (pair of numerator and denominator).
Definition rational.h:58
#define av_free(p)
#define av_mallocz(s)
#define av_freep(p)
#define PEAK_LUMINANCE_DEN
#define KNEE_POINT_DEN
static void fill_hdr_plus_conforming(AVDynamicHDRPlus *s)
static void test_hdr_plus(void)
static int hdr_plus_round_trip(const AVDynamicHDRPlus *in, AVDynamicHDRPlus *out)
static void print_peak_matrix(const char *label, int rows, int cols, const AVRational m[25][25])
#define LUMINANCE_DEN
static void print_app5(const char *label, const AVDynamicHDRSmpte2094App5 *s)
static void test_app5(void)
#define FRACTION_PIXEL_DEN
static void fill_app5_reference_white(AVDynamicHDRSmpte2094App5 *s)
static void fill_app5_per_image(AVDynamicHDRSmpte2094App5 *s)
int main(void)
static int app5_round_trip(const AVDynamicHDRSmpte2094App5 *in, AVDynamicHDRSmpte2094App5 *out)
#define SATURATION_DEN
static void fill_app5_common(AVDynamicHDRSmpte2094App5 *s)
#define BEZIER_ANCHOR_DEN
static void print_hdr_plus(const char *label, const AVDynamicHDRPlus *s)
#define RGB_DEN
static void fill_app5_minimal(AVDynamicHDRSmpte2094App5 *s)
static void fill_hdr_plus_synthetic(AVDynamicHDRPlus *s)
static void print_window(int w, const AVHDRPlusColorTransformParams *p, int geometry)
int size
static double c[64]