PR #24889 opened by Kagejitsu URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/24889 Patch URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/24889.patch
### Problem `libavcodec/flac_parser.c` decides the input is junk while it is still synchronising (fewer than `FLAC_MIN_HEADERS` buffered) once the fifo holds more than `20 * FLAC_AVG_FRAME_SIZE` (160 KiB) per valid header found. That assumes small frames. The format allows frames far larger (65535 samples × 8 ch × 32 bit), and real encodes with large block sizes on hi-res or multichannel audio routinely exceed 160 KiB per frame. For those streams the parser silently discards valid data every time it has to (re)synchronise: * **Seeking fails.** `flac_read_timestamp()` gets no frame (`read_timestamp() failed in the middle`); with `-fflags +fastseek` the SEEKTABLE lands on the right byte but nothing is output until EOF. In mpv this looks like the player quitting with *End of file* the moment you seek. * **Files whose first frames are already over the limit do not decode at all**, with no error message. libFLAC decodes the same files fine, and so does this parser once it has synchronised (play from the start of a file whose first frames are small, and the big frames later on decode without complaint), so only the heuristic is at fault. The check was added in b18346817d with a factor of 10 and raised to 20 in b0b3676e13 for a valid file that tripped it (trac #5428). Tying it to the stream's own STREAMINFO fixes the class rather than moving the threshold again. ### Reproduction (reference encoder, no external files) ```sh ffmpeg -f lavfi -i "anoisesrc=d=30:c=white:r=48000:a=1" -ac 2 -c:a pcm_s24le n24.wav flac --lax -b 65535 -o n24.flac n24.wav # ~195 KiB frames flac -t n24.flac # ok ffmpeg -i n24.flac -f null - # plays (5625KiB) ffmpeg -ss 10 -i n24.flac -f null - # before: 0 bytes, "could not seek to position 10.000" ffmpeg -fflags +fastseek -ss 10 -i n24.flac -f null - # before: 0 bytes, silent ffmpeg -f lavfi -i "anoisesrc=d=30:c=white:r=48000:a=1" -ac 2 -c:a pcm_s32le n32.wav flac --lax -b 65535 -o n32.flac n32.wav # ~262 KiB frames ffmpeg -i n32.flac -f null - # before: 0 bytes decoded, no error ``` Measured on a real 6-channel 24-bit/48 kHz recording re-encoded at fixed block sizes: 20480 (max frame 220 KiB) seeks, 24576 (264 KiB) and above fail. ### Fix Read `max_framesize` from the STREAMINFO block in `extradata` (falling back to a bound from max block size × channels × bytes per sample when the encoder left it at 0) and use `FFMAX(FLAC_AVG_FRAME_SIZE, max_framesize)` as the per-header allowance. Worst-case buffering on non-FLAC input stays bounded by `20 * max_framesize` per buffered header. Also log once (warning) when data is discarded as junk, since the silent discard made this very hard to diagnose. ### Testing | file | start | default seek | `+fastseek` seek | |---|---|---|---| | synthetic 24-bit, b=65535 | 5625 KiB → 5625 KiB | 0 → 3750 KiB | 0 → 3750 KiB | | synthetic 32-bit, b=65535 | 0 → 5625 KiB | 0 → 3750 KiB | 0 → 3750 KiB | | real 6ch/24/48, variable blocksize (flaccid) | ok | 0 → 99110 KiB | 0 → 99110 KiB | | real 2ch/24/96, variable blocksize (flaccid) | ok | 0 → 45075 KiB | ok | | control: 2ch/16/44.1, b=4096 | ok | ok | ok | Random data after a valid STREAMINFO is still rejected immediately (now with one warning line). `fate-acodec-flac`, `fate-acodec-flac-exact-rice`, `fate-api-flac`, `fate-checkasm-flacdsp`, `fate-seek-acodec-flac` pass. `tools/patcheck` clean. From 26722f392f768859599077ea0ed7bcd743ac2774 Mon Sep 17 00:00:00 2001 From: Kagejitsu <[email protected]> Date: Mon, 5 Oct 2026 11:46:19 -0500 Subject: [PATCH] avcodec/flac_parser: scale junk detection by the stream's max frame size While synchronising (fewer than FLAC_MIN_HEADERS buffered) the parser treats the fifo as junk once it holds more than 20 * FLAC_AVG_FRAME_SIZE (160 KiB) per valid header found. That assumes frames are small; the format allows frames far larger (up to 65535 samples x 8 channels x 32 bit), and real encodes with large block sizes on high sample rate, high bit depth or multichannel audio routinely exceed 160 KiB per frame. For such streams the parser discards valid data every time it has to (re)synchronise: - seeking fails: read_timestamp() gets no frame ("read_timestamp() failed in the middle"), and with -fflags +fastseek the seektable lands on the right byte but nothing is output until EOF; - a file whose first frames are already over the limit does not decode at all, with no error message. libFLAC decodes the same files fine, and so does this parser once it has synchronised, so only the heuristic is at fault. Use the stream's own maximum frame size from the STREAMINFO block in extradata as the per-header allowance instead of the fixed estimate (falling back to a bound derived from max block size, channels and bit depth when the encoder left max_framesize at 0). The worst-case buffer growth on non-FLAC input stays bounded by 20 * max_framesize per buffered header. Also log once when data is discarded as junk, since the silent discard made this very hard to diagnose. The check was added in b18346817d with a factor of 10 and raised to 20 in b0b3676e13 for a valid file that tripped it (trac #5428); tying it to STREAMINFO fixes the whole class instead of moving the threshold again. Reproduce with the reference encoder: ffmpeg -f lavfi -i "anoisesrc=d=30:c=white:r=48000:a=1" -ac 2 \ -c:a pcm_s24le n24.wav flac --lax -b 65535 -o n24.flac n24.wav ffmpeg -ss 10 -i n24.flac -f null - # before: 0 bytes decoded Co-Authored-By: Claude Fable 5.1 <[email protected]> --- libavcodec/flac_parser.c | 62 +++++++++++++++++++++++++++++++++++++--- 1 file changed, 58 insertions(+), 4 deletions(-) diff --git a/libavcodec/flac_parser.c b/libavcodec/flac_parser.c index 9a5f5fd03a..3a836a1435 100644 --- a/libavcodec/flac_parser.c +++ b/libavcodec/flac_parser.c @@ -34,7 +34,9 @@ #include "libavutil/attributes.h" #include "libavutil/crc.h" +#include "libavutil/intreadwrite.h" #include "libavutil/mem.h" +#include "flac.h" #include "flac_parse.h" #include "parser_internal.h" @@ -98,8 +100,49 @@ typedef struct FLACParseContext { int wrap_buf_allocated_size; /**< actual allocated size of the buffer */ FLACFrameInfo last_fi; /**< last decoded frame header info */ int last_fi_valid; /**< set if last_fi is valid */ + int max_framesize; /**< largest possible frame size, from + STREAMINFO; 0 if unknown */ + int streaminfo_checked; /**< set once extradata has been examined */ + int junk_reported; /**< set once junk has been reported */ } FLACParseContext; +/** + * Derive the largest frame size the stream can contain from the STREAMINFO + * block in extradata, if present. Used to scale the junk-detection + * threshold: without it, valid streams with frames larger than + * 20 * FLAC_AVG_FRAME_SIZE (high sample rate / bit depth / channel count + * with large block sizes) are mistaken for garbage whenever the parser has + * to (re)synchronise, e.g. after a seek. + */ +static void read_streaminfo_max_framesize(FLACParseContext *fpc, + AVCodecContext *avctx) +{ + const uint8_t *si = avctx->extradata; + int size = avctx->extradata_size; + int max_framesize; + + fpc->streaminfo_checked = 1; + if (!si || size < FLAC_STREAMINFO_SIZE) + return; + /* extradata may carry the full "fLaC" + block header prefix */ + if (AV_RL32(si) == MKTAG('f', 'L', 'a', 'C')) { + if (size < 8 + FLAC_STREAMINFO_SIZE) + return; + si += 8; + } + + max_framesize = AV_RB24(si + 7); + if (!max_framesize) { + /* unknown (e.g. live encode): bound it from the stream parameters */ + int max_blocksize = AV_RB16(si + 2); + int channels = ((si[12] >> 1) & 7) + 1; + int bps = (((si[12] & 1) << 4) | (si[13] >> 4)) + 1; + max_framesize = max_blocksize * channels * ((bps + 7) >> 3) + + MAX_FRAME_HEADER_SIZE + 2; + } + fpc->max_framesize = max_framesize; +} + static int frame_header_is_valid(AVCodecContext *avctx, const uint8_t *buf, FLACFrameInfo *fi) { @@ -701,6 +744,8 @@ static int flac_parse(AVCodecParserContext *s, AVCodecContext *avctx, } fpc->avctx = avctx; + if (!fpc->streaminfo_checked) + read_streaminfo_max_framesize(fpc, avctx); if (fpc->best_header_valid && fpc->nb_headers_buffered >= FLAC_MIN_HEADERS) return get_best_header(fpc, poutbuf, poutbuf_size); @@ -770,11 +815,20 @@ static int flac_parse(AVCodecParserContext *s, AVCodecContext *avctx, } if (!flac_fifo_space(&fpc->fifo_buf) && - flac_fifo_size(&fpc->fifo_buf) / FLAC_AVG_FRAME_SIZE > + flac_fifo_size(&fpc->fifo_buf) / + FFMAX(FLAC_AVG_FRAME_SIZE, fpc->max_framesize) > fpc->nb_headers_buffered * 20) { - /* There is less than one valid flac header buffered for 20 headers - * buffered. Therefore the fifo is most likely filled with invalid - * data and the input is not a flac file. */ + /* There is less than one valid flac header buffered for 20 + * frames' worth of data (using the stream's maximum frame size + * when STREAMINFO is available). Therefore the fifo is most + * likely filled with invalid data and the input is not a flac + * file. */ + if (!fpc->junk_reported) { + av_log(avctx, AV_LOG_WARNING, "no valid frame header in " + "%zu bytes, discarding as junk\n", + flac_fifo_size(&fpc->fifo_buf)); + fpc->junk_reported = 1; + } goto handle_error; } -- 2.52.0 _______________________________________________ ffmpeg-devel mailing list -- [email protected] To unsubscribe send an email to [email protected]
