PR #24575 opened by James Almer (jamrial)
URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/24575
Patch URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/24575.patch

Backport: #24528

Shift timestamps when available and take into account the caller requested 
samples to be discarded.
The decoder can't know where a seek landed, so the "delay_samples" way to 
discard the decoder delay in the first frame is not truly viable, so it needs 
to be removed.

Should fix issues #24526 and #24527.


>From 416d8cbaf98f49fa116ecf200d2b55622fccf145 Mon Sep 17 00:00:00 2001
From: James Almer <[email protected]>
Date: Sun, 17 Nov 2024 15:53:03 -0300
Subject: [PATCH 1/4] avcodec/libfdk-aacdec: set keyframe flag and profile in
 output frames

Don't depend on the generic code setting this.
This is in preparation for a following change.

Signed-off-by: James Almer <[email protected]>
(cherry picked from commit 281b7fc02e3158ee62c3c1d41267ddebb86578f0)
---
 libavcodec/libfdk-aacdec.c | 7 +++++--
 1 file changed, 5 insertions(+), 2 deletions(-)

diff --git a/libavcodec/libfdk-aacdec.c b/libavcodec/libfdk-aacdec.c
index 8c1586e25e..7a8554219d 100644
--- a/libavcodec/libfdk-aacdec.c
+++ b/libavcodec/libfdk-aacdec.c
@@ -110,7 +110,7 @@ static const AVClass fdk_aac_dec_class = {
     .version    = LIBAVUTIL_VERSION_INT,
 };
 
-static int get_stream_info(AVCodecContext *avctx)
+static int get_stream_info(AVCodecContext *avctx, AVFrame *frame)
 {
     FDKAACDecContext *s   = avctx->priv_data;
     CStreamInfo *info     = aacDecoder_GetStreamInfo(s->handle);
@@ -129,6 +129,9 @@ static int get_stream_info(AVCodecContext *avctx)
     }
     avctx->sample_rate = info->sampleRate;
     avctx->frame_size  = info->frameSize;
+    avctx->profile     = info->aot - 1;
+
+    frame->flags |= AV_FRAME_FLAG_KEY * !!(info->flags & AC_INDEP);
 #if FDKDEC_VER_AT_LEAST(2, 5) // 2.5.10
     if (!s->output_delay_set && info->outputDelay) {
         // Set this only once.
@@ -420,7 +423,7 @@ static int fdk_aac_decode_frame(AVCodecContext *avctx, 
AVFrame *frame,
         goto end;
     }
 
-    if ((ret = get_stream_info(avctx)) < 0)
+    if ((ret = get_stream_info(avctx, frame)) < 0)
         goto end;
     frame->nb_samples = avctx->frame_size;
 
-- 
2.52.0


>From a34013df6dd19885e639ca1a0e7d2780a66014f2 Mon Sep 17 00:00:00 2001
From: James Almer <[email protected]>
Date: Tue, 19 May 2026 13:13:28 -0300
Subject: [PATCH 2/4] avcodec/libfdk-aacdec: fix the check for downmix layout
 order

The code reads a mask afer this check, meaning it expects NATIVE order, not
others.

Signed-off-by: James Almer <[email protected]>
(cherry picked from commit 7755d264bdec8367fec467539b20d3dc73d18b92)
---
 libavcodec/libfdk-aacdec.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libavcodec/libfdk-aacdec.c b/libavcodec/libfdk-aacdec.c
index 7a8554219d..20f0fdc653 100644
--- a/libavcodec/libfdk-aacdec.c
+++ b/libavcodec/libfdk-aacdec.c
@@ -276,7 +276,7 @@ FF_DISABLE_DEPRECATION_WARNINGS
 FF_ENABLE_DEPRECATION_WARNINGS
 #endif
     if (s->downmix_layout.nb_channels > 0 &&
-        s->downmix_layout.order != AV_CHANNEL_ORDER_NATIVE) {
+        s->downmix_layout.order == AV_CHANNEL_ORDER_NATIVE) {
         int downmix_channels = -1;
 
         switch (s->downmix_layout.u.mask) {
-- 
2.52.0


>From 374049720c801a0e496fe692b00edaf7500e7e90 Mon Sep 17 00:00:00 2001
From: James Almer <[email protected]>
Date: Tue, 15 Sep 2026 22:35:25 -0300
Subject: [PATCH 3/4] avcodec/libfdk-aacdec: properly handle decoder delay

Shift timestamps when available, and take into account the caller requested
samples to be discarded alongside the decoder delay.

Should fix issue #24526.

Signed-off-by: James Almer <[email protected]>
(cherry picked from commit c8595be8e62afd720dadaf72bc8be170b07477e9)
---
 libavcodec/libfdk-aacdec.c | 79 ++++++++++++++++++++++++++++++++++----
 1 file changed, 72 insertions(+), 7 deletions(-)

diff --git a/libavcodec/libfdk-aacdec.c b/libavcodec/libfdk-aacdec.c
index 20f0fdc653..29cb733fa5 100644
--- a/libavcodec/libfdk-aacdec.c
+++ b/libavcodec/libfdk-aacdec.c
@@ -21,6 +21,8 @@
 
 #include "libavutil/channel_layout.h"
 #include "libavutil/common.h"
+#include "libavutil/intreadwrite.h"
+#include "libavutil/mem.h"
 #include "libavutil/opt.h"
 #include "avcodec.h"
 #include "codec_internal.h"
@@ -62,7 +64,12 @@ typedef struct FDKAACDecContext {
 #if FDKDEC_VER_AT_LEAST(2, 5) // 2.5.10
     int output_delay_set;
     int flush_samples;
+    int skip_samples;
     int delay_samples;
+    int output_delay;
+    int discard_padding;
+    int64_t last_pts;
+    int64_t last_dts;
 #endif
     AVChannelLayout downmix_layout;
 } FDKAACDecContext;
@@ -137,6 +144,10 @@ static int get_stream_info(AVCodecContext *avctx, AVFrame 
*frame)
         // Set this only once.
         s->flush_samples    = info->outputDelay;
         s->delay_samples    = info->outputDelay;
+        s->skip_samples     = info->outputDelay;
+        s->output_delay     = info->outputDelay;
+        s->last_pts         = AV_NOPTS_VALUE;
+        s->last_dts         = AV_NOPTS_VALUE;
         s->output_delay_set = 1;
     }
 #endif
@@ -427,19 +438,76 @@ static int fdk_aac_decode_frame(AVCodecContext *avctx, 
AVFrame *frame,
         goto end;
     frame->nb_samples = avctx->frame_size;
 
+    if ((ret = ff_get_buffer(avctx, frame, 0)) < 0)
+        goto end;
+
 #if FDKDEC_VER_AT_LEAST(2, 5) // 2.5.10
     if (flags & AACDEC_FLUSH) {
+        if (s->last_pts != AV_NOPTS_VALUE)
+            frame->pts = av_sat_add64(s->last_pts,
+                                      av_rescale_q(frame->nb_samples, 
avctx->time_base,
+                                                   (AVRational){ 1, 
avctx->sample_rate }));
+        if (s->last_dts != AV_NOPTS_VALUE)
+            frame->pkt_dts = av_sat_add64(s->last_dts,
+                                          av_rescale_q(frame->nb_samples, 
avctx->time_base,
+                                                       (AVRational){ 1, 
avctx->sample_rate }));
+
         // Only return the right amount of samples at the end; if calling the
         // decoder with AACDEC_FLUSH, it will keep returning frames 
indefinitely.
         frame->nb_samples = FFMIN(s->flush_samples, frame->nb_samples);
         av_log(s, AV_LOG_DEBUG, "Returning %d/%d delayed samples.\n",
                                 frame->nb_samples, s->flush_samples);
+        if (s->skip_samples || s->discard_padding) {
+            AVFrameSideData *sd = av_frame_get_side_data(frame, 
AV_FRAME_DATA_SKIP_SAMPLES);
+            if (!sd) {
+                sd = av_frame_new_side_data(frame, AV_FRAME_DATA_SKIP_SAMPLES, 
10);
+                if (sd)
+                    memset(sd->data, 0, 10);
+            }
+            if (sd && sd->size >= 10) {
+                AV_WL32(sd->data,     s->skip_samples);
+                AV_WL32(sd->data + 4, s->discard_padding);
+            }
+            s->skip_samples = 0;
+            s->discard_padding = 0;
+        }
         s->flush_samples -= frame->nb_samples;
+        s->last_pts = frame->pts;
+        s->last_dts = frame->pkt_dts;
     } else {
-        // Trim off samples from the start to compensate for extra decoder
-        // delay. We could also just adjust the pts, but this avoids
-        // including the extra samples in the output altogether.
-        if (s->delay_samples) {
+        AVFrameSideData *sd = av_frame_get_side_data(frame, 
AV_FRAME_DATA_SKIP_SAMPLES);
+        if (!sd && s->skip_samples) {
+            sd = av_frame_new_side_data(frame, AV_FRAME_DATA_SKIP_SAMPLES, 10);
+            if (sd)
+                memset(sd->data, 0, 10);
+        }
+        if (sd && sd->size >= 10) {
+            int skip_samples = AV_RL32(sd->data);
+            s->discard_padding = AV_RL32(sd->data + 4);
+            if (s->discard_padding >= s->output_delay) {
+                AV_WL32(sd->data + 4, s->discard_padding - s->output_delay);
+                s->discard_padding = s->output_delay;
+            } else
+                AV_WL32(sd->data + 4, 0);
+            AV_WL32(sd->data, skip_samples + s->skip_samples);
+            s->skip_samples = 0;
+        }
+        if (frame->pts != AV_NOPTS_VALUE && s->output_delay) {
+            frame->pts = av_sat_sub64(frame->pts,
+                                      av_rescale_q(s->output_delay,
+                                                   (AVRational){ 1, 
avctx->sample_rate },
+                                                   avctx->time_base));
+            if (frame->pkt_dts != AV_NOPTS_VALUE && s->output_delay)
+                frame->pkt_dts = av_sat_sub64(frame->pkt_dts,
+                                              av_rescale_q(s->output_delay,
+                                                           (AVRational){ 1, 
avctx->sample_rate },
+                                                           avctx->time_base));
+            s->delay_samples = 0;
+            s->last_pts = frame->pts;
+            s->last_dts = frame->pkt_dts;
+        } else if (s->delay_samples) {
+            // Trim off samples from the start to compensate for extra decoder
+            // delay, in the absense of timestamps.
             int drop_samples = FFMIN(s->delay_samples, frame->nb_samples);
             av_log(s, AV_LOG_DEBUG, "Dropping %d/%d delayed samples.\n",
                                     drop_samples, s->delay_samples);
@@ -452,9 +520,6 @@ static int fdk_aac_decode_frame(AVCodecContext *avctx, 
AVFrame *frame,
     }
 #endif
 
-    if ((ret = ff_get_buffer(avctx, frame, 0)) < 0)
-        goto end;
-
     memcpy(frame->extended_data[0], s->decoder_buffer + input_offset,
            avctx->ch_layout.nb_channels * frame->nb_samples *
            av_get_bytes_per_sample(avctx->sample_fmt));
-- 
2.52.0


>From 78f37c74b21c6e90e80af3fd60aa33b80f2d1b89 Mon Sep 17 00:00:00 2001
From: James Almer <[email protected]>
Date: Tue, 15 Sep 2026 22:39:17 -0300
Subject: [PATCH 4/4] avcodec/libfdk-aacdec: clear and reset some fields when
 flushing

The decoder can't know where a seek landed, so the "delay_samples" way to
discard the decoder delay in the first frame is not truly viable, so it needs
to be removed.
Add a call to aacDecoder_DecodeFrame() with the AACDEC_CLRHIST flag set, as the
call to aacDecoder_SetParam() with the AAC_TPDEC_CLEAR_BUFFER setting was
seemingly not enough to truly flush all the fdkaac internal buffers.

Should fix issue #24527.

Signed-off-by: James Almer <[email protected]>
(cherry picked from commit b894a6f7c1c1af8cc1b6c5d949ba2e5ae2ab9b02)
---
 libavcodec/libfdk-aacdec.c | 53 ++++++++++++++++++++------------------
 1 file changed, 28 insertions(+), 25 deletions(-)

diff --git a/libavcodec/libfdk-aacdec.c b/libavcodec/libfdk-aacdec.c
index 29cb733fa5..ef2d4608af 100644
--- a/libavcodec/libfdk-aacdec.c
+++ b/libavcodec/libfdk-aacdec.c
@@ -65,7 +65,6 @@ typedef struct FDKAACDecContext {
     int output_delay_set;
     int flush_samples;
     int skip_samples;
-    int delay_samples;
     int output_delay;
     int discard_padding;
     int64_t last_pts;
@@ -143,7 +142,6 @@ static int get_stream_info(AVCodecContext *avctx, AVFrame 
*frame)
     if (!s->output_delay_set && info->outputDelay) {
         // Set this only once.
         s->flush_samples    = info->outputDelay;
-        s->delay_samples    = info->outputDelay;
         s->skip_samples     = info->outputDelay;
         s->output_delay     = info->outputDelay;
         s->last_pts         = AV_NOPTS_VALUE;
@@ -399,7 +397,6 @@ static int fdk_aac_decode_frame(AVCodecContext *avctx, 
AVFrame *frame,
     AAC_DECODER_ERROR err;
     UINT valid = avpkt->size;
     UINT flags = 0;
-    int input_offset = 0;
 
     if (avpkt->size) {
         err = aacDecoder_Fill(s->handle, &avpkt->data, &avpkt->size, &valid);
@@ -492,35 +489,22 @@ static int fdk_aac_decode_frame(AVCodecContext *avctx, 
AVFrame *frame,
             AV_WL32(sd->data, skip_samples + s->skip_samples);
             s->skip_samples = 0;
         }
-        if (frame->pts != AV_NOPTS_VALUE && s->output_delay) {
+        if (frame->pts != AV_NOPTS_VALUE && s->output_delay)
             frame->pts = av_sat_sub64(frame->pts,
                                       av_rescale_q(s->output_delay,
                                                    (AVRational){ 1, 
avctx->sample_rate },
                                                    avctx->time_base));
-            if (frame->pkt_dts != AV_NOPTS_VALUE && s->output_delay)
-                frame->pkt_dts = av_sat_sub64(frame->pkt_dts,
-                                              av_rescale_q(s->output_delay,
-                                                           (AVRational){ 1, 
avctx->sample_rate },
-                                                           avctx->time_base));
-            s->delay_samples = 0;
-            s->last_pts = frame->pts;
-            s->last_dts = frame->pkt_dts;
-        } else if (s->delay_samples) {
-            // Trim off samples from the start to compensate for extra decoder
-            // delay, in the absense of timestamps.
-            int drop_samples = FFMIN(s->delay_samples, frame->nb_samples);
-            av_log(s, AV_LOG_DEBUG, "Dropping %d/%d delayed samples.\n",
-                                    drop_samples, s->delay_samples);
-            s->delay_samples  -= drop_samples;
-            frame->nb_samples -= drop_samples;
-            input_offset = drop_samples * avctx->ch_layout.nb_channels;
-            if (frame->nb_samples <= 0)
-                return 0;
-        }
+        if (frame->pkt_dts != AV_NOPTS_VALUE && s->output_delay)
+            frame->pkt_dts = av_sat_sub64(frame->pkt_dts,
+                                          av_rescale_q(s->output_delay,
+                                                       (AVRational){ 1, 
avctx->sample_rate },
+                                                       avctx->time_base));
+        s->last_pts = frame->pts;
+        s->last_dts = frame->pkt_dts;
     }
 #endif
 
-    memcpy(frame->extended_data[0], s->decoder_buffer + input_offset,
+    memcpy(frame->extended_data[0], s->decoder_buffer,
            avctx->ch_layout.nb_channels * frame->nb_samples *
            av_get_bytes_per_sample(avctx->sample_fmt));
 
@@ -542,6 +526,25 @@ static av_cold void fdk_aac_decode_flush(AVCodecContext 
*avctx)
     if ((err = aacDecoder_SetParam(s->handle,
                                    AAC_TPDEC_CLEAR_BUFFER, 1)) != AAC_DEC_OK)
         av_log(avctx, AV_LOG_WARNING, "failed to clear buffer when 
flushing\n");
+
+#if FDKDEC_VER_AT_LEAST(2, 5) // 2.5.10
+    s->skip_samples = 0;
+    s->discard_padding = 0;
+    s->flush_samples = 0;
+    s->output_delay = 0;
+    s->last_pts = AV_NOPTS_VALUE;
+    s->last_dts = AV_NOPTS_VALUE;
+    s->output_delay_set = 0;
+
+    // Call aacDecoder_DecodeFrame() with flush and clear history flags as the
+    // above aacDecoder_SetParam() call is seemingly not sufficient.
+    // Ignore the return code given it will not be AAC_DEC_OK if nothing is
+    // buffered internally (e.g. trying to flush the decoder before passing a
+    // single packet to it).
+    aacDecoder_DecodeFrame(s->handle, (INT_PCM *) s->decoder_buffer,
+                           s->decoder_buffer_size / sizeof(INT_PCM),
+                           AACDEC_FLUSH | AACDEC_CLRHIST);
+#endif
 }
 
 const FFCodec ff_libfdk_aac_decoder = {
-- 
2.52.0

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

Reply via email to