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]

Reply via email to