PR #25637 opened by JuliusBairaktaris
URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/25637
Patch URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/25637.patch

Three fixes found while following up #25525, where the review listed the 
surface buffers that still carry no device reference.

- `avfilter/vsrc_amf: keep the AMF device alive while frames hold surfaces`: 
the captured surface's buffer now holds a reference to the AMF device context 
and releases the surface before dropping it, as amfdec does since #25525. 
Without it, a frame that outlives the filter graph releases a surface of a 
terminated context.
- `avfilter/vsrc_amf: free the frame and the surface on output errors`: 
Convert() and buffer allocation failures leaked the frame and the surface and 
reported AVERROR_UNKNOWN; the hw_frames_ctx reference is checked too.
- `avutil/hwcontext_amf: report the frames context's sw_format for transfers`: 
`amf_transfer_get_formats()` returned a fixed list of seven formats, while the 
transfers accept only `sw_format`. hwdownload therefore either configured a 
format the transfer rejected with EINVAL or, for BGR0 (the mapping of 
AMF_SURFACE_BGRA, which the capture source produces), refused the download at 
configuration. With the list reduced to `sw_format`, the captured frames can be 
downloaded.

Tested on Windows 11, RX 9070 XT, driver 32.0.32015.7029: `-filter_complex 
vsrc_amf=framerate=30,hwdownload,format=bgr0` delivers 30 frames with an 
explicit AMF device and with the filter's own device (on master, hwdownload 
refuses bgr0 and fails bgra with EINVAL); d3d11va decode through vpp_amf and 
hwdownload gives the same framemd5 as master; hevc_amf 10-bit AMF surface 
output downloads as p010 as before.

Written with Claude Mythos 5.1 in Claude Code; `Assisted-by` trailers on the 
commits.


>From dd20a9c0938b29ecaaffd9677ad7838e9379393f Mon Sep 17 00:00:00 2001
From: Julius Bairaktaris <[email protected]>
Date: Sat, 31 Oct 2026 09:42:58 +0100
Subject: [PATCH 1/3] avfilter/vsrc_amf: keep the AMF device alive while frames
 hold surfaces

The frames the capture source outputs own their AMFSurface through an
AVBufferRef whose free callback releases the surface. In software
output mode the frame has no hw_frames_ctx, so nothing ties the frame
to the AMF device; once the filter graph is freed, releasing a frame
that is still held by the application releases a surface of a
terminated context. Hardware frames rely on their hw_frames_ctx
reference, which does not cover a surface buffer that is retained
independently of its frame.

Make the surface buffer hold a reference to the device context and
release the surface before dropping it, as amfdec does since the fix
for #21326.

Assisted-by: Claude Mythos 5.1
Signed-off-by: Julius Bairaktaris <[email protected]>
---
 libavfilter/vsrc_amf.c | 39 ++++++++++++++++++++++++++-------------
 1 file changed, 26 insertions(+), 13 deletions(-)

diff --git a/libavfilter/vsrc_amf.c b/libavfilter/vsrc_amf.c
index e3fb8b9d38d..bde2bd9610d 100644
--- a/libavfilter/vsrc_amf.c
+++ b/libavfilter/vsrc_amf.c
@@ -107,11 +107,27 @@ static void amf_restore_timer_precision(AMFGrabContext 
*ctx)
 
 static void amf_release_surface(void *opaque, uint8_t *data)
 {
-    if(!!data){
-        AMFInterface *surface = (AMFInterface*)(data);
-        if (surface && surface->pVtbl)
-            surface->pVtbl->Release(surface);
+    AMFInterface *surface = (AMFInterface*)data;
+    AVBufferRef *device_ref = opaque;
+
+    if (surface && surface->pVtbl)
+        surface->pVtbl->Release(surface);
+    av_buffer_unref(&device_ref);
+}
+
+static int amf_wrap_surface(AMFGrabContext *ctx, AVFrame *frame, AMFSurface 
*surface, int flags)
+{
+    AVBufferRef *device_ref = av_buffer_ref(ctx->device_ctx_ref);
+
+    if (!device_ref)
+        return AVERROR(ENOMEM);
+    frame->buf[0] = av_buffer_create((uint8_t *)surface, sizeof(surface),
+                                     amf_release_surface, device_ref, flags);
+    if (!frame->buf[0]) {
+        av_buffer_unref(&device_ref);
+        return AVERROR(ENOMEM);
     }
+    return 0;
 }
 
 static av_cold void amf_uninit(AVFilterContext *avctx)
@@ -345,14 +361,13 @@ static int amf_capture_frame(AVFilterLink *outlink)
     if (fl->hw_frames_ctx) {
         frame->format =  AV_PIX_FMT_AMF_SURFACE;
         frame->data[0] = (uint8_t*)surface;
-        frame->buf[0] = av_buffer_create((uint8_t*)surface, sizeof(surface),
-                                         amf_release_surface, NULL, 0);
-        frame->hw_frames_ctx = av_buffer_ref(fl->hw_frames_ctx);
-        if (!frame->buf[0]) {
+        ret = amf_wrap_surface(ctx, frame, surface, 0);
+        if (ret < 0) {
             av_frame_free(&frame);
             surface->pVtbl->Release(surface);
-            return AVERROR(ENOMEM);
+            return ret;
         }
+        frame->hw_frames_ctx = av_buffer_ref(fl->hw_frames_ctx);
     } else {
         ret = surface->pVtbl->Convert(surface, AMF_MEMORY_HOST);
         AMF_RETURN_IF_FALSE(avctx, ret == AMF_OK, AVERROR_UNKNOWN, 
"Convert(amf::AMF_MEMORY_HOST) failed with error %d\n", ret);
@@ -363,10 +378,8 @@ static int amf_capture_frame(AVFilterLink *outlink)
             frame->linesize[i] = plane->pVtbl->GetHPitch(plane);
         }
 
-        frame->buf[0] = av_buffer_create((uint8_t *)surface, sizeof(surface),
-                                         amf_release_surface, (void*)avctx,
-                                         AV_BUFFER_FLAG_READONLY);
-        AMF_RETURN_IF_FALSE(avctx, !!frame->buf[0], AVERROR(ENOMEM), 
"av_buffer_create for amf surface failed.");
+        ret = amf_wrap_surface(ctx, frame, surface, AV_BUFFER_FLAG_READONLY);
+        AMF_RETURN_IF_FALSE(avctx, ret >= 0, ret, "av_buffer_create for amf 
surface failed.");
 
         format_amf = surface->pVtbl->GetFormat(surface);
         frame->format = av_amf_to_av_format(format_amf);
-- 
2.54.0


>From 45bd03acbebe6ec2215c1bb8ece7b5eeba087845 Mon Sep 17 00:00:00 2001
From: Julius Bairaktaris <[email protected]>
Date: Sat, 31 Oct 2026 09:42:58 +0100
Subject: [PATCH 2/3] avfilter/vsrc_amf: free the frame and the surface on
 output errors

When Convert() or the surface buffer allocation fails after the output
frame was allocated, the frame and the captured surface leak, and the
Convert() failure is reported as AVERROR_UNKNOWN. Release both on every
error path and return AVERROR_EXTERNAL for the failed AMF call. Also
check the hw_frames_ctx reference allocation of hardware frames.

Assisted-by: Claude Mythos 5.1
Signed-off-by: Julius Bairaktaris <[email protected]>
---
 libavfilter/vsrc_amf.c | 28 ++++++++++++++++++++--------
 1 file changed, 20 insertions(+), 8 deletions(-)

diff --git a/libavfilter/vsrc_amf.c b/libavfilter/vsrc_amf.c
index bde2bd9610d..8a3d70c6239 100644
--- a/libavfilter/vsrc_amf.c
+++ b/libavfilter/vsrc_amf.c
@@ -362,15 +362,20 @@ static int amf_capture_frame(AVFilterLink *outlink)
         frame->format =  AV_PIX_FMT_AMF_SURFACE;
         frame->data[0] = (uint8_t*)surface;
         ret = amf_wrap_surface(ctx, frame, surface, 0);
-        if (ret < 0) {
-            av_frame_free(&frame);
-            surface->pVtbl->Release(surface);
-            return ret;
-        }
+        if (ret < 0)
+            goto fail;
         frame->hw_frames_ctx = av_buffer_ref(fl->hw_frames_ctx);
+        if (!frame->hw_frames_ctx) {
+            ret = AVERROR(ENOMEM);
+            goto fail;
+        }
     } else {
-        ret = surface->pVtbl->Convert(surface, AMF_MEMORY_HOST);
-        AMF_RETURN_IF_FALSE(avctx, ret == AMF_OK, AVERROR_UNKNOWN, 
"Convert(amf::AMF_MEMORY_HOST) failed with error %d\n", ret);
+        res = surface->pVtbl->Convert(surface, AMF_MEMORY_HOST);
+        if (res != AMF_OK) {
+            av_log(avctx, AV_LOG_ERROR, "Convert(amf::AMF_MEMORY_HOST) failed 
with error %d\n", res);
+            ret = AVERROR_EXTERNAL;
+            goto fail;
+        }
 
         for (i = 0; i < surface->pVtbl->GetPlanesCount(surface); i++) {
             plane = surface->pVtbl->GetPlaneAt(surface, i);
@@ -379,13 +384,20 @@ static int amf_capture_frame(AVFilterLink *outlink)
         }
 
         ret = amf_wrap_surface(ctx, frame, surface, AV_BUFFER_FLAG_READONLY);
-        AMF_RETURN_IF_FALSE(avctx, ret >= 0, ret, "av_buffer_create for amf 
surface failed.");
+        if (ret < 0)
+            goto fail;
 
         format_amf = surface->pVtbl->GetFormat(surface);
         frame->format = av_amf_to_av_format(format_amf);
     }
 
     return ff_filter_frame(outlink, frame);
+
+fail:
+    if (!frame->buf[0])
+        surface->pVtbl->Release(surface);
+    av_frame_free(&frame);
+    return ret;
 }
 
 static const AVFilterPad amf_outputs[] = {
-- 
2.54.0


>From 286c834155ad45b8451477f50742ecb37e7296cf Mon Sep 17 00:00:00 2001
From: Julius Bairaktaris <[email protected]>
Date: Sat, 31 Oct 2026 12:02:47 +0100
Subject: [PATCH 3/3] avutil/hwcontext_amf: report the frames context's
 sw_format for transfers

amf_transfer_get_formats() returns a fixed list of seven formats, while
amf_transfer_data_to() and amf_transfer_data_from() accept only the
frames context's sw_format. Callers that pick from the list, such as
hwdownload and the ffmpeg CLI's software output of the AMF decoders,
therefore configure a format the transfer then rejects with EINVAL
whenever sw_format is not the first entry, and formats the transfer
does accept, like BGR0 (the mapping of AMF_SURFACE_BGRA), are missing
from the list: hwdownload refuses them at configuration. The frames of
the vsrc_amf capture source, whose surfaces are BGRA, cannot be
downloaded at all.

Return sw_format as the only transferable format.

Assisted-by: Claude Mythos 5.1
Signed-off-by: Julius Bairaktaris <[email protected]>
---
 libavutil/hwcontext_amf.c | 18 +++---------------
 1 file changed, 3 insertions(+), 15 deletions(-)

diff --git a/libavutil/hwcontext_amf.c b/libavutil/hwcontext_amf.c
index 7f47df5b59d..76e1ef073dd 100644
--- a/libavutil/hwcontext_amf.c
+++ b/libavutil/hwcontext_amf.c
@@ -330,17 +330,6 @@ static const enum AVPixelFormat supported_formats[] = {
 #endif
 };
 
-static const enum AVPixelFormat supported_transfer_formats[] = {
-    AV_PIX_FMT_NV12,
-    AV_PIX_FMT_YUV420P,
-    AV_PIX_FMT_BGRA,
-    AV_PIX_FMT_RGBA,
-    AV_PIX_FMT_P010,
-    AV_PIX_FMT_X2BGR10,
-    AV_PIX_FMT_RGBAF16,
-    AV_PIX_FMT_NONE,
-};
-
 static int amf_frames_get_constraints(AVHWDeviceContext *ctx,
                                        const void *hwconfig,
                                        AVHWFramesConstraints *constraints)
@@ -424,13 +413,12 @@ static int amf_transfer_get_formats(AVHWFramesContext 
*ctx,
                                      enum AVPixelFormat **formats)
 {
     enum AVPixelFormat *fmts;
-    int i;
 
-    fmts = av_malloc_array(FF_ARRAY_ELEMS(supported_transfer_formats), 
sizeof(*fmts));
+    fmts = av_malloc_array(2, sizeof(*fmts));
     if (!fmts)
         return AVERROR(ENOMEM);
-    for (i = 0; i < FF_ARRAY_ELEMS(supported_transfer_formats); i++)
-        fmts[i] = supported_transfer_formats[i];
+    fmts[0] = ctx->sw_format;
+    fmts[1] = AV_PIX_FMT_NONE;
 
     *formats = fmts;
 
-- 
2.54.0

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

Reply via email to