2 * Copyright (c) 2010 Stefano Sabatini
3 * This file is part of FFmpeg.
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.
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.
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
31 #include "libavutil/avstring.h"
32 #include "libavutil/common.h"
33 #include "libavutil/imgutils.h"
34 #include "libavutil/internal.h"
35 #include "libavutil/mathematics.h"
36 #include "libavutil/mem.h"
37 #include "libavutil/opt.h"
38 #include "libavutil/parseutils.h"
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
);
55 typedef struct Frei0rContext
{
58 void *dl_handle
; /* dynamic library handle */
59 f0r_instance_t instance
;
60 f0r_plugin_info_t plugin_info
;
62 f0r_get_param_info_f get_param_info
;
63 f0r_get_param_value_f get_param_value
;
64 f0r_set_param_value_f set_param_value
;
65 f0r_construct_f construct
;
66 f0r_destruct_f destruct
;
73 /* only used by the source */
79 static void *load_sym(AVFilterContext
*ctx
, const char *sym_name
)
81 Frei0rContext
*s
= ctx
->priv
;
82 void *sym
= dlsym(s
->dl_handle
, sym_name
);
84 av_log(ctx
, AV_LOG_ERROR
, "Could not find symbol '%s' in loaded module.\n", sym_name
);
88 static int set_param(AVFilterContext
*ctx
, f0r_param_info_t info
, int index
, char *param
)
90 Frei0rContext
*s
= ctx
->priv
;
93 f0r_param_color_t col
;
94 f0r_param_position_t pos
;
101 if (!strcmp(param
, "y")) val
.d
= 1.0;
102 else if (!strcmp(param
, "n")) val
.d
= 0.0;
106 case F0R_PARAM_DOUBLE
:
107 val
.d
= strtod(param
, &tail
);
108 if (*tail
|| val
.d
== HUGE_VAL
)
112 case F0R_PARAM_COLOR
:
113 if (sscanf(param
, "%f/%f/%f", &val
.col
.r
, &val
.col
.g
, &val
.col
.b
) != 3) {
114 if (av_parse_color(rgba
, param
, -1, ctx
) < 0)
116 val
.col
.r
= rgba
[0] / 255.0;
117 val
.col
.g
= rgba
[1] / 255.0;
118 val
.col
.b
= rgba
[2] / 255.0;
122 case F0R_PARAM_POSITION
:
123 if (sscanf(param
, "%lf/%lf", &val
.pos
.x
, &val
.pos
.y
) != 2)
128 s
->set_param_value(s
->instance
, &val
, index
);
132 av_log(ctx
, AV_LOG_ERROR
, "Invalid value '%s' for parameter '%s'.\n",
134 return AVERROR(EINVAL
);
137 static int set_params(AVFilterContext
*ctx
, const char *params
)
139 Frei0rContext
*s
= ctx
->priv
;
145 for (i
= 0; i
< s
->plugin_info
.num_params
; i
++) {
146 f0r_param_info_t info
;
150 s
->get_param_info(&info
, i
);
153 if (!(param
= av_get_token(¶ms
, "|")))
154 return AVERROR(ENOMEM
);
156 params
++; /* skip ':' */
157 ret
= set_param(ctx
, info
, i
, param
);
163 av_log(ctx
, AV_LOG_VERBOSE
,
164 "idx:%d name:'%s' type:%s explanation:'%s' ",
166 info
.type
== F0R_PARAM_BOOL
? "bool" :
167 info
.type
== F0R_PARAM_DOUBLE
? "double" :
168 info
.type
== F0R_PARAM_COLOR
? "color" :
169 info
.type
== F0R_PARAM_POSITION
? "position" :
170 info
.type
== F0R_PARAM_STRING
? "string" : "unknown",
174 av_log(ctx
, AV_LOG_DEBUG
, "value:");
179 f0r_param_color_t col
;
180 f0r_param_position_t pos
;
184 s
->get_param_value(s
->instance
, v
, i
);
185 av_log(ctx
, AV_LOG_DEBUG
, "%s", d
>= 0.5 && d
<= 1.0 ? "y" : "n");
187 case F0R_PARAM_DOUBLE
:
189 s
->get_param_value(s
->instance
, v
, i
);
190 av_log(ctx
, AV_LOG_DEBUG
, "%f", d
);
192 case F0R_PARAM_COLOR
:
194 s
->get_param_value(s
->instance
, v
, i
);
195 av_log(ctx
, AV_LOG_DEBUG
, "%f/%f/%f", col
.r
, col
.g
, col
.b
);
197 case F0R_PARAM_POSITION
:
199 s
->get_param_value(s
->instance
, v
, i
);
200 av_log(ctx
, AV_LOG_DEBUG
, "%f/%f", pos
.x
, pos
.y
);
202 default: /* F0R_PARAM_STRING */
204 s
->get_param_value(s
->instance
, v
, i
);
205 av_log(ctx
, AV_LOG_DEBUG
, "'%s'", s
);
209 av_log(ctx
, AV_LOG_VERBOSE
, ".\n");
215 static int load_path(AVFilterContext
*ctx
, void **handle_ptr
, const char *prefix
, const char *name
)
217 char *path
= av_asprintf("%s%s%s", prefix
, name
, SLIBSUF
);
219 return AVERROR(ENOMEM
);
220 av_log(ctx
, AV_LOG_DEBUG
, "Looking for frei0r effect in '%s'.\n", path
);
221 *handle_ptr
= dlopen(path
, RTLD_NOW
|RTLD_LOCAL
);
226 static av_cold
int frei0r_init(AVFilterContext
*ctx
,
227 const char *dl_name
, int type
)
229 Frei0rContext
*s
= ctx
->priv
;
231 f0r_get_plugin_info_f f0r_get_plugin_info
;
232 f0r_plugin_info_t
*pi
;
236 static const char* const frei0r_pathlist
[] = {
237 "/usr/local/lib/frei0r-1/",
238 "/usr/lib/frei0r-1/",
239 "/usr/local/lib64/frei0r-1/",
240 "/usr/lib64/frei0r-1/"
244 av_log(ctx
, AV_LOG_ERROR
, "No filter name provided.\n");
245 return AVERROR(EINVAL
);
248 /* see: http://frei0r.dyne.org/codedoc/html/group__pluglocations.html */
249 if ((path
= av_strdup(getenv("FREI0R_PATH")))) {
251 const char *separator
= ";";
253 const char *separator
= ":";
255 char *p
, *ptr
= NULL
;
256 for (p
= path
; p
= av_strtok(p
, separator
, &ptr
); p
= NULL
) {
257 /* add additional trailing slash in case it is missing */
258 char *p1
= av_asprintf("%s/", p
);
260 ret
= AVERROR(ENOMEM
);
263 ret
= load_path(ctx
, &s
->dl_handle
, p1
, dl_name
);
276 if (!s
->dl_handle
&& (path
= getenv("HOME"))) {
277 char *prefix
= av_asprintf("%s/.frei0r-1/lib/", path
);
279 return AVERROR(ENOMEM
);
280 ret
= load_path(ctx
, &s
->dl_handle
, prefix
, dl_name
);
285 for (i
= 0; !s
->dl_handle
&& i
< FF_ARRAY_ELEMS(frei0r_pathlist
); i
++) {
286 ret
= load_path(ctx
, &s
->dl_handle
, frei0r_pathlist
[i
], dl_name
);
291 av_log(ctx
, AV_LOG_ERROR
, "Could not find module '%s'.\n", dl_name
);
292 return AVERROR(EINVAL
);
295 if (!(f0r_init
= load_sym(ctx
, "f0r_init" )) ||
296 !(f0r_get_plugin_info
= load_sym(ctx
, "f0r_get_plugin_info")) ||
297 !(s
->get_param_info
= load_sym(ctx
, "f0r_get_param_info" )) ||
298 !(s
->get_param_value
= load_sym(ctx
, "f0r_get_param_value")) ||
299 !(s
->set_param_value
= load_sym(ctx
, "f0r_set_param_value")) ||
300 !(s
->update
= load_sym(ctx
, "f0r_update" )) ||
301 !(s
->construct
= load_sym(ctx
, "f0r_construct" )) ||
302 !(s
->destruct
= load_sym(ctx
, "f0r_destruct" )) ||
303 !(s
->deinit
= load_sym(ctx
, "f0r_deinit" )))
304 return AVERROR(EINVAL
);
306 if (f0r_init() < 0) {
307 av_log(ctx
, AV_LOG_ERROR
, "Could not init the frei0r module.\n");
308 return AVERROR(EINVAL
);
311 f0r_get_plugin_info(&s
->plugin_info
);
312 pi
= &s
->plugin_info
;
313 if (pi
->plugin_type
!= type
) {
314 av_log(ctx
, AV_LOG_ERROR
,
315 "Invalid type '%s' for this plugin\n",
316 pi
->plugin_type
== F0R_PLUGIN_TYPE_FILTER
? "filter" :
317 pi
->plugin_type
== F0R_PLUGIN_TYPE_SOURCE
? "source" :
318 pi
->plugin_type
== F0R_PLUGIN_TYPE_MIXER2
? "mixer2" :
319 pi
->plugin_type
== F0R_PLUGIN_TYPE_MIXER3
? "mixer3" : "unknown");
320 return AVERROR(EINVAL
);
323 av_log(ctx
, AV_LOG_VERBOSE
,
324 "name:%s author:'%s' explanation:'%s' color_model:%s "
325 "frei0r_version:%d version:%d.%d num_params:%d\n",
326 pi
->name
, pi
->author
, pi
->explanation
,
327 pi
->color_model
== F0R_COLOR_MODEL_BGRA8888
? "bgra8888" :
328 pi
->color_model
== F0R_COLOR_MODEL_RGBA8888
? "rgba8888" :
329 pi
->color_model
== F0R_COLOR_MODEL_PACKED32
? "packed32" : "unknown",
330 pi
->frei0r_version
, pi
->major_version
, pi
->minor_version
, pi
->num_params
);
335 static av_cold
int filter_init(AVFilterContext
*ctx
)
337 Frei0rContext
*s
= ctx
->priv
;
339 return frei0r_init(ctx
, s
->dl_name
, F0R_PLUGIN_TYPE_FILTER
);
342 static av_cold
void uninit(AVFilterContext
*ctx
)
344 Frei0rContext
*s
= ctx
->priv
;
346 if (s
->destruct
&& s
->instance
)
347 s
->destruct(s
->instance
);
351 dlclose(s
->dl_handle
);
354 static int config_input_props(AVFilterLink
*inlink
)
356 AVFilterContext
*ctx
= inlink
->dst
;
357 Frei0rContext
*s
= ctx
->priv
;
359 if (s
->destruct
&& s
->instance
)
360 s
->destruct(s
->instance
);
361 if (!(s
->instance
= s
->construct(inlink
->w
, inlink
->h
))) {
362 av_log(ctx
, AV_LOG_ERROR
, "Impossible to load frei0r instance.\n");
363 return AVERROR(EINVAL
);
366 return set_params(ctx
, s
->params
);
369 static int query_formats(AVFilterContext
*ctx
)
371 Frei0rContext
*s
= ctx
->priv
;
372 AVFilterFormats
*formats
= NULL
;
374 if (s
->plugin_info
.color_model
== F0R_COLOR_MODEL_BGRA8888
) {
375 ff_add_format(&formats
, AV_PIX_FMT_BGRA
);
376 } else if (s
->plugin_info
.color_model
== F0R_COLOR_MODEL_RGBA8888
) {
377 ff_add_format(&formats
, AV_PIX_FMT_RGBA
);
378 } else { /* F0R_COLOR_MODEL_PACKED32 */
379 static const enum AVPixelFormat pix_fmts
[] = {
380 AV_PIX_FMT_BGRA
, AV_PIX_FMT_ARGB
, AV_PIX_FMT_ABGR
, AV_PIX_FMT_ARGB
, AV_PIX_FMT_NONE
382 formats
= ff_make_format_list(pix_fmts
);
386 return AVERROR(ENOMEM
);
388 ff_set_common_formats(ctx
, formats
);
392 static int filter_frame(AVFilterLink
*inlink
, AVFrame
*in
)
394 Frei0rContext
*s
= inlink
->dst
->priv
;
395 AVFilterLink
*outlink
= inlink
->dst
->outputs
[0];
398 out
= ff_get_video_buffer(outlink
, outlink
->w
, outlink
->h
);
401 return AVERROR(ENOMEM
);
403 av_frame_copy_props(out
, in
);
405 s
->update(s
->instance
, in
->pts
* av_q2d(inlink
->time_base
) * 1000,
406 (const uint32_t *)in
->data
[0],
407 (uint32_t *)out
->data
[0]);
411 return ff_filter_frame(outlink
, out
);
414 #define OFFSET(x) offsetof(Frei0rContext, x)
415 #define FLAGS AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_FILTERING_PARAM
416 static const AVOption frei0r_options
[] = {
417 { "filter_name", NULL
, OFFSET(dl_name
), AV_OPT_TYPE_STRING
, .flags
= FLAGS
},
418 { "filter_params", NULL
, OFFSET(params
), AV_OPT_TYPE_STRING
, .flags
= FLAGS
},
422 AVFILTER_DEFINE_CLASS(frei0r
);
424 static const AVFilterPad avfilter_vf_frei0r_inputs
[] = {
427 .type
= AVMEDIA_TYPE_VIDEO
,
428 .config_props
= config_input_props
,
429 .filter_frame
= filter_frame
,
434 static const AVFilterPad avfilter_vf_frei0r_outputs
[] = {
437 .type
= AVMEDIA_TYPE_VIDEO
,
442 AVFilter ff_vf_frei0r
= {
444 .description
= NULL_IF_CONFIG_SMALL("Apply a frei0r effect."),
445 .query_formats
= query_formats
,
448 .priv_size
= sizeof(Frei0rContext
),
449 .priv_class
= &frei0r_class
,
450 .inputs
= avfilter_vf_frei0r_inputs
,
451 .outputs
= avfilter_vf_frei0r_outputs
,
454 static av_cold
int source_init(AVFilterContext
*ctx
)
456 Frei0rContext
*s
= ctx
->priv
;
458 s
->time_base
.num
= s
->framerate
.den
;
459 s
->time_base
.den
= s
->framerate
.num
;
461 return frei0r_init(ctx
, s
->dl_name
, F0R_PLUGIN_TYPE_SOURCE
);
464 static int source_config_props(AVFilterLink
*outlink
)
466 AVFilterContext
*ctx
= outlink
->src
;
467 Frei0rContext
*s
= ctx
->priv
;
469 if (av_image_check_size(s
->w
, s
->h
, 0, ctx
) < 0)
470 return AVERROR(EINVAL
);
473 outlink
->time_base
= s
->time_base
;
474 outlink
->sample_aspect_ratio
= (AVRational
){1,1};
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
);
483 av_log(ctx
, AV_LOG_ERROR
, "frei0r filter parameters not set.\n");
484 return AVERROR(EINVAL
);
487 return set_params(ctx
, s
->params
);
490 static int source_request_frame(AVFilterLink
*outlink
)
492 Frei0rContext
*s
= outlink
->src
->priv
;
493 AVFrame
*frame
= ff_get_video_buffer(outlink
, outlink
->w
, outlink
->h
);
496 return AVERROR(ENOMEM
);
498 frame
->sample_aspect_ratio
= (AVRational
) {1, 1};
499 frame
->pts
= s
->pts
++;
501 s
->update(s
->instance
, av_rescale_q(frame
->pts
, s
->time_base
, (AVRational
){1,1000}),
502 NULL
, (uint32_t *)frame
->data
[0]);
504 return ff_filter_frame(outlink
, frame
);
507 static const AVOption frei0r_src_options
[] = {
508 { "size", "Dimensions of the generated video.", OFFSET(w
), AV_OPT_TYPE_IMAGE_SIZE
, { .str
= "320x240" }, .flags
= FLAGS
},
509 { "framerate", NULL
, OFFSET(framerate
), AV_OPT_TYPE_VIDEO_RATE
, { .str
= "25" }, .flags
= FLAGS
},
510 { "filter_name", NULL
, OFFSET(dl_name
), AV_OPT_TYPE_STRING
, .flags
= FLAGS
},
511 { "filter_params", NULL
, OFFSET(params
), AV_OPT_TYPE_STRING
, .flags
= FLAGS
},
515 AVFILTER_DEFINE_CLASS(frei0r_src
);
517 static const AVFilterPad avfilter_vsrc_frei0r_src_outputs
[] = {
520 .type
= AVMEDIA_TYPE_VIDEO
,
521 .request_frame
= source_request_frame
,
522 .config_props
= source_config_props
527 AVFilter ff_vsrc_frei0r_src
= {
528 .name
= "frei0r_src",
529 .description
= NULL_IF_CONFIG_SMALL("Generate a frei0r source."),
530 .priv_size
= sizeof(Frei0rContext
),
531 .priv_class
= &frei0r_src_class
,
534 .query_formats
= query_formats
,
536 .outputs
= avfilter_vsrc_frei0r_src_outputs
,