summaryrefslogtreecommitdiffstats
path: root/video/filter/vf_lavfi.c
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 /video/filter/vf_lavfi.c
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.
Diffstat (limited to 'video/filter/vf_lavfi.c')
-rw-r--r--video/filter/vf_lavfi.c311
1 files changed, 311 insertions, 0 deletions
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
+};