[FFmpeg-devel] [PATCH] lavfi: reimplement MPlayer's af_pan filter for libavfilter.

Nicolas George nicolas.george at normalesup.org
Sun Nov 6 17:28:14 CET 2011


From: Clément Bœsch <ubitux at gmail.com>


Signed-off-by: Nicolas George <nicolas.george at normalesup.org>
---
 Changelog                |    1 +
 doc/filters.texi         |   37 +++++++++++
 libavfilter/Makefile     |    1 +
 libavfilter/af_pan.c     |  157 ++++++++++++++++++++++++++++++++++++++++++++++
 libavfilter/allfilters.c |    1 +
 libavfilter/avfilter.h   |    2 +-
 6 files changed, 198 insertions(+), 1 deletions(-)
 create mode 100644 libavfilter/af_pan.c

diff --git a/Changelog b/Changelog
index 3fb8e65..c704de6 100644
--- a/Changelog
+++ b/Changelog
@@ -77,6 +77,7 @@ easier to use. The changes are:
 - earwax audio filter added
 - libv4l2 support (--enable-libv4l2)
 - TLS/SSL and HTTPS protocol support
+- pan audio filter added
 
 
 version 0.8:
diff --git a/doc/filters.texi b/doc/filters.texi
index f8a2d1b..8a4b838 100644
--- a/doc/filters.texi
+++ b/doc/filters.texi
@@ -183,6 +183,43 @@ The shown line contains a sequence of key/value pairs of the form
 
 A description of each shown parameter follows:
 
+ at section pan
+
+Mix channels with specific gain levels. The filter accepts the number of output
+channels followed by a set of coefficients. The number of those gain levels
+depends on the number of output channels of the given layout.
+
+The filter accepts parameters of the form:
+"@var{l}:L0A:L0B:L0C:...L1A:L1B:L1C:...LnA:LnB:LnC:...]"
+
+ at table @option
+ at item l
+output channel layout or number of channels
+
+ at item Lij
+gain level (as a factor) of input channel i to mix in output channel j.
+ at end table
+
+Channel gain levels are grouped by input channel (there are as many output
+levels per group as there are output channels). Using this filter will print
+out a summary of the mixing grouped by output channels. Note that all the
+input levels are not mandatory, but if you do not specify some of them, 0.0
+is assumed.
+
+For example, if you want to down-mix from stereo to mono, but with a bigger
+factor for the left channel:
+ at example
+af pan=1:0.9:0.1
+ at end example
+
+A customized down-mix from 5.1 to stereo could be done with:
+ at example
+pan=stereo:0.4:0:0:0.4:0.2:0:0:0.2:0.3:0.3:0.1:0.1
+ at end example
+
+Note that FFmpeg integrates a default down-mix (and up-mix) system that should
+be preferred (see "-ac" option) unless you have very specific needs.
+
 @table @option
 @item n
 sequential number of the input frame, starting from 0
diff --git a/libavfilter/Makefile b/libavfilter/Makefile
index bb30ccb..aeb5575 100644
--- a/libavfilter/Makefile
+++ b/libavfilter/Makefile
@@ -30,6 +30,7 @@ OBJS-$(CONFIG_ARESAMPLE_FILTER)              += af_aresample.o
 OBJS-$(CONFIG_ASHOWINFO_FILTER)              += af_ashowinfo.o
 OBJS-$(CONFIG_EARWAX_FILTER)                 += af_earwax.o
 OBJS-$(CONFIG_VOLUME_FILTER)                 += af_volume.o
+OBJS-$(CONFIG_PAN_FILTER)                    += af_pan.o
 
 OBJS-$(CONFIG_ABUFFER_FILTER)                += asrc_abuffer.o
 OBJS-$(CONFIG_AEVALSRC_FILTER)               += asrc_aevalsrc.o
diff --git a/libavfilter/af_pan.c b/libavfilter/af_pan.c
new file mode 100644
index 0000000..045e4f2
--- /dev/null
+++ b/libavfilter/af_pan.c
@@ -0,0 +1,157 @@
+/*
+ * Copyright (C) 2002 Anders Johansson <ajh at atri.curtin.edu.au>
+ * Copyright (C) 2011 Clément Bœsch <ubitux at gmail.com>
+ * Copyright (C) 2011 Nicolas George <nicolas.george at normalesup.org>
+ *
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * FFmpeg 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 Lesser General Public
+ * License along with FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+/**
+ * @file
+ * Audio panning filter (channels mixing)
+ * Original code written by Anders Johansson for MPlayer,
+ * reimplemented for FFmpeg.
+ */
+
+#include <stdlib.h>
+#include "libavcodec/avcodec.h"
+#include "libavutil/avstring.h"
+#include "libswresample/swresample.h" // only for SWR_CH_MAX
+#include "avfilter.h"
+#include "internal.h"
+
+typedef struct {
+    int nb_input_channels;
+    int nb_output_channels;
+    int64_t out_channels_layout;
+    int gain_level[SWR_CH_MAX][SWR_CH_MAX]; // 1:7:8 fixed point
+} PanContext;
+
+static av_cold int init(AVFilterContext *ctx, const char *args0, void *opaque)
+{
+    int i, j;
+    PanContext * const pan = ctx->priv;
+    int out_ch_id = 0, in_ch_id = 0;
+    char *arg, *tokenizer, *args = av_strdup(args0);
+
+    if (!args)
+        return AVERROR(ENOMEM);
+    arg = av_strtok(args, ":", &tokenizer);
+    pan->out_channels_layout = av_get_channel_layout(arg);
+    if (!pan->out_channels_layout) {
+        av_log(ctx, AV_LOG_ERROR, "unknown channel layout \"%s\"\n", arg);
+        return AVERROR(EINVAL);
+    }
+    pan->nb_output_channels = av_get_channel_layout_nb_channels(pan->out_channels_layout);
+
+    while (in_ch_id < SWR_CH_MAX && (arg = av_strtok(NULL, ":", &tokenizer))) {
+        pan->gain_level[out_ch_id++][in_ch_id] = 256 * strtof(arg, NULL);
+        if (out_ch_id >= pan->nb_output_channels) {
+            out_ch_id = 0;
+            in_ch_id++;
+        }
+    }
+    if (tokenizer)
+        av_log(ctx, AV_LOG_WARNING, "max of %d channels reached, "
+                                    "ignoring end of buffer\n", SWR_CH_MAX);
+    pan->nb_input_channels = -1;
+
+    // summary
+    for (j = 0; j < pan->nb_output_channels; j++) {
+        av_log(ctx, AV_LOG_INFO, "output channel %d:", j);
+        for (i = 0; i < in_ch_id; i++)
+            av_log(ctx, AV_LOG_INFO, " %.1f", pan->gain_level[j][i] / 256.0);
+        av_log(ctx, AV_LOG_INFO, "\n");
+    }
+
+    av_free(args);
+    return 0;
+}
+
+static void filter_samples(AVFilterLink *inlink, AVFilterBufferRef *insamples)
+{
+    PanContext * const pan = inlink->dst->priv;
+    int i, o, n = insamples->audio->nb_samples;
+
+    /* input */
+    const int16_t *in     = (int16_t*)insamples->data[0];
+    const int16_t *in_end = in + n * pan->nb_input_channels;
+
+    /* output */
+    AVFilterLink * const outlink = inlink->dst->outputs[0];
+    AVFilterBufferRef *outsamples = avfilter_get_audio_buffer(outlink, AV_PERM_WRITE, n);
+    int16_t *out = (int16_t*)outsamples->data[0];
+
+    if (pan->nb_input_channels < 0)
+        pan->nb_input_channels = av_get_channel_layout_nb_channels(inlink->channel_layout);
+
+    for (; in < in_end; in += pan->nb_input_channels) {
+        for (o = 0; o < pan->nb_output_channels; o++) {
+            int v = 0;
+            for (i = 0; i < pan->nb_input_channels; i++)
+                v += pan->gain_level[o][i] * in[i];
+            *(out++) = v >> 8;
+        }
+    }
+
+    avfilter_filter_samples(outlink, outsamples);
+}
+
+static int query_formats(AVFilterContext *ctx)
+{
+    PanContext *pan = ctx->priv;
+    AVFilterLink *inlink  = ctx->inputs[0];
+    AVFilterLink *outlink = ctx->outputs[0];
+    AVFilterFormats *formats;
+
+    const enum AVSampleFormat sample_fmts[] = {AV_SAMPLE_FMT_S16, -1};
+    const int                packing_fmts[] = {AVFILTER_PACKED,   -1};
+
+    avfilter_set_common_sample_formats (ctx, avfilter_make_format_list(sample_fmts));
+    avfilter_set_common_packing_formats(ctx, avfilter_make_format_list(packing_fmts));
+
+    // inlink supports any channel layout
+    formats = avfilter_make_all_channel_layouts();
+    avfilter_formats_ref(formats, &inlink->out_chlayouts);
+
+    // outlink supports only requested output channel layout
+    formats = NULL;
+    avfilter_add_format(&formats, pan->out_channels_layout);
+    avfilter_formats_ref(formats, &outlink->in_chlayouts);
+    return 0;
+}
+
+AVFilter avfilter_af_pan = {
+    .name          = "pan",
+    .description   = NULL_IF_CONFIG_SMALL("Remix channels with coefficients (panning)"),
+    .priv_size     = sizeof(PanContext),
+    .init          = init,
+    .query_formats = query_formats,
+
+    .inputs    = (const AVFilterPad[]) {
+        { .name             = "default",
+          .type             = AVMEDIA_TYPE_AUDIO,
+          .filter_samples   = filter_samples,
+          .min_perms        = AV_PERM_READ, },
+        { .name = NULL}
+    },
+    .outputs   = (const AVFilterPad[]) {
+        { .name             = "default",
+          .type             = AVMEDIA_TYPE_AUDIO, },
+        { .name = NULL}
+    },
+};
diff --git a/libavfilter/allfilters.c b/libavfilter/allfilters.c
index de54bbb..d36ede7 100644
--- a/libavfilter/allfilters.c
+++ b/libavfilter/allfilters.c
@@ -41,6 +41,7 @@ void avfilter_register_all(void)
     REGISTER_FILTER (ASHOWINFO,   ashowinfo,   af);
     REGISTER_FILTER (EARWAX,      earwax,      af);
     REGISTER_FILTER (VOLUME,      volume,      af);
+    REGISTER_FILTER (PAN,         pan,         af);
 
     REGISTER_FILTER (ABUFFER,     abuffer,     asrc);
     REGISTER_FILTER (AEVALSRC,    aevalsrc,    asrc);
diff --git a/libavfilter/avfilter.h b/libavfilter/avfilter.h
index b8205fd..bebf0ed 100644
--- a/libavfilter/avfilter.h
+++ b/libavfilter/avfilter.h
@@ -29,7 +29,7 @@
 #include "libavutil/rational.h"
 
 #define LIBAVFILTER_VERSION_MAJOR  2
-#define LIBAVFILTER_VERSION_MINOR 47
+#define LIBAVFILTER_VERSION_MINOR 48
 #define LIBAVFILTER_VERSION_MICRO  0
 
 #define LIBAVFILTER_VERSION_INT AV_VERSION_INT(LIBAVFILTER_VERSION_MAJOR, \
-- 
1.7.7.1



More information about the ffmpeg-devel mailing list