vf_frei0r.c
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2010 Stefano Sabatini
3  * This file is part of Libav.
4  *
5  * Libav 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  * Libav 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 Libav; if not, write to the Free Software
17  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
18  */
19 
25 /* #define DEBUG */
26 
27 #include <dlfcn.h>
28 #include <frei0r.h>
29 #include "libavutil/avstring.h"
30 #include "libavutil/imgutils.h"
31 #include "libavutil/mathematics.h"
32 #include "libavutil/parseutils.h"
33 #include "avfilter.h"
34 
35 typedef f0r_instance_t (*f0r_construct_f)(unsigned int width, unsigned int height);
36 typedef void (*f0r_destruct_f)(f0r_instance_t instance);
37 typedef void (*f0r_deinit_f)(void);
38 typedef int (*f0r_init_f)(void);
39 typedef void (*f0r_get_plugin_info_f)(f0r_plugin_info_t *info);
40 typedef void (*f0r_get_param_info_f)(f0r_param_info_t *info, int param_index);
41 typedef void (*f0r_update_f)(f0r_instance_t instance, double time, const uint32_t *inframe, uint32_t *outframe);
42 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);
43 typedef void (*f0r_set_param_value_f)(f0r_instance_t instance, f0r_param_t param, int param_index);
44 typedef void (*f0r_get_param_value_f)(f0r_instance_t instance, f0r_param_t param, int param_index);
45 
46 typedef struct Frei0rContext {
48  void *dl_handle; /* dynamic library handle */
49  f0r_instance_t instance;
50  f0r_plugin_info_t plugin_info;
51 
58  char params[256];
59 
60  /* only used by the source */
61  int w, h;
63  uint64_t pts;
65 
66 static void *load_sym(AVFilterContext *ctx, const char *sym_name)
67 {
68  Frei0rContext *frei0r = ctx->priv;
69  void *sym = dlsym(frei0r->dl_handle, sym_name);
70  if (!sym)
71  av_log(ctx, AV_LOG_ERROR, "Could not find symbol '%s' in loaded module\n", sym_name);
72  return sym;
73 }
74 
75 static int set_param(AVFilterContext *ctx, f0r_param_info_t info, int index, char *param)
76 {
77  Frei0rContext *frei0r = ctx->priv;
78  union {
79  double d;
80  f0r_param_color_t col;
81  f0r_param_position_t pos;
82  } val;
83  char *tail;
84  uint8_t rgba[4];
85 
86  switch (info.type) {
87  case F0R_PARAM_BOOL:
88  if (!strcmp(param, "y")) val.d = 1.0;
89  else if (!strcmp(param, "n")) val.d = 0.0;
90  else goto fail;
91  break;
92 
93  case F0R_PARAM_DOUBLE:
94  val.d = strtod(param, &tail);
95  if (*tail || val.d == HUGE_VAL)
96  goto fail;
97  break;
98 
99  case F0R_PARAM_COLOR:
100  if (sscanf(param, "%f/%f/%f", &val.col.r, &val.col.g, &val.col.b) != 3) {
101  if (av_parse_color(rgba, param, -1, ctx) < 0)
102  goto fail;
103  val.col.r = rgba[0] / 255.0;
104  val.col.g = rgba[1] / 255.0;
105  val.col.b = rgba[2] / 255.0;
106  }
107  break;
108 
109  case F0R_PARAM_POSITION:
110  if (sscanf(param, "%lf/%lf", &val.pos.x, &val.pos.y) != 2)
111  goto fail;
112  break;
113  }
114 
115  frei0r->set_param_value(frei0r->instance, &val, index);
116  return 0;
117 
118 fail:
119  av_log(ctx, AV_LOG_ERROR, "Invalid value '%s' for parameter '%s'\n",
120  param, info.name);
121  return AVERROR(EINVAL);
122 }
123 
124 static int set_params(AVFilterContext *ctx, const char *params)
125 {
126  Frei0rContext *frei0r = ctx->priv;
127  int i;
128 
129  for (i = 0; i < frei0r->plugin_info.num_params; i++) {
130  f0r_param_info_t info;
131  char *param;
132  int ret;
133 
134  frei0r->get_param_info(&info, i);
135 
136  if (*params) {
137  if (!(param = av_get_token(&params, ":")))
138  return AVERROR(ENOMEM);
139  params++; /* skip ':' */
140  ret = set_param(ctx, info, i, param);
141  av_free(param);
142  if (ret < 0)
143  return ret;
144  }
145 
146  av_log(ctx, AV_LOG_INFO,
147  "idx:%d name:'%s' type:%s explanation:'%s' ",
148  i, info.name,
149  info.type == F0R_PARAM_BOOL ? "bool" :
150  info.type == F0R_PARAM_DOUBLE ? "double" :
151  info.type == F0R_PARAM_COLOR ? "color" :
152  info.type == F0R_PARAM_POSITION ? "position" :
153  info.type == F0R_PARAM_STRING ? "string" : "unknown",
154  info.explanation);
155 
156 #ifdef DEBUG
157  av_log(ctx, AV_LOG_INFO, "value:");
158  switch (info.type) {
159  void *v;
160  double d;
161  char s[128];
162  f0r_param_color_t col;
163  f0r_param_position_t pos;
164 
165  case F0R_PARAM_BOOL:
166  v = &d;
167  frei0r->get_param_value(frei0r->instance, v, i);
168  av_log(ctx, AV_LOG_INFO, "%s", d >= 0.5 && d <= 1.0 ? "y" : "n");
169  break;
170  case F0R_PARAM_DOUBLE:
171  v = &d;
172  frei0r->get_param_value(frei0r->instance, v, i);
173  av_log(ctx, AV_LOG_INFO, "%f", d);
174  break;
175  case F0R_PARAM_COLOR:
176  v = &col;
177  frei0r->get_param_value(frei0r->instance, v, i);
178  av_log(ctx, AV_LOG_INFO, "%f/%f/%f", col.r, col.g, col.b);
179  break;
180  case F0R_PARAM_POSITION:
181  v = &pos;
182  frei0r->get_param_value(frei0r->instance, v, i);
183  av_log(ctx, AV_LOG_INFO, "%lf/%lf", pos.x, pos.y);
184  break;
185  default: /* F0R_PARAM_STRING */
186  v = s;
187  frei0r->get_param_value(frei0r->instance, v, i);
188  av_log(ctx, AV_LOG_INFO, "'%s'\n", s);
189  break;
190  }
191 #endif
192  av_log(ctx, AV_LOG_INFO, "\n");
193  }
194 
195  return 0;
196 }
197 
198 static void *load_path(AVFilterContext *ctx, const char *prefix, const char *name)
199 {
200  char path[1024];
201 
202  snprintf(path, sizeof(path), "%s%s%s", prefix, name, SLIBSUF);
203  av_log(ctx, AV_LOG_DEBUG, "Looking for frei0r effect in '%s'\n", path);
204  return dlopen(path, RTLD_NOW|RTLD_LOCAL);
205 }
206 
208  const char *dl_name, int type)
209 {
210  Frei0rContext *frei0r = ctx->priv;
211  f0r_init_f f0r_init;
212  f0r_get_plugin_info_f f0r_get_plugin_info;
213  f0r_plugin_info_t *pi;
214  char *path;
215 
216  /* see: http://piksel.org/frei0r/1.2/spec/1.2/spec/group__pluglocations.html */
217  if ((path = av_strdup(getenv("FREI0R_PATH")))) {
218  char *p, *ptr = NULL;
219  for (p = path; p = strtok_r(p, ":", &ptr); p = NULL)
220  if (frei0r->dl_handle = load_path(ctx, p, dl_name))
221  break;
222  av_free(path);
223  }
224  if (!frei0r->dl_handle && (path = getenv("HOME"))) {
225  char prefix[1024];
226  snprintf(prefix, sizeof(prefix), "%s/.frei0r-1/lib/", path);
227  frei0r->dl_handle = load_path(ctx, prefix, dl_name);
228  }
229  if (!frei0r->dl_handle)
230  frei0r->dl_handle = load_path(ctx, "/usr/local/lib/frei0r-1/", dl_name);
231  if (!frei0r->dl_handle)
232  frei0r->dl_handle = load_path(ctx, "/usr/lib/frei0r-1/", dl_name);
233  if (!frei0r->dl_handle) {
234  av_log(ctx, AV_LOG_ERROR, "Could not find module '%s'\n", dl_name);
235  return AVERROR(EINVAL);
236  }
237 
238  if (!(f0r_init = load_sym(ctx, "f0r_init" )) ||
239  !(f0r_get_plugin_info = load_sym(ctx, "f0r_get_plugin_info")) ||
240  !(frei0r->get_param_info = load_sym(ctx, "f0r_get_param_info" )) ||
241  !(frei0r->get_param_value = load_sym(ctx, "f0r_get_param_value")) ||
242  !(frei0r->set_param_value = load_sym(ctx, "f0r_set_param_value")) ||
243  !(frei0r->update = load_sym(ctx, "f0r_update" )) ||
244  !(frei0r->construct = load_sym(ctx, "f0r_construct" )) ||
245  !(frei0r->destruct = load_sym(ctx, "f0r_destruct" )) ||
246  !(frei0r->deinit = load_sym(ctx, "f0r_deinit" )))
247  return AVERROR(EINVAL);
248 
249  if (f0r_init() < 0) {
250  av_log(ctx, AV_LOG_ERROR, "Could not init the frei0r module");
251  return AVERROR(EINVAL);
252  }
253 
254  f0r_get_plugin_info(&frei0r->plugin_info);
255  pi = &frei0r->plugin_info;
256  if (pi->plugin_type != type) {
257  av_log(ctx, AV_LOG_ERROR,
258  "Invalid type '%s' for the plugin\n",
259  pi->plugin_type == F0R_PLUGIN_TYPE_FILTER ? "filter" :
260  pi->plugin_type == F0R_PLUGIN_TYPE_SOURCE ? "source" :
261  pi->plugin_type == F0R_PLUGIN_TYPE_MIXER2 ? "mixer2" :
262  pi->plugin_type == F0R_PLUGIN_TYPE_MIXER3 ? "mixer3" : "unknown");
263  return AVERROR(EINVAL);
264  }
265 
266  av_log(ctx, AV_LOG_INFO,
267  "name:%s author:'%s' explanation:'%s' color_model:%s "
268  "frei0r_version:%d version:%d.%d num_params:%d\n",
269  pi->name, pi->author, pi->explanation,
270  pi->color_model == F0R_COLOR_MODEL_BGRA8888 ? "bgra8888" :
271  pi->color_model == F0R_COLOR_MODEL_RGBA8888 ? "rgba8888" :
272  pi->color_model == F0R_COLOR_MODEL_PACKED32 ? "packed32" : "unknown",
273  pi->frei0r_version, pi->major_version, pi->minor_version, pi->num_params);
274 
275  return 0;
276 }
277 
278 static av_cold int filter_init(AVFilterContext *ctx, const char *args, void *opaque)
279 {
280  Frei0rContext *frei0r = ctx->priv;
281  char dl_name[1024], c;
282  *frei0r->params = 0;
283 
284  if (args)
285  sscanf(args, "%1023[^:=]%c%255c", dl_name, &c, frei0r->params);
286 
287  return frei0r_init(ctx, dl_name, F0R_PLUGIN_TYPE_FILTER);
288 }
289 
290 static av_cold void uninit(AVFilterContext *ctx)
291 {
292  Frei0rContext *frei0r = ctx->priv;
293 
294  if (frei0r->destruct && frei0r->instance)
295  frei0r->destruct(frei0r->instance);
296  if (frei0r->deinit)
297  frei0r->deinit();
298  if (frei0r->dl_handle)
299  dlclose(frei0r->dl_handle);
300 
301  memset(frei0r, 0, sizeof(*frei0r));
302 }
303 
304 static int config_input_props(AVFilterLink *inlink)
305 {
306  AVFilterContext *ctx = inlink->dst;
307  Frei0rContext *frei0r = ctx->priv;
308 
309  if (!(frei0r->instance = frei0r->construct(inlink->w, inlink->h))) {
310  av_log(ctx, AV_LOG_ERROR, "Impossible to load frei0r instance");
311  return AVERROR(EINVAL);
312  }
313 
314  return set_params(ctx, frei0r->params);
315 }
316 
318 {
319  Frei0rContext *frei0r = ctx->priv;
320  AVFilterFormats *formats = NULL;
321 
322  if (frei0r->plugin_info.color_model == F0R_COLOR_MODEL_BGRA8888) {
324  } else if (frei0r->plugin_info.color_model == F0R_COLOR_MODEL_RGBA8888) {
326  } else { /* F0R_COLOR_MODEL_PACKED32 */
327  static const enum PixelFormat pix_fmts[] = {
329  };
330  formats = avfilter_make_format_list(pix_fmts);
331  }
332 
333  if (!formats)
334  return AVERROR(ENOMEM);
335 
336  avfilter_set_common_formats(ctx, formats);
337  return 0;
338 }
339 
340 static void null_draw_slice(AVFilterLink *inlink, int y, int h, int slice_dir) { }
341 
342 static void end_frame(AVFilterLink *inlink)
343 {
344  Frei0rContext *frei0r = inlink->dst->priv;
345  AVFilterLink *outlink = inlink->dst->outputs[0];
346  AVFilterBufferRef *inpicref = inlink->cur_buf;
347  AVFilterBufferRef *outpicref = outlink->out_buf;
348 
349  frei0r->update(frei0r->instance, inpicref->pts * av_q2d(inlink->time_base) * 1000,
350  (const uint32_t *)inpicref->data[0],
351  (uint32_t *)outpicref->data[0]);
352  avfilter_unref_buffer(inpicref);
353  avfilter_draw_slice(outlink, 0, outlink->h, 1);
354  avfilter_end_frame(outlink);
355  avfilter_unref_buffer(outpicref);
356 }
357 
359  .name = "frei0r",
360  .description = NULL_IF_CONFIG_SMALL("Apply a frei0r effect."),
361 
362  .query_formats = query_formats,
363  .init = filter_init,
364  .uninit = uninit,
365 
366  .priv_size = sizeof(Frei0rContext),
367 
368  .inputs = (AVFilterPad[]) {{ .name = "default",
369  .type = AVMEDIA_TYPE_VIDEO,
370  .draw_slice = null_draw_slice,
371  .config_props = config_input_props,
372  .end_frame = end_frame,
373  .min_perms = AV_PERM_READ },
374  { .name = NULL}},
375 
376  .outputs = (AVFilterPad[]) {{ .name = "default",
377  .type = AVMEDIA_TYPE_VIDEO, },
378  { .name = NULL}},
379 };
380 
381 static av_cold int source_init(AVFilterContext *ctx, const char *args, void *opaque)
382 {
383  Frei0rContext *frei0r = ctx->priv;
384  char dl_name[1024], c;
385  char frame_size[128] = "";
386  char frame_rate[128] = "";
387  AVRational frame_rate_q;
388 
389  memset(frei0r->params, 0, sizeof(frei0r->params));
390 
391  if (args)
392  sscanf(args, "%127[^:]:%127[^:]:%1023[^:=]%c%255c",
393  frame_size, frame_rate, dl_name, &c, frei0r->params);
394 
395  if (av_parse_video_size(&frei0r->w, &frei0r->h, frame_size) < 0) {
396  av_log(ctx, AV_LOG_ERROR, "Invalid frame size: '%s'\n", frame_size);
397  return AVERROR(EINVAL);
398  }
399 
400  if (av_parse_video_rate(&frame_rate_q, frame_rate) < 0 ||
401  frame_rate_q.den <= 0 || frame_rate_q.num <= 0) {
402  av_log(ctx, AV_LOG_ERROR, "Invalid frame rate: '%s'\n", frame_rate);
403  return AVERROR(EINVAL);
404  }
405  frei0r->time_base.num = frame_rate_q.den;
406  frei0r->time_base.den = frame_rate_q.num;
407 
408  return frei0r_init(ctx, dl_name, F0R_PLUGIN_TYPE_SOURCE);
409 }
410 
411 static int source_config_props(AVFilterLink *outlink)
412 {
413  AVFilterContext *ctx = outlink->src;
414  Frei0rContext *frei0r = ctx->priv;
415 
416  if (av_image_check_size(frei0r->w, frei0r->h, 0, ctx) < 0)
417  return AVERROR(EINVAL);
418  outlink->w = frei0r->w;
419  outlink->h = frei0r->h;
420  outlink->time_base = frei0r->time_base;
421 
422  if (!(frei0r->instance = frei0r->construct(outlink->w, outlink->h))) {
423  av_log(ctx, AV_LOG_ERROR, "Impossible to load frei0r instance");
424  return AVERROR(EINVAL);
425  }
426 
427  return set_params(ctx, frei0r->params);
428 }
429 
430 static int source_request_frame(AVFilterLink *outlink)
431 {
432  Frei0rContext *frei0r = outlink->src->priv;
433  AVFilterBufferRef *picref = avfilter_get_video_buffer(outlink, AV_PERM_WRITE, outlink->w, outlink->h);
434  picref->video->pixel_aspect = (AVRational) {1, 1};
435  picref->pts = frei0r->pts++;
436  picref->pos = -1;
437 
438  avfilter_start_frame(outlink, avfilter_ref_buffer(picref, ~0));
439  frei0r->update(frei0r->instance, av_rescale_q(picref->pts, frei0r->time_base, (AVRational){1,1000}),
440  NULL, (uint32_t *)picref->data[0]);
441  avfilter_draw_slice(outlink, 0, outlink->h, 1);
442  avfilter_end_frame(outlink);
443  avfilter_unref_buffer(picref);
444 
445  return 0;
446 }
447 
449  .name = "frei0r_src",
450  .description = NULL_IF_CONFIG_SMALL("Generate a frei0r source."),
451 
452  .priv_size = sizeof(Frei0rContext),
453  .init = source_init,
454  .uninit = uninit,
455 
457 
458  .inputs = (AVFilterPad[]) {{ .name = NULL}},
459 
460  .outputs = (AVFilterPad[]) {{ .name = "default",
461  .type = AVMEDIA_TYPE_VIDEO,
462  .request_frame = source_request_frame,
463  .config_props = source_config_props },
464  { .name = NULL}},
465 };