]> git.sesse.net Git - ffmpeg/blob - libavfilter/vf_overlay_opencl.c
libavfilter/opencl: Add macro for setting opencl kernel arguments
[ffmpeg] / libavfilter / vf_overlay_opencl.c
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
19 #include "libavutil/log.h"
20 #include "libavutil/mem.h"
21 #include "libavutil/opt.h"
22 #include "libavutil/pixdesc.h"
23
24 #include "avfilter.h"
25 #include "framesync.h"
26 #include "internal.h"
27 #include "opencl.h"
28 #include "opencl_source.h"
29 #include "video.h"
30
31 typedef struct OverlayOpenCLContext {
32     OpenCLFilterContext ocf;
33
34     int              initialised;
35     cl_kernel        kernel;
36     cl_command_queue command_queue;
37
38     FFFrameSync      fs;
39
40     int              nb_planes;
41     int              x_subsample;
42     int              y_subsample;
43     int              alpha_separate;
44
45     int              x_position;
46     int              y_position;
47 } OverlayOpenCLContext;
48
49 static int overlay_opencl_load(AVFilterContext *avctx,
50                                enum AVPixelFormat main_format,
51                                enum AVPixelFormat overlay_format)
52 {
53     OverlayOpenCLContext *ctx = avctx->priv;
54     cl_int cle;
55     const char *source = ff_opencl_source_overlay;
56     const char *kernel;
57     const AVPixFmtDescriptor *main_desc, *overlay_desc;
58     int err, i, main_planes, overlay_planes;
59
60     main_desc    = av_pix_fmt_desc_get(main_format);
61     overlay_desc = av_pix_fmt_desc_get(overlay_format);
62
63     main_planes = overlay_planes = 0;
64     for (i = 0; i < main_desc->nb_components; i++)
65         main_planes = FFMAX(main_planes,
66                             main_desc->comp[i].plane + 1);
67     for (i = 0; i < overlay_desc->nb_components; i++)
68         overlay_planes = FFMAX(overlay_planes,
69                                overlay_desc->comp[i].plane + 1);
70
71     ctx->nb_planes = main_planes;
72     ctx->x_subsample = 1 << main_desc->log2_chroma_w;
73     ctx->y_subsample = 1 << main_desc->log2_chroma_h;
74
75     if (ctx->x_position % ctx->x_subsample ||
76         ctx->y_position % ctx->y_subsample) {
77         av_log(avctx, AV_LOG_WARNING, "Warning: overlay position (%d, %d) "
78                "does not match subsampling (%d, %d).\n",
79                ctx->x_position, ctx->y_position,
80                ctx->x_subsample, ctx->y_subsample);
81     }
82
83     if (main_planes == overlay_planes) {
84         if (main_desc->nb_components == overlay_desc->nb_components)
85             kernel = "overlay_no_alpha";
86         else
87             kernel = "overlay_internal_alpha";
88         ctx->alpha_separate = 0;
89     } else {
90         kernel = "overlay_external_alpha";
91         ctx->alpha_separate = 1;
92     }
93
94     av_log(avctx, AV_LOG_DEBUG, "Using kernel %s.\n", kernel);
95
96     err = ff_opencl_filter_load_program(avctx, &source, 1);
97     if (err < 0)
98         goto fail;
99
100     ctx->command_queue = clCreateCommandQueue(ctx->ocf.hwctx->context,
101                                               ctx->ocf.hwctx->device_id,
102                                               0, &cle);
103     if (!ctx->command_queue) {
104         av_log(avctx, AV_LOG_ERROR, "Failed to create OpenCL "
105                "command queue: %d.\n", cle);
106         err = AVERROR(EIO);
107         goto fail;
108     }
109
110     ctx->kernel = clCreateKernel(ctx->ocf.program, kernel, &cle);
111     if (!ctx->kernel) {
112         av_log(avctx, AV_LOG_ERROR, "Failed to create kernel: %d.\n", cle);
113         err = AVERROR(EIO);
114         goto fail;
115     }
116
117     ctx->initialised = 1;
118     return 0;
119
120 fail:
121     if (ctx->command_queue)
122         clReleaseCommandQueue(ctx->command_queue);
123     if (ctx->kernel)
124         clReleaseKernel(ctx->kernel);
125     return err;
126 }
127
128 static int overlay_opencl_blend(FFFrameSync *fs)
129 {
130     AVFilterContext    *avctx = fs->parent;
131     AVFilterLink     *outlink = avctx->outputs[0];
132     OverlayOpenCLContext *ctx = avctx->priv;
133     AVFrame *input_main, *input_overlay;
134     AVFrame *output;
135     cl_mem mem;
136     cl_int cle, x, y;
137     size_t global_work[2];
138     int kernel_arg = 0;
139     int err, plane;
140
141     err = ff_framesync_get_frame(fs, 0, &input_main, 0);
142     if (err < 0)
143         return err;
144     err = ff_framesync_get_frame(fs, 1, &input_overlay, 0);
145     if (err < 0)
146         return err;
147
148     if (!ctx->initialised) {
149         AVHWFramesContext *main_fc =
150             (AVHWFramesContext*)input_main->hw_frames_ctx->data;
151         AVHWFramesContext *overlay_fc =
152             (AVHWFramesContext*)input_overlay->hw_frames_ctx->data;
153
154         err = overlay_opencl_load(avctx, main_fc->sw_format,
155                                   overlay_fc->sw_format);
156         if (err < 0)
157             return err;
158     }
159
160     output = ff_get_video_buffer(outlink, outlink->w, outlink->h);
161     if (!output) {
162         err = AVERROR(ENOMEM);
163         goto fail;
164     }
165
166     for (plane = 0; plane < ctx->nb_planes; plane++) {
167         kernel_arg = 0;
168
169         mem = (cl_mem)output->data[plane];
170         CL_SET_KERNEL_ARG(ctx->kernel, kernel_arg, cl_mem, &mem);
171         kernel_arg++;
172
173         mem = (cl_mem)input_main->data[plane];
174         CL_SET_KERNEL_ARG(ctx->kernel, kernel_arg, cl_mem, &mem);
175         kernel_arg++;
176
177         mem = (cl_mem)input_overlay->data[plane];
178         CL_SET_KERNEL_ARG(ctx->kernel, kernel_arg, cl_mem, &mem);
179         kernel_arg++;
180
181         if (ctx->alpha_separate) {
182             mem = (cl_mem)input_overlay->data[ctx->nb_planes];
183             CL_SET_KERNEL_ARG(ctx->kernel, kernel_arg, cl_mem, &mem);
184             kernel_arg++;
185         }
186
187         x = ctx->x_position / (plane == 0 ? 1 : ctx->x_subsample);
188         y = ctx->y_position / (plane == 0 ? 1 : ctx->y_subsample);
189
190         CL_SET_KERNEL_ARG(ctx->kernel, kernel_arg, cl_int, &x);
191         kernel_arg++;
192         CL_SET_KERNEL_ARG(ctx->kernel, kernel_arg, cl_int, &y);
193         kernel_arg++;
194
195         if (ctx->alpha_separate) {
196             cl_int alpha_adj_x = plane == 0 ? 1 : ctx->x_subsample;
197             cl_int alpha_adj_y = plane == 0 ? 1 : ctx->y_subsample;
198
199             CL_SET_KERNEL_ARG(ctx->kernel, kernel_arg, cl_int, &alpha_adj_x);
200             kernel_arg++;
201             CL_SET_KERNEL_ARG(ctx->kernel, kernel_arg, cl_int, &alpha_adj_y);
202             kernel_arg++;
203         }
204
205         err = ff_opencl_filter_work_size_from_image(avctx, global_work,
206                                                     output, plane, 0);
207         if (err < 0)
208             goto fail;
209
210         cle = clEnqueueNDRangeKernel(ctx->command_queue, ctx->kernel, 2, NULL,
211                                      global_work, NULL, 0, NULL, NULL);
212         if (cle != CL_SUCCESS) {
213             av_log(avctx, AV_LOG_ERROR, "Failed to enqueue "
214                    "overlay kernel for plane %d: %d.\n", cle, plane);
215             err = AVERROR(EIO);
216             goto fail;
217         }
218     }
219
220     cle = clFinish(ctx->command_queue);
221     if (cle != CL_SUCCESS) {
222         av_log(avctx, AV_LOG_ERROR, "Failed to finish "
223                "command queue: %d.\n", cle);
224         err = AVERROR(EIO);
225         goto fail;
226     }
227
228     err = av_frame_copy_props(output, input_main);
229
230     av_log(avctx, AV_LOG_DEBUG, "Filter output: %s, %ux%u (%"PRId64").\n",
231            av_get_pix_fmt_name(output->format),
232            output->width, output->height, output->pts);
233
234     return ff_filter_frame(outlink, output);
235
236 fail:
237     av_frame_free(&output);
238     return err;
239 }
240
241 static int overlay_opencl_config_output(AVFilterLink *outlink)
242 {
243     AVFilterContext *avctx = outlink->src;
244     OverlayOpenCLContext *ctx = avctx->priv;
245     int err;
246
247     err = ff_opencl_filter_config_output(outlink);
248     if (err < 0)
249         return err;
250
251     err = ff_framesync_init_dualinput(&ctx->fs, avctx);
252     if (err < 0)
253         return err;
254
255     return ff_framesync_configure(&ctx->fs);
256 }
257
258 static av_cold int overlay_opencl_init(AVFilterContext *avctx)
259 {
260     OverlayOpenCLContext *ctx = avctx->priv;
261
262     ctx->fs.on_event = &overlay_opencl_blend;
263
264     return ff_opencl_filter_init(avctx);
265 }
266
267 static int overlay_opencl_activate(AVFilterContext *avctx)
268 {
269     OverlayOpenCLContext *ctx = avctx->priv;
270
271     return ff_framesync_activate(&ctx->fs);
272 }
273
274 static av_cold void overlay_opencl_uninit(AVFilterContext *avctx)
275 {
276     OverlayOpenCLContext *ctx = avctx->priv;
277     cl_int cle;
278
279     if (ctx->kernel) {
280         cle = clReleaseKernel(ctx->kernel);
281         if (cle != CL_SUCCESS)
282             av_log(avctx, AV_LOG_ERROR, "Failed to release "
283                    "kernel: %d.\n", cle);
284     }
285
286     if (ctx->command_queue) {
287         cle = clReleaseCommandQueue(ctx->command_queue);
288         if (cle != CL_SUCCESS)
289             av_log(avctx, AV_LOG_ERROR, "Failed to release "
290                    "command queue: %d.\n", cle);
291     }
292
293     ff_opencl_filter_uninit(avctx);
294
295     ff_framesync_uninit(&ctx->fs);
296 }
297
298 #define OFFSET(x) offsetof(OverlayOpenCLContext, x)
299 #define FLAGS (AV_OPT_FLAG_FILTERING_PARAM | AV_OPT_FLAG_VIDEO_PARAM)
300 static const AVOption overlay_opencl_options[] = {
301     { "x", "Overlay x position",
302       OFFSET(x_position), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, INT_MAX, .flags = FLAGS },
303     { "y", "Overlay y position",
304       OFFSET(y_position), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, INT_MAX, .flags = FLAGS },
305     { NULL },
306 };
307
308 AVFILTER_DEFINE_CLASS(overlay_opencl);
309
310 static const AVFilterPad overlay_opencl_inputs[] = {
311     {
312         .name         = "main",
313         .type         = AVMEDIA_TYPE_VIDEO,
314         .config_props = &ff_opencl_filter_config_input,
315     },
316     {
317         .name         = "overlay",
318         .type         = AVMEDIA_TYPE_VIDEO,
319         .config_props = &ff_opencl_filter_config_input,
320     },
321     { NULL }
322 };
323
324 static const AVFilterPad overlay_opencl_outputs[] = {
325     {
326         .name          = "default",
327         .type          = AVMEDIA_TYPE_VIDEO,
328         .config_props  = &overlay_opencl_config_output,
329     },
330     { NULL }
331 };
332
333 AVFilter ff_vf_overlay_opencl = {
334     .name            = "overlay_opencl",
335     .description     = NULL_IF_CONFIG_SMALL("Overlay one video on top of another"),
336     .priv_size       = sizeof(OverlayOpenCLContext),
337     .priv_class      = &overlay_opencl_class,
338     .init            = &overlay_opencl_init,
339     .uninit          = &overlay_opencl_uninit,
340     .query_formats   = &ff_opencl_filter_query_formats,
341     .activate        = &overlay_opencl_activate,
342     .inputs          = overlay_opencl_inputs,
343     .outputs         = overlay_opencl_outputs,
344     .flags_internal  = FF_FILTER_FLAG_HWFRAME_AWARE,
345 };