This is an automated email from the git hooks/post-receive script.

Git pushed a commit to branch master
in repository ffmpeg.

commit 8f1e6edbabf811786f63fa0a69f669d88efb7e39
Author:     Julius Bairaktaris <[email protected]>
AuthorDate: Sun Sep 13 15:52:13 2026 +0200
Commit:     ArazIusubov <[email protected]>
CommitDate: Wed Sep 16 22:05:28 2026 +0000

    avfilter/amf: hold the wrapped D3D11 and DXVA2 frame until AMF releases it
    
    CreateSurfaceFromDX11Native() and CreateSurfaceFromDX9Native() wrap the
    texture without taking a reference on anything FFmpeg owns. The input
    AVFrame was freed at the end of the activation that submitted it, which
    hands its pool entry back to the decoder while the component may still
    hold the surface, so the decoder could write the next picture into a
    texture the component has not finished reading. Nothing pinned the
    frame for the surface's lifetime; the AMF encoder retains a clone of its
    input for the same reason.
    
    Pass an AMFSurfaceObserver that carries a clone of the frame and free
    both from OnSurfaceDataRelease(), which AMF calls when the wrapped
    surface is destroyed. The pool entry stays checked out for exactly as
    long as AMF references the surface. The copy path for D3D11VA decoder
    textures is unaffected: its copy is queued on the immediate context
    before the frame is released, so it is ordered ahead of any later write.
    
    Tested on Windows 11, RX 9070 XT, driver 32.0.31041.1004 with a probe
    counting the callbacks: every wrapped frame is released exactly once,
    for D3D11VA decoder textures into frc_amf, the path that wraps without
    copying, and for DXVA2 surfaces into all four filters. On this device
    the release arrives before the next frame is submitted, so the
    components consume the texture within the submitting activation and
    the hold changes nothing observable there: framecrc output is identical
    to the previous revision and decodes of 300 frames over DXVA2 and 600
    over D3D11VA complete.
    
    Assisted-by: Claude Fable 5.1
    Signed-off-by: Julius Bairaktaris <[email protected]>
---
 libavfilter/vf_amf_common.c | 51 +++++++++++++++++++++++++++++++++++++++++++--
 1 file changed, 49 insertions(+), 2 deletions(-)

diff --git a/libavfilter/vf_amf_common.c b/libavfilter/vf_amf_common.c
index 5cd93aef2b..e1d0b42ee3 100644
--- a/libavfilter/vf_amf_common.c
+++ b/libavfilter/vf_amf_common.c
@@ -739,6 +739,39 @@ static int amf_copy_d3d11_texture(AVFilterContext *avctx, 
const AVFrame *frame,
 }
 #endif
 
+#if CONFIG_D3D11VA || CONFIG_DXVA2
+typedef struct AMFFrameHolder {
+    AMFSurfaceObserver observer;
+    AVFrame *frame;
+} AMFFrameHolder;
+
+static void AMF_STD_CALL amf_release_held_frame(AMFSurfaceObserver *observer, 
AMFSurface *surface)
+{
+    AMFFrameHolder *holder = (AMFFrameHolder*)observer;
+
+    av_frame_free(&holder->frame);
+    av_free(holder);
+}
+
+static const AMFSurfaceObserverVtbl amf_frame_holder_vtbl = { 
amf_release_held_frame };
+
+static int amf_hold_frame(const AVFrame *frame, AMFSurfaceObserver **observer)
+{
+    AMFFrameHolder *holder = av_mallocz(sizeof(*holder));
+
+    if (!holder)
+        return AVERROR(ENOMEM);
+    holder->frame = av_frame_clone(frame);
+    if (!holder->frame) {
+        av_free(holder);
+        return AVERROR(ENOMEM);
+    }
+    holder->observer.pVtbl = &amf_frame_holder_vtbl;
+    *observer = &holder->observer;
+    return 0;
+}
+#endif
+
 int amf_avframe_to_amfsurface(AVFilterContext *avctx, const AVFrame *frame, 
AMFSurface** ppSurface)
 {
     AMFVariantStruct var = { 0 };
@@ -747,6 +780,9 @@ int amf_avframe_to_amfsurface(AVFilterContext *avctx, const 
AVFrame *frame, AMFS
     AMFSurface *surface;
     AMF_RESULT  res;
     int hw_surface = 0;
+#if CONFIG_D3D11VA || CONFIG_DXVA2
+    AMFSurfaceObserver *observer;
+#endif
 
     switch (frame->format) {
 #if CONFIG_D3D11VA
@@ -769,7 +805,12 @@ int amf_avframe_to_amfsurface(AVFilterContext *avctx, 
const AVFrame *frame, AMFS
 
             texture->lpVtbl->SetPrivateData(texture, 
&AMFTextureArrayIndexGUID, sizeof(index), &index);
 
-            res = 
ctx->amf_device_ctx->context->pVtbl->CreateSurfaceFromDX11Native(ctx->amf_device_ctx->context,
 texture, &surface, NULL); // wrap to AMF surface
+            ret = amf_hold_frame(frame, &observer);
+            if (ret < 0)
+                return ret;
+            res = 
ctx->amf_device_ctx->context->pVtbl->CreateSurfaceFromDX11Native(ctx->amf_device_ctx->context,
 texture, &surface, observer); // wrap to AMF surface
+            if (res != AMF_OK)
+                amf_release_held_frame(observer, NULL);
             AMF_RETURN_IF_FALSE(avctx, res == AMF_OK, AVERROR(ENOMEM), 
"CreateSurfaceFromDX11Native() failed  with error %d\n", res);
             hw_surface = 1;
         }
@@ -787,8 +828,14 @@ int amf_avframe_to_amfsurface(AVFilterContext *avctx, 
const AVFrame *frame, AMFS
     case AV_PIX_FMT_DXVA2_VLD:
         {
             IDirect3DSurface9 *texture = (IDirect3DSurface9 *)frame->data[3]; 
// actual texture
+            int ret;
 
-            res = 
ctx->amf_device_ctx->context->pVtbl->CreateSurfaceFromDX9Native(ctx->amf_device_ctx->context,
 texture, &surface, NULL); // wrap to AMF surface
+            ret = amf_hold_frame(frame, &observer);
+            if (ret < 0)
+                return ret;
+            res = 
ctx->amf_device_ctx->context->pVtbl->CreateSurfaceFromDX9Native(ctx->amf_device_ctx->context,
 texture, &surface, observer); // wrap to AMF surface
+            if (res != AMF_OK)
+                amf_release_held_frame(observer, NULL);
             AMF_RETURN_IF_FALSE(avctx, res == AMF_OK, AVERROR(ENOMEM), 
"CreateSurfaceFromDX9Native() failed  with error %d\n", res);
             hw_surface = 1;
         }

-- 
To stop receiving notification emails like this one, please contact
[email protected].
_______________________________________________
ffmpeg-cvslog mailing list -- [email protected]
To unsubscribe send an email to [email protected]

Reply via email to