PR #24200 opened by AYOUB NABIL BOUBAGRAT (ayoubnabil) URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/24200 Patch URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/24200.patch
ac-3 cores and dependent e-ac-3 substreams may use different coefficient scales while owning separate delay slots. the global reset added in 5ba2a2c841 cleared both sets of slots on every substream switch, corrupting the mdct overlap of valid 7.1 streams. track the scale per substream and clear only its own delay slots when it changes. add a fate test using the existing the_great_wall sample. >From 730d12d224f2ee747971d7f0ac282403ed62a499 Mon Sep 17 00:00:00 2001 From: Ayoub Nabil Boubagrat <[email protected]> Date: Tue, 18 Aug 2026 14:40:56 +0200 Subject: [PATCH] avcodec/ac3dec_fixed: preserve overlap across dependent substreams ac-3 cores and dependent e-ac-3 substreams may use different coefficient scales while owning separate delay slots. the global reset added in 5ba2a2c841 cleared both sets of slots on every substream switch, corrupting the mdct overlap of valid 7.1 streams. track the scale per substream and clear only its own delay slots when it changes. add a fate test using the existing the_great_wall sample. Signed-off-by: Ayoub Nabil Boubagrat <[email protected]> --- libavcodec/ac3dec.c | 30 +++++++++++++++++------------- libavcodec/ac3dec.h | 3 +++ tests/fate/ac3.mak | 9 ++++++++- 3 files changed, 28 insertions(+), 14 deletions(-) diff --git a/libavcodec/ac3dec.c b/libavcodec/ac3dec.c index e3a711ebbf..9f85446bf4 100644 --- a/libavcodec/ac3dec.c +++ b/libavcodec/ac3dec.c @@ -1398,9 +1398,6 @@ static int ac3_decode_frame(AVCodecContext *avctx, AVFrame *frame, AC3DecodeContext *s = avctx->priv_data; int blk, ch, err, offset, ret; int i; -#if USE_FIXED - int previous_coeff_bits; -#endif int skip = 0, got_independent_frame = 0; const uint8_t *channel_map; uint8_t extended_channel_map[EAC3_MAX_CHANNELS]; @@ -1435,9 +1432,6 @@ static int ac3_decode_frame(AVCodecContext *avctx, AVFrame *frame, buf = s->input_buffer; dependent_frame: -#if USE_FIXED - previous_coeff_bits = fixed_coeff_bits(s); -#endif /* initialize the GetBitContext with the start of valid AC-3 Frame */ if ((ret = init_get_bits8(&s->gbc, buf, buf_size)) < 0) return ret; @@ -1445,13 +1439,6 @@ dependent_frame: /* parse the syncinfo */ err = parse_frame_header(s); -#if USE_FIXED - /* Do not mix Q0 and Q2 overlap samples if a malformed or explicitly - * forced stream switches between E-AC-3 and AC-3. */ - if (!err && previous_coeff_bits != fixed_coeff_bits(s)) - memset(s->delay, 0, sizeof(s->delay)); -#endif - if (err) { switch (err) { case AC3_PARSE_ERROR_SYNC: @@ -1573,6 +1560,23 @@ dependent_frame: /* decode the audio blocks */ channel_map = ff_ac3_dec_channel_map[s->output_mode & ~AC3_OUTPUT_LFEON][s->lfe_on]; offset = s->frame_type == EAC3_FRAME_TYPE_DEPENDENT ? AC3_MAX_CHANNELS : 0; +#if USE_FIXED + /* delay[] holds overlap samples scaled by the coefficient format that was + * in use when they were produced. The independent and the dependent + * substream own disjoint delay slots and may legitimately use different + * formats, so only drop the overlap of the substream whose format really + * changed, as happens when a malformed or explicitly forced stream + * switches between E-AC-3 and AC-3. */ + if (!err) { + const int coeff_bits = fixed_coeff_bits(s); + const int slot = offset ? 1 : 0; + + if (s->delay_coeff_bits[slot] != coeff_bits) { + memset(s->delay[offset], 0, AC3_MAX_CHANNELS * sizeof(s->delay[0])); + s->delay_coeff_bits[slot] = coeff_bits; + } + } +#endif for (ch = 0; ch < AC3_MAX_CHANNELS; ch++) { output[ch] = s->output[ch + offset]; s->outptr[ch] = s->output[ch + offset]; diff --git a/libavcodec/ac3dec.h b/libavcodec/ac3dec.h index 1a4da07fa3..c0e6f683ef 100644 --- a/libavcodec/ac3dec.h +++ b/libavcodec/ac3dec.h @@ -252,6 +252,9 @@ typedef struct AC3DecodeContext { DECLARE_ALIGNED(16, int, fixed_coeffs)[AC3_MAX_CHANNELS][AC3_MAX_COEFS]; ///< fixed-point transform coefficients DECLARE_ALIGNED(32, INTFLOAT, transform_coeffs)[AC3_MAX_CHANNELS][AC3_MAX_COEFS]; ///< transform coefficients DECLARE_ALIGNED(32, INTFLOAT, delay)[EAC3_MAX_CHANNELS][AC3_BLOCK_SIZE]; ///< delay - added to the next block +#if USE_FIXED + int delay_coeff_bits[2]; ///< coefficient format of delay[], per substream +#endif DECLARE_ALIGNED(32, INTFLOAT, window)[AC3_BLOCK_SIZE]; ///< window coefficients DECLARE_ALIGNED(32, INTFLOAT, tmp_output)[AC3_BLOCK_SIZE]; ///< temporary storage for output before windowing DECLARE_ALIGNED(32, SHORTFLOAT, output)[EAC3_MAX_CHANNELS][AC3_BLOCK_SIZE]; ///< output after imdct transform and windowing diff --git a/tests/fate/ac3.mak b/tests/fate/ac3.mak index 90977a80c0..70aa4f4568 100644 --- a/tests/fate/ac3.mak +++ b/tests/fate/ac3.mak @@ -63,10 +63,17 @@ FATE_EAC3 += fate-eac3-5 fate-eac3-5: CMD = pcm -i $(TARGET_SAMPLES)/eac3/the_great_wall_7.1.eac3 fate-eac3-5: REF = $(SAMPLES)/eac3/the_great_wall_7.1.pcm -$(FATE_AC3) $(FATE_EAC3): CMP = oneoff +# the fixed decoder has to keep the overlap of the independent substream when +# the dependent substream uses a different coefficient format +FATE_EAC3_FIXED += fate-eac3-fixed-dependent-substream +fate-eac3-fixed-dependent-substream: CMD = pcm -c ac3_fixed -i $(TARGET_SAMPLES)/eac3/the_great_wall_7.1.eac3 +fate-eac3-fixed-dependent-substream: REF = $(SAMPLES)/eac3/the_great_wall_7.1.pcm + +$(FATE_AC3) $(FATE_EAC3) $(FATE_EAC3_FIXED): CMP = oneoff FATE_AC3-$(call PCM, AC3, AC3 AC3_FIXED, PCM_S16LE_MUXER ARESAMPLE_FILTER) += $(FATE_AC3) FATE_EAC3-$(call PCM, EAC3, EAC3, PCM_S16LE_MUXER ARESAMPLE_FILTER) += $(FATE_EAC3) +FATE_EAC3-$(call PCM, EAC3, EAC3 AC3_FIXED, PCM_S16LE_MUXER ARESAMPLE_FILTER) += $(FATE_EAC3_FIXED) FATE_AC3-$(call ENCDEC, AC3, MP4 MOV, WAV_MUXER WAV_DEMUXER ARESAMPLE_FILTER PCM_S16LE_ENCODER PIPE_PROTOCOL) += fate-ac3-encode fate-ac3-encode: CMD = enc_dec_pcm mp4 wav s16le $(subst $(SAMPLES),$(TARGET_SAMPLES),$(REF)) -c:a ac3 -b:a 128k -- 2.52.0 _______________________________________________ ffmpeg-devel mailing list -- [email protected] To unsubscribe send an email to [email protected]
