summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorwm4 <wm4@nowhere>2013-03-11 00:16:34 +0100
committerwm4 <wm4@nowhere>2013-04-21 04:39:58 +0200
commit79797181598facbdb055e16fc7c13abbb1aa7db9 (patch)
tree037e17871dcf2dacb651f9dfead4bcc79b74e5f2
parent778e9c06336e77909a2e2bc87aec85dc8cb08811 (diff)
downloadmpv-79797181598facbdb055e16fc7c13abbb1aa7db9.tar.bz2
mpv-79797181598facbdb055e16fc7c13abbb1aa7db9.tar.xz
vf_lavfi: add libavfilter bridge
Requires recent FFmpeg/Libav git versions. Earlier versions will not be supported, as the API is different. (A libavfilter version that uses AVFrame instead of AVFilterBuffer is needed.) Note that this is sort of useless, because the option parser prevents you from making use of the full libavfilter graph syntax. This has to be fixed later. Most of the filter creation code (half of the config() function) has been taken from avplay.c. This code is not based on MPlayer's vf_lavfi. The MPlayer code doesn't compile as it hasn't been updated through multiple libavfilter API changes, making it completely useless as a starting point.
-rw-r--r--DOCS/man/en/vf.rst31
-rw-r--r--Makefile2
-rwxr-xr-xconfigure49
-rw-r--r--core/av_log.c7
-rw-r--r--video/filter/vf.c4
-rw-r--r--video/filter/vf_lavfi.c311
6 files changed, 399 insertions, 5 deletions
diff --git a/DOCS/man/en/vf.rst b/DOCS/man/en/vf.rst
index 4e1e0b733b..1d6a8512d6 100644
--- a/DOCS/man/en/vf.rst
+++ b/DOCS/man/en/vf.rst
@@ -367,6 +367,37 @@ pp[=filter1[:option1[:option2...]]/[-]filter2...]
Horizontal deblocking on luminance only, and switch vertical
deblocking on or off automatically depending on available CPU time.
+lavfi=graph[:sws_flags]
+
+ <graph>
+ The libavfilter graph string. The filter must have a single video input
+ pad and a single video output pad.
+
+ See ``https://ffmpeg.org/ffmpeg-filters.html`` for syntax and available
+ filters.
+
+ *WARNING*: if you want to use the full filter syntax with this option,
+ you have to quote the filter graph in order to prevent mpv's syntax
+ and the filter graph syntax from clashing.
+
+ *EXAMPLE*:
+
+ ``'--vf=lavfi=graph="gradfun=20:30"'``
+ gradfun filter with non-sensical parameters. The ``'`` quotes are
+ for the shell. Otherwise, the shell would remove the ``"`` quotes.
+
+ ``'--vf=lavfi=graph="gradfun=radius=30:strength=20,vflip"'``
+ same as before, but uses named parameters. Also a vflip filter is
+ appended, demonstrating how libavfilter actually takes a graph
+ description and not a single filter.
+
+ <sws_flags>
+ If libavfilter inserts filters for pixel format conversion, this
+ option gives the flags which should be passed to libswscale. This
+ option is numeric and takes a bit-wise combination of ``SWS_`` flags.
+
+ See ``http://git.videolan.org/?p=ffmpeg.git;a=blob;f=libswscale/swscale.h``.
+
noise[=luma[u][t|a][h][p]:chroma[u][t|a][h][p]]
Adds noise.
diff --git a/Makefile b/Makefile
index 6cd62d1bf3..421f243d61 100644
--- a/Makefile
+++ b/Makefile
@@ -117,6 +117,8 @@ SOURCES-$(VDPAU) += video/out/vo_vdpau.c
SOURCES-$(X11) += video/out/vo_x11.c video/out/x11_common.c
SOURCES-$(XV) += video/out/vo_xv.c
+SOURCES-$(VF_LAVFI) += video/filter/vf_lavfi.c
+
ifeq ($(HAVE_AVUTIL_REFCOUNTING),no)
SOURCES-yes += video/decode/lavc_dr1.c
endif
diff --git a/configure b/configure
index 57542fc804..b27d9b8fce 100755
--- a/configure
+++ b/configure
@@ -328,8 +328,9 @@ Optional features:
--disable-libass-osd disable OSD rendering with libass [autodetect]
--enable-rpath enable runtime linker path for extra libs [disabled]
--disable-libpostproc disable postprocess filter (vf_pp) [autodetect]
- --enable-libavdevice enable libavdevice demuxers [disabled]
- --enable-libavfilter enable libavfilter [disabled] [unused]
+ --disable-libavdevice enable libavdevice demuxers [autodetect]
+ --disable-libavfilter enable libavfilter [autodetect]
+ --disable-vf-lavfi enable vf_lavfi libavfilter bridge [audodetect]
Codecs:
--enable-mng enable MNG input support [autodetect]
@@ -487,7 +488,8 @@ _ass=auto
_libass_osd=auto
_rpath=no
libpostproc=auto
-libavfilter=no
+libavfilter=auto
+vf_lavfi=auto
libavdevice=no
_stream_cache=yes
_priority=no
@@ -702,6 +704,8 @@ for ac_option do
--disable-libavdevice) libavdevice=no ;;
--enable-libavfilter) libavfilter=auto ;;
--disable-libavfilter) libavfilter=no ;;
+ --enable-vf-lavfi) vf_lavfi=auto ;;
+ --disable-vf-lavfi) vf_lavfi=no ;;
--enable-enca) _enca=yes ;;
--disable-enca) _enca=no ;;
@@ -2694,11 +2698,25 @@ fi
echores "$_avutil_has_refcounting"
-echocheck "libavfilter >= 3.17.0"
+# libavfilter as it can be used by vf_lavfi is 3.45.101 in FFmpeg, and
+# 3.5.0 in Libav. Completely useless version numbers.
+echocheck "libavfilter"
if test "$libavfilter" = auto ; then
libavfilter=no
- if pkg_config_add "libavfilter >= 3.17.0" ; then
+
+ cat > $TMPC <<EOF
+#include <libavfilter/avfilter.h>
+void vf_next_query_format() {}
+int main(void) {
+ avfilter_register_all();
+ vf_next_query_format();
+ return 0;
+}
+EOF
+ if cc_check `$_pkg_config libavfilter --cflags --libs` && pkg_config_add "libavfilter" ; then
libavfilter=yes
+ else
+ res_comment="not found or broken"
fi
fi
if test "$libavfilter" = yes ; then
@@ -2709,6 +2727,25 @@ fi
echores "$libavfilter"
+echocheck "using libavfilter through vf_lavfi"
+if test "$vf_lavfi" = auto ; then
+ vf_lavfi=no
+ if test "$libavfilter" = yes ; then
+ if test "$_avutil_has_refcounting" = no ; then
+ res_comment="libavutil too old"
+ else
+ vf_lavfi=yes
+ fi
+ fi
+fi
+if test "$vf_lavfi" = yes ; then
+ def_vf_lavfi='#define CONFIG_VF_LAVFI 1'
+else
+ def_vf_lavfi='#undef CONFIG_VF_LAVFI'
+fi
+echores "$vf_lavfi"
+
+
echocheck "libavdevice >= 54.0.0"
if test "$libavdevice" = auto ; then
libavdevice=no
@@ -3044,6 +3081,7 @@ LCMS2 = $_lcms2
LIBPOSTPROC = $libpostproc
LIBAVDEVICE = $libavdevice
LIBAVFILTER = $libavfilter
+VF_LAVFI = $vf_lavfi
LIBSMBCLIENT = $_smb
LIBQUVI = $_libquvi
LIBTHEORA = $_theora
@@ -3200,6 +3238,7 @@ $def_avutil_has_qp_api
$def_libpostproc
$def_libavdevice
$def_libavfilter
+$def_vf_lavfi
/* Audio output drivers */
diff --git a/core/av_log.c b/core/av_log.c
index 9f6f84a39b..37c308be7a 100644
--- a/core/av_log.c
+++ b/core/av_log.c
@@ -37,6 +37,10 @@
#include <libavdevice/avdevice.h>
#endif
+#ifdef CONFIG_LIBAVFILTER
+#include <libavfilter/avfilter.h>
+#endif
+
static int av_log_level_to_mp_level(int av_level)
{
if (av_level > AV_LOG_VERBOSE)
@@ -118,6 +122,9 @@ void init_libav(void)
av_register_all();
avformat_network_init();
+#ifdef CONFIG_LIBAVFILTER
+ avfilter_register_all();
+#endif
#ifdef CONFIG_LIBAVDEVICE
avdevice_register_all();
#endif
diff --git a/video/filter/vf.c b/video/filter/vf.c
index aa1de0848b..257d65e58b 100644
--- a/video/filter/vf.c
+++ b/video/filter/vf.c
@@ -69,6 +69,7 @@ extern const vf_info_t vf_info_sub;
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;
// list of available filters:
static const vf_info_t *const filter_list[] = {
@@ -85,6 +86,9 @@ static const vf_info_t *const filter_list[] = {
#ifdef CONFIG_LIBPOSTPROC
&vf_info_pp,
#endif
+#ifdef CONFIG_VF_LAVFI
+ &vf_info_lavfi,
+#endif
&vf_info_screenshot,
diff --git a/video/filter/vf_lavfi.c b/video/filter/vf_lavfi.c
new file mode 100644
index 0000000000..6889aa0595
--- /dev/null
+++ b/video/filter/vf_lavfi.c
@@ -0,0 +1,311 @@
+/*
+ * This file is part of mpv.
+ *
+ * Filter graph creation code taken from Libav avplay.c (LGPL 2.1 or later)
+ *
+ * mpv is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * mpv 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with mpv. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <inttypes.h>
+#include <assert.h>
+
+#include <libavutil/avstring.h>
+#include <libavutil/mem.h>
+#include <libavutil/mathematics.h>
+#include <libavutil/rational.h>
+#include <libavutil/pixdesc.h>
+#include <libavutil/time.h>
+#include <libswscale/swscale.h>
+#include <libavfilter/avfilter.h>
+#include <libavfilter/avfiltergraph.h>
+#include <libavfilter/buffersink.h>
+#include <libavfilter/buffersrc.h>
+
+#include "core/mp_msg.h"
+#include "core/m_option.h"
+#include "core/m_struct.h"
+
+#include "video/img_format.h"
+#include "video/mp_image.h"
+#include "video/sws_utils.h"
+#include "video/fmt-conversion.h"
+#include "vf.h"
+
+struct vf_priv_s {
+ AVFilterGraph *graph;
+ AVFilterContext *in;
+ AVFilterContext *out;
+
+ AVRational timebase_in;
+ AVRational timebase_out;
+ AVRational par_in;
+
+ // options
+ char *cfg_graph;
+ int64_t cfg_sws_flags;
+};
+
+static const struct vf_priv_s vf_priv_dflt = {
+ .cfg_sws_flags = SWS_BICUBIC,
+};
+
+static void destroy_graph(struct vf_instance *vf)
+{
+ struct vf_priv_s *p = vf->priv;
+ avfilter_graph_free(&p->graph);
+}
+
+// FFmpeg and Libav have slightly different APIs, just enough to cause us
+// unnecessary pain. <Expletive deleted.>
+#if LIBAVFILTER_VERSION_MICRO < 100
+#define graph_parse(graph, filters, inputs, outputs, log_ctx) \
+ avfilter_graph_parse(graph, filters, inputs, outputs, log_ctx)
+#else
+#define graph_parse(graph, filters, inputs, outputs, log_ctx) \
+ avfilter_graph_parse(graph, filters, &(inputs), &(outputs), log_ctx)
+#endif
+
+static AVRational par_from_sar_dar(int width, int height,
+ int d_width, int d_height)
+{
+ return av_div_q((AVRational){d_width, d_height},
+ (AVRational){width, height});
+}
+
+static void dar_from_sar_par(int width, int height, AVRational par,
+ int *out_dw, int *out_dh)
+{
+ *out_dw = width;
+ *out_dh = height;
+ if (par.num != 0 && par.den != 0) {
+ double d = av_q2d(par);
+ if (d > 1.0) {
+ *out_dw *= d;
+ } else {
+ *out_dh /= d;
+ }
+ }
+}
+
+static int config(struct vf_instance *vf, int width, int height,
+ int d_width, int d_height, unsigned int flags,
+ unsigned int fmt)
+{
+ void *tmp = talloc_new(NULL);
+ struct vf_priv_s *p = vf->priv;
+ AVFilterContext *in = NULL, *out = NULL, *f_format = NULL;
+
+ if (bstr0(p->cfg_graph).len == 0) {
+ mp_msg(MSGT_VFILTER, MSGL_FATAL, "lavfi: no filter graph set\n");
+ return 0;
+ }
+
+ destroy_graph(vf);
+ mp_msg(MSGT_VFILTER, MSGL_V, "lavfi: create graph: '%s'\n", p->cfg_graph);
+
+ AVFilterGraph *graph = avfilter_graph_alloc();
+ if (!graph)
+ goto error;
+
+ AVFilterInOut *outputs = avfilter_inout_alloc();
+ AVFilterInOut *inputs = avfilter_inout_alloc();
+ if (!outputs || !inputs)
+ goto error;
+
+ // Build list of acceptable output pixel formats. libavfilter will insert
+ // conversion filters if needed.
+ char *fmtstr = talloc_strdup(tmp, "");
+ for (int n = IMGFMT_START; n < IMGFMT_END; n++) {
+ if (vf_next_query_format(vf, n)) {
+ const char *name = av_get_pix_fmt_name(imgfmt2pixfmt(n));
+ if (name) {
+ const char *s = fmtstr[0] ? ":" : "";
+ fmtstr = talloc_asprintf_append_buffer(fmtstr, "%s%s", s, name);
+ }
+ }
+ }
+
+ char *sws_flags = talloc_asprintf(tmp, "flags=%"PRId64, p->cfg_sws_flags);
+ graph->scale_sws_opts = av_strdup(sws_flags);
+
+ AVRational par = par_from_sar_dar(width, height, d_width, d_height);
+ AVRational timebase = AV_TIME_BASE_Q;
+
+ char *src_args = talloc_asprintf(tmp, "%d:%d:%d:%d:%d:%d:%d",
+ width, height, imgfmt2pixfmt(fmt),
+ timebase.num, timebase.den,
+ par.num, par.den);
+
+ if (avfilter_graph_create_filter(&in, avfilter_get_by_name("buffer"),
+ "src", src_args, NULL, graph) < 0)
+ goto error;
+
+ if (avfilter_graph_create_filter(&out, avfilter_get_by_name("buffersink"),
+ "out", NULL, NULL, graph) < 0)
+ goto error;
+
+ if (avfilter_graph_create_filter(&f_format, avfilter_get_by_name("format"),
+ "format", fmtstr, NULL, graph) < 0)
+ goto error;
+
+ if (avfilter_link(f_format, 0, out, 0) < 0)
+ goto error;
+
+ outputs->name = av_strdup("in");
+ outputs->filter_ctx = in;
+ outputs->pad_idx = 0;
+ outputs->next = NULL;
+
+ inputs->name = av_strdup("out");
+ inputs->filter_ctx = f_format;
+ inputs->pad_idx = 0;
+ inputs->next = NULL;
+
+ if (graph_parse(graph, p->cfg_graph, inputs, outputs, NULL) < 0)
+ goto error;
+
+ if (avfilter_graph_config(graph, NULL) < 0)
+ goto error;
+
+ p->in = in;
+ p->out = out;
+ p->graph = graph;
+
+ assert(out->nb_inputs == 1);
+ assert(in->nb_outputs == 1);
+ AVFilterLink *l_out = out->inputs[0];
+ AVFilterLink *l_in = in->outputs[0];
+
+ p->timebase_in = l_in->time_base;
+ p->timebase_out = l_out->time_base;
+
+ p->par_in = l_in->sample_aspect_ratio;
+
+ int dw, dh;
+ dar_from_sar_par(l_out->w, l_out->h, l_out->sample_aspect_ratio, &dw, &dh);
+
+ talloc_free(tmp);
+ return vf_next_config(vf, l_out->w, l_out->h, dw, dh, flags,
+ pixfmt2imgfmt(l_out->format));
+
+error:
+ mp_msg(MSGT_VFILTER, MSGL_FATAL, "Can't configure libavfilter graph.\n");
+ avfilter_graph_free(&graph);
+ talloc_free(tmp);
+ return 0;
+}
+
+static int query_format(struct vf_instance *vf, unsigned int fmt)
+{
+ // We accept all sws-convertable formats as inputs. Output formats are
+ // handled in config(). The current public libavfilter API doesn't really
+ // allow us to do anything more sophisticated.
+ // This breaks with filters which accept input pixel formats not
+ // supported by libswscale.
+ return mp_sws_supported_format(fmt) ? VFCAP_CSP_SUPPORTED : 0;
+}
+
+static AVFrame *mp_to_av(struct vf_instance *vf, struct mp_image *img)
+{
+ struct vf_priv_s *p = vf->priv;
+ uint64_t pts = img->pts == MP_NOPTS_VALUE ?
+ AV_NOPTS_VALUE : img->pts * av_q2d(av_inv_q(p->timebase_in));
+ AVFrame *frame = mp_image_to_av_frame_and_unref(img);
+ frame->pts = pts;
+ frame->sample_aspect_ratio = p->par_in;
+ return frame;
+}
+
+static struct mp_image *av_to_mp(struct vf_instance *vf, AVFrame *av_frame)
+{
+ struct vf_priv_s *p = vf->priv;
+ struct mp_image *img = mp_image_from_av_frame(av_frame);
+ img->pts = av_frame->pts == AV_NOPTS_VALUE ?
+ MP_NOPTS_VALUE : av_frame->pts * av_q2d(p->timebase_out);
+ av_frame_free(&av_frame);
+ return img;
+}
+
+static int filter_ext(struct vf_instance *vf, struct mp_image *mpi)
+{
+ struct vf_priv_s *p = vf->priv;
+
+ if (!p->graph)
+ return -1;
+
+ AVFrame *frame = mp_to_av(vf, mpi);
+ if (av_buffersrc_add_frame(p->in, frame) < 0) {
+ av_frame_free(&frame);
+ return -1;
+ }
+ av_frame_free(&frame);
+
+ for (;;) {
+ AVFrame *frame = av_frame_alloc();
+ if (av_buffersink_get_frame(p->out, frame) < 0) {
+ // Not an error situation - no more output buffers in queue.
+ av_frame_free(&frame);
+ break;
+ }
+ vf_add_output_frame(vf, av_to_mp(vf, frame));
+ }
+
+ return 0;
+}
+
+static void uninit(struct vf_instance *vf)
+{
+ if (!vf->priv)
+ return;
+ destroy_graph(vf);
+ free(vf->priv);
+}
+
+static int vf_open(vf_instance_t *vf, char *args)
+{
+ //struct vf_priv_s *priv = vf->priv;
+
+ vf->config = config;
+ vf->filter_ext = filter_ext;
+ vf->query_format = query_format;
+ vf->uninit = uninit;
+ return 1;
+}
+
+#undef ST_OFF
+#define ST_OFF(f) M_ST_OFF(struct vf_priv_s,f)
+static const m_option_t vf_opts_fields[] = {
+ {"graph", ST_OFF(cfg_graph), CONF_TYPE_STRING, CONF_MIN, 1},
+ {"sws_flags", ST_OFF(cfg_sws_flags), CONF_TYPE_INT64},
+ {0}
+};
+
+static const m_struct_t vf_opts = {
+ "lavfi",
+ sizeof(struct vf_priv_s),
+ &vf_priv_dflt,
+ vf_opts_fields
+};
+
+const vf_info_t vf_info_lavfi = {
+ "libavfilter bridge",
+ "lavfi",
+ "",
+ "",
+ vf_open,
+ &vf_opts
+};