]> git.sesse.net Git - ffmpeg/blob - libavfilter/vf_program_opencl.c
Merge commit '5b6213ef6bf5e0781c83e86926eb0b33a98dc185'
[ffmpeg] / libavfilter / vf_program_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/avstring.h"
20 #include "libavutil/buffer.h"
21 #include "libavutil/common.h"
22 #include "libavutil/hwcontext.h"
23 #include "libavutil/hwcontext_opencl.h"
24 #include "libavutil/log.h"
25 #include "libavutil/mem.h"
26 #include "libavutil/pixdesc.h"
27 #include "libavutil/opt.h"
28
29 #include "avfilter.h"
30 #include "framesync.h"
31 #include "internal.h"
32 #include "opencl.h"
33 #include "video.h"
34
35 typedef struct ProgramOpenCLContext {
36     OpenCLFilterContext ocf;
37
38     int                 loaded;
39     cl_uint             index;
40     cl_kernel           kernel;
41     cl_command_queue    command_queue;
42
43     FFFrameSync         fs;
44     AVFrame           **frames;
45
46     const char         *source_file;
47     const char         *kernel_name;
48     int                 nb_inputs;
49     int                 width, height;
50     enum AVPixelFormat  source_format;
51     AVRational          source_rate;
52 } ProgramOpenCLContext;
53
54 static int program_opencl_load(AVFilterContext *avctx)
55 {
56     ProgramOpenCLContext *ctx = avctx->priv;
57     cl_int cle;
58     int err;
59
60     err = ff_opencl_filter_load_program_from_file(avctx, ctx->source_file);
61     if (err < 0)
62         return err;
63
64     ctx->command_queue = clCreateCommandQueue(ctx->ocf.hwctx->context,
65                                               ctx->ocf.hwctx->device_id,
66                                               0, &cle);
67     if (!ctx->command_queue) {
68         av_log(avctx, AV_LOG_ERROR, "Failed to create OpenCL "
69                "command queue: %d.\n", cle);
70         return AVERROR(EIO);
71     }
72
73     ctx->kernel = clCreateKernel(ctx->ocf.program, ctx->kernel_name, &cle);
74     if (!ctx->kernel) {
75         if (cle == CL_INVALID_KERNEL_NAME) {
76             av_log(avctx, AV_LOG_ERROR, "Kernel function '%s' not found in "
77                    "program.\n", ctx->kernel_name);
78         } else {
79             av_log(avctx, AV_LOG_ERROR, "Failed to create kernel: %d.\n", cle);
80         }
81         return AVERROR(EIO);
82     }
83
84     ctx->loaded = 1;
85     return 0;
86 }
87
88 static int program_opencl_run(AVFilterContext *avctx)
89 {
90     AVFilterLink     *outlink = avctx->outputs[0];
91     ProgramOpenCLContext *ctx = avctx->priv;
92     AVFrame *output = NULL;
93     cl_int cle;
94     size_t global_work[2];
95     cl_mem src, dst;
96     int err, input, plane;
97
98     if (!ctx->loaded) {
99         err = program_opencl_load(avctx);
100         if (err < 0)
101             return err;
102     }
103
104     output = ff_get_video_buffer(outlink, outlink->w, outlink->h);
105     if (!output) {
106         err = AVERROR(ENOMEM);
107         goto fail;
108     }
109
110     for (plane = 0; plane < FF_ARRAY_ELEMS(output->data); plane++) {
111         dst = (cl_mem)output->data[plane];
112         if (!dst)
113             break;
114
115         cle = clSetKernelArg(ctx->kernel, 0, sizeof(cl_mem), &dst);
116         if (cle != CL_SUCCESS) {
117             av_log(avctx, AV_LOG_ERROR, "Failed to set kernel "
118                    "destination image argument: %d.\n", cle);
119             err = AVERROR_UNKNOWN;
120             goto fail;
121         }
122         cle = clSetKernelArg(ctx->kernel, 1, sizeof(cl_uint), &ctx->index);
123         if (cle != CL_SUCCESS) {
124             av_log(avctx, AV_LOG_ERROR, "Failed to set kernel "
125                    "index argument: %d.\n", cle);
126             err = AVERROR_UNKNOWN;
127             goto fail;
128         }
129
130         for (input = 0; input < ctx->nb_inputs; input++) {
131             av_assert0(ctx->frames[input]);
132
133             src = (cl_mem)ctx->frames[input]->data[plane];
134             av_assert0(src);
135
136             cle = clSetKernelArg(ctx->kernel, 2 + input, sizeof(cl_mem), &src);
137             if (cle != CL_SUCCESS) {
138                 av_log(avctx, AV_LOG_ERROR, "Failed to set kernel "
139                        "source image argument %d: %d.\n", input, cle);
140                 err = AVERROR_UNKNOWN;
141                 goto fail;
142             }
143         }
144
145         cle = clGetImageInfo(dst, CL_IMAGE_WIDTH,  sizeof(size_t),
146                              &global_work[0], NULL);
147         cle = clGetImageInfo(dst, CL_IMAGE_HEIGHT, sizeof(size_t),
148                              &global_work[1], NULL);
149
150         av_log(avctx, AV_LOG_DEBUG, "Run kernel on plane %d "
151                "(%zux%zu).\n", plane, global_work[0], global_work[1]);
152
153         cle = clEnqueueNDRangeKernel(ctx->command_queue, ctx->kernel, 2, NULL,
154                                      global_work, NULL, 0, NULL, NULL);
155         if (cle != CL_SUCCESS) {
156             av_log(avctx, AV_LOG_ERROR, "Failed to enqueue kernel: %d.\n",
157                    cle);
158             err = AVERROR(EIO);
159             goto fail;
160         }
161     }
162
163     cle = clFinish(ctx->command_queue);
164     if (cle != CL_SUCCESS) {
165         av_log(avctx, AV_LOG_ERROR, "Failed to finish command queue: %d.\n",
166                cle);
167         err = AVERROR(EIO);
168         goto fail;
169     }
170
171     if (ctx->nb_inputs > 0) {
172         err = av_frame_copy_props(output, ctx->frames[0]);
173         if (err < 0)
174             goto fail;
175     } else {
176         output->pts = ctx->index;
177     }
178     ++ctx->index;
179
180     av_log(ctx, AV_LOG_DEBUG, "Filter output: %s, %ux%u (%"PRId64").\n",
181            av_get_pix_fmt_name(output->format),
182            output->width, output->height, output->pts);
183
184     return ff_filter_frame(outlink, output);
185
186 fail:
187     clFinish(ctx->command_queue);
188     av_frame_free(&output);
189     return err;
190 }
191
192 static int program_opencl_request_frame(AVFilterLink *outlink)
193 {
194     AVFilterContext *avctx = outlink->src;
195
196     return program_opencl_run(avctx);
197 }
198
199 static int program_opencl_filter(FFFrameSync *fs)
200 {
201     AVFilterContext    *avctx = fs->parent;
202     ProgramOpenCLContext *ctx = avctx->priv;
203     int err, i;
204
205     for (i = 0; i < ctx->nb_inputs; i++) {
206         err = ff_framesync_get_frame(&ctx->fs, i, &ctx->frames[i], 0);
207         if (err < 0)
208             return err;
209     }
210
211     return program_opencl_run(avctx);
212 }
213
214 static int program_opencl_activate(AVFilterContext *avctx)
215 {
216     ProgramOpenCLContext *ctx = avctx->priv;
217
218     av_assert0(ctx->nb_inputs > 0);
219
220     return ff_framesync_activate(&ctx->fs);
221 }
222
223 static int program_opencl_config_output(AVFilterLink *outlink)
224 {
225     AVFilterContext    *avctx = outlink->src;
226     ProgramOpenCLContext *ctx = avctx->priv;
227     int err;
228
229     err = ff_opencl_filter_config_output(outlink);
230     if (err < 0)
231         return err;
232
233     if (ctx->nb_inputs > 0) {
234         FFFrameSyncIn *in;
235         int i;
236
237         err = ff_framesync_init(&ctx->fs, avctx, ctx->nb_inputs);
238         if (err < 0)
239             return err;
240
241         ctx->fs.opaque = ctx;
242         ctx->fs.on_event = &program_opencl_filter;
243
244         in = ctx->fs.in;
245         for (i = 0; i < ctx->nb_inputs; i++) {
246             const AVFilterLink *inlink = avctx->inputs[i];
247
248             in[i].time_base = inlink->time_base;
249             in[i].sync      = 1;
250             in[i].before    = EXT_STOP;
251             in[i].after     = EXT_INFINITY;
252         }
253
254         err = ff_framesync_configure(&ctx->fs);
255         if (err < 0)
256             return err;
257
258     } else {
259         outlink->time_base = av_inv_q(ctx->source_rate);
260     }
261
262     return 0;
263 }
264
265 static av_cold int program_opencl_init(AVFilterContext *avctx)
266 {
267     ProgramOpenCLContext *ctx = avctx->priv;
268     int err;
269
270     ff_opencl_filter_init(avctx);
271
272     ctx->ocf.output_width  = ctx->width;
273     ctx->ocf.output_height = ctx->height;
274
275     if (!strcmp(avctx->filter->name, "openclsrc")) {
276         if (!ctx->ocf.output_width || !ctx->ocf.output_height) {
277             av_log(avctx, AV_LOG_ERROR, "OpenCL source requires output "
278                    "dimensions to be specified.\n");
279             return AVERROR(EINVAL);
280         }
281
282         ctx->nb_inputs = 0;
283         ctx->ocf.output_format = ctx->source_format;
284     } else {
285         int i;
286
287         ctx->frames = av_mallocz_array(ctx->nb_inputs,
288                                        sizeof(*ctx->frames));
289         if (!ctx->frames)
290             return AVERROR(ENOMEM);
291
292         for (i = 0; i < ctx->nb_inputs; i++) {
293             AVFilterPad input;
294             memset(&input, 0, sizeof(input));
295
296             input.type = AVMEDIA_TYPE_VIDEO;
297             input.name = av_asprintf("input%d", i);
298             if (!input.name)
299                 return AVERROR(ENOMEM);
300
301             input.config_props = &ff_opencl_filter_config_input;
302
303             err = ff_insert_inpad(avctx, i, &input);
304             if (err < 0) {
305                 av_freep(&input.name);
306                 return err;
307             }
308         }
309     }
310
311     return 0;
312 }
313
314 static av_cold void program_opencl_uninit(AVFilterContext *avctx)
315 {
316     ProgramOpenCLContext *ctx = avctx->priv;
317     cl_int cle;
318     int i;
319
320     if (ctx->nb_inputs > 0) {
321         ff_framesync_uninit(&ctx->fs);
322
323         av_freep(&ctx->frames);
324         for (i = 0; i < avctx->nb_inputs; i++)
325             av_freep(&avctx->input_pads[i].name);
326     }
327
328     if (ctx->kernel) {
329         cle = clReleaseKernel(ctx->kernel);
330         if (cle != CL_SUCCESS)
331             av_log(avctx, AV_LOG_ERROR, "Failed to release "
332                    "kernel: %d.\n", cle);
333     }
334
335     if (ctx->command_queue) {
336         cle = clReleaseCommandQueue(ctx->command_queue);
337         if (cle != CL_SUCCESS)
338             av_log(avctx, AV_LOG_ERROR, "Failed to release "
339                    "command queue: %d.\n", cle);
340     }
341
342     ff_opencl_filter_uninit(avctx);
343 }
344
345 #define OFFSET(x) offsetof(ProgramOpenCLContext, x)
346 #define FLAGS (AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_FILTERING_PARAM)
347
348 #if CONFIG_PROGRAM_OPENCL_FILTER
349
350 static const AVOption program_opencl_options[] = {
351     { "source", "OpenCL program source file", OFFSET(source_file),
352       AV_OPT_TYPE_STRING, { .str = NULL }, .flags = FLAGS },
353     { "kernel", "Kernel name in program",     OFFSET(kernel_name),
354       AV_OPT_TYPE_STRING, { .str = NULL }, .flags = FLAGS },
355
356     { "inputs", "Number of inputs", OFFSET(nb_inputs),
357       AV_OPT_TYPE_INT,              { .i64 = 1 }, 1, INT_MAX, FLAGS },
358
359     { "size",   "Video size",       OFFSET(width),
360       AV_OPT_TYPE_IMAGE_SIZE,       { .str = NULL }, 0, 0, FLAGS },
361     { "s",      "Video size",       OFFSET(width),
362       AV_OPT_TYPE_IMAGE_SIZE,       { .str = NULL }, 0, 0, FLAGS },
363
364     { NULL },
365 };
366
367 FRAMESYNC_DEFINE_CLASS(program_opencl, ProgramOpenCLContext, fs);
368
369 static const AVFilterPad program_opencl_outputs[] = {
370     {
371         .name         = "default",
372         .type         = AVMEDIA_TYPE_VIDEO,
373         .config_props = &program_opencl_config_output,
374     },
375     { NULL }
376 };
377
378 AVFilter ff_vf_program_opencl = {
379     .name           = "program_opencl",
380     .description    = NULL_IF_CONFIG_SMALL("Filter video using an OpenCL program"),
381     .priv_size      = sizeof(ProgramOpenCLContext),
382     .priv_class     = &program_opencl_class,
383     .preinit        = &program_opencl_framesync_preinit,
384     .init           = &program_opencl_init,
385     .uninit         = &program_opencl_uninit,
386     .query_formats  = &ff_opencl_filter_query_formats,
387     .activate       = &program_opencl_activate,
388     .inputs         = NULL,
389     .outputs        = program_opencl_outputs,
390     .flags_internal = FF_FILTER_FLAG_HWFRAME_AWARE,
391 };
392
393 #endif
394
395 #if CONFIG_OPENCLSRC_FILTER
396
397 static const AVOption openclsrc_options[] = {
398     { "source", "OpenCL program source file", OFFSET(source_file),
399       AV_OPT_TYPE_STRING, { .str = NULL }, .flags = FLAGS },
400     { "kernel", "Kernel name in program",     OFFSET(kernel_name),
401       AV_OPT_TYPE_STRING, { .str = NULL }, .flags = FLAGS },
402
403     { "size",   "Video size",       OFFSET(width),
404       AV_OPT_TYPE_IMAGE_SIZE,       { .str = NULL }, 0, 0, FLAGS },
405     { "s",      "Video size",       OFFSET(width),
406       AV_OPT_TYPE_IMAGE_SIZE,       { .str = NULL }, 0, 0, FLAGS },
407
408     { "format", "Video format",     OFFSET(source_format),
409       AV_OPT_TYPE_PIXEL_FMT,        { .i64 = AV_PIX_FMT_NONE }, -1, INT_MAX, FLAGS },
410
411     { "rate",   "Video frame rate", OFFSET(source_rate),
412       AV_OPT_TYPE_VIDEO_RATE,       { .str = "25" }, 0, INT_MAX, FLAGS },
413     { "r",      "Video frame rate", OFFSET(source_rate),
414       AV_OPT_TYPE_VIDEO_RATE,       { .str = "25" }, 0, INT_MAX, FLAGS },
415
416     { NULL },
417 };
418
419 AVFILTER_DEFINE_CLASS(openclsrc);
420
421 static const AVFilterPad openclsrc_outputs[] = {
422     {
423         .name          = "default",
424         .type          = AVMEDIA_TYPE_VIDEO,
425         .config_props  = &program_opencl_config_output,
426         .request_frame = &program_opencl_request_frame,
427     },
428     { NULL }
429 };
430
431 AVFilter ff_vsrc_openclsrc = {
432     .name           = "openclsrc",
433     .description    = NULL_IF_CONFIG_SMALL("Generate video using an OpenCL program"),
434     .priv_size      = sizeof(ProgramOpenCLContext),
435     .priv_class     = &openclsrc_class,
436     .init           = &program_opencl_init,
437     .uninit         = &program_opencl_uninit,
438     .query_formats  = &ff_opencl_filter_query_formats,
439     .inputs         = NULL,
440     .outputs        = openclsrc_outputs,
441     .flags_internal = FF_FILTER_FLAG_HWFRAME_AWARE,
442 };
443
444 #endif