]> git.sesse.net Git - ffmpeg/blob - libavfilter/vf_frei0r.c
Merge commit '545a0b807cf45b2bbc4c9087a297b741ce00f508'
[ffmpeg] / 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 #include <dlfcn.h>
26 #include <frei0r.h>
27 #include <stdio.h>
28 #include <string.h>
29 #include <stdlib.h>
30 #include "config.h"
31 #include "libavutil/avstring.h"
32 #include "libavutil/imgutils.h"
33 #include "libavutil/internal.h"
34 #include "libavutil/mathematics.h"
35 #include "libavutil/mem.h"
36 #include "libavutil/opt.h"
37 #include "libavutil/parseutils.h"
38 #include "avfilter.h"
39 #include "formats.h"
40 #include "internal.h"
41 #include "video.h"
42
43 typedef f0r_instance_t (*f0r_construct_f)(unsigned int width, unsigned int height);
44 typedef void (*f0r_destruct_f)(f0r_instance_t instance);
45 typedef void (*f0r_deinit_f)(void);
46 typedef int (*f0r_init_f)(void);
47 typedef void (*f0r_get_plugin_info_f)(f0r_plugin_info_t *info);
48 typedef void (*f0r_get_param_info_f)(f0r_param_info_t *info, int param_index);
49 typedef void (*f0r_update_f)(f0r_instance_t instance, double time, const uint32_t *inframe, uint32_t *outframe);
50 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);
51 typedef void (*f0r_set_param_value_f)(f0r_instance_t instance, f0r_param_t param, int param_index);
52 typedef void (*f0r_get_param_value_f)(f0r_instance_t instance, f0r_param_t param, int param_index);
53
54 typedef struct Frei0rContext {
55     const AVClass *class;
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
68     char *dl_name;
69     char *params;
70     AVRational framerate;
71
72     /* only used by the source */
73     int w, h;
74     AVRational time_base;
75     uint64_t pts;
76 } Frei0rContext;
77
78 static void *load_sym(AVFilterContext *ctx, const char *sym_name)
79 {
80     Frei0rContext *s = ctx->priv;
81     void *sym = dlsym(s->dl_handle, sym_name);
82     if (!sym)
83         av_log(ctx, AV_LOG_ERROR, "Could not find symbol '%s' in loaded module\n", sym_name);
84     return sym;
85 }
86
87 static int set_param(AVFilterContext *ctx, f0r_param_info_t info, int index, char *param)
88 {
89     Frei0rContext *s = ctx->priv;
90     union {
91         double d;
92         f0r_param_color_t col;
93         f0r_param_position_t pos;
94     } val;
95     char *tail;
96     uint8_t rgba[4];
97
98     switch (info.type) {
99     case F0R_PARAM_BOOL:
100         if      (!strcmp(param, "y")) val.d = 1.0;
101         else if (!strcmp(param, "n")) val.d = 0.0;
102         else goto fail;
103         break;
104
105     case F0R_PARAM_DOUBLE:
106         val.d = strtod(param, &tail);
107         if (*tail || val.d == HUGE_VAL)
108             goto fail;
109         break;
110
111     case F0R_PARAM_COLOR:
112         if (sscanf(param, "%f/%f/%f", &val.col.r, &val.col.g, &val.col.b) != 3) {
113             if (av_parse_color(rgba, param, -1, ctx) < 0)
114                 goto fail;
115             val.col.r = rgba[0] / 255.0;
116             val.col.g = rgba[1] / 255.0;
117             val.col.b = rgba[2] / 255.0;
118         }
119         break;
120
121     case F0R_PARAM_POSITION:
122         if (sscanf(param, "%lf/%lf", &val.pos.x, &val.pos.y) != 2)
123             goto fail;
124         break;
125     }
126
127     s->set_param_value(s->instance, &val, index);
128     return 0;
129
130 fail:
131     av_log(ctx, AV_LOG_ERROR, "Invalid value '%s' for parameter '%s'\n",
132            param, info.name);
133     return AVERROR(EINVAL);
134 }
135
136 static int set_params(AVFilterContext *ctx, const char *params)
137 {
138     Frei0rContext *s = ctx->priv;
139     int i;
140
141     if (!params)
142         return 0;
143
144     for (i = 0; i < s->plugin_info.num_params; i++) {
145         f0r_param_info_t info;
146         char *param;
147         int ret;
148
149         s->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             s->get_param_value(s->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             s->get_param_value(s->instance, v, i);
188             av_log(ctx, AV_LOG_DEBUG, "%f", d);
189             break;
190         case F0R_PARAM_COLOR:
191             v = &col;
192             s->get_param_value(s->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             s->get_param_value(s->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             s->get_param_value(s->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 *s = 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, &s->dl_handle, p1, dl_name);
255             av_free(p1);
256             if (ret < 0)
257                 goto check_path_end;
258             if (s->dl_handle)
259                 break;
260         }
261
262     check_path_end:
263         av_free(path);
264         if (ret < 0)
265             return ret;
266     }
267     if (!s->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, &s->dl_handle, prefix, dl_name);
272         av_free(prefix);
273         if (ret < 0)
274             return ret;
275     }
276     if (!s->dl_handle) {
277         ret = load_path(ctx, &s->dl_handle, "/usr/local/lib/frei0r-1/", dl_name);
278         if (ret < 0)
279             return ret;
280     }
281     if (!s->dl_handle) {
282         ret = load_path(ctx, &s->dl_handle, "/usr/lib/frei0r-1/", dl_name);
283         if (ret < 0)
284             return ret;
285     }
286     if (!s->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         !(s->get_param_info  = load_sym(ctx, "f0r_get_param_info" )) ||
294         !(s->get_param_value = load_sym(ctx, "f0r_get_param_value")) ||
295         !(s->set_param_value = load_sym(ctx, "f0r_set_param_value")) ||
296         !(s->update          = load_sym(ctx, "f0r_update"         )) ||
297         !(s->construct       = load_sym(ctx, "f0r_construct"      )) ||
298         !(s->destruct        = load_sym(ctx, "f0r_destruct"       )) ||
299         !(s->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(&s->plugin_info);
308     pi = &s->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)
332 {
333     Frei0rContext *s = ctx->priv;
334
335     return frei0r_init(ctx, s->dl_name, F0R_PLUGIN_TYPE_FILTER);
336 }
337
338 static av_cold void uninit(AVFilterContext *ctx)
339 {
340     Frei0rContext *s = ctx->priv;
341
342     if (s->destruct && s->instance)
343         s->destruct(s->instance);
344     if (s->deinit)
345         s->deinit();
346     if (s->dl_handle)
347         dlclose(s->dl_handle);
348 }
349
350 static int config_input_props(AVFilterLink *inlink)
351 {
352     AVFilterContext *ctx = inlink->dst;
353     Frei0rContext *s = ctx->priv;
354
355     if (s->destruct && s->instance)
356         s->destruct(s->instance);
357     if (!(s->instance = s->construct(inlink->w, inlink->h))) {
358         av_log(ctx, AV_LOG_ERROR, "Impossible to load frei0r instance\n");
359         return AVERROR(EINVAL);
360     }
361
362     return set_params(ctx, s->params);
363 }
364
365 static int query_formats(AVFilterContext *ctx)
366 {
367     Frei0rContext *s = ctx->priv;
368     AVFilterFormats *formats = NULL;
369
370     if        (s->plugin_info.color_model == F0R_COLOR_MODEL_BGRA8888) {
371         ff_add_format(&formats, AV_PIX_FMT_BGRA);
372     } else if (s->plugin_info.color_model == F0R_COLOR_MODEL_RGBA8888) {
373         ff_add_format(&formats, AV_PIX_FMT_RGBA);
374     } else {                                   /* F0R_COLOR_MODEL_PACKED32 */
375         static const enum AVPixelFormat pix_fmts[] = {
376             AV_PIX_FMT_BGRA, AV_PIX_FMT_ARGB, AV_PIX_FMT_ABGR, AV_PIX_FMT_ARGB, AV_PIX_FMT_NONE
377         };
378         formats = ff_make_format_list(pix_fmts);
379     }
380
381     if (!formats)
382         return AVERROR(ENOMEM);
383
384     ff_set_common_formats(ctx, formats);
385     return 0;
386 }
387
388 static int filter_frame(AVFilterLink *inlink, AVFrame *in)
389 {
390     Frei0rContext *s = inlink->dst->priv;
391     AVFilterLink *outlink = inlink->dst->outputs[0];
392     AVFrame *out;
393
394     out = ff_get_video_buffer(outlink, outlink->w, outlink->h);
395     if (!out) {
396         av_frame_free(&in);
397         return AVERROR(ENOMEM);
398     }
399     av_frame_copy_props(out, in);
400
401     s->update(s->instance, in->pts * av_q2d(inlink->time_base) * 1000,
402                    (const uint32_t *)in->data[0],
403                    (uint32_t *)out->data[0]);
404
405     av_frame_free(&in);
406
407     return ff_filter_frame(outlink, out);
408 }
409
410 #define OFFSET(x) offsetof(Frei0rContext, x)
411 #define FLAGS AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_FILTERING_PARAM
412 static const AVOption frei0r_options[] = {
413     { "filter_name",   NULL, OFFSET(dl_name), AV_OPT_TYPE_STRING, .flags = FLAGS },
414     { "filter_params", NULL, OFFSET(params),  AV_OPT_TYPE_STRING, .flags = FLAGS },
415     { NULL },
416 };
417
418 AVFILTER_DEFINE_CLASS(frei0r);
419
420 static const AVFilterPad avfilter_vf_frei0r_inputs[] = {
421     {
422         .name         = "default",
423         .type         = AVMEDIA_TYPE_VIDEO,
424         .config_props = config_input_props,
425         .filter_frame = filter_frame,
426     },
427     { NULL }
428 };
429
430 static const AVFilterPad avfilter_vf_frei0r_outputs[] = {
431     {
432         .name = "default",
433         .type = AVMEDIA_TYPE_VIDEO,
434     },
435     { NULL }
436 };
437
438 AVFilter avfilter_vf_frei0r = {
439     .name      = "frei0r",
440     .description = NULL_IF_CONFIG_SMALL("Apply a frei0r effect."),
441
442     .query_formats = query_formats,
443     .init = filter_init,
444     .uninit = uninit,
445
446     .priv_size = sizeof(Frei0rContext),
447     .priv_class = &frei0r_class,
448
449     .inputs    = avfilter_vf_frei0r_inputs,
450
451     .outputs   = avfilter_vf_frei0r_outputs,
452 };
453
454 static av_cold int source_init(AVFilterContext *ctx)
455 {
456     Frei0rContext *s = ctx->priv;
457
458     s->time_base.num = s->framerate.den;
459     s->time_base.den = s->framerate.num;
460
461     return frei0r_init(ctx, s->dl_name, F0R_PLUGIN_TYPE_SOURCE);
462 }
463
464 static int source_config_props(AVFilterLink *outlink)
465 {
466     AVFilterContext *ctx = outlink->src;
467     Frei0rContext *s = ctx->priv;
468
469     if (av_image_check_size(s->w, s->h, 0, ctx) < 0)
470         return AVERROR(EINVAL);
471     outlink->w = s->w;
472     outlink->h = s->h;
473     outlink->time_base = s->time_base;
474     outlink->sample_aspect_ratio = (AVRational){1,1};
475
476     if (s->destruct && s->instance)
477         s->destruct(s->instance);
478     if (!(s->instance = s->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, s->params);
484 }
485
486 static int source_request_frame(AVFilterLink *outlink)
487 {
488     Frei0rContext *s = 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 = s->pts++;
496
497     s->update(s->instance, av_rescale_q(frame->pts, s->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 AVOption frei0r_src_options[] = {
504     { "size",          "Dimensions of the generated video.", OFFSET(w),         AV_OPT_TYPE_IMAGE_SIZE, { .str = "320x240" }, .flags = FLAGS },
505     { "framerate",     NULL,                                 OFFSET(framerate), AV_OPT_TYPE_VIDEO_RATE, { .str = "25" }, .flags = FLAGS },
506     { "filter_name",   NULL,                                 OFFSET(dl_name),   AV_OPT_TYPE_STRING,                  .flags = FLAGS },
507     { "filter_params", NULL,                                 OFFSET(params),    AV_OPT_TYPE_STRING,                  .flags = FLAGS },
508     { NULL },
509 };
510
511 AVFILTER_DEFINE_CLASS(frei0r_src);
512
513 static const AVFilterPad avfilter_vsrc_frei0r_src_outputs[] = {
514     {
515         .name          = "default",
516         .type          = AVMEDIA_TYPE_VIDEO,
517         .request_frame = source_request_frame,
518         .config_props  = source_config_props
519     },
520     { NULL }
521 };
522
523 AVFilter avfilter_vsrc_frei0r_src = {
524     .name        = "frei0r_src",
525     .description = NULL_IF_CONFIG_SMALL("Generate a frei0r source."),
526
527     .priv_size = sizeof(Frei0rContext),
528     .priv_class = &frei0r_src_class,
529     .init      = source_init,
530     .uninit    = uninit,
531
532     .query_formats = query_formats,
533
534     .inputs    = NULL,
535
536     .outputs   = avfilter_vsrc_frei0r_src_outputs,
537 };