a5e8c41c28
It is not used in any filters currently and is inherently evil. If passing binary data to filters is required in the future, it should be done with some AVOptions-based system.
469 lines
16 KiB
C
469 lines
16 KiB
C
/*
|
|
* Copyright (c) 2010 Stefano Sabatini
|
|
* This file is part of Libav.
|
|
*
|
|
* Libav is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
* License as published by the Free Software Foundation; either
|
|
* version 2.1 of the License, or (at your option) any later version.
|
|
*
|
|
* Libav is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* Lesser General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
* License along with Libav; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
|
*/
|
|
|
|
/**
|
|
* @file
|
|
* frei0r wrapper
|
|
*/
|
|
|
|
/* #define DEBUG */
|
|
|
|
#include <dlfcn.h>
|
|
#include <frei0r.h>
|
|
#include "libavutil/avstring.h"
|
|
#include "libavutil/imgutils.h"
|
|
#include "libavutil/mathematics.h"
|
|
#include "libavutil/parseutils.h"
|
|
#include "avfilter.h"
|
|
#include "formats.h"
|
|
#include "internal.h"
|
|
#include "video.h"
|
|
|
|
typedef f0r_instance_t (*f0r_construct_f)(unsigned int width, unsigned int height);
|
|
typedef void (*f0r_destruct_f)(f0r_instance_t instance);
|
|
typedef void (*f0r_deinit_f)(void);
|
|
typedef int (*f0r_init_f)(void);
|
|
typedef void (*f0r_get_plugin_info_f)(f0r_plugin_info_t *info);
|
|
typedef void (*f0r_get_param_info_f)(f0r_param_info_t *info, int param_index);
|
|
typedef void (*f0r_update_f)(f0r_instance_t instance, double time, const uint32_t *inframe, uint32_t *outframe);
|
|
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);
|
|
typedef void (*f0r_set_param_value_f)(f0r_instance_t instance, f0r_param_t param, int param_index);
|
|
typedef void (*f0r_get_param_value_f)(f0r_instance_t instance, f0r_param_t param, int param_index);
|
|
|
|
typedef struct Frei0rContext {
|
|
f0r_update_f update;
|
|
void *dl_handle; /* dynamic library handle */
|
|
f0r_instance_t instance;
|
|
f0r_plugin_info_t plugin_info;
|
|
|
|
f0r_get_param_info_f get_param_info;
|
|
f0r_get_param_value_f get_param_value;
|
|
f0r_set_param_value_f set_param_value;
|
|
f0r_construct_f construct;
|
|
f0r_destruct_f destruct;
|
|
f0r_deinit_f deinit;
|
|
char params[256];
|
|
|
|
/* only used by the source */
|
|
int w, h;
|
|
AVRational time_base;
|
|
uint64_t pts;
|
|
} Frei0rContext;
|
|
|
|
static void *load_sym(AVFilterContext *ctx, const char *sym_name)
|
|
{
|
|
Frei0rContext *frei0r = ctx->priv;
|
|
void *sym = dlsym(frei0r->dl_handle, sym_name);
|
|
if (!sym)
|
|
av_log(ctx, AV_LOG_ERROR, "Could not find symbol '%s' in loaded module\n", sym_name);
|
|
return sym;
|
|
}
|
|
|
|
static int set_param(AVFilterContext *ctx, f0r_param_info_t info, int index, char *param)
|
|
{
|
|
Frei0rContext *frei0r = ctx->priv;
|
|
union {
|
|
double d;
|
|
f0r_param_color_t col;
|
|
f0r_param_position_t pos;
|
|
} val;
|
|
char *tail;
|
|
uint8_t rgba[4];
|
|
|
|
switch (info.type) {
|
|
case F0R_PARAM_BOOL:
|
|
if (!strcmp(param, "y")) val.d = 1.0;
|
|
else if (!strcmp(param, "n")) val.d = 0.0;
|
|
else goto fail;
|
|
break;
|
|
|
|
case F0R_PARAM_DOUBLE:
|
|
val.d = strtod(param, &tail);
|
|
if (*tail || val.d == HUGE_VAL)
|
|
goto fail;
|
|
break;
|
|
|
|
case F0R_PARAM_COLOR:
|
|
if (sscanf(param, "%f/%f/%f", &val.col.r, &val.col.g, &val.col.b) != 3) {
|
|
if (av_parse_color(rgba, param, -1, ctx) < 0)
|
|
goto fail;
|
|
val.col.r = rgba[0] / 255.0;
|
|
val.col.g = rgba[1] / 255.0;
|
|
val.col.b = rgba[2] / 255.0;
|
|
}
|
|
break;
|
|
|
|
case F0R_PARAM_POSITION:
|
|
if (sscanf(param, "%lf/%lf", &val.pos.x, &val.pos.y) != 2)
|
|
goto fail;
|
|
break;
|
|
}
|
|
|
|
frei0r->set_param_value(frei0r->instance, &val, index);
|
|
return 0;
|
|
|
|
fail:
|
|
av_log(ctx, AV_LOG_ERROR, "Invalid value '%s' for parameter '%s'\n",
|
|
param, info.name);
|
|
return AVERROR(EINVAL);
|
|
}
|
|
|
|
static int set_params(AVFilterContext *ctx, const char *params)
|
|
{
|
|
Frei0rContext *frei0r = ctx->priv;
|
|
int i;
|
|
|
|
for (i = 0; i < frei0r->plugin_info.num_params; i++) {
|
|
f0r_param_info_t info;
|
|
char *param;
|
|
int ret;
|
|
|
|
frei0r->get_param_info(&info, i);
|
|
|
|
if (*params) {
|
|
if (!(param = av_get_token(¶ms, ":")))
|
|
return AVERROR(ENOMEM);
|
|
params++; /* skip ':' */
|
|
ret = set_param(ctx, info, i, param);
|
|
av_free(param);
|
|
if (ret < 0)
|
|
return ret;
|
|
}
|
|
|
|
av_log(ctx, AV_LOG_INFO,
|
|
"idx:%d name:'%s' type:%s explanation:'%s' ",
|
|
i, info.name,
|
|
info.type == F0R_PARAM_BOOL ? "bool" :
|
|
info.type == F0R_PARAM_DOUBLE ? "double" :
|
|
info.type == F0R_PARAM_COLOR ? "color" :
|
|
info.type == F0R_PARAM_POSITION ? "position" :
|
|
info.type == F0R_PARAM_STRING ? "string" : "unknown",
|
|
info.explanation);
|
|
|
|
#ifdef DEBUG
|
|
av_log(ctx, AV_LOG_INFO, "value:");
|
|
switch (info.type) {
|
|
void *v;
|
|
double d;
|
|
char s[128];
|
|
f0r_param_color_t col;
|
|
f0r_param_position_t pos;
|
|
|
|
case F0R_PARAM_BOOL:
|
|
v = &d;
|
|
frei0r->get_param_value(frei0r->instance, v, i);
|
|
av_log(ctx, AV_LOG_INFO, "%s", d >= 0.5 && d <= 1.0 ? "y" : "n");
|
|
break;
|
|
case F0R_PARAM_DOUBLE:
|
|
v = &d;
|
|
frei0r->get_param_value(frei0r->instance, v, i);
|
|
av_log(ctx, AV_LOG_INFO, "%f", d);
|
|
break;
|
|
case F0R_PARAM_COLOR:
|
|
v = &col;
|
|
frei0r->get_param_value(frei0r->instance, v, i);
|
|
av_log(ctx, AV_LOG_INFO, "%f/%f/%f", col.r, col.g, col.b);
|
|
break;
|
|
case F0R_PARAM_POSITION:
|
|
v = &pos;
|
|
frei0r->get_param_value(frei0r->instance, v, i);
|
|
av_log(ctx, AV_LOG_INFO, "%lf/%lf", pos.x, pos.y);
|
|
break;
|
|
default: /* F0R_PARAM_STRING */
|
|
v = s;
|
|
frei0r->get_param_value(frei0r->instance, v, i);
|
|
av_log(ctx, AV_LOG_INFO, "'%s'\n", s);
|
|
break;
|
|
}
|
|
#endif
|
|
av_log(ctx, AV_LOG_INFO, "\n");
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void *load_path(AVFilterContext *ctx, const char *prefix, const char *name)
|
|
{
|
|
char path[1024];
|
|
|
|
snprintf(path, sizeof(path), "%s%s%s", prefix, name, SLIBSUF);
|
|
av_log(ctx, AV_LOG_DEBUG, "Looking for frei0r effect in '%s'\n", path);
|
|
return dlopen(path, RTLD_NOW|RTLD_LOCAL);
|
|
}
|
|
|
|
static av_cold int frei0r_init(AVFilterContext *ctx,
|
|
const char *dl_name, int type)
|
|
{
|
|
Frei0rContext *frei0r = ctx->priv;
|
|
f0r_init_f f0r_init;
|
|
f0r_get_plugin_info_f f0r_get_plugin_info;
|
|
f0r_plugin_info_t *pi;
|
|
char *path;
|
|
|
|
/* see: http://piksel.org/frei0r/1.2/spec/1.2/spec/group__pluglocations.html */
|
|
if ((path = av_strdup(getenv("FREI0R_PATH")))) {
|
|
char *p, *ptr = NULL;
|
|
for (p = path; p = strtok_r(p, ":", &ptr); p = NULL)
|
|
if (frei0r->dl_handle = load_path(ctx, p, dl_name))
|
|
break;
|
|
av_free(path);
|
|
}
|
|
if (!frei0r->dl_handle && (path = getenv("HOME"))) {
|
|
char prefix[1024];
|
|
snprintf(prefix, sizeof(prefix), "%s/.frei0r-1/lib/", path);
|
|
frei0r->dl_handle = load_path(ctx, prefix, dl_name);
|
|
}
|
|
if (!frei0r->dl_handle)
|
|
frei0r->dl_handle = load_path(ctx, "/usr/local/lib/frei0r-1/", dl_name);
|
|
if (!frei0r->dl_handle)
|
|
frei0r->dl_handle = load_path(ctx, "/usr/lib/frei0r-1/", dl_name);
|
|
if (!frei0r->dl_handle) {
|
|
av_log(ctx, AV_LOG_ERROR, "Could not find module '%s'\n", dl_name);
|
|
return AVERROR(EINVAL);
|
|
}
|
|
|
|
if (!(f0r_init = load_sym(ctx, "f0r_init" )) ||
|
|
!(f0r_get_plugin_info = load_sym(ctx, "f0r_get_plugin_info")) ||
|
|
!(frei0r->get_param_info = load_sym(ctx, "f0r_get_param_info" )) ||
|
|
!(frei0r->get_param_value = load_sym(ctx, "f0r_get_param_value")) ||
|
|
!(frei0r->set_param_value = load_sym(ctx, "f0r_set_param_value")) ||
|
|
!(frei0r->update = load_sym(ctx, "f0r_update" )) ||
|
|
!(frei0r->construct = load_sym(ctx, "f0r_construct" )) ||
|
|
!(frei0r->destruct = load_sym(ctx, "f0r_destruct" )) ||
|
|
!(frei0r->deinit = load_sym(ctx, "f0r_deinit" )))
|
|
return AVERROR(EINVAL);
|
|
|
|
if (f0r_init() < 0) {
|
|
av_log(ctx, AV_LOG_ERROR, "Could not init the frei0r module");
|
|
return AVERROR(EINVAL);
|
|
}
|
|
|
|
f0r_get_plugin_info(&frei0r->plugin_info);
|
|
pi = &frei0r->plugin_info;
|
|
if (pi->plugin_type != type) {
|
|
av_log(ctx, AV_LOG_ERROR,
|
|
"Invalid type '%s' for the plugin\n",
|
|
pi->plugin_type == F0R_PLUGIN_TYPE_FILTER ? "filter" :
|
|
pi->plugin_type == F0R_PLUGIN_TYPE_SOURCE ? "source" :
|
|
pi->plugin_type == F0R_PLUGIN_TYPE_MIXER2 ? "mixer2" :
|
|
pi->plugin_type == F0R_PLUGIN_TYPE_MIXER3 ? "mixer3" : "unknown");
|
|
return AVERROR(EINVAL);
|
|
}
|
|
|
|
av_log(ctx, AV_LOG_INFO,
|
|
"name:%s author:'%s' explanation:'%s' color_model:%s "
|
|
"frei0r_version:%d version:%d.%d num_params:%d\n",
|
|
pi->name, pi->author, pi->explanation,
|
|
pi->color_model == F0R_COLOR_MODEL_BGRA8888 ? "bgra8888" :
|
|
pi->color_model == F0R_COLOR_MODEL_RGBA8888 ? "rgba8888" :
|
|
pi->color_model == F0R_COLOR_MODEL_PACKED32 ? "packed32" : "unknown",
|
|
pi->frei0r_version, pi->major_version, pi->minor_version, pi->num_params);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static av_cold int filter_init(AVFilterContext *ctx, const char *args)
|
|
{
|
|
Frei0rContext *frei0r = ctx->priv;
|
|
char dl_name[1024], c;
|
|
*frei0r->params = 0;
|
|
|
|
if (args)
|
|
sscanf(args, "%1023[^:=]%c%255c", dl_name, &c, frei0r->params);
|
|
|
|
return frei0r_init(ctx, dl_name, F0R_PLUGIN_TYPE_FILTER);
|
|
}
|
|
|
|
static av_cold void uninit(AVFilterContext *ctx)
|
|
{
|
|
Frei0rContext *frei0r = ctx->priv;
|
|
|
|
if (frei0r->destruct && frei0r->instance)
|
|
frei0r->destruct(frei0r->instance);
|
|
if (frei0r->deinit)
|
|
frei0r->deinit();
|
|
if (frei0r->dl_handle)
|
|
dlclose(frei0r->dl_handle);
|
|
|
|
memset(frei0r, 0, sizeof(*frei0r));
|
|
}
|
|
|
|
static int config_input_props(AVFilterLink *inlink)
|
|
{
|
|
AVFilterContext *ctx = inlink->dst;
|
|
Frei0rContext *frei0r = ctx->priv;
|
|
|
|
if (!(frei0r->instance = frei0r->construct(inlink->w, inlink->h))) {
|
|
av_log(ctx, AV_LOG_ERROR, "Impossible to load frei0r instance");
|
|
return AVERROR(EINVAL);
|
|
}
|
|
|
|
return set_params(ctx, frei0r->params);
|
|
}
|
|
|
|
static int query_formats(AVFilterContext *ctx)
|
|
{
|
|
Frei0rContext *frei0r = ctx->priv;
|
|
AVFilterFormats *formats = NULL;
|
|
|
|
if (frei0r->plugin_info.color_model == F0R_COLOR_MODEL_BGRA8888) {
|
|
ff_add_format(&formats, PIX_FMT_BGRA);
|
|
} else if (frei0r->plugin_info.color_model == F0R_COLOR_MODEL_RGBA8888) {
|
|
ff_add_format(&formats, PIX_FMT_RGBA);
|
|
} else { /* F0R_COLOR_MODEL_PACKED32 */
|
|
static const enum PixelFormat pix_fmts[] = {
|
|
PIX_FMT_BGRA, PIX_FMT_ARGB, PIX_FMT_ABGR, PIX_FMT_ARGB, PIX_FMT_NONE
|
|
};
|
|
formats = ff_make_format_list(pix_fmts);
|
|
}
|
|
|
|
if (!formats)
|
|
return AVERROR(ENOMEM);
|
|
|
|
ff_set_common_formats(ctx, formats);
|
|
return 0;
|
|
}
|
|
|
|
static void null_draw_slice(AVFilterLink *inlink, int y, int h, int slice_dir) { }
|
|
|
|
static void end_frame(AVFilterLink *inlink)
|
|
{
|
|
Frei0rContext *frei0r = inlink->dst->priv;
|
|
AVFilterLink *outlink = inlink->dst->outputs[0];
|
|
AVFilterBufferRef *inpicref = inlink->cur_buf;
|
|
AVFilterBufferRef *outpicref = outlink->out_buf;
|
|
|
|
frei0r->update(frei0r->instance, inpicref->pts * av_q2d(inlink->time_base) * 1000,
|
|
(const uint32_t *)inpicref->data[0],
|
|
(uint32_t *)outpicref->data[0]);
|
|
avfilter_unref_buffer(inpicref);
|
|
ff_draw_slice(outlink, 0, outlink->h, 1);
|
|
ff_end_frame(outlink);
|
|
avfilter_unref_buffer(outpicref);
|
|
}
|
|
|
|
AVFilter avfilter_vf_frei0r = {
|
|
.name = "frei0r",
|
|
.description = NULL_IF_CONFIG_SMALL("Apply a frei0r effect."),
|
|
|
|
.query_formats = query_formats,
|
|
.init = filter_init,
|
|
.uninit = uninit,
|
|
|
|
.priv_size = sizeof(Frei0rContext),
|
|
|
|
.inputs = (AVFilterPad[]) {{ .name = "default",
|
|
.type = AVMEDIA_TYPE_VIDEO,
|
|
.draw_slice = null_draw_slice,
|
|
.config_props = config_input_props,
|
|
.end_frame = end_frame,
|
|
.min_perms = AV_PERM_READ },
|
|
{ .name = NULL}},
|
|
|
|
.outputs = (AVFilterPad[]) {{ .name = "default",
|
|
.type = AVMEDIA_TYPE_VIDEO, },
|
|
{ .name = NULL}},
|
|
};
|
|
|
|
static av_cold int source_init(AVFilterContext *ctx, const char *args)
|
|
{
|
|
Frei0rContext *frei0r = ctx->priv;
|
|
char dl_name[1024], c;
|
|
char frame_size[128] = "";
|
|
char frame_rate[128] = "";
|
|
AVRational frame_rate_q;
|
|
|
|
memset(frei0r->params, 0, sizeof(frei0r->params));
|
|
|
|
if (args)
|
|
sscanf(args, "%127[^:]:%127[^:]:%1023[^:=]%c%255c",
|
|
frame_size, frame_rate, dl_name, &c, frei0r->params);
|
|
|
|
if (av_parse_video_size(&frei0r->w, &frei0r->h, frame_size) < 0) {
|
|
av_log(ctx, AV_LOG_ERROR, "Invalid frame size: '%s'\n", frame_size);
|
|
return AVERROR(EINVAL);
|
|
}
|
|
|
|
if (av_parse_video_rate(&frame_rate_q, frame_rate) < 0 ||
|
|
frame_rate_q.den <= 0 || frame_rate_q.num <= 0) {
|
|
av_log(ctx, AV_LOG_ERROR, "Invalid frame rate: '%s'\n", frame_rate);
|
|
return AVERROR(EINVAL);
|
|
}
|
|
frei0r->time_base.num = frame_rate_q.den;
|
|
frei0r->time_base.den = frame_rate_q.num;
|
|
|
|
return frei0r_init(ctx, dl_name, F0R_PLUGIN_TYPE_SOURCE);
|
|
}
|
|
|
|
static int source_config_props(AVFilterLink *outlink)
|
|
{
|
|
AVFilterContext *ctx = outlink->src;
|
|
Frei0rContext *frei0r = ctx->priv;
|
|
|
|
if (av_image_check_size(frei0r->w, frei0r->h, 0, ctx) < 0)
|
|
return AVERROR(EINVAL);
|
|
outlink->w = frei0r->w;
|
|
outlink->h = frei0r->h;
|
|
outlink->time_base = frei0r->time_base;
|
|
|
|
if (!(frei0r->instance = frei0r->construct(outlink->w, outlink->h))) {
|
|
av_log(ctx, AV_LOG_ERROR, "Impossible to load frei0r instance");
|
|
return AVERROR(EINVAL);
|
|
}
|
|
|
|
return set_params(ctx, frei0r->params);
|
|
}
|
|
|
|
static int source_request_frame(AVFilterLink *outlink)
|
|
{
|
|
Frei0rContext *frei0r = outlink->src->priv;
|
|
AVFilterBufferRef *picref = ff_get_video_buffer(outlink, AV_PERM_WRITE, outlink->w, outlink->h);
|
|
picref->video->pixel_aspect = (AVRational) {1, 1};
|
|
picref->pts = frei0r->pts++;
|
|
picref->pos = -1;
|
|
|
|
ff_start_frame(outlink, avfilter_ref_buffer(picref, ~0));
|
|
frei0r->update(frei0r->instance, av_rescale_q(picref->pts, frei0r->time_base, (AVRational){1,1000}),
|
|
NULL, (uint32_t *)picref->data[0]);
|
|
ff_draw_slice(outlink, 0, outlink->h, 1);
|
|
ff_end_frame(outlink);
|
|
avfilter_unref_buffer(picref);
|
|
|
|
return 0;
|
|
}
|
|
|
|
AVFilter avfilter_vsrc_frei0r_src = {
|
|
.name = "frei0r_src",
|
|
.description = NULL_IF_CONFIG_SMALL("Generate a frei0r source."),
|
|
|
|
.priv_size = sizeof(Frei0rContext),
|
|
.init = source_init,
|
|
.uninit = uninit,
|
|
|
|
.query_formats = query_formats,
|
|
|
|
.inputs = (AVFilterPad[]) {{ .name = NULL}},
|
|
|
|
.outputs = (AVFilterPad[]) {{ .name = "default",
|
|
.type = AVMEDIA_TYPE_VIDEO,
|
|
.request_frame = source_request_frame,
|
|
.config_props = source_config_props },
|
|
{ .name = NULL}},
|
|
};
|