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