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]

Reply via email to