FFmpeg coverage


Directory: ../../../ffmpeg/
File: src/libavutil/tests/hdr_dynamic_metadata.c
Date: 2026-09-28 04:46:15
Exec Total Coverage
Lines: 467 475 98.3%
Functions: 15 15 100.0%
Branches: 112 156 71.8%

Line Branch Exec Source
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"
25 #include "libavutil/hdr_dynamic_metadata.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
47 2 static void print_peak_matrix(const char *label, int rows, int cols,
48 const AVRational m[25][25])
49 {
50 2 printf(" %s %dx%d:", label, rows, cols);
51
2/2
✓ Branch 0 taken 5 times.
✓ Branch 1 taken 2 times.
7 for (int i = 0; i < rows; i++)
52
2/2
✓ Branch 0 taken 12 times.
✓ Branch 1 taken 5 times.
17 for (int j = 0; j < cols; j++)
53 12 printf(" %d/%d", m[i][j].num, m[i][j].den);
54 2 printf("\n");
55 2 }
56
57 3 static void print_window(int w, const AVHDRPlusColorTransformParams *p,
58 int geometry)
59 {
60 3 printf(" window %d:\n", w);
61 /* Window 0 covers the whole frame; its geometry is not serialized. */
62
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 2 times.
3 if (geometry) {
63 1 printf(" corners: ul=%d/%d,%d/%d lr=%d/%d,%d/%d\n",
64 1 p->window_upper_left_corner_x.num,
65 1 p->window_upper_left_corner_x.den,
66 1 p->window_upper_left_corner_y.num,
67 1 p->window_upper_left_corner_y.den,
68 1 p->window_lower_right_corner_x.num,
69 1 p->window_lower_right_corner_x.den,
70 1 p->window_lower_right_corner_y.num,
71 1 p->window_lower_right_corner_y.den);
72 1 printf(" ellipse: center=%u,%u rotation=%u semimajor_int=%u"
73 " semimajor_ext=%u semiminor_ext=%u overlap=%u\n",
74 1 p->center_of_ellipse_x, p->center_of_ellipse_y,
75 1 p->rotation_angle, p->semimajor_axis_internal_ellipse,
76 1 p->semimajor_axis_external_ellipse,
77 1 p->semiminor_axis_external_ellipse,
78 1 p->overlap_process_option);
79 }
80 3 printf(" maxscl: %d/%d %d/%d %d/%d average_maxrgb=%d/%d\n",
81 3 p->maxscl[0].num, p->maxscl[0].den,
82 3 p->maxscl[1].num, p->maxscl[1].den,
83 3 p->maxscl[2].num, p->maxscl[2].den,
84 3 p->average_maxrgb.num, p->average_maxrgb.den);
85 3 printf(" distribution_maxrgb: n=%u",
86 3 p->num_distribution_maxrgb_percentiles);
87
2/2
✓ Branch 0 taken 12 times.
✓ Branch 1 taken 3 times.
15 for (int i = 0; i < p->num_distribution_maxrgb_percentiles; i++)
88 12 printf(" %u%%=%d/%d", p->distribution_maxrgb[i].percentage,
89 12 p->distribution_maxrgb[i].percentile.num,
90 12 p->distribution_maxrgb[i].percentile.den);
91 3 printf("\n fraction_bright_pixels=%d/%d\n",
92 3 p->fraction_bright_pixels.num, p->fraction_bright_pixels.den);
93 3 printf(" tone_mapping_flag=%u", p->tone_mapping_flag);
94
1/2
✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
3 if (p->tone_mapping_flag) {
95 3 printf(" knee=%d/%d,%d/%d anchors=%u:",
96 3 p->knee_point_x.num, p->knee_point_x.den,
97 3 p->knee_point_y.num, p->knee_point_y.den,
98 3 p->num_bezier_curve_anchors);
99
2/2
✓ Branch 0 taken 15 times.
✓ Branch 1 taken 3 times.
18 for (int i = 0; i < p->num_bezier_curve_anchors; i++)
100 15 printf(" %d/%d", p->bezier_curve_anchors[i].num,
101 15 p->bezier_curve_anchors[i].den);
102 }
103 3 printf("\n color_saturation_mapping_flag=%u",
104 3 p->color_saturation_mapping_flag);
105
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 1 times.
3 if (p->color_saturation_mapping_flag)
106 2 printf(" weight=%d/%d", p->color_saturation_weight.num,
107 2 p->color_saturation_weight.den);
108 3 printf("\n");
109 3 }
110
111 2 static void print_hdr_plus(const char *label, const AVDynamicHDRPlus *s)
112 {
113 2 printf("%s:\n", label);
114 2 printf(" application_version=%u num_windows=%u"
115 " targeted_system_display_maximum_luminance=%d/%d\n",
116 2 s->application_version, s->num_windows,
117 2 s->targeted_system_display_maximum_luminance.num,
118 2 s->targeted_system_display_maximum_luminance.den);
119 2 printf(" targeted_system_display_actual_peak_luminance_flag=%u\n",
120 2 s->targeted_system_display_actual_peak_luminance_flag);
121
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 1 times.
2 if (s->targeted_system_display_actual_peak_luminance_flag)
122 1 print_peak_matrix("targeted_peak",
123 1 s->num_rows_targeted_system_display_actual_peak_luminance,
124 1 s->num_cols_targeted_system_display_actual_peak_luminance,
125 1 s->targeted_system_display_actual_peak_luminance);
126 2 printf(" mastering_display_actual_peak_luminance_flag=%u\n",
127 2 s->mastering_display_actual_peak_luminance_flag);
128
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 1 times.
2 if (s->mastering_display_actual_peak_luminance_flag)
129 1 print_peak_matrix("mastering_peak",
130 1 s->num_rows_mastering_display_actual_peak_luminance,
131 1 s->num_cols_mastering_display_actual_peak_luminance,
132 1 s->mastering_display_actual_peak_luminance);
133
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 2 times.
5 for (int w = 0; w < s->num_windows; w++)
134 3 print_window(w, &s->params[w], w > 0);
135 2 }
136
137 4 static void print_app5(const char *label, const AVDynamicHDRSmpte2094App5 *s)
138 {
139 4 printf("%s:\n", label);
140 4 printf(" application_version=%u minimum_application_version=%u\n",
141 4 s->application_version, s->minimum_application_version);
142 4 printf(" has_custom_hdr_reference_white_flag=%u",
143 4 s->has_custom_hdr_reference_white_flag);
144
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 3 times.
4 if (s->has_custom_hdr_reference_white_flag)
145 1 printf(" hdr_reference_white=0x%04x", s->hdr_reference_white);
146 4 printf("\n has_adaptive_tone_map_flag=%u\n",
147 4 s->has_adaptive_tone_map_flag);
148
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 3 times.
4 if (!s->has_adaptive_tone_map_flag)
149 1 return;
150
151 3 printf(" baseline_hdr_headroom=0x%04x"
152 " use_reference_white_tone_mapping_flag=%u\n",
153 3 s->baseline_hdr_headroom, s->use_reference_white_tone_mapping_flag);
154
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 2 times.
3 if (s->use_reference_white_tone_mapping_flag)
155 1 return;
156
157 2 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 2 s->num_alternate_images,
161 2 s->gain_application_space_chromaticities_flag,
162 2 s->has_common_component_mix_params_flag,
163 2 s->has_common_curve_params_flag);
164
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 1 times.
2 if (s->gain_application_space_chromaticities_flag == 3) {
165 1 printf(" chromaticities:");
166
2/2
✓ Branch 0 taken 8 times.
✓ Branch 1 taken 1 times.
9 for (int r = 0; r < 8; r++)
167 8 printf(" 0x%04x", s->gain_application_space_chromaticities[r]);
168 1 printf("\n");
169 }
170
171
2/2
✓ Branch 0 taken 5 times.
✓ Branch 1 taken 2 times.
7 for (int a = 0; a < s->num_alternate_images; a++) {
172 5 int n = s->gain_curve_num_control_points_minus_1[a] + 1;
173 5 printf(" alternate %d: hdr_headroom=0x%04x component_mixing_type=%u\n",
174 5 a, s->alternate_hdr_headrooms[a], s->component_mixing_type[a]);
175
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 3 times.
5 if (s->component_mixing_type[a] == 3) {
176 2 printf(" mixing flag:coefficient:");
177
2/2
✓ Branch 0 taken 12 times.
✓ Branch 1 taken 2 times.
14 for (int k = 0; k < 6; k++)
178 12 printf(" %u:0x%04x",
179 12 s->has_component_mixing_coefficient_flag[a][k],
180 12 s->component_mixing_coefficient[a][k]);
181 2 printf("\n");
182 }
183 5 printf(" curve: num_control_points=%d use_pchip_slope=%u\n", n,
184 5 s->gain_curve_use_pchip_slope_flag[a]);
185 5 printf(" x:");
186
2/2
✓ Branch 0 taken 13 times.
✓ Branch 1 taken 5 times.
18 for (int c = 0; c < n; c++)
187 13 printf(" 0x%04x", s->gain_curve_control_points_x[a][c]);
188 5 printf("\n y:");
189
2/2
✓ Branch 0 taken 13 times.
✓ Branch 1 taken 5 times.
18 for (int c = 0; c < n; c++)
190 13 printf(" 0x%04x", s->gain_curve_control_points_y[a][c]);
191 5 printf("\n");
192 /* theta is only serialized when the pchip slope is not used. */
193
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 3 times.
5 if (!s->gain_curve_use_pchip_slope_flag[a]) {
194 2 printf(" theta:");
195
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 2 times.
6 for (int c = 0; c < n; c++)
196 4 printf(" 0x%04x", s->gain_curve_control_points_theta[a][c]);
197 2 printf("\n");
198 }
199 }
200 }
201
202 /*
203 * Round-trip helpers: serialize, then parse back into a zeroed struct.
204 */
205
206 3 static int hdr_plus_round_trip(const AVDynamicHDRPlus *in, AVDynamicHDRPlus *out)
207 {
208 3 uint8_t *buf = NULL;
209 3 size_t size = 0;
210 int ret;
211
212 3 ret = av_dynamic_hdr_plus_to_t35(in, &buf, &size);
213
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
3 if (ret < 0)
214 ✗ return ret;
215
216 3 memset(out, 0, sizeof(*out));
217 3 ret = av_dynamic_hdr_plus_from_t35(out, buf, size);
218 3 av_free(buf);
219 3 return ret;
220 }
221
222 4 static int app5_round_trip(const AVDynamicHDRSmpte2094App5 *in,
223 AVDynamicHDRSmpte2094App5 *out)
224 {
225 4 uint8_t *buf = NULL;
226 4 size_t size = 0;
227 int ret;
228
229 4 ret = av_dynamic_hdr_smpte2094_app5_to_t35(in, &buf, &size);
230
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 4 times.
4 if (ret < 0)
231 ✗ return ret;
232
233 4 memset(out, 0, sizeof(*out));
234 4 ret = av_dynamic_hdr_smpte2094_app5_from_t35(out, buf, size);
235 4 av_free(buf);
236 4 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. */
254 1 static void fill_hdr_plus_conforming(AVDynamicHDRPlus *s)
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 1 memset(s, 0, sizeof(*s));
262
263 1 s->application_version = 1;
264 1 s->num_windows = 1;
265 1 s->targeted_system_display_maximum_luminance =
266 (AVRational){ 400, LUMINANCE_DEN };
267 1 s->targeted_system_display_actual_peak_luminance_flag = 0;
268 1 s->mastering_display_actual_peak_luminance_flag = 0;
269
270 1 s->params[0].maxscl[0] = (AVRational){ 50000, RGB_DEN };
271 1 s->params[0].maxscl[1] = (AVRational){ 60000, RGB_DEN };
272 1 s->params[0].maxscl[2] = (AVRational){ 99000, RGB_DEN };
273 1 s->params[0].average_maxrgb = (AVRational){ 40000, RGB_DEN };
274
275 1 s->params[0].num_distribution_maxrgb_percentiles = 9;
276
2/2
✓ Branch 0 taken 9 times.
✓ Branch 1 taken 1 times.
10 for (int i = 0; i < 9; i++) {
277 9 s->params[0].distribution_maxrgb[i].percentage = percentages[i];
278 9 s->params[0].distribution_maxrgb[i].percentile =
279 9 (AVRational){ percentiles[i], RGB_DEN };
280 }
281 1 s->params[0].fraction_bright_pixels =
282 (AVRational){ 250, FRACTION_PIXEL_DEN };
283
284 1 s->params[0].tone_mapping_flag = 1;
285 1 s->params[0].knee_point_x = (AVRational){ 512, KNEE_POINT_DEN };
286 1 s->params[0].knee_point_y = (AVRational){ 1024, KNEE_POINT_DEN };
287 1 s->params[0].num_bezier_curve_anchors = 9;
288
2/2
✓ Branch 0 taken 9 times.
✓ Branch 1 taken 1 times.
10 for (int i = 0; i < 9; i++)
289 9 s->params[0].bezier_curve_anchors[i] =
290 9 (AVRational){ 100 * (i + 1), BEZIER_ANCHOR_DEN };
291
292 1 s->params[0].color_saturation_mapping_flag = 0;
293 1 }
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. */
303 1 static void fill_hdr_plus_synthetic(AVDynamicHDRPlus *s)
304 {
305 1 memset(s, 0, sizeof(*s));
306
307 1 s->application_version = 1;
308 1 s->num_windows = 2;
309 1 s->targeted_system_display_maximum_luminance =
310 (AVRational){ 10000, LUMINANCE_DEN };
311
312 1 s->params[1].window_upper_left_corner_x = (AVRational){ 100, 1 };
313 1 s->params[1].window_upper_left_corner_y = (AVRational){ 120, 1 };
314 1 s->params[1].window_lower_right_corner_x = (AVRational){ 500, 1 };
315 1 s->params[1].window_lower_right_corner_y = (AVRational){ 520, 1 };
316 1 s->params[1].center_of_ellipse_x = 300;
317 1 s->params[1].center_of_ellipse_y = 320;
318 1 s->params[1].rotation_angle = 45;
319 1 s->params[1].semimajor_axis_internal_ellipse = 50;
320 1 s->params[1].semimajor_axis_external_ellipse = 100;
321 1 s->params[1].semiminor_axis_external_ellipse = 80;
322 1 s->params[1].overlap_process_option = 1;
323
324 1 s->params[0].maxscl[0] = (AVRational){ 50000, RGB_DEN };
325 1 s->params[0].maxscl[1] = (AVRational){ 60000, RGB_DEN };
326 1 s->params[0].maxscl[2] = (AVRational){ 70000, RGB_DEN };
327 1 s->params[0].average_maxrgb = (AVRational){ 40000, RGB_DEN };
328 1 s->params[0].num_distribution_maxrgb_percentiles = 2;
329 1 s->params[0].distribution_maxrgb[0].percentage = 50;
330 1 s->params[0].distribution_maxrgb[0].percentile =
331 (AVRational){ 30000, RGB_DEN };
332 1 s->params[0].distribution_maxrgb[1].percentage = 99;
333 1 s->params[0].distribution_maxrgb[1].percentile =
334 (AVRational){ 90000, RGB_DEN };
335 1 s->params[0].fraction_bright_pixels =
336 (AVRational){ 250, FRACTION_PIXEL_DEN };
337
338 1 s->params[1].maxscl[0] = (AVRational){ 20000, RGB_DEN };
339 1 s->params[1].maxscl[1] = (AVRational){ 30000, RGB_DEN };
340 1 s->params[1].maxscl[2] = (AVRational){ 40000, RGB_DEN };
341 1 s->params[1].average_maxrgb = (AVRational){ 25000, RGB_DEN };
342 1 s->params[1].num_distribution_maxrgb_percentiles = 1;
343 1 s->params[1].distribution_maxrgb[0].percentage = 25;
344 1 s->params[1].distribution_maxrgb[0].percentile =
345 (AVRational){ 12500, RGB_DEN };
346 1 s->params[1].fraction_bright_pixels =
347 (AVRational){ 100, FRACTION_PIXEL_DEN };
348
349 1 s->targeted_system_display_actual_peak_luminance_flag = 1;
350 1 s->num_rows_targeted_system_display_actual_peak_luminance = 2;
351 1 s->num_cols_targeted_system_display_actual_peak_luminance = 3;
352
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 1 times.
3 for (int i = 0; i < 2; i++)
353
2/2
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 2 times.
8 for (int j = 0; j < 3; j++)
354 6 s->targeted_system_display_actual_peak_luminance[i][j] =
355 6 (AVRational){ i * 3 + j, PEAK_LUMINANCE_DEN };
356
357 1 s->mastering_display_actual_peak_luminance_flag = 1;
358 1 s->num_rows_mastering_display_actual_peak_luminance = 3;
359 1 s->num_cols_mastering_display_actual_peak_luminance = 2;
360
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 1 times.
4 for (int i = 0; i < 3; i++)
361
2/2
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 3 times.
9 for (int j = 0; j < 2; j++)
362 6 s->mastering_display_actual_peak_luminance[i][j] =
363 6 (AVRational){ 15 - (i * 2 + j), PEAK_LUMINANCE_DEN };
364
365
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 1 times.
3 for (int w = 0; w < 2; w++) {
366 2 s->params[w].tone_mapping_flag = 1;
367 2 s->params[w].knee_point_x = (AVRational){ 500 + w, KNEE_POINT_DEN };
368 2 s->params[w].knee_point_y = (AVRational){ 800 + w, KNEE_POINT_DEN };
369 2 s->params[w].num_bezier_curve_anchors = 3;
370 2 s->params[w].bezier_curve_anchors[0] =
371 2 (AVRational){ 100 + w, BEZIER_ANCHOR_DEN };
372 2 s->params[w].bezier_curve_anchors[1] =
373 2 (AVRational){ 500 + w, BEZIER_ANCHOR_DEN };
374 2 s->params[w].bezier_curve_anchors[2] =
375 2 (AVRational){ 900 + w, BEZIER_ANCHOR_DEN };
376 2 s->params[w].color_saturation_mapping_flag = 1;
377 2 s->params[w].color_saturation_weight =
378 2 (AVRational){ 8 + w, SATURATION_DEN };
379 }
380 1 }
381
382 1 static void test_hdr_plus(void)
383 {
384 AVDynamicHDRPlus *hdr, *hdr_rt;
385 AVFrame *frame;
386 1 size_t size = 0, required = 0;
387 1 uint8_t *buf = NULL;
388 int ret;
389
390 1 printf("=== AVDynamicHDRPlus ===\n");
391
392 1 printf("Testing av_dynamic_hdr_plus_alloc()\n");
393 1 hdr = av_dynamic_hdr_plus_alloc(&size);
394
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 printf("alloc: %s size>0=%s\n", hdr ? "OK" : "FAIL",
395
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 size > 0 ? "yes" : "no");
396 1 av_freep(&hdr);
397
398 1 hdr = av_dynamic_hdr_plus_alloc(NULL);
399
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 printf("alloc (no size): %s\n", hdr ? "OK" : "FAIL");
400 1 av_freep(&hdr);
401
402 1 printf("\nTesting av_dynamic_hdr_plus_create_side_data()\n");
403 1 frame = av_frame_alloc();
404
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 if (frame) {
405 1 hdr = av_dynamic_hdr_plus_create_side_data(frame);
406
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 printf("create_side_data: %s\n", hdr ? "OK" : "FAIL");
407 1 av_frame_free(&frame);
408 }
409
410 1 hdr = av_dynamic_hdr_plus_alloc(NULL);
411 1 hdr_rt = av_dynamic_hdr_plus_alloc(NULL);
412
2/4
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 1 times.
1 if (!hdr || !hdr_rt) {
413 ✗ av_freep(&hdr);
414 ✗ av_freep(&hdr_rt);
415 ✗ return;
416 }
417 1 fill_hdr_plus_conforming(hdr);
418
419 /* Size-query mode: data=NULL, size receives the required byte count. */
420 1 printf("\nTesting av_dynamic_hdr_plus_to_t35() size query\n");
421 1 required = 0;
422 1 ret = av_dynamic_hdr_plus_to_t35(hdr, NULL, &required);
423 1 printf("size query: ret=%d required>0=%s\n", ret,
424
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 required > 0 ? "yes" : "no");
425
426 /* Allocation mode: *data=NULL, the function allocates the buffer. */
427 1 printf("\nTesting av_dynamic_hdr_plus_to_t35() allocation\n");
428 1 buf = NULL;
429 1 size = 0;
430 1 ret = av_dynamic_hdr_plus_to_t35(hdr, &buf, &size);
431 1 printf("alloc mode: ret=%d size_match=%s\n", ret,
432
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 size == required ? "yes" : "no");
433 1 av_freep(&buf);
434
435 /* Existing-buffer mode: a caller-owned buffer is filled in place. */
436 1 printf("\nTesting av_dynamic_hdr_plus_to_t35() existing buffer\n");
437 1 buf = av_malloc(required);
438
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 if (buf) {
439 1 uint8_t *orig = buf;
440 1 size = required;
441 1 ret = av_dynamic_hdr_plus_to_t35(hdr, &buf, &size);
442 2 printf("existing buf: ret=%d same_pointer=%s size_match=%s\n", ret,
443
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 buf == orig ? "yes" : "no",
444
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 size == required ? "yes" : "no");
445 1 av_freep(&buf);
446 }
447
448 /* Buffer-too-small: caller-owned buffer shorter than required. */
449 1 printf("\nTesting av_dynamic_hdr_plus_to_t35() buffer too small\n");
450
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 if (required > 1) {
451 1 buf = av_malloc(required - 1);
452
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 if (buf) {
453 1 size = required - 1;
454 1 ret = av_dynamic_hdr_plus_to_t35(hdr, &buf, &size);
455
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 printf("too small: ret==AVERROR_BUFFER_TOO_SMALL=%s\n",
456 ret == AVERROR_BUFFER_TOO_SMALL ? "yes" : "no");
457 1 av_freep(&buf);
458 }
459 }
460
461 1 printf("\nTesting round trip, conforming ST 2094-40 Version 1 payload\n");
462 1 ret = hdr_plus_round_trip(hdr, hdr_rt);
463 1 printf("round trip: ret=%d\n", ret);
464
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 if (ret >= 0)
465 1 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 1 printf("\nTesting that to_t35() overrides application_version\n");
470 1 hdr->application_version = 7;
471 1 ret = hdr_plus_round_trip(hdr, hdr_rt);
472 1 printf("input=7 ret=%d decoded application_version=%u\n", ret,
473 1 hdr_rt->application_version);
474 1 hdr->application_version = 1;
475
476 1 printf("\nTesting round trip, synthetic payload (branch coverage only)\n");
477 1 fill_hdr_plus_synthetic(hdr);
478 1 ret = hdr_plus_round_trip(hdr, hdr_rt);
479 1 printf("round trip: ret=%d\n", ret);
480
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 if (ret >= 0)
481 1 print_hdr_plus("decoded", hdr_rt);
482
483 1 printf("\nTesting error paths\n");
484 1 buf = NULL;
485 1 size = 0;
486 1 ret = av_dynamic_hdr_plus_to_t35(NULL, &buf, &size);
487
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 printf("to_t35 NULL s: ret==AVERROR(EINVAL)=%s\n",
488 ret == AVERROR(EINVAL) ? "yes" : "no");
489
490 1 ret = av_dynamic_hdr_plus_to_t35(hdr, NULL, NULL);
491
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 printf("to_t35 no data no size: ret==AVERROR(EINVAL)=%s\n",
492 ret == AVERROR(EINVAL) ? "yes" : "no");
493
494 1 ret = av_dynamic_hdr_plus_from_t35(NULL, (const uint8_t *)"", 0);
495
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 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 1 size_t big = AV_HDR_PLUS_MAX_PAYLOAD_SIZE + 1;
501 1 uint8_t *oversized = av_mallocz(big);
502
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 if (oversized) {
503 1 ret = av_dynamic_hdr_plus_from_t35(hdr_rt, oversized, big);
504
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 printf("from_t35 oversized: ret==AVERROR(EINVAL)=%s\n",
505 ret == AVERROR(EINVAL) ? "yes" : "no");
506 1 av_free(oversized);
507 }
508 }
509
510 1 av_freep(&hdr);
511 1 av_freep(&hdr_rt);
512
513 1 printf("\nTesting OOM path\n");
514 1 av_max_alloc(1);
515 1 hdr = av_dynamic_hdr_plus_alloc(&size);
516
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 printf("alloc OOM: %s\n", hdr ? "FAIL" : "OK");
517 1 av_max_alloc(INT_MAX);
518 1 av_freep(&hdr);
519 }
520
521 /* Table C.2 early-out: no adaptive tone map, custom reference white only. */
522 1 static void fill_app5_minimal(AVDynamicHDRSmpte2094App5 *s)
523 {
524 1 memset(s, 0, sizeof(*s));
525 1 s->application_version = 1;
526 1 s->minimum_application_version = 0;
527 1 s->has_custom_hdr_reference_white_flag = 1;
528 1 s->hdr_reference_white = 0x0203;
529 1 s->has_adaptive_tone_map_flag = 0;
530 1 }
531
532 /* Table C.3 early-out: reference-white tone mapping, no alternate images. */
533 1 static void fill_app5_reference_white(AVDynamicHDRSmpte2094App5 *s)
534 {
535 1 memset(s, 0, sizeof(*s));
536 1 s->application_version = 2;
537 1 s->has_adaptive_tone_map_flag = 1;
538 1 s->baseline_hdr_headroom = 0x0abc;
539 1 s->use_reference_white_tone_mapping_flag = 1;
540 1 }
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). */
545 1 static void fill_app5_per_image(AVDynamicHDRSmpte2094App5 *s)
546 {
547 1 memset(s, 0, sizeof(*s));
548 1 s->application_version = 1;
549 1 s->has_adaptive_tone_map_flag = 1;
550 1 s->baseline_hdr_headroom = 0x0abc;
551 1 s->num_alternate_images = 2;
552 1 s->gain_application_space_chromaticities_flag = 3;
553
2/2
✓ Branch 0 taken 8 times.
✓ Branch 1 taken 1 times.
9 for (int r = 0; r < 8; r++)
554 8 s->gain_application_space_chromaticities[r] = 100 * (r + 1);
555 1 s->has_common_component_mix_params_flag = 0;
556 1 s->has_common_curve_params_flag = 0;
557
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 1 times.
3 for (int a = 0; a < 2; a++) {
558 2 s->alternate_hdr_headrooms[a] = 0x0100 + a;
559 2 s->component_mixing_type[a] = 3;
560
2/2
✓ Branch 0 taken 12 times.
✓ Branch 1 taken 2 times.
14 for (int k = 0; k < 6; k++) {
561 12 s->has_component_mixing_coefficient_flag[a][k] = k & 1;
562 12 s->component_mixing_coefficient[a][k] = 0x1000 + 0x10 * a + k;
563 }
564 2 s->gain_curve_num_control_points_minus_1[a] = 1;
565 2 s->gain_curve_use_pchip_slope_flag[a] = 0;
566
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 2 times.
6 for (int c = 0; c < 2; c++) {
567 4 s->gain_curve_control_points_x[a][c] = 0x2000 + 0x10 * a + c;
568 4 s->gain_curve_control_points_y[a][c] = 0x3000 + 0x10 * a + c;
569 4 s->gain_curve_control_points_theta[a][c] = 0x4000 + 0x10 * a + c;
570 }
571 }
572 1 }
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. */
581 1 static void fill_app5_common(AVDynamicHDRSmpte2094App5 *s)
582 {
583 1 memset(s, 0, sizeof(*s));
584 1 s->application_version = 1;
585 1 s->has_adaptive_tone_map_flag = 1;
586 1 s->baseline_hdr_headroom = 0x0def;
587 1 s->num_alternate_images = 3;
588 1 s->gain_application_space_chromaticities_flag = 0;
589 1 s->has_common_component_mix_params_flag = 1;
590 1 s->has_common_curve_params_flag = 1;
591
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 1 times.
4 for (int a = 0; a < 3; a++) {
592 3 s->alternate_hdr_headrooms[a] = 0x0200 + a;
593 3 s->component_mixing_type[a] = 1;
594 3 s->gain_curve_num_control_points_minus_1[a] = 2;
595 3 s->gain_curve_use_pchip_slope_flag[a] = 1;
596
2/2
✓ Branch 0 taken 9 times.
✓ Branch 1 taken 3 times.
12 for (int c = 0; c < 3; c++) {
597 9 s->gain_curve_control_points_x[a][c] = 0x5000 + c;
598 9 s->gain_curve_control_points_y[a][c] = 0x6000 + 0x10 * a + c;
599 }
600 }
601 1 }
602
603 1 static void test_app5(void)
604 {
605 AVDynamicHDRSmpte2094App5 *app5, *app5_rt;
606 AVFrame *frame;
607 1 size_t size = 0, required = 0;
608 1 uint8_t *buf = NULL;
609 int ret;
610
611 1 printf("\n=== AVDynamicHDRSmpte2094App5 ===\n");
612 1 printf("Testing av_dynamic_hdr_smpte2094_app5_alloc()\n");
613 1 app5 = av_dynamic_hdr_smpte2094_app5_alloc(&size);
614
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 printf("alloc: %s size>0=%s\n", app5 ? "OK" : "FAIL",
615
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 size > 0 ? "yes" : "no");
616 1 av_freep(&app5);
617
618 1 printf("\nTesting av_dynamic_hdr_smpte2094_app5_create_side_data()\n");
619 1 frame = av_frame_alloc();
620
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 if (frame) {
621 1 app5 = av_dynamic_hdr_smpte2094_app5_create_side_data(frame);
622
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 printf("create_side_data: %s\n", app5 ? "OK" : "FAIL");
623 1 av_frame_free(&frame);
624 }
625
626 1 app5 = av_dynamic_hdr_smpte2094_app5_alloc(NULL);
627 1 app5_rt = av_dynamic_hdr_smpte2094_app5_alloc(NULL);
628
2/4
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 1 times.
1 if (!app5 || !app5_rt) {
629 ✗ av_freep(&app5);
630 ✗ av_freep(&app5_rt);
631 ✗ return;
632 }
633 1 fill_app5_minimal(app5);
634
635 1 printf("\nTesting av_dynamic_hdr_smpte2094_app5_to_t35()\n");
636 1 required = 0;
637 1 ret = av_dynamic_hdr_smpte2094_app5_to_t35(app5, NULL, &required);
638 1 printf("size query: ret=%d required>0=%s\n", ret,
639
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 required > 0 ? "yes" : "no");
640
641 1 buf = NULL;
642 1 size = 0;
643 1 ret = av_dynamic_hdr_smpte2094_app5_to_t35(app5, &buf, &size);
644 1 printf("alloc mode: ret=%d size_match=%s\n", ret,
645
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 size == required ? "yes" : "no");
646 1 av_freep(&buf);
647
648 1 printf("\nTesting round trip, no adaptive tone map\n");
649 1 ret = app5_round_trip(app5, app5_rt);
650 1 printf("round trip: ret=%d\n", ret);
651
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 if (ret >= 0)
652 1 print_app5("decoded", app5_rt);
653
654 1 printf("\nTesting round trip, reference-white tone mapping\n");
655 1 fill_app5_reference_white(app5);
656 1 ret = app5_round_trip(app5, app5_rt);
657 1 printf("round trip: ret=%d\n", ret);
658
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 if (ret >= 0)
659 1 print_app5("decoded", app5_rt);
660
661 1 printf("\nTesting round trip, per-image mixing and curve parameters\n");
662 1 fill_app5_per_image(app5);
663 1 ret = app5_round_trip(app5, app5_rt);
664 1 printf("round trip: ret=%d\n", ret);
665
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 if (ret >= 0)
666 1 print_app5("decoded", app5_rt);
667
668 1 printf("\nTesting round trip, common mixing and curve parameters\n");
669 1 fill_app5_common(app5);
670 1 ret = app5_round_trip(app5, app5_rt);
671 1 printf("round trip: ret=%d\n", ret);
672
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 if (ret >= 0)
673 1 print_app5("decoded", app5_rt);
674
675 1 printf("\nTesting error paths\n");
676 1 buf = NULL;
677 1 size = 0;
678 1 ret = av_dynamic_hdr_smpte2094_app5_to_t35(NULL, &buf, &size);
679
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 printf("to_t35 NULL s: ret==AVERROR(EINVAL)=%s\n",
680 ret == AVERROR(EINVAL) ? "yes" : "no");
681
682 1 ret = av_dynamic_hdr_smpte2094_app5_from_t35(NULL, (const uint8_t *)"", 0);
683
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 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 1 app5->application_version = 8;
688 1 ret = av_dynamic_hdr_smpte2094_app5_to_t35(app5, NULL, &size);
689
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 printf("to_t35 invalid application_version: ret==AVERROR_INVALIDDATA=%s\n",
690 ret == AVERROR_INVALIDDATA ? "yes" : "no");
691 1 app5->application_version = 1;
692
693 /* minimum_application_version above the documented maximum. */
694 1 app5->minimum_application_version = 3;
695 1 ret = av_dynamic_hdr_smpte2094_app5_to_t35(app5, NULL, &size);
696
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 printf("to_t35 invalid minimum_application_version:"
697 " ret==AVERROR_INVALIDDATA=%s\n",
698 ret == AVERROR_INVALIDDATA ? "yes" : "no");
699 1 app5->minimum_application_version = 0;
700
701 /* More alternate images than the 3-bit count admits. */
702 1 app5->num_alternate_images = 5;
703 1 ret = av_dynamic_hdr_smpte2094_app5_to_t35(app5, NULL, &size);
704
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 printf("to_t35 too many alternate images: ret==AVERROR_INVALIDDATA=%s\n",
705 ret == AVERROR_INVALIDDATA ? "yes" : "no");
706
707 1 av_freep(&app5);
708 1 av_freep(&app5_rt);
709
710 1 printf("\nTesting OOM path\n");
711 1 av_max_alloc(1);
712 1 app5 = av_dynamic_hdr_smpte2094_app5_alloc(&size);
713
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 printf("alloc OOM: %s\n", app5 ? "FAIL" : "OK");
714 1 av_max_alloc(INT_MAX);
715 1 av_freep(&app5);
716 }
717
718 1 int main(void)
719 {
720 1 test_hdr_plus();
721 1 test_app5();
722 1 return 0;
723 }
724