lavfi: add channelsplit audio filter.
authorAnton Khirnov <anton@khirnov.net>
Wed, 30 May 2012 11:59:30 +0000 (13:59 +0200)
committerAnton Khirnov <anton@khirnov.net>
Tue, 12 Jun 2012 19:38:59 +0000 (21:38 +0200)
Changelog
doc/filters.texi
libavfilter/Makefile
libavfilter/af_channelsplit.c [new file with mode: 0644]
libavfilter/allfilters.c
libavfilter/formats.c
libavfilter/formats.h
libavfilter/version.h

index 898a247..b80ff88 100644 (file)
--- 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
+- channelsplit audio filter
 
 
 version 0.8:
index 0179682..f17dad8 100644 (file)
@@ -207,6 +207,31 @@ Maximum compensation in samples per second.
 
 @end table
 
+@section channelsplit
+Split each channel in input audio stream into a separate output stream.
+
+This filter accepts the following named parameters:
+@table @option
+@item channel_layout
+Channel layout of the input stream. Default is "stereo".
+@end table
+
+For example, assuming a stereo input MP3 file
+@example
+avconv -i in.mp3 -filter_complex channelsplit out.mkv
+@end example
+will create an output Matroska file with two audio streams, one containing only
+the left channel and the other the right channel.
+
+To split a 5.1 WAV file into per-channel files
+@example
+avconv -i in.wav -filter_complex
+'channelsplit=channel_layout=5.1[FL][FR][FC][LFE][SL][SR]'
+-map '[FL]' front_left.wav -map '[FR]' front_right.wav -map '[FC]'
+front_center.wav -map '[LFE]' lfe.wav -map '[SL]' side_left.wav -map '[SR]'
+side_right.wav
+@end example
+
 @section resample
 Convert the audio sample format, sample rate and channel layout. This filter is
 not meant to be used directly, it is inserted automatically by libavfilter
index 914f0c6..7e1a6d1 100644 (file)
@@ -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_CHANNELSPLIT_FILTER)           += af_channelsplit.o
 OBJS-$(CONFIG_RESAMPLE_FILTER)               += af_resample.o
 
 OBJS-$(CONFIG_ANULLSRC_FILTER)               += asrc_anullsrc.o
diff --git a/libavfilter/af_channelsplit.c b/libavfilter/af_channelsplit.c
new file mode 100644 (file)
index 0000000..c3e7ccf
--- /dev/null
@@ -0,0 +1,146 @@
+/*
+ * 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
+ * Channel split filter
+ *
+ * Split an audio stream into per-channel streams.
+ */
+
+#include "libavutil/audioconvert.h"
+#include "libavutil/opt.h"
+
+#include "audio.h"
+#include "avfilter.h"
+#include "formats.h"
+#include "internal.h"
+
+typedef struct ChannelSplitContext {
+    const AVClass *class;
+
+    uint64_t channel_layout;
+    char    *channel_layout_str;
+} ChannelSplitContext;
+
+#define OFFSET(x) offsetof(ChannelSplitContext, x)
+#define A AV_OPT_FLAG_AUDIO_PARAM
+static const AVOption options[] = {
+    { "channel_layout", "Input channel layout.", OFFSET(channel_layout_str), AV_OPT_TYPE_STRING, { .str = "stereo" }, .flags = A },
+    { NULL },
+};
+
+static const AVClass channelsplit_class = {
+    .class_name = "channelsplit filter",
+    .item_name  = av_default_item_name,
+    .option     = options,
+    .version    = LIBAVUTIL_VERSION_INT,
+};
+
+static int init(AVFilterContext *ctx, const char *arg, void *opaque)
+{
+    ChannelSplitContext *s = ctx->priv;
+    int nb_channels;
+    int ret = 0, i;
+
+    s->class = &channelsplit_class;
+    av_opt_set_defaults(s);
+    if ((ret = av_set_options_string(s, arg, "=", ":")) < 0) {
+        av_log(ctx, AV_LOG_ERROR, "Error parsing options string '%s'.\n", arg);
+        return ret;
+    }
+    if (!(s->channel_layout = av_get_channel_layout(s->channel_layout_str))) {
+        av_log(ctx, AV_LOG_ERROR, "Error parsing channel layout '%s'.\n",
+               s->channel_layout_str);
+        ret = AVERROR(EINVAL);
+        goto fail;
+    }
+
+    nb_channels = av_get_channel_layout_nb_channels(s->channel_layout);
+    for (i = 0; i < nb_channels; i++) {
+        uint64_t channel = av_channel_layout_extract_channel(s->channel_layout, i);
+        AVFilterPad pad  = { 0 };
+
+        pad.type = AVMEDIA_TYPE_AUDIO;
+        pad.name = av_get_channel_name(channel);
+
+        ff_insert_outpad(ctx, i, &pad);
+    }
+
+fail:
+    av_opt_free(s);
+    return ret;
+}
+
+static int query_formats(AVFilterContext *ctx)
+{
+    ChannelSplitContext *s = ctx->priv;
+    AVFilterChannelLayouts *in_layouts = NULL;
+    int i;
+
+    ff_set_common_formats    (ctx, ff_planar_sample_fmts());
+    ff_set_common_samplerates(ctx, ff_all_samplerates());
+
+    ff_add_channel_layout(&in_layouts, s->channel_layout);
+    ff_channel_layouts_ref(in_layouts, &ctx->inputs[0]->out_channel_layouts);
+
+    for (i = 0; i < ctx->output_count; i++) {
+        AVFilterChannelLayouts *out_layouts = NULL;
+        uint64_t channel = av_channel_layout_extract_channel(s->channel_layout, i);
+
+        ff_add_channel_layout(&out_layouts, channel);
+        ff_channel_layouts_ref(out_layouts, &ctx->outputs[i]->in_channel_layouts);
+    }
+
+    return 0;
+}
+
+static void filter_samples(AVFilterLink *inlink, AVFilterBufferRef *buf)
+{
+    AVFilterContext *ctx = inlink->dst;
+    int i;
+
+    for (i = 0; i < ctx->output_count; i++) {
+        AVFilterBufferRef *buf_out = avfilter_ref_buffer(buf, ~AV_PERM_WRITE);
+
+        if (!buf_out)
+            return;
+
+        buf_out->data[0] = buf_out->extended_data[0] = buf_out->extended_data[i];
+        buf_out->audio->channel_layout =
+            av_channel_layout_extract_channel(buf->audio->channel_layout, i);
+
+        ff_filter_samples(ctx->outputs[i], buf_out);
+    }
+    avfilter_unref_buffer(buf);
+}
+
+AVFilter avfilter_af_channelsplit = {
+    .name           = "channelsplit",
+    .description    = NULL_IF_CONFIG_SMALL("Split audio into per-channel streams"),
+    .priv_size      = sizeof(ChannelSplitContext),
+
+    .init           = init,
+    .query_formats  = query_formats,
+
+    .inputs  = (const AVFilterPad[]){{ .name           = "default",
+                                       .type           = AVMEDIA_TYPE_AUDIO,
+                                       .filter_samples = filter_samples, },
+                                     { NULL }},
+    .outputs = (const AVFilterPad[]){{ NULL }},
+};
index 941ca6a..6814871 100644 (file)
@@ -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 (CHANNELSPLIT,channelsplit,af);
     REGISTER_FILTER (RESAMPLE,    resample,    af);
 
     REGISTER_FILTER (ANULLSRC,    anullsrc,    asrc);
index 83bdcd7..1d58d95 100644 (file)
@@ -220,6 +220,18 @@ AVFilterFormats *ff_all_formats(enum AVMediaType type)
     return ret;
 }
 
+AVFilterFormats *ff_planar_sample_fmts(void)
+{
+    AVFilterFormats *ret = NULL;
+    int fmt;
+
+    for (fmt = 0; fmt < AV_SAMPLE_FMT_NB; fmt++)
+        if (av_sample_fmt_is_planar(fmt))
+            ff_add_format(&ret, fmt);
+
+    return ret;
+}
+
 AVFilterFormats *ff_all_samplerates(void)
 {
     AVFilterFormats *ret = av_mallocz(sizeof(*ret));
index 8961cb1..c3e9522 100644 (file)
@@ -160,6 +160,11 @@ int ff_add_format(AVFilterFormats **avff, int fmt);
 AVFilterFormats *ff_all_formats(enum AVMediaType type);
 
 /**
+ * Construct a formats list containing all planar sample formats.
+ */
+AVFilterFormats *ff_planar_sample_fmts(void);
+
+/**
  * Return a format list which contains the intersection of the formats of
  * a and b. Also, all the references of a, all the references of b, and
  * a and b themselves will be deallocated.
index 9a64316..09cf4c6 100644 (file)
@@ -29,7 +29,7 @@
 #include "libavutil/avutil.h"
 
 #define LIBAVFILTER_VERSION_MAJOR  2
-#define LIBAVFILTER_VERSION_MINOR  20
+#define LIBAVFILTER_VERSION_MINOR  21
 #define LIBAVFILTER_VERSION_MICRO  0
 
 #define LIBAVFILTER_VERSION_INT AV_VERSION_INT(LIBAVFILTER_VERSION_MAJOR, \