Merge commit '5aa1a668cfae7f617e1a06efad20f87283badd8a'
[ffmpeg.git] / libavfilter / vf_frei0r.c
1 /*
2  * Copyright (c) 2010 Stefano Sabatini
3  * This file is part of FFmpeg.
4  *
5  * FFmpeg is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Lesser General Public
7  * License as published by the Free Software Foundation; either
8  * version 2.1 of the License, or (at your option) any later version.
9  *
10  * FFmpeg is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Lesser General Public License for more details.
14  *
15  * You should have received a copy of the GNU Lesser General Public
16  * License along with FFmpeg; if not, write to the Free Software
17  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
18  */
19
20 /**
21  * @file
22  * frei0r wrapper
23  */
24
25 /* #define DEBUG */
26
27 #include <dlfcn.h>
28 #include <frei0r.h>
29 #include <stdio.h>
30 #include <string.h>
31 #include <stdlib.h>
32 #include "config.h"
33 #include "libavutil/avstring.h"
34 #include "libavutil/imgutils.h"
35 #include "libavutil/internal.h"
36 #include "libavutil/mathematics.h"
37 #include "libavutil/mem.h"
38 #include "libavutil/opt.h"
39 #include "libavutil/parseutils.h"
40 #include "avfilter.h"
41 #include "formats.h"
42 #include "internal.h"
43 #include "video.h"
44
45 typedef f0r_instance_t (*f0r_construct_f)(unsigned int width, unsigned int height);
46 typedef void (*f0r_destruct_f)(f0r_instance_t instance);
47 typedef void (*f0r_deinit_f)(void);
48 typedef int (*f0r_init_f)(void);
49 typedef void (*f0r_get_plugin_info_f)(f0r_plugin_info_t *info);
50 typedef void (*f0r_get_param_info_f)(f0r_param_info_t *info, int param_index);
51 typedef void (*f0r_update_f)(f0r_instance_t instance, double time, const uint32_t *inframe, uint32_t *outframe);
52 typedef void (*f0r_update2_f)(f0r_instance_t instance, double time, const uint32_t *inframe1, const uint32_t *inframe2, const uint32_t *inframe3, uint32_t *outframe);
53 typedef void (*f0r_set_param_value_f)(f0r_instance_t instance, f0r_param_t param, int param_index);
54 typedef void (*f0r_get_param_value_f)(f0r_instance_t instance, f0r_param_t param, int param_index);
55
56 typedef struct Frei0rContext {
57     const AVClass *class;
58     f0r_update_f update;
59     void *dl_handle;            /* dynamic library handle   */
60     f0r_instance_t instance;
61     f0r_plugin_info_t plugin_info;
62
63     f0r_get_param_info_f  get_param_info;
64     f0r_get_param_value_f get_param_value;
65     f0r_set_param_value_f set_param_value;
66     f0r_construct_f       construct;
67     f0r_destruct_f        destruct;
68     f0r_deinit_f          deinit;
69
70     char *dl_name;
71     char *params;
72     char *size;
73     char *framerate;
74
75     /* only used by the source */
76     int w, h;
77     AVRational time_base;
78     uint64_t pts;
79 } Frei0rContext;
80
81 static void *load_sym(AVFilterContext *ctx, const char *sym_name)
82 {
83     Frei0rContext *frei0r = ctx->priv;
84     void *sym = dlsym(frei0r->dl_handle, sym_name);
85     if (!sym)
86         av_log(ctx, AV_LOG_ERROR, "Could not find symbol '%s' in loaded module\n", sym_name);
87     return sym;
88 }
89
90 static int set_param(AVFilterContext *ctx, f0r_param_info_t info, int index, char *param)
91 {
92     Frei0rContext *frei0r = ctx->priv;
93     union {
94         double d;
95         f0r_param_color_t col;
96         f0r_param_position_t pos;
97     } val;
98     char *tail;
99     uint8_t rgba[4];
100
101     switch (info.type) {
102     case F0R_PARAM_BOOL:
103         if      (!strcmp(param, "y")) val.d = 1.0;
104         else if (!strcmp(param, "n")) val.d = 0.0;
105         else goto fail;
106         break;
107
108     case F0R_PARAM_DOUBLE:
109         val.d = strtod(param, &tail);
110         if (*tail || val.d == HUGE_VAL)
111             goto fail;
112         break;
113
114     case F0R_PARAM_COLOR:
115         if (sscanf(param, "%f/%f/%f", &val.col.r, &val.col.g, &val.col.b) != 3) {
116             if (av_parse_color(rgba, param, -1, ctx) < 0)
117                 goto fail;
118             val.col.r = rgba[0] / 255.0;
119             val.col.g = rgba[1] / 255.0;
120             val.col.b = rgba[2] / 255.0;
121         }
122         break;
123
124     case F0R_PARAM_POSITION:
125         if (sscanf(param, "%lf/%lf", &val.pos.x, &val.pos.y) != 2)
126             goto fail;
127         break;
128     }
129
130     frei0r->set_param_value(frei0r->instance, &val, index);
131     return 0;
132
133 fail:
134     av_log(ctx, AV_LOG_ERROR, "Invalid value '%s' for parameter '%s'\n",
135            param, info.name);
136     return AVERROR(EINVAL);
137 }
138
139 static int set_params(AVFilterContext *ctx, const char *params)
140 {
141     Frei0rContext *frei0r = ctx->priv;
142     int i;
143
144     for (i = 0; i < frei0r->plugin_info.num_params; i++) {
145         f0r_param_info_t info;
146         char *param;
147         int ret;
148
149         frei0r->get_param_info(&info, i);
150
151         if (*params) {
152             if (!(param = av_get_token(&params, "|")))
153                 return AVERROR(ENOMEM);
154             params++;               /* skip ':' */
155             ret = set_param(ctx, info, i, param);
156             av_free(param);
157             if (ret < 0)
158                 return ret;
159         }
160
161         av_log(ctx, AV_LOG_VERBOSE,
162                "idx:%d name:'%s' type:%s explanation:'%s' ",
163                i, info.name,
164                info.type == F0R_PARAM_BOOL     ? "bool"     :
165                info.type == F0R_PARAM_DOUBLE   ? "double"   :
166                info.type == F0R_PARAM_COLOR    ? "color"    :
167                info.type == F0R_PARAM_POSITION ? "position" :
168                info.type == F0R_PARAM_STRING   ? "string"   : "unknown",
169                info.explanation);
170
171 #ifdef DEBUG
172         av_log(ctx, AV_LOG_DEBUG, "value:");
173         switch (info.type) {
174             void *v;
175             double d;
176             char s[128];
177             f0r_param_color_t col;
178             f0r_param_position_t pos;
179
180         case F0R_PARAM_BOOL:
181             v = &d;
182             frei0r->get_param_value(frei0r->instance, v, i);
183             av_log(ctx, AV_LOG_DEBUG, "%s", d >= 0.5 && d <= 1.0 ? "y" : "n");
184             break;
185         case F0R_PARAM_DOUBLE:
186             v = &d;
187             frei0r->get_param_value(frei0r->instance, v, i);
188             av_log(ctx, AV_LOG_DEBUG, "%f", d);
189             break;
190         case F0R_PARAM_COLOR:
191             v = &col;
192             frei0r->get_param_value(frei0r->instance, v, i);
193             av_log(ctx, AV_LOG_DEBUG, "%f/%f/%f", col.r, col.g, col.b);
194             break;
195         case F0R_PARAM_POSITION:
196             v = &pos;
197             frei0r->get_param_value(frei0r->instance, v, i);
198             av_log(ctx, AV_LOG_DEBUG, "%f/%f", pos.x, pos.y);
199             break;
200         default: /* F0R_PARAM_STRING */
201             v = s;
202             frei0r->get_param_value(frei0r->instance, v, i);
203             av_log(ctx, AV_LOG_DEBUG, "'%s'\n", s);
204             break;
205         }
206 #endif
207         av_log(ctx, AV_LOG_VERBOSE, "\n");
208     }
209
210     return 0;
211 }
212
213 static int load_path(AVFilterContext *ctx, void **handle_ptr, const char *prefix, const char *name)
214 {
215     char *path = av_asprintf("%s%s%s", prefix, name, SLIBSUF);
216     if (!path)
217         return AVERROR(ENOMEM);
218     av_log(ctx, AV_LOG_DEBUG, "Looking for frei0r effect in '%s'\n", path);
219     *handle_ptr = dlopen(path, RTLD_NOW|RTLD_LOCAL);
220     av_free(path);
221     return 0;
222 }
223
224 static av_cold int frei0r_init(AVFilterContext *ctx,
225                                const char *dl_name, int type)
226 {
227     Frei0rContext *frei0r = ctx->priv;
228     f0r_init_f            f0r_init;
229     f0r_get_plugin_info_f f0r_get_plugin_info;
230     f0r_plugin_info_t *pi;
231     char *path;
232     int ret = 0;
233
234     if (!dl_name) {
235         av_log(ctx, AV_LOG_ERROR, "No filter name provided.\n");
236         return AVERROR(EINVAL);
237     }
238
239     /* see: http://frei0r.dyne.org/codedoc/html/group__pluglocations.html */
240     if ((path = av_strdup(getenv("FREI0R_PATH")))) {
241 #ifdef _WIN32
242         const char *separator = ";";
243 #else
244         const char *separator = ":";
245 #endif
246         char *p, *ptr = NULL;
247         for (p = path; p = av_strtok(p, separator, &ptr); p = NULL) {
248             /* add additional trailing slash in case it is missing */
249             char *p1 = av_asprintf("%s/", p);
250             if (!p1) {
251                 ret = AVERROR(ENOMEM);
252                 goto check_path_end;
253             }
254             ret = load_path(ctx, &frei0r->dl_handle, p1, dl_name);
255             av_free(p1);
256             if (ret < 0)
257                 goto check_path_end;
258             if (frei0r->dl_handle)
259                 break;
260         }
261
262     check_path_end:
263         av_free(path);
264         if (ret < 0)
265             return ret;
266     }
267     if (!frei0r->dl_handle && (path = getenv("HOME"))) {
268         char *prefix = av_asprintf("%s/.frei0r-1/lib/", path);
269         if (!prefix)
270             return AVERROR(ENOMEM);
271         ret = load_path(ctx, &frei0r->dl_handle, prefix, dl_name);
272         av_free(prefix);
273         if (ret < 0)
274             return ret;
275     }
276     if (!frei0r->dl_handle) {
277         ret = load_path(ctx, &frei0r->dl_handle, "/usr/local/lib/frei0r-1/", dl_name);
278         if (ret < 0)
279             return ret;
280     }
281     if (!frei0r->dl_handle) {
282         ret = load_path(ctx, &frei0r->dl_handle, "/usr/lib/frei0r-1/", dl_name);
283         if (ret < 0)
284             return ret;
285     }
286     if (!frei0r->dl_handle) {
287         av_log(ctx, AV_LOG_ERROR, "Could not find module '%s'\n", dl_name);
288         return AVERROR(EINVAL);
289     }
290
291     if (!(f0r_init                = load_sym(ctx, "f0r_init"           )) ||
292         !(f0r_get_plugin_info     = load_sym(ctx, "f0r_get_plugin_info")) ||
293         !(frei0r->get_param_info  = load_sym(ctx, "f0r_get_param_info" )) ||
294         !(frei0r->get_param_value = load_sym(ctx, "f0r_get_param_value")) ||
295         !(frei0r->set_param_value = load_sym(ctx, "f0r_set_param_value")) ||
296         !(frei0r->update          = load_sym(ctx, "f0r_update"         )) ||
297         !(frei0r->construct       = load_sym(ctx, "f0r_construct"      )) ||
298         !(frei0r->destruct        = load_sym(ctx, "f0r_destruct"       )) ||
299         !(frei0r->deinit          = load_sym(ctx, "f0r_deinit"         )))
300         return AVERROR(EINVAL);
301
302     if (f0r_init() < 0) {
303         av_log(ctx, AV_LOG_ERROR, "Could not init the frei0r module\n");
304         return AVERROR(EINVAL);
305     }
306
307     f0r_get_plugin_info(&frei0r->plugin_info);
308     pi = &frei0r->plugin_info;
309     if (pi->plugin_type != type) {
310         av_log(ctx, AV_LOG_ERROR,
311                "Invalid type '%s' for the plugin\n",
312                pi->plugin_type == F0R_PLUGIN_TYPE_FILTER ? "filter" :
313                pi->plugin_type == F0R_PLUGIN_TYPE_SOURCE ? "source" :
314                pi->plugin_type == F0R_PLUGIN_TYPE_MIXER2 ? "mixer2" :
315                pi->plugin_type == F0R_PLUGIN_TYPE_MIXER3 ? "mixer3" : "unknown");
316         return AVERROR(EINVAL);
317     }
318
319     av_log(ctx, AV_LOG_VERBOSE,
320            "name:%s author:'%s' explanation:'%s' color_model:%s "
321            "frei0r_version:%d version:%d.%d num_params:%d\n",
322            pi->name, pi->author, pi->explanation,
323            pi->color_model == F0R_COLOR_MODEL_BGRA8888 ? "bgra8888" :
324            pi->color_model == F0R_COLOR_MODEL_RGBA8888 ? "rgba8888" :
325            pi->color_model == F0R_COLOR_MODEL_PACKED32 ? "packed32" : "unknown",
326            pi->frei0r_version, pi->major_version, pi->minor_version, pi->num_params);
327
328     return 0;
329 }
330
331 static av_cold int filter_init(AVFilterContext *ctx, const char *args)
332 {
333     Frei0rContext *frei0r = ctx->priv;
334
335     return frei0r_init(ctx, frei0r->dl_name, F0R_PLUGIN_TYPE_FILTER);
336 }
337
338 static av_cold void uninit(AVFilterContext *ctx)
339 {
340     Frei0rContext *frei0r = ctx->priv;
341
342     if (frei0r->destruct && frei0r->instance)
343         frei0r->destruct(frei0r->instance);
344     if (frei0r->deinit)
345         frei0r->deinit();
346     if (frei0r->dl_handle)
347         dlclose(frei0r->dl_handle);
348 }
349
350 static int config_input_props(AVFilterLink *inlink)
351 {
352     AVFilterContext *ctx = inlink->dst;
353     Frei0rContext *frei0r = ctx->priv;
354
355     if (!(frei0r->instance = frei0r->construct(inlink->w, inlink->h))) {
356         av_log(ctx, AV_LOG_ERROR, "Impossible to load frei0r instance\n");
357         return AVERROR(EINVAL);
358     }
359
360     return set_params(ctx, frei0r->params);
361 }
362
363 static int query_formats(AVFilterContext *ctx)
364 {
365     Frei0rContext *frei0r = ctx->priv;
366     AVFilterFormats *formats = NULL;
367
368     if        (frei0r->plugin_info.color_model == F0R_COLOR_MODEL_BGRA8888) {
369         ff_add_format(&formats, AV_PIX_FMT_BGRA);
370     } else if (frei0r->plugin_info.color_model == F0R_COLOR_MODEL_RGBA8888) {
371         ff_add_format(&formats, AV_PIX_FMT_RGBA);
372     } else {                                   /* F0R_COLOR_MODEL_PACKED32 */
373         static const enum AVPixelFormat pix_fmts[] = {
374             AV_PIX_FMT_BGRA, AV_PIX_FMT_ARGB, AV_PIX_FMT_ABGR, AV_PIX_FMT_ARGB, AV_PIX_FMT_NONE
375         };
376         formats = ff_make_format_list(pix_fmts);
377     }
378
379     if (!formats)
380         return AVERROR(ENOMEM);
381
382     ff_set_common_formats(ctx, formats);
383     return 0;
384 }
385
386 static int filter_frame(AVFilterLink *inlink, AVFrame *in)
387 {
388     Frei0rContext *frei0r = inlink->dst->priv;
389     AVFilterLink *outlink = inlink->dst->outputs[0];
390     AVFrame *out;
391
392     out = ff_get_video_buffer(outlink, outlink->w, outlink->h);
393     if (!out) {
394         av_frame_free(&in);
395         return AVERROR(ENOMEM);
396     }
397     av_frame_copy_props(out, in);
398
399     frei0r->update(frei0r->instance, in->pts * av_q2d(inlink->time_base) * 1000,
400                    (const uint32_t *)in->data[0],
401                    (uint32_t *)out->data[0]);
402
403     av_frame_free(&in);
404
405     return ff_filter_frame(outlink, out);
406 }
407
408 #define OFFSET(x) offsetof(Frei0rContext, x)
409 #define FLAGS AV_OPT_FLAG_VIDEO_PARAM
410 static const AVOption filter_options[] = {
411     { "filter_name",   NULL, OFFSET(dl_name), AV_OPT_TYPE_STRING, .flags = FLAGS },
412     { "filter_params", NULL, OFFSET(params),  AV_OPT_TYPE_STRING, .flags = FLAGS },
413     { NULL },
414 };
415
416 static const AVClass filter_class = {
417     .class_name = "frei0r",
418     .item_name  = av_default_item_name,
419     .option     = filter_options,
420     .version    = LIBAVUTIL_VERSION_INT,
421 };
422
423 static const AVFilterPad avfilter_vf_frei0r_inputs[] = {
424     {
425         .name         = "default",
426         .type         = AVMEDIA_TYPE_VIDEO,
427         .config_props = config_input_props,
428         .filter_frame = filter_frame,
429     },
430     { NULL }
431 };
432
433 static const AVFilterPad avfilter_vf_frei0r_outputs[] = {
434     {
435         .name = "default",
436         .type = AVMEDIA_TYPE_VIDEO,
437     },
438     { NULL }
439 };
440
441 AVFilter avfilter_vf_frei0r = {
442     .name      = "frei0r",
443     .description = NULL_IF_CONFIG_SMALL("Apply a frei0r effect."),
444
445     .query_formats = query_formats,
446     .init = filter_init,
447     .uninit = uninit,
448
449     .priv_size = sizeof(Frei0rContext),
450     .priv_class = &filter_class,
451
452     .inputs    = avfilter_vf_frei0r_inputs,
453
454     .outputs   = avfilter_vf_frei0r_outputs,
455 };
456
457 static av_cold int source_init(AVFilterContext *ctx, const char *args)
458 {
459     Frei0rContext *frei0r = ctx->priv;
460     AVRational frame_rate_q;
461
462     if (av_parse_video_size(&frei0r->w, &frei0r->h, frei0r->size) < 0) {
463         av_log(ctx, AV_LOG_ERROR, "Invalid frame size: '%s'\n", frei0r->size);
464         return AVERROR(EINVAL);
465     }
466
467     if (av_parse_video_rate(&frame_rate_q, frei0r->framerate) < 0) {
468         av_log(ctx, AV_LOG_ERROR, "Invalid frame rate: '%s'\n", frei0r->framerate);
469         return AVERROR(EINVAL);
470     }
471     frei0r->time_base.num = frame_rate_q.den;
472     frei0r->time_base.den = frame_rate_q.num;
473
474     return frei0r_init(ctx, frei0r->dl_name, F0R_PLUGIN_TYPE_SOURCE);
475 }
476
477 static int source_config_props(AVFilterLink *outlink)
478 {
479     AVFilterContext *ctx = outlink->src;
480     Frei0rContext *frei0r = ctx->priv;
481
482     if (av_image_check_size(frei0r->w, frei0r->h, 0, ctx) < 0)
483         return AVERROR(EINVAL);
484     outlink->w = frei0r->w;
485     outlink->h = frei0r->h;
486     outlink->time_base = frei0r->time_base;
487     outlink->sample_aspect_ratio = (AVRational){1,1};
488
489     if (!(frei0r->instance = frei0r->construct(outlink->w, outlink->h))) {
490         av_log(ctx, AV_LOG_ERROR, "Impossible to load frei0r instance\n");
491         return AVERROR(EINVAL);
492     }
493
494     return set_params(ctx, frei0r->params);
495 }
496
497 static int source_request_frame(AVFilterLink *outlink)
498 {
499     Frei0rContext *frei0r = outlink->src->priv;
500     AVFrame *frame = ff_get_video_buffer(outlink, outlink->w, outlink->h);
501
502     if (!frame)
503         return AVERROR(ENOMEM);
504
505     frame->sample_aspect_ratio = (AVRational) {1, 1};
506     frame->pts = frei0r->pts++;
507
508     frei0r->update(frei0r->instance, av_rescale_q(frame->pts, frei0r->time_base, (AVRational){1,1000}),
509                    NULL, (uint32_t *)frame->data[0]);
510
511     return ff_filter_frame(outlink, frame);
512 }
513
514 static const AVOption src_options[] = {
515     { "size",          "Dimensions of the generated video.", OFFSET(size),      AV_OPT_TYPE_STRING, { .str = "" },   .flags = FLAGS },
516     { "framerate",     NULL,                                 OFFSET(framerate), AV_OPT_TYPE_STRING, { .str = "25" }, .flags = FLAGS },
517     { "filter_name",   NULL,                                 OFFSET(dl_name),   AV_OPT_TYPE_STRING,                  .flags = FLAGS },
518     { "filter_params", NULL,                                 OFFSET(params),    AV_OPT_TYPE_STRING,                  .flags = FLAGS },
519     { NULL },
520 };
521
522 static const AVClass src_class = {
523     .class_name = "frei0r_src",
524     .item_name  = av_default_item_name,
525     .option     = src_options,
526     .version    = LIBAVUTIL_VERSION_INT,
527 };
528
529 static const AVFilterPad avfilter_vsrc_frei0r_src_outputs[] = {
530     {
531         .name          = "default",
532         .type          = AVMEDIA_TYPE_VIDEO,
533         .request_frame = source_request_frame,
534         .config_props  = source_config_props
535     },
536     { NULL }
537 };
538
539 AVFilter avfilter_vsrc_frei0r_src = {
540     .name        = "frei0r_src",
541     .description = NULL_IF_CONFIG_SMALL("Generate a frei0r source."),
542
543     .priv_size = sizeof(Frei0rContext),
544     .priv_class = &src_class,
545     .init      = source_init,
546     .uninit    = uninit,
547
548     .query_formats = query_formats,
549
550     .inputs    = NULL,
551
552     .outputs   = avfilter_vsrc_frei0r_src_outputs,
553 };