PR #25640 opened by JuliusBairaktaris URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/25640 Patch URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/25640.patch
`ffmpeg -hwaccel amf -c:v h264_amf -i input -f null -` fails with EINVAL in the encoder. The AMF decoders never call `ff_get_format()`, so with an AMF device they always output AMF surfaces, and ffmpeg, which learns the hardware format from `get_format()`, never downloads them. The decoders now offer AV_PIX_FMT_AMF_SURFACE and their software format through `ff_get_format()`, and output AMF surfaces only when that format is chosen. `avcodec_default_get_format()` still picks AV_PIX_FMT_AMF_SURFACE when an AMF device is set, so API users without a callback see no change. Tested on Windows 11, RX 9070 XT, driver 32.0.32015.7029: with `-hwaccel amf` alone, 8-bit h264_amf, hevc_amf, av1_amf and vp9_amf output the same frames as without `-hwaccel`. With `-hwaccel_output_format amf`, without `-hwaccel` and with `-hwaccel d3d11va`, the framemd5 is unchanged. 10-bit streams also need the frames context to carry the surface's format (#25561) and the transfer to report it (#25637). With all three applied, 10-bit HEVC and AV1 match the software output as well. #25147 is still needed: a frames context supplied by the caller remains unsupported. Written with Claude Opus 5.5 in Claude Code; `Assisted-by` trailer on the commit. >From 0618cba9100309b9e7a0092bbc0115475cc03f8b Mon Sep 17 00:00:00 2001 From: Julius Bairaktaris <[email protected]> Date: Sat, 31 Oct 2026 12:27:12 +0100 Subject: [PATCH] avcodec/amfdec: select the output format with get_format() The AMF decoders never called ff_get_format(). With an AMF device in hw_device_ctx they always output AMF surfaces, whatever the caller's get_format() callback would have chosen. ffmpeg -hwaccel amf without -hwaccel_output_format therefore passed AMF surfaces to the encoder, which failed with EINVAL, instead of downloading them as it does for every other hwaccel. Offer AV_PIX_FMT_AMF_SURFACE and the decoder's software format through ff_get_format() when an AMF device is set, and output AMF surfaces only when that format is chosen. The frames context is allocated only in that case, and avctx->pix_fmt now reports the selected format. Tested on Windows 11, RX 9070 XT, driver 32.0.32015.7029: with -hwaccel amf alone, 8-bit h264_amf, hevc_amf, av1_amf and vp9_amf now output the same frames as without -hwaccel, where they failed before. With -hwaccel_output_format amf, without -hwaccel and with -hwaccel d3d11va the framemd5 is unchanged. Assisted-by: Claude Opus 5.5 Signed-off-by: Julius Bairaktaris <[email protected]> --- libavcodec/amfdec.c | 33 ++++++++++++++++++++------------- 1 file changed, 20 insertions(+), 13 deletions(-) diff --git a/libavcodec/amfdec.c b/libavcodec/amfdec.c index 00a17360f55..0d583567afa 100644 --- a/libavcodec/amfdec.c +++ b/libavcodec/amfdec.c @@ -243,7 +243,7 @@ static int amf_init_frames_context(AVCodecContext *avctx, int sw_format, int new static int amf_reinit_frames_context(AVCodecContext *avctx, int sw_format, int new_width, int new_height) { - if (!avctx->hw_device_ctx) + if (avctx->pix_fmt != AV_PIX_FMT_AMF_SURFACE) return 0; av_buffer_unref(&avctx->hw_frames_ctx); @@ -269,10 +269,6 @@ static int amf_decode_init(AVCodecContext *avctx) if (hwdev_ctx->type == AV_HWDEVICE_TYPE_AMF) { ctx->device_ctx_ref = av_buffer_ref(avctx->hw_device_ctx); - if (!avctx->hw_frames_ctx) { - avctx->hw_frames_ctx = av_hwframe_ctx_alloc(avctx->hw_device_ctx); - AMF_GOTO_FAIL_IF_FALSE(avctx, !!avctx->hw_frames_ctx, AVERROR(ENOMEM), "av_hwframe_ctx_alloc failed\n"); - } } else { ret = av_hwdevice_ctx_create_derived(&ctx->device_ctx_ref, AV_HWDEVICE_TYPE_AMF, avctx->hw_device_ctx, 0); AMF_GOTO_FAIL_IF_FALSE(avctx, ret == 0, ret, "Failed to create derived AMF device context: %s\n", av_err2str(ret)); @@ -303,15 +299,30 @@ static int amf_decode_init(AVCodecContext *avctx) surf_pix_fmt = av_amf_to_av_format(format_var.int64Value); } - if (avctx->hw_frames_ctx) + if (avctx->hw_device_ctx && + ((AVHWDeviceContext*)avctx->hw_device_ctx->data)->type == AV_HWDEVICE_TYPE_AMF) { + enum AVPixelFormat pix_fmts[3] = { AV_PIX_FMT_AMF_SURFACE, AV_PIX_FMT_NONE, AV_PIX_FMT_NONE }; + + if (surf_pix_fmt == AV_PIX_FMT_NONE) + surf_pix_fmt = AV_PIX_FMT_NV12; // for older drivers + pix_fmts[1] = surf_pix_fmt; + avctx->pix_fmt = ff_get_format(avctx, pix_fmts); + AMF_GOTO_FAIL_IF_FALSE(avctx, avctx->pix_fmt != AV_PIX_FMT_NONE, AVERROR(EINVAL), "No output format selected\n"); + } else + avctx->pix_fmt = surf_pix_fmt; + + if (avctx->pix_fmt == AV_PIX_FMT_AMF_SURFACE) { // this values should be set for avcodec_open2 // will be updated after header decoded if not true. - if (surf_pix_fmt == AV_PIX_FMT_NONE) - surf_pix_fmt = AV_PIX_FMT_NV12; // for older drivers int frames_w = 0; int frames_h = 0; + if (!avctx->hw_frames_ctx) { + avctx->hw_frames_ctx = av_hwframe_ctx_alloc(avctx->hw_device_ctx); + AMF_GOTO_FAIL_IF_FALSE(avctx, !!avctx->hw_frames_ctx, AVERROR(ENOMEM), "av_hwframe_ctx_alloc failed\n"); + } + if (avctx->coded_width > 0 && avctx->coded_height > 0) { frames_w = avctx->coded_width; frames_h = avctx->coded_height; @@ -325,8 +336,6 @@ static int amf_decode_init(AVCodecContext *avctx) ret = amf_init_frames_context(avctx, surf_pix_fmt, frames_w, frames_h); AMF_GOTO_FAIL_IF_FALSE(avctx, ret == 0, ret, "Failed to init frames context (AMF) : %s\n", av_err2str(ret)); } - else - avctx->pix_fmt = surf_pix_fmt; return 0; } @@ -511,15 +520,13 @@ static int amf_amfsurface_to_avframe(AVCodecContext *avctx, AMFSurface* surface, int ret; int format_amf; - if (avctx->hw_device_ctx && ((AVHWDeviceContext*)avctx->hw_device_ctx->data)->type == AV_HWDEVICE_TYPE_AMF) { + if (avctx->pix_fmt == AV_PIX_FMT_AMF_SURFACE) { // prepare frame similar to ff_get_buffer(avctx, frame, AV_GET_BUFFER_FLAG_REF); ret = ff_decode_frame_props(avctx, frame); if (ret < 0) return ret; - avctx->sw_pix_fmt = avctx->pix_fmt; - ret = ff_attach_decode_data(avctx, frame); if (ret < 0) return ret; -- 2.54.0 _______________________________________________ ffmpeg-devel mailing list -- [email protected] To unsubscribe send an email to [email protected]
