[FFmpeg-devel] [PATCH] ffmpeg: add -map_channel option.

Clément Bœsch ubitux at gmail.com
Mon Oct 31 18:07:37 CET 2011


On Fri, Oct 28, 2011 at 02:29:52AM +0200, Clément Bœsch wrote:
[...]
> From 7df0c3b531f290f4f560c91a57c7cfa01a322164 Mon Sep 17 00:00:00 2001
> From: =?UTF-8?q?Cl=C3=A9ment=20B=C5=93sch?= <clement.boesch at smartjog.com>
> Date: Mon, 17 Oct 2011 10:33:47 +0200
> Subject: [PATCH] ffmpeg: add -map_channel option.

New patch reworked again. libswresample part should now be OK according to
Michael (the map audio channel takes over the input channel layout instead
of a previous hack for instance).

I also added some regression tests (not sure if it's the appropriate
place, so if anyone could have a look...).

Now a bikeshed question: is anyone OK with the "-map_channel" name?
Originally the option was named "-map_audio_channel", but the syntax is
different and I'd like eventually to avoid a pain in the ass for Baptiste
to deal with the conflict (since he still maintains the
"-map_audio_channel" option in FFmbc); two options with the same name but
different syntax can be confusing for the users of both projects.  Also,
"-map_audio_channel" is quite a long option name, and the cmd line gets
really big when using multiple times the option (which is common). So if
you think of an alternative, please keep it short.

If no one complains during the following 3 days, I'll push this patch.

-- 
Clément B.
-------------- next part --------------
From 7d8e0d8a74170ca2f23e02f503b3fa18d767a2b2 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Cl=C3=A9ment=20B=C5=93sch?= <clement.boesch at smartjog.com>
Date: Mon, 17 Oct 2011 10:33:47 +0200
Subject: [PATCH] ffmpeg: add -map_channel option.

Based on an initial work by Baptiste Coudurier.
---
 Changelog                                         |    1 +
 doc/ffmpeg.texi                                   |   38 +++++++
 ffmpeg.c                                          |  123 ++++++++++++++++++++-
 ffplay.c                                          |    2 +-
 libswresample/audioconvert.c                      |   11 ++-
 libswresample/audioconvert.h                      |    5 +-
 libswresample/swresample.c                        |   29 ++++--
 libswresample/swresample.h                        |    4 +-
 libswresample/swresample_internal.h               |    2 +
 libswresample/swresample_test.c                   |    8 +-
 tests/Makefile                                    |    8 ++
 tests/fate2.mak                                   |   12 ++
 tests/ref/fate/mapchan-6ch-extract-2              |    2 +
 tests/ref/fate/mapchan-6ch-extract-2-downmix-mono |    1 +
 tests/ref/fate/mapchan-silent-mono                |    1 +
 15 files changed, 225 insertions(+), 22 deletions(-)
 create mode 100644 tests/ref/fate/mapchan-6ch-extract-2
 create mode 100644 tests/ref/fate/mapchan-6ch-extract-2-downmix-mono
 create mode 100644 tests/ref/fate/mapchan-silent-mono

diff --git a/Changelog b/Changelog
index c6d88b6..150153b 100644
--- a/Changelog
+++ b/Changelog
@@ -70,6 +70,7 @@ easier to use. The changes are:
 - 4:2:2 H.264 decoding support
 - Pulseaudio input device
 - Prores encoder
+- new ffmpeg option: -map_channel
 
 version 0.8:
 
diff --git a/doc/ffmpeg.texi b/doc/ffmpeg.texi
index 529f830..06ef004 100644
--- a/doc/ffmpeg.texi
+++ b/doc/ffmpeg.texi
@@ -721,6 +721,44 @@ ffmpeg -i INPUT -map 0 -map -0:a:1 OUTPUT
 
 Note that using this option disables the default mappings for this output file.
 
+ at item -map_channel [@var{input_file_id}. at var{stream_specifier}. at var{channel_id}|-1][:@var{output_file_id}. at var{stream_specifier}]
+Map an audio channel from a given input to an output. If
+ at var{output_file_id}. at var{stream_specifier} are not set, the audio channel will
+be mapped on all the audio streams.
+
+Using "-1" instead of
+ at var{input_file_id}. at var{stream_specifier}. at var{channel_id} will map a muted
+channel.
+
+For example, assuming @var{INPUT} is a stereo audio file, you can switch the
+two audio channels with the following command:
+ at example
+ffmpeg -i INPUT -map_channel 0.0.1 -map_channel 0.0.0 OUTPUT
+ at end example
+
+If you want to mute the first channel and keep the second:
+ at example
+ffmpeg -i INPUT -map_channel -1 -map_channel 0.0.1 OUTPUT
+ at end example
+
+The order of the "-map_channel" option specifies the order of the channels in
+the output stream. The output channel layout is guessed from the number of
+channels mapped (mono if one "-map_channel", stereo if two, etc.). Using "-ac"
+in combination of "-map_channel" makes the channel gain levels to be updated if
+channel layouts don't match (for instance two "-map_channel" options and "-ac
+6").
+
+You can also extract each channel of an @var{INPUT} to specific outputs; the
+following command extract each channel of the audio stream (file 0, stream 0)
+to the respective @var{OUTPUT_CH0} and @var{OUTPUT_CH1}:
+ at example
+ffmpeg -i INPUT -map_channel 0.0.0 OUTPUT_CH0 -map_channel 0.0.1 OUTPUT_CH1
+ at end example
+
+Note that "-map_channel" is currently limited to the scope of one input for
+each output; you can't for example use it to pick multiple input audio files
+and mix them into one single output.
+
 @item -map_metadata[:@var{metadata_type}][:@var{index}] @var{infile}[:@var{metadata_type}][:@var{index}] (@emph{output,per-metadata})
 Set metadata information of the next output file from @var{infile}. Note that
 those are file indices (zero-based), not filenames.
diff --git a/ffmpeg.c b/ffmpeg.c
index e9ac81a..8327611 100644
--- a/ffmpeg.c
+++ b/ffmpeg.c
@@ -99,6 +99,11 @@ typedef struct StreamMap {
     int sync_stream_index;
 } StreamMap;
 
+typedef struct {
+    int  file_idx,  stream_idx,  channel_idx; // input
+    int ofile_idx, ostream_idx;               // output
+} AudioChannelMap;
+
 /**
  * select an input file for an output file
  */
@@ -231,6 +236,8 @@ typedef struct OutputStream {
 
     /* audio only */
     int audio_resample;
+    int audio_channels_map[SWR_CH_MAX];  ///< list of the channels id to pick from the source stream
+    int audio_channels_mapped;           ///< number of channels in audio_channels_map
     int resample_sample_fmt;
     int resample_channels;
     int resample_sample_rate;
@@ -310,6 +317,8 @@ typedef struct OptionsContext {
     /* output options */
     StreamMap *stream_maps;
     int     nb_stream_maps;
+    AudioChannelMap *audio_channel_maps; ///< one info entry per -map_channel
+    int           nb_audio_channel_maps; ///< number of (valid) -map_channel settings
     /* first item specifies output metadata, second is input */
     MetadataMap (*meta_data_maps)[2];
     int nb_meta_data_maps;
@@ -404,6 +413,7 @@ static void reset_options(OptionsContext *o, int is_input)
     }
 
     av_freep(&o->stream_maps);
+    av_freep(&o->audio_channel_maps);
     av_freep(&o->meta_data_maps);
     av_freep(&o->streamid_map);
 
@@ -852,7 +862,7 @@ need_realloc:
                        ost->resample_channels    != dec->channels   ||
                        ost->resample_sample_rate != dec->sample_rate;
 
-    if ((ost->audio_resample && !ost->swr) || resample_changed) {
+    if ((ost->audio_resample && !ost->swr) || resample_changed || ost->audio_channels_mapped) {
         if (resample_changed) {
             av_log(NULL, AV_LOG_INFO, "Input stream #%d.%d frame changed from rate:%d fmt:%s ch:%d to rate:%d fmt:%s ch:%d\n",
                    ist->file_index, ist->st->index,
@@ -864,7 +874,7 @@ need_realloc:
             swr_free(&ost->swr);
         }
         /* if audio_sync_method is >1 the resampler is needed for audio drift compensation */
-        if (audio_sync_method <= 1 &&
+        if (audio_sync_method <= 1 && !ost->audio_channels_mapped &&
             ost->resample_sample_fmt  == enc->sample_fmt &&
             ost->resample_channels    == enc->channels   &&
             ost->resample_sample_rate == enc->sample_rate) {
@@ -874,8 +884,13 @@ need_realloc:
             ost->swr = swr_alloc2(ost->swr,
                                   enc->channel_layout, enc->sample_fmt, enc->sample_rate,
                                   dec->channel_layout, dec->sample_fmt, dec->sample_rate,
+                                  ost->audio_channels_mapped ? ost->audio_channels_map : NULL,
                                   0, NULL);
             av_set_double(ost->swr, "rmvol", ost->rematrix_volume);
+            if (ost->audio_channels_mapped) {
+                av_set_int(ost->swr, "icl", av_get_default_channel_layout(ost->audio_channels_mapped));
+                av_set_int(ost->swr, "uch", ost->audio_channels_mapped);
+            }
             av_set_int(ost->swr, "ich", dec->channels);
             av_set_int(ost->swr, "och", enc->channels);
             if(audio_sync_method>1) av_set_int(ost->swr, "flags", SWR_FLAG_RESAMPLE);
@@ -2168,7 +2183,23 @@ static int transcode_init(OutputFile *output_files, int nb_output_files,
                 if (codec->sample_fmt == AV_SAMPLE_FMT_NONE)
                     codec->sample_fmt = icodec->sample_fmt;
                 choose_sample_fmt(ost->st, ost->enc);
-                if (!codec->channels) {
+                if (ost->audio_channels_mapped) {
+                    /* the requested output channel is set to the number of
+                     * -map_channel only if no -ac are specified */
+                    if (!codec->channels) {
+                        codec->channels       = ost->audio_channels_mapped;
+                        codec->channel_layout = av_get_default_channel_layout(codec->channels);
+                        if (!codec->channel_layout) {
+                            av_log(NULL, AV_LOG_FATAL, "Unable to find an appropriate channel layout for requested number of channel\n");
+                            exit_program(1);
+                        }
+                    }
+                    /* fill unused channel mapping with -1 (which means a muted
+                     * channel in case the number of output channels is bigger
+                     * than the number of mapped channel) */
+                    for (j = ost->audio_channels_mapped; j < FF_ARRAY_ELEMS(ost->audio_channels_map); j++)
+                        ost->audio_channels_map[j] = -1;
+                } else if (!codec->channels) {
                     codec->channels = icodec->channels;
                     codec->channel_layout = icodec->channel_layout;
                 }
@@ -2373,6 +2404,15 @@ static int transcode_init(OutputFile *output_files, int nb_output_files,
                input_streams[ost->source_index].st->index,
                ost->file_index,
                ost->index);
+        if (ost->audio_channels_mapped) {
+            av_log(NULL, AV_LOG_INFO, " [ch:");
+            for (j = 0; j < ost->audio_channels_mapped; j++)
+                if (ost->audio_channels_map[j] == -1)
+                    av_log(NULL, AV_LOG_INFO, " M");
+                else
+                    av_log(NULL, AV_LOG_INFO, " %d", ost->audio_channels_map[j]);
+            av_log(NULL, AV_LOG_INFO, "]");
+        }
         if (ost->sync_ist != &input_streams[ost->source_index])
             av_log(NULL, AV_LOG_INFO, " [sync #%d.%d]",
                    ost->sync_ist->file_index,
@@ -2875,6 +2915,66 @@ static int opt_map(OptionsContext *o, const char *opt, const char *arg)
     return 0;
 }
 
+static int opt_map_channel(OptionsContext *o, const char *opt, const char *arg)
+{
+    int n;
+    AVStream *st;
+    AudioChannelMap *m;
+
+    o->audio_channel_maps =
+        grow_array(o->audio_channel_maps, sizeof(*o->audio_channel_maps),
+                   &o->nb_audio_channel_maps, o->nb_audio_channel_maps + 1);
+    m = &o->audio_channel_maps[o->nb_audio_channel_maps - 1];
+
+    /* muted channel syntax */
+    n = sscanf(arg, "%d:%d.%d", &m->channel_idx, &m->ofile_idx, &m->ostream_idx);
+    if ((n == 1 || n == 3) && m->channel_idx == -1) {
+        m->file_idx = m->stream_idx = -1;
+        if (n == 1)
+            m->ofile_idx = m->ostream_idx = -1;
+        return 0;
+    }
+
+    /* normal syntax */
+    n = sscanf(arg, "%d.%d.%d:%d.%d",
+               &m->file_idx,  &m->stream_idx, &m->channel_idx,
+               &m->ofile_idx, &m->ostream_idx);
+
+    if (n != 3 && n != 5) {
+        av_log(NULL, AV_LOG_FATAL, "Syntax error, mapchan usage: "
+               "[file.stream.channel|-1][:syncfile:syncstream]\n");
+        exit_program(1);
+    }
+
+    if (n != 5) // only file.stream.channel specified
+        m->ofile_idx = m->ostream_idx = -1;
+
+    /* check input */
+    if (m->file_idx < 0 || m->file_idx >= nb_input_files) {
+        av_log(NULL, AV_LOG_FATAL, "mapchan: invalid input file index: %d\n",
+               m->file_idx);
+        exit_program(1);
+    }
+    if (m->stream_idx < 0 ||
+        m->stream_idx >= input_files[m->file_idx].nb_streams) {
+        av_log(NULL, AV_LOG_FATAL, "mapchan: invalid input file stream index #%d.%d\n",
+               m->file_idx, m->stream_idx);
+        exit_program(1);
+    }
+    st = input_files[m->file_idx].ctx->streams[m->stream_idx];
+    if (st->codec->codec_type != AVMEDIA_TYPE_AUDIO) {
+        av_log(NULL, AV_LOG_FATAL, "mapchan: stream #%d.%d is not an audio stream.\n",
+               m->file_idx, m->stream_idx);
+        exit_program(1);
+    }
+    if (m->channel_idx < 0 || m->channel_idx >= st->codec->channels) {
+        av_log(NULL, AV_LOG_FATAL, "mapchan: invalid audio channel #%d.%d.%d\n",
+               m->file_idx, m->stream_idx, m->channel_idx);
+        exit_program(1);
+    }
+    return 0;
+}
+
 static void parse_meta_type(char *arg, char *type, int *index)
 {
     if (*arg) {
@@ -3497,6 +3597,7 @@ static OutputStream *new_video_stream(OptionsContext *o, AVFormatContext *oc)
 
 static OutputStream *new_audio_stream(OptionsContext *o, AVFormatContext *oc)
 {
+    int n;
     AVStream *st;
     OutputStream *ost;
     AVCodecContext *audio_enc;
@@ -3525,6 +3626,21 @@ static OutputStream *new_audio_stream(OptionsContext *o, AVFormatContext *oc)
         MATCH_PER_STREAM_OPT(rematrix_volume, f, ost->rematrix_volume, oc, st);
     }
 
+    /* check for channel mapping for this audio stream */
+    for (n = 0; n < o->nb_audio_channel_maps; n++) {
+        AudioChannelMap *map = &o->audio_channel_maps[n];
+        InputStream *ist = &input_streams[ost->source_index];
+        if ((map->channel_idx == -1 || (ist->file_index == map->file_idx && ist->st->index == map->stream_idx)) &&
+            (map->ofile_idx   == -1 || ost->file_index == map->ofile_idx) &&
+            (map->ostream_idx == -1 || ost->st->index  == map->ostream_idx)) {
+            if (ost->audio_channels_mapped < FF_ARRAY_ELEMS(ost->audio_channels_map))
+                ost->audio_channels_map[ost->audio_channels_mapped++] = map->channel_idx;
+            else
+                av_log(NULL, AV_LOG_FATAL, "Max channel mapping for output %d.%d reached\n",
+                       ost->file_index, ost->st->index);
+        }
+    }
+
     return ost;
 }
 
@@ -4320,6 +4436,7 @@ static const OptionDef options[] = {
     { "codec", HAS_ARG | OPT_STRING | OPT_SPEC, {.off = OFFSET(codec_names)}, "codec name", "codec" },
     { "pre", HAS_ARG | OPT_STRING | OPT_SPEC, {.off = OFFSET(presets)}, "preset name", "preset" },
     { "map", HAS_ARG | OPT_EXPERT | OPT_FUNC2, {(void*)opt_map}, "set input stream mapping", "file.stream[:syncfile.syncstream]" },
+    { "map_channel", HAS_ARG | OPT_EXPERT | OPT_FUNC2, {(void*)opt_map_channel}, "map an audio channel from one stream to another", "file.stream.channel[:syncfile.syncstream]" },
     { "map_meta_data", HAS_ARG | OPT_EXPERT | OPT_FUNC2, {(void*)opt_map_meta_data}, "DEPRECATED set meta data information of outfile from infile",
       "outfile[,metadata]:infile[,metadata]" },
     { "map_metadata", HAS_ARG | OPT_EXPERT | OPT_FUNC2, {(void*)opt_map_metadata}, "set metadata information of outfile from infile",
diff --git a/ffplay.c b/ffplay.c
index 5a32d4e..0fc9818 100644
--- a/ffplay.c
+++ b/ffplay.c
@@ -2077,7 +2077,7 @@ static int audio_decode_frame(VideoState *is, double *pts_ptr)
                     swr_free(&is->swr_ctx);
                 is->swr_ctx = swr_alloc2(NULL, is->audio_tgt_channel_layout, is->audio_tgt_fmt, is->audio_tgt_freq,
                                                dec_channel_layout,          dec->sample_fmt,   dec->sample_rate,
-                                               0, NULL);
+                                               NULL, 0, NULL);
                 if (!is->swr_ctx || swr_init(is->swr_ctx) < 0) {
                     fprintf(stderr, "Cannot create sample rate converter for conversion of %d Hz %s %d channels to %d Hz %s %d channels!\n",
                         dec->sample_rate,
diff --git a/libswresample/audioconvert.c b/libswresample/audioconvert.c
index a1fa3eb..ba90348 100644
--- a/libswresample/audioconvert.c
+++ b/libswresample/audioconvert.c
@@ -35,11 +35,13 @@
 struct AVAudioConvert {
     int channels;
     int fmt_pair;
+    const int *ch_map;
 };
 
 AVAudioConvert *swr_audio_convert_alloc(enum AVSampleFormat out_fmt,
                                         enum AVSampleFormat in_fmt,
-                                        int channels, int flags)
+                                        int channels, const int *ch_map,
+                                        int flags)
 {
     AVAudioConvert *ctx;
     ctx = av_malloc(sizeof(AVAudioConvert));
@@ -47,6 +49,7 @@ AVAudioConvert *swr_audio_convert_alloc(enum AVSampleFormat out_fmt,
         return NULL;
     ctx->channels = channels;
     ctx->fmt_pair = out_fmt + AV_SAMPLE_FMT_NB*in_fmt;
+    ctx->ch_map   = ch_map;
     return ctx;
 }
 
@@ -58,15 +61,17 @@ void swr_audio_convert_free(AVAudioConvert **ctx)
 int swr_audio_convert(AVAudioConvert *ctx, AudioData *out, AudioData*in, int len)
 {
     int ch;
+    const uint8_t null_input[8] = {0};
 
     av_assert0(ctx->channels == out->ch_count);
 
     //FIXME optimize common cases
 
     for(ch=0; ch<ctx->channels; ch++){
-        const int is= (in ->planar ? 1 : in->ch_count) * in->bps;
+        const int ich= ctx->ch_map ? ctx->ch_map[ch] : ch;
+        const int is= ich < 0 ? 0 : (in->planar ? 1 : in->ch_count) * in->bps;
         const int os= (out->planar ? 1 :out->ch_count) *out->bps;
-        const uint8_t *pi= in ->ch[ch];
+        const uint8_t *pi= ich < 0 ? null_input : in->ch[ich];
         uint8_t       *po= out->ch[ch];
         uint8_t *end= po + os*len;
         if(!po)
diff --git a/libswresample/audioconvert.h b/libswresample/audioconvert.h
index e5fd4df..ca98d54 100644
--- a/libswresample/audioconvert.h
+++ b/libswresample/audioconvert.h
@@ -42,11 +42,14 @@ typedef struct AVAudioConvert AVAudioConvert;
  * @param in_fmt Input sample format
  * @param channels Number of channels
  * @param flags See AV_CPU_FLAG_xx
+ * @param ch_map list of the channels id to pick from the source stream, NULL
+ *               if all channels must be selected
  * @return NULL on error
  */
 AVAudioConvert *swr_audio_convert_alloc(enum AVSampleFormat out_fmt,
                                         enum AVSampleFormat in_fmt,
-                                        int channels, int flags);
+                                        int channels, const int *ch_map,
+                                        int flags);
 
 /**
  * Free audio sample format converter context.
diff --git a/libswresample/swresample.c b/libswresample/swresample.c
index d0fb96e..81df450 100644
--- a/libswresample/swresample.c
+++ b/libswresample/swresample.c
@@ -38,6 +38,7 @@
 static const AVOption options[]={
 {"ich",  "input channel count", OFFSET( in.ch_count   ), AV_OPT_TYPE_INT, {.dbl=2}, 1, SWR_CH_MAX, 0},
 {"och", "output channel count", OFFSET(out.ch_count   ), AV_OPT_TYPE_INT, {.dbl=2}, 1, SWR_CH_MAX, 0},
+{"uch",   "used channel count", OFFSET(used_ch_count  ), AV_OPT_TYPE_INT, {.dbl=2}, 1, SWR_CH_MAX, 0},
 {"isr",  "input sample rate"  , OFFSET( in_sample_rate), AV_OPT_TYPE_INT, {.dbl=48000}, 1, INT_MAX, 0},
 {"osr", "output sample rate"  , OFFSET(out_sample_rate), AV_OPT_TYPE_INT, {.dbl=48000}, 1, INT_MAX, 0},
 //{"ip" ,  "input planar"       , OFFSET( in.planar     ), AV_OPT_TYPE_INT, {.dbl=0},    0,       1, 0},
@@ -76,7 +77,7 @@ SwrContext *swr_alloc(void){
 
 SwrContext *swr_alloc2(struct SwrContext *s, int64_t out_ch_layout, enum AVSampleFormat out_sample_fmt, int out_sample_rate,
                        int64_t  in_ch_layout, enum AVSampleFormat  in_sample_fmt, int  in_sample_rate,
-                       int log_offset, void *log_ctx){
+                       const int *channel_map, int log_offset, void *log_ctx){
     if(!s) s= swr_alloc();
     if(!s) return NULL;
 
@@ -90,9 +91,11 @@ SwrContext *swr_alloc2(struct SwrContext *s, int64_t out_ch_layout, enum AVSampl
     av_set_int(s, "isf", in_sample_fmt);
     av_set_int(s, "isr", in_sample_rate);
 
+    s->channel_map = channel_map;
     s-> in.ch_count= av_get_channel_layout_nb_channels(s-> in_ch_layout);
     s->out.ch_count= av_get_channel_layout_nb_channels(s->out_ch_layout);
     s->int_sample_fmt = AV_SAMPLE_FMT_S16;
+    s->used_ch_count= s-> in.ch_count;
 
     return s;
 }
@@ -167,13 +170,13 @@ int swr_init(SwrContext *s){
         return -1;
     }
 
-    if(s-> in.ch_count && s-> in_ch_layout && s->in.ch_count != av_get_channel_layout_nb_channels(s-> in_ch_layout)){
-        av_log(s, AV_LOG_WARNING, "Input channel layout has a different number of channels than there actually is, ignoring layout\n");
+    if(s->used_ch_count && s-> in_ch_layout && s->used_ch_count != av_get_channel_layout_nb_channels(s-> in_ch_layout)){
+        av_log(s, AV_LOG_WARNING, "Input channel layout has a different number of channels than the number of used channels, ignoring layout\n");
         s-> in_ch_layout= 0;
     }
 
     if(!s-> in_ch_layout)
-        s-> in_ch_layout= av_get_default_channel_layout(s->in.ch_count);
+        s-> in_ch_layout= av_get_default_channel_layout(s->used_ch_count);
     if(!s->out_ch_layout)
         s->out_ch_layout= av_get_default_channel_layout(s->out.ch_count);
 
@@ -182,10 +185,13 @@ int swr_init(SwrContext *s){
 #define RSC 1 //FIXME finetune
     if(!s-> in.ch_count)
         s-> in.ch_count= av_get_channel_layout_nb_channels(s-> in_ch_layout);
+    if(!s->used_ch_count)
+        s->used_ch_count= s->in.ch_count;
     if(!s->out.ch_count)
         s->out.ch_count= av_get_channel_layout_nb_channels(s->out_ch_layout);
 
 av_assert0(s-> in.ch_count);
+av_assert0(s->used_ch_count);
 av_assert0(s->out.ch_count);
     s->resample_first= RSC*s->out.ch_count/s->in.ch_count - RSC < s->out_sample_rate/(float)s-> in_sample_rate - 1.0;
 
@@ -193,22 +199,27 @@ av_assert0(s->out.ch_count);
     s->int_bps= av_get_bits_per_sample_fmt(s->int_sample_fmt)/8;
     s->out.bps= av_get_bits_per_sample_fmt(s->out_sample_fmt)/8;
 
-    if(!s->resample && !s->rematrix){
+    if(!s->resample && !s->rematrix && !s->channel_map){
         s->full_convert = swr_audio_convert_alloc(s->out_sample_fmt,
-                                                  s-> in_sample_fmt, s-> in.ch_count, 0);
+                                                  s-> in_sample_fmt, s-> in.ch_count, NULL, 0);
         return 0;
     }
 
     s->in_convert = swr_audio_convert_alloc(s->int_sample_fmt,
-                                            s-> in_sample_fmt, s-> in.ch_count, 0);
+                                            s-> in_sample_fmt, s->used_ch_count, s->channel_map, 0);
     s->out_convert= swr_audio_convert_alloc(s->out_sample_fmt,
-                                            s->int_sample_fmt, s->out.ch_count, 0);
+                                            s->int_sample_fmt, s->out.ch_count, NULL, 0);
 
 
     s->postin= s->in;
     s->preout= s->out;
     s->midbuf= s->in;
     s->in_buffer= s->in;
+    if(s->channel_map){
+        s->postin.ch_count=
+        s->midbuf.ch_count=
+        s->in_buffer.ch_count= s->used_ch_count;
+    }
     if(!s->resample_first){
         s->midbuf.ch_count= s->out.ch_count;
         s->in_buffer.ch_count = s->out.ch_count;
@@ -306,7 +317,7 @@ int swr_convert(struct SwrContext *s, uint8_t *out_arg[SWR_CH_MAX], int out_coun
     if((ret=realloc_audio(&s->postin, in_count))<0)
         return ret;
     if(s->resample_first){
-        av_assert0(s->midbuf.ch_count ==  s-> in.ch_count);
+        av_assert0(s->midbuf.ch_count == s->used_ch_count);
         if((ret=realloc_audio(&s->midbuf, out_count))<0)
             return ret;
     }else{
diff --git a/libswresample/swresample.h b/libswresample/swresample.h
index 05c4f6d..85bd39f 100644
--- a/libswresample/swresample.h
+++ b/libswresample/swresample.h
@@ -25,7 +25,7 @@
 #include "libavutil/samplefmt.h"
 
 #define LIBSWRESAMPLE_VERSION_MAJOR 0
-#define LIBSWRESAMPLE_VERSION_MINOR 0
+#define LIBSWRESAMPLE_VERSION_MINOR 1
 #define LIBSWRESAMPLE_VERSION_MICRO 0
 
 #define SWR_CH_MAX 16
@@ -57,7 +57,7 @@ int swr_init(struct SwrContext *s);
  */
 struct SwrContext *swr_alloc2(struct SwrContext *s, int64_t out_ch_layout, enum AVSampleFormat out_sample_fmt, int out_sample_rate,
                               int64_t  in_ch_layout, enum AVSampleFormat  in_sample_fmt, int  in_sample_rate,
-                              int log_offset, void *log_ctx);
+                              const int *channel_map, int log_offset, void *log_ctx);
 
 /**
  * Free the given SwrContext.
diff --git a/libswresample/swresample_internal.h b/libswresample/swresample_internal.h
index 4678886..4764ddf 100644
--- a/libswresample/swresample_internal.h
+++ b/libswresample/swresample_internal.h
@@ -45,6 +45,8 @@ typedef struct SwrContext {          //FIXME find unused fields
     int     out_sample_rate;
     int flags;
     float slev, clev, rematrix_volume;
+    const int *channel_map;             ///< channel index (or -1 if muted channel) map
+    int used_ch_count;                  ///< number of used channels (mapped channel count if channel_map, otherwise in.ch_count)
 
     //below are private
     int int_bps;
diff --git a/libswresample/swresample_test.c b/libswresample/swresample_test.c
index 61e1b09..bb229f8 100644
--- a/libswresample/swresample_test.c
+++ b/libswresample/swresample_test.c
@@ -131,9 +131,11 @@ int main(int argc, char **argv){
                                in_sample_rate, out_sample_rate,
                                av_get_sample_fmt_name(in_sample_fmt), av_get_sample_fmt_name(out_sample_fmt));
                         forw_ctx  = swr_alloc2(forw_ctx, out_ch_layout, out_sample_fmt+planar_out, out_sample_rate,
-                                                                  in_ch_layout,  in_sample_fmt+planar_in ,  in_sample_rate, 0, 0);
-                        backw_ctx = swr_alloc2(backw_ctx,in_ch_layout,  in_sample_fmt,  in_sample_rate,
-                                                                 out_ch_layout, out_sample_fmt+planar_out, out_sample_rate, 0, 0);
+                                                          in_ch_layout,  in_sample_fmt+planar_in ,  in_sample_rate,
+                                                          NULL, 0, 0);
+                        backw_ctx = swr_alloc2(backw_ctx,in_ch_layout,  in_sample_fmt,             in_sample_rate,
+                                                        out_ch_layout, out_sample_fmt+planar_out, out_sample_rate,
+                                                        NULL, 0, 0);
                         if(swr_init( forw_ctx) < 0)
                             fprintf(stderr, "swr_init(->) failed\n");
                         if(swr_init(backw_ctx) < 0)
diff --git a/tests/Makefile b/tests/Makefile
index 93f07a5..48e81fa 100644
--- a/tests/Makefile
+++ b/tests/Makefile
@@ -30,6 +30,14 @@ tests/data/asynth-16000-1.sw: tests/audiogen$(HOSTEXESUF)
 	@mkdir -p tests/data
 	$(M)./$< $@ 16000 1
 
+tests/data/mapchan-6ch.sw: tests/audiogen$(HOSTEXESUF)
+	@mkdir -p tests/data
+	$(M)./$< $@ 22050 6
+
+tests/data/mapchan-mono.sw: tests/audiogen$(HOSTEXESUF)
+	@mkdir -p tests/data
+	$(M)./$< $@ 22050 1
+
 tests/data/asynth%.sw tests/vsynth%/00.pgm: TAG = GEN
 
 include $(SRC_PATH)/tests/fate.mak
diff --git a/tests/fate2.mak b/tests/fate2.mak
index 376a019..ad565b7 100644
--- a/tests/fate2.mak
+++ b/tests/fate2.mak
@@ -137,6 +137,18 @@ FATE_TESTS += fate-g722enc
 fate-g722enc: tests/data/asynth-16000-1.sw
 fate-g722enc: CMD = md5 -ar 16000 -ac 1 -f s16le -i $(TARGET_PATH)/tests/data/asynth-16000-1.sw -acodec g722 -ac 1 -f g722
 
+FATE_TESTS += fate-mapchan-6ch-extract-2
+fate-mapchan-6ch-extract-2: tests/data/mapchan-6ch.sw
+fate-mapchan-6ch-extract-2: CMD = avconv -ar 22050 -ac 6 -i $(TARGET_PATH)/tests/data/mapchan-6ch.sw -map_channel 0.0.0 -f wav md5: -map_channel 0.0.1 -f wav md5:
+
+FATE_TESTS += fate-mapchan-6ch-extract-2-downmix-mono
+fate-mapchan-6ch-extract-2-downmix-mono: tests/data/mapchan-6ch.sw
+fate-mapchan-6ch-extract-2-downmix-mono: CMD = md5 -ar 22050 -ac 6 -i $(TARGET_PATH)/tests/data/mapchan-6ch.sw -map_channel 0.0.1 -map_channel 0.0.0 -ac 1 -f wav
+
+FATE_TESTS += fate-mapchan-silent-mono
+fate-mapchan-silent-mono: tests/data/mapchan-mono.sw
+fate-mapchan-silent-mono: CMD = md5 -ar 22050 -ac 1 -i $(TARGET_PATH)/tests/data/mapchan-mono.sw -map_channel -1 -map_channel 0.0.0 -f wav
+
 FATE_TESTS += fate-msmpeg4v1
 fate-msmpeg4v1: CMD = framecrc -flags +bitexact -dct fastint -idct simple -i $(SAMPLES)/msmpeg4v1/mpg4.avi -an
 
diff --git a/tests/ref/fate/mapchan-6ch-extract-2 b/tests/ref/fate/mapchan-6ch-extract-2
new file mode 100644
index 0000000..98c8540
--- /dev/null
+++ b/tests/ref/fate/mapchan-6ch-extract-2
@@ -0,0 +1,2 @@
+6f091fe8c0be88c75921731dc9f74314
+5c2d162b9024329eb367295d37b8ca0a
diff --git a/tests/ref/fate/mapchan-6ch-extract-2-downmix-mono b/tests/ref/fate/mapchan-6ch-extract-2-downmix-mono
new file mode 100644
index 0000000..f42de0c
--- /dev/null
+++ b/tests/ref/fate/mapchan-6ch-extract-2-downmix-mono
@@ -0,0 +1 @@
+959645ed73e6d08d8f1e947eac5d0b92
diff --git a/tests/ref/fate/mapchan-silent-mono b/tests/ref/fate/mapchan-silent-mono
new file mode 100644
index 0000000..a867e3b
--- /dev/null
+++ b/tests/ref/fate/mapchan-silent-mono
@@ -0,0 +1 @@
+4f5148f08587a4b9794aa52aec7852ac
-- 
1.7.7

-------------- next part --------------
A non-text attachment was scrubbed...
Name: not available
Type: application/pgp-signature
Size: 490 bytes
Desc: not available
URL: <http://ffmpeg.org/pipermail/ffmpeg-devel/attachments/20111031/df4427cf/attachment.asc>


More information about the ffmpeg-devel mailing list