[FFmpeg-devel] [PATCH] lavfi: port MPlayer's af_pan filter to libavfilter.

Clément Bœsch ubitux at gmail.com
Sat Nov 5 14:48:41 CET 2011


---
I wasn't able to contact the original author for a relicensing (I tried 5 or 6
mails I could find, but none seemed valid), so I kept the GPL and the original
(invalid) mail. It's OK with me to relicense to any preferred license if you
are able to contact Anders Johansson and find an agreement with him.
---
 Changelog                |    1 +
 configure                |    1 +
 doc/filters.texi         |   36 ++++++++++++
 libavfilter/Makefile     |    1 +
 libavfilter/af_pan.c     |  144 ++++++++++++++++++++++++++++++++++++++++++++++
 libavfilter/allfilters.c |    1 +
 libavfilter/avfilter.h   |    2 +-
 7 files changed, 185 insertions(+), 1 deletions(-)
 create mode 100644 libavfilter/af_pan.c

diff --git a/Changelog b/Changelog
index 7bc4764..11f44bd 100644
--- a/Changelog
+++ b/Changelog
@@ -75,6 +75,7 @@ easier to use. The changes are:
 - new ffmpeg option: -map_channel
 - volume audio filter added
 - earwax audio filter added
+- pan audio filter added
 
 
 version 0.8:
diff --git a/configure b/configure
index 509a822..a4a50b8 100755
--- a/configure
+++ b/configure
@@ -1608,6 +1608,7 @@ mp_filter_deps="gpl avcodec"
 mptestsrc_filter_deps="gpl"
 negate_filter_deps="lut_filter"
 ocv_filter_deps="libopencv"
+pan_filter_deps="gpl"
 scale_filter_deps="swscale"
 yadif_filter_deps="gpl"
 
diff --git a/doc/filters.texi b/doc/filters.texi
index 92ef589..2603f38 100644
--- a/doc/filters.texi
+++ b/doc/filters.texi
@@ -183,6 +183,42 @@ 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{n}:L0A:L0B:L0C:...L1A:L1B:L1C:...LnA:LnB:LnC:...]"
+
+ at table @option
+ at item n
+number of output 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 (you have @var{n} output
+levels per group). 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=2: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..369a795
--- /dev/null
+++ b/libavfilter/af_pan.c
@@ -0,0 +1,144 @@
+/*
+ * Copyright (C) 2002 Anders Johansson <ajh at atri.curtin.edu.au>
+ * Copyright (C) 2011 Clément Bœsch <ubitux at gmail.com>
+ *
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg 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.
+ *
+ * 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 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.
+ */
+
+#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 {
+    int64_t out_channels_layout;
+    float gain_level[SWR_CH_MAX][SWR_CH_MAX];
+} PanContext;
+
+static av_cold int init(AVFilterContext *ctx, const char *args, void *opaque)
+{
+    int i, j;
+    PanContext * const pan = ctx->priv;
+    int out_ch_id = 0, in_ch_id = 0, nb_output_channels;
+    char *arg0 = av_strdup(args);
+
+    // first parameter is the number of channels...
+    nb_output_channels = strtol(args, NULL, 10);
+    pan->out_channels_layout = av_get_default_channel_layout(nb_output_channels);
+
+    // ...followed by the channel gain levels
+    while (in_ch_id < SWR_CH_MAX && (args = strchr(args, ':')) != NULL) {
+        pan->gain_level[out_ch_id++][in_ch_id] = strtof(args + 1, (char**)&args);
+        if (out_ch_id >= nb_output_channels) {
+            out_ch_id = 0;
+            in_ch_id++;
+        }
+    }
+    if (args)
+        av_log(ctx, AV_LOG_WARNING, "max of %d channels reached, "
+                                    "ignoring end of buffer\n", SWR_CH_MAX);
+
+    // summary
+    for (j = 0; j < 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]);
+        av_log(ctx, AV_LOG_INFO, "\n");
+    }
+
+    av_free(arg0);
+    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 */
+    int nb_input_channels = av_get_channel_layout_nb_channels(inlink->channel_layout);
+    const int16_t *in     = (int16_t*)insamples->data[0];
+    const int16_t *in_end = in + n*nb_input_channels;
+
+    /* output */
+    AVFilterLink * const outlink = inlink->dst->outputs[0];
+    int nb_output_channels = av_get_channel_layout_nb_channels(pan->out_channels_layout);
+    AVFilterBufferRef *outsamples = avfilter_get_audio_buffer(outlink, AV_PERM_WRITE, n);
+    int16_t *out = (int16_t*)outsamples->data[0];
+
+    /* panning */
+    while (in < in_end) {
+        for (o = 0; o < nb_output_channels; o++) {
+            register float x = 0.0;
+            for (i = 0; i < nb_input_channels; i++)
+                x += in[i] * pan->gain_level[o][i];
+            *out++ = x;
+        }
+        in += nb_input_channels;
+    }
+
+    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    = (AVFilterPad[]) {{ .name             = "default",
+                                    .type             = AVMEDIA_TYPE_AUDIO,
+                                    .filter_samples   = filter_samples,
+                                    .min_perms        = AV_PERM_READ, },
+                                  { .name = NULL}},
+    .outputs   = (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.2



More information about the ffmpeg-devel mailing list