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]

Reply via email to