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

On AMF_RESOLUTION_CHANGED, amfdec calls `ReInit()`, which keeps the existing 
surface allocation and only crops the output (AMD in 
GPUOpen-LibrariesAndSDKs/AMF#486). That works when the resolution shrinks; when 
it grows, the decoder keeps returning surfaces of the old size while the frames 
are labelled with the new one. System memory output then reads past the surface 
and crashes, and AMF surface output hits the linesize assertion in 
`av_image_copy2()` on download. This uses `ReInit()` only while the new size 
fits into the decoder's surfaces and otherwise `Terminate()`/`Init()`, which 
AMD documents as the way to get a new allocation.

Tested on Windows 11, RX 9070 XT, driver 32.0.32015.7029, with H.264 and HEVC 
elementary streams concatenated from 1280x720, 640x360 and 1920x1080 parts: 
every frame matches FFmpeg's software decoders with system memory and AMF 
surface output, where master crashes at the first larger part; streams that 
only shrink are unchanged. The AV1 decoder does not report the change when the 
resolution grows and loses the device instead, which I reported as 
GPUOpen-LibrariesAndSDKs/AMF#639.

Written with Claude Opus 5.5 in Claude Code; `Assisted-by` trailer on the 
commit.


>From 4f939d5971f2156d77e6f94c73c242d45ca826c1 Mon Sep 17 00:00:00 2001
From: Julius Bairaktaris <[email protected]>
Date: Sat, 31 Oct 2026 17:34:43 +0100
Subject: [PATCH] avcodec/amfdec: re-create the decoder when the resolution
 grows

On AMF_RESOLUTION_CHANGED, amf_decode_frame() calls ReInit() with the
new size. ReInit() keeps the existing surface allocation and only crops
the output (as AMD explained in GPUOpen-LibrariesAndSDKs/AMF#486), so
it works when the resolution shrinks. When it grows, the decoder keeps
returning surfaces of the old size while the frames are labelled with
the new one: system memory output reads past the surface and crashes,
and AMF surface output hits the linesize assertion in av_image_copy2()
on download.

Track the size of the decoder's output surfaces and use ReInit() only
while the new resolution fits into them; otherwise Terminate() and
Init() the decoder with the new size, which AMD documents as the way
to get a new allocation.

Tested on Windows 11, RX 9070 XT, driver 32.0.32015.7029, with H.264
and HEVC elementary streams concatenated from 1280x720, 640x360 and
1920x1080 parts: every frame now matches the software decoders with
system memory and AMF surface output, where master crashed at the
first larger part. Streams that only shrink are unchanged. The AV1
decoder does not report the change at all when the resolution grows
(GPUOpen-LibrariesAndSDKs/AMF#639).

Assisted-by: Claude Opus 5.5
Signed-off-by: Julius Bairaktaris <[email protected]>
---
 libavcodec/amfdec.c | 17 +++++++++++++++--
 libavcodec/amfdec.h |  2 ++
 2 files changed, 17 insertions(+), 2 deletions(-)

diff --git a/libavcodec/amfdec.c b/libavcodec/amfdec.c
index 00a17360f55..2c886d60a3f 100644
--- a/libavcodec/amfdec.c
+++ b/libavcodec/amfdec.c
@@ -511,6 +511,13 @@ static int amf_amfsurface_to_avframe(AVCodecContext 
*avctx, AMFSurface* surface,
     int                 ret;
     int                 format_amf;
 
+    plane = surface->pVtbl->GetPlaneAt(surface, 0);
+    if (plane) {
+        AMFDecoderContext *ctx = avctx->priv_data;
+        ctx->surface_width  = plane->pVtbl->GetWidth(plane);
+        ctx->surface_height = plane->pVtbl->GetHeight(plane);
+    }
+
     if (avctx->hw_device_ctx && 
((AVHWDeviceContext*)avctx->hw_device_ctx->data)->type == AV_HWDEVICE_TYPE_AMF) 
{
         // prepare frame similar to  ff_get_buffer(avctx, frame, 
AV_GET_BUFFER_FLAG_REF);
 
@@ -757,9 +764,15 @@ static int amf_decode_frame(AVCodecContext *avctx, struct 
AVFrame *frame)
             avctx->height = size_var.sizeValue.height;
             avctx->coded_width  = size_var.sizeValue.width;
             avctx->coded_height = size_var.sizeValue.height;
-            res = ctx->decoder->pVtbl->ReInit(ctx->decoder, avctx->width, 
avctx->height);
+            if (avctx->width <= ctx->surface_width && avctx->height <= 
ctx->surface_height) {
+                res = ctx->decoder->pVtbl->ReInit(ctx->decoder, avctx->width, 
avctx->height);
+            } else {
+                ctx->decoder->pVtbl->Terminate(ctx->decoder);
+                res = ctx->decoder->pVtbl->Init(ctx->decoder, 
AMF_SURFACE_UNKNOWN, avctx->width, avctx->height);
+            }
             if (res != AMF_OK) {
-                av_log(avctx, AV_LOG_ERROR, "ReInit() returned %d\n", res);
+                av_log(avctx, AV_LOG_ERROR, "Reinitializing the decoder at 
%dx%d failed with error %d\n",
+                       avctx->width, avctx->height, res);
                 return AVERROR(EINVAL);
             }
             res = ctx->decoder->pVtbl->GetProperty(ctx->decoder, 
AMF_VIDEO_DECODER_OUTPUT_FORMAT, &format_var);
diff --git a/libavcodec/amfdec.h b/libavcodec/amfdec.h
index fd5e00f9db5..7a238f2cc2b 100644
--- a/libavcodec/amfdec.h
+++ b/libavcodec/amfdec.h
@@ -56,6 +56,8 @@ typedef struct AMFDecoderContext {
     int                 resolution_changed;
     int                 copy_output;
     int                 dimensions_initialized;
+    int                 surface_width;
+    int                 surface_height;
     AVPacket*           in_pkt;
     enum AMF_SURFACE_FORMAT output_format;
 
-- 
2.54.0

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

Reply via email to