Inspired by MPlayer's af_channels filter and SoX's remix effect.
---
 Changelog                 |    1 +
 libavfilter/Makefile      |    1 +
 libavfilter/af_channels.c |  218 +++++++++++++++++++++++++++++++++++++++++++++
 libavfilter/allfilters.c  |    1 +
 4 files changed, 221 insertions(+), 0 deletions(-)
 create mode 100644 libavfilter/af_channels.c

diff --git a/Changelog b/Changelog
index 898a247..2ee532a 100644
--- a/Changelog
+++ b/Changelog
@@ -24,6 +24,7 @@ version <next>:
 - avprobe output is now standard INI or JSON. The old format can still
   be used with -of old.
 - Indeo Audio decoder
+- audio channel mapping filter
 
 
 version 0.8:
diff --git a/libavfilter/Makefile b/libavfilter/Makefile
index 914f0c6..a3a1f27 100644
--- a/libavfilter/Makefile
+++ b/libavfilter/Makefile
@@ -29,6 +29,7 @@ OBJS-$(CONFIG_AMIX_FILTER)                   += af_amix.o
 OBJS-$(CONFIG_ANULL_FILTER)                  += af_anull.o
 OBJS-$(CONFIG_ASPLIT_FILTER)                 += split.o
 OBJS-$(CONFIG_ASYNCTS_FILTER)                += af_asyncts.o
+OBJS-$(CONFIG_CHANNELS_FILTER)               += af_channels.o
 OBJS-$(CONFIG_RESAMPLE_FILTER)               += af_resample.o
 
 OBJS-$(CONFIG_ANULLSRC_FILTER)               += asrc_anullsrc.o
diff --git a/libavfilter/af_channels.c b/libavfilter/af_channels.c
new file mode 100644
index 0000000..afbe8f1
--- /dev/null
+++ b/libavfilter/af_channels.c
@@ -0,0 +1,218 @@
+/*
+ * Copyright (c) 2012 Google, Inc.
+ *
+ * This file is part of Libav.
+ *
+ * Libav 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.
+ *
+ * Libav 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with Libav; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+/**
+ * @file
+ * audio channel mapping filter
+ */
+
+#include "libavutil/audioconvert.h"
+#include "libavutil/avstring.h"
+#include "libavutil/mathematics.h"
+#include "libavutil/opt.h"
+#include "libavutil/samplefmt.h"
+
+#include "audio.h"
+#include "avfilter.h"
+#include "formats.h"
+#include "internal.h"
+
+#define MAX_CH 8
+typedef struct ChannelsContext {
+    const AVClass   *class;
+
+    AVFilterFormats *formats;
+    AVFilterFormats *sample_rates;
+    AVFilterChannelLayouts *channel_layouts;
+
+    char *mapping_str;
+    char *channel_layout_str;
+    int map[MAX_CH];
+    int nch;
+    int output_layout;
+} ChannelsContext;
+
+#define OFFSET(x) offsetof(ChannelsContext, x)
+#define A AV_OPT_FLAG_AUDIO_PARAM
+static const AVOption options[] = {
+    { "mapping",        "A comma-separated list of input channel numbers in 
the order to be outputted.",
+          OFFSET(mapping_str),        AV_OPT_TYPE_STRING, .flags = A },
+    { "channel_layout", "Output channel layout.",
+          OFFSET(channel_layout_str), AV_OPT_TYPE_STRING, .flags = A },
+    { NULL },
+};
+
+static const AVClass aformat_class = {
+    .class_name = "channels filter",
+    .item_name  = av_default_item_name,
+    .option     = options,
+    .version    = LIBAVUTIL_VERSION_INT,
+};
+
+static av_cold int init(AVFilterContext *ctx, const char *args, void *opaque)
+{
+    ChannelsContext *s = ctx->priv;
+    int ret;
+    const char *mapping;
+    int nch = 0;
+    int res;
+
+    if (!args) {
+        av_log(ctx, AV_LOG_ERROR, "No parameters supplied.\n");
+        return AVERROR(EINVAL);
+    }
+
+    s->class = &aformat_class;
+    av_opt_set_defaults(s);
+
+    if ((ret = av_set_options_string(s, args, "=", ":")) < 0) {
+        av_log(ctx, AV_LOG_ERROR, "Error parsing options string '%s'.\n", 
args);
+        return ret;
+    }
+
+    mapping = s->mapping_str;
+    if (mapping) do {
+        int ch = 0, n = 0;
+        res = sscanf(mapping, "%d%n", &ch, &n);
+        if (res > 0) {
+            mapping += n;
+            if (*mapping == ',')
+                mapping++;
+            s->map[nch++] = ch;
+        }
+    } while (res > 0 && *mapping && nch < MAX_CH);
+    s->nch = nch;
+    s->output_layout = av_get_default_channel_layout(nch);
+
+    if (s->channel_layout_str) {
+        uint64_t fmt;
+        if ((fmt = av_get_channel_layout(s->channel_layout_str)) == 0) {
+            av_log(ctx, AV_LOG_ERROR, "Error parsing channel layout: %s.\n",
+                   s->channel_layout_str);
+            ret = AVERROR(EINVAL);
+            goto fail;
+        }
+        if (!s->nch) {
+            int i;
+            s->nch = av_get_channel_layout_nb_channels(fmt);
+            for (i = 0; i < MAX_CH && i < s->nch; i++)
+                s->map[i] = i;
+        } else if (s->nch != av_get_channel_layout_nb_channels(fmt)) {
+            av_log(ctx, AV_LOG_ERROR, "Output channel layout %s does not match 
the number of channels mapped %d.\n",
+                   s->channel_layout_str, s->nch);
+            ret = AVERROR(EINVAL);
+            goto fail;
+        }
+        s->output_layout = fmt;
+    }
+    ff_add_channel_layout(&s->channel_layouts, s->output_layout);
+
+fail:
+    av_opt_free(s);
+    return ret;
+}
+
+static int query_formats(AVFilterContext *ctx)
+{
+    ChannelsContext *s = ctx->priv;
+
+    ff_set_common_formats(ctx, ff_all_formats(AVMEDIA_TYPE_AUDIO));
+    ff_set_common_samplerates(ctx, ff_all_samplerates());
+    ff_channel_layouts_ref(ff_all_channel_layouts(), 
&ctx->inputs[0]->out_channel_layouts);
+    ff_channel_layouts_ref(s->channel_layouts,       
&ctx->outputs[0]->in_channel_layouts);
+
+    return 0;
+}
+
+#define COPY_INTERLEAVED(type) \
+do { \
+    const type *restrict in = (const type*)buf->extended_data[0]; \
+    type *restrict out = (type*)buf_out->extended_data[0]; \
+    for (ch = 0; ch < nch_out; ch++) { \
+        for (i = 0; i < nb_samples; i++) { \
+            out[i * nch_out + ch] = in[i * nch_in + map[ch]]; \
+        }\
+    }\
+} while (0)
+
+static void filter_samples(AVFilterLink *inlink, AVFilterBufferRef *buf)
+{
+    AVFilterContext  *ctx = inlink->dst;
+    AVFilterLink *outlink = ctx->outputs[0];
+    AVFilterBufferRef *buf_out;
+    int ch, i;
+    const ChannelsContext *s = ctx->priv;
+    const int nch_in = 
av_get_channel_layout_nb_channels(inlink->channel_layout);
+    const int nch_out = s->nch;
+    const int nb_samples = buf->audio->nb_samples;
+    const int *restrict map = s->map;
+
+    buf_out = ff_get_audio_buffer(outlink, AV_PERM_WRITE, 
buf->audio->nb_samples);
+
+    if (buf_out->audio->planar) {
+        for (ch = 0; ch < nch_out; ch++) {
+            memcpy(buf_out->extended_data[ch], buf->extended_data[map[ch]], 
buf->linesize[0]);
+        }
+    } else {
+        switch (inlink->format) {
+        case AV_SAMPLE_FMT_U8:  COPY_INTERLEAVED( uint8_t); break;
+        case AV_SAMPLE_FMT_S16: COPY_INTERLEAVED(uint16_t); break;
+        case AV_SAMPLE_FMT_FLT:
+        case AV_SAMPLE_FMT_S32: COPY_INTERLEAVED(uint32_t); break;
+        case AV_SAMPLE_FMT_DBL: COPY_INTERLEAVED(uint64_t); break;
+        }
+    }
+
+    if (buf->pts != AV_NOPTS_VALUE)
+        buf_out->pts = av_rescale_q(buf->pts, inlink->time_base,
+                                    outlink->time_base);
+    else
+        buf_out->pts = AV_NOPTS_VALUE;
+
+    ff_filter_samples(outlink, buf_out);
+    avfilter_unref_buffer(buf);
+}
+
+static int config_output(AVFilterLink *outlink)
+{
+    AVFilterContext *ctx = outlink->src;
+    ChannelsContext *s   = ctx->priv;
+
+    outlink->channel_layout = s->output_layout;
+
+    return 0;
+}
+
+AVFilter avfilter_af_channels = {
+    .name          = "channels",
+    .description   = NULL_IF_CONFIG_SMALL("Remap audio channels."),
+    .init          = init,
+    .query_formats = query_formats,
+    .priv_size     = sizeof(ChannelsContext),
+
+    .inputs        = (AVFilterPad[]) {{ .name            = "default",
+                                        .type            = AVMEDIA_TYPE_AUDIO,
+                                        .filter_samples  = filter_samples },
+                                      { .name = NULL }},
+    .outputs       = (AVFilterPad[]) {{ .name            = "default",
+                                        .type            = AVMEDIA_TYPE_AUDIO,
+                                        .config_props    = config_output },
+                                      { .name = NULL }},
+};
diff --git a/libavfilter/allfilters.c b/libavfilter/allfilters.c
index 941ca6a..6dd5d7e 100644
--- a/libavfilter/allfilters.c
+++ b/libavfilter/allfilters.c
@@ -39,6 +39,7 @@ void avfilter_register_all(void)
     REGISTER_FILTER (ANULL,       anull,       af);
     REGISTER_FILTER (ASPLIT,      asplit,      af);
     REGISTER_FILTER (ASYNCTS,     asyncts,     af);
+    REGISTER_FILTER (CHANNELS,    channels,    af);
     REGISTER_FILTER (RESAMPLE,    resample,    af);
 
     REGISTER_FILTER (ANULLSRC,    anullsrc,    asrc);
-- 
1.7.7.3

_______________________________________________
libav-devel mailing list
[email protected]
https://lists.libav.org/mailman/listinfo/libav-devel

Reply via email to