PR #24576 opened by James Almer (jamrial) URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/24576 Patch URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/24576.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 1b8a5568306538106c313a48f5b2004cb6f9eeae 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 11eee51a98..5d476687ed 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 c53a38a433cf756f84f1e973659a2ec437c05692 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 5d476687ed..79f450d27e 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 1c2f0e8ececb748f6db882d3314df333cefb182f 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 79f450d27e..5a9b3c6bf2 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 a1332ad5c557449b1cb8f6d41517b90ab1ec28d2 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 5a9b3c6bf2..67211a7aed 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]
