FFmpeg coverage


Directory: ../../../ffmpeg/
File: src/libavfilter/vf_transpose_vaapi.c
Date: 2024-04-26 14:42:52
Exec Total Coverage
Lines: 0 125 0.0%
Functions: 0 5 0.0%
Branches: 0 46 0.0%

Line Branch Exec Source
1 /*
2 * This file is part of FFmpeg.
3 *
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.
8 *
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.
13 *
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
17 */
18 #include <string.h>
19
20 #include "libavutil/opt.h"
21 #include "libavutil/pixdesc.h"
22
23 #include "avfilter.h"
24 #include "internal.h"
25 #include "transpose.h"
26 #include "vaapi_vpp.h"
27 #include "video.h"
28
29 typedef struct TransposeVAAPIContext {
30 VAAPIVPPContext vpp_ctx; // must be the first field
31 int passthrough; // PassthroughType, landscape passthrough mode enabled
32 int dir; // TransposeDir
33
34 int rotation_state;
35 int mirror_state;
36 } TransposeVAAPIContext;
37
38 static int transpose_vaapi_build_filter_params(AVFilterContext *avctx)
39 {
40 VAAPIVPPContext *vpp_ctx = avctx->priv;
41 TransposeVAAPIContext *ctx = avctx->priv;
42 VAStatus vas;
43 int support_flag;
44 VAProcPipelineCaps pipeline_caps;
45
46 memset(&pipeline_caps, 0, sizeof(pipeline_caps));
47 vas = vaQueryVideoProcPipelineCaps(vpp_ctx->hwctx->display,
48 vpp_ctx->va_context,
49 NULL, 0,
50 &pipeline_caps);
51 if (vas != VA_STATUS_SUCCESS) {
52 av_log(avctx, AV_LOG_ERROR, "Failed to query pipeline "
53 "caps: %d (%s).\n", vas, vaErrorStr(vas));
54 return AVERROR(EIO);
55 }
56
57 if (!pipeline_caps.rotation_flags) {
58 av_log(avctx, AV_LOG_ERROR, "VAAPI driver doesn't support transpose\n");
59 return AVERROR(EINVAL);
60 }
61
62 switch (ctx->dir) {
63 case TRANSPOSE_CCLOCK_FLIP:
64 ctx->rotation_state = VA_ROTATION_270;
65 ctx->mirror_state = VA_MIRROR_VERTICAL;
66 break;
67 case TRANSPOSE_CLOCK:
68 ctx->rotation_state = VA_ROTATION_90;
69 ctx->mirror_state = VA_MIRROR_NONE;
70 break;
71 case TRANSPOSE_CCLOCK:
72 ctx->rotation_state = VA_ROTATION_270;
73 ctx->mirror_state = VA_MIRROR_NONE;
74 break;
75 case TRANSPOSE_CLOCK_FLIP:
76 ctx->rotation_state = VA_ROTATION_90;
77 ctx->mirror_state = VA_MIRROR_VERTICAL;
78 break;
79 case TRANSPOSE_REVERSAL:
80 ctx->rotation_state = VA_ROTATION_180;
81 ctx->mirror_state = VA_MIRROR_NONE;
82 break;
83 case TRANSPOSE_HFLIP:
84 ctx->rotation_state = VA_ROTATION_NONE;
85 ctx->mirror_state = VA_MIRROR_HORIZONTAL;
86 break;
87 case TRANSPOSE_VFLIP:
88 ctx->rotation_state = VA_ROTATION_NONE;
89 ctx->mirror_state = VA_MIRROR_VERTICAL;
90 break;
91 default:
92 av_log(avctx, AV_LOG_ERROR, "Failed to set direction to %d\n", ctx->dir);
93 return AVERROR(EINVAL);
94 }
95
96 if (VA_ROTATION_NONE != ctx->rotation_state) {
97 support_flag = pipeline_caps.rotation_flags & (1 << ctx->rotation_state);
98 if (!support_flag) {
99 av_log(avctx, AV_LOG_ERROR, "VAAPI driver doesn't support rotation %d\n",
100 ctx->rotation_state);
101 return AVERROR(EINVAL);
102 }
103 }
104
105 if (VA_MIRROR_NONE != ctx->mirror_state) {
106 support_flag = pipeline_caps.mirror_flags & ctx->mirror_state;
107 if (!support_flag) {
108 av_log(avctx, AV_LOG_ERROR, "VAAPI driver doesn't support mirror %d\n",
109 ctx->mirror_state);
110 return AVERROR(EINVAL);
111 }
112 }
113
114 return 0;
115 }
116
117 static int transpose_vaapi_filter_frame(AVFilterLink *inlink, AVFrame *input_frame)
118 {
119 AVFilterContext *avctx = inlink->dst;
120 AVFilterLink *outlink = avctx->outputs[0];
121 VAAPIVPPContext *vpp_ctx = avctx->priv;
122 TransposeVAAPIContext *ctx = avctx->priv;
123 AVFrame *output_frame = NULL;
124 VAProcPipelineParameterBuffer params;
125 int err;
126
127 if (ctx->passthrough)
128 return ff_filter_frame(outlink, input_frame);
129
130 av_log(avctx, AV_LOG_DEBUG, "Filter input: %s, %ux%u (%"PRId64").\n",
131 av_get_pix_fmt_name(input_frame->format),
132 input_frame->width, input_frame->height, input_frame->pts);
133
134 if (vpp_ctx->va_context == VA_INVALID_ID)
135 return AVERROR(EINVAL);
136
137 output_frame = ff_get_video_buffer(outlink, vpp_ctx->output_width,
138 vpp_ctx->output_height);
139 if (!output_frame) {
140 err = AVERROR(ENOMEM);
141 goto fail;
142 }
143
144 err = av_frame_copy_props(output_frame, input_frame);
145 if (err < 0)
146 goto fail;
147
148 err = ff_vaapi_vpp_init_params(avctx, &params,
149 input_frame, output_frame);
150 if (err < 0)
151 goto fail;
152
153 params.rotation_state = ctx->rotation_state;
154 params.mirror_state = ctx->mirror_state;
155
156 err = ff_vaapi_vpp_render_picture(avctx, &params, output_frame);
157 if (err < 0)
158 goto fail;
159
160 av_frame_free(&input_frame);
161
162 av_log(avctx, AV_LOG_DEBUG, "Filter output: %s, %ux%u (%"PRId64").\n",
163 av_get_pix_fmt_name(output_frame->format),
164 output_frame->width, output_frame->height, output_frame->pts);
165
166 return ff_filter_frame(outlink, output_frame);
167
168 fail:
169 av_frame_free(&input_frame);
170 av_frame_free(&output_frame);
171 return err;
172 }
173
174 static av_cold int transpose_vaapi_init(AVFilterContext *avctx)
175 {
176 VAAPIVPPContext *vpp_ctx = avctx->priv;
177
178 ff_vaapi_vpp_ctx_init(avctx);
179 vpp_ctx->pipeline_uninit = ff_vaapi_vpp_pipeline_uninit;
180 vpp_ctx->build_filter_params = transpose_vaapi_build_filter_params;
181 vpp_ctx->output_format = AV_PIX_FMT_NONE;
182
183 return 0;
184 }
185
186 static int transpose_vaapi_vpp_config_output(AVFilterLink *outlink)
187 {
188 AVFilterContext *avctx = outlink->src;
189 VAAPIVPPContext *vpp_ctx = avctx->priv;
190 TransposeVAAPIContext *ctx = avctx->priv;
191 AVFilterLink *inlink = avctx->inputs[0];
192
193 if ((inlink->w >= inlink->h && ctx->passthrough == TRANSPOSE_PT_TYPE_LANDSCAPE) ||
194 (inlink->w <= inlink->h && ctx->passthrough == TRANSPOSE_PT_TYPE_PORTRAIT)) {
195 outlink->hw_frames_ctx = av_buffer_ref(inlink->hw_frames_ctx);
196 if (!outlink->hw_frames_ctx)
197 return AVERROR(ENOMEM);
198 av_log(avctx, AV_LOG_VERBOSE,
199 "w:%d h:%d -> w:%d h:%d (passthrough mode)\n",
200 inlink->w, inlink->h, inlink->w, inlink->h);
201 return 0;
202 }
203
204 ctx->passthrough = TRANSPOSE_PT_TYPE_NONE;
205
206 switch (ctx->dir) {
207 case TRANSPOSE_CCLOCK_FLIP:
208 case TRANSPOSE_CCLOCK:
209 case TRANSPOSE_CLOCK:
210 case TRANSPOSE_CLOCK_FLIP:
211 vpp_ctx->output_width = avctx->inputs[0]->h;
212 vpp_ctx->output_height = avctx->inputs[0]->w;
213 av_log(avctx, AV_LOG_DEBUG, "swap width and height for clock/cclock rotation\n");
214 break;
215 default:
216 break;
217 }
218
219 return ff_vaapi_vpp_config_output(outlink);
220 }
221
222 static AVFrame *get_video_buffer(AVFilterLink *inlink, int w, int h)
223 {
224 TransposeVAAPIContext *ctx = inlink->dst->priv;
225
226 return ctx->passthrough ?
227 ff_null_get_video_buffer(inlink, w, h) :
228 ff_default_get_video_buffer(inlink, w, h);
229 }
230
231 #define OFFSET(x) offsetof(TransposeVAAPIContext, x)
232 #define FLAGS (AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_FILTERING_PARAM)
233 static const AVOption transpose_vaapi_options[] = {
234 { "dir", "set transpose direction", OFFSET(dir), AV_OPT_TYPE_INT, { .i64 = TRANSPOSE_CCLOCK_FLIP }, 0, 6, FLAGS, .unit = "dir" },
235 { "cclock_flip", "rotate counter-clockwise with vertical flip", 0, AV_OPT_TYPE_CONST, { .i64 = TRANSPOSE_CCLOCK_FLIP }, .flags=FLAGS, .unit = "dir" },
236 { "clock", "rotate clockwise", 0, AV_OPT_TYPE_CONST, { .i64 = TRANSPOSE_CLOCK }, .flags=FLAGS, .unit = "dir" },
237 { "cclock", "rotate counter-clockwise", 0, AV_OPT_TYPE_CONST, { .i64 = TRANSPOSE_CCLOCK }, .flags=FLAGS, .unit = "dir" },
238 { "clock_flip", "rotate clockwise with vertical flip", 0, AV_OPT_TYPE_CONST, { .i64 = TRANSPOSE_CLOCK_FLIP }, .flags=FLAGS, .unit = "dir" },
239 { "reversal", "rotate by half-turn", 0, AV_OPT_TYPE_CONST, { .i64 = TRANSPOSE_REVERSAL }, .flags=FLAGS, .unit = "dir" },
240 { "hflip", "flip horizontally", 0, AV_OPT_TYPE_CONST, { .i64 = TRANSPOSE_HFLIP }, .flags=FLAGS, .unit = "dir" },
241 { "vflip", "flip vertically", 0, AV_OPT_TYPE_CONST, { .i64 = TRANSPOSE_VFLIP }, .flags=FLAGS, .unit = "dir" },
242
243 { "passthrough", "do not apply transposition if the input matches the specified geometry",
244 OFFSET(passthrough), AV_OPT_TYPE_INT, {.i64=TRANSPOSE_PT_TYPE_NONE}, 0, INT_MAX, FLAGS, .unit = "passthrough" },
245 { "none", "always apply transposition", 0, AV_OPT_TYPE_CONST, {.i64=TRANSPOSE_PT_TYPE_NONE}, INT_MIN, INT_MAX, FLAGS, .unit = "passthrough" },
246 { "portrait", "preserve portrait geometry", 0, AV_OPT_TYPE_CONST, {.i64=TRANSPOSE_PT_TYPE_PORTRAIT}, INT_MIN, INT_MAX, FLAGS, .unit = "passthrough" },
247 { "landscape", "preserve landscape geometry", 0, AV_OPT_TYPE_CONST, {.i64=TRANSPOSE_PT_TYPE_LANDSCAPE}, INT_MIN, INT_MAX, FLAGS, .unit = "passthrough" },
248
249 { NULL }
250 };
251
252
253 AVFILTER_DEFINE_CLASS(transpose_vaapi);
254
255 static const AVFilterPad transpose_vaapi_inputs[] = {
256 {
257 .name = "default",
258 .type = AVMEDIA_TYPE_VIDEO,
259 .filter_frame = &transpose_vaapi_filter_frame,
260 .get_buffer.video = get_video_buffer,
261 .config_props = &ff_vaapi_vpp_config_input,
262 },
263 };
264
265 static const AVFilterPad transpose_vaapi_outputs[] = {
266 {
267 .name = "default",
268 .type = AVMEDIA_TYPE_VIDEO,
269 .config_props = &transpose_vaapi_vpp_config_output,
270 },
271 };
272
273 const AVFilter ff_vf_transpose_vaapi = {
274 .name = "transpose_vaapi",
275 .description = NULL_IF_CONFIG_SMALL("VAAPI VPP for transpose"),
276 .priv_size = sizeof(TransposeVAAPIContext),
277 .init = &transpose_vaapi_init,
278 .uninit = &ff_vaapi_vpp_ctx_uninit,
279 FILTER_INPUTS(transpose_vaapi_inputs),
280 FILTER_OUTPUTS(transpose_vaapi_outputs),
281 FILTER_QUERY_FUNC(&ff_vaapi_vpp_query_formats),
282 .priv_class = &transpose_vaapi_class,
283 .flags_internal = FF_FILTER_FLAG_HWFRAME_AWARE,
284 };
285