From d4c1ddd6b166ff2d883a9d2b80c11f9cbf028226 Mon Sep 17 00:00:00 2001 From: wm4 Date: Sun, 2 Apr 2017 18:47:36 +0200 Subject: 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. --- DOCS/man/vf.rst | 7 ++++ video/filter/vf.c | 27 ++++++++++++-- video/filter/vf_lavfi.c | 94 ++++++++++++++++++++++++++++++++++++++++++------- 3 files changed, 113 insertions(+), 15 deletions(-) diff --git a/DOCS/man/vf.rst b/DOCS/man/vf.rst index 28fd7d7e8e..f2bd16033c 100644 --- a/DOCS/man/vf.rst +++ b/DOCS/man/vf.rst @@ -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. diff --git a/video/filter/vf.c b/video/filter/vf.c index fcf273e207..b5383732e3 100644 --- a/video/filter/vf.c +++ b/video/filter/vf.c @@ -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) diff --git a/video/filter/vf_lavfi.c b/video/filter/vf_lavfi.c index d79bfd4011..91930e3b02 100644 --- a/video/filter/vf_lavfi.c +++ b/video/filter/vf_lavfi.c @@ -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; + + if (mp_set_avopts(vf->log, filter, p->cfg_filter_opts) < 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); - inputs->name = av_strdup("out"); - inputs->filter_ctx = out; + outputs->name = av_strdup("in"); + outputs->filter_ctx = in; - if (graph_parse(graph, p->cfg_graph, inputs, outputs, NULL) < 0) - goto error; + 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; -- cgit v1.2.3