PR #24084 opened by OursCodeur URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/24084 Patch URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/24084.patch
Opening a canonical PCM wav runs a content probe over the early packet data before the stream is finalized; debug log on any pcm_s16le file: ``` [wav @ ...] Probe with size=8192, packets=2470 detected mp3 with score=1 [wav @ ...] probed stream 0 ``` The mp3 "detection" scores 1 (the junk floor), and the stream settles on exactly what the fmt chunk already said. handle_stream_probing() requests this for PCM_S16LE deliberately, and for mislabelled files it is crucial, so this PR does not change the default. It adds an opt-out for callers that trust their container: - ~3.3ms of CPU per open on an i7-13700KF, on the calling thread, inside the first av_read_frame calls. - For a game engine, effect sounds are (often) small wavs opened and (also often) decoded synchronously at play time, dozens per minute; 3.3ms is a large budget if you want to hold 60+ FPS, paid even when every file is canonical PCM. - Found it while validating storage-latency work in OpenMW (https://gitlab.com/OpenMW/openmw/-/merge_requests/5488 and follow-ups); it reproduces from page cache and through bare ffprobe. Validation (patched ffprobe): - default + honest pcm_s16le: probe runs, stream pcm_s16le (unchanged) - `-stream_probe 0` + accurate pcm: no probe, stream pcm_s16le - default + crafted mislabelled file (PCM tag, mp3 data): mp3 detected (unchanged) - `-stream_probe 0` + mislabelled: header trusted, reports pcm_s16le (as intended if you opt out) >From 192cff0bdc66f075620ac810de5b34bc61362b32 Mon Sep 17 00:00:00 2001 From: Thomas Portal <[email protected]> Date: Tue, 11 Aug 2026 19:21:28 +0200 Subject: [PATCH] avformat/wavdec: add stream_probe option to allow skipping content probing The wav demuxer requests a codec probe of the packet data for PCM_S16LE streams to catch mislabelled files (mp3 data carrying a PCM codec tag). The probe runs the format probe battery over the first packets on every open: ~3.3ms of CPU on an i7-13700KF, on the calling thread, and proportionally more on weaker machines. Callers that open many known-good files at interactive latency (a game engine decoding its own effect assets at play time) have no way to skip it. Add a stream_probe bool option next to ignore_length, default on, so such callers can opt out and trust the header. Covers the w64 demuxer through the shared options table. Default behavior is unchanged. Signed-off-by: Thomas Portal <[email protected]> --- doc/demuxers.texi | 8 ++++++++ libavformat/version.h | 2 +- libavformat/wavdec.c | 12 +++++++++--- 3 files changed, 18 insertions(+), 4 deletions(-) diff --git a/doc/demuxers.texi b/doc/demuxers.texi index a1dd879b59..ca7b567ae3 100644 --- a/doc/demuxers.texi +++ b/doc/demuxers.texi @@ -1172,6 +1172,9 @@ This demuxer accepts the following options: @table @option @item max_size See the same option for the @ref{wav} demuxer. + +@item stream_probe +See the same option for the @ref{wav} demuxer. @end table @anchor{wav} @@ -1191,6 +1194,11 @@ Default is disabled. Specify the maximum packet size in bytes for the demuxed packets. By default this is set to 0, which means that a sensible value is chosen based on the input format. + +@item stream_probe @var{bool} +Probe the packet content of PCM streams to detect mislabelled files (mp3 data +carrying a PCM codec tag). Disabling skips this check and trusts the header, +saving the per-open CPU cost on known-good files. Default is enabled. @end table @section webp diff --git a/libavformat/version.h b/libavformat/version.h index 384cbd49cc..fab766fccd 100644 --- a/libavformat/version.h +++ b/libavformat/version.h @@ -32,7 +32,7 @@ #include "version_major.h" #define LIBAVFORMAT_VERSION_MINOR 5 -#define LIBAVFORMAT_VERSION_MICRO 101 +#define LIBAVFORMAT_VERSION_MICRO 102 #define LIBAVFORMAT_VERSION_INT AV_VERSION_INT(LIBAVFORMAT_VERSION_MAJOR, \ LIBAVFORMAT_VERSION_MINOR, \ diff --git a/libavformat/wavdec.c b/libavformat/wavdec.c index 8501d94deb..718550406d 100644 --- a/libavformat/wavdec.c +++ b/libavformat/wavdec.c @@ -62,6 +62,7 @@ typedef struct WAVDemuxContext { int audio_eof; int ignore_length; int max_size; + int stream_probe; int spdif; int smv_given_first; int unaligned; // e.g. if an odd number of bytes ID3 tag was prepended @@ -76,6 +77,7 @@ static const AVOption demux_options[] = { { "ignore_length", "Ignore length", OFFSET(ignore_length), AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, DEC }, #endif { "max_size", "max size of single packet", OFFSET(max_size), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, 1 << 22, DEC }, + { "stream_probe", "probe packet content for codecs the header cannot vouch for", OFFSET(stream_probe), AV_OPT_TYPE_BOOL, { .i64 = 1 }, 0, 1, DEC }, { NULL }, }; @@ -177,8 +179,12 @@ static int wav_probe(const AVProbeData *p) return 0; } -static void handle_stream_probing(AVStream *st) +static void handle_stream_probing(AVFormatContext *s, AVStream *st) { + WAVDemuxContext *wav = s->priv_data; + + if (!wav->stream_probe) + return; if (st->codecpar->codec_id == AV_CODEC_ID_PCM_S16LE) { FFStream *const sti = ffstream(st); sti->request_probe = AVPROBE_SCORE_EXTENSION + 1; @@ -196,7 +202,7 @@ static int wav_parse_fmt_tag(AVFormatContext *s, int64_t size, AVStream *st) ret = ff_get_wav_header(s, pb, st->codecpar, size, wav->rifx); if (ret < 0) return ret; - handle_stream_probing(st); + handle_stream_probing(s, st); ffstream(st)->need_parsing = AVSTREAM_PARSE_FULL_RAW; @@ -1007,7 +1013,7 @@ static int w64_read_header(AVFormatContext *s) ff_metadata_conv_ctx(s, NULL, wav_metadata_conv); ff_metadata_conv_ctx(s, NULL, ff_riff_info_conv); - handle_stream_probing(st); + handle_stream_probing(s, st); ffstream(st)->need_parsing = AVSTREAM_PARSE_FULL_RAW; avio_seek(pb, data_ofs, SEEK_SET); -- 2.52.0 _______________________________________________ ffmpeg-devel mailing list -- [email protected] To unsubscribe send an email to [email protected]
