[FFmpeg-devel,v4] libavfilter: add transpose_vaapi filter

Submitted by Zachary Zhou on Nov. 29, 2018, 6:14 a.m.

Details

Message ID 20181129061402.5497-1-zachary.zhou@intel.com
State Accepted
Headers show

Commit Message

Zachary Zhou Nov. 29, 2018, 6:14 a.m.
Swap width and height when do clock/cclock rotation
Add 180/180_flip options

ffmpeg -hwaccel vaapi -vaapi_device /dev/dri/renderD128
-hwaccel_output_format vaapi -i input.264 -vf "transpose_vaapi=clock_flip"
-c:v h264_vaapi output.h264

Signed-off-by: Zachary Zhou <zachary.zhou@hotmail.com>
---
 configure                        |   2 +
 libavfilter/Makefile             |   1 +
 libavfilter/allfilters.c         |   1 +
 libavfilter/vf_transpose_vaapi.c | 356 +++++++++++++++++++++++++++++++
 4 files changed, 360 insertions(+)
 create mode 100644 libavfilter/vf_transpose_vaapi.c

Comments

Zachary Zhou Dec. 3, 2018, 10:56 a.m.
ping? any more comments ?

> -----Original Message-----

> From: ffmpeg-devel [mailto:ffmpeg-devel-bounces@ffmpeg.org] On Behalf Of

> Zachary Zhou

> Sent: Thursday, November 29, 2018 2:14 PM

> To: ffmpeg-devel@ffmpeg.org

> Cc: Zachary Zhou <zachary.zhou@hotmail.com>; Zhou, Zachary

> <zachary.zhou@intel.com>

> Subject: [FFmpeg-devel] [PATCH v4] libavfilter: add transpose_vaapi filter

> 

> Swap width and height when do clock/cclock rotation Add 180/180_flip

> options

> 

> ffmpeg -hwaccel vaapi -vaapi_device /dev/dri/renderD128 -

> hwaccel_output_format vaapi -i input.264 -vf "transpose_vaapi=clock_flip"

> -c:v h264_vaapi output.h264

> 

> Signed-off-by: Zachary Zhou <zachary.zhou@hotmail.com>

> ---

>  configure                        |   2 +

>  libavfilter/Makefile             |   1 +

>  libavfilter/allfilters.c         |   1 +

>  libavfilter/vf_transpose_vaapi.c | 356 +++++++++++++++++++++++++++++++

>  4 files changed, 360 insertions(+)

>  create mode 100644 libavfilter/vf_transpose_vaapi.c

> 

> diff --git a/configure b/configure

> index 3e9222ed1b..9e3908ef61 100755

> --- a/configure

> +++ b/configure

> @@ -3480,6 +3480,7 @@ tinterlace_merge_test_deps="tinterlace_filter"

>  tinterlace_pad_test_deps="tinterlace_filter"

>  tonemap_filter_deps="const_nan"

>  tonemap_opencl_filter_deps="opencl const_nan"

> +transpose_vaapi_filter_deps="vaapi VAProcPipelineCaps_rotation_flags"

>  unsharp_opencl_filter_deps="opencl"

>  uspp_filter_deps="gpl avcodec"

>  vaguedenoiser_filter_deps="gpl"

> @@ -5979,6 +5980,7 @@ check_type "d3d9.h dxva2api.h"

> DXVA2_ConfigPictureDecode -D_WIN32_WINNT=0x0602

> 

>  check_type "va/va.h va/va_dec_hevc.h" "VAPictureParameterBufferHEVC"

>  check_struct "va/va.h" "VADecPictureParameterBufferVP9" bit_depth

> +check_struct "va/va.h va/va_vpp.h" "VAProcPipelineCaps" rotation_flags

>  check_type "va/va.h va/va_enc_hevc.h"

> "VAEncPictureParameterBufferHEVC"

>  check_type "va/va.h va/va_enc_jpeg.h" "VAEncPictureParameterBufferJPEG"

>  check_type "va/va.h va/va_enc_vp8.h"  "VAEncPictureParameterBufferVP8"

> diff --git a/libavfilter/Makefile b/libavfilter/Makefile index

> 1895fa2b0d..ccce4bbb2f 100644

> --- a/libavfilter/Makefile

> +++ b/libavfilter/Makefile

> @@ -393,6 +393,7 @@ OBJS-$(CONFIG_TONEMAP_OPENCL_FILTER)         +=

> vf_tonemap_opencl.o colorspace.o

>  OBJS-$(CONFIG_TPAD_FILTER)                   += vf_tpad.o

>  OBJS-$(CONFIG_TRANSPOSE_FILTER)              += vf_transpose.o

>  OBJS-$(CONFIG_TRANSPOSE_NPP_FILTER)          += vf_transpose_npp.o

> cuda_check.o

> +OBJS-$(CONFIG_TRANSPOSE_VAAPI_FILTER)        += vf_transpose_vaapi.o

> vaapi_vpp.o

>  OBJS-$(CONFIG_TRIM_FILTER)                   += trim.o

>  OBJS-$(CONFIG_UNPREMULTIPLY_FILTER)          += vf_premultiply.o

> framesync.o

>  OBJS-$(CONFIG_UNSHARP_FILTER)                += vf_unsharp.o

> diff --git a/libavfilter/allfilters.c b/libavfilter/allfilters.c index

> 837c99eb75..40e5d82b62 100644

> --- a/libavfilter/allfilters.c

> +++ b/libavfilter/allfilters.c

> @@ -372,6 +372,7 @@ extern AVFilter ff_vf_tonemap_opencl;  extern

> AVFilter ff_vf_tpad;  extern AVFilter ff_vf_transpose;  extern AVFilter

> ff_vf_transpose_npp;

> +extern AVFilter ff_vf_transpose_vaapi;

>  extern AVFilter ff_vf_trim;

>  extern AVFilter ff_vf_unpremultiply;

>  extern AVFilter ff_vf_unsharp;

> diff --git a/libavfilter/vf_transpose_vaapi.c b/libavfilter/vf_transpose_vaapi.c

> new file mode 100644

> index 0000000000..d0502b7944

> --- /dev/null

> +++ b/libavfilter/vf_transpose_vaapi.c

> @@ -0,0 +1,356 @@

> +/*

> + * This file is part of FFmpeg.

> + *

> + * FFmpeg is free software; you can redistribute it and/or

> + * modify it under the terms of the GNU Lesser General Public

> + * License as published by the Free Software Foundation; either

> + * version 2.1 of the License, or (at your option) any later version.

> + *

> + * FFmpeg is distributed in the hope that it will be useful,

> + * but WITHOUT ANY WARRANTY; without even the implied warranty of

> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU

> + * Lesser General Public License for more details.

> + *

> + * You should have received a copy of the GNU Lesser General Public

> + * License along with FFmpeg; if not, write to the Free Software

> + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA

> +02110-1301 USA  */ #include <string.h>

> +

> +#include "libavutil/avassert.h"

> +#include "libavutil/mem.h"

> +#include "libavutil/opt.h"

> +#include "libavutil/pixdesc.h"

> +

> +#include "avfilter.h"

> +#include "formats.h"

> +#include "internal.h"

> +#include "vaapi_vpp.h"

> +

> +typedef enum {

> +    TRANSPOSE_PT_TYPE_NONE,

> +    TRANSPOSE_PT_TYPE_LANDSCAPE,

> +    TRANSPOSE_PT_TYPE_PORTRAIT,

> +} PassthroughType;

> +

> +enum TransposeDir {

> +    TRANSPOSE_CCLOCK_FLIP,

> +    TRANSPOSE_CLOCK,

> +    TRANSPOSE_CCLOCK,

> +    TRANSPOSE_CLOCK_FLIP,

> +    TRANSPOSE_180,

> +    TRANSPOSE_180_FLIP,

> +};

> +

> +typedef struct TransposeVAAPIContext {

> +    VAAPIVPPContext vpp_ctx; // must be the first field

> +    int passthrough;         // PassthroughType, landscape passthrough mode

> enabled

> +    int dir;                 // TransposeDir

> +

> +    int rotation_state;

> +    int mirror_state;

> +} TransposeVAAPIContext;

> +

> +static int transpose_vaapi_build_filter_params(AVFilterContext *avctx)

> +{

> +    VAAPIVPPContext *vpp_ctx   = avctx->priv;

> +    TransposeVAAPIContext *ctx = avctx->priv;

> +    VAStatus vas;

> +    int support_flag;

> +    VAProcPipelineCaps pipeline_caps;

> +

> +    memset(&pipeline_caps, 0, sizeof(pipeline_caps));

> +    vas = vaQueryVideoProcPipelineCaps(vpp_ctx->hwctx->display,

> +                                       vpp_ctx->va_context,

> +                                       NULL, 0,

> +                                       &pipeline_caps);

> +    if (vas != VA_STATUS_SUCCESS) {

> +        av_log(avctx, AV_LOG_ERROR, "Failed to query pipeline "

> +               "caps: %d (%s).\n", vas, vaErrorStr(vas));

> +        return AVERROR(EIO);

> +    }

> +

> +    if (!pipeline_caps.rotation_flags) {

> +        av_log(avctx, AV_LOG_ERROR, "VAAPI driver doesn't support

> transpose\n");

> +        return AVERROR(EINVAL);

> +    }

> +

> +    switch (ctx->dir) {

> +    case TRANSPOSE_CCLOCK_FLIP:

> +        ctx->rotation_state = VA_ROTATION_270;

> +        ctx->mirror_state   = VA_MIRROR_VERTICAL;

> +        break;

> +    case TRANSPOSE_CLOCK:

> +        ctx->rotation_state = VA_ROTATION_90;

> +        ctx->mirror_state   = VA_MIRROR_NONE;

> +        break;

> +    case TRANSPOSE_CCLOCK:

> +        ctx->rotation_state = VA_ROTATION_270;

> +        ctx->mirror_state   = VA_MIRROR_NONE;

> +        break;

> +    case TRANSPOSE_CLOCK_FLIP:

> +        ctx->rotation_state = VA_ROTATION_90;

> +        ctx->mirror_state   = VA_MIRROR_VERTICAL;

> +        break;

> +    case TRANSPOSE_180:

> +        ctx->rotation_state = VA_ROTATION_180;

> +        ctx->mirror_state   = VA_MIRROR_NONE;

> +        break;

> +    case TRANSPOSE_180_FLIP:

> +        ctx->rotation_state = VA_ROTATION_180;

> +        ctx->mirror_state   = VA_MIRROR_VERTICAL;

> +        break;

> +    default:

> +        av_log(avctx, AV_LOG_ERROR, "Failed to set direction to %d\n", ctx->dir);

> +        return AVERROR(EINVAL);

> +    }

> +

> +    support_flag = pipeline_caps.rotation_flags & (1 << ctx->rotation_state);

> +    if (!support_flag) {

> +        av_log(avctx, AV_LOG_ERROR, "VAAPI driver doesn't support %d\n",

> +               ctx->rotation_state);

> +        return AVERROR(EINVAL);

> +    }

> +

> +    return 0;

> +}

> +

> +static int transpose_vaapi_filter_frame(AVFilterLink *inlink, AVFrame

> +*input_frame) {

> +    AVFilterContext *avctx     = inlink->dst;

> +    AVFilterLink *outlink      = avctx->outputs[0];

> +    VAAPIVPPContext *vpp_ctx   = avctx->priv;

> +    TransposeVAAPIContext *ctx = avctx->priv;

> +    AVFrame *output_frame      = NULL;

> +    VASurfaceID input_surface, output_surface;

> +    VARectangle input_region, output_region;

> +

> +    VAProcPipelineParameterBuffer params;

> +    int err;

> +

> +    if (ctx->passthrough)

> +        return ff_filter_frame(outlink, input_frame);

> +

> +    av_log(avctx, AV_LOG_DEBUG, "Filter input: %s, %ux%u (%"PRId64").\n",

> +           av_get_pix_fmt_name(input_frame->format),

> +           input_frame->width, input_frame->height, input_frame->pts);

> +

> +    if (vpp_ctx->va_context == VA_INVALID_ID)

> +        return AVERROR(EINVAL);

> +

> +    input_surface = (VASurfaceID)(uintptr_t)input_frame->data[3];

> +    av_log(avctx, AV_LOG_DEBUG, "Using surface %#x for transpose vpp

> input.\n",

> +           input_surface);

> +

> +    output_frame = ff_get_video_buffer(outlink, vpp_ctx->output_width,

> +                                       vpp_ctx->output_height);

> +    if (!output_frame) {

> +        err = AVERROR(ENOMEM);

> +        goto fail;

> +    }

> +

> +    output_surface = (VASurfaceID)(uintptr_t)output_frame->data[3];

> +    av_log(avctx, AV_LOG_DEBUG, "Using surface %#x for transpose vpp

> output.\n",

> +           output_surface);

> +    memset(&params, 0, sizeof(params));

> +    input_region = (VARectangle) {

> +        .x      = 0,

> +        .y      = 0,

> +        .width  = input_frame->width,

> +        .height = input_frame->height,

> +    };

> +

> +    output_region = (VARectangle) {

> +        .x      = 0,

> +        .y      = 0,

> +        .width  = output_frame->width,

> +        .height = output_frame->height,

> +    };

> +

> +    switch (ctx->rotation_state) {

> +    case VA_ROTATION_NONE:

> +    case VA_ROTATION_90:

> +    case VA_ROTATION_180:

> +    case VA_ROTATION_270:

> +        params.rotation_state = ctx->rotation_state;

> +        break;

> +    default:

> +        av_log(avctx, AV_LOG_ERROR, "VAAPI doesn't support rotation %d\n",

> +               ctx->rotation_state);

> +        goto fail;

> +    }

> +

> +    switch (ctx->mirror_state) {

> +    case VA_MIRROR_HORIZONTAL:

> +    case VA_MIRROR_VERTICAL:

> +    case VA_MIRROR_NONE:

> +        params.mirror_state = ctx->mirror_state;

> +        break;

> +    default:

> +        av_log(avctx, AV_LOG_ERROR, "VAAPI doesn't support mirroring %d\n",

> +               ctx->mirror_state);

> +        goto fail;

> +    }

> +

> +    if (vpp_ctx->nb_filter_buffers) {

> +        params.filters     = &vpp_ctx->filter_buffers[0];

> +        params.num_filters = vpp_ctx->nb_filter_buffers;

> +    }

> +

> +    params.surface = input_surface;

> +    params.surface_region = &input_region;

> +    params.surface_color_standard =

> +        ff_vaapi_vpp_colour_standard(input_frame->colorspace);

> +

> +    params.output_region = &output_region;

> +    params.output_background_color = 0xff000000;

> +    params.output_color_standard = params.surface_color_standard;

> +

> +    params.pipeline_flags = 0;

> +    params.filter_flags = VA_FRAME_PICTURE;

> +

> +    err = ff_vaapi_vpp_render_picture(avctx, &params, output_surface);

> +    if (err < 0)

> +        goto fail;

> +

> +    err = av_frame_copy_props(output_frame, input_frame);

> +    if (err < 0)

> +        goto fail;

> +    av_frame_free(&input_frame);

> +

> +    av_log(avctx, AV_LOG_DEBUG, "Filter output: %s, %ux%u (%"PRId64").\n",

> +           av_get_pix_fmt_name(output_frame->format),

> +           output_frame->width, output_frame->height,

> + output_frame->pts);

> +

> +    return ff_filter_frame(outlink, output_frame);

> +

> +fail:

> +    av_frame_free(&input_frame);

> +    av_frame_free(&output_frame);

> +    return err;

> +}

> +

> +static av_cold int transpose_vaapi_init(AVFilterContext *avctx) {

> +    VAAPIVPPContext *vpp_ctx = avctx->priv;

> +

> +    ff_vaapi_vpp_ctx_init(avctx);

> +    vpp_ctx->pipeline_uninit     = ff_vaapi_vpp_pipeline_uninit;

> +    vpp_ctx->build_filter_params = transpose_vaapi_build_filter_params;

> +    vpp_ctx->output_format       = AV_PIX_FMT_NONE;

> +

> +    return 0;

> +}

> +

> +static void transpose_vaapi_vpp_ctx_uninit(AVFilterContext *avctx) {

> +    return ff_vaapi_vpp_ctx_uninit(avctx); }

> +

> +static int transpose_vaapi_vpp_query_formats(AVFilterContext *avctx) {

> +    return ff_vaapi_vpp_query_formats(avctx);

> +}

> +

> +static int transpose_vaapi_vpp_config_input(AVFilterLink *inlink) {

> +    return ff_vaapi_vpp_config_input(inlink);

> +}

> +

> +static int transpose_vaapi_vpp_config_output(AVFilterLink *outlink) {

> +    AVFilterContext *avctx     = outlink->src;

> +    VAAPIVPPContext *vpp_ctx   = avctx->priv;

> +    TransposeVAAPIContext *ctx = avctx->priv;

> +    AVFilterLink *inlink       = avctx->inputs[0];

> +

> +    if ((inlink->w >= inlink->h && ctx->passthrough ==

> TRANSPOSE_PT_TYPE_LANDSCAPE) ||

> +        (inlink->w <= inlink->h && ctx->passthrough ==

> TRANSPOSE_PT_TYPE_PORTRAIT)) {

> +        av_log(avctx, AV_LOG_VERBOSE,

> +               "w:%d h:%d -> w:%d h:%d (passthrough mode)\n",

> +               inlink->w, inlink->h, inlink->w, inlink->h);

> +        return ff_vaapi_vpp_config_output(outlink);

> +    }

> +

> +    ctx->passthrough = TRANSPOSE_PT_TYPE_NONE;

> +

> +    switch (ctx->dir) {

> +    case TRANSPOSE_CCLOCK_FLIP:

> +    case TRANSPOSE_CCLOCK:

> +    case TRANSPOSE_CLOCK:

> +    case TRANSPOSE_CLOCK_FLIP:

> +        vpp_ctx->output_width  = avctx->inputs[0]->h;

> +        vpp_ctx->output_height = avctx->inputs[0]->w;

> +        av_log(avctx, AV_LOG_DEBUG, "swap width and height for clock/cclock

> rotation\n");

> +        break;

> +    default:

> +        break;

> +    }

> +

> +    return ff_vaapi_vpp_config_output(outlink);

> +}

> +

> +static AVFrame *get_video_buffer(AVFilterLink *inlink, int w, int h) {

> +    TransposeVAAPIContext *ctx = inlink->dst->priv;

> +

> +    return ctx->passthrough ?

> +        ff_null_get_video_buffer(inlink, w, h) :

> +        ff_default_get_video_buffer(inlink, w, h); }

> +

> +#define OFFSET(x) offsetof(TransposeVAAPIContext, x) #define FLAGS

> +(AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_FILTERING_PARAM) static

> const

> +AVOption transpose_vaapi_options[] = {

> +    { "dir", "set transpose direction", OFFSET(dir), AV_OPT_TYPE_INT, { .i64 =

> TRANSPOSE_CCLOCK_FLIP }, 0, 5, FLAGS, "dir" },

> +        { "cclock_flip", "rotate counter-clockwise with vertical flip",     0,

> AV_OPT_TYPE_CONST, { .i64 = TRANSPOSE_CCLOCK_FLIP }, .flags=FLAGS, .unit

> = "dir" },

> +        { "clock",       "rotate clockwise",                                0,

> AV_OPT_TYPE_CONST, { .i64 = TRANSPOSE_CLOCK       }, .flags=FLAGS, .unit =

> "dir" },

> +        { "cclock",      "rotate counter-clockwise",                        0,

> AV_OPT_TYPE_CONST, { .i64 = TRANSPOSE_CCLOCK      }, .flags=FLAGS, .unit =

> "dir" },

> +        { "clock_flip",  "rotate clockwise with vertical flip",             0,

> AV_OPT_TYPE_CONST, { .i64 = TRANSPOSE_CLOCK_FLIP  }, .flags=FLAGS, .unit =

> "dir" },

> +        { "180",         "rotate clockwise 180 degrees",                    0,

> AV_OPT_TYPE_CONST, { .i64 = TRANSPOSE_180         }, .flags=FLAGS, .unit =

> "dir" },

> +        { "180_flip",    "rotate clockwise 180 degrees with vertical flip", 0,

> AV_OPT_TYPE_CONST, { .i64 = TRANSPOSE_180_FLIP    }, .flags=FLAGS, .unit =

> "dir" },

> +

> +    { "passthrough", "do not apply transposition if the input matches the

> specified geometry",

> +      OFFSET(passthrough), AV_OPT_TYPE_INT,

> {.i64=TRANSPOSE_PT_TYPE_NONE},  0, INT_MAX, FLAGS, "passthrough" },

> +        { "none",      "always apply transposition",   0, AV_OPT_TYPE_CONST,

> {.i64=TRANSPOSE_PT_TYPE_NONE},      INT_MIN, INT_MAX, FLAGS,

> "passthrough" },

> +        { "portrait",  "preserve portrait geometry",   0, AV_OPT_TYPE_CONST,

> {.i64=TRANSPOSE_PT_TYPE_PORTRAIT},  INT_MIN, INT_MAX, FLAGS,

> "passthrough" },

> +        { "landscape", "preserve landscape geometry",  0,

> + AV_OPT_TYPE_CONST, {.i64=TRANSPOSE_PT_TYPE_LANDSCAPE}, INT_MIN,

> + INT_MAX, FLAGS, "passthrough" },

> +

> +    { NULL }

> +};

> +

> +

> +AVFILTER_DEFINE_CLASS(transpose_vaapi);

> +

> +static const AVFilterPad transpose_vaapi_inputs[] = {

> +    {

> +        .name         = "default",

> +        .type         = AVMEDIA_TYPE_VIDEO,

> +        .filter_frame = &transpose_vaapi_filter_frame,

> +        .get_video_buffer = get_video_buffer,

> +        .config_props = &transpose_vaapi_vpp_config_input,

> +    },

> +    { NULL }

> +};

> +

> +static const AVFilterPad transpose_vaapi_outputs[] = {

> +    {

> +        .name = "default",

> +        .type = AVMEDIA_TYPE_VIDEO,

> +        .config_props = &transpose_vaapi_vpp_config_output,

> +    },

> +    { NULL }

> +};

> +

> +AVFilter ff_vf_transpose_vaapi = {

> +    .name           = "transpose_vaapi",

> +    .description    = NULL_IF_CONFIG_SMALL("VAAPI VPP for transpose"),

> +    .priv_size      = sizeof(TransposeVAAPIContext),

> +    .init           = &transpose_vaapi_init,

> +    .uninit         = &transpose_vaapi_vpp_ctx_uninit,

> +    .query_formats  = &transpose_vaapi_vpp_query_formats,

> +    .inputs         = transpose_vaapi_inputs,

> +    .outputs        = transpose_vaapi_outputs,

> +    .priv_class     = &transpose_vaapi_class,

> +    .flags_internal = FF_FILTER_FLAG_HWFRAME_AWARE, };

> --

> 2.17.1

> 

> _______________________________________________

> ffmpeg-devel mailing list

> ffmpeg-devel@ffmpeg.org

> http://ffmpeg.org/mailman/listinfo/ffmpeg-devel
Zachary Zhou Dec. 10, 2018, 8:57 a.m.
Hi Mark,

Do you have any comments regarding this patch ?

Best Regards,
Zachary

> -----Original Message-----

> From: Zhou, Zachary

> Sent: Monday, December 3, 2018 6:56 PM

> To: FFmpeg development discussions and patches <ffmpeg-devel@ffmpeg.org>

> Cc: Zhou, Zachary <zachary.zhou@intel.com>

> Subject: RE: [FFmpeg-devel] [PATCH v4] libavfilter: add transpose_vaapi filter

> 

> ping? any more comments ?

> 

> >

> > _______________________________________________

> > ffmpeg-devel mailing list

> > ffmpeg-devel@ffmpeg.org

> > http://ffmpeg.org/mailman/listinfo/ffmpeg-devel
Zhong Li Dec. 18, 2018, 6:51 a.m.
> From: ffmpeg-devel [mailto:ffmpeg-devel-bounces@ffmpeg.org] On Behalf

> Of Zhou, Zachary

> Sent: Monday, December 3, 2018 6:56 PM

> To: FFmpeg development discussions and patches

> <ffmpeg-devel@ffmpeg.org>

> Cc: Zhou, Zachary <zachary.zhou@intel.com>

> Subject: Re: [FFmpeg-devel] [PATCH v4] libavfilter: add transpose_vaapi filter

> 

> ping? any more comments ?

> 

> > -----Original Message-----

> > From: ffmpeg-devel [mailto:ffmpeg-devel-bounces@ffmpeg.org] On

> Behalf

> > Of Zachary Zhou

> > Sent: Thursday, November 29, 2018 2:14 PM

> > To: ffmpeg-devel@ffmpeg.org

> > Cc: Zachary Zhou <zachary.zhou@hotmail.com>; Zhou, Zachary

> > <zachary.zhou@intel.com>

> > Subject: [FFmpeg-devel] [PATCH v4] libavfilter: add transpose_vaapi

> > filter

> >

> > Swap width and height when do clock/cclock rotation Add 180/180_flip

> > options

> >

> > ffmpeg -hwaccel vaapi -vaapi_device /dev/dri/renderD128 -

> > hwaccel_output_format vaapi -i input.264 -vf

> "transpose_vaapi=clock_flip"

> > -c:v h264_vaapi output.h264

> >

> > Signed-off-by: Zachary Zhou <zachary.zhou@hotmail.com>

> > ---

> >  configure                        |   2 +

> >  libavfilter/Makefile             |   1 +

> >  libavfilter/allfilters.c         |   1 +

> >  libavfilter/vf_transpose_vaapi.c | 356

> > +++++++++++++++++++++++++++++++

> >  4 files changed, 360 insertions(+)

> >  create mode 100644 libavfilter/vf_transpose_vaapi.c

> >

> > diff --git a/configure b/configure

> > index 3e9222ed1b..9e3908ef61 100755

> > --- a/configure

> > +++ b/configure

> > @@ -3480,6 +3480,7 @@ tinterlace_merge_test_deps="tinterlace_filter"

> >  tinterlace_pad_test_deps="tinterlace_filter"

> >  tonemap_filter_deps="const_nan"

> >  tonemap_opencl_filter_deps="opencl const_nan"

> > +transpose_vaapi_filter_deps="vaapi VAProcPipelineCaps_rotation_flags"

> >  unsharp_opencl_filter_deps="opencl"

> >  uspp_filter_deps="gpl avcodec"

> >  vaguedenoiser_filter_deps="gpl"

> > @@ -5979,6 +5980,7 @@ check_type "d3d9.h dxva2api.h"

> > DXVA2_ConfigPictureDecode -D_WIN32_WINNT=0x0602

> >

> >  check_type "va/va.h va/va_dec_hevc.h"

> "VAPictureParameterBufferHEVC"

> >  check_struct "va/va.h" "VADecPictureParameterBufferVP9" bit_depth

> > +check_struct "va/va.h va/va_vpp.h" "VAProcPipelineCaps"

> > +rotation_flags

> >  check_type "va/va.h va/va_enc_hevc.h"

> > "VAEncPictureParameterBufferHEVC"

> >  check_type "va/va.h va/va_enc_jpeg.h"

> "VAEncPictureParameterBufferJPEG"

> >  check_type "va/va.h va/va_enc_vp8.h"

> "VAEncPictureParameterBufferVP8"

> > diff --git a/libavfilter/Makefile b/libavfilter/Makefile index

> > 1895fa2b0d..ccce4bbb2f 100644

> > --- a/libavfilter/Makefile

> > +++ b/libavfilter/Makefile

> > @@ -393,6 +393,7 @@ OBJS-$(CONFIG_TONEMAP_OPENCL_FILTER)

> +=

> > vf_tonemap_opencl.o colorspace.o

> >  OBJS-$(CONFIG_TPAD_FILTER)                   += vf_tpad.o

> >  OBJS-$(CONFIG_TRANSPOSE_FILTER)              += vf_transpose.o

> >  OBJS-$(CONFIG_TRANSPOSE_NPP_FILTER)          +=

> vf_transpose_npp.o

> > cuda_check.o

> > +OBJS-$(CONFIG_TRANSPOSE_VAAPI_FILTER)        +=

> vf_transpose_vaapi.o

> > vaapi_vpp.o

> >  OBJS-$(CONFIG_TRIM_FILTER)                   += trim.o

> >  OBJS-$(CONFIG_UNPREMULTIPLY_FILTER)          +=

> vf_premultiply.o

> > framesync.o

> >  OBJS-$(CONFIG_UNSHARP_FILTER)                += vf_unsharp.o

> > diff --git a/libavfilter/allfilters.c b/libavfilter/allfilters.c index

> > 837c99eb75..40e5d82b62 100644

> > --- a/libavfilter/allfilters.c

> > +++ b/libavfilter/allfilters.c

> > @@ -372,6 +372,7 @@ extern AVFilter ff_vf_tonemap_opencl;  extern

> > AVFilter ff_vf_tpad;  extern AVFilter ff_vf_transpose;  extern

> > AVFilter ff_vf_transpose_npp;

> > +extern AVFilter ff_vf_transpose_vaapi;

> >  extern AVFilter ff_vf_trim;

> >  extern AVFilter ff_vf_unpremultiply;

> >  extern AVFilter ff_vf_unsharp;

> > diff --git a/libavfilter/vf_transpose_vaapi.c

> > b/libavfilter/vf_transpose_vaapi.c

> > new file mode 100644

> > index 0000000000..d0502b7944

> > --- /dev/null

> > +++ b/libavfilter/vf_transpose_vaapi.c

> > @@ -0,0 +1,356 @@

> > +/*

> > + * This file is part of FFmpeg.

> > + *

> > + * FFmpeg is free software; you can redistribute it and/or

> > + * modify it under the terms of the GNU Lesser General Public

> > + * License as published by the Free Software Foundation; either

> > + * version 2.1 of the License, or (at your option) any later version.

> > + *

> > + * FFmpeg is distributed in the hope that it will be useful,

> > + * but WITHOUT ANY WARRANTY; without even the implied warranty of

> > + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the

> GNU

> > + * Lesser General Public License for more details.

> > + *

> > + * You should have received a copy of the GNU Lesser General Public

> > + * License along with FFmpeg; if not, write to the Free Software

> > + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA

> > +02110-1301 USA  */ #include <string.h>

> > +

> > +#include "libavutil/avassert.h"

> > +#include "libavutil/mem.h"

> > +#include "libavutil/opt.h"

> > +#include "libavutil/pixdesc.h"

> > +

> > +#include "avfilter.h"

> > +#include "formats.h"

> > +#include "internal.h"

> > +#include "vaapi_vpp.h"

> > +

> > +typedef enum {

> > +    TRANSPOSE_PT_TYPE_NONE,

> > +    TRANSPOSE_PT_TYPE_LANDSCAPE,

> > +    TRANSPOSE_PT_TYPE_PORTRAIT,

> > +} PassthroughType;

> > +

> > +enum TransposeDir {

> > +    TRANSPOSE_CCLOCK_FLIP,

> > +    TRANSPOSE_CLOCK,

> > +    TRANSPOSE_CCLOCK,

> > +    TRANSPOSE_CLOCK_FLIP,

> > +    TRANSPOSE_180,

> > +    TRANSPOSE_180_FLIP,

> > +};

> > +

> > +typedef struct TransposeVAAPIContext {

> > +    VAAPIVPPContext vpp_ctx; // must be the first field

> > +    int passthrough;         // PassthroughType, landscape

> passthrough mode

> > enabled

> > +    int dir;                 // TransposeDir

> > +

> > +    int rotation_state;

> > +    int mirror_state;

> > +} TransposeVAAPIContext;

> > +

> > +static int transpose_vaapi_build_filter_params(AVFilterContext

> > +*avctx) {

> > +    VAAPIVPPContext *vpp_ctx   = avctx->priv;

> > +    TransposeVAAPIContext *ctx = avctx->priv;

> > +    VAStatus vas;

> > +    int support_flag;

> > +    VAProcPipelineCaps pipeline_caps;

> > +

> > +    memset(&pipeline_caps, 0, sizeof(pipeline_caps));

> > +    vas = vaQueryVideoProcPipelineCaps(vpp_ctx->hwctx->display,

> > +                                       vpp_ctx->va_context,

> > +                                       NULL, 0,

> > +                                       &pipeline_caps);

> > +    if (vas != VA_STATUS_SUCCESS) {

> > +        av_log(avctx, AV_LOG_ERROR, "Failed to query pipeline "

> > +               "caps: %d (%s).\n", vas, vaErrorStr(vas));

> > +        return AVERROR(EIO);

> > +    }

> > +

> > +    if (!pipeline_caps.rotation_flags) {

> > +        av_log(avctx, AV_LOG_ERROR, "VAAPI driver doesn't support

> > transpose\n");

> > +        return AVERROR(EINVAL);

> > +    }

> > +

> > +    switch (ctx->dir) {

> > +    case TRANSPOSE_CCLOCK_FLIP:

> > +        ctx->rotation_state = VA_ROTATION_270;

> > +        ctx->mirror_state   = VA_MIRROR_VERTICAL;

> > +        break;

> > +    case TRANSPOSE_CLOCK:

> > +        ctx->rotation_state = VA_ROTATION_90;

> > +        ctx->mirror_state   = VA_MIRROR_NONE;

> > +        break;

> > +    case TRANSPOSE_CCLOCK:

> > +        ctx->rotation_state = VA_ROTATION_270;

> > +        ctx->mirror_state   = VA_MIRROR_NONE;

> > +        break;

> > +    case TRANSPOSE_CLOCK_FLIP:

> > +        ctx->rotation_state = VA_ROTATION_90;

> > +        ctx->mirror_state   = VA_MIRROR_VERTICAL;

> > +        break;

> > +    case TRANSPOSE_180:

> > +        ctx->rotation_state = VA_ROTATION_180;

> > +        ctx->mirror_state   = VA_MIRROR_NONE;

> > +        break;

> > +    case TRANSPOSE_180_FLIP:

> > +        ctx->rotation_state = VA_ROTATION_180;

> > +        ctx->mirror_state   = VA_MIRROR_VERTICAL;

> > +        break;

> > +    default:

> > +        av_log(avctx, AV_LOG_ERROR, "Failed to set direction to %d\n",

> ctx->dir);

> > +        return AVERROR(EINVAL);

> > +    }

> > +

> > +    support_flag = pipeline_caps.rotation_flags & (1 <<

> ctx->rotation_state);

> > +    if (!support_flag) {

> > +        av_log(avctx, AV_LOG_ERROR, "VAAPI driver doesn't

> support %d\n",

> > +               ctx->rotation_state);

> > +        return AVERROR(EINVAL);

> > +    }

> > +

> > +    return 0;

> > +}

> > +

> > +static int transpose_vaapi_filter_frame(AVFilterLink *inlink, AVFrame

> > +*input_frame) {

> > +    AVFilterContext *avctx     = inlink->dst;

> > +    AVFilterLink *outlink      = avctx->outputs[0];

> > +    VAAPIVPPContext *vpp_ctx   = avctx->priv;

> > +    TransposeVAAPIContext *ctx = avctx->priv;

> > +    AVFrame *output_frame      = NULL;

> > +    VASurfaceID input_surface, output_surface;

> > +    VARectangle input_region, output_region;

> > +

> > +    VAProcPipelineParameterBuffer params;

> > +    int err;

> > +

> > +    if (ctx->passthrough)

> > +        return ff_filter_frame(outlink, input_frame);

> > +

> > +    av_log(avctx, AV_LOG_DEBUG, "Filter input: %s, %ux%u

> (%"PRId64").\n",

> > +           av_get_pix_fmt_name(input_frame->format),

> > +           input_frame->width, input_frame->height,

> > + input_frame->pts);

> > +

> > +    if (vpp_ctx->va_context == VA_INVALID_ID)

> > +        return AVERROR(EINVAL);

> > +

> > +    input_surface = (VASurfaceID)(uintptr_t)input_frame->data[3];

> > +    av_log(avctx, AV_LOG_DEBUG, "Using surface %#x for transpose

> vpp

> > input.\n",

> > +           input_surface);

> > +

> > +    output_frame = ff_get_video_buffer(outlink,

> vpp_ctx->output_width,

> > +                                       vpp_ctx->output_height);

> > +    if (!output_frame) {

> > +        err = AVERROR(ENOMEM);

> > +        goto fail;

> > +    }

> > +

> > +    output_surface = (VASurfaceID)(uintptr_t)output_frame->data[3];

> > +    av_log(avctx, AV_LOG_DEBUG, "Using surface %#x for transpose

> vpp

> > output.\n",

> > +           output_surface);

> > +    memset(&params, 0, sizeof(params));

> > +    input_region = (VARectangle) {

> > +        .x      = 0,

> > +        .y      = 0,

> > +        .width  = input_frame->width,

> > +        .height = input_frame->height,

> > +    };

> > +

> > +    output_region = (VARectangle) {

> > +        .x      = 0,

> > +        .y      = 0,

> > +        .width  = output_frame->width,

> > +        .height = output_frame->height,

> > +    };

> > +

> > +    switch (ctx->rotation_state) {

> > +    case VA_ROTATION_NONE:

> > +    case VA_ROTATION_90:

> > +    case VA_ROTATION_180:

> > +    case VA_ROTATION_270:

> > +        params.rotation_state = ctx->rotation_state;

> > +        break;

> > +    default:

> > +        av_log(avctx, AV_LOG_ERROR, "VAAPI doesn't support

> rotation %d\n",

> > +               ctx->rotation_state);


"err" hasn't been not initialized here.

> > +        goto fail;

> > +    }

> > +

> > +    switch (ctx->mirror_state) {

> > +    case VA_MIRROR_HORIZONTAL:

> > +    case VA_MIRROR_VERTICAL:

> > +    case VA_MIRROR_NONE:

> > +        params.mirror_state = ctx->mirror_state;

> > +        break;

> > +    default:

> > +        av_log(avctx, AV_LOG_ERROR, "VAAPI doesn't support

> mirroring %d\n",

> > +               ctx->mirror_state);

> > +        goto fail;

> > +    }

> > +

> > +    if (vpp_ctx->nb_filter_buffers) {

> > +        params.filters     = &vpp_ctx->filter_buffers[0];

> > +        params.num_filters = vpp_ctx->nb_filter_buffers;

> > +    }

> > +

> > +    params.surface = input_surface;

> > +    params.surface_region = &input_region;

> > +    params.surface_color_standard =

> > +        ff_vaapi_vpp_colour_standard(input_frame->colorspace);

> > +

> > +    params.output_region = &output_region;

> > +    params.output_background_color = 0xff000000;


Hard code should be avoid. Would better to define a MICRO and comment what 0xff000000 means.

> > +    params.output_color_standard = params.surface_color_standard;

> > +

> > +    params.pipeline_flags = 0;

> > +    params.filter_flags = VA_FRAME_PICTURE;

> > +

> > +    err = ff_vaapi_vpp_render_picture(avctx, &params, output_surface);

> > +    if (err < 0)

> > +        goto fail;

> > +

> > +    err = av_frame_copy_props(output_frame, input_frame);

> > +    if (err < 0)

> > +        goto fail;

> > +    av_frame_free(&input_frame);

> > +

> > +    av_log(avctx, AV_LOG_DEBUG, "Filter output: %s, %ux%u

> (%"PRId64").\n",

> > +           av_get_pix_fmt_name(output_frame->format),

> > +           output_frame->width, output_frame->height,

> > + output_frame->pts);

> > +

> > +    return ff_filter_frame(outlink, output_frame);

> > +

> > +fail:

> > +    av_frame_free(&input_frame);

> > +    av_frame_free(&output_frame);

> > +    return err;

> > +}

> > +

> > +static av_cold int transpose_vaapi_init(AVFilterContext *avctx) {

> > +    VAAPIVPPContext *vpp_ctx = avctx->priv;

> > +

> > +    ff_vaapi_vpp_ctx_init(avctx);

> > +    vpp_ctx->pipeline_uninit     = ff_vaapi_vpp_pipeline_uninit;

> > +    vpp_ctx->build_filter_params =

> transpose_vaapi_build_filter_params;

> > +    vpp_ctx->output_format       = AV_PIX_FMT_NONE;

> > +

> > +    return 0;

> > +}

> > +

> > +static void transpose_vaapi_vpp_ctx_uninit(AVFilterContext *avctx) {

> > +    return ff_vaapi_vpp_ctx_uninit(avctx); }

> > +

> > +static int transpose_vaapi_vpp_query_formats(AVFilterContext *avctx) {

> > +    return ff_vaapi_vpp_query_formats(avctx);

> > +}

> > +

> > +static int transpose_vaapi_vpp_config_input(AVFilterLink *inlink) {

> > +    return ff_vaapi_vpp_config_input(inlink);

> > +}

> > +

> > +static int transpose_vaapi_vpp_config_output(AVFilterLink *outlink) {

> > +    AVFilterContext *avctx     = outlink->src;

> > +    VAAPIVPPContext *vpp_ctx   = avctx->priv;

> > +    TransposeVAAPIContext *ctx = avctx->priv;

> > +    AVFilterLink *inlink       = avctx->inputs[0];

> > +

> > +    if ((inlink->w >= inlink->h && ctx->passthrough ==

> > TRANSPOSE_PT_TYPE_LANDSCAPE) ||

> > +        (inlink->w <= inlink->h && ctx->passthrough ==

> > TRANSPOSE_PT_TYPE_PORTRAIT)) {

> > +        av_log(avctx, AV_LOG_VERBOSE,

> > +               "w:%d h:%d -> w:%d h:%d (passthrough mode)\n",

> > +               inlink->w, inlink->h, inlink->w, inlink->h);

> > +        return ff_vaapi_vpp_config_output(outlink);

> > +    }

> > +

> > +    ctx->passthrough = TRANSPOSE_PT_TYPE_NONE;

> > +

> > +    switch (ctx->dir) {

> > +    case TRANSPOSE_CCLOCK_FLIP:

> > +    case TRANSPOSE_CCLOCK:

> > +    case TRANSPOSE_CLOCK:

> > +    case TRANSPOSE_CLOCK_FLIP:

> > +        vpp_ctx->output_width  = avctx->inputs[0]->h;

> > +        vpp_ctx->output_height = avctx->inputs[0]->w;

> > +        av_log(avctx, AV_LOG_DEBUG, "swap width and height for

> > + clock/cclock

> > rotation\n");

> > +        break;

> > +    default:

> > +        break;

> > +    }

> > +

> > +    return ff_vaapi_vpp_config_output(outlink);

> > +}

> > +

> > +static AVFrame *get_video_buffer(AVFilterLink *inlink, int w, int h) {

> > +    TransposeVAAPIContext *ctx = inlink->dst->priv;

> > +

> > +    return ctx->passthrough ?

> > +        ff_null_get_video_buffer(inlink, w, h) :

> > +        ff_default_get_video_buffer(inlink, w, h); }

> > +

> > +#define OFFSET(x) offsetof(TransposeVAAPIContext, x) #define FLAGS

> > +(AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_FILTERING_PARAM)

> static

> > const

> > +AVOption transpose_vaapi_options[] = {

> > +    { "dir", "set transpose direction", OFFSET(dir), AV_OPT_TYPE_INT,

> > +{ .i64 =

> > TRANSPOSE_CCLOCK_FLIP }, 0, 5, FLAGS, "dir" },

> > +        { "cclock_flip", "rotate counter-clockwise with vertical flip",

> 0,

> > AV_OPT_TYPE_CONST, { .i64 = TRANSPOSE_CCLOCK_FLIP }, .flags=FLAGS,

> > .unit = "dir" },

> > +        { "clock",       "rotate clockwise",

> 0,

> > AV_OPT_TYPE_CONST, { .i64 =

> TRANSPOSE_CLOCK       }, .flags=FLAGS, .unit =

> > "dir" },

> > +        { "cclock",      "rotate counter-clockwise",

> 0,

> > AV_OPT_TYPE_CONST, { .i64 =

> TRANSPOSE_CCLOCK      }, .flags=FLAGS, .unit =

> > "dir" },

> > +        { "clock_flip",  "rotate clockwise with vertical flip",

> 0,

> > AV_OPT_TYPE_CONST, { .i64 = TRANSPOSE_CLOCK_FLIP  }, .flags=FLAGS,

> > .unit = "dir" },

> > +        { "180",         "rotate clockwise 180 degrees",

> 0,


The name style is unmatched with others. Not sure what the exact name should be, may be "reversal"?
BTW, this is not supported by software transpose, would you like to add it?

> > AV_OPT_TYPE_CONST, { .i64 =

> TRANSPOSE_180         }, .flags=FLAGS, .unit =

> > "dir" },

> > +        { "180_flip",    "rotate clockwise 180 degrees with vertical

> flip", 0,

> > AV_OPT_TYPE_CONST, { .i64 =

> TRANSPOSE_180_FLIP    }, .flags=FLAGS, .unit =

> > "dir" },

> > +

> > +    { "passthrough", "do not apply transposition if the input matches

> > + the

> > specified geometry",

> > +      OFFSET(passthrough), AV_OPT_TYPE_INT,

> > {.i64=TRANSPOSE_PT_TYPE_NONE},  0, INT_MAX, FLAGS,

> "passthrough" },

> > +        { "none",      "always apply transposition",   0,

> AV_OPT_TYPE_CONST,

> > {.i64=TRANSPOSE_PT_TYPE_NONE},      INT_MIN, INT_MAX, FLAGS,

> > "passthrough" },

> > +        { "portrait",  "preserve portrait geometry",   0,

> AV_OPT_TYPE_CONST,

> > {.i64=TRANSPOSE_PT_TYPE_PORTRAIT},  INT_MIN, INT_MAX, FLAGS,

> > "passthrough" },

> > +        { "landscape", "preserve landscape geometry",  0,

> > + AV_OPT_TYPE_CONST, {.i64=TRANSPOSE_PT_TYPE_LANDSCAPE},

> INT_MIN,

> > + INT_MAX, FLAGS, "passthrough" },

> > +

> > +    { NULL }

> > +};

> > +

> > +

> > +AVFILTER_DEFINE_CLASS(transpose_vaapi);

> > +

> > +static const AVFilterPad transpose_vaapi_inputs[] = {

> > +    {

> > +        .name         = "default",

> > +        .type         = AVMEDIA_TYPE_VIDEO,

> > +        .filter_frame = &transpose_vaapi_filter_frame,

> > +        .get_video_buffer = get_video_buffer,

> > +        .config_props = &transpose_vaapi_vpp_config_input,

> > +    },

> > +    { NULL }

> > +};

> > +

> > +static const AVFilterPad transpose_vaapi_outputs[] = {

> > +    {

> > +        .name = "default",

> > +        .type = AVMEDIA_TYPE_VIDEO,

> > +        .config_props = &transpose_vaapi_vpp_config_output,

> > +    },

> > +    { NULL }

> > +};

> > +

> > +AVFilter ff_vf_transpose_vaapi = {

> > +    .name           = "transpose_vaapi",

> > +    .description    = NULL_IF_CONFIG_SMALL("VAAPI VPP for

> transpose"),

> > +    .priv_size      = sizeof(TransposeVAAPIContext),

> > +    .init           = &transpose_vaapi_init,

> > +    .uninit         = &transpose_vaapi_vpp_ctx_uninit,

> > +    .query_formats  = &transpose_vaapi_vpp_query_formats,

> > +    .inputs         = transpose_vaapi_inputs,

> > +    .outputs        = transpose_vaapi_outputs,

> > +    .priv_class     = &transpose_vaapi_class,

> > +    .flags_internal = FF_FILTER_FLAG_HWFRAME_AWARE, };

> > --

> > 2.17.1

> >
Zachary Zhou Dec. 18, 2018, 9:01 a.m.
Thank you for the review, my reply inline.
new patch version will be sent out.

> -----Original Message-----


> From: Li, Zhong

> Sent: Tuesday, December 18, 2018 2:52 PM

> To: FFmpeg development discussions and patches <ffmpeg-devel@ffmpeg.org>

> Cc: Zhou, Zachary <zachary.zhou@intel.com>

> Subject: RE: [FFmpeg-devel] [PATCH v4] libavfilter: add transpose_vaapi filter

> 

> > From: ffmpeg-devel [mailto:ffmpeg-devel-bounces@ffmpeg.org] On Behalf

> > Of Zhou, Zachary

> > Sent: Monday, December 3, 2018 6:56 PM

> > To: FFmpeg development discussions and patches

> > <ffmpeg-devel@ffmpeg.org>

> > Cc: Zhou, Zachary <zachary.zhou@intel.com>

> > Subject: Re: [FFmpeg-devel] [PATCH v4] libavfilter: add

> > transpose_vaapi filter

> >

> > ping? any more comments ?

> >

> > > -----Original Message-----

> > > From: ffmpeg-devel [mailto:ffmpeg-devel-bounces@ffmpeg.org] On

> > Behalf

> > > Of Zachary Zhou

> > > Sent: Thursday, November 29, 2018 2:14 PM

> > > To: ffmpeg-devel@ffmpeg.org

> > > Cc: Zachary Zhou <zachary.zhou@hotmail.com>; Zhou, Zachary

> > > <zachary.zhou@intel.com>

> > > Subject: [FFmpeg-devel] [PATCH v4] libavfilter: add transpose_vaapi

> > > filter

> > >

> > > Swap width and height when do clock/cclock rotation Add 180/180_flip

> > > options

> > >

> > > ffmpeg -hwaccel vaapi -vaapi_device /dev/dri/renderD128 -

> > > hwaccel_output_format vaapi -i input.264 -vf

> > "transpose_vaapi=clock_flip"

> > > -c:v h264_vaapi output.h264

> > >

> > > Signed-off-by: Zachary Zhou <zachary.zhou@hotmail.com>

> > > ---

> > >  configure                        |   2 +

> > >  libavfilter/Makefile             |   1 +

> > >  libavfilter/allfilters.c         |   1 +

> > >  libavfilter/vf_transpose_vaapi.c | 356

> > > +++++++++++++++++++++++++++++++

> > >  4 files changed, 360 insertions(+)

> > >  create mode 100644 libavfilter/vf_transpose_vaapi.c

> > >

> > > diff --git a/configure b/configure

> > > index 3e9222ed1b..9e3908ef61 100755

> > > --- a/configure

> > > +++ b/configure

> > > @@ -3480,6 +3480,7 @@ tinterlace_merge_test_deps="tinterlace_filter"

> > >  tinterlace_pad_test_deps="tinterlace_filter"

> > >  tonemap_filter_deps="const_nan"

> > >  tonemap_opencl_filter_deps="opencl const_nan"

> > > +transpose_vaapi_filter_deps="vaapi VAProcPipelineCaps_rotation_flags"

> > >  unsharp_opencl_filter_deps="opencl"

> > >  uspp_filter_deps="gpl avcodec"

> > >  vaguedenoiser_filter_deps="gpl"

> > > @@ -5979,6 +5980,7 @@ check_type "d3d9.h dxva2api.h"

> > > DXVA2_ConfigPictureDecode -D_WIN32_WINNT=0x0602

> > >

> > >  check_type "va/va.h va/va_dec_hevc.h"

> > "VAPictureParameterBufferHEVC"

> > >  check_struct "va/va.h" "VADecPictureParameterBufferVP9" bit_depth

> > > +check_struct "va/va.h va/va_vpp.h" "VAProcPipelineCaps"

> > > +rotation_flags

> > >  check_type "va/va.h va/va_enc_hevc.h"

> > > "VAEncPictureParameterBufferHEVC"

> > >  check_type "va/va.h va/va_enc_jpeg.h"

> > "VAEncPictureParameterBufferJPEG"

> > >  check_type "va/va.h va/va_enc_vp8.h"

> > "VAEncPictureParameterBufferVP8"

> > > diff --git a/libavfilter/Makefile b/libavfilter/Makefile index

> > > 1895fa2b0d..ccce4bbb2f 100644

> > > --- a/libavfilter/Makefile

> > > +++ b/libavfilter/Makefile

> > > @@ -393,6 +393,7 @@ OBJS-$(CONFIG_TONEMAP_OPENCL_FILTER)

> > +=

> > > vf_tonemap_opencl.o colorspace.o

> > >  OBJS-$(CONFIG_TPAD_FILTER)                   += vf_tpad.o

> > >  OBJS-$(CONFIG_TRANSPOSE_FILTER)              += vf_transpose.o

> > >  OBJS-$(CONFIG_TRANSPOSE_NPP_FILTER)          +=

> > vf_transpose_npp.o

> > > cuda_check.o

> > > +OBJS-$(CONFIG_TRANSPOSE_VAAPI_FILTER)        +=

> > vf_transpose_vaapi.o

> > > vaapi_vpp.o

> > >  OBJS-$(CONFIG_TRIM_FILTER)                   += trim.o

> > >  OBJS-$(CONFIG_UNPREMULTIPLY_FILTER)          +=

> > vf_premultiply.o

> > > framesync.o

> > >  OBJS-$(CONFIG_UNSHARP_FILTER)                += vf_unsharp.o

> > > diff --git a/libavfilter/allfilters.c b/libavfilter/allfilters.c

> > > index

> > > 837c99eb75..40e5d82b62 100644

> > > --- a/libavfilter/allfilters.c

> > > +++ b/libavfilter/allfilters.c

> > > @@ -372,6 +372,7 @@ extern AVFilter ff_vf_tonemap_opencl;  extern

> > > AVFilter ff_vf_tpad;  extern AVFilter ff_vf_transpose;  extern

> > > AVFilter ff_vf_transpose_npp;

> > > +extern AVFilter ff_vf_transpose_vaapi;

> > >  extern AVFilter ff_vf_trim;

> > >  extern AVFilter ff_vf_unpremultiply;  extern AVFilter

> > > ff_vf_unsharp; diff --git a/libavfilter/vf_transpose_vaapi.c

> > > b/libavfilter/vf_transpose_vaapi.c

> > > new file mode 100644

> > > index 0000000000..d0502b7944

> > > --- /dev/null

> > > +++ b/libavfilter/vf_transpose_vaapi.c

> > > @@ -0,0 +1,356 @@

> > > +/*

> > > + * This file is part of FFmpeg.

> > > + *

> > > + * FFmpeg is free software; you can redistribute it and/or

> > > + * modify it under the terms of the GNU Lesser General Public

> > > + * License as published by the Free Software Foundation; either

> > > + * version 2.1 of the License, or (at your option) any later version.

> > > + *

> > > + * FFmpeg is distributed in the hope that it will be useful,

> > > + * but WITHOUT ANY WARRANTY; without even the implied warranty of

> > > + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the

> > GNU

> > > + * Lesser General Public License for more details.

> > > + *

> > > + * You should have received a copy of the GNU Lesser General Public

> > > + * License along with FFmpeg; if not, write to the Free Software

> > > + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA

> > > +02110-1301 USA  */ #include <string.h>

> > > +

> > > +#include "libavutil/avassert.h"

> > > +#include "libavutil/mem.h"

> > > +#include "libavutil/opt.h"

> > > +#include "libavutil/pixdesc.h"

> > > +

> > > +#include "avfilter.h"

> > > +#include "formats.h"

> > > +#include "internal.h"

> > > +#include "vaapi_vpp.h"

> > > +

> > > +typedef enum {

> > > +    TRANSPOSE_PT_TYPE_NONE,

> > > +    TRANSPOSE_PT_TYPE_LANDSCAPE,

> > > +    TRANSPOSE_PT_TYPE_PORTRAIT,

> > > +} PassthroughType;

> > > +

> > > +enum TransposeDir {

> > > +    TRANSPOSE_CCLOCK_FLIP,

> > > +    TRANSPOSE_CLOCK,

> > > +    TRANSPOSE_CCLOCK,

> > > +    TRANSPOSE_CLOCK_FLIP,

> > > +    TRANSPOSE_180,

> > > +    TRANSPOSE_180_FLIP,

> > > +};

> > > +

> > > +typedef struct TransposeVAAPIContext {

> > > +    VAAPIVPPContext vpp_ctx; // must be the first field

> > > +    int passthrough;         // PassthroughType, landscape

> > passthrough mode

> > > enabled

> > > +    int dir;                 // TransposeDir

> > > +

> > > +    int rotation_state;

> > > +    int mirror_state;

> > > +} TransposeVAAPIContext;

> > > +

> > > +static int transpose_vaapi_build_filter_params(AVFilterContext

> > > +*avctx) {

> > > +    VAAPIVPPContext *vpp_ctx   = avctx->priv;

> > > +    TransposeVAAPIContext *ctx = avctx->priv;

> > > +    VAStatus vas;

> > > +    int support_flag;

> > > +    VAProcPipelineCaps pipeline_caps;

> > > +

> > > +    memset(&pipeline_caps, 0, sizeof(pipeline_caps));

> > > +    vas = vaQueryVideoProcPipelineCaps(vpp_ctx->hwctx->display,

> > > +                                       vpp_ctx->va_context,

> > > +                                       NULL, 0,

> > > +                                       &pipeline_caps);

> > > +    if (vas != VA_STATUS_SUCCESS) {

> > > +        av_log(avctx, AV_LOG_ERROR, "Failed to query pipeline "

> > > +               "caps: %d (%s).\n", vas, vaErrorStr(vas));

> > > +        return AVERROR(EIO);

> > > +    }

> > > +

> > > +    if (!pipeline_caps.rotation_flags) {

> > > +        av_log(avctx, AV_LOG_ERROR, "VAAPI driver doesn't support

> > > transpose\n");

> > > +        return AVERROR(EINVAL);

> > > +    }

> > > +

> > > +    switch (ctx->dir) {

> > > +    case TRANSPOSE_CCLOCK_FLIP:

> > > +        ctx->rotation_state = VA_ROTATION_270;

> > > +        ctx->mirror_state   = VA_MIRROR_VERTICAL;

> > > +        break;

> > > +    case TRANSPOSE_CLOCK:

> > > +        ctx->rotation_state = VA_ROTATION_90;

> > > +        ctx->mirror_state   = VA_MIRROR_NONE;

> > > +        break;

> > > +    case TRANSPOSE_CCLOCK:

> > > +        ctx->rotation_state = VA_ROTATION_270;

> > > +        ctx->mirror_state   = VA_MIRROR_NONE;

> > > +        break;

> > > +    case TRANSPOSE_CLOCK_FLIP:

> > > +        ctx->rotation_state = VA_ROTATION_90;

> > > +        ctx->mirror_state   = VA_MIRROR_VERTICAL;

> > > +        break;

> > > +    case TRANSPOSE_180:

> > > +        ctx->rotation_state = VA_ROTATION_180;

> > > +        ctx->mirror_state   = VA_MIRROR_NONE;

> > > +        break;

> > > +    case TRANSPOSE_180_FLIP:

> > > +        ctx->rotation_state = VA_ROTATION_180;

> > > +        ctx->mirror_state   = VA_MIRROR_VERTICAL;

> > > +        break;

> > > +    default:

> > > +        av_log(avctx, AV_LOG_ERROR, "Failed to set direction to

> > > + %d\n",

> > ctx->dir);

> > > +        return AVERROR(EINVAL);

> > > +    }

> > > +

> > > +    support_flag = pipeline_caps.rotation_flags & (1 <<

> > ctx->rotation_state);

> > > +    if (!support_flag) {

> > > +        av_log(avctx, AV_LOG_ERROR, "VAAPI driver doesn't

> > support %d\n",

> > > +               ctx->rotation_state);

> > > +        return AVERROR(EINVAL);

> > > +    }

> > > +

> > > +    return 0;

> > > +}

> > > +

> > > +static int transpose_vaapi_filter_frame(AVFilterLink *inlink,

> > > +AVFrame

> > > +*input_frame) {

> > > +    AVFilterContext *avctx     = inlink->dst;

> > > +    AVFilterLink *outlink      = avctx->outputs[0];

> > > +    VAAPIVPPContext *vpp_ctx   = avctx->priv;

> > > +    TransposeVAAPIContext *ctx = avctx->priv;

> > > +    AVFrame *output_frame      = NULL;

> > > +    VASurfaceID input_surface, output_surface;

> > > +    VARectangle input_region, output_region;

> > > +

> > > +    VAProcPipelineParameterBuffer params;

> > > +    int err;

> > > +

> > > +    if (ctx->passthrough)

> > > +        return ff_filter_frame(outlink, input_frame);

> > > +

> > > +    av_log(avctx, AV_LOG_DEBUG, "Filter input: %s, %ux%u

> > (%"PRId64").\n",

> > > +           av_get_pix_fmt_name(input_frame->format),

> > > +           input_frame->width, input_frame->height,

> > > + input_frame->pts);

> > > +

> > > +    if (vpp_ctx->va_context == VA_INVALID_ID)

> > > +        return AVERROR(EINVAL);

> > > +

> > > +    input_surface = (VASurfaceID)(uintptr_t)input_frame->data[3];

> > > +    av_log(avctx, AV_LOG_DEBUG, "Using surface %#x for transpose

> > vpp

> > > input.\n",

> > > +           input_surface);

> > > +

> > > +    output_frame = ff_get_video_buffer(outlink,

> > vpp_ctx->output_width,

> > > +                                       vpp_ctx->output_height);

> > > +    if (!output_frame) {

> > > +        err = AVERROR(ENOMEM);

> > > +        goto fail;

> > > +    }

> > > +

> > > +    output_surface = (VASurfaceID)(uintptr_t)output_frame->data[3];

> > > +    av_log(avctx, AV_LOG_DEBUG, "Using surface %#x for transpose

> > vpp

> > > output.\n",

> > > +           output_surface);

> > > +    memset(&params, 0, sizeof(params));

> > > +    input_region = (VARectangle) {

> > > +        .x      = 0,

> > > +        .y      = 0,

> > > +        .width  = input_frame->width,

> > > +        .height = input_frame->height,

> > > +    };

> > > +

> > > +    output_region = (VARectangle) {

> > > +        .x      = 0,

> > > +        .y      = 0,

> > > +        .width  = output_frame->width,

> > > +        .height = output_frame->height,

> > > +    };

> > > +

> > > +    switch (ctx->rotation_state) {

> > > +    case VA_ROTATION_NONE:

> > > +    case VA_ROTATION_90:

> > > +    case VA_ROTATION_180:

> > > +    case VA_ROTATION_270:

> > > +        params.rotation_state = ctx->rotation_state;

> > > +        break;

> > > +    default:

> > > +        av_log(avctx, AV_LOG_ERROR, "VAAPI doesn't support

> > rotation %d\n",

> > > +               ctx->rotation_state);

> 

> "err" hasn't been not initialized here.


added err setting before "goto fail" statement.

> 

> > > +        goto fail;

> > > +    }

> > > +

> > > +    switch (ctx->mirror_state) {

> > > +    case VA_MIRROR_HORIZONTAL:

> > > +    case VA_MIRROR_VERTICAL:

> > > +    case VA_MIRROR_NONE:

> > > +        params.mirror_state = ctx->mirror_state;

> > > +        break;

> > > +    default:

> > > +        av_log(avctx, AV_LOG_ERROR, "VAAPI doesn't support

> > mirroring %d\n",

> > > +               ctx->mirror_state);

> > > +        goto fail;

> > > +    }

> > > +

> > > +    if (vpp_ctx->nb_filter_buffers) {

> > > +        params.filters     = &vpp_ctx->filter_buffers[0];

> > > +        params.num_filters = vpp_ctx->nb_filter_buffers;

> > > +    }

> > > +

> > > +    params.surface = input_surface;

> > > +    params.surface_region = &input_region;

> > > +    params.surface_color_standard =

> > > +        ff_vaapi_vpp_colour_standard(input_frame->colorspace);

> > > +

> > > +    params.output_region = &output_region;

> > > +    params.output_background_color = 0xff000000;

> 

> Hard code should be avoid. Would better to define a MICRO and comment

> what 0xff000000 means.


added #define BG_BLACK 0xff000000

> 

> > > +    params.output_color_standard = params.surface_color_standard;

> > > +

> > > +    params.pipeline_flags = 0;

> > > +    params.filter_flags = VA_FRAME_PICTURE;

> > > +

> > > +    err = ff_vaapi_vpp_render_picture(avctx, &params, output_surface);

> > > +    if (err < 0)

> > > +        goto fail;

> > > +

> > > +    err = av_frame_copy_props(output_frame, input_frame);

> > > +    if (err < 0)

> > > +        goto fail;

> > > +    av_frame_free(&input_frame);

> > > +

> > > +    av_log(avctx, AV_LOG_DEBUG, "Filter output: %s, %ux%u

> > (%"PRId64").\n",

> > > +           av_get_pix_fmt_name(output_frame->format),

> > > +           output_frame->width, output_frame->height,

> > > + output_frame->pts);

> > > +

> > > +    return ff_filter_frame(outlink, output_frame);

> > > +

> > > +fail:

> > > +    av_frame_free(&input_frame);

> > > +    av_frame_free(&output_frame);

> > > +    return err;

> > > +}

> > > +

> > > +static av_cold int transpose_vaapi_init(AVFilterContext *avctx) {

> > > +    VAAPIVPPContext *vpp_ctx = avctx->priv;

> > > +

> > > +    ff_vaapi_vpp_ctx_init(avctx);

> > > +    vpp_ctx->pipeline_uninit     = ff_vaapi_vpp_pipeline_uninit;

> > > +    vpp_ctx->build_filter_params =

> > transpose_vaapi_build_filter_params;

> > > +    vpp_ctx->output_format       = AV_PIX_FMT_NONE;

> > > +

> > > +    return 0;

> > > +}

> > > +

> > > +static void transpose_vaapi_vpp_ctx_uninit(AVFilterContext *avctx) {

> > > +    return ff_vaapi_vpp_ctx_uninit(avctx); }

> > > +

> > > +static int transpose_vaapi_vpp_query_formats(AVFilterContext *avctx) {

> > > +    return ff_vaapi_vpp_query_formats(avctx);

> > > +}

> > > +

> > > +static int transpose_vaapi_vpp_config_input(AVFilterLink *inlink) {

> > > +    return ff_vaapi_vpp_config_input(inlink);

> > > +}

> > > +

> > > +static int transpose_vaapi_vpp_config_output(AVFilterLink *outlink) {

> > > +    AVFilterContext *avctx     = outlink->src;

> > > +    VAAPIVPPContext *vpp_ctx   = avctx->priv;

> > > +    TransposeVAAPIContext *ctx = avctx->priv;

> > > +    AVFilterLink *inlink       = avctx->inputs[0];

> > > +

> > > +    if ((inlink->w >= inlink->h && ctx->passthrough ==

> > > TRANSPOSE_PT_TYPE_LANDSCAPE) ||

> > > +        (inlink->w <= inlink->h && ctx->passthrough ==

> > > TRANSPOSE_PT_TYPE_PORTRAIT)) {

> > > +        av_log(avctx, AV_LOG_VERBOSE,

> > > +               "w:%d h:%d -> w:%d h:%d (passthrough mode)\n",

> > > +               inlink->w, inlink->h, inlink->w, inlink->h);

> > > +        return ff_vaapi_vpp_config_output(outlink);

> > > +    }

> > > +

> > > +    ctx->passthrough = TRANSPOSE_PT_TYPE_NONE;

> > > +

> > > +    switch (ctx->dir) {

> > > +    case TRANSPOSE_CCLOCK_FLIP:

> > > +    case TRANSPOSE_CCLOCK:

> > > +    case TRANSPOSE_CLOCK:

> > > +    case TRANSPOSE_CLOCK_FLIP:

> > > +        vpp_ctx->output_width  = avctx->inputs[0]->h;

> > > +        vpp_ctx->output_height = avctx->inputs[0]->w;

> > > +        av_log(avctx, AV_LOG_DEBUG, "swap width and height for

> > > + clock/cclock

> > > rotation\n");

> > > +        break;

> > > +    default:

> > > +        break;

> > > +    }

> > > +

> > > +    return ff_vaapi_vpp_config_output(outlink);

> > > +}

> > > +

> > > +static AVFrame *get_video_buffer(AVFilterLink *inlink, int w, int h) {

> > > +    TransposeVAAPIContext *ctx = inlink->dst->priv;

> > > +

> > > +    return ctx->passthrough ?

> > > +        ff_null_get_video_buffer(inlink, w, h) :

> > > +        ff_default_get_video_buffer(inlink, w, h); }

> > > +

> > > +#define OFFSET(x) offsetof(TransposeVAAPIContext, x) #define FLAGS

> > > +(AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_FILTERING_PARAM)

> > static

> > > const

> > > +AVOption transpose_vaapi_options[] = {

> > > +    { "dir", "set transpose direction", OFFSET(dir),

> > > +AV_OPT_TYPE_INT, { .i64 =

> > > TRANSPOSE_CCLOCK_FLIP }, 0, 5, FLAGS, "dir" },

> > > +        { "cclock_flip", "rotate counter-clockwise with vertical

> > > + flip",

> > 0,

> > > AV_OPT_TYPE_CONST, { .i64 = TRANSPOSE_CCLOCK_FLIP }, .flags=FLAGS,

> > > .unit = "dir" },

> > > +        { "clock",       "rotate clockwise",

> > 0,

> > > AV_OPT_TYPE_CONST, { .i64 =

> > TRANSPOSE_CLOCK       }, .flags=FLAGS, .unit =

> > > "dir" },

> > > +        { "cclock",      "rotate counter-clockwise",

> > 0,

> > > AV_OPT_TYPE_CONST, { .i64 =

> > TRANSPOSE_CCLOCK      }, .flags=FLAGS, .unit =

> > > "dir" },

> > > +        { "clock_flip",  "rotate clockwise with vertical flip",

> > 0,

> > > AV_OPT_TYPE_CONST, { .i64 = TRANSPOSE_CLOCK_FLIP  }, .flags=FLAGS,

> > > .unit = "dir" },

> > > +        { "180",         "rotate clockwise 180 degrees",

> > 0,

> 

> The name style is unmatched with others. Not sure what the exact name

> should be, may be "reversal"?

> BTW, this is not supported by software transpose, would you like to add it?


changed the name to "reversal",  I am happy to do it.


> 

> > > AV_OPT_TYPE_CONST, { .i64 =

> > TRANSPOSE_180         }, .flags=FLAGS, .unit =

> > > "dir" },

> > > +        { "180_flip",    "rotate clockwise 180 degrees with vertical

> > flip", 0,

> > > AV_OPT_TYPE_CONST, { .i64 =

> > TRANSPOSE_180_FLIP    }, .flags=FLAGS, .unit =

> > > "dir" },

> > > +

> > > +    { "passthrough", "do not apply transposition if the input

> > > + matches the

> > > specified geometry",

> > > +      OFFSET(passthrough), AV_OPT_TYPE_INT,

> > > {.i64=TRANSPOSE_PT_TYPE_NONE},  0, INT_MAX, FLAGS,

> > "passthrough" },

> > > +        { "none",      "always apply transposition",   0,

> > AV_OPT_TYPE_CONST,

> > > {.i64=TRANSPOSE_PT_TYPE_NONE},      INT_MIN, INT_MAX, FLAGS,

> > > "passthrough" },

> > > +        { "portrait",  "preserve portrait geometry",   0,

> > AV_OPT_TYPE_CONST,

> > > {.i64=TRANSPOSE_PT_TYPE_PORTRAIT},  INT_MIN, INT_MAX, FLAGS,

> > > "passthrough" },

> > > +        { "landscape", "preserve landscape geometry",  0,

> > > + AV_OPT_TYPE_CONST, {.i64=TRANSPOSE_PT_TYPE_LANDSCAPE},

> > INT_MIN,

> > > + INT_MAX, FLAGS, "passthrough" },

> > > +

> > > +    { NULL }

> > > +};

> > > +

> > > +

> > > +AVFILTER_DEFINE_CLASS(transpose_vaapi);

> > > +

> > > +static const AVFilterPad transpose_vaapi_inputs[] = {

> > > +    {

> > > +        .name         = "default",

> > > +        .type         = AVMEDIA_TYPE_VIDEO,

> > > +        .filter_frame = &transpose_vaapi_filter_frame,

> > > +        .get_video_buffer = get_video_buffer,

> > > +        .config_props = &transpose_vaapi_vpp_config_input,

> > > +    },

> > > +    { NULL }

> > > +};

> > > +

> > > +static const AVFilterPad transpose_vaapi_outputs[] = {

> > > +    {

> > > +        .name = "default",

> > > +        .type = AVMEDIA_TYPE_VIDEO,

> > > +        .config_props = &transpose_vaapi_vpp_config_output,

> > > +    },

> > > +    { NULL }

> > > +};

> > > +

> > > +AVFilter ff_vf_transpose_vaapi = {

> > > +    .name           = "transpose_vaapi",

> > > +    .description    = NULL_IF_CONFIG_SMALL("VAAPI VPP for

> > transpose"),

> > > +    .priv_size      = sizeof(TransposeVAAPIContext),

> > > +    .init           = &transpose_vaapi_init,

> > > +    .uninit         = &transpose_vaapi_vpp_ctx_uninit,

> > > +    .query_formats  = &transpose_vaapi_vpp_query_formats,

> > > +    .inputs         = transpose_vaapi_inputs,

> > > +    .outputs        = transpose_vaapi_outputs,

> > > +    .priv_class     = &transpose_vaapi_class,

> > > +    .flags_internal = FF_FILTER_FLAG_HWFRAME_AWARE, };

> > > --

> > > 2.17.1

> > >

Patch hide | download patch | download mbox

diff --git a/configure b/configure
index 3e9222ed1b..9e3908ef61 100755
--- a/configure
+++ b/configure
@@ -3480,6 +3480,7 @@  tinterlace_merge_test_deps="tinterlace_filter"
 tinterlace_pad_test_deps="tinterlace_filter"
 tonemap_filter_deps="const_nan"
 tonemap_opencl_filter_deps="opencl const_nan"
+transpose_vaapi_filter_deps="vaapi VAProcPipelineCaps_rotation_flags"
 unsharp_opencl_filter_deps="opencl"
 uspp_filter_deps="gpl avcodec"
 vaguedenoiser_filter_deps="gpl"
@@ -5979,6 +5980,7 @@  check_type "d3d9.h dxva2api.h" DXVA2_ConfigPictureDecode -D_WIN32_WINNT=0x0602
 
 check_type "va/va.h va/va_dec_hevc.h" "VAPictureParameterBufferHEVC"
 check_struct "va/va.h" "VADecPictureParameterBufferVP9" bit_depth
+check_struct "va/va.h va/va_vpp.h" "VAProcPipelineCaps" rotation_flags
 check_type "va/va.h va/va_enc_hevc.h" "VAEncPictureParameterBufferHEVC"
 check_type "va/va.h va/va_enc_jpeg.h" "VAEncPictureParameterBufferJPEG"
 check_type "va/va.h va/va_enc_vp8.h"  "VAEncPictureParameterBufferVP8"
diff --git a/libavfilter/Makefile b/libavfilter/Makefile
index 1895fa2b0d..ccce4bbb2f 100644
--- a/libavfilter/Makefile
+++ b/libavfilter/Makefile
@@ -393,6 +393,7 @@  OBJS-$(CONFIG_TONEMAP_OPENCL_FILTER)         += vf_tonemap_opencl.o colorspace.o
 OBJS-$(CONFIG_TPAD_FILTER)                   += vf_tpad.o
 OBJS-$(CONFIG_TRANSPOSE_FILTER)              += vf_transpose.o
 OBJS-$(CONFIG_TRANSPOSE_NPP_FILTER)          += vf_transpose_npp.o cuda_check.o
+OBJS-$(CONFIG_TRANSPOSE_VAAPI_FILTER)        += vf_transpose_vaapi.o vaapi_vpp.o
 OBJS-$(CONFIG_TRIM_FILTER)                   += trim.o
 OBJS-$(CONFIG_UNPREMULTIPLY_FILTER)          += vf_premultiply.o framesync.o
 OBJS-$(CONFIG_UNSHARP_FILTER)                += vf_unsharp.o
diff --git a/libavfilter/allfilters.c b/libavfilter/allfilters.c
index 837c99eb75..40e5d82b62 100644
--- a/libavfilter/allfilters.c
+++ b/libavfilter/allfilters.c
@@ -372,6 +372,7 @@  extern AVFilter ff_vf_tonemap_opencl;
 extern AVFilter ff_vf_tpad;
 extern AVFilter ff_vf_transpose;
 extern AVFilter ff_vf_transpose_npp;
+extern AVFilter ff_vf_transpose_vaapi;
 extern AVFilter ff_vf_trim;
 extern AVFilter ff_vf_unpremultiply;
 extern AVFilter ff_vf_unsharp;
diff --git a/libavfilter/vf_transpose_vaapi.c b/libavfilter/vf_transpose_vaapi.c
new file mode 100644
index 0000000000..d0502b7944
--- /dev/null
+++ b/libavfilter/vf_transpose_vaapi.c
@@ -0,0 +1,356 @@ 
+/*
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * FFmpeg is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+#include <string.h>
+
+#include "libavutil/avassert.h"
+#include "libavutil/mem.h"
+#include "libavutil/opt.h"
+#include "libavutil/pixdesc.h"
+
+#include "avfilter.h"
+#include "formats.h"
+#include "internal.h"
+#include "vaapi_vpp.h"
+
+typedef enum {
+    TRANSPOSE_PT_TYPE_NONE,
+    TRANSPOSE_PT_TYPE_LANDSCAPE,
+    TRANSPOSE_PT_TYPE_PORTRAIT,
+} PassthroughType;
+
+enum TransposeDir {
+    TRANSPOSE_CCLOCK_FLIP,
+    TRANSPOSE_CLOCK,
+    TRANSPOSE_CCLOCK,
+    TRANSPOSE_CLOCK_FLIP,
+    TRANSPOSE_180,
+    TRANSPOSE_180_FLIP,
+};
+
+typedef struct TransposeVAAPIContext {
+    VAAPIVPPContext vpp_ctx; // must be the first field
+    int passthrough;         // PassthroughType, landscape passthrough mode enabled
+    int dir;                 // TransposeDir
+
+    int rotation_state;
+    int mirror_state;
+} TransposeVAAPIContext;
+
+static int transpose_vaapi_build_filter_params(AVFilterContext *avctx)
+{
+    VAAPIVPPContext *vpp_ctx   = avctx->priv;
+    TransposeVAAPIContext *ctx = avctx->priv;
+    VAStatus vas;
+    int support_flag;
+    VAProcPipelineCaps pipeline_caps;
+
+    memset(&pipeline_caps, 0, sizeof(pipeline_caps));
+    vas = vaQueryVideoProcPipelineCaps(vpp_ctx->hwctx->display,
+                                       vpp_ctx->va_context,
+                                       NULL, 0,
+                                       &pipeline_caps);
+    if (vas != VA_STATUS_SUCCESS) {
+        av_log(avctx, AV_LOG_ERROR, "Failed to query pipeline "
+               "caps: %d (%s).\n", vas, vaErrorStr(vas));
+        return AVERROR(EIO);
+    }
+
+    if (!pipeline_caps.rotation_flags) {
+        av_log(avctx, AV_LOG_ERROR, "VAAPI driver doesn't support transpose\n");
+        return AVERROR(EINVAL);
+    }
+
+    switch (ctx->dir) {
+    case TRANSPOSE_CCLOCK_FLIP:
+        ctx->rotation_state = VA_ROTATION_270;
+        ctx->mirror_state   = VA_MIRROR_VERTICAL;
+        break;
+    case TRANSPOSE_CLOCK:
+        ctx->rotation_state = VA_ROTATION_90;
+        ctx->mirror_state   = VA_MIRROR_NONE;
+        break;
+    case TRANSPOSE_CCLOCK:
+        ctx->rotation_state = VA_ROTATION_270;
+        ctx->mirror_state   = VA_MIRROR_NONE;
+        break;
+    case TRANSPOSE_CLOCK_FLIP:
+        ctx->rotation_state = VA_ROTATION_90;
+        ctx->mirror_state   = VA_MIRROR_VERTICAL;
+        break;
+    case TRANSPOSE_180:
+        ctx->rotation_state = VA_ROTATION_180;
+        ctx->mirror_state   = VA_MIRROR_NONE;
+        break;
+    case TRANSPOSE_180_FLIP:
+        ctx->rotation_state = VA_ROTATION_180;
+        ctx->mirror_state   = VA_MIRROR_VERTICAL;
+        break;
+    default:
+        av_log(avctx, AV_LOG_ERROR, "Failed to set direction to %d\n", ctx->dir);
+        return AVERROR(EINVAL);
+    }
+
+    support_flag = pipeline_caps.rotation_flags & (1 << ctx->rotation_state);
+    if (!support_flag) {
+        av_log(avctx, AV_LOG_ERROR, "VAAPI driver doesn't support %d\n",
+               ctx->rotation_state);
+        return AVERROR(EINVAL);
+    }
+
+    return 0;
+}
+
+static int transpose_vaapi_filter_frame(AVFilterLink *inlink, AVFrame *input_frame)
+{
+    AVFilterContext *avctx     = inlink->dst;
+    AVFilterLink *outlink      = avctx->outputs[0];
+    VAAPIVPPContext *vpp_ctx   = avctx->priv;
+    TransposeVAAPIContext *ctx = avctx->priv;
+    AVFrame *output_frame      = NULL;
+    VASurfaceID input_surface, output_surface;
+    VARectangle input_region, output_region;
+
+    VAProcPipelineParameterBuffer params;
+    int err;
+
+    if (ctx->passthrough)
+        return ff_filter_frame(outlink, input_frame);
+
+    av_log(avctx, AV_LOG_DEBUG, "Filter input: %s, %ux%u (%"PRId64").\n",
+           av_get_pix_fmt_name(input_frame->format),
+           input_frame->width, input_frame->height, input_frame->pts);
+
+    if (vpp_ctx->va_context == VA_INVALID_ID)
+        return AVERROR(EINVAL);
+
+    input_surface = (VASurfaceID)(uintptr_t)input_frame->data[3];
+    av_log(avctx, AV_LOG_DEBUG, "Using surface %#x for transpose vpp input.\n",
+           input_surface);
+
+    output_frame = ff_get_video_buffer(outlink, vpp_ctx->output_width,
+                                       vpp_ctx->output_height);
+    if (!output_frame) {
+        err = AVERROR(ENOMEM);
+        goto fail;
+    }
+
+    output_surface = (VASurfaceID)(uintptr_t)output_frame->data[3];
+    av_log(avctx, AV_LOG_DEBUG, "Using surface %#x for transpose vpp output.\n",
+           output_surface);
+    memset(&params, 0, sizeof(params));
+    input_region = (VARectangle) {
+        .x      = 0,
+        .y      = 0,
+        .width  = input_frame->width,
+        .height = input_frame->height,
+    };
+
+    output_region = (VARectangle) {
+        .x      = 0,
+        .y      = 0,
+        .width  = output_frame->width,
+        .height = output_frame->height,
+    };
+
+    switch (ctx->rotation_state) {
+    case VA_ROTATION_NONE:
+    case VA_ROTATION_90:
+    case VA_ROTATION_180:
+    case VA_ROTATION_270:
+        params.rotation_state = ctx->rotation_state;
+        break;
+    default:
+        av_log(avctx, AV_LOG_ERROR, "VAAPI doesn't support rotation %d\n",
+               ctx->rotation_state);
+        goto fail;
+    }
+
+    switch (ctx->mirror_state) {
+    case VA_MIRROR_HORIZONTAL:
+    case VA_MIRROR_VERTICAL:
+    case VA_MIRROR_NONE:
+        params.mirror_state = ctx->mirror_state;
+        break;
+    default:
+        av_log(avctx, AV_LOG_ERROR, "VAAPI doesn't support mirroring %d\n",
+               ctx->mirror_state);
+        goto fail;
+    }
+
+    if (vpp_ctx->nb_filter_buffers) {
+        params.filters     = &vpp_ctx->filter_buffers[0];
+        params.num_filters = vpp_ctx->nb_filter_buffers;
+    }
+
+    params.surface = input_surface;
+    params.surface_region = &input_region;
+    params.surface_color_standard =
+        ff_vaapi_vpp_colour_standard(input_frame->colorspace);
+
+    params.output_region = &output_region;
+    params.output_background_color = 0xff000000;
+    params.output_color_standard = params.surface_color_standard;
+
+    params.pipeline_flags = 0;
+    params.filter_flags = VA_FRAME_PICTURE;
+
+    err = ff_vaapi_vpp_render_picture(avctx, &params, output_surface);
+    if (err < 0)
+        goto fail;
+
+    err = av_frame_copy_props(output_frame, input_frame);
+    if (err < 0)
+        goto fail;
+    av_frame_free(&input_frame);
+
+    av_log(avctx, AV_LOG_DEBUG, "Filter output: %s, %ux%u (%"PRId64").\n",
+           av_get_pix_fmt_name(output_frame->format),
+           output_frame->width, output_frame->height, output_frame->pts);
+
+    return ff_filter_frame(outlink, output_frame);
+
+fail:
+    av_frame_free(&input_frame);
+    av_frame_free(&output_frame);
+    return err;
+}
+
+static av_cold int transpose_vaapi_init(AVFilterContext *avctx)
+{
+    VAAPIVPPContext *vpp_ctx = avctx->priv;
+
+    ff_vaapi_vpp_ctx_init(avctx);
+    vpp_ctx->pipeline_uninit     = ff_vaapi_vpp_pipeline_uninit;
+    vpp_ctx->build_filter_params = transpose_vaapi_build_filter_params;
+    vpp_ctx->output_format       = AV_PIX_FMT_NONE;
+
+    return 0;
+}
+
+static void transpose_vaapi_vpp_ctx_uninit(AVFilterContext *avctx)
+{
+    return ff_vaapi_vpp_ctx_uninit(avctx);
+}
+
+static int transpose_vaapi_vpp_query_formats(AVFilterContext *avctx)
+{
+    return ff_vaapi_vpp_query_formats(avctx);
+}
+
+static int transpose_vaapi_vpp_config_input(AVFilterLink *inlink)
+{
+    return ff_vaapi_vpp_config_input(inlink);
+}
+
+static int transpose_vaapi_vpp_config_output(AVFilterLink *outlink)
+{
+    AVFilterContext *avctx     = outlink->src;
+    VAAPIVPPContext *vpp_ctx   = avctx->priv;
+    TransposeVAAPIContext *ctx = avctx->priv;
+    AVFilterLink *inlink       = avctx->inputs[0];
+
+    if ((inlink->w >= inlink->h && ctx->passthrough == TRANSPOSE_PT_TYPE_LANDSCAPE) ||
+        (inlink->w <= inlink->h && ctx->passthrough == TRANSPOSE_PT_TYPE_PORTRAIT)) {
+        av_log(avctx, AV_LOG_VERBOSE,
+               "w:%d h:%d -> w:%d h:%d (passthrough mode)\n",
+               inlink->w, inlink->h, inlink->w, inlink->h);
+        return ff_vaapi_vpp_config_output(outlink);
+    }
+
+    ctx->passthrough = TRANSPOSE_PT_TYPE_NONE;
+
+    switch (ctx->dir) {
+    case TRANSPOSE_CCLOCK_FLIP:
+    case TRANSPOSE_CCLOCK:
+    case TRANSPOSE_CLOCK:
+    case TRANSPOSE_CLOCK_FLIP:
+        vpp_ctx->output_width  = avctx->inputs[0]->h;
+        vpp_ctx->output_height = avctx->inputs[0]->w;
+        av_log(avctx, AV_LOG_DEBUG, "swap width and height for clock/cclock rotation\n");
+        break;
+    default:
+        break;
+    }
+
+    return ff_vaapi_vpp_config_output(outlink);
+}
+
+static AVFrame *get_video_buffer(AVFilterLink *inlink, int w, int h)
+{
+    TransposeVAAPIContext *ctx = inlink->dst->priv;
+
+    return ctx->passthrough ?
+        ff_null_get_video_buffer(inlink, w, h) :
+        ff_default_get_video_buffer(inlink, w, h);
+}
+
+#define OFFSET(x) offsetof(TransposeVAAPIContext, x)
+#define FLAGS (AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_FILTERING_PARAM)
+static const AVOption transpose_vaapi_options[] = {
+    { "dir", "set transpose direction", OFFSET(dir), AV_OPT_TYPE_INT, { .i64 = TRANSPOSE_CCLOCK_FLIP }, 0, 5, FLAGS, "dir" },
+        { "cclock_flip", "rotate counter-clockwise with vertical flip",     0, AV_OPT_TYPE_CONST, { .i64 = TRANSPOSE_CCLOCK_FLIP }, .flags=FLAGS, .unit = "dir" },
+        { "clock",       "rotate clockwise",                                0, AV_OPT_TYPE_CONST, { .i64 = TRANSPOSE_CLOCK       }, .flags=FLAGS, .unit = "dir" },
+        { "cclock",      "rotate counter-clockwise",                        0, AV_OPT_TYPE_CONST, { .i64 = TRANSPOSE_CCLOCK      }, .flags=FLAGS, .unit = "dir" },
+        { "clock_flip",  "rotate clockwise with vertical flip",             0, AV_OPT_TYPE_CONST, { .i64 = TRANSPOSE_CLOCK_FLIP  }, .flags=FLAGS, .unit = "dir" },
+        { "180",         "rotate clockwise 180 degrees",                    0, AV_OPT_TYPE_CONST, { .i64 = TRANSPOSE_180         }, .flags=FLAGS, .unit = "dir" },
+        { "180_flip",    "rotate clockwise 180 degrees with vertical flip", 0, AV_OPT_TYPE_CONST, { .i64 = TRANSPOSE_180_FLIP    }, .flags=FLAGS, .unit = "dir" },
+
+    { "passthrough", "do not apply transposition if the input matches the specified geometry",
+      OFFSET(passthrough), AV_OPT_TYPE_INT, {.i64=TRANSPOSE_PT_TYPE_NONE},  0, INT_MAX, FLAGS, "passthrough" },
+        { "none",      "always apply transposition",   0, AV_OPT_TYPE_CONST, {.i64=TRANSPOSE_PT_TYPE_NONE},      INT_MIN, INT_MAX, FLAGS, "passthrough" },
+        { "portrait",  "preserve portrait geometry",   0, AV_OPT_TYPE_CONST, {.i64=TRANSPOSE_PT_TYPE_PORTRAIT},  INT_MIN, INT_MAX, FLAGS, "passthrough" },
+        { "landscape", "preserve landscape geometry",  0, AV_OPT_TYPE_CONST, {.i64=TRANSPOSE_PT_TYPE_LANDSCAPE}, INT_MIN, INT_MAX, FLAGS, "passthrough" },
+
+    { NULL }
+};
+
+
+AVFILTER_DEFINE_CLASS(transpose_vaapi);
+
+static const AVFilterPad transpose_vaapi_inputs[] = {
+    {
+        .name         = "default",
+        .type         = AVMEDIA_TYPE_VIDEO,
+        .filter_frame = &transpose_vaapi_filter_frame,
+        .get_video_buffer = get_video_buffer,
+        .config_props = &transpose_vaapi_vpp_config_input,
+    },
+    { NULL }
+};
+
+static const AVFilterPad transpose_vaapi_outputs[] = {
+    {
+        .name = "default",
+        .type = AVMEDIA_TYPE_VIDEO,
+        .config_props = &transpose_vaapi_vpp_config_output,
+    },
+    { NULL }
+};
+
+AVFilter ff_vf_transpose_vaapi = {
+    .name           = "transpose_vaapi",
+    .description    = NULL_IF_CONFIG_SMALL("VAAPI VPP for transpose"),
+    .priv_size      = sizeof(TransposeVAAPIContext),
+    .init           = &transpose_vaapi_init,
+    .uninit         = &transpose_vaapi_vpp_ctx_uninit,
+    .query_formats  = &transpose_vaapi_vpp_query_formats,
+    .inputs         = transpose_vaapi_inputs,
+    .outputs        = transpose_vaapi_outputs,
+    .priv_class     = &transpose_vaapi_class,
+    .flags_internal = FF_FILTER_FLAG_HWFRAME_AWARE,
+};