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

Clément Bœsch ubitux at gmail.com
Tue Oct 25 16:29:28 CEST 2011


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,

-- 
Clément B.
-------------- next part --------------
From c63e60eb86e1a67c6fb17d8e741951811a27b370 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                            |   91 +++++++++++++++++++++++++++++++++++
 ffplay.c                            |    2 +-
 libswresample/audioconvert.c        |    8 ++-
 libswresample/audioconvert.h        |    5 ++-
 libswresample/swresample.c          |   13 +++--
 libswresample/swresample.h          |    4 +-
 libswresample/swresample_internal.h |    1 +
 libswresample/swresample_test.c     |    8 ++-
 10 files changed, 145 insertions(+), 15 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..e15a2c2 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);
 
@@ -871,6 +881,7 @@ 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);
@@ -2169,6 +2180,14 @@ 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);
+                    }
+                }
                 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 +2388,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 +2896,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 +3562,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 +3588,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 +4378,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..1a61cac 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;
 }
 
@@ -66,7 +69,8 @@ int swr_audio_convert(AVAudioConvert *ctx, AudioData *out, AudioData*in, int len
     for(ch=0; ch<ctx->channels; ch++){
         const int is= (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 int ich= ctx->ch_map ? ctx->ch_map[ch] : ch;
+        const uint8_t *pi= 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..f9f7399 100644
--- a/libswresample/swresample.c
+++ b/libswresample/swresample.c
@@ -75,7 +75,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 +89,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;
@@ -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;
 
 #define RSC 1 //FIXME finetune
     if(!s-> in.ch_count)
@@ -192,16 +193,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..cb9c2fd 100644
--- a/libswresample/swresample_internal.h
+++ b/libswresample/swresample_internal.h
@@ -45,6 +45,7 @@ typedef struct SwrContext {          //FIXME find unused fields
     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.5.4

-------------- 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/20111025/4237e145/attachment.asc>


More information about the ffmpeg-devel mailing list