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]
