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