video: add automatic libavfilter bridge to option parsing

Now you can for example do "--vf=hue=h=60" - there is no "hue" filter in
mpv, so libavfilter's will be used.

This has certain caveats (see manpage).

The point of this is providing a relatively smooth transition path to
removing our own filter stuff.
This commit is contained in:
wm4 2017-04-02 18:47:36 +02:00
parent 3a9e661e92
commit d4c1ddd6b1
3 changed files with 113 additions and 15 deletions

View File

@ -59,6 +59,13 @@ normal filter parameters.
in mpv (such as filters which deal with mpv specifics, or which are in mpv (such as filters which deal with mpv specifics, or which are
implemented in mpv only). implemented in mpv only).
If a filter is not builtin, the ``lavfi-bridge`` will be automatically
tried. Keep in mind that this filter does not support positional arguments
like ``--vf=name=arg1:arg2``. Instead, you must use
``--vf=name=arg1name=arg1value:...``. This bridge also does not support
help output, and does not verify parameters before the filter is actually
used.
Video filters are managed in lists. There are a few commands to manage the Video filters are managed in lists. There are a few commands to manage the
filter list. filter list.

View File

@ -56,6 +56,7 @@ extern const vf_info_t vf_info_yadif;
extern const vf_info_t vf_info_stereo3d; extern const vf_info_t vf_info_stereo3d;
extern const vf_info_t vf_info_dlopen; extern const vf_info_t vf_info_dlopen;
extern const vf_info_t vf_info_lavfi; extern const vf_info_t vf_info_lavfi;
extern const vf_info_t vf_info_lavfi_bridge;
extern const vf_info_t vf_info_vaapi; extern const vf_info_t vf_info_vaapi;
extern const vf_info_t vf_info_vapoursynth; extern const vf_info_t vf_info_vapoursynth;
extern const vf_info_t vf_info_vapoursynth_lazy; extern const vf_info_t vf_info_vapoursynth_lazy;
@ -74,6 +75,7 @@ static const vf_info_t *const filter_list[] = {
&vf_info_mirror, &vf_info_mirror,
&vf_info_lavfi, &vf_info_lavfi,
&vf_info_lavfi_bridge,
&vf_info_rotate, &vf_info_rotate,
&vf_info_gradfun, &vf_info_gradfun,
&vf_info_pullup, &vf_info_pullup,
@ -129,6 +131,7 @@ const struct m_obj_list vf_obj_list = {
.get_desc = get_desc, .get_desc = get_desc,
.description = "video filters", .description = "video filters",
.allow_disable_entries = true, .allow_disable_entries = true,
.allow_unknown_entries = true,
}; };
// Try the cmd on each filter (starting with the first), and stop at the first // Try the cmd on each filter (starting with the first), and stop at the first
@ -241,10 +244,17 @@ void vf_print_filter_chain(struct vf_chain *c, int msglevel,
static struct vf_instance *vf_open(struct vf_chain *c, const char *name, static struct vf_instance *vf_open(struct vf_chain *c, const char *name,
char **args) char **args)
{ {
const char *lavfi_name = NULL;
char **lavfi_args = NULL;
struct m_obj_desc desc; struct m_obj_desc desc;
if (!m_obj_list_find(&desc, &vf_obj_list, bstr0(name))) { if (!m_obj_list_find(&desc, &vf_obj_list, bstr0(name))) {
MP_ERR(c, "Couldn't find video filter '%s'.\n", name); if (!m_obj_list_find(&desc, &vf_obj_list, bstr0("lavfi-bridge"))) {
return NULL; MP_ERR(c, "Couldn't find video filter '%s'.\n", name);
return NULL;
}
lavfi_name = name;
lavfi_args = args;
args = NULL;
} }
vf_instance_t *vf = talloc_zero(NULL, struct vf_instance); vf_instance_t *vf = talloc_zero(NULL, struct vf_instance);
*vf = (vf_instance_t) { *vf = (vf_instance_t) {
@ -260,6 +270,19 @@ static struct vf_instance *vf_open(struct vf_chain *c, const char *name,
name, c->opts->vf_defs, args); name, c->opts->vf_defs, args);
if (!config) if (!config)
goto error; goto error;
if (lavfi_name) {
// Pass the filter arguments as proper sub-options to the bridge filter.
struct m_config_option *name_opt = m_config_get_co(config, bstr0("name"));
assert(name_opt);
assert(name_opt->opt->type == &m_option_type_string);
if (m_config_set_option_raw(config, name_opt, &lavfi_name, 0) < 0)
goto error;
struct m_config_option *opts = m_config_get_co(config, bstr0("opts"));
assert(opts);
assert(opts->opt->type == &m_option_type_keyvalue_list);
if (m_config_set_option_raw(config, opts, &lavfi_args, 0) < 0)
goto error;
}
vf->priv = config->optstruct; vf->priv = config->optstruct;
int retcode = vf->info->open(vf); int retcode = vf->info->open(vf);
if (retcode < 1) if (retcode < 1)

View File

@ -65,6 +65,9 @@
#endif #endif
struct vf_priv_s { struct vf_priv_s {
// Single filter bridge, instead of a graph.
bool is_bridge;
AVFilterGraph *graph; AVFilterGraph *graph;
AVFilterContext *in; AVFilterContext *in;
AVFilterContext *out; AVFilterContext *out;
@ -86,10 +89,9 @@ struct vf_priv_s {
char *cfg_graph; char *cfg_graph;
int64_t cfg_sws_flags; int64_t cfg_sws_flags;
char **cfg_avopts; char **cfg_avopts;
};
static const struct vf_priv_s vf_priv_dflt = { char *cfg_filter_name;
.cfg_sws_flags = SWS_BICUBIC, char **cfg_filter_opts;
}; };
static void destroy_graph(struct vf_instance *vf) static void destroy_graph(struct vf_instance *vf)
@ -113,13 +115,12 @@ static bool recreate_graph(struct vf_instance *vf, struct mp_image_params *fmt)
AVFilterContext *in = NULL, *out = NULL; AVFilterContext *in = NULL, *out = NULL;
int ret; int ret;
if (bstr0(p->cfg_graph).len == 0) { if (!p->is_bridge && bstr0(p->cfg_graph).len == 0) {
MP_FATAL(vf, "lavfi: no filter graph set\n"); MP_FATAL(vf, "lavfi: no filter graph set\n");
return false; return false;
} }
destroy_graph(vf); destroy_graph(vf);
MP_VERBOSE(vf, "lavfi: create graph: '%s'\n", p->cfg_graph);
AVFilterGraph *graph = avfilter_graph_alloc(); AVFilterGraph *graph = avfilter_graph_alloc();
if (!graph) if (!graph)
@ -165,14 +166,46 @@ static bool recreate_graph(struct vf_instance *vf, struct mp_image_params *fmt)
"out", NULL, NULL, graph) < 0) "out", NULL, NULL, graph) < 0)
goto error; goto error;
outputs->name = av_strdup("in"); if (p->is_bridge) {
outputs->filter_ctx = in; AVFilterContext *filter = avfilter_graph_alloc_filter(graph,
avfilter_get_by_name(p->cfg_filter_name), "filter");
if (!filter)
goto error;
inputs->name = av_strdup("out"); if (mp_set_avopts(vf->log, filter, p->cfg_filter_opts) < 0)
inputs->filter_ctx = out; goto error;
if (graph_parse(graph, p->cfg_graph, inputs, outputs, NULL) < 0) if (avfilter_init_str(filter, NULL) < 0)
goto error; goto error;
// Yep, we have to manually link those filters.
if (filter->nb_inputs != 1 ||
avfilter_pad_get_type(filter->input_pads, 0) != AVMEDIA_TYPE_VIDEO ||
filter->nb_outputs != 1 ||
avfilter_pad_get_type(filter->output_pads, 0) != AVMEDIA_TYPE_VIDEO)
{
MP_ERR(vf, "The filter is required to have 1 video input pad and "
"1 video output pad.\n");
goto error;
}
if (avfilter_link(in, 0, filter, 0) < 0 ||
avfilter_link(filter, 0, out, 0) < 0)
{
MP_ERR(vf, "Failed to link filter.\n");
goto error;
}
} else {
MP_VERBOSE(vf, "lavfi: create graph: '%s'\n", p->cfg_graph);
outputs->name = av_strdup("in");
outputs->filter_ctx = in;
inputs->name = av_strdup("out");
inputs->filter_ctx = out;
if (graph_parse(graph, p->cfg_graph, inputs, outputs, NULL) < 0)
goto error;
}
if (vf->hwdec_devs) { if (vf->hwdec_devs) {
struct mp_hwdec_ctx *hwdec = hwdec_devices_get_first(vf->hwdec_devs); struct mp_hwdec_ctx *hwdec = hwdec_devices_get_first(vf->hwdec_devs);
@ -384,6 +417,8 @@ static void uninit(struct vf_instance *vf)
static int vf_open(vf_instance_t *vf) static int vf_open(vf_instance_t *vf)
{ {
struct vf_priv_s *p = vf->priv;
vf->reconfig = reconfig; vf->reconfig = reconfig;
vf->filter_ext = filter_ext; vf->filter_ext = filter_ext;
vf->filter_out = filter_out; vf->filter_out = filter_out;
@ -391,6 +426,18 @@ static int vf_open(vf_instance_t *vf)
vf->query_format = query_format; vf->query_format = query_format;
vf->control = control; vf->control = control;
vf->uninit = uninit; vf->uninit = uninit;
if (p->is_bridge) {
if (!p->cfg_filter_name) {
MP_ERR(vf, "Filter name not set!\n");
return 0;
}
if (!avfilter_get_by_name(p->cfg_filter_name)) {
MP_ERR(vf, "libavfilter filter '%s' not found!\n", p->cfg_filter_name);
return 0;
}
}
return 1; return 1;
} }
@ -445,11 +492,32 @@ const vf_info_t vf_info_lavfi = {
.name = "lavfi", .name = "lavfi",
.open = vf_open, .open = vf_open,
.priv_size = sizeof(struct vf_priv_s), .priv_size = sizeof(struct vf_priv_s),
.priv_defaults = &vf_priv_dflt, .priv_defaults = &(const struct vf_priv_s){
.cfg_sws_flags = SWS_BICUBIC,
},
.options = vf_opts_fields, .options = vf_opts_fields,
.print_help = print_help, .print_help = print_help,
}; };
const vf_info_t vf_info_lavfi_bridge = {
.description = "libavfilter bridge (explicit options)",
.name = "lavfi-bridge",
.open = vf_open,
.priv_size = sizeof(struct vf_priv_s),
.options = (const m_option_t[]) {
OPT_STRING("name", cfg_filter_name, M_OPT_MIN, .min = 1),
OPT_KEYVALUELIST("opts", cfg_filter_opts, 0),
OPT_INT64("sws-flags", cfg_sws_flags, 0),
OPT_KEYVALUELIST("o", cfg_avopts, 0),
{0}
},
.priv_defaults = &(const struct vf_priv_s){
.is_bridge = true,
.cfg_sws_flags = SWS_BICUBIC,
},
.print_help = print_help,
};
// The following code is for the old filters wrapper code. // The following code is for the old filters wrapper code.
struct vf_lw_opts { struct vf_lw_opts {
@ -497,7 +565,7 @@ int vf_lw_set_graph(struct vf_instance *vf, struct vf_lw_opts *lavfi_opts,
void *old_priv = vf->priv; void *old_priv = vf->priv;
struct vf_priv_s *p = talloc(vf, struct vf_priv_s); struct vf_priv_s *p = talloc(vf, struct vf_priv_s);
vf->priv = p; vf->priv = p;
*p = vf_priv_dflt; *p = *(const struct vf_priv_s *)vf_info_lavfi.priv_defaults;
p->cfg_sws_flags = lavfi_opts->sws_flags; p->cfg_sws_flags = lavfi_opts->sws_flags;
p->cfg_avopts = lavfi_opts->avopts; p->cfg_avopts = lavfi_opts->avopts;
va_list ap; va_list ap;