Commit 16d33f1f16 for ffmpeg

commit 16d33f1f160d94203d0a9685e73289e0e3027a88
Author: Michael Niedermayer <michael@niedermayer.cc>
Date:   Tue Sep 29 21:41:11 2026 +0000

    avformat/id3v2enc: initialize tag length in ff_id3v2_start

    Reset the length accumulator when starting a new tag, so the encoding
    context need not be zeroed by the caller and can be reused after finish.
    Existing callers already supply zeroed contexts, so their output is
    unchanged. Clarify that start initializes the encoding context.

    Add a sample-free FATE test for ID3v2.3 and ID3v2.4 with zeroed, reused,
    and explicitly nonzero length accumulators. Compare the declared tag
    length with the bytes written using a reference file.

    Addresses the internal initialization improvement requested in #24035;
    this does not change the public muxer lifecycle contract.

    Assisted-by: Fairy

diff --git a/libavformat/Makefile b/libavformat/Makefile
index 302df282cd..512e7ec214 100644
--- a/libavformat/Makefile
+++ b/libavformat/Makefile
@@ -801,6 +801,7 @@ TESTPROGS-$(CONFIG_HTTP_PROTOCOL)        += http
 TESTPROGS-$(CONFIG_NETWORK)              += noproxy
 TESTPROGS-$(CONFIG_SRTP)                 += srtp
 TESTPROGS-$(CONFIG_IMF_DEMUXER)          += imf
+TESTPROGS-$(CONFIG_MP3_MUXER)            += id3v2enc
 TESTPROGS-$(CONFIG_OGG_MUXER)            += ogg_pages

 TOOLS     = aviocat                                                     \
diff --git a/libavformat/id3v2.h b/libavformat/id3v2.h
index 77f4e6eb5b..c4a2a4f1a2 100644
--- a/libavformat/id3v2.h
+++ b/libavformat/id3v2.h
@@ -130,7 +130,7 @@ void ff_id3v2_read(AVFormatContext *s, const char *magic, ID3v2ExtraMeta **extra
                    unsigned int max_search_size);

 /**
- * Initialize an ID3v2 tag.
+ * Initialize the encoding context and write the header for a new ID3v2 tag.
  */
 void ff_id3v2_start(ID3v2EncContext *id3, AVIOContext *pb, int id3v2_version,
                     const char *magic);
diff --git a/libavformat/id3v2enc.c b/libavformat/id3v2enc.c
index 015a767202..9206af8837 100644
--- a/libavformat/id3v2enc.c
+++ b/libavformat/id3v2enc.c
@@ -350,6 +350,7 @@ void ff_id3v2_start(ID3v2EncContext *id3, AVIOContext *pb, int id3v2_version,
                     const char *magic)
 {
     id3->version = id3v2_version;
+    id3->len = 0;

     avio_wb32(pb, MKBETAG(magic[0], magic[1], magic[2], id3v2_version));
     avio_w8(pb, 0);
diff --git a/libavformat/tests/.gitignore b/libavformat/tests/.gitignore
index f7f9a868c7..0298cae0c9 100644
--- a/libavformat/tests/.gitignore
+++ b/libavformat/tests/.gitignore
@@ -1,4 +1,5 @@
 /id3v2
+/id3v2enc
 /fifo_muxer
 /imf
 /mkdir
diff --git a/libavformat/tests/id3v2enc.c b/libavformat/tests/id3v2enc.c
new file mode 100644
index 0000000000..fcce8977f9
--- /dev/null
+++ b/libavformat/tests/id3v2enc.c
@@ -0,0 +1,56 @@
+/*
+ * 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/mem.h"
+#include "libavformat/avio.h"
+#include "libavformat/id3v2.h"
+
+int main(void)
+{
+    for (int version = 3; version <= 4; version++) {
+        ID3v2EncContext id3 = { 0 };
+
+        for (int tag = 0; tag < 3; tag++) {
+            AVIOContext *pb;
+            uint8_t *buf;
+            int initial_len, size;
+
+            /* Test zeroed, reused, and explicitly nonzero contexts. */
+            if (tag == 2)
+                id3.len = 123;
+
+            if (avio_open_dyn_buf(&pb) < 0)
+                return 1;
+
+            ff_id3v2_start(&id3, pb, version, ID3v2_DEFAULT_MAGIC);
+            initial_len = id3.len;
+            ff_id3v2_finish(&id3, pb, 10);
+            size = avio_close_dyn_buf(pb, &buf);
+            if (size < ID3v2_HEADER_SIZE) {
+                av_free(buf);
+                return 1;
+            }
+            printf("version=%d tag=%d initial_len=%d declared=%d actual=%d\n",
+                   version, tag, initial_len, ff_id3v2_tag_len(buf), size);
+            av_free(buf);
+        }
+    }
+    return 0;
+}
diff --git a/tests/fate/libavformat.mak b/tests/fate/libavformat.mak
index f306e19245..9b6d059650 100644
--- a/tests/fate/libavformat.mak
+++ b/tests/fate/libavformat.mak
@@ -2,6 +2,10 @@
 #fate-async: libavformat/tests/async$(EXESUF)
 #fate-async: CMD = run libavformat/tests/async

+FATE_LIBAVFORMAT-$(CONFIG_MP3_MUXER) += fate-id3v2enc
+fate-id3v2enc: libavformat/tests/id3v2enc$(EXESUF)
+fate-id3v2enc: CMD = run libavformat/tests/id3v2enc$(EXESUF)
+
 FATE_LIBAVFORMAT += fate-mkdir
 fate-mkdir: libavformat/tests/mkdir$(EXESUF)
 fate-mkdir: CMD = run libavformat/tests/mkdir$(EXESUF)
diff --git a/tests/ref/fate/id3v2enc b/tests/ref/fate/id3v2enc
new file mode 100644
index 0000000000..ec4f789226
--- /dev/null
+++ b/tests/ref/fate/id3v2enc
@@ -0,0 +1,6 @@
+version=3 tag=0 initial_len=0 declared=20 actual=20
+version=3 tag=1 initial_len=0 declared=20 actual=20
+version=3 tag=2 initial_len=0 declared=20 actual=20
+version=4 tag=0 initial_len=0 declared=20 actual=20
+version=4 tag=1 initial_len=0 declared=20 actual=20
+version=4 tag=2 initial_len=0 declared=20 actual=20