libavfilter/vf_scale_vaapi.c
98114d70
 /*
34245ecc
  * This file is part of FFmpeg.
98114d70
  *
34245ecc
  * FFmpeg is free software; you can redistribute it and/or
98114d70
  * 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.
  *
34245ecc
  * FFmpeg is distributed in the hope that it will be useful,
98114d70
  * 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
34245ecc
  * License along with FFmpeg; if not, write to the Free Software
98114d70
  * 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"
037bb402
 #include "scale.h"
7e2561fa
 #include "video.h"
19214f00
 #include "vaapi_vpp.h"
98114d70
 
 typedef struct ScaleVAAPIContext {
4dbae00b
     VAAPIVPPContext vpp_ctx; // must be the first field
98114d70
 
     char *output_format_string;
 
037bb402
     char *w_expr;      // width expression string
     char *h_expr;      // height expression string
98114d70
 } ScaleVAAPIContext;
 
 static int scale_vaapi_config_output(AVFilterLink *outlink)
 {
19214f00
     AVFilterLink *inlink     = outlink->src->inputs[0];
     AVFilterContext *avctx   = outlink->src;
     VAAPIVPPContext *vpp_ctx = avctx->priv;
     ScaleVAAPIContext *ctx   = avctx->priv;
     int err;
98114d70
 
037bb402
     if ((err = ff_scale_eval_dimensions(ctx,
                                         ctx->w_expr, ctx->h_expr,
                                         inlink, outlink,
19214f00
                                         &vpp_ctx->output_width, &vpp_ctx->output_height)) < 0)
         return err;
98114d70
 
19214f00
     err = ff_vaapi_vpp_config_output(outlink);
     if (err < 0)
         return err;
98114d70
 
381a4820
     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;
 
98114d70
     return 0;
 }
 
 static int scale_vaapi_filter_frame(AVFilterLink *inlink, AVFrame *input_frame)
 {
19214f00
     AVFilterContext *avctx   = inlink->dst;
     AVFilterLink *outlink    = avctx->outputs[0];
     VAAPIVPPContext *vpp_ctx = avctx->priv;
     AVFrame *output_frame    = NULL;
98114d70
     VASurfaceID input_surface, output_surface;
     VAProcPipelineParameterBuffer params;
bdf7610e
     VARectangle input_region;
98114d70
     int err;
 
19214f00
     av_log(avctx, AV_LOG_DEBUG, "Filter input: %s, %ux%u (%"PRId64").\n",
98114d70
            av_get_pix_fmt_name(input_frame->format),
            input_frame->width, input_frame->height, input_frame->pts);
 
19214f00
     if (vpp_ctx->va_context == VA_INVALID_ID)
98114d70
         return AVERROR(EINVAL);
 
     input_surface = (VASurfaceID)(uintptr_t)input_frame->data[3];
19214f00
     av_log(avctx, AV_LOG_DEBUG, "Using surface %#x for scale input.\n",
98114d70
            input_surface);
 
19214f00
     output_frame = ff_get_video_buffer(outlink, vpp_ctx->output_width,
                                        vpp_ctx->output_height);
98114d70
     if (!output_frame) {
         err = AVERROR(ENOMEM);
         goto fail;
     }
 
     output_surface = (VASurfaceID)(uintptr_t)output_frame->data[3];
19214f00
     av_log(avctx, AV_LOG_DEBUG, "Using surface %#x for scale output.\n",
98114d70
            output_surface);
 
     memset(&params, 0, sizeof(params));
 
bdf7610e
     input_region = (VARectangle) {
abf35afb
         .x      = input_frame->crop_left,
         .y      = input_frame->crop_top,
         .width  = input_frame->width -
                  (input_frame->crop_left + input_frame->crop_right),
         .height = input_frame->height -
                  (input_frame->crop_top + input_frame->crop_bottom),
bdf7610e
     };
 
98114d70
     params.surface = input_surface;
bdf7610e
     params.surface_region = &input_region;
98114d70
     params.surface_color_standard =
19214f00
         ff_vaapi_vpp_colour_standard(input_frame->colorspace);
98114d70
 
     params.output_region = 0;
     params.output_background_color = 0xff000000;
     params.output_color_standard = params.surface_color_standard;
 
     params.pipeline_flags = 0;
     params.filter_flags = VA_FILTER_SCALING_HQ;
 
19214f00
     err = ff_vaapi_vpp_render_picture(avctx, &params, output_surface);
     if (err < 0)
98114d70
         goto fail;
 
19214f00
     err = av_frame_copy_props(output_frame, input_frame);
     if (err < 0)
         goto fail;
98114d70
 
     av_frame_free(&input_frame);
 
19214f00
     av_log(avctx, AV_LOG_DEBUG, "Filter output: %s, %ux%u (%"PRId64").\n",
98114d70
            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 scale_vaapi_init(AVFilterContext *avctx)
 {
19214f00
     VAAPIVPPContext *vpp_ctx = avctx->priv;
     ScaleVAAPIContext *ctx   = avctx->priv;
98114d70
 
19214f00
     ff_vaapi_vpp_ctx_init(avctx);
     vpp_ctx->pipeline_uninit = ff_vaapi_vpp_pipeline_uninit;
98114d70
 
     if (ctx->output_format_string) {
19214f00
         vpp_ctx->output_format = av_get_pix_fmt(ctx->output_format_string);
         if (vpp_ctx->output_format == AV_PIX_FMT_NONE) {
             av_log(avctx, AV_LOG_ERROR, "Invalid output format.\n");
98114d70
             return AVERROR(EINVAL);
         }
     } else {
         // Use the input format once that is configured.
19214f00
         vpp_ctx->output_format = AV_PIX_FMT_NONE;
98114d70
     }
 
     return 0;
 }
 
 #define OFFSET(x) offsetof(ScaleVAAPIContext, x)
7b7c338e
 #define FLAGS (AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM)
98114d70
 static const AVOption scale_vaapi_options[] = {
     { "w", "Output video width",
037bb402
       OFFSET(w_expr), AV_OPT_TYPE_STRING, {.str = "iw"}, .flags = FLAGS },
98114d70
     { "h", "Output video height",
037bb402
       OFFSET(h_expr), AV_OPT_TYPE_STRING, {.str = "ih"}, .flags = FLAGS },
98114d70
     { "format", "Output video format (software format of hardware frames)",
       OFFSET(output_format_string), AV_OPT_TYPE_STRING, .flags = FLAGS },
     { NULL },
 };
 
19214f00
 AVFILTER_DEFINE_CLASS(scale_vaapi);
98114d70
 
 static const AVFilterPad scale_vaapi_inputs[] = {
     {
         .name         = "default",
         .type         = AVMEDIA_TYPE_VIDEO,
         .filter_frame = &scale_vaapi_filter_frame,
19214f00
         .config_props = &ff_vaapi_vpp_config_input,
98114d70
     },
     { NULL }
 };
 
 static const AVFilterPad scale_vaapi_outputs[] = {
     {
         .name = "default",
         .type = AVMEDIA_TYPE_VIDEO,
         .config_props = &scale_vaapi_config_output,
     },
     { NULL }
 };
 
 AVFilter ff_vf_scale_vaapi = {
     .name          = "scale_vaapi",
     .description   = NULL_IF_CONFIG_SMALL("Scale to/from VAAPI surfaces."),
     .priv_size     = sizeof(ScaleVAAPIContext),
     .init          = &scale_vaapi_init,
19214f00
     .uninit        = &ff_vaapi_vpp_ctx_uninit,
     .query_formats = &ff_vaapi_vpp_query_formats,
98114d70
     .inputs        = scale_vaapi_inputs,
     .outputs       = scale_vaapi_outputs,
     .priv_class    = &scale_vaapi_class,
e3fb74f7
     .flags_internal = FF_FILTER_FLAG_HWFRAME_AWARE,
98114d70
 };