[PR] avformat/wavdec: add stream_probe option to allow skipping content probing (PR #24084)

OursCodeur via ffmpeg-devel <[email protected]>
Newsgroups gmane.comp.video.ffmpeg.devel
Message-ID <178647077031.59.14078402916723771427@29965ddac10e>
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]
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.