PR #25209 opened by Peter Krefting (nafmo)
URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/25209
Patch URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/25209.patch

We are decoding several HE-AAC audio streams in parallel, and calling 
avcodec_flush_buffers() between each call. We are seeing that the full state is 
not reset between calls, similar to what we previously experienced with the 
[E-AC3 decoder](https://trac.ffmpeg.org/ticket/10732) (fixed in 
045a8b15b19ec7f872fb01cfb986faeaa26cb8bb).

The problem occurs in the SBR handling for HE-AAC.

After throwing the problem on opencode.ai and the free OpenCode Zen Big Pickle 
model, it came up with the attached patch, which does fix the issue on our side.

The LLM output referenced [this 
message](https://ffmpeg.org/pipermail/ffmpeg-devel/2020-January/255176.html) as 
information about why the SBR data had not been flushed, so it deserves credit.

Originally filed as a bug report as 
https://code.ffmpeg.org/FFmpeg/FFmpeg/issues/25066


>From 8a56c1b58525909fb8f61aa3ee78f3cc64391545 Mon Sep 17 00:00:00 2001
From: Peter Krefting <[email protected]>
Date: Mon, 19 Oct 2026 09:07:17 +0100
Subject: [PATCH] avcodec/aac: Reset SBR context (HE-AAC) when flushing AAC
 decoder

When calling avcodec_flush_buffers() on the AAC decoder, decoding an
HE-AAC stream, the SBR state would not be cleared properly.

See: https://ffmpeg.org/pipermail/ffmpeg-devel/2020-January/255176.html
Assisted-by: OpenZen Big Pickle
---
 libavcodec/aac/aacdec.c               |  1 +
 libavcodec/aac/aacdec.h               |  1 +
 libavcodec/aac/aacdec_proc_template.c |  1 +
 libavcodec/aacsbr.h                   |  5 ++++
 libavcodec/aacsbr_template.c          | 35 +++++++++++++++++++++++++++
 5 files changed, 43 insertions(+)

diff --git a/libavcodec/aac/aacdec.c b/libavcodec/aac/aacdec.c
index 66ab648016..5bdabe388c 100644
--- a/libavcodec/aac/aacdec.c
+++ b/libavcodec/aac/aacdec.c
@@ -564,6 +564,7 @@ static av_cold void flush(AVCodecContext *avctx)
                 for (j = 0; j <= 1; j++) {
                     memset(che->ch[j].saved, 0, sizeof(che->ch[j].saved));
                 }
+                ac->proc.sbr_ctx_reset(ac, che);
             }
         }
     }
diff --git a/libavcodec/aac/aacdec.h b/libavcodec/aac/aacdec.h
index 18412fb97a..1fb89ace61 100644
--- a/libavcodec/aac/aacdec.h
+++ b/libavcodec/aac/aacdec.h
@@ -458,6 +458,7 @@ typedef struct AACDecProc {
     void (*sbr_apply)(AACDecContext *ac, ChannelElement *che, int id_aac, int 
fl960,
                       void /* INTFLOAT */ *L, void /* INTFLOAT */ *R);
     void (*sbr_ctx_close)(ChannelElement *che);
+    void (*sbr_ctx_reset)(AACDecContext *ac, ChannelElement *che);
 } AACDecProc;
 
 /**
diff --git a/libavcodec/aac/aacdec_proc_template.c 
b/libavcodec/aac/aacdec_proc_template.c
index 327f3117b5..b56302a93f 100644
--- a/libavcodec/aac/aacdec_proc_template.c
+++ b/libavcodec/aac/aacdec_proc_template.c
@@ -444,5 +444,6 @@ static av_cold void AAC_RENAME(aac_proc_init)(AACDecProc 
*aac_proc)
     SET(sbr_decode_extension);
     SET(sbr_apply);
     SET(sbr_ctx_close);
+    SET(sbr_ctx_reset);
 #undef SET
 }
diff --git a/libavcodec/aacsbr.h b/libavcodec/aacsbr.h
index afc8ad188a..c330a13183 100644
--- a/libavcodec/aacsbr.h
+++ b/libavcodec/aacsbr.h
@@ -82,6 +82,11 @@ int ff_aac_sbr_ctx_alloc_init_fixed(AACDecContext *ac, 
ChannelElement **che, int
 void ff_aac_sbr_ctx_close(ChannelElement *che);
 void ff_aac_sbr_ctx_close_fixed(ChannelElement *che);
 
+/** Reset the SBR context contained in a ChannelElement, discarding all
+ *  inter-frame state while keeping the allocated transform contexts. */
+void ff_aac_sbr_ctx_reset(AACDecContext *ac, ChannelElement *che);
+void ff_aac_sbr_ctx_reset_fixed(AACDecContext *ac, ChannelElement *che);
+
 /** Decode one SBR element. */
 int ff_aac_sbr_decode_extension(AACDecContext *ac, ChannelElement *che,
                                 GetBitContext *gb, int crc, int cnt, int 
id_aac,
diff --git a/libavcodec/aacsbr_template.c b/libavcodec/aacsbr_template.c
index cc22b64416..ed07ea6108 100644
--- a/libavcodec/aacsbr_template.c
+++ b/libavcodec/aacsbr_template.c
@@ -123,6 +123,41 @@ av_cold void 
AAC_RENAME(ff_aac_sbr_ctx_close)(ChannelElement *che)
     av_tx_uninit(&sbr->mdct_ana);
 }
 
+av_cold void AAC_RENAME(ff_aac_sbr_ctx_reset)(AACDecContext *ac, 
ChannelElement *che)
+{
+    SpectralBandReplication *sbr = get_sbr(che);
+
+    /* Keep the members set up by ff_aac_sbr_ctx_alloc_init() that must survive
+     * a reset (the allocated transform contexts and the function pointer
+     * tables); everything else is inter-frame state that has to be discarded. 
*/
+    AVTXContext      *mdct        = sbr->mdct;
+    av_tx_fn          mdct_fn     = sbr->mdct_fn;
+    AVTXContext      *mdct_ana    = sbr->mdct_ana;
+    av_tx_fn          mdct_ana_fn = sbr->mdct_ana_fn;
+    SBRDSPContext     dsp;
+    AACSBRContext     c;
+    int               id_aac      = sbr->id_aac;
+
+    memcpy(&dsp, &sbr->dsp, sizeof(dsp));
+    memcpy(&c,   &sbr->c,   sizeof(c));
+
+    memset(sbr, 0, sizeof(*sbr));
+
+    sbr->mdct        = mdct;
+    sbr->mdct_fn     = mdct_fn;
+    sbr->mdct_ana    = mdct_ana;
+    sbr->mdct_ana_fn = mdct_ana_fn;
+    sbr->dsp         = dsp;
+    sbr->c           = c;
+
+    sbr->id_aac = id_aac;
+    sbr->kx[0]  = sbr->kx[1];
+    sbr_turnoff(sbr);
+    sbr->data[0].synthesis_filterbank_samples_offset = SBR_SYNTHESIS_BUF_SIZE 
- (1280 - 128);
+    sbr->data[1].synthesis_filterbank_samples_offset = SBR_SYNTHESIS_BUF_SIZE 
- (1280 - 128);
+    AAC_RENAME(ff_ps_ctx_init)(&sbr->ps);
+}
+
 static int qsort_comparison_function_int16(const void *a, const void *b)
 {
     return *(const int16_t *)a - *(const int16_t *)b;
-- 
2.54.0

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

Reply via email to