[PR] lavc/cudivdec: discard frames decoded from AV_PKT_FLAG_DISCARD (PR #24321)

Romain Beauxis via ffmpeg-devel <[email protected]>
Newsgroups gmane.comp.video.ffmpeg.devel
Message-ID <[email protected]>
PR #24321 opened by Romain Beauxis (toots)
URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/24321
Patch URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/24321.patch

This is a follow-up from https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/23839. 

Looking at discarding decoded frames from packets marked with `AV_PKT_FLAG_DISCARD`, the cuvid decoder appears to not be able to do that yet so this PR adds this functionality.

There's also an initial commit fixing small gap where memory is extended but not zeroed.

Worth noting: I do not have the hardware to properly test those changes.


>From f13ddc0eb2874f079c844fed22e38afc75a1317b Mon Sep 17 00:00:00 2001
From: Romain Beauxis <[email protected]>
Date: Sat, 29 Aug 2026 13:46:17 -0500
Subject: [PATCH 1/2] avcodec/cuviddec: clear the grown key frame array tail

av_reallocp_array() leaves the added entries uninitialized and a slot is
written only for an intra picture, so a surface index first used after a
growth can report an arbitrary value as a key frame.
---
 libavcodec/cuviddec.c | 13 +++++++++----
 1 file changed, 9 insertions(+), 4 deletions(-)

diff --git a/libavcodec/cuviddec.c b/libavcodec/cuviddec.c
index 14744cf067..151c401ed7 100644
--- a/libavcodec/cuviddec.c
+++ b/libavcodec/cuviddec.c
@@ -582,10 +582,15 @@ static int CUDAAPI cuvid_handle_video_sequence(void *opaque, CUVIDEOFORMAT* form
         return 0;
     }
 
-    if (ctx->nb_surfaces > old_nb_surfaces && av_reallocp_array(&ctx->key_frame, ctx->nb_surfaces, sizeof(int)) < 0) {
-        av_log(avctx, AV_LOG_ERROR, "Failed to grow key frame array on video sequence callback\n");
-        ctx->internal_error = AVERROR(ENOMEM);
-        return 0;
+    if (ctx->nb_surfaces > old_nb_surfaces) {
+        if (av_reallocp_array(&ctx->key_frame, ctx->nb_surfaces, sizeof(*ctx->key_frame)) < 0) {
+            av_log(avctx, AV_LOG_ERROR, "Failed to grow key frame array on video sequence callback\n");
+            ctx->internal_error = AVERROR(ENOMEM);
+            return 0;
+        }
+        /* A slot is written only for an intra picture, so it has to start clear. */
+        memset(ctx->key_frame + old_nb_surfaces, 0,
+               (ctx->nb_surfaces - old_nb_surfaces) * sizeof(*ctx->key_frame));
     }
 
     cuinfo.ulNumDecodeSurfaces = ctx->nb_surfaces;
-- 
2.52.0


>From ba0e0129c361e56126499fb3e3acbcb9bbda3adc Mon Sep 17 00:00:00 2001
From: Romain Beauxis <[email protected]>
Date: Sat, 29 Aug 2026 13:46:43 -0500
Subject: [PATCH 2/2] avcodec/cuviddec: honor AV_PKT_FLAG_DISCARD

Decoders declaring FF_CODEC_CAP_SETS_FRAME_PROPS skip the generic packet
property step, so the AV_PKT_FLAG_DISCARD to AV_FRAME_FLAG_DISCARD copy
that ff_decode_frame_props_from_pkt() performs never runs for them.
libdav1d and libxevd call it themselves; cuvid did not, and returned
frames the caller had asked to have dropped.

The packet is unreferenced as soon as it has been parsed, and cuvid
reorders, so the flag travels the way the key frame flag already does:
recorded per picture index in the decode callback, which runs inside the
parse of the packet the picture comes from, then carried on the queued
frame so both fields of a deinterlaced picture keep it.
---
 libavcodec/cuviddec.c | 35 +++++++++++++++++++++++++++++++++++
 1 file changed, 35 insertions(+)

diff --git a/libavcodec/cuviddec.c b/libavcodec/cuviddec.c
index 151c401ed7..fa2c4ea3b0 100644
--- a/libavcodec/cuviddec.c
+++ b/libavcodec/cuviddec.c
@@ -107,6 +107,8 @@ typedef struct CuvidContext
     atomic_int abort_decode;
 
     int *key_frame;
+    int *discard_frame;
+    int pkt_discard;
 
     cudaVideoCodec codec_type;
     cudaVideoChromaFormat chroma_format;
@@ -137,6 +139,7 @@ typedef struct CuvidParsedFrame
     CUVIDPARSERDISPINFO dispinfo;
     int second_field;
     int is_deinterlacing;
+    int discard;
 } CuvidParsedFrame;
 
 #define CHECK_CU(x) FF_CUDA_CHECK_DL(avctx, ctx->cudl, x)
@@ -591,6 +594,12 @@ static int CUDAAPI cuvid_handle_video_sequence(void *opaque, CUVIDEOFORMAT* form
         /* A slot is written only for an intra picture, so it has to start clear. */
         memset(ctx->key_frame + old_nb_surfaces, 0,
                (ctx->nb_surfaces - old_nb_surfaces) * sizeof(*ctx->key_frame));
+
+        if (av_reallocp_array(&ctx->discard_frame, ctx->nb_surfaces, sizeof(*ctx->discard_frame)) < 0) {
+            av_log(avctx, AV_LOG_ERROR, "Failed to grow discard frame array on video sequence callback\n");
+            ctx->internal_error = AVERROR(ENOMEM);
+            return 0;
+        }
     }
 
     cuinfo.ulNumDecodeSurfaces = ctx->nb_surfaces;
@@ -749,6 +758,10 @@ static int CUDAAPI cuvid_handle_picture_decode(void *opaque, CUVIDPICPARAMS* pic
     if(picparams->intra_pic_flag)
         ctx->key_frame[picparams->CurrPicIdx] = picparams->intra_pic_flag;
 
+    /* This is the only point where the picture is still tied to the packet it
+     * came from, display order being unrelated to it. */
+    ctx->discard_frame[picparams->CurrPicIdx] = ctx->pkt_discard;
+
 #ifdef NVDEC_HAVE_OPAQUE_OUTPUT_SUPPORT
     if (ctx->opaque_output) {
         if (ctx->surface_in_use &&
@@ -790,6 +803,10 @@ static int CUDAAPI cuvid_handle_picture_display(void *opaque, CUVIDPARSERDISPINF
     parsed_frame.dispinfo = *dispinfo;
     ctx->internal_error = 0;
 
+    /* Carried per frame rather than read at output time, so that both fields of
+     * a deinterlaced picture get it. */
+    parsed_frame.discard = ctx->discard_frame[dispinfo->picture_index];
+
     // For some reason, dispinfo->progressive_frame is sometimes wrong.
     parsed_frame.dispinfo.progressive_frame = ctx->progressive_sequence;
 
@@ -853,6 +870,9 @@ static int cuvid_decode_packet(AVCodecContext *avctx, const AVPacket *avpkt)
 
     memset(&cupkt, 0, sizeof(cupkt));
 
+    ctx->pkt_discard = avpkt && avpkt->size &&
+                       (avpkt->flags & AV_PKT_FLAG_DISCARD);
+
     if (avpkt && avpkt->size) {
         cupkt.payload_size = avpkt->size;
         cupkt.payload = avpkt->data;
@@ -1186,6 +1206,11 @@ static int cuvid_output_frame(AVCodecContext *avctx, AVFrame *frame)
             frame->flags &= ~AV_FRAME_FLAG_KEY;
         ctx->key_frame[parsed_frame.dispinfo.picture_index] = 0;
 
+        if (parsed_frame.discard)
+            frame->flags |= AV_FRAME_FLAG_DISCARD;
+        else
+            frame->flags &= ~AV_FRAME_FLAG_DISCARD;
+
         frame->width = avctx->width;
         frame->height = avctx->height;
         if (avctx->pkt_timebase.num && avctx->pkt_timebase.den)
@@ -1295,6 +1320,7 @@ static av_cold int cuvid_decode_end(AVCodecContext *avctx)
     av_buffer_unref(&ctx->hwdevice);
 
     av_freep(&ctx->key_frame);
+    av_freep(&ctx->discard_frame);
     av_freep(&ctx->cuparse_ext);
 
     cuvid_free_functions(&ctx->cvdl);
@@ -1673,6 +1699,12 @@ static av_cold int cuvid_decode_init(AVCodecContext *avctx)
         goto error;
     }
 
+    ctx->discard_frame = av_mallocz(ctx->nb_surfaces * sizeof(*ctx->discard_frame));
+    if (!ctx->discard_frame) {
+        ret = AVERROR(ENOMEM);
+        goto error;
+    }
+
     ctx->cuparseinfo.ulMaxNumDecodeSurfaces = 1;
     ctx->cuparseinfo.ulMaxDisplayDelay = (avctx->flags & AV_CODEC_FLAG_LOW_DELAY) ? 0 : CUVID_MAX_DISPLAY_DELAY;
     ctx->cuparseinfo.pUserData = avctx;
@@ -1729,6 +1761,9 @@ static void cuvid_flush(AVCodecContext *avctx)
     CUVIDSOURCEDATAPACKET seq_pkt = { 0 };
     int ret;
 
+    /* The sequence header replayed below belongs to no packet. */
+    ctx->pkt_discard = 0;
+
     ret = CHECK_CU(ctx->cudl->cuCtxPushCurrent(cuda_ctx));
     if (ret < 0)
         goto error;
-- 
2.52.0

_______________________________________________
ffmpeg-devel mailing list -- [email protected]
To unsubscribe send an email to [email protected]
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.