This is an automated email from the git hooks/post-receive script.

Git pushed a commit to branch master
in repository ffmpeg.

commit 17c109e8d94d19976dc2f5db9c3577b3f831df43
Author:     marcos ashton <[email protected]>
AuthorDate: Sat Apr 18 14:30:39 2026 +0100
Commit:     michaelni <[email protected]>
CommitDate: Thu Aug 27 10:54:38 2026 +0000

    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/.gitignore  |   1 +
 libavutil/tests/side_data.c | 328 ++++++++++++++++++++++++++++++++++++++++++++
 tests/fate/libavutil.mak    |   4 +
 tests/ref/fate/side_data    |  60 ++++++++
 6 files changed, 396 insertions(+)

diff --git a/.forgejo/CODEOWNERS b/.forgejo/CODEOWNERS
index 3e55f000d7..325cd43aac 100644
--- a/.forgejo/CODEOWNERS
+++ b/.forgejo/CODEOWNERS
@@ -242,6 +242,7 @@ libavutil/tests/hdr_dynamic_metadata.* @MarcosAsh
 libavutil/tests/hdr_dynamic_vivid_metadata.* @MarcosAsh
 libavutil/tests/mastering_display_metadata.* @MarcosAsh
 libavutil/tests/pixdesc.* @MarcosAsh
+libavutil/tests/side_data.* @MarcosAsh
 libavutil/tests/tdrdi.* @MarcosAsh
 libavutil/tests/timestamp.* @MarcosAsh
 libavutil/tests/video_hint.* @MarcosAsh
@@ -255,6 +256,7 @@ tests/ref/fate/hdr_dynamic_metadata @MarcosAsh
 tests/ref/fate/hdr_dynamic_vivid_metadata @MarcosAsh
 tests/ref/fate/mastering_display_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 a1170af6eb..92174fa29e 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/.gitignore b/libavutil/tests/.gitignore
index 6e69431bca..ac30059249 100644
--- a/libavutil/tests/.gitignore
+++ b/libavutil/tests/.gitignore
@@ -58,6 +58,7 @@
 /samplefmt
 /sha
 /sha512
+/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..3900cb9587
--- /dev/null
+++ b/libavutil/tests/side_data.c
@@ -0,0 +1,328 @@
+/*
+ * 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 <string.h>
+
+#include "libavutil/avassert.h"
+#include "libavutil/buffer.h"
+#include "libavutil/dict.h"
+#include "libavutil/display.h"
+#include "libavutil/downmix_info.h"
+#include "libavutil/error.h"
+#include "libavutil/frame.h"
+#include "libavutil/macros.h"
+#include "libavutil/mem.h"
+#include "libavutil/motion_vector.h"
+#include "libavutil/spherical.h"
+#include "libavutil/stereo3d.h"
+
+typedef struct Set {
+    AVFrameSideData **sd;
+    int            nb_sd;
+} Set;
+
+/* Every entry below carries a payload laid out as AVFrameSideDataType
+ * documents for its type: a producer owes that layout even when no consumer
+ * reads the buffer back. The small number that identifies an entry in the
+ * reference output therefore lives in the metadata dictionary, which
+ * av_frame_side_data_clone() copies along with the buffer. */
+static void set_marker(AVFrameSideData *sd, int marker)
+{
+    char value[16];
+
+    snprintf(value, sizeof(value), "%d", marker);
+    av_assert0(av_dict_set(&sd->metadata, "marker", value, 0) >= 0);
+}
+
+static AVFrameSideData *new_entry(Set *s, enum AVFrameSideDataType type,
+                                  const void *payload, size_t size, int marker)
+{
+    AVFrameSideData *sd = av_frame_side_data_new(&s->sd, &s->nb_sd, type,
+                                                 size, 0);
+
+    av_assert0(sd && sd->size == size);
+    memcpy(sd->data, payload, size);
+    set_marker(sd, marker);
+    return sd;
+}
+
+static AVBufferRef *payload_buffer(const void *payload, size_t size)
+{
+    AVBufferRef *buf = av_buffer_alloc(size);
+
+    av_assert0(buf);
+    memcpy(buf->data, payload, size);
+    return buf;
+}
+
+static const char *entry_marker(const AVFrameSideData *sd)
+{
+    const AVDictionaryEntry *e = av_dict_get(sd->metadata, "marker", NULL, 0);
+
+    return e ? e->value : "none";
+}
+
+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 marker=%s\n", i,
+               av_frame_side_data_name(s->sd[i]->type),
+               entry_marker(s->sd[i]));
+}
+
+/* Dumps a set by looking each type up instead of walking the array. Removal is
+ * documented to drop the matching entries, not to keep the survivors where
+ * they were, so printing by index would tie the reference to the current habit
+ * of moving the last entry into the freed slot. */
+static void print_lookup(const char *label, const Set *s,
+                         const enum AVFrameSideDataType *types, size_t 
nb_types)
+{
+    printf("%s: n=%d\n", label, s->nb_sd);
+    for (size_t i = 0; i < nb_types; i++) {
+        const AVFrameSideData *sd =
+            av_frame_side_data_get(s->sd, s->nb_sd, types[i]);
+        const char *name = av_frame_side_data_name(types[i]);
+
+        if (sd)
+            printf("    %s: marker=%s\n", name, entry_marker(sd));
+        else
+            printf("    %s: absent\n", name);
+    }
+}
+
+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 (size_t 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]);
+        /* Compare content: the API only promises a string identifying the
+         * type, not the descriptor's own pointer. */
+        printf("  type=%d name_match=%s props=0x%x\n", known[i],
+               d && n && !strcmp(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");
+    }
+
+    /* Payloads. The two structs are allocated through their public
+     * allocators because their sizes are not part of the public ABI. */
+    size_t stereo3d_size, spherical_size;
+    AVStereo3D         *stereo3d  = av_stereo3d_alloc_size(&stereo3d_size);
+    AVSphericalMapping *spherical = av_spherical_alloc(&spherical_size);
+    /* 16 bytes of uuid_iso_iec_11578 followed by user_data_payload_byte. */
+    uint8_t udu[16 + 8] = {
+        0x9a, 0x21, 0xf3, 0x4d, 0x6b, 0x1c, 0x47, 0x8e,
+        0xa5, 0x30, 0xc2, 0x7f, 0x11, 0x8d, 0x46, 0x02,
+        'p',  'a',  'y',  'l',  'o',  'a',  'd',  '1',
+    };
+    int32_t displaymatrix[9];
+    /* src_x = dst_x + motion_x / motion_scale, likewise for y. */
+    AVMotionVector mvs[2] = {
+        { .source = -1, .w = 16, .h = 16, .src_x = 32, .src_y = 48,
+          .dst_x = 40, .dst_y = 48, .motion_x = -8, .motion_y = 0,
+          .motion_scale = 1 },
+        { .source =  1, .w =  8, .h =  8, .src_x = 64, .src_y = 64,
+          .dst_x = 64, .dst_y = 72, .motion_x = 0, .motion_y = -8,
+          .motion_scale = 1 },
+    };
+    /* The mix levels are absolute linear scale factors, not dB. */
+    AVDownmixInfo downmix = {
+        .preferred_downmix_type  = AV_DOWNMIX_TYPE_LTRT,
+        .center_mix_level        = 0.5,
+        .center_mix_level_ltrt   = 0.625,
+        .surround_mix_level      = 0.25,
+        .surround_mix_level_ltrt = 0.375,
+        .lfe_mix_level           = 0.125,
+    };
+
+    av_assert0(stereo3d && spherical);
+
+    stereo3d->type        = AV_STEREO3D_SIDEBYSIDE;
+    stereo3d->view        = AV_STEREO3D_VIEW_PACKED;
+    stereo3d->primary_eye = AV_PRIMARY_EYE_LEFT;
+    stereo3d->baseline    = 65000;
+
+    spherical->projection = AV_SPHERICAL_EQUIRECTANGULAR;
+    spherical->yaw        = 90 << 16;
+
+    av_display_rotation_set(displaymatrix, 90.0);
+
+    /* Populate a set with several types, one of them MULTI. */
+    Set set = { 0 };
+    new_entry(&set, AV_FRAME_DATA_STEREO3D, stereo3d, stereo3d_size, 100);
+    new_entry(&set, AV_FRAME_DATA_DOWNMIX_INFO, &downmix, sizeof(downmix),
+              200);
+    new_entry(&set, AV_FRAME_DATA_SEI_UNREGISTERED, udu, sizeof(udu), 1);
+    udu[sizeof(udu) - 1] = '2';
+    new_entry(&set, AV_FRAME_DATA_SEI_UNREGISTERED, udu, sizeof(udu), 2);
+    new_entry(&set, AV_FRAME_DATA_SPHERICAL, spherical, spherical_size, 300);
+    /* Entries are appended, so this dump is the insertion order by
+     * construction and shows both MULTI entries. */
+    print_set("\nInitial set", &set);
+
+    /* Types probed by every dump below. */
+    static const enum AVFrameSideDataType probed[] = {
+        AV_FRAME_DATA_STEREO3D,
+        AV_FRAME_DATA_DOWNMIX_INFO,
+        AV_FRAME_DATA_SEI_UNREGISTERED,
+        AV_FRAME_DATA_SPHERICAL,
+        AV_FRAME_DATA_DISPLAYMATRIX,
+        AV_FRAME_DATA_MOTION_VECTORS,
+    };
+
+    /* 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 marker=%s payload_match=%s\n",
+               got ? "found" : "missing", got ? entry_marker(got) : "none",
+               got && got->size == stereo3d_size &&
+               !memcmp(got->data, stereo3d, stereo3d_size) ? "yes" : "no");
+        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_lookup("  after remove SEI_UNREGISTERED", &set,
+                 probed, FF_ARRAY_ELEMS(probed));
+
+    /* 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_lookup("  after remove_by_props(GLOBAL)", &set,
+                 probed, FF_ARRAY_ELEMS(probed));
+
+    /* 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 marker=%s\n", ret, dst.nb_sd,
+               dst.nb_sd ? entry_marker(dst.sd[0]) : "none");
+
+        /* 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 = payload_buffer(displaymatrix, 
sizeof(displaymatrix));
+
+        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");
+        if (sd_added)
+            set_marker(sd_added, 999);
+
+        /* NEW_REF: the caller retains its reference. */
+        AVBufferRef *keep = payload_buffer(mvs, sizeof(mvs));
+        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 nb_mvs=%d\n",
+               sd_added ? "yes" : "no", keep ? "yes" : "no",
+               sd_added ? (int)(sd_added->size / sizeof(AVMotionVector)) : -1);
+        if (sd_added)
+            set_marker(sd_added, 42);
+        av_buffer_unref(&keep);
+
+        /* REPLACE on existing entry: same type, payload swaps in place. */
+        int32_t replacement[9];
+        av_display_rotation_set(replacement, 180.0);
+        AVBufferRef *repl = payload_buffer(replacement, sizeof(replacement));
+        sd_added = av_frame_side_data_add(&dst.sd, &dst.nb_sd,
+                                          AV_FRAME_DATA_DISPLAYMATRIX, &repl,
+                                          AV_FRAME_SIDE_DATA_FLAG_REPLACE);
+        if (sd_added)
+            set_marker(sd_added, 1234);
+        {
+            const AVFrameSideData *after =
+                av_frame_side_data_get(dst.sd, dst.nb_sd,
+                                       AV_FRAME_DATA_DISPLAYMATRIX);
+            printf("  add (REPLACE existing): added=%s marker=%s"
+                   " payload_match=%s\n",
+                   sd_added ? "yes" : "no",
+                   after ? entry_marker(after) : "none",
+                   after && after->size == sizeof(replacement) &&
+                   !memcmp(after->data, replacement, sizeof(replacement)) ?
+                   "yes" : "no");
+        }
+    }
+
+    /* 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_lookup("\nFinal dst", &dst, probed, FF_ARRAY_ELEMS(probed));
+
+    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);
+
+    av_free(stereo3d);
+    av_free(spherical);
+
+    return 0;
+}
diff --git a/tests/fate/libavutil.mak b/tests/fate/libavutil.mak
index 7796c21b7f..11c495d361 100644
--- a/tests/fate/libavutil.mak
+++ b/tests/fate/libavutil.mak
@@ -204,6 +204,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..5d0b230765
--- /dev/null
+++ b/tests/ref/fate/side_data
@@ -0,0 +1,60 @@
+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 marker=100
+  [1] Metadata relevant to a downmix procedure marker=200
+  [2] H.26[45] User Data Unregistered SEI message marker=1
+  [3] H.26[45] User Data Unregistered SEI message marker=2
+  [4] Spherical Mapping marker=300
+
+Testing av_frame_side_data_get()
+  stereo3d: found marker=100 payload_match=yes
+  mastering (absent): null
+
+Testing av_frame_side_data_remove()
+  after remove SEI_UNREGISTERED: n=3
+    Stereo 3D: marker=100
+    Metadata relevant to a downmix procedure: marker=200
+    H.26[45] User Data Unregistered SEI message: absent
+    Spherical Mapping: marker=300
+    3x3 displaymatrix: absent
+    Motion vectors: absent
+
+Testing av_frame_side_data_remove_by_props()
+  after remove_by_props(GLOBAL): n=1
+    Stereo 3D: absent
+    Metadata relevant to a downmix procedure: marker=200
+    H.26[45] User Data Unregistered SEI message: absent
+    Spherical Mapping: absent
+    3x3 displaymatrix: absent
+    Motion vectors: absent
+
+Testing av_frame_side_data_clone()
+  clone into empty: ret=0 nb=1 marker=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 nb_mvs=2
+  add (REPLACE existing): added=yes marker=1234 payload_match=yes
+
+Testing av_frame_side_data_clone() UNIQUE
+  clone UNIQUE: ret=0 before=3 after=3
+
+Final dst: n=3
+    Stereo 3D: absent
+    Metadata relevant to a downmix procedure: marker=200
+    H.26[45] User Data Unregistered SEI message: absent
+    Spherical Mapping: absent
+    3x3 displaymatrix: marker=1234
+    Motion vectors: marker=42
+
+free: set_nb=0 dst_nb=0

-- 
To stop receiving notification emails like this one, please contact
[email protected].
_______________________________________________
ffmpeg-cvslog mailing list -- [email protected]
To unsubscribe send an email to [email protected]

Reply via email to