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]

Reply via email to