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

The AMF filters output AMF surfaces. A D3D11 consumer gets them only through 
hwdownload and a new upload, although on a D3D11VA-derived AMF device they 
already are textures on that device.

- `avutil/hwcontext_amf: do not map to system memory for a hardware target`: 
hwmap to a hardware format now fails instead of passing a system memory frame 
on.
- `avutil/hwcontext_amf: map AMF surfaces to D3D11 frames`: exports the texture 
and array slice; lavu 61.14.101 and an APIchanges entry.

Usage: `-vf vqe_amf,hwmap=derive_device=d3d11va,format=d3d11`

Not covered: DXVA2 and D3D12 targets, Linux.

framemd5 over 300 frames matches the hwdownload path for vqe_amf, sr_amf, 
vpp_amf (scale and bgra) and 10-bit vqe_amf. 1200 frames, median of 3: utime 
0.594 s → 0.219 s, stime 0.625 s → 0.266 s, rtime 1.306 s → 1.209 s. Tested on 
Windows 11, RX 9070 XT, driver 32.0.31041.1004.

Written with Claude Opus 5.5 in Claude Code; `Assisted-by` trailers on the 
commits.



From df7524ee55326acfd638128d9d309b5de408f2cf Mon Sep 17 00:00:00 2001
From: Julius Bairaktaris <[email protected]>
Date: Mon, 28 Sep 2026 11:22:38 +0200
Subject: [PATCH 1/2] avutil/hwcontext_amf: do not map to system memory for a
 hardware target

amf_map_frame() ignores dst->format and always maps to system memory.
hwmap to a hardware format therefore passes a system memory frame down a
hardware link, and the next filter fails. Return AVERROR(ENOSYS) for a
hardware destination format. Mapping to system memory is unchanged.

Assisted-by: Claude Opus 5.5
Signed-off-by: Julius Bairaktaris <[email protected]>
---
 libavutil/hwcontext_amf.c | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/libavutil/hwcontext_amf.c b/libavutil/hwcontext_amf.c
index 7f47df5b59..de785db68a 100644
--- a/libavutil/hwcontext_amf.c
+++ b/libavutil/hwcontext_amf.c
@@ -931,6 +931,10 @@ static int amf_map_frame(AVHWFramesContext *device_ctx, 
AVFrame *dst, const AVFr
     size_t plane_count = 0;
     int ret = -1;
 
+    if (dst->format != AV_PIX_FMT_NONE &&
+        av_pix_fmt_desc_get(dst->format)->flags & AV_PIX_FMT_FLAG_HWACCEL)
+        return AVERROR(ENOSYS);
+
     AMF_RETURN_IF_FALSE(device_ctx, surface != NULL, AVERROR(EINVAL) , "Source 
surface is null");
 
     res = AMF_IFACE_CALL(surface, QueryInterface, &guid, (void**)&surface1);
-- 
2.54.0


From bdb29eff08a91db943e17be20655c2fda7385eb3 Mon Sep 17 00:00:00 2001
From: Julius Bairaktaris <[email protected]>
Date: Mon, 28 Sep 2026 11:23:11 +0200
Subject: [PATCH 2/2] avutil/hwcontext_amf: map AMF surfaces to D3D11 frames

AMF surfaces reach a D3D11 consumer only through hwdownload and a new
upload, although on an AMF device derived from D3D11VA they already are
textures on that device. Export the surface's texture and array slice as
a D3D11 frame, converting the surface to AMF_MEMORY_DX11 first if
needed. The mapping requires the AMF context to run on the target's
D3D11 device and keeps a reference to the source frame, so it needs no
unmap callback. hwmap does not restrict its output to the derived
device, so format=d3d11 must follow it.

Assisted-by: Claude Opus 5.5
Signed-off-by: Julius Bairaktaris <[email protected]>
---
 doc/APIchanges            |  3 +++
 libavutil/hwcontext_amf.c | 49 +++++++++++++++++++++++++++++++++++++++
 libavutil/version.h       |  2 +-
 3 files changed, 53 insertions(+), 1 deletion(-)

diff --git a/doc/APIchanges b/doc/APIchanges
index f7f9f5d9e1..cd93194770 100644
--- a/doc/APIchanges
+++ b/doc/APIchanges
@@ -2,6 +2,9 @@ The last version increases of all libraries were on 2026-06-23.
 
 API changes, most recent first:
 
+2026-10-xx - xxxxxxxxxx - lavu 61.14.101 - hwcontext_amf.h
+  AMF frames can be mapped to D3D11 frames with av_hwframe_map().
+
 2026-10-xx - xxxxxxxxxx - lavu 61.14.100 - gain_map.h
   Add av_gain_map_params_from_iso21496() and av_gain_map_params_to_iso21496().
   Add AV_GAIN_MAP_MAX_PAYLOAD_SIZE.
diff --git a/libavutil/hwcontext_amf.c b/libavutil/hwcontext_amf.c
index de785db68a..6c30775d8f 100644
--- a/libavutil/hwcontext_amf.c
+++ b/libavutil/hwcontext_amf.c
@@ -919,6 +919,51 @@ static void amf_unmap_frame(av_unused AVHWFramesContext 
*unused, HWMapDescriptor
     AMF_IFACE_CALL(surface1, Release);
 }
 
+#if CONFIG_D3D11VA
+static int amf_map_to_d3d11(AVHWFramesContext *ctx, AVFrame *dst, const 
AVFrame *src)
+{
+    AVAMFDeviceContext *amf_ctx = ctx->device_ctx->hwctx;
+    AVHWFramesContext *dst_fc;
+    AVD3D11VADeviceContext *d3d11_ctx;
+    static const GUID AMFTextureArrayIndexGUID = { 0x28115527, 0xe7c3, 0x4b66, 
{ 0x99, 0xd3, 0x4f, 0x2a, 0xe6, 0xb4, 0x7f, 0xaf } };
+    AMFSurface *surface = (AMFSurface *)src->data[0];
+    ID3D11Texture2D *texture;
+    AMFPlane *plane;
+    UINT size = sizeof(int);
+    AMF_RESULT res;
+    int index = 0;
+    int ret;
+
+    AMF_RETURN_IF_FALSE(ctx, surface, AVERROR(EINVAL), "Source surface is 
null\n");
+    if (!dst->hw_frames_ctx)
+        return AVERROR(EINVAL);
+    dst_fc    = (AVHWFramesContext *)dst->hw_frames_ctx->data;
+    d3d11_ctx = dst_fc->device_ctx->hwctx;
+    if (AMF_IFACE_CALL(amf_ctx->context, GetDX11Device, AMF_DX11_0) != 
d3d11_ctx->device) {
+        av_log(ctx, AV_LOG_ERROR, "The AMF context does not use the D3D11 
device of the target frames.\n");
+        return AVERROR(EINVAL);
+    }
+    res = AMF_IFACE_CALL(surface, Convert, AMF_MEMORY_DX11);
+    AMF_RETURN_IF_FALSE(ctx, res == AMF_OK, AVERROR(EINVAL), 
"Convert(AMF_MEMORY_DX11) failed with error %d\n", res);
+
+    plane   = AMF_IFACE_CALL(surface, GetPlaneAt, 0);
+    texture = plane->pVtbl->GetNative(plane);
+    if (FAILED(texture->lpVtbl->GetPrivateData(texture, 
&AMFTextureArrayIndexGUID, &size, &index)) ||
+        size != sizeof(index))
+        index = 0;
+
+    ret = ff_hwframe_map_create(dst->hw_frames_ctx, dst, src, NULL, NULL);
+    if (ret < 0)
+        return ret;
+
+    dst->data[0] = (uint8_t *)texture;
+    dst->data[1] = (uint8_t *)(intptr_t)index;
+    dst->width   = src->width;
+    dst->height  = src->height;
+    return 0;
+}
+#endif
+
 static int amf_map_frame(AVHWFramesContext *device_ctx, AVFrame *dst, const 
AVFrame *src, int flags)
 {
     AMFSurface *surface = (AMFSurface *)src->data[0];
@@ -931,6 +976,10 @@ static int amf_map_frame(AVHWFramesContext *device_ctx, 
AVFrame *dst, const AVFr
     size_t plane_count = 0;
     int ret = -1;
 
+#if CONFIG_D3D11VA
+    if (dst->format == AV_PIX_FMT_D3D11)
+        return amf_map_to_d3d11(device_ctx, dst, src);
+#endif
     if (dst->format != AV_PIX_FMT_NONE &&
         av_pix_fmt_desc_get(dst->format)->flags & AV_PIX_FMT_FLAG_HWACCEL)
         return AVERROR(ENOSYS);
diff --git a/libavutil/version.h b/libavutil/version.h
index ed6abeab78..0ae09719ae 100644
--- a/libavutil/version.h
+++ b/libavutil/version.h
@@ -80,7 +80,7 @@
 
 #define LIBAVUTIL_VERSION_MAJOR  61
 #define LIBAVUTIL_VERSION_MINOR  14
-#define LIBAVUTIL_VERSION_MICRO 100
+#define LIBAVUTIL_VERSION_MICRO 101
 
 #define LIBAVUTIL_VERSION_INT   AV_VERSION_INT(LIBAVUTIL_VERSION_MAJOR, \
                                                LIBAVUTIL_VERSION_MINOR, \
-- 
2.54.0

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

Reply via email to