2 * Copyright (c) 2010 Stefano Sabatini
3 * This file is part of Libav.
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.
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.
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
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"
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
{
57 void *dl_handle
; /* dynamic library handle */
58 f0r_instance_t instance
;
59 f0r_plugin_info_t plugin_info
;
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
;
69 /* only used by the source */
75 static void *load_sym(AVFilterContext
*ctx
, const char *sym_name
)
77 Frei0rContext
*frei0r
= ctx
->priv
;
78 void *sym
= dlsym(frei0r
->dl_handle
, sym_name
);
80 av_log(ctx
, AV_LOG_ERROR
, "Could not find symbol '%s' in loaded module\n", sym_name
);
84 static int set_param(AVFilterContext
*ctx
, f0r_param_info_t info
, int index
, char *param
)
86 Frei0rContext
*frei0r
= ctx
->priv
;
89 f0r_param_color_t col
;
90 f0r_param_position_t pos
;
97 if (!strcmp(param
, "y")) val
.d
= 1.0;
98 else if (!strcmp(param
, "n")) val
.d
= 0.0;
102 case F0R_PARAM_DOUBLE
:
103 val
.d
= strtod(param
, &tail
);
104 if (*tail
|| val
.d
== HUGE_VAL
)
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)
112 val
.col
.r
= rgba
[0] / 255.0;
113 val
.col
.g
= rgba
[1] / 255.0;
114 val
.col
.b
= rgba
[2] / 255.0;
118 case F0R_PARAM_POSITION
:
119 if (sscanf(param
, "%lf/%lf", &val
.pos
.x
, &val
.pos
.y
) != 2)
124 frei0r
->set_param_value(frei0r
->instance
, &val
, index
);
128 av_log(ctx
, AV_LOG_ERROR
, "Invalid value '%s' for parameter '%s'\n",
130 return AVERROR(EINVAL
);
133 static int set_params(AVFilterContext
*ctx
, const char *params
)
135 Frei0rContext
*frei0r
= ctx
->priv
;
138 for (i
= 0; i
< frei0r
->plugin_info
.num_params
; i
++) {
139 f0r_param_info_t info
;
143 frei0r
->get_param_info(&info
, i
);
146 if (!(param
= av_get_token(¶ms
, ":")))
147 return AVERROR(ENOMEM
);
148 params
++; /* skip ':' */
149 ret
= set_param(ctx
, info
, i
, param
);
155 av_log(ctx
, AV_LOG_VERBOSE
,
156 "idx:%d name:'%s' type:%s explanation:'%s' ",
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",
166 av_log(ctx
, AV_LOG_DEBUG
, "value:");
171 f0r_param_color_t col
;
172 f0r_param_position_t pos
;
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");
179 case F0R_PARAM_DOUBLE
:
181 frei0r
->get_param_value(frei0r
->instance
, v
, i
);
182 av_log(ctx
, AV_LOG_DEBUG
, "%f", d
);
184 case F0R_PARAM_COLOR
:
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
);
189 case F0R_PARAM_POSITION
:
191 frei0r
->get_param_value(frei0r
->instance
, v
, i
);
192 av_log(ctx
, AV_LOG_DEBUG
, "%f/%f", pos
.x
, pos
.y
);
194 default: /* F0R_PARAM_STRING */
196 frei0r
->get_param_value(frei0r
->instance
, v
, i
);
197 av_log(ctx
, AV_LOG_DEBUG
, "'%s'\n", s
);
201 av_log(ctx
, AV_LOG_VERBOSE
, "\n");
207 static void *load_path(AVFilterContext
*ctx
, const char *prefix
, const char *name
)
211 snprintf(path
, sizeof(path
), "%s%s%s", prefix
, name
, SLIBSUF
);
212 av_log(ctx
, AV_LOG_DEBUG
, "Looking for frei0r effect in '%s'\n", path
);
213 return dlopen(path
, RTLD_NOW
|RTLD_LOCAL
);
216 static av_cold
int frei0r_init(AVFilterContext
*ctx
,
217 const char *dl_name
, int type
)
219 Frei0rContext
*frei0r
= ctx
->priv
;
221 f0r_get_plugin_info_f f0r_get_plugin_info
;
222 f0r_plugin_info_t
*pi
;
225 /* see: http://piksel.org/frei0r/1.2/spec/1.2/spec/group__pluglocations.html */
226 if ((path
= av_strdup(getenv("FREI0R_PATH")))) {
227 char *p
, *ptr
= NULL
;
228 for (p
= path
; p
= strtok_r(p
, ":", &ptr
); p
= NULL
)
229 if (frei0r
->dl_handle
= load_path(ctx
, p
, dl_name
))
233 if (!frei0r
->dl_handle
&& (path
= getenv("HOME"))) {
235 snprintf(prefix
, sizeof(prefix
), "%s/.frei0r-1/lib/", path
);
236 frei0r
->dl_handle
= load_path(ctx
, prefix
, dl_name
);
238 if (!frei0r
->dl_handle
)
239 frei0r
->dl_handle
= load_path(ctx
, "/usr/local/lib/frei0r-1/", dl_name
);
240 if (!frei0r
->dl_handle
)
241 frei0r
->dl_handle
= load_path(ctx
, "/usr/lib/frei0r-1/", dl_name
);
242 if (!frei0r
->dl_handle
) {
243 av_log(ctx
, AV_LOG_ERROR
, "Could not find module '%s'\n", dl_name
);
244 return AVERROR(EINVAL
);
247 if (!(f0r_init
= load_sym(ctx
, "f0r_init" )) ||
248 !(f0r_get_plugin_info
= load_sym(ctx
, "f0r_get_plugin_info")) ||
249 !(frei0r
->get_param_info
= load_sym(ctx
, "f0r_get_param_info" )) ||
250 !(frei0r
->get_param_value
= load_sym(ctx
, "f0r_get_param_value")) ||
251 !(frei0r
->set_param_value
= load_sym(ctx
, "f0r_set_param_value")) ||
252 !(frei0r
->update
= load_sym(ctx
, "f0r_update" )) ||
253 !(frei0r
->construct
= load_sym(ctx
, "f0r_construct" )) ||
254 !(frei0r
->destruct
= load_sym(ctx
, "f0r_destruct" )) ||
255 !(frei0r
->deinit
= load_sym(ctx
, "f0r_deinit" )))
256 return AVERROR(EINVAL
);
258 if (f0r_init() < 0) {
259 av_log(ctx
, AV_LOG_ERROR
, "Could not init the frei0r module");
260 return AVERROR(EINVAL
);
263 f0r_get_plugin_info(&frei0r
->plugin_info
);
264 pi
= &frei0r
->plugin_info
;
265 if (pi
->plugin_type
!= type
) {
266 av_log(ctx
, AV_LOG_ERROR
,
267 "Invalid type '%s' for the plugin\n",
268 pi
->plugin_type
== F0R_PLUGIN_TYPE_FILTER
? "filter" :
269 pi
->plugin_type
== F0R_PLUGIN_TYPE_SOURCE
? "source" :
270 pi
->plugin_type
== F0R_PLUGIN_TYPE_MIXER2
? "mixer2" :
271 pi
->plugin_type
== F0R_PLUGIN_TYPE_MIXER3
? "mixer3" : "unknown");
272 return AVERROR(EINVAL
);
275 av_log(ctx
, AV_LOG_VERBOSE
,
276 "name:%s author:'%s' explanation:'%s' color_model:%s "
277 "frei0r_version:%d version:%d.%d num_params:%d\n",
278 pi
->name
, pi
->author
, pi
->explanation
,
279 pi
->color_model
== F0R_COLOR_MODEL_BGRA8888
? "bgra8888" :
280 pi
->color_model
== F0R_COLOR_MODEL_RGBA8888
? "rgba8888" :
281 pi
->color_model
== F0R_COLOR_MODEL_PACKED32
? "packed32" : "unknown",
282 pi
->frei0r_version
, pi
->major_version
, pi
->minor_version
, pi
->num_params
);
287 static av_cold
int filter_init(AVFilterContext
*ctx
, const char *args
)
289 Frei0rContext
*frei0r
= ctx
->priv
;
290 char dl_name
[1024], c
;
294 sscanf(args
, "%1023[^:=]%c%255c", dl_name
, &c
, frei0r
->params
);
296 return frei0r_init(ctx
, dl_name
, F0R_PLUGIN_TYPE_FILTER
);
299 static av_cold
void uninit(AVFilterContext
*ctx
)
301 Frei0rContext
*frei0r
= ctx
->priv
;
303 if (frei0r
->destruct
&& frei0r
->instance
)
304 frei0r
->destruct(frei0r
->instance
);
307 if (frei0r
->dl_handle
)
308 dlclose(frei0r
->dl_handle
);
310 memset(frei0r
, 0, sizeof(*frei0r
));
313 static int config_input_props(AVFilterLink
*inlink
)
315 AVFilterContext
*ctx
= inlink
->dst
;
316 Frei0rContext
*frei0r
= ctx
->priv
;
318 if (!(frei0r
->instance
= frei0r
->construct(inlink
->w
, inlink
->h
))) {
319 av_log(ctx
, AV_LOG_ERROR
, "Impossible to load frei0r instance");
320 return AVERROR(EINVAL
);
323 return set_params(ctx
, frei0r
->params
);
326 static int query_formats(AVFilterContext
*ctx
)
328 Frei0rContext
*frei0r
= ctx
->priv
;
329 AVFilterFormats
*formats
= NULL
;
331 if (frei0r
->plugin_info
.color_model
== F0R_COLOR_MODEL_BGRA8888
) {
332 ff_add_format(&formats
, AV_PIX_FMT_BGRA
);
333 } else if (frei0r
->plugin_info
.color_model
== F0R_COLOR_MODEL_RGBA8888
) {
334 ff_add_format(&formats
, AV_PIX_FMT_RGBA
);
335 } else { /* F0R_COLOR_MODEL_PACKED32 */
336 static const enum AVPixelFormat pix_fmts
[] = {
337 AV_PIX_FMT_BGRA
, AV_PIX_FMT_ARGB
, AV_PIX_FMT_ABGR
, AV_PIX_FMT_ARGB
, AV_PIX_FMT_NONE
339 formats
= ff_make_format_list(pix_fmts
);
343 return AVERROR(ENOMEM
);
345 ff_set_common_formats(ctx
, formats
);
349 static int filter_frame(AVFilterLink
*inlink
, AVFilterBufferRef
*in
)
351 Frei0rContext
*frei0r
= inlink
->dst
->priv
;
352 AVFilterLink
*outlink
= inlink
->dst
->outputs
[0];
353 AVFilterBufferRef
*out
;
355 out
= ff_get_video_buffer(outlink
, AV_PERM_WRITE
, outlink
->w
, outlink
->h
);
357 avfilter_unref_bufferp(&in
);
358 return AVERROR(ENOMEM
);
360 avfilter_copy_buffer_ref_props(out
, in
);
362 frei0r
->update(frei0r
->instance
, in
->pts
* av_q2d(inlink
->time_base
) * 1000,
363 (const uint32_t *)in
->data
[0],
364 (uint32_t *)out
->data
[0]);
366 avfilter_unref_bufferp(&in
);
368 return ff_filter_frame(outlink
, out
);
371 static const AVFilterPad avfilter_vf_frei0r_inputs
[] = {
374 .type
= AVMEDIA_TYPE_VIDEO
,
375 .config_props
= config_input_props
,
376 .filter_frame
= filter_frame
,
377 .min_perms
= AV_PERM_READ
382 static const AVFilterPad avfilter_vf_frei0r_outputs
[] = {
385 .type
= AVMEDIA_TYPE_VIDEO
,
390 AVFilter avfilter_vf_frei0r
= {
392 .description
= NULL_IF_CONFIG_SMALL("Apply a frei0r effect."),
394 .query_formats
= query_formats
,
398 .priv_size
= sizeof(Frei0rContext
),
400 .inputs
= avfilter_vf_frei0r_inputs
,
402 .outputs
= avfilter_vf_frei0r_outputs
,
405 static av_cold
int source_init(AVFilterContext
*ctx
, const char *args
)
407 Frei0rContext
*frei0r
= ctx
->priv
;
408 char dl_name
[1024], c
;
409 char frame_size
[128] = "";
410 char frame_rate
[128] = "";
411 AVRational frame_rate_q
;
413 memset(frei0r
->params
, 0, sizeof(frei0r
->params
));
416 sscanf(args
, "%127[^:]:%127[^:]:%1023[^:=]%c%255c",
417 frame_size
, frame_rate
, dl_name
, &c
, frei0r
->params
);
419 if (av_parse_video_size(&frei0r
->w
, &frei0r
->h
, frame_size
) < 0) {
420 av_log(ctx
, AV_LOG_ERROR
, "Invalid frame size: '%s'\n", frame_size
);
421 return AVERROR(EINVAL
);
424 if (av_parse_video_rate(&frame_rate_q
, frame_rate
) < 0 ||
425 frame_rate_q
.den
<= 0 || frame_rate_q
.num
<= 0) {
426 av_log(ctx
, AV_LOG_ERROR
, "Invalid frame rate: '%s'\n", frame_rate
);
427 return AVERROR(EINVAL
);
429 frei0r
->time_base
.num
= frame_rate_q
.den
;
430 frei0r
->time_base
.den
= frame_rate_q
.num
;
432 return frei0r_init(ctx
, dl_name
, F0R_PLUGIN_TYPE_SOURCE
);
435 static int source_config_props(AVFilterLink
*outlink
)
437 AVFilterContext
*ctx
= outlink
->src
;
438 Frei0rContext
*frei0r
= ctx
->priv
;
440 if (av_image_check_size(frei0r
->w
, frei0r
->h
, 0, ctx
) < 0)
441 return AVERROR(EINVAL
);
442 outlink
->w
= frei0r
->w
;
443 outlink
->h
= frei0r
->h
;
444 outlink
->time_base
= frei0r
->time_base
;
446 if (!(frei0r
->instance
= frei0r
->construct(outlink
->w
, outlink
->h
))) {
447 av_log(ctx
, AV_LOG_ERROR
, "Impossible to load frei0r instance");
448 return AVERROR(EINVAL
);
451 return set_params(ctx
, frei0r
->params
);
454 static int source_request_frame(AVFilterLink
*outlink
)
456 Frei0rContext
*frei0r
= outlink
->src
->priv
;
457 AVFilterBufferRef
*picref
= ff_get_video_buffer(outlink
, AV_PERM_WRITE
, outlink
->w
, outlink
->h
);
460 return AVERROR(ENOMEM
);
462 picref
->video
->pixel_aspect
= (AVRational
) {1, 1};
463 picref
->pts
= frei0r
->pts
++;
466 frei0r
->update(frei0r
->instance
, av_rescale_q(picref
->pts
, frei0r
->time_base
, (AVRational
){1,1000}),
467 NULL
, (uint32_t *)picref
->data
[0]);
469 return ff_filter_frame(outlink
, picref
);
472 static const AVFilterPad avfilter_vsrc_frei0r_src_outputs
[] = {
475 .type
= AVMEDIA_TYPE_VIDEO
,
476 .request_frame
= source_request_frame
,
477 .config_props
= source_config_props
482 AVFilter avfilter_vsrc_frei0r_src
= {
483 .name
= "frei0r_src",
484 .description
= NULL_IF_CONFIG_SMALL("Generate a frei0r source."),
486 .priv_size
= sizeof(Frei0rContext
),
490 .query_formats
= query_formats
,
494 .outputs
= avfilter_vsrc_frei0r_src_outputs
,