PR #25146 opened by JuliusBairaktaris URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/25146 Patch URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/25146.patch
The AMF decoders do not drop frames decoded from packets flagged AV_PKT_FLAG_DISCARD. The mov demuxer sets that flag before the start of an edit list, so MP4 input whose edit list starts after the first sample decodes to extra leading frames with negative pts. - `avcodec/amfdec: drop frames decoded from discarded packets` Not covered: the Linux AMF runtime and other GPU generations; there is no FATE test because the path needs AMD hardware. Seven MP4 inputs with edit lists (H.264, AV1, VP9, 8- and 10-bit HEVC) now match the software decoders in frame count, pts and framemd5, for both system memory and AMF surface output; HEVC goes from 488 to 480 frames. Inputs without discarded packets are unchanged. Tested on Windows 11, RX 9070 XT, driver 32.0.31041.1004. Written with Claude Opus 5.5 in Claude Code; `Assisted-by` trailer on the commit. >From 2f7e9e2f692534bcab9a070bbf66220930ca3f76 Mon Sep 17 00:00:00 2001 From: Julius Bairaktaris <[email protected]> Date: Mon, 28 Sep 2026 07:15:01 +0200 Subject: [PATCH] avcodec/amfdec: drop frames decoded from discarded packets The AMF decoders do not drop frames decoded from packets flagged AV_PKT_FLAG_DISCARD. System memory output ignores the flag, and AMF surface output takes it from the last submitted packet instead of the frame's own. Record the pts of each discarded packet and set AV_FRAME_FLAG_DISCARD on output frames with that pts; the list is cleared on flush. The pts is the key because a custom input buffer property does not reach the shown frame of an AV1 temporal unit or VP9 superframe that also holds hidden frames. Assisted-by: Claude Opus 5.5 Signed-off-by: Julius Bairaktaris <[email protected]> --- libavcodec/amfdec.c | 36 ++++++++++++++++++++++++++++++++++++ libavcodec/amfdec.h | 3 +++ 2 files changed, 39 insertions(+) diff --git a/libavcodec/amfdec.c b/libavcodec/amfdec.c index 00a17360f5..9112efafd2 100644 --- a/libavcodec/amfdec.c +++ b/libavcodec/amfdec.c @@ -18,6 +18,7 @@ #include "libavutil/hwcontext_amf.h" #include "libavutil/hwcontext_amf_internal.h" +#include "libavutil/mem.h" #include "libavutil/pixdesc.h" #include "amfdec.h" #include "codec_internal.h" @@ -206,6 +207,7 @@ static int amf_decode_close(AVCodecContext *avctx) av_buffer_unref(&ctx->device_ctx_ref); av_packet_free(&ctx->in_pkt); + av_freep(&ctx->discard_pts); return 0; } @@ -503,6 +505,30 @@ static int amf_attach_hdr_metadata(AVCodecContext *avctx, AMFSurface *surface, A return ret; } +static int amf_add_discard_pts(AMFDecoderContext *ctx, int64_t pts) +{ + int64_t *discard_pts = av_fast_realloc(ctx->discard_pts, &ctx->discard_pts_size, + (ctx->nb_discard_pts + 1) * sizeof(*discard_pts)); + if (!discard_pts) + return AVERROR(ENOMEM); + ctx->discard_pts = discard_pts; + ctx->discard_pts[ctx->nb_discard_pts++] = pts; + return 0; +} + +static int amf_take_discard_pts(AMFDecoderContext *ctx, int64_t pts) +{ + for (int i = 0; i < ctx->nb_discard_pts; i++) { + if (ctx->discard_pts[i] == pts) { + ctx->nb_discard_pts--; + memmove(&ctx->discard_pts[i], &ctx->discard_pts[i + 1], + (ctx->nb_discard_pts - i) * sizeof(*ctx->discard_pts)); + return 1; + } + } + return 0; +} + static int amf_amfsurface_to_avframe(AVCodecContext *avctx, AMFSurface* surface, AVFrame *frame) { AMFVariantStruct var = {0}; @@ -561,6 +587,11 @@ static int amf_amfsurface_to_avframe(AVCodecContext *avctx, AMFSurface* surface, frame->pts = surface->pVtbl->GetPts(surface); + if (amf_take_discard_pts(avctx->priv_data, frame->pts)) + frame->flags |= AV_FRAME_FLAG_DISCARD; + else + frame->flags &= ~AV_FRAME_FLAG_DISCARD; + surface->pVtbl->GetProperty(surface, L"FFMPEG:dts", &var); frame->pkt_dts = var.int64Value; @@ -730,6 +761,10 @@ static int amf_decode_frame(AVCodecContext *avctx, struct AVFrame *frame) } else if (res != AMF_OK && res != AMF_NEED_MORE_INPUT && res != AMF_REPEAT) { av_log(avctx, AV_LOG_ERROR, "SubmitInput() returned error %d\n", res); return AVERROR(EINVAL); + } else if ((avpkt->flags & AV_PKT_FLAG_DISCARD) && avpkt->pts != AV_NOPTS_VALUE) { + int ret = amf_add_discard_pts(ctx, avpkt->pts); + if (ret < 0) + return ret; } } @@ -781,6 +816,7 @@ static void amf_decode_flush(AVCodecContext *avctx) { AMFDecoderContext *ctx = avctx->priv_data; ctx->decoder->pVtbl->Flush(ctx->decoder); + ctx->nb_discard_pts = 0; } #define OFFSET(x) offsetof(AMFDecoderContext, x) diff --git a/libavcodec/amfdec.h b/libavcodec/amfdec.h index fd5e00f9db..4372481964 100644 --- a/libavcodec/amfdec.h +++ b/libavcodec/amfdec.h @@ -57,6 +57,9 @@ typedef struct AMFDecoderContext { int copy_output; int dimensions_initialized; AVPacket* in_pkt; + int64_t *discard_pts; + unsigned int discard_pts_size; + int nb_discard_pts; enum AMF_SURFACE_FORMAT output_format; } AMFDecoderContext; -- 2.54.0 _______________________________________________ ffmpeg-devel mailing list -- [email protected] To unsubscribe send an email to [email protected]
