Subject: [PATCH] avformat/demux: avoid unconditional ID3v2 tag consumption
From: nilfm <[email-masked]>
To: [email-masked]
Date: Wed, 2 Jul 2025 14:37:24 -0400

ID3v2 headers are now only parsed for formats that explicitly support them,
avoiding premature data consumption that broke demuxing in other formats.

Introduces AVFMT_FLAG_ID3V2_AUTO and applies it to mp3, aac, tta, and wav.

Signed-off-by: nilfm <[email-masked]>
---
 libavformat/aacdec.c   | 2 +-
 libavformat/avformat.h | 1 +
 libavformat/demux.c    | 9 ++++++---
 libavformat/mp3dec.c   | 2 +-
 libavformat/tta.c      | 1 +
 libavformat/wavdec.c   | 2 +-
 6 files changed, 11 insertions(+), 6 deletions(-)

diff --git a/libavformat/aacdec.c b/libavformat/aacdec.c
index 0b4bd69dd2..21d63ce189 100644
--- a/libavformat/aacdec.c
+++ b/libavformat/aacdec.c
@@ -208,7 +208,7 @@ retry:
 const FFInputFormat ff_aac_demuxer = {
     .p.name       = "aac",
     .p.long_name  = NULL_IF_CONFIG_SMALL("raw ADTS AAC (Advanced Audio Coding)"),
-    .p.flags      = AVFMT_GENERIC_INDEX,
+    .p.flags      = AVFMT_GENERIC_INDEX | AVFMT_FLAG_ID3V2_AUTO,
     .p.extensions = "aac",
     .p.mime_type  = "audio/aac,audio/aacp,audio/x-aac",
     .read_probe   = adts_aac_probe,
diff --git a/libavformat/avformat.h b/libavformat/avformat.h
index b6c63e2237..6fe4559a6f 100644
--- a/libavformat/avformat.h
+++ b/libavformat/avformat.h
@@ -1435,6 +1435,7 @@ typedef struct AVFormatContext {
 #define AVFMT_FLAG_SORT_DTS    0x10000 ///< try to interleave outputted packets by dts (using this flag can slow demuxing down)
 #define AVFMT_FLAG_FAST_SEEK   0x80000 ///< Enable fast, but inaccurate seeks for some formats
 #define AVFMT_FLAG_AUTO_BSF   0x200000 ///< Add bitstream filters as requested by the muxer
+#define AVFMT_FLAG_ID3V2_AUTO 0x400000 ///< Automatically parse ID3v2 metadata
 
     /**
      * Maximum number of bytes read from input in order to determine stream
diff --git a/libavformat/demux.c b/libavformat/demux.c
index 3749ab67a3..dfc146d9c4 100644
--- a/libavformat/demux.c
+++ b/libavformat/demux.c
@@ -214,6 +214,10 @@ static int update_stream_avctx(AVFormatContext *s)
     return 0;
 }
 
+static av_always_inline int is_id3v2_format(const AVInputFormat *fmt) {
+    return fmt->flags & AVFMT_FLAG_ID3V2_AUTO;
+}
+
 int avformat_open_input(AVFormatContext **ps, const char *filename,
                         const AVInputFormat *fmt, AVDictionary **options)
 {
@@ -302,7 +306,7 @@ int avformat_open_input(AVFormatContext **ps, const char *filename,
     }
 
     /* e.g. AVFMT_NOFILE formats will not have an AVIOContext */
-    if (s->pb)
+    if (s->pb && is_id3v2_format(s->iformat))
         ff_id3v2_read_dict(s->pb, &si->id3v2_meta, ID3v2_DEFAULT_MAGIC, &id3v2_extra_meta);
 
     if (ffifmt(s->iformat)->read_header)
@@ -321,8 +325,7 @@ int avformat_open_input(AVFormatContext **ps, const char *filename,
     }
 
     if (id3v2_extra_meta) {
-        if (!strcmp(s->iformat->name, "mp3") || !strcmp(s->iformat->name, "aac") ||
-            !strcmp(s->iformat->name, "tta") || !strcmp(s->iformat->name, "wav")) {
+        if (is_id3v2_format(s->iformat)) {
             if ((ret = ff_id3v2_parse_apic(s, id3v2_extra_meta)) < 0)
                 goto close;
             if ((ret = ff_id3v2_parse_chapters(s, id3v2_extra_meta)) < 0)
diff --git a/libavformat/mp3dec.c b/libavformat/mp3dec.c
index 31eeb68ebb..9cfc711493 100644
--- a/libavformat/mp3dec.c
+++ b/libavformat/mp3dec.c
@@ -618,7 +618,7 @@ static const AVClass demuxer_class = {
 const FFInputFormat ff_mp3_demuxer = {
     .p.name         = "mp3",
     .p.long_name    = NULL_IF_CONFIG_SMALL("MP2/3 (MPEG audio layer 2/3)"),
-    .p.flags        = AVFMT_GENERIC_INDEX,
+    .p.flags        = AVFMT_GENERIC_INDEX | AVFMT_FLAG_ID3V2_AUTO,
     .p.extensions   = "mp2,mp3,m2a,mpa", /* XXX: use probe */
     .p.priv_class   = &demuxer_class,
     .p.mime_type    = "audio/mpeg",
diff --git a/libavformat/tta.c b/libavformat/tta.c
index fdc18216c8..26335202c7 100644
--- a/libavformat/tta.c
+++ b/libavformat/tta.c
@@ -191,6 +191,7 @@ static int tta_read_seek(AVFormatContext *s, int stream_index, int64_t timestamp
 const FFInputFormat ff_tta_demuxer = {
     .p.name         = "tta",
     .p.long_name    = NULL_IF_CONFIG_SMALL("TTA (True Audio)"),
+    .p.flags        = AVFMT_FLAG_ID3V2_AUTO,
     .p.extensions   = "tta",
     .priv_data_size = sizeof(TTAContext),
     .read_probe     = tta_probe,
diff --git a/libavformat/wavdec.c b/libavformat/wavdec.c
index c65e0a2723..4c47a5f05c 100644
--- a/libavformat/wavdec.c
+++ b/libavformat/wavdec.c
@@ -1011,7 +1011,7 @@ static const AVClass w64_demuxer_class = {
 const FFInputFormat ff_w64_demuxer = {
     .p.name         = "w64",
     .p.long_name    = NULL_IF_CONFIG_SMALL("Sony Wave64"),
-    .p.flags        = AVFMT_GENERIC_INDEX,
+    .p.flags        = AVFMT_GENERIC_INDEX | AVFMT_FLAG_ID3V2_AUTO,
     .p.codec_tag    = ff_wav_codec_tags_list,
     .p.priv_class   = &w64_demuxer_class,
     .priv_data_size = sizeof(WAVDemuxContext),
-- 
2.47.1