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]
