PR #25643 opened by Romain Beauxis (toots)
URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/25643
Patch URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/25643.patch

This PR implements decoding of chained Ogg FLAC streams whose links change bit 
depth or block size, which the decoder currently rejects.

The Ogg demuxer passes each new link's STREAMINFO as AV_PKT_DATA_NEW_EXTRADATA, 
and the decoder applies it, handing it on to later frame threads through 
update_thread_context. The same change inside one stream, without a new 
STREAMINFO, is still rejected.



>From 56e1f796c1727de132191ab6aec45617e865e580 Mon Sep 17 00:00:00 2001
From: Romain Beauxis <[email protected]>
Date: Sat, 31 Oct 2026 09:57:15 -0500
Subject: [PATCH 1/6] fate/flac: add chained and mid-stream parameter change
 tests

The chained tests pipe two generated Ogg FLAC links into one decoder,
the second link changing the block size or bit depth.

The flac-midstream tests make the same changes inside one raw FLAC
stream without a new STREAMINFO, which is invalid and must stay
rejected, and each frame-threads variant expects the output of its
single-threaded test.

The references record what decoding gives today, for the following
commits to update as they fix each case.
---
 tests/fate/flac.mak                       | 36 ++++++++++++++++++++++-
 tests/fate/ogg-flac.mak                   | 30 ++++++++++++++++++-
 tests/ref/fate/flac-midstream-blocksize   | 26 ++++++++++++++++
 tests/ref/fate/flac-midstream-bps         | 26 ++++++++++++++++
 tests/ref/fate/ogg-flac-chained-blocksize | 27 +++++++++++++++++
 tests/ref/fate/ogg-flac-chained-bps       | 27 +++++++++++++++++
 6 files changed, 170 insertions(+), 2 deletions(-)
 create mode 100644 tests/ref/fate/flac-midstream-blocksize
 create mode 100644 tests/ref/fate/flac-midstream-bps
 create mode 100644 tests/ref/fate/ogg-flac-chained-blocksize
 create mode 100644 tests/ref/fate/ogg-flac-chained-bps

diff --git a/tests/fate/flac.mak b/tests/fate/flac.mak
index 62f2126d92d..01ab1d170fe 100644
--- a/tests/fate/flac.mak
+++ b/tests/fate/flac.mak
@@ -32,5 +32,39 @@ fate-flac-%: FUZZ = 0
 
 FATE_FLAC-$(call ENCMUX, FLAC, FLAC, PIPE_PROTOCOL) += $(FATE_FLAC)
 
+# One stream whose second half changes the block size or bit depth without a
+# new STREAMINFO, which is invalid: the decoder rejects those frames.
+#
+# The first half ends on a whole frame, since a short frame anywhere but last
+# gets a bogus timestamp from the parser.
+FLAC_MIDSTREAM_CHANGE = { ffmpeg -auto_conversion_filters -f lavfi -i 
sine=frequency=440:duration=0.5 \
+                            -af atrim=end_sample=21504 \
+                            -c:a flac -frame_size 1024 -flags +bitexact 
-fflags +bitexact -f flac - && \
+                          ffmpeg -auto_conversion_filters -f lavfi -i 
sine=frequency=660:duration=0.5 \
+                            -c:a flac $(1) -flags +bitexact -fflags +bitexact 
-f flac -write_header 0 -; } |
+
+FLAC_MIDSTREAM_BLOCKSIZE = $(call FLAC_MIDSTREAM_CHANGE,-frame_size 4096) 
framecrc -i -
+FLAC_MIDSTREAM_BPS = $(call FLAC_MIDSTREAM_CHANGE,-sample_fmt s32 
-bits_per_raw_sample 24) \
+    framecrc -auto_conversion_filters -i - -c:a pcm_s32le
+
+# Each frame-threaded variant expects the output of its single-threaded test.
+FATE_FLAC_MIDSTREAM-$(call TRANSCODE, FLAC, FLAC, LAVFI_INDEV SINE_FILTER 
ATRIM_FILTER FLAC_PARSER) += fate-flac-midstream-blocksize 
fate-flac-midstream-blocksize-frame-threads
+fate-flac-midstream-blocksize: CMD = $(FLAC_MIDSTREAM_BLOCKSIZE)
+fate-flac-midstream-blocksize-frame-threads: CMD = $(FLAC_MIDSTREAM_BLOCKSIZE)
+fate-flac-midstream-blocksize-frame-threads: REF = 
$(SRC_PATH)/tests/ref/fate/flac-midstream-blocksize
+fate-flac-midstream-blocksize-frame-threads: THREADS = 2
+fate-flac-midstream-blocksize-frame-threads: THREAD_TYPE = frame
+
+FATE_FLAC_MIDSTREAM-$(call TRANSCODE, FLAC, FLAC, LAVFI_INDEV SINE_FILTER 
ATRIM_FILTER FLAC_PARSER ARESAMPLE_FILTER PCM_S32LE_ENCODER) += 
fate-flac-midstream-bps fate-flac-midstream-bps-frame-threads
+fate-flac-midstream-bps: CMD = $(FLAC_MIDSTREAM_BPS)
+fate-flac-midstream-bps-frame-threads: CMD = $(FLAC_MIDSTREAM_BPS)
+fate-flac-midstream-bps-frame-threads: REF = 
$(SRC_PATH)/tests/ref/fate/flac-midstream-bps
+fate-flac-midstream-bps-frame-threads: THREADS = 2
+fate-flac-midstream-bps-frame-threads: THREAD_TYPE = frame
+
+$(FATE_FLAC_MIDSTREAM-yes): CMP = diff
+
+FATE_FFMPEG += $(FATE_FLAC_MIDSTREAM-yes)
+
 FATE_SAMPLES_AVCONV += $(FATE_FLAC-yes)
-fate-flac: $(FATE_FLAC)
+fate-flac: $(FATE_FLAC) $(FATE_FLAC_MIDSTREAM-yes)
diff --git a/tests/fate/ogg-flac.mak b/tests/fate/ogg-flac.mak
index 204ea3f32b4..a19b9324f8e 100644
--- a/tests/fate/ogg-flac.mak
+++ b/tests/fate/ogg-flac.mak
@@ -22,6 +22,32 @@ fate-ogg-flac-serial-offset: 
libavformat/tests/ogg_pages$(EXESUF)
 fate-ogg-flac-serial-offset: CMD = run_with_temp $(OGG_FLAC_SERIAL_MUX)" 
-serial_offset 67574 -f ogg -y" \
     "libavformat/tests/ogg_pages$(EXESUF)" ogg
 
+# Two links of tones piped as one chained stream, the second encoded with
+# options $(2) that the first, encoded with $(1), does not share.
+OGG_FLAC_CHAIN = { ffmpeg -auto_conversion_filters -f lavfi -i 
sine=frequency=440:duration=0.5 \
+                     -c:a flac $(1) -flags +bitexact -fflags +bitexact -f ogg 
- && \
+                   ffmpeg -auto_conversion_filters -f lavfi -i 
sine=frequency=660:duration=0.5 \
+                     -c:a flac $(2) -flags +bitexact -fflags +bitexact 
-serial_offset 1 -f ogg -; } |
+
+OGG_FLAC_CHAINED_BLOCKSIZE = $(call OGG_FLAC_CHAIN,-frame_size 
1024,-frame_size 4096) framecrc -i -
+OGG_FLAC_CHAINED_BPS = $(call OGG_FLAC_CHAIN,,-sample_fmt s32 
-bits_per_raw_sample 24) \
+    framecrc -auto_conversion_filters -i - -c:a pcm_s32le
+
+# Each frame-threaded variant expects the output of its single-threaded test.
+FATE_OGG_FLAC_CHAIN-$(call TRANSCODE, FLAC, OGG, LAVFI_INDEV SINE_FILTER 
FLAC_PARSER) += fate-ogg-flac-chained-blocksize 
fate-ogg-flac-chained-blocksize-frame-threads
+fate-ogg-flac-chained-blocksize: CMD = $(OGG_FLAC_CHAINED_BLOCKSIZE)
+fate-ogg-flac-chained-blocksize-frame-threads: CMD = 
$(OGG_FLAC_CHAINED_BLOCKSIZE)
+fate-ogg-flac-chained-blocksize-frame-threads: REF = 
$(SRC_PATH)/tests/ref/fate/ogg-flac-chained-blocksize
+fate-ogg-flac-chained-blocksize-frame-threads: THREADS = 2
+fate-ogg-flac-chained-blocksize-frame-threads: THREAD_TYPE = frame
+
+FATE_OGG_FLAC_CHAIN-$(call TRANSCODE, FLAC, OGG, LAVFI_INDEV SINE_FILTER 
FLAC_PARSER ARESAMPLE_FILTER PCM_S32LE_ENCODER) += fate-ogg-flac-chained-bps 
fate-ogg-flac-chained-bps-frame-threads
+fate-ogg-flac-chained-bps: CMD = $(OGG_FLAC_CHAINED_BPS)
+fate-ogg-flac-chained-bps-frame-threads: CMD = $(OGG_FLAC_CHAINED_BPS)
+fate-ogg-flac-chained-bps-frame-threads: REF = 
$(SRC_PATH)/tests/ref/fate/ogg-flac-chained-bps
+fate-ogg-flac-chained-bps-frame-threads: THREADS = 2
+fate-ogg-flac-chained-bps-frame-threads: THREAD_TYPE = frame
+
 FATE_OGG_FLAC-$(call DEMDEC, OGG, FLAC, FLAC_PARSER) += $(FATE_OGG_FLAC)
 
 FATE_SAMPLES_DUMP_STREAM_META += $(FATE_OGG_FLAC-yes)
@@ -30,4 +56,6 @@ FATE_EXTERN += $(FATE_OGG_FLAC-yes)
 
 FATE_SAMPLES_FFMPEG += $(FATE_OGG_COPY_FLAC-yes)
 
-fate-ogg-flac: $(FATE_OGG_FLAC-yes) $(FATE_OGG_COPY_FLAC-yes)
+FATE_FFMPEG += $(FATE_OGG_FLAC_CHAIN-yes)
+
+fate-ogg-flac: $(FATE_OGG_FLAC-yes) $(FATE_OGG_COPY_FLAC-yes) 
$(FATE_OGG_FLAC_CHAIN-yes)
diff --git a/tests/ref/fate/flac-midstream-blocksize 
b/tests/ref/fate/flac-midstream-blocksize
new file mode 100644
index 00000000000..036eaef687b
--- /dev/null
+++ b/tests/ref/fate/flac-midstream-blocksize
@@ -0,0 +1,26 @@
+#tb 0: 1/44100
+#media_type 0: audio
+#codec_id 0: pcm_s16le
+#sample_rate 0: 44100
+#channel_layout_name 0: mono
+0,          0,          0,     1024,     2048, 0x2096f45b
+0,       1024,       1024,     1024,     2048, 0x2262f6ec
+0,       2048,       2048,     1024,     2048, 0xaa83fe05
+0,       3072,       3072,     1024,     2048, 0x487e06b5
+0,       4096,       4096,     1024,     2048, 0xb0abfcca
+0,       5120,       5120,     1024,     2048, 0x869ef510
+0,       6144,       6144,     1024,     2048, 0x547cf717
+0,       7168,       7168,     1024,     2048, 0xca830826
+0,       8192,       8192,     1024,     2048, 0xf7700954
+0,       9216,       9216,     1024,     2048, 0x3759f55c
+0,      10240,      10240,     1024,     2048, 0x0ca9f7ee
+0,      11264,      11264,     1024,     2048, 0xfb78fe99
+0,      12288,      12288,     1024,     2048, 0x93580191
+0,      13312,      13312,     1024,     2048, 0x079f0797
+0,      14336,      14336,     1024,     2048, 0xcf5ff38b
+0,      15360,      15360,     1024,     2048, 0xb201f701
+0,      16384,      16384,     1024,     2048, 0x7aac0476
+0,      17408,      17408,     1024,     2048, 0xd89b0222
+0,      18432,      18432,     1024,     2048, 0x160b013e
+0,      19456,      19456,     1024,     2048, 0x950ef0eb
+0,      20480,      20480,     1024,     2048, 0x9b51fada
diff --git a/tests/ref/fate/flac-midstream-bps 
b/tests/ref/fate/flac-midstream-bps
new file mode 100644
index 00000000000..1e93bcfc818
--- /dev/null
+++ b/tests/ref/fate/flac-midstream-bps
@@ -0,0 +1,26 @@
+#tb 0: 1/44100
+#media_type 0: audio
+#codec_id 0: pcm_s32le
+#sample_rate 0: 44100
+#channel_layout_name 0: mono
+0,          0,          0,     1024,     4096, 0x4c38f45b
+0,       1024,       1024,     1024,     4096, 0x4b0cf6ec
+0,       2048,       2048,     1024,     4096, 0x59c4fe05
+0,       3072,       3072,     1024,     4096, 0x8b7006b5
+0,       4096,       4096,     1024,     4096, 0x6799fcca
+0,       5120,       5120,     1024,     4096, 0x1753f510
+0,       6144,       6144,     1024,     4096, 0xaf7ef717
+0,       7168,       7168,     1024,     4096, 0x8d0a0826
+0,       8192,       8192,     1024,     4096, 0xe4520954
+0,       9216,       9216,     1024,     4096, 0x7c69f55c
+0,      10240,      10240,     1024,     4096, 0x1db1f7ee
+0,      11264,      11264,     1024,     4096, 0xf6e4fe99
+0,      12288,      12288,     1024,     4096, 0x2a8c0191
+0,      13312,      13312,     1024,     4096, 0x05940797
+0,      14336,      14336,     1024,     4096, 0xabadf38b
+0,      15360,      15360,     1024,     4096, 0x6a0af701
+0,      16384,      16384,     1024,     4096, 0xf0fe0476
+0,      17408,      17408,     1024,     4096, 0xb3ec0222
+0,      18432,      18432,     1024,     4096, 0x27c0013e
+0,      19456,      19456,     1024,     4096, 0x3b94f0eb
+0,      20480,      20480,     1024,     4096, 0x3973fada
diff --git a/tests/ref/fate/ogg-flac-chained-blocksize 
b/tests/ref/fate/ogg-flac-chained-blocksize
new file mode 100644
index 00000000000..0fb6fa8292c
--- /dev/null
+++ b/tests/ref/fate/ogg-flac-chained-blocksize
@@ -0,0 +1,27 @@
+#tb 0: 1/44100
+#media_type 0: audio
+#codec_id 0: pcm_s16le
+#sample_rate 0: 44100
+#channel_layout_name 0: mono
+0,          0,          0,     1024,     2048, 0x2096f45b
+0,       1024,       1024,     1024,     2048, 0x2262f6ec
+0,       2048,       2048,     1024,     2048, 0xaa83fe05
+0,       3072,       3072,     1024,     2048, 0x487e06b5
+0,       4096,       4096,     1024,     2048, 0xb0abfcca
+0,       5120,       5120,     1024,     2048, 0x869ef510
+0,       6144,       6144,     1024,     2048, 0x547cf717
+0,       7168,       7168,     1024,     2048, 0xca830826
+0,       8192,       8192,     1024,     2048, 0xf7700954
+0,       9216,       9216,     1024,     2048, 0x3759f55c
+0,      10240,      10240,     1024,     2048, 0x0ca9f7ee
+0,      11264,      11264,     1024,     2048, 0xfb78fe99
+0,      12288,      12288,     1024,     2048, 0x93580191
+0,      13312,      13312,     1024,     2048, 0x079f0797
+0,      14336,      14336,     1024,     2048, 0xcf5ff38b
+0,      15360,      15360,     1024,     2048, 0xb201f701
+0,      16384,      16384,     1024,     2048, 0x7aac0476
+0,      17408,      17408,     1024,     2048, 0xd89b0222
+0,      18432,      18432,     1024,     2048, 0x160b013e
+0,      19456,      19456,     1024,     2048, 0x950ef0eb
+0,      20480,      20480,     1024,     2048, 0x9b51fada
+0,      21504,      21504,      546,     1092, 0x60af340b
diff --git a/tests/ref/fate/ogg-flac-chained-bps 
b/tests/ref/fate/ogg-flac-chained-bps
new file mode 100644
index 00000000000..291e7d9cf57
--- /dev/null
+++ b/tests/ref/fate/ogg-flac-chained-bps
@@ -0,0 +1,27 @@
+#tb 0: 1/44100
+#media_type 0: audio
+#codec_id 0: pcm_s32le
+#sample_rate 0: 44100
+#channel_layout_name 0: mono
+0,          0,          0,     1024,     4096, 0x4c38f45b
+0,       1024,       1024,     1024,     4096, 0x4b0cf6ec
+0,       2048,       2048,     1024,     4096, 0x59c4fe05
+0,       3072,       3072,     1024,     4096, 0x8b7006b5
+0,       4096,       4096,     1024,     4096, 0x6799fcca
+0,       5120,       5120,     1024,     4096, 0x1753f510
+0,       6144,       6144,     1024,     4096, 0xaf7ef717
+0,       7168,       7168,     1024,     4096, 0x8d0a0826
+0,       8192,       8192,     1024,     4096, 0xe4520954
+0,       9216,       9216,     1024,     4096, 0x7c69f55c
+0,      10240,      10240,     1024,     4096, 0x1db1f7ee
+0,      11264,      11264,     1024,     4096, 0xf6e4fe99
+0,      12288,      12288,     1024,     4096, 0x2a8c0191
+0,      13312,      13312,     1024,     4096, 0x05940797
+0,      14336,      14336,     1024,     4096, 0xabadf38b
+0,      15360,      15360,     1024,     4096, 0x6a0af701
+0,      16384,      16384,     1024,     4096, 0xf0fe0476
+0,      17408,      17408,     1024,     4096, 0xb3ec0222
+0,      18432,      18432,     1024,     4096, 0x27c0013e
+0,      19456,      19456,     1024,     4096, 0x3b94f0eb
+0,      20480,      20480,     1024,     4096, 0x3973fada
+0,      21504,      21504,      546,     2184, 0x7e55340b
-- 
2.54.0


>From 2ffeddc7d1eb983e297a478ddd69f61903aa3d72 Mon Sep 17 00:00:00 2001
From: Romain Beauxis <[email protected]>
Date: Sat, 31 Oct 2026 09:16:23 -0500
Subject: [PATCH 2/6] avcodec/flacdec: factor out applying a STREAMINFO

Share the steps that initialization and an inline header both run to
set the decoder up from a STREAMINFO.
---
 libavcodec/flacdec.c | 45 +++++++++++++++++++-------------------------
 1 file changed, 19 insertions(+), 26 deletions(-)

diff --git a/libavcodec/flacdec.c b/libavcodec/flacdec.c
index 0c88f577a1a..b3e56df0a0f 100644
--- a/libavcodec/flacdec.c
+++ b/libavcodec/flacdec.c
@@ -95,10 +95,25 @@ static void flac_set_bps(FLACContext *s)
     }
 }
 
+static int apply_streaminfo(FLACContext *s, const uint8_t *streaminfo)
+{
+    int ret = ff_flac_parse_streaminfo(s->avctx, &s->stream_info, streaminfo);
+    if (ret < 0)
+        return ret;
+    ret = allocate_buffers(s);
+    if (ret < 0)
+        return ret;
+    flac_set_bps(s);
+    ff_flacdsp_init(&s->dsp, s->avctx->sample_fmt,
+                    s->stream_info.channels);
+    s->got_streaminfo = 1;
+
+    return 0;
+}
+
 static av_cold int flac_decode_init(AVCodecContext *avctx)
 {
     uint8_t *streaminfo;
-    int ret;
     FLACContext *s = avctx->priv_data;
     s->avctx = avctx;
 
@@ -111,18 +126,7 @@ static av_cold int flac_decode_init(AVCodecContext *avctx)
         return AVERROR_INVALIDDATA;
 
     /* initialize based on the demuxer-supplied streamdata header */
-    ret = ff_flac_parse_streaminfo(avctx, &s->stream_info, streaminfo);
-    if (ret < 0)
-        return ret;
-    ret = allocate_buffers(s);
-    if (ret < 0)
-        return ret;
-    flac_set_bps(s);
-    ff_flacdsp_init(&s->dsp, avctx->sample_fmt,
-                    s->stream_info.channels);
-    s->got_streaminfo = 1;
-
-    return 0;
+    return apply_streaminfo(s, streaminfo);
 }
 
 static void dump_headers(AVCodecContext *avctx, FLACStreaminfo *s)
@@ -186,7 +190,7 @@ static int allocate_buffers(FLACContext *s)
  */
 static int parse_streaminfo(FLACContext *s, const uint8_t *buf, int buf_size)
 {
-    int metadata_type, metadata_size, ret;
+    int metadata_type, metadata_size;
 
     if (buf_size < FLAC_STREAMINFO_SIZE+8) {
         /* need more data */
@@ -197,18 +201,7 @@ static int parse_streaminfo(FLACContext *s, const uint8_t 
*buf, int buf_size)
         metadata_size != FLAC_STREAMINFO_SIZE) {
         return AVERROR_INVALIDDATA;
     }
-    ret = ff_flac_parse_streaminfo(s->avctx, &s->stream_info, &buf[8]);
-    if (ret < 0)
-        return ret;
-    ret = allocate_buffers(s);
-    if (ret < 0)
-        return ret;
-    flac_set_bps(s);
-    ff_flacdsp_init(&s->dsp, s->avctx->sample_fmt,
-                    s->stream_info.channels);
-    s->got_streaminfo = 1;
-
-    return 0;
+    return apply_streaminfo(s, &buf[8]);
 }
 
 /**
-- 
2.54.0


>From 83f46bd1af7e741d7b026bb3d5bcb07a4af78a29 Mon Sep 17 00:00:00 2001
From: Romain Beauxis <[email protected]>
Date: Sat, 31 Oct 2026 09:16:23 -0500
Subject: [PATCH 3/6] avcodec/flacdec: split frame header and subframe decoding

Request the output buffer once the frame header has given the block
size, before decoding the subframes.

With frame threading, all the state a frame header sets can then be
handed to the next thread before the costly part of the frame starts.
---
 libavcodec/flacdec.c | 29 +++++++++++++++++++++--------
 1 file changed, 21 insertions(+), 8 deletions(-)

diff --git a/libavcodec/flacdec.c b/libavcodec/flacdec.c
index b3e56df0a0f..d269e6b4329 100644
--- a/libavcodec/flacdec.c
+++ b/libavcodec/flacdec.c
@@ -599,9 +599,9 @@ static inline int decode_subframe(FLACContext *s, int 
channel)
     return 0;
 }
 
-static int decode_frame(FLACContext *s)
+static int decode_frame_header(FLACContext *s)
 {
-    int i, ret;
+    int ret;
     GetBitContext *gb = &s->gb;
     FLACFrameInfo fi;
 
@@ -670,6 +670,14 @@ static int decode_frame(FLACContext *s)
 
 //    dump_headers(s->avctx, &s->stream_info);
 
+    return 0;
+}
+
+static int decode_subframes(FLACContext *s)
+{
+    int i, ret;
+    GetBitContext *gb = &s->gb;
+
     /* subframes */
     for (i = 0; i < s->stream_info.channels; i++) {
         if ((ret = decode_subframe(s, i)) < 0)
@@ -743,7 +751,17 @@ static int flac_decode_frame(AVCodecContext *avctx, 
AVFrame *frame,
     /* decode frame */
     if ((ret = init_get_bits8(&s->gb, buf, buf_size)) < 0)
         return ret;
-    if ((ret = decode_frame(s)) < 0) {
+    if ((ret = decode_frame_header(s)) < 0) {
+        av_log(s->avctx, AV_LOG_ERROR, "decode_frame() failed\n");
+        return ret;
+    }
+
+    /* get output buffer */
+    frame->nb_samples = s->blocksize;
+    if ((ret = ff_thread_get_buffer(avctx, frame, 0)) < 0)
+        return ret;
+
+    if ((ret = decode_subframes(s)) < 0) {
         av_log(s->avctx, AV_LOG_ERROR, "decode_frame() failed\n");
         return ret;
     }
@@ -757,11 +775,6 @@ static int flac_decode_frame(AVCodecContext *avctx, 
AVFrame *frame,
             return AVERROR_INVALIDDATA;
     }
 
-    /* get output buffer */
-    frame->nb_samples = s->blocksize;
-    if ((ret = ff_thread_get_buffer(avctx, frame, 0)) < 0)
-        return ret;
-
     if (s->stream_info.bps == 32 && s->ch_mode > 0) {
         decorrelate_33bps(s->ch_mode, s->decoded, s->decoded_33bps, 
s->blocksize);
         s->dsp.decorrelate[0](frame->data, s->decoded, s->stream_info.channels,
-- 
2.54.0


>From b25f49250a96626be5a84d381d53b9149b8cf2f1 Mon Sep 17 00:00:00 2001
From: Romain Beauxis <[email protected]>
Date: Sat, 31 Oct 2026 09:21:58 -0500
Subject: [PATCH 4/6] avcodec/flacdec: take a new STREAMINFO from packet side
 data

A chained Ogg FLAC stream can change its bit depth, sample rate or
block sizes at each new link, which frame headers alone cannot carry.

Apply a STREAMINFO passed as AV_PKT_DATA_NEW_EXTRADATA before decoding
the packet that carries it, still rejecting frames that disagree with
the STREAMINFO in effect.
---
 libavcodec/flacdec.c | 11 +++++++++++
 1 file changed, 11 insertions(+)

diff --git a/libavcodec/flacdec.c b/libavcodec/flacdec.c
index d269e6b4329..0aeb4c14708 100644
--- a/libavcodec/flacdec.c
+++ b/libavcodec/flacdec.c
@@ -718,11 +718,22 @@ static int flac_decode_frame(AVCodecContext *avctx, 
AVFrame *frame,
     const uint8_t *buf = avpkt->data;
     int buf_size = avpkt->size;
     FLACContext *s = avctx->priv_data;
+    const uint8_t *streaminfo;
+    size_t streaminfo_size;
     int bytes_read = 0;
     int ret;
 
     *got_frame_ptr = 0;
 
+    streaminfo = av_packet_get_side_data(avpkt, AV_PKT_DATA_NEW_EXTRADATA,
+                                         &streaminfo_size);
+    if (streaminfo) {
+        if (streaminfo_size != FLAC_STREAMINFO_SIZE)
+            return AVERROR_INVALIDDATA;
+        if ((ret = apply_streaminfo(s, streaminfo)) < 0)
+            return ret;
+    }
+
     if (buf_size > 5 && !memcmp(buf, "\177FLAC", 5)) {
         av_log(s->avctx, AV_LOG_DEBUG, "skipping flac header packet 1\n");
         return buf_size;
-- 
2.54.0


>From 7106935dcc3753a9882a8634e09102463697e8dc Mon Sep 17 00:00:00 2001
From: Romain Beauxis <[email protected]>
Date: Sat, 31 Oct 2026 09:21:58 -0500
Subject: [PATCH 5/6] avcodec/flacdec: hand the stream parameters on to the
 next frame thread

A STREAMINFO from packet side data only reaches the frame thread
decoding that packet.

Copy the stream parameters from one frame thread to the next, finishing
setup once the output buffer is allocated so that the subframes still
decode in parallel.
---
 libavcodec/flacdec.c | 30 ++++++++++++++++++++++++++++++
 1 file changed, 30 insertions(+)

diff --git a/libavcodec/flacdec.c b/libavcodec/flacdec.c
index 0aeb4c14708..2fc9546a0b4 100644
--- a/libavcodec/flacdec.c
+++ b/libavcodec/flacdec.c
@@ -772,6 +772,8 @@ static int flac_decode_frame(AVCodecContext *avctx, AVFrame 
*frame,
     if ((ret = ff_thread_get_buffer(avctx, frame, 0)) < 0)
         return ret;
 
+    ff_thread_finish_setup(avctx);
+
     if ((ret = decode_subframes(s)) < 0) {
         av_log(s->avctx, AV_LOG_ERROR, "decode_frame() failed\n");
         return ret;
@@ -810,6 +812,33 @@ static int flac_decode_frame(AVCodecContext *avctx, 
AVFrame *frame,
     return bytes_read;
 }
 
+#if HAVE_THREADS
+/**
+ * Hand the stream parameters on to the next frame thread, so that a STREAMINFO
+ * taken from packet side data holds for the frames after the one carrying it.
+ */
+static int flac_update_thread_context(AVCodecContext *dst,
+                                      const AVCodecContext *src)
+{
+    FLACContext *s        = dst->priv_data;
+    const FLACContext *s1 = src->priv_data;
+    int ret;
+
+    if (s == s1 || !s1->got_streaminfo)
+        return 0;
+
+    s->stream_info    = s1->stream_info;
+    s->got_streaminfo = 1;
+    ret = allocate_buffers(s);
+    if (ret < 0)
+        return ret;
+    flac_set_bps(s);
+    ff_flacdsp_init(&s->dsp, dst->sample_fmt, s->stream_info.channels);
+
+    return 0;
+}
+#endif
+
 static av_cold int flac_decode_close(AVCodecContext *avctx)
 {
     FLACContext *s = avctx->priv_data;
@@ -841,6 +870,7 @@ const FFCodec ff_flac_decoder = {
     .init           = flac_decode_init,
     .close          = flac_decode_close,
     FF_CODEC_DECODE_CB(flac_decode_frame),
+    UPDATE_THREAD_CONTEXT(flac_update_thread_context),
     .p.capabilities = AV_CODEC_CAP_CHANNEL_CONF |
                       AV_CODEC_CAP_DR1 |
                       AV_CODEC_CAP_FRAME_THREADS,
-- 
2.54.0


>From 6b785a080e3ed0755e9eafc7cbd451da8090a72a Mon Sep 17 00:00:00 2001
From: Romain Beauxis <[email protected]>
Date: Sat, 31 Oct 2026 09:21:58 -0500
Subject: [PATCH 6/6] avformat/oggparseflac: pass a later link's STREAMINFO to
 the decoder

When a chained stream moves to a new link, the decoder is still set
up from the first link's STREAMINFO.

Attach the new link's STREAMINFO to its first packet as
AV_PKT_DATA_NEW_EXTRADATA, as the vorbis parser already does with
its headers.

The second link of both chained FLAC tests now decodes, with or
without frame threads, and matches that link decoded on its own.
---
 libavformat/oggparseflac.c                | 14 +++++++++++++-
 tests/ref/fate/ogg-flac-chained-blocksize |  6 ++++++
 tests/ref/fate/ogg-flac-chained-bps       | 22 ++++++++++++++++++++++
 3 files changed, 41 insertions(+), 1 deletion(-)

diff --git a/libavformat/oggparseflac.c b/libavformat/oggparseflac.c
index e81e4021a10..31dd1439da5 100644
--- a/libavformat/oggparseflac.c
+++ b/libavformat/oggparseflac.c
@@ -19,6 +19,7 @@
  */
 
 #include <stdlib.h>
+#include "libavutil/mem.h"
 #include "libavcodec/avcodec.h"
 #include "libavcodec/bytestream.h"
 #include "libavcodec/flac.h"
@@ -29,6 +30,7 @@
 #define OGG_FLAC_METADATA_TYPE_STREAMINFO 0x7F
 #define OGG_FLAC_MAGIC "\177FLAC"
 #define OGG_FLAC_MAGIC_SIZE sizeof(OGG_FLAC_MAGIC)-1
+#define OGG_FLAC_STREAMINFO_OFFSET (OGG_FLAC_MAGIC_SIZE + 1 + 1 + 2 + 4 + 4)
 
 static int
 flac_header (AVFormatContext * s, int idx)
@@ -92,8 +94,18 @@ flac_packet (AVFormatContext * s, int idx)
         !memcmp(
             os->buf + os->pstart,
             OGG_FLAC_MAGIC,
-            OGG_FLAC_MAGIC_SIZE))
+            OGG_FLAC_MAGIC_SIZE)) {
+        /* A later link's STREAMINFO: the decoder is still set up for the 
previous one. */
+        if (os->psize >= OGG_FLAC_STREAMINFO_OFFSET + FLAC_STREAMINFO_SIZE) {
+            av_freep(&os->new_extradata);
+            os->new_extradata = av_memdup(os->buf + os->pstart + 
OGG_FLAC_STREAMINFO_OFFSET,
+                                          FLAC_STREAMINFO_SIZE);
+            if (!os->new_extradata)
+                return AVERROR(ENOMEM);
+            os->new_extradata_size = FLAC_STREAMINFO_SIZE;
+        }
         return 1;
+    }
 
     if (os->psize > 0 &&
         ((os->buf[os->pstart] & 0x7F) == FLAC_METADATA_TYPE_VORBIS_COMMENT)) {
diff --git a/tests/ref/fate/ogg-flac-chained-blocksize 
b/tests/ref/fate/ogg-flac-chained-blocksize
index 0fb6fa8292c..c7aa24eab98 100644
--- a/tests/ref/fate/ogg-flac-chained-blocksize
+++ b/tests/ref/fate/ogg-flac-chained-blocksize
@@ -25,3 +25,9 @@
 0,      19456,      19456,     1024,     2048, 0x950ef0eb
 0,      20480,      20480,     1024,     2048, 0x9b51fada
 0,      21504,      21504,      546,     1092, 0x60af340b
+0,      22050,      22050,     4096,     8192, 0x6fc0e872
+0,      26146,      26146,     4096,     8192, 0x6f80f031
+0,      30242,      30242,     4096,     8192, 0x6f75f855
+0,      34338,      34338,     4096,     8192, 0x9866f00b
+0,      38434,      38434,     4096,     8192, 0xfb4def68
+0,      42530,      42530,     1570,     3140, 0xb6d32de8
diff --git a/tests/ref/fate/ogg-flac-chained-bps 
b/tests/ref/fate/ogg-flac-chained-bps
index 291e7d9cf57..84b16132a83 100644
--- a/tests/ref/fate/ogg-flac-chained-bps
+++ b/tests/ref/fate/ogg-flac-chained-bps
@@ -25,3 +25,25 @@
 0,      19456,      19456,     1024,     4096, 0x3b94f0eb
 0,      20480,      20480,     1024,     4096, 0x3973fada
 0,      21504,      21504,      546,     2184, 0x7e55340b
+0,      22050,      22050,     1024,     4096, 0x22f2f151
+0,      23074,      23074,     1024,     4096, 0x7736f949
+0,      24098,      24098,     1024,     4096, 0x24480657
+0,      25122,      25122,     1024,     4096, 0xfc05f763
+0,      26146,      26146,     1024,     4096, 0x4b8eff25
+0,      27170,      27170,     1024,     4096, 0x21510055
+0,      28194,      28194,     1024,     4096, 0x1ee7f371
+0,      29218,      29218,     1024,     4096, 0x11f0fd28
+0,      30242,      30242,     1024,     4096, 0xf7c60713
+0,      31266,      31266,     1024,     4096, 0x4853fa2d
+0,      32290,      32290,     1024,     4096, 0x6a78f34f
+0,      33314,      33314,     1024,     4096, 0x54ae03b7
+0,      34338,      34338,     1024,     4096, 0x4c11fc94
+0,      35362,      35362,     1024,     4096, 0xf16bf999
+0,      36386,      36386,     1024,     4096, 0xad13042e
+0,      37410,      37410,     1024,     4096, 0xfa29f592
+0,      38434,      38434,     1024,     4096, 0x3885f74a
+0,      39458,      39458,     1024,     4096, 0x3c0a094a
+0,      40482,      40482,     1024,     4096, 0xe169fd1c
+0,      41506,      41506,     1024,     4096, 0x18aaf19a
+0,      42530,      42530,     1024,     4096, 0xc892ffaa
+0,      43554,      43554,      546,     2184, 0xc3e22e2f
-- 
2.54.0

_______________________________________________
ffmpeg-devel mailing list -- [email protected]
To unsubscribe send an email to [email protected]

Reply via email to