2 * This file is part of FFmpeg.
4 * FFmpeg is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2.1 of the License, or (at your option) any later version.
9 * FFmpeg is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with FFmpeg; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
21 #include "libavutil/opt.h"
22 #include "libavutil/pixdesc.h"
26 #include "scale_eval.h"
28 #include "vaapi_vpp.h"
30 typedef struct ScaleVAAPIContext
{
31 VAAPIVPPContext vpp_ctx
; // must be the first field
33 char *output_format_string
;
37 char *w_expr
; // width expression string
38 char *h_expr
; // height expression string
40 int force_original_aspect_ratio
;
41 int force_divisible_by
;
43 char *colour_primaries_string
;
44 char *colour_transfer_string
;
45 char *colour_matrix_string
;
47 char *chroma_location_string
;
49 enum AVColorPrimaries colour_primaries
;
50 enum AVColorTransferCharacteristic colour_transfer
;
51 enum AVColorSpace colour_matrix
;
52 enum AVChromaLocation chroma_location
;
55 static const char *scale_vaapi_mode_name(int mode
)
58 #define D(name) case VA_FILTER_SCALING_ ## name: return #name
70 static int scale_vaapi_config_output(AVFilterLink
*outlink
)
72 AVFilterLink
*inlink
= outlink
->src
->inputs
[0];
73 AVFilterContext
*avctx
= outlink
->src
;
74 VAAPIVPPContext
*vpp_ctx
= avctx
->priv
;
75 ScaleVAAPIContext
*ctx
= avctx
->priv
;
78 if ((err
= ff_scale_eval_dimensions(ctx
,
79 ctx
->w_expr
, ctx
->h_expr
,
81 &vpp_ctx
->output_width
, &vpp_ctx
->output_height
)) < 0)
84 ff_scale_adjust_dimensions(inlink
, &vpp_ctx
->output_width
, &vpp_ctx
->output_height
,
85 ctx
->force_original_aspect_ratio
, ctx
->force_divisible_by
);
87 if (inlink
->w
== vpp_ctx
->output_width
&& inlink
->h
== vpp_ctx
->output_height
&&
88 (vpp_ctx
->input_frames
->sw_format
== vpp_ctx
->output_format
||
89 vpp_ctx
->output_format
== AV_PIX_FMT_NONE
) &&
90 ctx
->colour_primaries
== AVCOL_PRI_UNSPECIFIED
&&
91 ctx
->colour_transfer
== AVCOL_TRC_UNSPECIFIED
&&
92 ctx
->colour_matrix
== AVCOL_SPC_UNSPECIFIED
&&
93 ctx
->colour_range
== AVCOL_RANGE_UNSPECIFIED
&&
94 ctx
->chroma_location
== AVCHROMA_LOC_UNSPECIFIED
)
95 vpp_ctx
->passthrough
= 1;
97 err
= ff_vaapi_vpp_config_output(outlink
);
101 if (inlink
->sample_aspect_ratio
.num
)
102 outlink
->sample_aspect_ratio
= av_mul_q((AVRational
){outlink
->h
* inlink
->w
, outlink
->w
* inlink
->h
}, inlink
->sample_aspect_ratio
);
104 outlink
->sample_aspect_ratio
= inlink
->sample_aspect_ratio
;
109 static int scale_vaapi_filter_frame(AVFilterLink
*inlink
, AVFrame
*input_frame
)
111 AVFilterContext
*avctx
= inlink
->dst
;
112 AVFilterLink
*outlink
= avctx
->outputs
[0];
113 VAAPIVPPContext
*vpp_ctx
= avctx
->priv
;
114 ScaleVAAPIContext
*ctx
= avctx
->priv
;
115 AVFrame
*output_frame
= NULL
;
116 VAProcPipelineParameterBuffer params
;
119 av_log(avctx
, AV_LOG_DEBUG
, "Filter input: %s, %ux%u (%"PRId64
").\n",
120 av_get_pix_fmt_name(input_frame
->format
),
121 input_frame
->width
, input_frame
->height
, input_frame
->pts
);
123 if (vpp_ctx
->passthrough
)
124 return ff_filter_frame(outlink
, input_frame
);
126 if (vpp_ctx
->va_context
== VA_INVALID_ID
)
127 return AVERROR(EINVAL
);
129 output_frame
= ff_get_video_buffer(outlink
, vpp_ctx
->output_width
,
130 vpp_ctx
->output_height
);
132 err
= AVERROR(ENOMEM
);
136 err
= av_frame_copy_props(output_frame
, input_frame
);
140 if (output_frame
->width
!= input_frame
->width
|| output_frame
->height
!= input_frame
->height
) {
141 av_frame_side_data_remove_by_props(&output_frame
->side_data
,
142 &output_frame
->nb_side_data
,
143 AV_SIDE_DATA_PROP_SIZE_DEPENDENT
);
146 if (ctx
->colour_primaries
!= AVCOL_PRI_UNSPECIFIED
)
147 output_frame
->color_primaries
= ctx
->colour_primaries
;
148 if (ctx
->colour_transfer
!= AVCOL_TRC_UNSPECIFIED
)
149 output_frame
->color_trc
= ctx
->colour_transfer
;
150 if (ctx
->colour_matrix
!= AVCOL_SPC_UNSPECIFIED
)
151 output_frame
->colorspace
= ctx
->colour_matrix
;
152 if (ctx
->colour_range
!= AVCOL_RANGE_UNSPECIFIED
)
153 output_frame
->color_range
= ctx
->colour_range
;
154 if (ctx
->chroma_location
!= AVCHROMA_LOC_UNSPECIFIED
)
155 output_frame
->chroma_location
= ctx
->chroma_location
;
157 err
= ff_vaapi_vpp_init_params(avctx
, ¶ms
,
158 input_frame
, output_frame
);
162 params
.filter_flags
|= ctx
->mode
;
164 err
= ff_vaapi_vpp_render_picture(avctx
, ¶ms
, output_frame
);
168 av_frame_free(&input_frame
);
170 av_log(avctx
, AV_LOG_DEBUG
, "Filter output: %s, %ux%u (%"PRId64
"), mode: %s.\n",
171 av_get_pix_fmt_name(output_frame
->format
),
172 output_frame
->width
, output_frame
->height
, output_frame
->pts
,
173 scale_vaapi_mode_name(ctx
->mode
));
175 return ff_filter_frame(outlink
, output_frame
);
178 av_frame_free(&input_frame
);
179 av_frame_free(&output_frame
);
183 static av_cold
int scale_vaapi_init(AVFilterContext
*avctx
)
185 VAAPIVPPContext
*vpp_ctx
= avctx
->priv
;
186 ScaleVAAPIContext
*ctx
= avctx
->priv
;
188 ff_vaapi_vpp_ctx_init(avctx
);
189 vpp_ctx
->pipeline_uninit
= ff_vaapi_vpp_pipeline_uninit
;
191 if (ctx
->output_format_string
) {
192 vpp_ctx
->output_format
= av_get_pix_fmt(ctx
->output_format_string
);
193 if (vpp_ctx
->output_format
== AV_PIX_FMT_NONE
) {
194 av_log(avctx
, AV_LOG_ERROR
, "Invalid output format.\n");
195 return AVERROR(EINVAL
);
198 // Use the input format once that is configured.
199 vpp_ctx
->output_format
= AV_PIX_FMT_NONE
;
202 #define STRING_OPTION(var_name, func_name, default_value) do { \
203 if (ctx->var_name ## _string) { \
204 int var = av_ ## func_name ## _from_name(ctx->var_name ## _string); \
206 av_log(avctx, AV_LOG_ERROR, "Invalid %s.\n", #var_name); \
207 return AVERROR(EINVAL); \
209 ctx->var_name = var; \
211 ctx->var_name = default_value; \
215 STRING_OPTION(colour_primaries
, color_primaries
, AVCOL_PRI_UNSPECIFIED
);
216 STRING_OPTION(colour_transfer
, color_transfer
, AVCOL_TRC_UNSPECIFIED
);
217 STRING_OPTION(colour_matrix
, color_space
, AVCOL_SPC_UNSPECIFIED
);
218 STRING_OPTION(chroma_location
, chroma_location
, AVCHROMA_LOC_UNSPECIFIED
);
223 #define OFFSET(x) offsetof(ScaleVAAPIContext, x)
224 #define FLAGS (AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM)
225 static const AVOption scale_vaapi_options
[] = {
226 { "w", "Output video width",
227 OFFSET(w_expr
), AV_OPT_TYPE_STRING
, {.str
= "iw"}, .flags
= FLAGS
},
228 { "h", "Output video height",
229 OFFSET(h_expr
), AV_OPT_TYPE_STRING
, {.str
= "ih"}, .flags
= FLAGS
},
230 { "format", "Output video format (software format of hardware frames)",
231 OFFSET(output_format_string
), AV_OPT_TYPE_STRING
, .flags
= FLAGS
},
232 { "mode", "Scaling mode",
233 OFFSET(mode
), AV_OPT_TYPE_INT
, { .i64
= VA_FILTER_SCALING_HQ
},
234 0, VA_FILTER_SCALING_NL_ANAMORPHIC
, FLAGS
, .unit
= "mode" },
235 { "default", "Use the default (depend on the driver) scaling algorithm",
236 0, AV_OPT_TYPE_CONST
, { .i64
= VA_FILTER_SCALING_DEFAULT
}, 0, 0, FLAGS
, .unit
= "mode" },
237 { "fast", "Use fast scaling algorithm",
238 0, AV_OPT_TYPE_CONST
, { .i64
= VA_FILTER_SCALING_FAST
}, 0, 0, FLAGS
, .unit
= "mode" },
239 { "hq", "Use high quality scaling algorithm",
240 0, AV_OPT_TYPE_CONST
, { .i64
= VA_FILTER_SCALING_HQ
}, 0, 0, FLAGS
, .unit
= "mode" },
241 { "nl_anamorphic", "Use nolinear anamorphic scaling algorithm",
242 0, AV_OPT_TYPE_CONST
, { .i64
= VA_FILTER_SCALING_NL_ANAMORPHIC
}, 0, 0, FLAGS
, .unit
= "mode" },
244 // These colour properties match the ones of the same name in vf_scale.
245 { "out_color_matrix", "Output colour matrix coefficient set",
246 OFFSET(colour_matrix_string
), AV_OPT_TYPE_STRING
, { .str
= NULL
}, .flags
= FLAGS
},
247 { "out_range", "Output colour range",
248 OFFSET(colour_range
), AV_OPT_TYPE_INT
, { .i64
= AVCOL_RANGE_UNSPECIFIED
},
249 AVCOL_RANGE_UNSPECIFIED
, AVCOL_RANGE_JPEG
, FLAGS
, .unit
= "range" },
250 { "full", "Full range",
251 0, AV_OPT_TYPE_CONST
, { .i64
= AVCOL_RANGE_JPEG
}, 0, 0, FLAGS
, .unit
= "range" },
252 { "limited", "Limited range",
253 0, AV_OPT_TYPE_CONST
, { .i64
= AVCOL_RANGE_MPEG
}, 0, 0, FLAGS
, .unit
= "range" },
254 { "jpeg", "Full range",
255 0, AV_OPT_TYPE_CONST
, { .i64
= AVCOL_RANGE_JPEG
}, 0, 0, FLAGS
, .unit
= "range" },
256 { "mpeg", "Limited range",
257 0, AV_OPT_TYPE_CONST
, { .i64
= AVCOL_RANGE_MPEG
}, 0, 0, FLAGS
, .unit
= "range" },
258 { "tv", "Limited range",
259 0, AV_OPT_TYPE_CONST
, { .i64
= AVCOL_RANGE_MPEG
}, 0, 0, FLAGS
, .unit
= "range" },
260 { "pc", "Full range",
261 0, AV_OPT_TYPE_CONST
, { .i64
= AVCOL_RANGE_JPEG
}, 0, 0, FLAGS
, .unit
= "range" },
262 // These colour properties are new here.
263 { "out_color_primaries", "Output colour primaries",
264 OFFSET(colour_primaries_string
), AV_OPT_TYPE_STRING
,
265 { .str
= NULL
}, .flags
= FLAGS
},
266 { "out_color_transfer", "Output colour transfer characteristics",
267 OFFSET(colour_transfer_string
), AV_OPT_TYPE_STRING
,
268 { .str
= NULL
}, .flags
= FLAGS
},
269 { "out_chroma_location", "Output chroma sample location",
270 OFFSET(chroma_location_string
), AV_OPT_TYPE_STRING
,
271 { .str
= NULL
}, .flags
= FLAGS
},
272 { "force_original_aspect_ratio", "decrease or increase w/h if necessary to keep the original AR", OFFSET(force_original_aspect_ratio
), AV_OPT_TYPE_INT
, { .i64
= 0}, 0, 2, FLAGS
, .unit
= "force_oar" },
273 { "disable", NULL
, 0, AV_OPT_TYPE_CONST
, {.i64
= 0 }, 0, 0, FLAGS
, .unit
= "force_oar" },
274 { "decrease", NULL
, 0, AV_OPT_TYPE_CONST
, {.i64
= 1 }, 0, 0, FLAGS
, .unit
= "force_oar" },
275 { "increase", NULL
, 0, AV_OPT_TYPE_CONST
, {.i64
= 2 }, 0, 0, FLAGS
, .unit
= "force_oar" },
276 { "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
},
281 AVFILTER_DEFINE_CLASS(scale_vaapi
);
283 static const AVFilterPad scale_vaapi_inputs
[] = {
286 .type
= AVMEDIA_TYPE_VIDEO
,
287 .filter_frame
= &scale_vaapi_filter_frame
,
288 .config_props
= &ff_vaapi_vpp_config_input
,
292 static const AVFilterPad scale_vaapi_outputs
[] = {
295 .type
= AVMEDIA_TYPE_VIDEO
,
296 .config_props
= &scale_vaapi_config_output
,
300 const FFFilter ff_vf_scale_vaapi
= {
301 .p
.name
= "scale_vaapi",
302 .p
.description
= NULL_IF_CONFIG_SMALL("Scale to/from VAAPI surfaces."),
303 .p
.priv_class
= &scale_vaapi_class
,
304 .priv_size
= sizeof(ScaleVAAPIContext
),
305 .init
= &scale_vaapi_init
,
306 .uninit
= &ff_vaapi_vpp_ctx_uninit
,
307 FILTER_INPUTS(scale_vaapi_inputs
),
308 FILTER_OUTPUTS(scale_vaapi_outputs
),
309 FILTER_QUERY_FUNC2(&ff_vaapi_vpp_query_formats
),
310 .flags_internal
= FF_FILTER_FLAG_HWFRAME_AWARE
,