PR #23422 opened by Marcos Ashton (MarcosAsh)
URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/23422
Patch URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/23422.patch
## Summary
- pixdesc -- av_get_pix_fmt / av_get_pix_fmt_name round-trip across all pixel
formats, av_color_{range,primaries,transfer,space}_name,
av_chroma_location_name and av_alpha_mode_name round-trips through their
from_name() counterparts using a name-equivalence check,
av_chroma_location_enum_to_pos / pos_to_enum round-trip, and
av_get_padded_bits_per_pixel / av_pix_fmt_count_planes for every format.
Coverage for libavutil/pixdesc.c: 83.63% -> 90.03%
Note: the reference encodes the fixed from_name() behavior, so this depends
on #23421 being merged first.
- downmix_info -- av_downmix_info_update_side_data() first-call allocation
with zero-initialized defaults, field write/read-back, second-call reuse (same
pointer, preserved values), and the OOM path via av_max_alloc. On a full FATE
run the file is already covered by the AC-3 decoder sample tests. This
exercises the API directly and keeps it covered on builds running without
FATE_SAMPLES.
Coverage for libavutil/downmix_info.c: 100.00% -> 100.00%
- hdr_dynamic_metadata -- av_dynamic_hdr_plus_to_t35 in all three documented
modes including AVERROR_BUFFER_TOO_SMALL, av_dynamic_hdr_plus_from_t35 as a
round-trip inverse for minimal and two-window rich payloads (peak luminance
matrices, percentile distribution, tone mapping, color saturation), NULL and
oversized error paths, and the SMPTE 2094-50 App5 alloc / create_side_data /
to_t35 / from_t35 round trip. OOM paths via av_max_alloc.
Coverage for libavutil/hdr_dynamic_metadata.c: 31.02% -> 91.56% (baseline
comes from fate-hevc-hdr10-plus-metadata, fate-png-mdcv and the Matroska/WebM
HDR10+ remux tests)
- side_data -- av_frame_side_data_desc/name on valid and invalid types, get
and its _c variant, remove (including MULTI types with duplicates),
remove_by_props, clone with and without REPLACE/UNIQUE including the EEXIST and
EINVAL paths, and add in move mode, with NEW_REF and with REPLACE. Complements
fate-side_data_array, which only covers new/name/free.
Coverage for libavutil/side_data.c: 72.14% -> 86.43%
>From 30329a1f8318b5e413771e8d3f08bd39b59c8dc7 Mon Sep 17 00:00:00 2001
From: marcos ashton <[email protected]>
Date: Fri, 8 May 2026 15:47:00 +0100
Subject: [PATCH 1/4] tests/fate/libavutil: add FATE test for pixdesc
Test av_get_pix_fmt / av_get_pix_fmt_name round-trip across all
pixel formats, av_color_{range,primaries,transfer,space}_name,
av_chroma_location_name and av_alpha_mode_name round-trips
through their from_name() counterparts using a name-equivalence
check (two enum values may legitimately share the same name
string), av_chroma_location_enum_to_pos and pos_to_enum
round-trip across all valid chroma locations, and iterate every
pixel format printing av_get_padded_bits_per_pixel and
av_pix_fmt_count_planes.
Coverage for libavutil/pixdesc.c: 83.63% -> 90.03%
Numbers measured against a full FATE run. Remaining uncovered
lines are av_get_pix_fmt_string, the av_get_pix_fmt_loss /
av_find_best_pix_fmt_of_2 public wrappers and a few loss
branches, the palette branches of av_read_image_line and
av_write_image_line, and the *_ext name lookups in
av_color_{primaries,transfer}_from_name.
Signed-off-by: marcos ashton <[email protected]>
---
.forgejo/CODEOWNERS | 2 +
libavutil/tests/pixdesc.c | 174 +++++++++++++++++++++--
tests/fate/libavutil.mak | 4 +
tests/ref/fate/pixdesc | 282 ++++++++++++++++++++++++++++++++++++++
4 files changed, 451 insertions(+), 11 deletions(-)
create mode 100644 tests/ref/fate/pixdesc
diff --git a/.forgejo/CODEOWNERS b/.forgejo/CODEOWNERS
index 3ada7b3e18..427cae7a1a 100644
--- a/.forgejo/CODEOWNERS
+++ b/.forgejo/CODEOWNERS
@@ -236,6 +236,7 @@ libavutil/tests/ambient_viewing_environment.* @MarcosAsh
libavutil/tests/buffer.* @MarcosAsh
libavutil/tests/csp.* @MarcosAsh
libavutil/tests/hdr_dynamic_vivid_metadata.* @MarcosAsh
+libavutil/tests/pixdesc.* @MarcosAsh
libavutil/tests/tdrdi.* @MarcosAsh
libavutil/tests/timestamp.* @MarcosAsh
tests/ref/.*drawvg.* @ayosec
@@ -243,6 +244,7 @@ tests/ref/fate/ambient_viewing_environment @MarcosAsh
tests/ref/fate/buffer @MarcosAsh
tests/ref/fate/csp @MarcosAsh
tests/ref/fate/hdr_dynamic_vivid_metadata @MarcosAsh
+tests/ref/fate/pixdesc @MarcosAsh
tests/ref/fate/sub-mcc.* @programmerjake
tests/ref/fate/tdrdi @MarcosAsh
tests/ref/fate/timestamp @MarcosAsh
diff --git a/libavutil/tests/pixdesc.c b/libavutil/tests/pixdesc.c
index b13aba598b..ed8e74f204 100644
--- a/libavutil/tests/pixdesc.c
+++ b/libavutil/tests/pixdesc.c
@@ -19,25 +19,177 @@
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
-#include "libavutil/log.h"
-#include "libavutil/pixdesc.c"
+#include <stdio.h>
+#include <string.h>
-int main(void){
- int i;
- int err=0;
+#include "libavutil/macros.h"
+#include "libavutil/pixdesc.h"
+#include "libavutil/pixfmt.h"
+
+/*
+ * from_name() may legitimately map to a different index when two enum
+ * values share the same name string (e.g. AVCOL_PRI_RESERVED0 and
+ * AVCOL_PRI_RESERVED both use "reserved"). Accept that as long as
+ * name(back) equals name(i).
+ */
+static int check_name_equivalence(const char *name, const char *back_name)
+{
+ return back_name && !strcmp(name, back_name);
+}
+
+static int test_pix_fmt_round_trip(void)
+{
+ int fail = 0;
+ for (int i = 0; i < AV_PIX_FMT_NB; i++) {
+ const char *name = av_get_pix_fmt_name(i);
+ if (!name)
+ continue;
+ if (av_get_pix_fmt(name) != i)
+ fail++;
+ }
+ return fail;
+}
+
+static int test_color_range_round_trip(void)
+{
+ int fail = 0;
+ for (int i = 0; i < AVCOL_RANGE_NB; i++) {
+ const char *name = av_color_range_name(i);
+ if (!name)
+ continue;
+ int back = av_color_range_from_name(name);
+ if (back < 0 || !check_name_equivalence(name,
av_color_range_name(back)))
+ fail++;
+ }
+ return fail;
+}
+
+static int test_color_primaries_round_trip(void)
+{
+ int fail = 0;
+ for (int i = 0; i < AVCOL_PRI_NB; i++) {
+ const char *name = av_color_primaries_name(i);
+ if (!name)
+ continue;
+ int back = av_color_primaries_from_name(name);
+ if (back < 0 || !check_name_equivalence(name,
av_color_primaries_name(back)))
+ fail++;
+ }
+ return fail;
+}
+
+static int test_color_transfer_round_trip(void)
+{
+ int fail = 0;
+ for (int i = 0; i < AVCOL_TRC_NB; i++) {
+ const char *name = av_color_transfer_name(i);
+ if (!name)
+ continue;
+ int back = av_color_transfer_from_name(name);
+ if (back < 0 || !check_name_equivalence(name,
av_color_transfer_name(back)))
+ fail++;
+ }
+ return fail;
+}
+
+static int test_color_space_round_trip(void)
+{
+ int fail = 0;
+ for (int i = 0; i < AVCOL_SPC_NB; i++) {
+ const char *name = av_color_space_name(i);
+ if (!name)
+ continue;
+ int back = av_color_space_from_name(name);
+ if (back < 0 || !check_name_equivalence(name,
av_color_space_name(back)))
+ fail++;
+ }
+ return fail;
+}
+
+static int test_chroma_location_round_trip(void)
+{
+ int fail = 0;
+ for (int i = 0; i < AVCHROMA_LOC_NB; i++) {
+ const char *name = av_chroma_location_name(i);
+ if (!name)
+ continue;
+ int back = av_chroma_location_from_name(name);
+ if (back < 0 || !check_name_equivalence(name,
av_chroma_location_name(back)))
+ fail++;
+ }
+ return fail;
+}
+
+static int test_alpha_mode_round_trip(void)
+{
+ int fail = 0;
+ for (int i = 0; i < AVALPHA_MODE_NB; i++) {
+ const char *name = av_alpha_mode_name(i);
+ if (!name)
+ continue;
+ int back = av_alpha_mode_from_name(name);
+ if (back < 0 || !check_name_equivalence(name,
av_alpha_mode_name(back)))
+ fail++;
+ }
+ return fail;
+}
+
+static int test_chroma_location_pos_round_trip(void)
+{
+ int fail = 0;
+ for (int i = AVCHROMA_LOC_UNSPECIFIED + 1; i < AVCHROMA_LOC_NB; i++) {
+ int xpos, ypos;
+ if (av_chroma_location_enum_to_pos(&xpos, &ypos, i) < 0) {
+ fail++;
+ continue;
+ }
+ if (av_chroma_location_pos_to_enum(xpos, ypos) != i)
+ fail++;
+ }
+ return fail;
+}
+
+int main(void)
+{
int skip = 0;
- for (i=0; i<AV_PIX_FMT_NB*2; i++) {
+ printf("Testing av_get_pix_fmt() / av_get_pix_fmt_name() round-trip\n");
+ printf(" result: %s\n",
+ test_pix_fmt_round_trip() ? "FAIL" : "OK");
+
+ printf("\nTesting av_color_*_name() / av_color_*_from_name()
round-trips\n");
+ printf(" av_color_range: %s\n",
+ test_color_range_round_trip() ? "FAIL" : "OK");
+ printf(" av_color_primaries: %s\n",
+ test_color_primaries_round_trip() ? "FAIL" : "OK");
+ printf(" av_color_transfer: %s\n",
+ test_color_transfer_round_trip() ? "FAIL" : "OK");
+ printf(" av_color_space: %s\n",
+ test_color_space_round_trip() ? "FAIL" : "OK");
+ printf(" av_chroma_location: %s\n",
+ test_chroma_location_round_trip() ? "FAIL" : "OK");
+ printf(" av_alpha_mode: %s\n",
+ test_alpha_mode_round_trip() ? "FAIL" : "OK");
+
+ printf("\nTesting av_chroma_location_enum_to_pos / pos_to_enum
round-trip\n");
+ printf(" result: %s\n",
+ test_chroma_location_pos_round_trip() ? "FAIL" : "OK");
+
+ printf("\nTesting pixel format descriptors\n");
+ for (int i = 0; i < AV_PIX_FMT_NB * 2; i++) {
const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(i);
- if(!desc || !desc->name) {
- skip ++;
+ if (!desc || !desc->name) {
+ skip++;
continue;
}
if (skip) {
- av_log(NULL, AV_LOG_INFO, "%3d unused pixel format values\n",
skip);
+ printf(" %3d unused pixel format values\n", skip);
skip = 0;
}
- av_log(NULL, AV_LOG_INFO, "pix fmt %s avg_bpp:%d colortype:%d\n",
desc->name, av_get_padded_bits_per_pixel(desc), get_color_type(desc));
+ printf(" pix fmt %s avg_bpp:%d planes:%d\n", desc->name,
+ av_get_padded_bits_per_pixel(desc),
+ av_pix_fmt_count_planes(i));
}
- return err;
+
+ return 0;
}
diff --git a/tests/fate/libavutil.mak b/tests/fate/libavutil.mak
index a2ea57d939..eb116280a7 100644
--- a/tests/fate/libavutil.mak
+++ b/tests/fate/libavutil.mak
@@ -148,6 +148,10 @@ FATE_LIBAVUTIL += fate-parseutils
fate-parseutils: libavutil/tests/parseutils$(EXESUF)
fate-parseutils: CMD = run libavutil/tests/parseutils$(EXESUF)
+FATE_LIBAVUTIL += fate-pixdesc
+fate-pixdesc: libavutil/tests/pixdesc$(EXESUF)
+fate-pixdesc: CMD = run libavutil/tests/pixdesc$(EXESUF)
+
FATE_LIBAVUTIL-$(CONFIG_PIXELUTILS) += fate-pixelutils
fate-pixelutils: libavutil/tests/pixelutils$(EXESUF)
fate-pixelutils: CMD = run libavutil/tests/pixelutils$(EXESUF)
diff --git a/tests/ref/fate/pixdesc b/tests/ref/fate/pixdesc
new file mode 100644
index 0000000000..84696e6a1b
--- /dev/null
+++ b/tests/ref/fate/pixdesc
@@ -0,0 +1,282 @@
+Testing av_get_pix_fmt() / av_get_pix_fmt_name() round-trip
+ result: OK
+
+Testing av_color_*_name() / av_color_*_from_name() round-trips
+ av_color_range: OK
+ av_color_primaries: OK
+ av_color_transfer: OK
+ av_color_space: OK
+ av_chroma_location: OK
+ av_alpha_mode: OK
+
+Testing av_chroma_location_enum_to_pos / pos_to_enum round-trip
+ result: OK
+
+Testing pixel format descriptors
+ pix fmt yuv420p avg_bpp:12 planes:3
+ pix fmt yuyv422 avg_bpp:16 planes:1
+ pix fmt rgb24 avg_bpp:24 planes:1
+ pix fmt bgr24 avg_bpp:24 planes:1
+ pix fmt yuv422p avg_bpp:16 planes:3
+ pix fmt yuv444p avg_bpp:24 planes:3
+ pix fmt yuv410p avg_bpp:9 planes:3
+ pix fmt yuv411p avg_bpp:12 planes:3
+ pix fmt gray avg_bpp:8 planes:1
+ pix fmt monow avg_bpp:1 planes:1
+ pix fmt monob avg_bpp:1 planes:1
+ pix fmt pal8 avg_bpp:8 planes:1
+ pix fmt yuvj420p avg_bpp:12 planes:3
+ pix fmt yuvj422p avg_bpp:16 planes:3
+ pix fmt yuvj444p avg_bpp:24 planes:3
+ pix fmt uyvy422 avg_bpp:16 planes:1
+ pix fmt uyyvyy411 avg_bpp:12 planes:1
+ pix fmt bgr8 avg_bpp:8 planes:1
+ pix fmt bgr4 avg_bpp:4 planes:1
+ pix fmt bgr4_byte avg_bpp:8 planes:1
+ pix fmt rgb8 avg_bpp:8 planes:1
+ pix fmt rgb4 avg_bpp:4 planes:1
+ pix fmt rgb4_byte avg_bpp:8 planes:1
+ pix fmt nv12 avg_bpp:12 planes:2
+ pix fmt nv21 avg_bpp:12 planes:2
+ pix fmt argb avg_bpp:32 planes:1
+ pix fmt rgba avg_bpp:32 planes:1
+ pix fmt abgr avg_bpp:32 planes:1
+ pix fmt bgra avg_bpp:32 planes:1
+ pix fmt gray16be avg_bpp:16 planes:1
+ pix fmt gray16le avg_bpp:16 planes:1
+ pix fmt yuv440p avg_bpp:16 planes:3
+ pix fmt yuvj440p avg_bpp:16 planes:3
+ pix fmt yuva420p avg_bpp:20 planes:4
+ pix fmt rgb48be avg_bpp:48 planes:1
+ pix fmt rgb48le avg_bpp:48 planes:1
+ pix fmt rgb565be avg_bpp:16 planes:1
+ pix fmt rgb565le avg_bpp:16 planes:1
+ pix fmt rgb555be avg_bpp:16 planes:1
+ pix fmt rgb555le avg_bpp:16 planes:1
+ pix fmt bgr565be avg_bpp:16 planes:1
+ pix fmt bgr565le avg_bpp:16 planes:1
+ pix fmt bgr555be avg_bpp:16 planes:1
+ pix fmt bgr555le avg_bpp:16 planes:1
+ pix fmt vaapi avg_bpp:0 planes:0
+ pix fmt yuv420p16le avg_bpp:24 planes:3
+ pix fmt yuv420p16be avg_bpp:24 planes:3
+ pix fmt yuv422p16le avg_bpp:32 planes:3
+ pix fmt yuv422p16be avg_bpp:32 planes:3
+ pix fmt yuv444p16le avg_bpp:48 planes:3
+ pix fmt yuv444p16be avg_bpp:48 planes:3
+ pix fmt dxva2_vld avg_bpp:0 planes:0
+ pix fmt rgb444le avg_bpp:16 planes:1
+ pix fmt rgb444be avg_bpp:16 planes:1
+ pix fmt bgr444le avg_bpp:16 planes:1
+ pix fmt bgr444be avg_bpp:16 planes:1
+ pix fmt ya8 avg_bpp:16 planes:1
+ pix fmt bgr48be avg_bpp:48 planes:1
+ pix fmt bgr48le avg_bpp:48 planes:1
+ pix fmt yuv420p9be avg_bpp:24 planes:3
+ pix fmt yuv420p9le avg_bpp:24 planes:3
+ pix fmt yuv420p10be avg_bpp:24 planes:3
+ pix fmt yuv420p10le avg_bpp:24 planes:3
+ pix fmt yuv422p10be avg_bpp:32 planes:3
+ pix fmt yuv422p10le avg_bpp:32 planes:3
+ pix fmt yuv444p9be avg_bpp:48 planes:3
+ pix fmt yuv444p9le avg_bpp:48 planes:3
+ pix fmt yuv444p10be avg_bpp:48 planes:3
+ pix fmt yuv444p10le avg_bpp:48 planes:3
+ pix fmt yuv422p9be avg_bpp:32 planes:3
+ pix fmt yuv422p9le avg_bpp:32 planes:3
+ pix fmt gbrp avg_bpp:24 planes:3
+ pix fmt gbrp9be avg_bpp:48 planes:3
+ pix fmt gbrp9le avg_bpp:48 planes:3
+ pix fmt gbrp10be avg_bpp:48 planes:3
+ pix fmt gbrp10le avg_bpp:48 planes:3
+ pix fmt gbrp16be avg_bpp:48 planes:3
+ pix fmt gbrp16le avg_bpp:48 planes:3
+ pix fmt yuva422p avg_bpp:24 planes:4
+ pix fmt yuva444p avg_bpp:32 planes:4
+ pix fmt yuva420p9be avg_bpp:40 planes:4
+ pix fmt yuva420p9le avg_bpp:40 planes:4
+ pix fmt yuva422p9be avg_bpp:48 planes:4
+ pix fmt yuva422p9le avg_bpp:48 planes:4
+ pix fmt yuva444p9be avg_bpp:64 planes:4
+ pix fmt yuva444p9le avg_bpp:64 planes:4
+ pix fmt yuva420p10be avg_bpp:40 planes:4
+ pix fmt yuva420p10le avg_bpp:40 planes:4
+ pix fmt yuva422p10be avg_bpp:48 planes:4
+ pix fmt yuva422p10le avg_bpp:48 planes:4
+ pix fmt yuva444p10be avg_bpp:64 planes:4
+ pix fmt yuva444p10le avg_bpp:64 planes:4
+ pix fmt yuva420p16be avg_bpp:40 planes:4
+ pix fmt yuva420p16le avg_bpp:40 planes:4
+ pix fmt yuva422p16be avg_bpp:48 planes:4
+ pix fmt yuva422p16le avg_bpp:48 planes:4
+ pix fmt yuva444p16be avg_bpp:64 planes:4
+ pix fmt yuva444p16le avg_bpp:64 planes:4
+ pix fmt vdpau avg_bpp:0 planes:0
+ pix fmt xyz12le avg_bpp:48 planes:1
+ pix fmt xyz12be avg_bpp:48 planes:1
+ pix fmt nv16 avg_bpp:16 planes:2
+ pix fmt nv20le avg_bpp:32 planes:2
+ pix fmt nv20be avg_bpp:32 planes:2
+ pix fmt rgba64be avg_bpp:64 planes:1
+ pix fmt rgba64le avg_bpp:64 planes:1
+ pix fmt bgra64be avg_bpp:64 planes:1
+ pix fmt bgra64le avg_bpp:64 planes:1
+ pix fmt yvyu422 avg_bpp:16 planes:1
+ pix fmt ya16be avg_bpp:32 planes:1
+ pix fmt ya16le avg_bpp:32 planes:1
+ pix fmt gbrap avg_bpp:32 planes:4
+ pix fmt gbrap16be avg_bpp:64 planes:4
+ pix fmt gbrap16le avg_bpp:64 planes:4
+ pix fmt qsv avg_bpp:0 planes:0
+ pix fmt mmal avg_bpp:0 planes:0
+ pix fmt d3d11va_vld avg_bpp:0 planes:0
+ pix fmt cuda avg_bpp:0 planes:0
+ pix fmt 0rgb avg_bpp:32 planes:1
+ pix fmt rgb0 avg_bpp:32 planes:1
+ pix fmt 0bgr avg_bpp:32 planes:1
+ pix fmt bgr0 avg_bpp:32 planes:1
+ pix fmt yuv420p12be avg_bpp:24 planes:3
+ pix fmt yuv420p12le avg_bpp:24 planes:3
+ pix fmt yuv420p14be avg_bpp:24 planes:3
+ pix fmt yuv420p14le avg_bpp:24 planes:3
+ pix fmt yuv422p12be avg_bpp:32 planes:3
+ pix fmt yuv422p12le avg_bpp:32 planes:3
+ pix fmt yuv422p14be avg_bpp:32 planes:3
+ pix fmt yuv422p14le avg_bpp:32 planes:3
+ pix fmt yuv444p12be avg_bpp:48 planes:3
+ pix fmt yuv444p12le avg_bpp:48 planes:3
+ pix fmt yuv444p14be avg_bpp:48 planes:3
+ pix fmt yuv444p14le avg_bpp:48 planes:3
+ pix fmt gbrp12be avg_bpp:48 planes:3
+ pix fmt gbrp12le avg_bpp:48 planes:3
+ pix fmt gbrp14be avg_bpp:48 planes:3
+ pix fmt gbrp14le avg_bpp:48 planes:3
+ pix fmt yuvj411p avg_bpp:12 planes:3
+ pix fmt bayer_bggr8 avg_bpp:8 planes:1
+ pix fmt bayer_rggb8 avg_bpp:8 planes:1
+ pix fmt bayer_gbrg8 avg_bpp:8 planes:1
+ pix fmt bayer_grbg8 avg_bpp:8 planes:1
+ pix fmt bayer_bggr16le avg_bpp:16 planes:1
+ pix fmt bayer_bggr16be avg_bpp:16 planes:1
+ pix fmt bayer_rggb16le avg_bpp:16 planes:1
+ pix fmt bayer_rggb16be avg_bpp:16 planes:1
+ pix fmt bayer_gbrg16le avg_bpp:16 planes:1
+ pix fmt bayer_gbrg16be avg_bpp:16 planes:1
+ pix fmt bayer_grbg16le avg_bpp:16 planes:1
+ pix fmt bayer_grbg16be avg_bpp:16 planes:1
+ pix fmt yuv440p10le avg_bpp:32 planes:3
+ pix fmt yuv440p10be avg_bpp:32 planes:3
+ pix fmt yuv440p12le avg_bpp:32 planes:3
+ pix fmt yuv440p12be avg_bpp:32 planes:3
+ pix fmt ayuv64le avg_bpp:64 planes:1
+ pix fmt ayuv64be avg_bpp:64 planes:1
+ pix fmt videotoolbox_vld avg_bpp:0 planes:0
+ pix fmt p010le avg_bpp:24 planes:2
+ pix fmt p010be avg_bpp:24 planes:2
+ pix fmt gbrap12be avg_bpp:64 planes:4
+ pix fmt gbrap12le avg_bpp:64 planes:4
+ pix fmt gbrap10be avg_bpp:64 planes:4
+ pix fmt gbrap10le avg_bpp:64 planes:4
+ pix fmt mediacodec avg_bpp:0 planes:0
+ pix fmt gray12be avg_bpp:16 planes:1
+ pix fmt gray12le avg_bpp:16 planes:1
+ pix fmt gray10be avg_bpp:16 planes:1
+ pix fmt gray10le avg_bpp:16 planes:1
+ pix fmt p016le avg_bpp:24 planes:2
+ pix fmt p016be avg_bpp:24 planes:2
+ pix fmt d3d11 avg_bpp:0 planes:0
+ pix fmt gray9be avg_bpp:16 planes:1
+ pix fmt gray9le avg_bpp:16 planes:1
+ pix fmt gbrpf32be avg_bpp:96 planes:3
+ pix fmt gbrpf32le avg_bpp:96 planes:3
+ pix fmt gbrapf32be avg_bpp:128 planes:4
+ pix fmt gbrapf32le avg_bpp:128 planes:4
+ pix fmt drm_prime avg_bpp:0 planes:0
+ pix fmt opencl avg_bpp:0 planes:0
+ pix fmt gray14be avg_bpp:16 planes:1
+ pix fmt gray14le avg_bpp:16 planes:1
+ pix fmt grayf32be avg_bpp:32 planes:1
+ pix fmt grayf32le avg_bpp:32 planes:1
+ pix fmt yuva422p12be avg_bpp:48 planes:4
+ pix fmt yuva422p12le avg_bpp:48 planes:4
+ pix fmt yuva444p12be avg_bpp:64 planes:4
+ pix fmt yuva444p12le avg_bpp:64 planes:4
+ pix fmt nv24 avg_bpp:24 planes:2
+ pix fmt nv42 avg_bpp:24 planes:2
+ pix fmt vulkan avg_bpp:0 planes:0
+ pix fmt y210be avg_bpp:32 planes:1
+ pix fmt y210le avg_bpp:32 planes:1
+ pix fmt x2rgb10le avg_bpp:32 planes:1
+ pix fmt x2rgb10be avg_bpp:32 planes:1
+ pix fmt x2bgr10le avg_bpp:32 planes:1
+ pix fmt x2bgr10be avg_bpp:32 planes:1
+ pix fmt p210be avg_bpp:32 planes:2
+ pix fmt p210le avg_bpp:32 planes:2
+ pix fmt p410be avg_bpp:48 planes:2
+ pix fmt p410le avg_bpp:48 planes:2
+ pix fmt p216be avg_bpp:32 planes:2
+ pix fmt p216le avg_bpp:32 planes:2
+ pix fmt p416be avg_bpp:48 planes:2
+ pix fmt p416le avg_bpp:48 planes:2
+ pix fmt vuya avg_bpp:32 planes:1
+ pix fmt rgbaf16be avg_bpp:64 planes:1
+ pix fmt rgbaf16le avg_bpp:64 planes:1
+ pix fmt vuyx avg_bpp:32 planes:1
+ pix fmt p012le avg_bpp:24 planes:2
+ pix fmt p012be avg_bpp:24 planes:2
+ pix fmt y212be avg_bpp:32 planes:1
+ pix fmt y212le avg_bpp:32 planes:1
+ pix fmt xv30be avg_bpp:32 planes:1
+ pix fmt xv30le avg_bpp:32 planes:1
+ pix fmt xv36be avg_bpp:64 planes:1
+ pix fmt xv36le avg_bpp:64 planes:1
+ pix fmt rgbf32be avg_bpp:96 planes:1
+ pix fmt rgbf32le avg_bpp:96 planes:1
+ pix fmt rgbaf32be avg_bpp:128 planes:1
+ pix fmt rgbaf32le avg_bpp:128 planes:1
+ pix fmt p212be avg_bpp:32 planes:2
+ pix fmt p212le avg_bpp:32 planes:2
+ pix fmt p412be avg_bpp:48 planes:2
+ pix fmt p412le avg_bpp:48 planes:2
+ pix fmt gbrap14be avg_bpp:64 planes:4
+ pix fmt gbrap14le avg_bpp:64 planes:4
+ pix fmt d3d12 avg_bpp:0 planes:0
+ pix fmt ayuv avg_bpp:32 planes:1
+ pix fmt uyva avg_bpp:32 planes:1
+ pix fmt vyu444 avg_bpp:24 planes:1
+ pix fmt v30xbe avg_bpp:32 planes:1
+ pix fmt v30xle avg_bpp:32 planes:1
+ pix fmt rgbf16be avg_bpp:48 planes:1
+ pix fmt rgbf16le avg_bpp:48 planes:1
+ pix fmt rgba128be avg_bpp:128 planes:1
+ pix fmt rgba128le avg_bpp:128 planes:1
+ pix fmt rgb96be avg_bpp:96 planes:1
+ pix fmt rgb96le avg_bpp:96 planes:1
+ pix fmt y216be avg_bpp:32 planes:1
+ pix fmt y216le avg_bpp:32 planes:1
+ pix fmt xv48be avg_bpp:64 planes:1
+ pix fmt xv48le avg_bpp:64 planes:1
+ pix fmt gbrpf16be avg_bpp:48 planes:3
+ pix fmt gbrpf16le avg_bpp:48 planes:3
+ pix fmt gbrapf16be avg_bpp:64 planes:4
+ pix fmt gbrapf16le avg_bpp:64 planes:4
+ pix fmt grayf16be avg_bpp:16 planes:1
+ pix fmt grayf16le avg_bpp:16 planes:1
+ pix fmt amf avg_bpp:0 planes:0
+ pix fmt gray32be avg_bpp:32 planes:1
+ pix fmt gray32le avg_bpp:32 planes:1
+ pix fmt yaf32be avg_bpp:64 planes:1
+ pix fmt yaf32le avg_bpp:64 planes:1
+ pix fmt yaf16be avg_bpp:32 planes:1
+ pix fmt yaf16le avg_bpp:32 planes:1
+ pix fmt gbrap32be avg_bpp:128 planes:4
+ pix fmt gbrap32le avg_bpp:128 planes:4
+ pix fmt yuv444p10msbbe avg_bpp:48 planes:3
+ pix fmt yuv444p10msble avg_bpp:48 planes:3
+ pix fmt yuv444p12msbbe avg_bpp:48 planes:3
+ pix fmt yuv444p12msble avg_bpp:48 planes:3
+ pix fmt gbrp10msbbe avg_bpp:48 planes:3
+ pix fmt gbrp10msble avg_bpp:48 planes:3
+ pix fmt gbrp12msbbe avg_bpp:48 planes:3
+ pix fmt gbrp12msble avg_bpp:48 planes:3
+ pix fmt ohcodec avg_bpp:0 planes:0
--
2.52.0
>From 50236888fc061ec09ef295d164487718f81a0568 Mon Sep 17 00:00:00 2001
From: marcos ashton <[email protected]>
Date: Sat, 18 Apr 2026 13:17:07 +0100
Subject: [PATCH 2/4] tests/fate/libavutil: add FATE test for downmix_info
Test av_downmix_info_update_side_data() including first-call
allocation with zero-initialized defaults, field write/read-back,
second-call reuse (same pointer, preserved values), and the OOM
path via av_max_alloc.
Coverage for libavutil/downmix_info.c: 100.00% -> 100.00%
On a full FATE run the file is already covered by the AC-3
decoder tests (fate-ac3-4.0 and friends), which require
FATE_SAMPLES. This test exercises the API directly and keeps the
file covered on builds that run without samples.
Signed-off-by: marcos ashton <[email protected]>
---
.forgejo/CODEOWNERS | 2 +
libavutil/Makefile | 1 +
libavutil/tests/downmix_info.c | 86 ++++++++++++++++++++++++++++++++++
tests/fate/libavutil.mak | 4 ++
tests/ref/fate/downmix_info | 8 ++++
5 files changed, 101 insertions(+)
create mode 100644 libavutil/tests/downmix_info.c
create mode 100644 tests/ref/fate/downmix_info
diff --git a/.forgejo/CODEOWNERS b/.forgejo/CODEOWNERS
index 427cae7a1a..87ab1abf62 100644
--- a/.forgejo/CODEOWNERS
+++ b/.forgejo/CODEOWNERS
@@ -235,6 +235,7 @@ tests/checkasm/riscv/.* @Courmisch
libavutil/tests/ambient_viewing_environment.* @MarcosAsh
libavutil/tests/buffer.* @MarcosAsh
libavutil/tests/csp.* @MarcosAsh
+libavutil/tests/downmix_info.* @MarcosAsh
libavutil/tests/hdr_dynamic_vivid_metadata.* @MarcosAsh
libavutil/tests/pixdesc.* @MarcosAsh
libavutil/tests/tdrdi.* @MarcosAsh
@@ -243,6 +244,7 @@ tests/ref/.*drawvg.* @ayosec
tests/ref/fate/ambient_viewing_environment @MarcosAsh
tests/ref/fate/buffer @MarcosAsh
tests/ref/fate/csp @MarcosAsh
+tests/ref/fate/downmix_info @MarcosAsh
tests/ref/fate/hdr_dynamic_vivid_metadata @MarcosAsh
tests/ref/fate/pixdesc @MarcosAsh
tests/ref/fate/sub-mcc.* @programmerjake
diff --git a/libavutil/Makefile b/libavutil/Makefile
index 2e8a5de551..57470bc6d4 100644
--- a/libavutil/Makefile
+++ b/libavutil/Makefile
@@ -278,6 +278,7 @@ TESTPROGS = adler32
\
detection_bbox \
dict \
display \
+ downmix_info \
encryption_info \
error \
eval \
diff --git a/libavutil/tests/downmix_info.c b/libavutil/tests/downmix_info.c
new file mode 100644
index 0000000000..588927ce78
--- /dev/null
+++ b/libavutil/tests/downmix_info.c
@@ -0,0 +1,86 @@
+/*
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * FFmpeg is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <limits.h>
+#include <stdio.h>
+
+#include "libavutil/downmix_info.h"
+#include "libavutil/frame.h"
+#include "libavutil/mem.h"
+
+int main(void)
+{
+ AVFrame *frame;
+ AVDownmixInfo *info, *info2;
+
+ /* First call: allocates side data, zero-initialized. */
+ printf("Testing av_downmix_info_update_side_data()\n");
+ frame = av_frame_alloc();
+ if (!frame)
+ return 1;
+
+ info = av_downmix_info_update_side_data(frame);
+ if (info) {
+ printf("create: OK\n");
+ printf("defaults: type=%d center=%.1f center_ltrt=%.1f surround=%.1f"
+ " surround_ltrt=%.1f lfe=%.1f\n",
+ info->preferred_downmix_type,
+ info->center_mix_level, info->center_mix_level_ltrt,
+ info->surround_mix_level, info->surround_mix_level_ltrt,
+ info->lfe_mix_level);
+
+ /* Write fields and read them back. */
+ info->preferred_downmix_type = AV_DOWNMIX_TYPE_LTRT;
+ info->center_mix_level = -3.0;
+ info->center_mix_level_ltrt = -3.0;
+ info->surround_mix_level = -6.0;
+ info->surround_mix_level_ltrt = -6.0;
+ info->lfe_mix_level = -10.0;
+ printf("write: type=%d center=%.1f center_ltrt=%.1f surround=%.1f"
+ " surround_ltrt=%.1f lfe=%.1f\n",
+ info->preferred_downmix_type,
+ info->center_mix_level, info->center_mix_level_ltrt,
+ info->surround_mix_level, info->surround_mix_level_ltrt,
+ info->lfe_mix_level);
+ } else {
+ printf("create: FAIL\n");
+ }
+
+ /* Second call must reuse the existing side data (same pointer, values
kept). */
+ info2 = av_downmix_info_update_side_data(frame);
+ if (info && info2) {
+ printf("reuse: same_pointer=%s preserved_type=%d
preserved_center=%.1f\n",
+ info == info2 ? "yes" : "no",
+ info2->preferred_downmix_type, info2->center_mix_level);
+ }
+
+ av_frame_free(&frame);
+
+ /* OOM path: av_max_alloc(1) forces the internal side data allocation to
fail. */
+ printf("\nTesting OOM path\n");
+ frame = av_frame_alloc();
+ if (frame) {
+ av_max_alloc(1);
+ info = av_downmix_info_update_side_data(frame);
+ printf("OOM: %s\n", info ? "FAIL" : "OK");
+ av_max_alloc(INT_MAX);
+ av_frame_free(&frame);
+ }
+
+ return 0;
+}
diff --git a/tests/fate/libavutil.mak b/tests/fate/libavutil.mak
index eb116280a7..6cec293faf 100644
--- a/tests/fate/libavutil.mak
+++ b/tests/fate/libavutil.mak
@@ -90,6 +90,10 @@ FATE_LIBAVUTIL += fate-dict
fate-dict: libavutil/tests/dict$(EXESUF)
fate-dict: CMD = run libavutil/tests/dict$(EXESUF)
+FATE_LIBAVUTIL += fate-downmix_info
+fate-downmix_info: libavutil/tests/downmix_info$(EXESUF)
+fate-downmix_info: CMD = run libavutil/tests/downmix_info$(EXESUF)
+
FATE_LIBAVUTIL += fate-encryption-info
fate-encryption-info: libavutil/tests/encryption_info$(EXESUF)
fate-encryption-info: CMD = run libavutil/tests/encryption_info$(EXESUF)
diff --git a/tests/ref/fate/downmix_info b/tests/ref/fate/downmix_info
new file mode 100644
index 0000000000..085396865c
--- /dev/null
+++ b/tests/ref/fate/downmix_info
@@ -0,0 +1,8 @@
+Testing av_downmix_info_update_side_data()
+create: OK
+defaults: type=0 center=0.0 center_ltrt=0.0 surround=0.0 surround_ltrt=0.0
lfe=0.0
+write: type=2 center=-3.0 center_ltrt=-3.0 surround=-6.0 surround_ltrt=-6.0
lfe=-10.0
+reuse: same_pointer=yes preserved_type=2 preserved_center=-3.0
+
+Testing OOM path
+OOM: OK
--
2.52.0
>From 3a7bebbfa911e6ae3bb0a3d223bfaf4bdbf0a01e Mon Sep 17 00:00:00 2001
From: marcos ashton <[email protected]>
Date: Sat, 18 Apr 2026 14:18:28 +0100
Subject: [PATCH 3/4] tests/fate/libavutil: add FATE test for
hdr_dynamic_metadata
Test av_dynamic_hdr_plus_alloc, av_dynamic_hdr_plus_create_side_data,
av_dynamic_hdr_plus_to_t35 in all three documented modes (size
query, caller-allocated, existing caller-owned buffer) including
the AVERROR_BUFFER_TOO_SMALL path, av_dynamic_hdr_plus_from_t35
as a round-trip inverse for minimal and two-window rich payloads
exercising peak luminance matrices, percentile distribution, tone
mapping and color saturation branches, NULL and oversized error
paths, and av_dynamic_hdr_smpte2094_app5_alloc,
av_dynamic_hdr_smpte2094_app5_create_side_data, the App5 to_t35
and from_t35 round trip with adaptive tone map enabled and
num_alternate_images=2. OOM paths via av_max_alloc.
Coverage for libavutil/hdr_dynamic_metadata.c: 31.02% -> 91.56%
The baseline comes from the HDR10+ sample tests on a full FATE
run (fate-hevc-hdr10-plus-metadata, fate-png-mdcv and the
Matroska/WebM HDR10+ remux tests).
Signed-off-by: marcos ashton <[email protected]>
---
.forgejo/CODEOWNERS | 2 +
libavutil/Makefile | 1 +
libavutil/tests/hdr_dynamic_metadata.c | 409 +++++++++++++++++++++++++
tests/fate/libavutil.mak | 4 +
tests/ref/fate/hdr_dynamic_metadata | 57 ++++
5 files changed, 473 insertions(+)
create mode 100644 libavutil/tests/hdr_dynamic_metadata.c
create mode 100644 tests/ref/fate/hdr_dynamic_metadata
diff --git a/.forgejo/CODEOWNERS b/.forgejo/CODEOWNERS
index 87ab1abf62..b2e85a9f66 100644
--- a/.forgejo/CODEOWNERS
+++ b/.forgejo/CODEOWNERS
@@ -236,6 +236,7 @@ libavutil/tests/ambient_viewing_environment.* @MarcosAsh
libavutil/tests/buffer.* @MarcosAsh
libavutil/tests/csp.* @MarcosAsh
libavutil/tests/downmix_info.* @MarcosAsh
+libavutil/tests/hdr_dynamic_metadata.* @MarcosAsh
libavutil/tests/hdr_dynamic_vivid_metadata.* @MarcosAsh
libavutil/tests/pixdesc.* @MarcosAsh
libavutil/tests/tdrdi.* @MarcosAsh
@@ -245,6 +246,7 @@ tests/ref/fate/ambient_viewing_environment @MarcosAsh
tests/ref/fate/buffer @MarcosAsh
tests/ref/fate/csp @MarcosAsh
tests/ref/fate/downmix_info @MarcosAsh
+tests/ref/fate/hdr_dynamic_metadata @MarcosAsh
tests/ref/fate/hdr_dynamic_vivid_metadata @MarcosAsh
tests/ref/fate/pixdesc @MarcosAsh
tests/ref/fate/sub-mcc.* @programmerjake
diff --git a/libavutil/Makefile b/libavutil/Makefile
index 57470bc6d4..5ea2b39ed2 100644
--- a/libavutil/Makefile
+++ b/libavutil/Makefile
@@ -286,6 +286,7 @@ TESTPROGS = adler32
\
fifo \
film_grain_params \
hash \
+ hdr_dynamic_metadata \
hdr_dynamic_vivid_metadata \
hmac \
hwdevice \
diff --git a/libavutil/tests/hdr_dynamic_metadata.c
b/libavutil/tests/hdr_dynamic_metadata.c
new file mode 100644
index 0000000000..fa16238f7e
--- /dev/null
+++ b/libavutil/tests/hdr_dynamic_metadata.c
@@ -0,0 +1,409 @@
+/*
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * FFmpeg is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <limits.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "libavutil/error.h"
+#include "libavutil/frame.h"
+#include "libavutil/hdr_dynamic_metadata.h"
+#include "libavutil/mem.h"
+#include "libavutil/rational.h"
+
+static void fill_hdr_plus_minimal(AVDynamicHDRPlus *s)
+{
+ /* Minimal single-window payload. to_t35 hard-codes application_mode=1, so
+ * use application_version=1 for a clean round-trip check. */
+ s->application_version = 1;
+ s->num_windows = 1;
+ s->targeted_system_display_maximum_luminance = (AVRational){ 10000, 1 };
+ s->targeted_system_display_actual_peak_luminance_flag = 0;
+
+ s->params[0].maxscl[0] = (AVRational){ 50000, 100000 };
+ s->params[0].maxscl[1] = (AVRational){ 60000, 100000 };
+ s->params[0].maxscl[2] = (AVRational){ 70000, 100000 };
+ s->params[0].average_maxrgb = (AVRational){ 40000, 100000 };
+ s->params[0].num_distribution_maxrgb_percentiles = 0;
+ s->params[0].fraction_bright_pixels = (AVRational){ 250, 1000 };
+
+ s->mastering_display_actual_peak_luminance_flag = 0;
+ s->params[0].tone_mapping_flag = 0;
+ s->params[0].color_saturation_mapping_flag = 0;
+}
+
+static void test_hdr_plus(void)
+{
+ AVDynamicHDRPlus *hdr, hdr_rt;
+ AVFrame *frame;
+ size_t size = 0, required = 0;
+ uint8_t *buf = NULL;
+ int ret;
+
+ /* av_dynamic_hdr_plus_alloc */
+ printf("Testing av_dynamic_hdr_plus_alloc()\n");
+ hdr = av_dynamic_hdr_plus_alloc(&size);
+ printf("alloc: %s size>0=%s\n", hdr ? "OK" : "FAIL",
+ size > 0 ? "yes" : "no");
+ av_freep(&hdr);
+
+ hdr = av_dynamic_hdr_plus_alloc(NULL);
+ printf("alloc (no size): %s\n", hdr ? "OK" : "FAIL");
+ av_freep(&hdr);
+
+ /* av_dynamic_hdr_plus_create_side_data */
+ printf("\nTesting av_dynamic_hdr_plus_create_side_data()\n");
+ frame = av_frame_alloc();
+ if (frame) {
+ hdr = av_dynamic_hdr_plus_create_side_data(frame);
+ printf("create_side_data: %s\n", hdr ? "OK" : "FAIL");
+ av_frame_free(&frame);
+ }
+
+ /* Build a minimal payload for round-trip tests. */
+ hdr = av_dynamic_hdr_plus_alloc(NULL);
+ if (!hdr)
+ return;
+ fill_hdr_plus_minimal(hdr);
+
+ /* Size-query mode: data=NULL, size receives required byte count. */
+ printf("\nTesting av_dynamic_hdr_plus_to_t35() size query\n");
+ required = 0;
+ ret = av_dynamic_hdr_plus_to_t35(hdr, NULL, &required);
+ printf("size query: ret=%d required>0=%s\n", ret,
+ required > 0 ? "yes" : "no");
+
+ /* Allocation mode: *data=NULL, function mallocs the buffer. */
+ printf("\nTesting av_dynamic_hdr_plus_to_t35() allocation\n");
+ buf = NULL;
+ size = 0;
+ ret = av_dynamic_hdr_plus_to_t35(hdr, &buf, &size);
+ printf("alloc mode: ret=%d size_match=%s\n", ret,
+ size == required ? "yes" : "no");
+ av_freep(&buf);
+
+ /* Existing-buffer mode: caller-owned buffer is filled, pointer unchanged.
*/
+ printf("\nTesting av_dynamic_hdr_plus_to_t35() existing buffer\n");
+ buf = av_malloc(required);
+ if (buf) {
+ uint8_t *orig = buf;
+ size = required;
+ ret = av_dynamic_hdr_plus_to_t35(hdr, &buf, &size);
+ printf("existing buf: ret=%d same_pointer=%s size=%zu\n", ret,
+ buf == orig ? "yes" : "no", size);
+ av_freep(&buf);
+ }
+
+ /* Buffer-too-small: caller-owned buffer shorter than required. */
+ printf("\nTesting av_dynamic_hdr_plus_to_t35() buffer too small\n");
+ if (required > 1) {
+ buf = av_malloc(required - 1);
+ if (buf) {
+ size = required - 1;
+ ret = av_dynamic_hdr_plus_to_t35(hdr, &buf, &size);
+ printf("too small: ret==AVERROR_BUFFER_TOO_SMALL=%s\n",
+ ret == AVERROR_BUFFER_TOO_SMALL ? "yes" : "no");
+ av_freep(&buf);
+ }
+ }
+
+ /* Round trip: from_t35(to_t35(hdr)) should reproduce the fields. */
+ printf("\nTesting av_dynamic_hdr_plus_from_t35() round trip\n");
+ buf = NULL;
+ size = 0;
+ ret = av_dynamic_hdr_plus_to_t35(hdr, &buf, &size);
+ if (ret >= 0 && buf) {
+ memset(&hdr_rt, 0, sizeof(hdr_rt));
+ ret = av_dynamic_hdr_plus_from_t35(&hdr_rt, buf, size);
+ printf("from_t35: ret=%d\n", ret);
+ printf("round trip: app_ver=%u num_windows=%u max_lum=%d/%d"
+ " maxscl0=%d/%d avg=%d/%d frac_bright=%d/%d\n",
+ hdr_rt.application_version, hdr_rt.num_windows,
+ hdr_rt.targeted_system_display_maximum_luminance.num,
+ hdr_rt.targeted_system_display_maximum_luminance.den,
+ hdr_rt.params[0].maxscl[0].num,
+ hdr_rt.params[0].maxscl[0].den,
+ hdr_rt.params[0].average_maxrgb.num,
+ hdr_rt.params[0].average_maxrgb.den,
+ hdr_rt.params[0].fraction_bright_pixels.num,
+ hdr_rt.params[0].fraction_bright_pixels.den);
+ }
+ av_freep(&buf);
+
+ /* Error paths. */
+ printf("\nTesting error paths\n");
+ ret = av_dynamic_hdr_plus_to_t35(NULL, &buf, &size);
+ printf("to_t35 NULL s: ret==AVERROR(EINVAL)=%s\n",
+ ret == AVERROR(EINVAL) ? "yes" : "no");
+
+ ret = av_dynamic_hdr_plus_to_t35(hdr, NULL, NULL);
+ printf("to_t35 no data no size: ret==AVERROR(EINVAL)=%s\n",
+ ret == AVERROR(EINVAL) ? "yes" : "no");
+
+ ret = av_dynamic_hdr_plus_from_t35(NULL, (const uint8_t *)"", 0);
+ printf("from_t35 NULL s: ret==AVERROR(ENOMEM)=%s\n",
+ ret == AVERROR(ENOMEM) ? "yes" : "no");
+
+ {
+ /* Oversized input is rejected before parsing. */
+ size_t big = AV_HDR_PLUS_MAX_PAYLOAD_SIZE + 1;
+ uint8_t *oversized = av_mallocz(big);
+ if (oversized) {
+ ret = av_dynamic_hdr_plus_from_t35(&hdr_rt, oversized, big);
+ printf("from_t35 oversized: ret==AVERROR(EINVAL)=%s\n",
+ ret == AVERROR(EINVAL) ? "yes" : "no");
+ av_free(oversized);
+ }
+ }
+
+ /* Rich payload: flip every optional flag so all branches get exercised. */
+ printf("\nTesting rich round trip (all flags set)\n");
+ hdr->num_windows = 2;
+ hdr->params[1].window_upper_left_corner_x = (AVRational){ 100, 1 };
+ hdr->params[1].window_upper_left_corner_y = (AVRational){ 100, 1 };
+ hdr->params[1].window_lower_right_corner_x = (AVRational){ 500, 1 };
+ hdr->params[1].window_lower_right_corner_y = (AVRational){ 500, 1 };
+ hdr->params[1].center_of_ellipse_x = 300;
+ hdr->params[1].center_of_ellipse_y = 300;
+ hdr->params[1].rotation_angle = 45;
+ hdr->params[1].semimajor_axis_internal_ellipse = 50;
+ hdr->params[1].semimajor_axis_external_ellipse = 100;
+ hdr->params[1].semiminor_axis_external_ellipse = 80;
+ hdr->params[1].overlap_process_option = 1;
+ hdr->params[1].maxscl[0] = (AVRational){ 20000, 100000 };
+ hdr->params[1].maxscl[1] = (AVRational){ 30000, 100000 };
+ hdr->params[1].maxscl[2] = (AVRational){ 40000, 100000 };
+ hdr->params[1].average_maxrgb = (AVRational){ 25000, 100000 };
+ hdr->params[1].num_distribution_maxrgb_percentiles = 0;
+ hdr->params[1].fraction_bright_pixels = (AVRational){ 100, 1000 };
+
+ hdr->params[0].num_distribution_maxrgb_percentiles = 2;
+ hdr->params[0].distribution_maxrgb[0].percentage = 50;
+ hdr->params[0].distribution_maxrgb[0].percentile = (AVRational){ 30000,
100000 };
+ hdr->params[0].distribution_maxrgb[1].percentage = 99;
+ hdr->params[0].distribution_maxrgb[1].percentile = (AVRational){ 90000,
100000 };
+
+ hdr->targeted_system_display_actual_peak_luminance_flag = 1;
+ hdr->num_rows_targeted_system_display_actual_peak_luminance = 2;
+ hdr->num_cols_targeted_system_display_actual_peak_luminance = 2;
+ for (int i = 0; i < 2; i++)
+ for (int j = 0; j < 2; j++)
+ hdr->targeted_system_display_actual_peak_luminance[i][j] =
+ (AVRational){ i + j, 15 };
+
+ hdr->mastering_display_actual_peak_luminance_flag = 1;
+ hdr->num_rows_mastering_display_actual_peak_luminance = 2;
+ hdr->num_cols_mastering_display_actual_peak_luminance = 2;
+ for (int i = 0; i < 2; i++)
+ for (int j = 0; j < 2; j++)
+ hdr->mastering_display_actual_peak_luminance[i][j] =
+ (AVRational){ 1, 15 };
+
+ for (int w = 0; w < 2; w++) {
+ hdr->params[w].tone_mapping_flag = 1;
+ hdr->params[w].knee_point_x = (AVRational){ 500, 4095 };
+ hdr->params[w].knee_point_y = (AVRational){ 800, 4095 };
+ hdr->params[w].num_bezier_curve_anchors = 3;
+ hdr->params[w].bezier_curve_anchors[0] = (AVRational){ 100, 1023 };
+ hdr->params[w].bezier_curve_anchors[1] = (AVRational){ 500, 1023 };
+ hdr->params[w].bezier_curve_anchors[2] = (AVRational){ 900, 1023 };
+ hdr->params[w].color_saturation_mapping_flag = 1;
+ hdr->params[w].color_saturation_weight = (AVRational){ 8, 8 };
+ }
+
+ buf = NULL;
+ size = 0;
+ ret = av_dynamic_hdr_plus_to_t35(hdr, &buf, &size);
+ if (ret >= 0 && buf) {
+ memset(&hdr_rt, 0, sizeof(hdr_rt));
+ ret = av_dynamic_hdr_plus_from_t35(&hdr_rt, buf, size);
+ printf("rich: ret=%d num_windows=%u target_peak_flag=%u"
+ " master_peak_flag=%u tone_w0=%u tone_w1=%u"
+ " num_pct_w0=%u sat_w0=%u\n", ret,
+ hdr_rt.num_windows,
+ hdr_rt.targeted_system_display_actual_peak_luminance_flag,
+ hdr_rt.mastering_display_actual_peak_luminance_flag,
+ hdr_rt.params[0].tone_mapping_flag,
+ hdr_rt.params[1].tone_mapping_flag,
+ hdr_rt.params[0].num_distribution_maxrgb_percentiles,
+ hdr_rt.params[0].color_saturation_mapping_flag);
+ }
+ av_freep(&buf);
+
+ av_freep(&hdr);
+
+ /* OOM path for alloc. */
+ printf("\nTesting OOM path\n");
+ av_max_alloc(1);
+ hdr = av_dynamic_hdr_plus_alloc(&size);
+ printf("alloc OOM: %s\n", hdr ? "FAIL" : "OK");
+ av_max_alloc(INT_MAX);
+ av_freep(&hdr);
+}
+
+static void fill_app5_minimal(AVDynamicHDRSmpte2094App5 *s)
+{
+ s->application_version = 1;
+ s->minimum_application_version = 0;
+ s->has_custom_hdr_reference_white_flag = 1;
+ s->hdr_reference_white = 0x0203;
+ s->has_adaptive_tone_map_flag = 0;
+}
+
+static void test_app5(void)
+{
+ AVDynamicHDRSmpte2094App5 *app5, app5_rt;
+ AVFrame *frame;
+ size_t size = 0, required = 0;
+ uint8_t *buf = NULL;
+ int ret;
+
+ /* alloc / create_side_data */
+ printf("\n=== AVDynamicHDRSmpte2094App5 ===\n");
+ printf("Testing av_dynamic_hdr_smpte2094_app5_alloc()\n");
+ app5 = av_dynamic_hdr_smpte2094_app5_alloc(&size);
+ printf("alloc: %s size>0=%s\n", app5 ? "OK" : "FAIL",
+ size > 0 ? "yes" : "no");
+ av_freep(&app5);
+
+ printf("\nTesting av_dynamic_hdr_smpte2094_app5_create_side_data()\n");
+ frame = av_frame_alloc();
+ if (frame) {
+ app5 = av_dynamic_hdr_smpte2094_app5_create_side_data(frame);
+ printf("create_side_data: %s\n", app5 ? "OK" : "FAIL");
+ av_frame_free(&frame);
+ }
+
+ app5 = av_dynamic_hdr_smpte2094_app5_alloc(NULL);
+ if (!app5)
+ return;
+ fill_app5_minimal(app5);
+
+ /* Size query. */
+ required = 0;
+ ret = av_dynamic_hdr_smpte2094_app5_to_t35(app5, NULL, &required);
+ printf("\nTesting av_dynamic_hdr_smpte2094_app5_to_t35()\n");
+ printf("size query: ret=%d required>0=%s\n", ret,
+ required > 0 ? "yes" : "no");
+
+ /* Allocation + round trip. */
+ buf = NULL;
+ size = 0;
+ ret = av_dynamic_hdr_smpte2094_app5_to_t35(app5, &buf, &size);
+ printf("alloc mode: ret=%d size_match=%s\n", ret,
+ size == required ? "yes" : "no");
+
+ if (ret >= 0 && buf) {
+ memset(&app5_rt, 0, sizeof(app5_rt));
+ ret = av_dynamic_hdr_smpte2094_app5_from_t35(&app5_rt, buf, size);
+ printf("round trip: ret=%d app_ver=%u min_ver=%u"
+ " custom_white_flag=%u ref_white=0x%04x adapt_flag=%u\n",
+ ret, app5_rt.application_version,
+ app5_rt.minimum_application_version,
+ app5_rt.has_custom_hdr_reference_white_flag,
+ app5_rt.hdr_reference_white,
+ app5_rt.has_adaptive_tone_map_flag);
+ }
+ av_freep(&buf);
+
+ /* Error paths. */
+ printf("\nTesting error paths\n");
+ ret = av_dynamic_hdr_smpte2094_app5_to_t35(NULL, &buf, &size);
+ printf("to_t35 NULL s: ret==AVERROR(EINVAL)=%s\n",
+ ret == AVERROR(EINVAL) ? "yes" : "no");
+
+ ret = av_dynamic_hdr_smpte2094_app5_from_t35(NULL, (const uint8_t *)"", 0);
+ printf("from_t35 NULL s: ret==AVERROR(EINVAL)=%s\n",
+ ret == AVERROR(EINVAL) ? "yes" : "no");
+
+ /* application_version >= 8 is rejected by to_t35. */
+ app5->application_version = 8;
+ ret = av_dynamic_hdr_smpte2094_app5_to_t35(app5, NULL, &size);
+ printf("to_t35 invalid ver: ret==AVERROR_INVALIDDATA=%s\n",
+ ret == AVERROR_INVALIDDATA ? "yes" : "no");
+ app5->application_version = 1;
+
+ /* Rich App5: adaptive tone map with alternate images to exercise every
+ * gain-curve branch. */
+ printf("\nTesting App5 rich round trip\n");
+ memset(app5, 0, sizeof(*app5));
+ app5->application_version = 1;
+ app5->minimum_application_version = 0;
+ app5->has_custom_hdr_reference_white_flag = 0;
+ app5->has_adaptive_tone_map_flag = 1;
+ app5->baseline_hdr_headroom = 0x0abc;
+ app5->use_reference_white_tone_mapping_flag = 0;
+ app5->num_alternate_images = 2;
+ app5->gain_application_space_chromaticities_flag = 3;
+ for (int r = 0; r < 8; r++)
+ app5->gain_application_space_chromaticities[r] = 100 * (r + 1);
+ app5->has_common_component_mix_params_flag = 0;
+ app5->has_common_curve_params_flag = 0;
+ for (int a = 0; a < 2; a++) {
+ app5->alternate_hdr_headrooms[a] = 0x0100 + a;
+ app5->component_mixing_type[a] = 3;
+ for (int k = 0; k < 6; k++) {
+ app5->has_component_mixing_coefficient_flag[a][k] = k & 1;
+ app5->component_mixing_coefficient[a][k] = 0x1000 + k;
+ }
+ app5->gain_curve_num_control_points_minus_1[a] = 1;
+ app5->gain_curve_use_pchip_slope_flag[a] = 0;
+ for (int c = 0; c < 2; c++) {
+ app5->gain_curve_control_points_x[a][c] = 0x2000 + c;
+ app5->gain_curve_control_points_y[a][c] = 0x3000 + c;
+ app5->gain_curve_control_points_theta[a][c] = 0x4000 + c;
+ }
+ }
+
+ buf = NULL;
+ size = 0;
+ ret = av_dynamic_hdr_smpte2094_app5_to_t35(app5, &buf, &size);
+ if (ret >= 0 && buf) {
+ memset(&app5_rt, 0, sizeof(app5_rt));
+ ret = av_dynamic_hdr_smpte2094_app5_from_t35(&app5_rt, buf, size);
+ printf("App5 rich: ret=%d adapt=%u num_alt=%u chroma_flag=%u"
+ " common_mix=%u common_curve=%u mix_type[0]=%u"
+ " ctrl_x[0][0]=0x%04x theta[1][1]=0x%04x\n", ret,
+ app5_rt.has_adaptive_tone_map_flag,
+ app5_rt.num_alternate_images,
+ app5_rt.gain_application_space_chromaticities_flag,
+ app5_rt.has_common_component_mix_params_flag,
+ app5_rt.has_common_curve_params_flag,
+ app5_rt.component_mixing_type[0],
+ app5_rt.gain_curve_control_points_x[0][0],
+ app5_rt.gain_curve_control_points_theta[1][1]);
+ }
+ av_freep(&buf);
+
+ av_freep(&app5);
+
+ /* OOM. */
+ printf("\nTesting OOM path\n");
+ av_max_alloc(1);
+ app5 = av_dynamic_hdr_smpte2094_app5_alloc(&size);
+ printf("alloc OOM: %s\n", app5 ? "FAIL" : "OK");
+ av_max_alloc(INT_MAX);
+ av_freep(&app5);
+}
+
+int main(void)
+{
+ test_hdr_plus();
+ test_app5();
+ return 0;
+}
diff --git a/tests/fate/libavutil.mak b/tests/fate/libavutil.mak
index 6cec293faf..c5bdf7b763 100644
--- a/tests/fate/libavutil.mak
+++ b/tests/fate/libavutil.mak
@@ -115,6 +115,10 @@ FATE_LIBAVUTIL += fate-hash
fate-hash: libavutil/tests/hash$(EXESUF)
fate-hash: CMD = run libavutil/tests/hash$(EXESUF)
+FATE_LIBAVUTIL += fate-hdr_dynamic_metadata
+fate-hdr_dynamic_metadata: libavutil/tests/hdr_dynamic_metadata$(EXESUF)
+fate-hdr_dynamic_metadata: CMD = run
libavutil/tests/hdr_dynamic_metadata$(EXESUF)
+
FATE_LIBAVUTIL += fate-hdr_dynamic_vivid_metadata
fate-hdr_dynamic_vivid_metadata:
libavutil/tests/hdr_dynamic_vivid_metadata$(EXESUF)
fate-hdr_dynamic_vivid_metadata: CMD = run
libavutil/tests/hdr_dynamic_vivid_metadata$(EXESUF)
diff --git a/tests/ref/fate/hdr_dynamic_metadata
b/tests/ref/fate/hdr_dynamic_metadata
new file mode 100644
index 0000000000..4d419b115d
--- /dev/null
+++ b/tests/ref/fate/hdr_dynamic_metadata
@@ -0,0 +1,57 @@
+Testing av_dynamic_hdr_plus_alloc()
+alloc: OK size>0=yes
+alloc (no size): OK
+
+Testing av_dynamic_hdr_plus_create_side_data()
+create_side_data: OK
+
+Testing av_dynamic_hdr_plus_to_t35() size query
+size query: ret=0 required>0=yes
+
+Testing av_dynamic_hdr_plus_to_t35() allocation
+alloc mode: ret=0 size_match=yes
+
+Testing av_dynamic_hdr_plus_to_t35() existing buffer
+existing buf: ret=0 same_pointer=yes size=16
+
+Testing av_dynamic_hdr_plus_to_t35() buffer too small
+too small: ret==AVERROR_BUFFER_TOO_SMALL=yes
+
+Testing av_dynamic_hdr_plus_from_t35() round trip
+from_t35: ret=0
+round trip: app_ver=1 num_windows=1 max_lum=10000/1 maxscl0=50000/100000
avg=40000/100000 frac_bright=250/1000
+
+Testing error paths
+to_t35 NULL s: ret==AVERROR(EINVAL)=yes
+to_t35 no data no size: ret==AVERROR(EINVAL)=yes
+from_t35 NULL s: ret==AVERROR(ENOMEM)=yes
+from_t35 oversized: ret==AVERROR(EINVAL)=yes
+
+Testing rich round trip (all flags set)
+rich: ret=0 num_windows=2 target_peak_flag=1 master_peak_flag=1 tone_w0=1
tone_w1=1 num_pct_w0=2 sat_w0=1
+
+Testing OOM path
+alloc OOM: OK
+
+=== AVDynamicHDRSmpte2094App5 ===
+Testing av_dynamic_hdr_smpte2094_app5_alloc()
+alloc: OK size>0=yes
+
+Testing av_dynamic_hdr_smpte2094_app5_create_side_data()
+create_side_data: OK
+
+Testing av_dynamic_hdr_smpte2094_app5_to_t35()
+size query: ret=0 required>0=yes
+alloc mode: ret=0 size_match=yes
+round trip: ret=0 app_ver=1 min_ver=0 custom_white_flag=1 ref_white=0x0203
adapt_flag=0
+
+Testing error paths
+to_t35 NULL s: ret==AVERROR(EINVAL)=yes
+from_t35 NULL s: ret==AVERROR(EINVAL)=yes
+to_t35 invalid ver: ret==AVERROR_INVALIDDATA=yes
+
+Testing App5 rich round trip
+App5 rich: ret=0 adapt=1 num_alt=2 chroma_flag=3 common_mix=0 common_curve=0
mix_type[0]=3 ctrl_x[0][0]=0x2000 theta[1][1]=0x4001
+
+Testing OOM path
+alloc OOM: OK
--
2.52.0
>From 9e0ef873484e44b6471510f3e9ff281ca5381520 Mon Sep 17 00:00:00 2001
From: marcos ashton <[email protected]>
Date: Sat, 18 Apr 2026 14:30:39 +0100
Subject: [PATCH 4/4] tests/fate/libavutil: add FATE test for side_data
Test av_frame_side_data_desc and av_frame_side_data_name on
valid and invalid types, av_frame_side_data_get and its _c
variant for present and absent types,
av_frame_side_data_remove (including MULTI types with
duplicates), av_frame_side_data_remove_by_props filtering by
AV_SIDE_DATA_PROP_*, av_frame_side_data_clone with and without
AV_FRAME_SIDE_DATA_FLAG_REPLACE (including the AVERROR(EEXIST)
and AVERROR(EINVAL) paths) and with AV_FRAME_SIDE_DATA_FLAG_UNIQUE,
av_frame_side_data_add in move mode, with
AV_FRAME_SIDE_DATA_FLAG_NEW_REF and with
AV_FRAME_SIDE_DATA_FLAG_REPLACE over an existing entry, and
av_frame_side_data_free.
Coverage for libavutil/side_data.c: 72.14% -> 86.43%
Numbers measured against a full FATE run. Remaining uncovered
lines are internal allocation-failure error paths requiring
injected failures mid-operation.
Signed-off-by: marcos ashton <[email protected]>
---
.forgejo/CODEOWNERS | 2 +
libavutil/Makefile | 1 +
libavutil/tests/side_data.c | 196 ++++++++++++++++++++++++++++++++++++
tests/fate/libavutil.mak | 4 +
tests/ref/fate/side_data | 49 +++++++++
5 files changed, 252 insertions(+)
create mode 100644 libavutil/tests/side_data.c
create mode 100644 tests/ref/fate/side_data
diff --git a/.forgejo/CODEOWNERS b/.forgejo/CODEOWNERS
index b2e85a9f66..bf03b2c5cc 100644
--- a/.forgejo/CODEOWNERS
+++ b/.forgejo/CODEOWNERS
@@ -239,6 +239,7 @@ libavutil/tests/downmix_info.* @MarcosAsh
libavutil/tests/hdr_dynamic_metadata.* @MarcosAsh
libavutil/tests/hdr_dynamic_vivid_metadata.* @MarcosAsh
libavutil/tests/pixdesc.* @MarcosAsh
+libavutil/tests/side_data.* @MarcosAsh
libavutil/tests/tdrdi.* @MarcosAsh
libavutil/tests/timestamp.* @MarcosAsh
tests/ref/.*drawvg.* @ayosec
@@ -249,6 +250,7 @@ tests/ref/fate/downmix_info @MarcosAsh
tests/ref/fate/hdr_dynamic_metadata @MarcosAsh
tests/ref/fate/hdr_dynamic_vivid_metadata @MarcosAsh
tests/ref/fate/pixdesc @MarcosAsh
+tests/ref/fate/side_data @MarcosAsh
tests/ref/fate/sub-mcc.* @programmerjake
tests/ref/fate/tdrdi @MarcosAsh
tests/ref/fate/timestamp @MarcosAsh
diff --git a/libavutil/Makefile b/libavutil/Makefile
index 5ea2b39ed2..25c04b7e97 100644
--- a/libavutil/Makefile
+++ b/libavutil/Makefile
@@ -311,6 +311,7 @@ TESTPROGS = adler32
\
sha \
sha512 \
samplefmt \
+ side_data \
side_data_array \
softfloat \
spherical \
diff --git a/libavutil/tests/side_data.c b/libavutil/tests/side_data.c
new file mode 100644
index 0000000000..401a10d759
--- /dev/null
+++ b/libavutil/tests/side_data.c
@@ -0,0 +1,196 @@
+/*
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * FFmpeg is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <stdio.h>
+
+#include "libavutil/avassert.h"
+#include "libavutil/buffer.h"
+#include "libavutil/error.h"
+#include "libavutil/frame.h"
+#include "libavutil/macros.h"
+
+typedef struct Set {
+ AVFrameSideData **sd;
+ int nb_sd;
+} Set;
+
+static AVFrameSideData *new_entry(Set *s, enum AVFrameSideDataType type,
+ int32_t value)
+{
+ AVFrameSideData *sd = av_frame_side_data_new(&s->sd, &s->nb_sd, type,
+ sizeof(int32_t), 0);
+ av_assert0(sd);
+ *(int32_t *)sd->data = value;
+ return sd;
+}
+
+static void print_set(const char *label, const Set *s)
+{
+ printf("%s: n=%d\n", label, s->nb_sd);
+ for (int i = 0; i < s->nb_sd; i++)
+ printf(" [%d] %s val=%d\n", i,
+ av_frame_side_data_name(s->sd[i]->type),
+ *(int32_t *)s->sd[i]->data);
+}
+
+int main(void)
+{
+ /* desc / name accessors. */
+ printf("Testing av_frame_side_data_desc() / av_frame_side_data_name()\n");
+ static const enum AVFrameSideDataType known[] = {
+ AV_FRAME_DATA_STEREO3D, /* PROP_GLOBAL */
+ AV_FRAME_DATA_DYNAMIC_HDR_PLUS, /* PROP_COLOR_DEPENDENT */
+ AV_FRAME_DATA_SPHERICAL, /*
PROP_GLOBAL|SIZE_DEPENDENT */
+ AV_FRAME_DATA_DOWNMIX_INFO, /* PROP_CHANNEL_DEPENDENT */
+ AV_FRAME_DATA_SEI_UNREGISTERED, /* PROP_MULTI */
+ };
+ for (int i = 0; i < FF_ARRAY_ELEMS(known); i++) {
+ const AVSideDataDescriptor *d = av_frame_side_data_desc(known[i]);
+ const char *n = av_frame_side_data_name(known[i]);
+ printf(" type=%d name_match=%s props=0x%x\n", known[i],
+ d && n && d->name == n ? "yes" : "no",
+ d ? d->props : 0);
+ }
+
+ /* Invalid / unmapped types must return NULL (sd_props gap). */
+ {
+ enum AVFrameSideDataType bad = (enum AVFrameSideDataType)-1;
+ printf(" invalid type desc=%s name=%s\n",
+ av_frame_side_data_desc(bad) ? "non-null" : "null",
+ av_frame_side_data_name(bad) ? "non-null" : "null");
+ }
+
+ /* Populate a set with several types, one of them MULTI. */
+ Set set = { 0 };
+ new_entry(&set, AV_FRAME_DATA_STEREO3D, 100);
+ new_entry(&set, AV_FRAME_DATA_DYNAMIC_HDR_PLUS, 200);
+ new_entry(&set, AV_FRAME_DATA_SEI_UNREGISTERED, 1);
+ new_entry(&set, AV_FRAME_DATA_SEI_UNREGISTERED, 2);
+ new_entry(&set, AV_FRAME_DATA_SPHERICAL, 300);
+ print_set("\nInitial set", &set);
+
+ /* get / get_c: present and missing types. */
+ printf("\nTesting av_frame_side_data_get()\n");
+ {
+ const AVFrameSideData *got =
+ av_frame_side_data_get(set.sd, set.nb_sd, AV_FRAME_DATA_STEREO3D);
+ printf(" stereo3d: %s val=%d\n", got ? "found" : "missing",
+ got ? *(int32_t *)got->data : -1);
+ got = av_frame_side_data_get(set.sd, set.nb_sd,
+ AV_FRAME_DATA_MASTERING_DISPLAY_METADATA);
+ printf(" mastering (absent): %s\n", got ? "FAIL" : "null");
+ }
+
+ /* remove by type clears all matching entries (including duplicates). */
+ printf("\nTesting av_frame_side_data_remove()\n");
+ av_frame_side_data_remove(&set.sd, &set.nb_sd,
+ AV_FRAME_DATA_SEI_UNREGISTERED);
+ print_set(" after remove SEI_UNREGISTERED", &set);
+
+ /* remove_by_props: PROP_GLOBAL drops STEREO3D and SPHERICAL. */
+ printf("\nTesting av_frame_side_data_remove_by_props()\n");
+ av_frame_side_data_remove_by_props(&set.sd, &set.nb_sd,
+ AV_SIDE_DATA_PROP_GLOBAL);
+ print_set(" after remove_by_props(GLOBAL)", &set);
+
+ /* Clone: copy remaining entry into a second set. */
+ printf("\nTesting av_frame_side_data_clone()\n");
+ Set dst = { 0 };
+ if (set.nb_sd > 0) {
+ int ret = av_frame_side_data_clone(&dst.sd, &dst.nb_sd,
+ set.sd[0], 0);
+ printf(" clone into empty: ret=%d nb=%d val=%d\n", ret, dst.nb_sd,
+ dst.nb_sd ? *(int32_t *)dst.sd[0]->data : -1);
+
+ /* Same type again without REPLACE must fail with EEXIST. */
+ ret = av_frame_side_data_clone(&dst.sd, &dst.nb_sd, set.sd[0], 0);
+ printf(" clone duplicate no REPLACE: ret==AVERROR(EEXIST)=%s\n",
+ ret == AVERROR(EEXIST) ? "yes" : "no");
+
+ /* With REPLACE, clone replaces the existing entry in place. */
+ ret = av_frame_side_data_clone(&dst.sd, &dst.nb_sd, set.sd[0],
+ AV_FRAME_SIDE_DATA_FLAG_REPLACE);
+ printf(" clone REPLACE: ret=%d nb=%d\n", ret, dst.nb_sd);
+
+ /* Invalid args. */
+ ret = av_frame_side_data_clone(NULL, &dst.nb_sd, set.sd[0], 0);
+ printf(" clone NULL sd: ret==AVERROR(EINVAL)=%s\n",
+ ret == AVERROR(EINVAL) ? "yes" : "no");
+ }
+
+ /* add: buffer ownership transfer and NEW_REF. */
+ printf("\nTesting av_frame_side_data_add()\n");
+ {
+ AVBufferRef *buf = av_buffer_allocz(sizeof(int32_t));
+ av_assert0(buf);
+ *(int32_t *)buf->data = 999;
+
+ AVFrameSideData *sd_added =
+ av_frame_side_data_add(&dst.sd, &dst.nb_sd,
+ AV_FRAME_DATA_DISPLAYMATRIX,
+ &buf, AV_FRAME_SIDE_DATA_FLAG_UNIQUE);
+ printf(" add (move): added=%s pbuf_nulled=%s\n",
+ sd_added ? "yes" : "no", buf == NULL ? "yes" : "no");
+
+ /* NEW_REF: the caller retains its reference. */
+ AVBufferRef *keep = av_buffer_allocz(sizeof(int32_t));
+ av_assert0(keep);
+ *(int32_t *)keep->data = 42;
+ sd_added = av_frame_side_data_add(&dst.sd, &dst.nb_sd,
+ AV_FRAME_DATA_MOTION_VECTORS, &keep,
+ AV_FRAME_SIDE_DATA_FLAG_NEW_REF);
+ printf(" add (NEW_REF): added=%s caller_ref_kept=%s\n",
+ sd_added ? "yes" : "no", keep ? "yes" : "no");
+ av_buffer_unref(&keep);
+
+ /* REPLACE on existing entry: same type, value swaps in place. */
+ AVBufferRef *repl = av_buffer_allocz(sizeof(int32_t));
+ av_assert0(repl);
+ *(int32_t *)repl->data = 1234;
+ sd_added = av_frame_side_data_add(&dst.sd, &dst.nb_sd,
+ AV_FRAME_DATA_DISPLAYMATRIX, &repl,
+ AV_FRAME_SIDE_DATA_FLAG_REPLACE);
+ {
+ const AVFrameSideData *after =
+ av_frame_side_data_get(dst.sd, dst.nb_sd,
+ AV_FRAME_DATA_DISPLAYMATRIX);
+ printf(" add (REPLACE existing): added=%s new_val=%d\n",
+ sd_added ? "yes" : "no",
+ after ? *(int32_t *)after->data : -1);
+ }
+ }
+
+ /* clone with UNIQUE: existing same-type entry is removed first. dst
+ * already has a set.sd[0]->type entry from the REPLACE clone above. */
+ printf("\nTesting av_frame_side_data_clone() UNIQUE\n");
+ if (set.nb_sd > 0) {
+ int before = dst.nb_sd;
+ int ret = av_frame_side_data_clone(&dst.sd, &dst.nb_sd, set.sd[0],
+ AV_FRAME_SIDE_DATA_FLAG_UNIQUE);
+ printf(" clone UNIQUE: ret=%d before=%d after=%d\n",
+ ret, before, dst.nb_sd);
+ }
+
+ print_set("\nFinal dst", &dst);
+
+ av_frame_side_data_free(&dst.sd, &dst.nb_sd);
+ av_frame_side_data_free(&set.sd, &set.nb_sd);
+ printf("\nfree: set_nb=%d dst_nb=%d\n", set.nb_sd, dst.nb_sd);
+
+ return 0;
+}
diff --git a/tests/fate/libavutil.mak b/tests/fate/libavutil.mak
index c5bdf7b763..73c0ad830f 100644
--- a/tests/fate/libavutil.mak
+++ b/tests/fate/libavutil.mak
@@ -196,6 +196,10 @@ FATE_LIBAVUTIL += fate-samplefmt
fate-samplefmt: libavutil/tests/samplefmt$(EXESUF)
fate-samplefmt: CMD = run libavutil/tests/samplefmt$(EXESUF)
+FATE_LIBAVUTIL += fate-side_data
+fate-side_data: libavutil/tests/side_data$(EXESUF)
+fate-side_data: CMD = run libavutil/tests/side_data$(EXESUF)
+
FATE_LIBAVUTIL += fate-side_data_array
fate-side_data_array: libavutil/tests/side_data_array$(EXESUF)
fate-side_data_array: CMD = run libavutil/tests/side_data_array$(EXESUF)
diff --git a/tests/ref/fate/side_data b/tests/ref/fate/side_data
new file mode 100644
index 0000000000..dcf9654bc3
--- /dev/null
+++ b/tests/ref/fate/side_data
@@ -0,0 +1,49 @@
+Testing av_frame_side_data_desc() / av_frame_side_data_name()
+ type=2 name_match=yes props=0x1
+ type=17 name_match=yes props=0x8
+ type=13 name_match=yes props=0x5
+ type=4 name_match=yes props=0x10
+ type=20 name_match=yes props=0x2
+ invalid type desc=null name=null
+
+Initial set: n=5
+ [0] Stereo 3D val=100
+ [1] HDR Dynamic Metadata SMPTE2094-40 (HDR10+) val=200
+ [2] H.26[45] User Data Unregistered SEI message val=1
+ [3] H.26[45] User Data Unregistered SEI message val=2
+ [4] Spherical Mapping val=300
+
+Testing av_frame_side_data_get()
+ stereo3d: found val=100
+ mastering (absent): null
+
+Testing av_frame_side_data_remove()
+ after remove SEI_UNREGISTERED: n=3
+ [0] Stereo 3D val=100
+ [1] HDR Dynamic Metadata SMPTE2094-40 (HDR10+) val=200
+ [2] Spherical Mapping val=300
+
+Testing av_frame_side_data_remove_by_props()
+ after remove_by_props(GLOBAL): n=1
+ [0] HDR Dynamic Metadata SMPTE2094-40 (HDR10+) val=200
+
+Testing av_frame_side_data_clone()
+ clone into empty: ret=0 nb=1 val=200
+ clone duplicate no REPLACE: ret==AVERROR(EEXIST)=yes
+ clone REPLACE: ret=0 nb=1
+ clone NULL sd: ret==AVERROR(EINVAL)=yes
+
+Testing av_frame_side_data_add()
+ add (move): added=yes pbuf_nulled=yes
+ add (NEW_REF): added=yes caller_ref_kept=yes
+ add (REPLACE existing): added=yes new_val=1234
+
+Testing av_frame_side_data_clone() UNIQUE
+ clone UNIQUE: ret=0 before=3 after=3
+
+Final dst: n=3
+ [0] Motion vectors val=42
+ [1] 3x3 displaymatrix val=1234
+ [2] HDR Dynamic Metadata SMPTE2094-40 (HDR10+) val=200
+
+free: set_nb=0 dst_nb=0
--
2.52.0
_______________________________________________
ffmpeg-devel mailing list -- [email protected]
To unsubscribe send an email to [email protected]