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
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
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_dlopen;
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_vapoursynth;
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_lavfi,
&vf_info_lavfi_bridge,
&vf_info_rotate,
&vf_info_gradfun,
&vf_info_pullup,
@ -129,6 +131,7 @@ const struct m_obj_list vf_obj_list = {
.get_desc = get_desc,
.description = "video filters",
.allow_disable_entries = true,
.allow_unknown_entries = true,
};
// 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,
char **args)
{
const char *lavfi_name = NULL;
char **lavfi_args = NULL;
struct m_obj_desc desc;
if (!m_obj_list_find(&desc, &vf_obj_list, bstr0(name))) {
MP_ERR(c, "Couldn't find video filter '%s'.\n", name);
return NULL;
if (!m_obj_list_find(&desc, &vf_obj_list, bstr0("lavfi-bridge"))) {
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 = (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);
if (!config)
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;
int retcode = vf->info->open(vf);
if (retcode < 1)

View File

@ -65,6 +65,9 @@
#endif
struct vf_priv_s {
// Single filter bridge, instead of a graph.
bool is_bridge;
AVFilterGraph *graph;
AVFilterContext *in;
AVFilterContext *out;
@ -86,10 +89,9 @@ struct vf_priv_s {
char *cfg_graph;
int64_t cfg_sws_flags;
char **cfg_avopts;
};
static const struct vf_priv_s vf_priv_dflt = {
.cfg_sws_flags = SWS_BICUBIC,
char *cfg_filter_name;
char **cfg_filter_opts;
};
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;
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");
return false;
}
destroy_graph(vf);
MP_VERBOSE(vf, "lavfi: create graph: '%s'\n", p->cfg_graph);
AVFilterGraph *graph = avfilter_graph_alloc();
if (!graph)
@ -165,14 +166,46 @@ static bool recreate_graph(struct vf_instance *vf, struct mp_image_params *fmt)
"out", NULL, NULL, graph) < 0)
goto error;
outputs->name = av_strdup("in");
outputs->filter_ctx = in;
if (p->is_bridge) {
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");
inputs->filter_ctx = out;
if (mp_set_avopts(vf->log, filter, p->cfg_filter_opts) < 0)
goto error;
if (graph_parse(graph, p->cfg_graph, inputs, outputs, NULL) < 0)
goto error;
if (avfilter_init_str(filter, NULL) < 0)
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) {
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)
{
struct vf_priv_s *p = vf->priv;
vf->reconfig = reconfig;
vf->filter_ext = filter_ext;
vf->filter_out = filter_out;
@ -391,6 +426,18 @@ static int vf_open(vf_instance_t *vf)
vf->query_format = query_format;
vf->control = control;
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;
}
@ -445,11 +492,32 @@ const vf_info_t vf_info_lavfi = {
.name = "lavfi",
.open = vf_open,
.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,
.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.
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;
struct vf_priv_s *p = talloc(vf, struct vf_priv_s);
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_avopts = lavfi_opts->avopts;
va_list ap;