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]
