* [FFmpeg-devel] [PATCH] avformat/demux: avoid unconditional ID3v2 tag consumption
@ 2025-07-08 22:07 Nil Fons Miret via ffmpeg-devel
0 siblings, 0 replies; only message in thread
From: Nil Fons Miret via ffmpeg-devel @ 2025-07-08 22:07 UTC (permalink / raw)
To: FFmpeg development discussions and patches; +Cc: Nil Fons Miret, Kyle Swanson
[-- Attachment #1: Type: message/rfc822, Size: 9887 bytes --]
[-- Attachment #1.1.1: Type: text/plain, Size: 1478 bytes --]
I have found a few instances of rawvideo files that coincidentally
start with a header matching the ID3v2 header format, and ffmpeg
consumes the header before demuxing, causing a decoding error. This
happens even if "-f rawvideo" is specified.
This patch limits the formats for which this automatic consuming of
ID3v2 headers is done. Note: I am not an expert in ID3v2 by any means,
and I'm happy to accept suggestions, both on the scope and mechanism
of the solution.
To reproduce, you can easily generate an example as follows:
```
( echo "49 44 33 2B 98 3A 49 44 33 2B 98 3A 6A 44 54 2B" | xxd -r -p
dd if=/dev/zero bs=1 count=115184 2>/dev/null
) > id3v2_320x240_yuv420p.yuv
ffmpeg -f rawvideo -s 320x240 -pix_fmt yuv420p -i
id3v2_320x240_yuv420p.yuv -f null -
```
This generates a 320x240 yuv420p file with a 16-byte header that
matches the ID3v2 header format (this was copied from a real case I
saw) and completed with zeroes for a total of 115200 bytes, the right
size for this resolution and pixel format.
On the master branch, I see:
```
[...]
ID3v2.43 tag skipped, cannot handle version
[...]
[out#0/null @ 0x15700a6e0] Output file is empty, nothing was
encoded(check -ss / -t / -frames parameters if used)
frame= 0 fps=0.0 q=0.0 Lsize=N/A time=N/A bitrate=N/A speed=N/A
```
After the changes: it decodes as expected.
```
[...]
frame= 1 fps=0.0 q=-0.0 Lsize=N/A time=00:00:00.04 bitrate=N/A
speed=16.5x elapsed=0:00:00.00
```
Thanks in advance,
Nil
[-- Attachment #1.1.2: 0001-avformat-demux-avoid-unconditional-ID3v2-tag-consumpti.eml --]
[-- Type: message/rfc822, Size: 5071 bytes --]
From: nilfm <nil.fons@gmail.com>
To: ffmpeg-devel@ffmpeg.org
Subject: [PATCH] avformat/demux: avoid unconditional ID3v2 tag consumption
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 <nil.fons@gmail.com>
---
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
[-- Attachment #2: Type: text/plain, Size: 251 bytes --]
_______________________________________________
ffmpeg-devel mailing list
ffmpeg-devel@ffmpeg.org
https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
To unsubscribe, visit link above, or email
ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".
^ permalink raw reply [flat|nested] only message in thread
only message in thread, other threads:[~2025-07-08 22:07 UTC | newest]
Thread overview: (only message) (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2025-07-08 22:07 [FFmpeg-devel] [PATCH] avformat/demux: avoid unconditional ID3v2 tag consumption Nil Fons Miret via ffmpeg-devel
Git Inbox Mirror of the ffmpeg-devel mailing list - see https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
This inbox may be cloned and mirrored by anyone:
git clone --mirror https://master.gitmailbox.com/ffmpegdev/0 ffmpegdev/git/0.git
# If you have public-inbox 1.1+ installed, you may
# initialize and index your mirror using the following commands:
public-inbox-init -V2 ffmpegdev ffmpegdev/ https://master.gitmailbox.com/ffmpegdev \
ffmpegdev@gitmailbox.com
public-inbox-index ffmpegdev
Example config snippet for mirrors.
AGPL code for this site: git clone https://public-inbox.org/public-inbox.git