mirror of https://github.com/mpv-player/mpv
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:
parent
3a9e661e92
commit
d4c1ddd6b1
|
@ -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.
|
||||||
|
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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;
|
||||||
|
|
Loading…
Reference in New Issue