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

Clément Bœsch ubitux at gmail.com
Wed Oct 26 16:42:05 CEST 2011


On Tue, Oct 25, 2011 at 07:42:52PM +0200, Michael Niedermayer wrote:
> On Tue, Oct 25, 2011 at 06:30:16PM +0200, Clément Bœsch wrote:
> > On Tue, Oct 25, 2011 at 05:10:26PM +0200, Michael Niedermayer wrote:
> > > On Tue, Oct 25, 2011 at 04:29:28PM +0200, Clément Bœsch wrote:
> > > > On Tue, Oct 25, 2011 at 03:28:36PM +0200, Clément Bœsch wrote:
> > > > > On Tue, Oct 25, 2011 at 03:22:12PM +0200, Clément Bœsch wrote:
> > > > > > On Tue, Oct 25, 2011 at 02:25:53PM +0200, Michael Niedermayer wrote:
> > > > > > > On Tue, Oct 25, 2011 at 11:07:53AM +0200, Clément Bœsch wrote:
> > > > > > > > On Mon, Oct 24, 2011 at 06:29:10PM +0200, Michael Niedermayer wrote:
> > > > > > > > > On Mon, Oct 24, 2011 at 04:19:10PM +0200, Michael Niedermayer wrote:
> > > > > > > > > > On Mon, Oct 24, 2011 at 02:05:55PM +0200, Clément Bœsch wrote:
> > > > > > > > > > > On Wed, Oct 19, 2011 at 11:42:15PM +0200, Michael Niedermayer wrote:
> > > > > > > > [...]
> > > > > > > > > > > > also shouldnt rematrix be independant of the existence of a channel_map
> > > > > > > > > > > > 
> > > > > > > > > > > 
> > > > > > > > > > > Well, AFAIU, the rematrix is meant to automatically set the audio gain
> > > > > > > > > > > levels for each channel; the channel mapping is "incompatible" with this,
> > > > > > > > > > > it's all about reordering, or maybe with another phrasing: ppl won't
> > > > > > > > > > > expect the levels to change (the number of output channels being fixed by
> > > > > > > > > > > the number of -map_channel and not a specific -ac option)
> > > > > > > > > > 
> > > > > > > > > > if your inputs represents 5.1 and your output represenst 7.0 you need
> > > > > > > > > > the rematrix code
> > > > > > > > > 
> > > > > > > > > a better and simpler example is stereo input and mono output
> > > > > > > > > 
> > > > > > > > 
> > > > > > > > In case of stereo → mono with map_channel, you're likely to have something
> > > > > > > > like:
> > > > > > > >     ffmpeg -i stereo.wav -map_channel 0.0.0 left.wav -map_channel 0.0.1 right.wav
> > > > > > > 
> > > > > > > i meant that we have 2 input files, one with right and one with
> > > > > > > left chanel and want one mono output file.
> > > > > > > 
> > > > > > 
> > > > > > Picking from multiple input files is unfortunately not supported ATM
> > > > > > because each output stream is limited to one input stream. Do you see any
> > > > > > other use case for rematrix?
> > > > > > 
> > > > > 
> > > > > Mmh forget this, we might workaround this with the -map.
> > > > > 
> > > > 
> > > > It won't be possible to adapt ffmpeg to this easily AFAIU, so patch
> > > > re-attached adding the mention of the limitation in the documentation.
> > > > 
> > > > The patch is also rebased on master dropping temporarily the two resample
> > > > flag patches (which might need some rework according to the other thread).
> > > > 
> > > > So to sum up, each output stream only has a single associated stream
> > > > (source_index field), so you can't have an output stream based on multiple
> > > > inputs given the current design. Using filters might be a solution, but
> > > > IMHO it does not belong to the scope of this feature.
> > > > 
> > > > Any other suggestion welcome,
> > > [...]
> > > > @@ -176,7 +177,7 @@ int swr_init(SwrContext *s){
> > > >      if(!s->out_ch_layout)
> > > >          s->out_ch_layout= av_get_default_channel_layout(s->out.ch_count);
> > > >  
> > > > -    s->rematrix= s->out_ch_layout  !=s->in_ch_layout;
> > > > +    s->rematrix= s->out_ch_layout != s->in_ch_layout && !s->channel_map;
> > > 
> > > iam still thinking this is problematic
> > > consider a 5.1 input that has its channels in wrong order and the
> > > user wanting stereo output, this would require remaping channels and
> > > rematrixing and it would be just 1 stream->1 stream
> > > 
> > 
> > You can't ATM mix -ac with -map_channel; the number of -map_channel
> > options is used to set the channel layout. If you want to reorder and then
> > downmix you actually have to do it in two rows.
> > 
> > Assuming the code is changed to set the channel layout using -ac if
> > specified (not much trouble to deal with), another issue comes out: how do
> > you make a distinction between:
> > 
> >   ffmepg -i INPUT_5.1 -map_channel 0.0.0 -map_channel 0.0.1 output
> > 
> > and:
> > 
> >   ffmepg -i INPUT_5.1 -map_channel 0.0.0 -map_channel 0.0.1 -ac 6 output
> > 
> > The first case must *not* rematrix, it's all about filtering (and
> > eventually reorder) channels. Now what would be the heuristic to say we
> > need to have the rematrix in the second case? I guess having "-ac" set and
> > at least one -map_channel option might be one, and in that case a "force
> > rematrix" flag need to be transmitted to libswr. This will enlarge the
> > current patch and might introduce more issues than expected.
> > 
> > So as already stated, ATM -ac is ignored with -map_channel (yes, another
> > limitation), but this can be changed later.
> > 
> 
> > Do you think adding this rematrix feature in the initial patch is really
> > worth?
> 
> I think rematrix should not be forced to be disabled in swr based on
> reordering. Iam not
> asking for it to be possible to be used from ffmpeg.c with reorder
> initially.
> 
> Rather the API for the reordering in swr should be flexible enough to
> allow both to be specified in an unambigous way. So that rematrix is
> disabled when the layouts match.

I see, then what about the attached patch?

If the requested remapped channel layout (option "rcl", ATM using -map_channel
count, and overriding the number of output channels) differs from the requested
output channel layout, the rematrix will be run. You can test it in ffmpeg by
commenting out the following chunk:

    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);
    }

It will trigger the rematrix code; right now, no rematrix is possible from the
command line, but it works on libswr side.

Also note I needed to add a "mute" channel code (mapping -1 instead of the
channel id) to pad the channels when the layouts differs (in case of rematrix
for instance); I also intend to use that code to mute a specific channel with
something like -map_channel -1.

> Maybe there could be
> input number of channels, a remaped number of channels and layout
> and a output number of channels and layout.
> when the layouts match no rematrix would be needed otherwise it would
> be needed.

Different layouts seems enough to me: if the number of channels is
different, the channel layout will differ too.

-- 
Clément B.
-------------- next part --------------
From fedfe19d8026a1147efab8c171ffab7563433f48 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                     |   27 +++++++++
 ffmpeg.c                            |  101 ++++++++++++++++++++++++++++++++++-
 ffplay.c                            |    2 +-
 libswresample/audioconvert.c        |   11 +++-
 libswresample/audioconvert.h        |    5 ++-
 libswresample/swresample.c          |   16 ++++--
 libswresample/swresample.h          |    4 +-
 libswresample/swresample_internal.h |    2 +
 libswresample/swresample_test.c     |    8 ++-
 10 files changed, 160 insertions(+), 17 deletions(-)

diff --git a/Changelog b/Changelog
index 5c9ccb5..f962d23 100644
--- a/Changelog
+++ b/Changelog
@@ -69,6 +69,7 @@ easier to use. The changes are:
 - Speex encoding via libspeex
 - 4:2:2 H.264 decoding support
 - Pulseaudio input device
+- new ffmpeg option: -map_channel
 
 version 0.8:
 
diff --git a/doc/ffmpeg.texi b/doc/ffmpeg.texi
index c59c757..04e221e 100644
--- a/doc/ffmpeg.texi
+++ b/doc/ffmpeg.texi
@@ -721,6 +721,33 @@ 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}[:@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.
+
+For example, assuming @var{INPUT} has two mono audio streams (say stream 1 and
+2), the following command creates a stereo audio stream in @var{OUTPUT} with
+first input stream as channel 0 and second input stream as channel 1:
+ at example
+ffmpeg -i INPUT -map_channel 0.1.0 -map_channel 0.2.0 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.)
+
+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_1} and @var{OUTPUT_2}:
+ at example
+ffmpeg -i INPUT -map_channel 0.0.0:0.0 -map_channel 0.0.1:1.0 OUTPUT_1 OUTPUT_2
+ 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 347c324..47c0601 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;
@@ -306,6 +313,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;
@@ -400,6 +409,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);
 
@@ -849,7 +859,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,
@@ -861,7 +871,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) {
@@ -871,9 +881,11 @@ 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_int(ost->swr, "ich", dec->channels);
             av_set_int(ost->swr, "och", enc->channels);
+            av_set_int(ost->swr, "rcl", enc->channel_layout); // XXX: use a different channel layout to request rematrix
             if(audio_sync_method>1) av_set_int(ost->swr, "flags", SWR_FLAG_RESAMPLE);
             if(ost->swr && swr_init(ost->swr) < 0){
                 av_log(NULL, AV_LOG_FATAL, "swr_init() failed\n");
@@ -2169,6 +2181,19 @@ static int transcode_init(OutputFile *output_files, int nb_output_files,
                     codec->channels = icodec->channels;
                     codec->channel_layout = icodec->channel_layout;
                 }
+                if (ost->audio_channels_mapped) {
+                    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 mute 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;
+                }
                 if (av_get_channel_layout_nb_channels(codec->channel_layout) != codec->channels)
                     codec->channel_layout = 0;
                 ost->audio_resample = codec->sample_rate != icodec->sample_rate || audio_sync_method > 1;
@@ -2369,6 +2394,12 @@ 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++)
+                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,
@@ -2871,6 +2902,55 @@ 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];
+    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[: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) {
@@ -3488,6 +3568,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;
@@ -3513,6 +3594,21 @@ static OutputStream *new_audio_stream(OptionsContext *o, AVFormatContext *oc)
         MATCH_PER_STREAM_OPT(audio_sample_rate, i, audio_enc->sample_rate, 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];
+        if (input_streams[ost->source_index].file_index == map->file_idx    &&
+            input_streams[ost->source_index].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;
 }
 
@@ -4288,6 +4384,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 a03d531..815ce2d 100644
--- a/libswresample/swresample.c
+++ b/libswresample/swresample.c
@@ -47,6 +47,7 @@ static const AVOption options[]={
 {"tsf", "internal sample format", OFFSET(int_sample_fmt ), AV_OPT_TYPE_INT, {.dbl=AV_SAMPLE_FMT_NONE}, -1, AV_SAMPLE_FMT_FLT, 0},
 {"icl",  "input channel layout" , OFFSET( in_ch_layout), AV_OPT_TYPE_INT64, {.dbl=0}, 0, INT64_MAX, 0, "channel_layout"},
 {"ocl",  "output channel layout", OFFSET(out_ch_layout), AV_OPT_TYPE_INT64, {.dbl=0}, 0, INT64_MAX, 0, "channel_layout"},
+{"rcl", "remapped channel layout",OFFSET(  r_ch_layout), AV_OPT_TYPE_INT64, {.dbl=0}, 0, INT64_MAX, 0, "channel_layout"},
 {"clev", "center mix level"     , OFFSET(clev)         , AV_OPT_TYPE_FLOAT, {.dbl=C_30DB}, 0, 4, 0},
 {"slev", "sourround mix level"  , OFFSET(slev)         , AV_OPT_TYPE_FLOAT, {.dbl=C_30DB}, 0, 4, 0},
 {"flags", NULL                  , OFFSET(flags)        , AV_OPT_TYPE_FLAGS, {.dbl=0}, 0,  UINT_MAX, 0, "flags"},
@@ -75,7 +76,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;
 
@@ -89,6 +90,7 @@ 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;
@@ -177,6 +179,10 @@ int swr_init(SwrContext *s){
         s->out_ch_layout= av_get_default_channel_layout(s->out.ch_count);
 
     s->rematrix= s->out_ch_layout  !=s->in_ch_layout;
+    // disable rematrix in case the remapped channel layout matches the
+    // requested output channel layout.
+    if (s->channel_map && s->r_ch_layout == s->out_ch_layout)
+        s->rematrix= 0;
 
 #define RSC 1 //FIXME finetune
     if(!s-> in.ch_count)
@@ -192,16 +198,16 @@ 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-> in.ch_count, NULL, 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, s->channel_map, 0);
 
 
     s->postin= s->in;
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 3137be6..a6af8da 100644
--- a/libswresample/swresample_internal.h
+++ b/libswresample/swresample_internal.h
@@ -41,10 +41,12 @@ typedef struct SwrContext {          //FIXME find unused fields
     enum AVSampleFormat out_sample_fmt;
     int64_t  in_ch_layout;
     int64_t out_ch_layout;
+    int64_t   r_ch_layout;              ///< remapped channel layout
     int      in_sample_rate;
     int     out_sample_rate;
     int flags;
     float slev, clev;
+    const int *channel_map;
 
     //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)
-- 
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/20111026/2b0855e0/attachment.asc>


More information about the ffmpeg-devel mailing list