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

Depends on #25454, whose commit this is stacked on; only the last commit 
belongs to this pull request.

AMF SDK 1.5.3 lets the video converter rotate by 90, 180 and 270 degrees 
(AMF_VIDEO_CONVERTER_ROTATE), but vpp_amf cannot request it.

- `avfilter/vf_vpp_amf: add a transpose option`: clock, cclock and reversal, as 
in the other hardware transpose filters. The converter cannot flip, so the flip 
values are refused. As in vpp_qsv, w and h give the size before the rotation, 
and a quarter turn swaps the size and the SAR. The option is only built with 
SDK 1.5.3 headers; with 1.5.2 headers the filter builds without it.

clock, cclock and reversal of a 1280x720 testsrc2 in NV12 are identical to 
transpose=1, transpose=2 and two transpose=1 (PSNR inf in all planes), and the 
output SAR matches transpose for anamorphic input. Tested on Windows 11, RX 
9070 XT, driver 32.0.32015.7029, AMF runtime 1.5.3.0.

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


>From 55494ec9644d0d9ff9dad7bc7c03c5a570a44c6e Mon Sep 17 00:00:00 2001
From: Julius Bairaktaris <[email protected]>
Date: Sun, 25 Oct 2026 16:46:57 +0100
Subject: [PATCH 1/2] avfilter/amf: set the output frames' sample aspect ratio

The AMF filters copy all properties of the input frame to the output
frame, including its sample aspect ratio, while the output link's
sample aspect ratio is computed for the output size. A scaled frame
therefore keeps the input's SAR: vpp_amf=w=1280:h=720:reset_sar=1 on
720x480 input with SAR 32:27 outputs frames tagged 32:27 instead of 1:1,
and w=720:h=240 outputs 32:27 instead of 16:27. Set the output frames'
SAR from the output link.

Tested on Windows 11, RX 9070 XT, driver 32.0.32015.7029: the frame SAR
of vpp_amf with reset_sar, with a non-proportional size, with a
proportional size and without scaling, and of sr_amf, now matches what
scale outputs for the same parameters.

Assisted-by: Claude Opus 5.5
Signed-off-by: Julius Bairaktaris <[email protected]>
---
 libavfilter/vf_amf_common.c | 1 +
 1 file changed, 1 insertion(+)

diff --git a/libavfilter/vf_amf_common.c b/libavfilter/vf_amf_common.c
index e1d0b42ee37..b8591451e7e 100644
--- a/libavfilter/vf_amf_common.c
+++ b/libavfilter/vf_amf_common.c
@@ -173,6 +173,7 @@ static int amf_deliver_output(AVFilterContext *avctx)
             return ret;
         }
         out->pts = pts;
+        out->sample_aspect_ratio = outlink->sample_aspect_ratio;
         if (ctx->outputs_per_input > 1)
             out->duration /= ctx->outputs_per_input;
 
-- 
2.54.0


>From a252c841368a4807969bc53d400322bf7f0a94af Mon Sep 17 00:00:00 2001
From: Julius Bairaktaris <[email protected]>
Date: Sun, 25 Oct 2026 16:40:49 +0100
Subject: [PATCH 2/2] avfilter/vf_vpp_amf: add a transpose option

The AMF video converter can rotate by 90, 180 and 270 degrees since AMF
SDK 1.5.3 (AMF_VIDEO_CONVERTER_ROTATE), but vpp_amf cannot request it.
Add a transpose option with the clock, cclock and reversal values of the
other hardware transpose filters; the converter cannot flip, so the flip
values are refused. As in vpp_qsv, the size from w and h is the size
before rotation and is swapped for a quarter turn, together with the
sample aspect ratio. The option is only built with SDK 1.5.3 headers,
and configuration fails if setting the property fails.

Tested on Windows 11, RX 9070 XT, driver 32.0.32015.7029, AMF runtime
1.5.3.0: clock, cclock and reversal of a 1280x720 testsrc2 in NV12 are
identical to transpose=1, transpose=2 and two transpose=1 (PSNR inf in
all planes), and the output SAR matches transpose for anamorphic input.
The filter builds with SDK 1.5.2 headers without the option.

Assisted-by: Claude Opus 5.5
Signed-off-by: Julius Bairaktaris <[email protected]>
---
 doc/filters.texi            | 15 +++++++++++++++
 libavfilter/vf_amf_common.c |  9 ++++++++-
 libavfilter/vf_amf_common.h |  1 +
 libavfilter/vf_vpp_amf.c    | 23 +++++++++++++++++++++++
 4 files changed, 47 insertions(+), 1 deletion(-)

diff --git a/doc/filters.texi b/doc/filters.texi
index 37f9123df49..3fb42bce94b 100644
--- a/doc/filters.texi
+++ b/doc/filters.texi
@@ -26259,6 +26259,21 @@ Work the same as the identical @ref{scale} filter 
options.
 @item reset_sar
 Works the same as the identical @ref{scale} filter option.
 
+@item transpose
+Rotate the video. The output size given by @option{w} and @option{h} is the
+size before the rotation. Requires AMF SDK 1.5.3 headers at build time and a
+runtime that supports rotation.
+@table @samp
+@item clock
+Rotate by 90 degrees clockwise.
+
+@item cclock
+Rotate by 90 degrees counterclockwise.
+
+@item reversal
+Rotate by 180 degrees.
+@end table
+
 @item in_color_range
 Override input color range.
 
diff --git a/libavfilter/vf_amf_common.c b/libavfilter/vf_amf_common.c
index b8591451e7e..1b2ce7a8d84 100644
--- a/libavfilter/vf_amf_common.c
+++ b/libavfilter/vf_amf_common.c
@@ -32,6 +32,7 @@
 #include "libavutil/hwcontext_amf.h"
 #include "libavutil/hwcontext_amf_internal.h"
 #include "scale_eval.h"
+#include "transpose.h"
 
 #if CONFIG_DXVA2
 #include <d3d9.h>
@@ -518,6 +519,9 @@ int amf_init_filter_config(AVFilterLink *outlink, enum 
AVPixelFormat *in_format)
     if (err < 0)
         return err;
 
+    if (ctx->transpose == TRANSPOSE_CLOCK || ctx->transpose == 
TRANSPOSE_CCLOCK)
+        FFSWAP(int, ctx->width, ctx->height);
+
     if (!ctx->pending) {
         ctx->pending = av_fifo_alloc2(1, sizeof(AVFrame*), 
AV_FIFO_FLAG_AUTO_GROW);
         if (!ctx->pending)
@@ -585,7 +589,10 @@ int amf_init_filter_config(AVFilterLink *outlink, enum 
AVPixelFormat *in_format)
 
     if (ctx->reset_sar)
         outlink->sample_aspect_ratio = (AVRational){1, 1};
-    else if (inlink->sample_aspect_ratio.num) {
+    else if (inlink->sample_aspect_ratio.num &&
+             (ctx->transpose == TRANSPOSE_CLOCK || ctx->transpose == 
TRANSPOSE_CCLOCK)) {
+        outlink->sample_aspect_ratio = av_div_q((AVRational){outlink->h * 
inlink->h, outlink->w * inlink->w}, inlink->sample_aspect_ratio);
+    } else if (inlink->sample_aspect_ratio.num) {
         outlink->sample_aspect_ratio = av_mul_q((AVRational){outlink->h * 
inlink->w, outlink->w * inlink->h}, inlink->sample_aspect_ratio);
     } else
         outlink->sample_aspect_ratio = inlink->sample_aspect_ratio;
diff --git a/libavfilter/vf_amf_common.h b/libavfilter/vf_amf_common.h
index 9af3d2ddda9..4ded7f07b7b 100644
--- a/libavfilter/vf_amf_common.h
+++ b/libavfilter/vf_amf_common.h
@@ -64,6 +64,7 @@ typedef struct AMFFilterContext {
     int force_original_aspect_ratio;
     int force_divisible_by;
     int reset_sar;
+    int transpose;
 
     AMFComponent        *component;
     AMFComponent        *pre_converter;
diff --git a/libavfilter/vf_vpp_amf.c b/libavfilter/vf_vpp_amf.c
index 1369c400124..c2561220742 100644
--- a/libavfilter/vf_vpp_amf.c
+++ b/libavfilter/vf_vpp_amf.c
@@ -31,6 +31,7 @@
 
 #include "AMF/components/VideoConverter.h"
 #include "vf_amf_common.h"
+#include "transpose.h"
 
 #include "avfilter.h"
 #include "scale_eval.h"
@@ -100,6 +101,21 @@ static int amf_filter_config_output(AVFilterLink *outlink)
     AMF_ASSIGN_PROPERTY_INT64(res, ctx->component, AMF_VIDEO_CONVERTER_SCALE, 
(amf_int32)ctx->scale_type);
     AMF_RETURN_IF_FALSE(avctx, res == AMF_OK, AVERROR_UNKNOWN, 
"AMFConverter-SetProperty() failed with error %d\n", res);
 
+#ifdef AMF_VIDEO_CONVERTER_ROTATE
+    if (ctx->transpose >= 0) {
+        if (ctx->transpose != TRANSPOSE_CLOCK && ctx->transpose != 
TRANSPOSE_CCLOCK &&
+            ctx->transpose != TRANSPOSE_REVERSAL) {
+            av_log(avctx, AV_LOG_ERROR, "Only clock, cclock and reversal are 
supported for transpose.\n");
+            return AVERROR(EINVAL);
+        }
+        AMF_ASSIGN_PROPERTY_INT64(res, ctx->component, 
AMF_VIDEO_CONVERTER_ROTATE,
+                                  ctx->transpose == TRANSPOSE_CLOCK  ? 
AMF_VIDEO_CONVERTER_ROTATE_90  :
+                                  ctx->transpose == TRANSPOSE_CCLOCK ? 
AMF_VIDEO_CONVERTER_ROTATE_270 :
+                                                                       
AMF_VIDEO_CONVERTER_ROTATE_180);
+        AMF_RETURN_IF_FALSE(avctx, res == AMF_OK, AVERROR(ENOSYS), "Rotation 
is not supported by the AMF runtime: %d\n", res);
+    }
+#endif
+
     amf_color_profile = AMF_VIDEO_CONVERTER_COLOR_PROFILE_UNKNOWN;
 
     switch(ctx->color_profile) {
@@ -256,6 +272,13 @@ static const AVOption vpp_amf_options[] = {
     { "force_divisible_by", "enforce that the output resolution is divisible 
by a defined integer when force_original_aspect_ratio is used", 
OFFSET(force_divisible_by), AV_OPT_TYPE_INT, { .i64 = 1}, 1, 256, FLAGS },
     { "reset_sar", "reset SAR to 1 and scale to square pixels if scaling 
proportionally", OFFSET(reset_sar), AV_OPT_TYPE_BOOL, { .i64 = 0}, 0, 1, FLAGS 
},
 
+#ifdef AMF_VIDEO_CONVERTER_ROTATE
+    { "transpose",      "Rotate the video",     OFFSET(transpose),   
AV_OPT_TYPE_INT, { .i64 = -1 }, -1, TRANSPOSE_REVERSAL, FLAGS, "transpose" },
+    { "clock",          "Rotate clockwise",         0, AV_OPT_TYPE_CONST, { 
.i64 = TRANSPOSE_CLOCK },    0, 0, FLAGS, "transpose" },
+    { "cclock",         "Rotate counter-clockwise", 0, AV_OPT_TYPE_CONST, { 
.i64 = TRANSPOSE_CCLOCK },   0, 0, FLAGS, "transpose" },
+    { "reversal",       "Rotate by half-turn",      0, AV_OPT_TYPE_CONST, { 
.i64 = TRANSPOSE_REVERSAL }, 0, 0, FLAGS, "transpose" },
+#endif
+
     { NULL },
 };
 
-- 
2.54.0

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

Reply via email to