lavr: add a public function for setting a custom channel map
authorJustin Ruggles <justin.ruggles@gmail.com>
Wed, 19 Dec 2012 19:58:57 +0000 (14:58 -0500)
committerJustin Ruggles <justin.ruggles@gmail.com>
Tue, 8 Jan 2013 02:49:06 +0000 (21:49 -0500)
This allows reordering, duplication, and silencing of input channels.

doc/APIchanges
libavresample/audio_convert.c
libavresample/audio_convert.h
libavresample/audio_data.c
libavresample/audio_data.h
libavresample/avresample.h
libavresample/dither.c
libavresample/dither.h
libavresample/internal.h
libavresample/utils.c
libavresample/version.h

index a5a0bea..5076e5d 100644 (file)
@@ -13,6 +13,10 @@ libavutil:     2012-10-22
 
 API changes, most recent first:
 
+2013-xx-xx - xxxxxxx - lavr 1.1.0
+  Add avresample_set_channel_mapping() for input channel reordering,
+  duplication, and silencing.
+
 2012-xx-xx - xxxxxxx - lavu 52.2.1 - avstring.h
   Add av_basename() and av_dirname().
 
index 288f0f4..b57d2fa 100644 (file)
@@ -50,6 +50,7 @@ struct AudioConvert {
     DitherContext *dc;
     enum AVSampleFormat in_fmt;
     enum AVSampleFormat out_fmt;
+    int apply_map;
     int channels;
     int planes;
     int ptr_align;
@@ -259,7 +260,8 @@ void ff_audio_convert_free(AudioConvert **ac)
 AudioConvert *ff_audio_convert_alloc(AVAudioResampleContext *avr,
                                      enum AVSampleFormat out_fmt,
                                      enum AVSampleFormat in_fmt,
-                                     int channels, int sample_rate)
+                                     int channels, int sample_rate,
+                                     int apply_map)
 {
     AudioConvert *ac;
     int in_planar, out_planar;
@@ -272,11 +274,13 @@ AudioConvert *ff_audio_convert_alloc(AVAudioResampleContext *avr,
     ac->out_fmt  = out_fmt;
     ac->in_fmt   = in_fmt;
     ac->channels = channels;
+    ac->apply_map = apply_map;
 
     if (avr->dither_method != AV_RESAMPLE_DITHER_NONE          &&
         av_get_packed_sample_fmt(out_fmt) == AV_SAMPLE_FMT_S16 &&
         av_get_bytes_per_sample(in_fmt) > 2) {
-        ac->dc = ff_dither_alloc(avr, out_fmt, in_fmt, channels, sample_rate);
+        ac->dc = ff_dither_alloc(avr, out_fmt, in_fmt, channels, sample_rate,
+                                 apply_map);
         if (!ac->dc) {
             av_free(ac);
             return NULL;
@@ -309,6 +313,7 @@ int ff_audio_convert(AudioConvert *ac, AudioData *out, AudioData *in)
 {
     int use_generic = 1;
     int len         = in->nb_samples;
+    int p;
 
     if (ac->dc) {
         /* dithered conversion */
@@ -335,9 +340,46 @@ int ff_audio_convert(AudioConvert *ac, AudioData *out, AudioData *in)
             av_get_sample_fmt_name(ac->out_fmt),
             use_generic ? ac->func_descr_generic : ac->func_descr);
 
+    if (ac->apply_map) {
+        ChannelMapInfo *map = &ac->avr->ch_map_info;
+
+        if (!av_sample_fmt_is_planar(ac->out_fmt)) {
+            av_log(ac->avr, AV_LOG_ERROR, "cannot remap packed format during conversion\n");
+            return AVERROR(EINVAL);
+        }
+
+        if (map->do_remap) {
+            if (av_sample_fmt_is_planar(ac->in_fmt)) {
+                conv_func_flat *convert = use_generic ? ac->conv_flat_generic :
+                                                        ac->conv_flat;
+
+                for (p = 0; p < ac->planes; p++)
+                    if (map->channel_map[p] >= 0)
+                        convert(out->data[p], in->data[map->channel_map[p]], len);
+            } else {
+                uint8_t *data[AVRESAMPLE_MAX_CHANNELS];
+                conv_func_deinterleave *convert = use_generic ?
+                                                  ac->conv_deinterleave_generic :
+                                                  ac->conv_deinterleave;
+
+                for (p = 0; p < ac->channels; p++)
+                    data[map->input_map[p]] = out->data[p];
+
+                convert(data, in->data[0], len, ac->channels);
+            }
+        }
+        if (map->do_copy || map->do_zero) {
+            for (p = 0; p < ac->planes; p++) {
+                if (map->channel_copy[p])
+                    memcpy(out->data[p], out->data[map->channel_copy[p]],
+                           len * out->stride);
+                else if (map->channel_zero[p])
+                    av_samples_set_silence(&out->data[p], 0, len, 1, ac->out_fmt);
+            }
+        }
+    } else {
     switch (ac->func_type) {
     case CONV_FUNC_TYPE_FLAT: {
-        int p;
         if (!in->is_planar)
             len *= in->channels;
         if (use_generic) {
@@ -362,6 +404,7 @@ int ff_audio_convert(AudioConvert *ac, AudioData *out, AudioData *in)
             ac->conv_deinterleave(out->data, in->data[0], len, ac->channels);
         break;
     }
+    }
 
     out->nb_samples = in->nb_samples;
     return 0;
index 7d47b15..6a3089d 100644 (file)
@@ -58,12 +58,14 @@ void ff_audio_convert_set_func(AudioConvert *ac, enum AVSampleFormat out_fmt,
  * @param in_fmt      input sample format
  * @param channels    number of channels
  * @param sample_rate sample rate (used for dithering)
+ * @param apply_map   apply channel map during conversion
  * @return            newly-allocated AudioConvert context
  */
 AudioConvert *ff_audio_convert_alloc(AVAudioResampleContext *avr,
                                      enum AVSampleFormat out_fmt,
                                      enum AVSampleFormat in_fmt,
-                                     int channels, int sample_rate);
+                                     int channels, int sample_rate,
+                                     int apply_map);
 
 /**
  * Free AudioConvert.
index 199a68c..c52f518 100644 (file)
@@ -213,7 +213,7 @@ void ff_audio_data_free(AudioData **a)
     av_freep(a);
 }
 
-int ff_audio_data_copy(AudioData *dst, AudioData *src)
+int ff_audio_data_copy(AudioData *dst, AudioData *src, ChannelMapInfo *map)
 {
     int ret, p;
 
@@ -221,6 +221,11 @@ int ff_audio_data_copy(AudioData *dst, AudioData *src)
     if (dst->sample_fmt != src->sample_fmt || dst->channels < src->channels)
         return AVERROR(EINVAL);
 
+    if (map && !src->is_planar) {
+        av_log(src, AV_LOG_ERROR, "cannot remap packed format during copy\n");
+        return AVERROR(EINVAL);
+    }
+
     /* if the input is empty, just empty the output */
     if (!src->nb_samples) {
         dst->nb_samples = 0;
@@ -233,8 +238,29 @@ int ff_audio_data_copy(AudioData *dst, AudioData *src)
         return ret;
 
     /* copy data */
-    for (p = 0; p < src->planes; p++)
-        memcpy(dst->data[p], src->data[p], src->nb_samples * src->stride);
+    if (map) {
+        if (map->do_remap) {
+            for (p = 0; p < src->planes; p++) {
+                if (map->channel_map[p] >= 0)
+                    memcpy(dst->data[p], src->data[map->channel_map[p]],
+                           src->nb_samples * src->stride);
+            }
+        }
+        if (map->do_copy || map->do_zero) {
+            for (p = 0; p < src->planes; p++) {
+                if (map->channel_copy[p])
+                    memcpy(dst->data[p], dst->data[map->channel_copy[p]],
+                           src->nb_samples * src->stride);
+                else if (map->channel_zero[p])
+                    av_samples_set_silence(&dst->data[p], 0, src->nb_samples,
+                                           1, dst->sample_fmt);
+            }
+        }
+    } else {
+        for (p = 0; p < src->planes; p++)
+            memcpy(dst->data[p], src->data[p], src->nb_samples * src->stride);
+    }
+
     dst->nb_samples = src->nb_samples;
 
     return 0;
index 4e53e31..97236bb 100644 (file)
@@ -118,9 +118,10 @@ void ff_audio_data_free(AudioData **a);
  *
  * @param out  output AudioData
  * @param in   input AudioData
+ * @param map  channel map, NULL if not remapping
  * @return     0 on success, negative AVERROR value on error
  */
-int ff_audio_data_copy(AudioData *out, AudioData *in);
+int ff_audio_data_copy(AudioData *out, AudioData *in, ChannelMapInfo *map);
 
 /**
  * Append data from one AudioData to the end of another.
index 0012787..d26f2ca 100644 (file)
@@ -259,6 +259,36 @@ int avresample_set_matrix(AVAudioResampleContext *avr, const double *matrix,
                           int stride);
 
 /**
+ * Set a customized input channel mapping.
+ *
+ * This function can only be called when the allocated context is not open.
+ * Also, the input channel layout must have already been set.
+ *
+ * Calling avresample_close() on the context will clear the channel mapping.
+ *
+ * The map for each input channel specifies the channel index in the source to
+ * use for that particular channel, or -1 to mute the channel. Source channels
+ * can be duplicated by using the same index for multiple input channels.
+ *
+ * Examples:
+ *
+ * Reordering 5.1 AAC order (C,L,R,Ls,Rs,LFE) to Libav order (L,R,C,LFE,Ls,Rs):
+ * { 1, 2, 0, 5, 3, 4 }
+ *
+ * Muting the 3rd channel in 4-channel input:
+ * { 0, 1, -1, 3 }
+ *
+ * Duplicating the left channel of stereo input:
+ * { 0, 0 }
+ *
+ * @param avr         audio resample context
+ * @param channel_map customized input channel mapping
+ * @return            0 on success, negative AVERROR code on failure
+ */
+int avresample_set_channel_mapping(AVAudioResampleContext *avr,
+                                   const int *channel_map);
+
+/**
  * Set compensation for resampling.
  *
  * This can be called anytime after avresample_open(). If resampling is not
index 9c1e1c1..dfff03e 100644 (file)
@@ -53,6 +53,8 @@ typedef struct DitherState {
 struct DitherContext {
     DitherDSPContext  ddsp;
     enum AVResampleDitherMethod method;
+    int apply_map;
+    ChannelMapInfo *ch_map_info;
 
     int mute_dither_threshold;  // threshold for disabling dither
     int mute_reset_threshold;   // threshold for resetting noise shaping
@@ -251,17 +253,23 @@ int ff_convert_dither(DitherContext *c, AudioData *dst, AudioData *src)
             return ret;
     }
 
-    if (src->sample_fmt != AV_SAMPLE_FMT_FLTP) {
+    if (src->sample_fmt != AV_SAMPLE_FMT_FLTP || c->apply_map) {
         /* make sure flt_data is large enough for the input */
         ret = ff_audio_data_realloc(c->flt_data, src->nb_samples);
         if (ret < 0)
             return ret;
         flt_data = c->flt_data;
+    }
 
+    if (src->sample_fmt != AV_SAMPLE_FMT_FLTP) {
         /* convert input samples to fltp and scale to s16 range */
         ret = ff_audio_convert(c->ac_in, flt_data, src);
         if (ret < 0)
             return ret;
+    } else if (c->apply_map) {
+        ret = ff_audio_data_copy(flt_data, src, c->ch_map_info);
+        if (ret < 0)
+            return ret;
     } else {
         flt_data = src;
     }
@@ -333,7 +341,7 @@ static void dither_init(DitherDSPContext *ddsp,
 DitherContext *ff_dither_alloc(AVAudioResampleContext *avr,
                                enum AVSampleFormat out_fmt,
                                enum AVSampleFormat in_fmt,
-                               int channels, int sample_rate)
+                               int channels, int sample_rate, int apply_map)
 {
     AVLFG seed_gen;
     DitherContext *c;
@@ -350,6 +358,10 @@ DitherContext *ff_dither_alloc(AVAudioResampleContext *avr,
     if (!c)
         return NULL;
 
+    c->apply_map = apply_map;
+    if (apply_map)
+        c->ch_map_info = &avr->ch_map_info;
+
     if (avr->dither_method == AV_RESAMPLE_DITHER_TRIANGULAR_NS &&
         sample_rate != 48000 && sample_rate != 44100) {
         av_log(avr, AV_LOG_WARNING, "sample rate must be 48000 or 44100 Hz "
@@ -379,19 +391,20 @@ DitherContext *ff_dither_alloc(AVAudioResampleContext *avr,
             goto fail;
 
         c->ac_out = ff_audio_convert_alloc(avr, out_fmt, AV_SAMPLE_FMT_S16P,
-                                           channels, sample_rate);
+                                           channels, sample_rate, 0);
         if (!c->ac_out)
             goto fail;
     }
 
-    if (in_fmt != AV_SAMPLE_FMT_FLTP) {
+    if (in_fmt != AV_SAMPLE_FMT_FLTP || c->apply_map) {
         c->flt_data = ff_audio_data_alloc(channels, 1024, AV_SAMPLE_FMT_FLTP,
                                           "dither flt buffer");
         if (!c->flt_data)
             goto fail;
-
+    }
+    if (in_fmt != AV_SAMPLE_FMT_FLTP) {
         c->ac_in = ff_audio_convert_alloc(avr, AV_SAMPLE_FMT_FLTP, in_fmt,
-                                          channels, sample_rate);
+                                          channels, sample_rate, c->apply_map);
         if (!c->ac_in)
             goto fail;
     }
index 8b30dd2..d6a7d3e 100644 (file)
@@ -66,7 +66,7 @@ typedef struct DitherDSPContext {
 DitherContext *ff_dither_alloc(AVAudioResampleContext *avr,
                                enum AVSampleFormat out_fmt,
                                enum AVSampleFormat in_fmt,
-                               int channels, int sample_rate);
+                               int channels, int sample_rate, int apply_map);
 
 /**
  * Free a DitherContext.
index c094f08..057f89a 100644 (file)
@@ -32,6 +32,24 @@ typedef struct AudioConvert AudioConvert;
 typedef struct AudioMix AudioMix;
 typedef struct ResampleContext ResampleContext;
 
+enum RemapPoint {
+    REMAP_NONE,
+    REMAP_IN_COPY,
+    REMAP_IN_CONVERT,
+    REMAP_OUT_COPY,
+    REMAP_OUT_CONVERT,
+};
+
+typedef struct ChannelMapInfo {
+    int channel_map[AVRESAMPLE_MAX_CHANNELS];   /**< source index of each output channel, -1 if not remapped */
+    int do_remap;                               /**< remap needed */
+    int channel_copy[AVRESAMPLE_MAX_CHANNELS];  /**< dest index to copy from */
+    int do_copy;                                /**< copy needed */
+    int channel_zero[AVRESAMPLE_MAX_CHANNELS];  /**< dest index to zero */
+    int do_zero;                                /**< zeroing needed */
+    int input_map[AVRESAMPLE_MAX_CHANNELS];     /**< dest index of each input channel */
+} ChannelMapInfo;
+
 struct AVAudioResampleContext {
     const AVClass *av_class;        /**< AVClass for logging and AVOptions  */
 
@@ -65,6 +83,7 @@ struct AVAudioResampleContext {
     int resample_needed;    /**< resampling is needed                       */
     int in_convert_needed;  /**< input sample format conversion is needed   */
     int out_convert_needed; /**< output sample format conversion is needed  */
+    int in_copy_needed;     /**< input data copy is needed                  */
 
     AudioData *in_buffer;           /**< buffer for converted input         */
     AudioData *resample_out_buffer; /**< buffer for output from resampler   */
@@ -82,6 +101,10 @@ struct AVAudioResampleContext {
      * only used if avresample_set_matrix() is called before avresample_open()
      */
     double *mix_matrix;
+
+    int use_channel_map;
+    enum RemapPoint remap_point;
+    ChannelMapInfo ch_map_info;
 };
 
 #endif /* AVRESAMPLE_INTERNAL_H */
index a303880..b79def9 100644 (file)
@@ -96,20 +96,84 @@ int avresample_open(AVAudioResampleContext *avr)
                av_get_sample_fmt_name(avr->internal_sample_fmt));
     }
 
-    /* set sample format conversion parameters */
+    /* treat all mono as planar for easier comparison */
     if (avr->in_channels == 1)
         avr->in_sample_fmt = av_get_planar_sample_fmt(avr->in_sample_fmt);
     if (avr->out_channels == 1)
         avr->out_sample_fmt = av_get_planar_sample_fmt(avr->out_sample_fmt);
-    avr->in_convert_needed = (avr->resample_needed || avr->mixing_needed) &&
-                              avr->in_sample_fmt != avr->internal_sample_fmt;
+
+    /* we may need to add an extra conversion in order to remap channels if
+       the output format is not planar */
+    if (avr->use_channel_map && !avr->mixing_needed && !avr->resample_needed &&
+        !av_sample_fmt_is_planar(avr->out_sample_fmt)) {
+        avr->internal_sample_fmt = av_get_planar_sample_fmt(avr->out_sample_fmt);
+    }
+
+    /* set sample format conversion parameters */
     if (avr->resample_needed || avr->mixing_needed)
+        avr->in_convert_needed = avr->in_sample_fmt != avr->internal_sample_fmt;
+    else
+        avr->in_convert_needed = avr->use_channel_map &&
+                                 !av_sample_fmt_is_planar(avr->out_sample_fmt);
+
+    if (avr->resample_needed || avr->mixing_needed || avr->in_convert_needed)
         avr->out_convert_needed = avr->internal_sample_fmt != avr->out_sample_fmt;
     else
         avr->out_convert_needed = avr->in_sample_fmt != avr->out_sample_fmt;
 
+    avr->in_copy_needed = !avr->in_convert_needed && (avr->mixing_needed ||
+                          (avr->use_channel_map && avr->resample_needed));
+
+    if (avr->use_channel_map) {
+        if (avr->in_copy_needed) {
+            avr->remap_point = REMAP_IN_COPY;
+            av_dlog(avr, "remap channels during in_copy\n");
+        } else if (avr->in_convert_needed) {
+            avr->remap_point = REMAP_IN_CONVERT;
+            av_dlog(avr, "remap channels during in_convert\n");
+        } else if (avr->out_convert_needed) {
+            avr->remap_point = REMAP_OUT_CONVERT;
+            av_dlog(avr, "remap channels during out_convert\n");
+        } else {
+            avr->remap_point = REMAP_OUT_COPY;
+            av_dlog(avr, "remap channels during out_copy\n");
+        }
+
+#ifdef DEBUG
+        {
+            int ch;
+            av_dlog(avr, "output map: ");
+            if (avr->ch_map_info.do_remap)
+                for (ch = 0; ch < avr->in_channels; ch++)
+                    av_dlog(avr, " % 2d", avr->ch_map_info.channel_map[ch]);
+            else
+                av_dlog(avr, "n/a");
+            av_dlog(avr, "\n");
+            av_dlog(avr, "copy map:   ");
+            if (avr->ch_map_info.do_copy)
+                for (ch = 0; ch < avr->in_channels; ch++)
+                    av_dlog(avr, " % 2d", avr->ch_map_info.channel_copy[ch]);
+            else
+                av_dlog(avr, "n/a");
+            av_dlog(avr, "\n");
+            av_dlog(avr, "zero map:   ");
+            if (avr->ch_map_info.do_zero)
+                for (ch = 0; ch < avr->in_channels; ch++)
+                    av_dlog(avr, " % 2d", avr->ch_map_info.channel_zero[ch]);
+            else
+                av_dlog(avr, "n/a");
+            av_dlog(avr, "\n");
+            av_dlog(avr, "input map:  ");
+            for (ch = 0; ch < avr->in_channels; ch++)
+                av_dlog(avr, " % 2d", avr->ch_map_info.input_map[ch]);
+            av_dlog(avr, "\n");
+        }
+#endif
+    } else
+        avr->remap_point = REMAP_NONE;
+
     /* allocate buffers */
-    if (avr->mixing_needed || avr->in_convert_needed) {
+    if (avr->in_copy_needed || avr->in_convert_needed) {
         avr->in_buffer = ff_audio_data_alloc(FFMAX(avr->in_channels, avr->out_channels),
                                              0, avr->internal_sample_fmt,
                                              "in_buffer");
@@ -146,7 +210,8 @@ int avresample_open(AVAudioResampleContext *avr)
     if (avr->in_convert_needed) {
         avr->ac_in = ff_audio_convert_alloc(avr, avr->internal_sample_fmt,
                                             avr->in_sample_fmt, avr->in_channels,
-                                            avr->in_sample_rate);
+                                            avr->in_sample_rate,
+                                            avr->remap_point == REMAP_IN_CONVERT);
         if (!avr->ac_in) {
             ret = AVERROR(ENOMEM);
             goto error;
@@ -160,7 +225,8 @@ int avresample_open(AVAudioResampleContext *avr)
             src_fmt = avr->in_sample_fmt;
         avr->ac_out = ff_audio_convert_alloc(avr, avr->out_sample_fmt, src_fmt,
                                              avr->out_channels,
-                                             avr->out_sample_rate);
+                                             avr->out_sample_rate,
+                                             avr->remap_point == REMAP_OUT_CONVERT);
         if (!avr->ac_out) {
             ret = AVERROR(ENOMEM);
             goto error;
@@ -200,6 +266,8 @@ void avresample_close(AVAudioResampleContext *avr)
     ff_audio_resample_free(&avr->resample);
     ff_audio_mix_free(&avr->am);
     av_freep(&avr->mix_matrix);
+
+    avr->use_channel_map = 0;
 }
 
 void avresample_free(AVAudioResampleContext **avr)
@@ -242,7 +310,9 @@ static int handle_buffered_output(AVAudioResampleContext *avr,
            data in the output FIFO */
         av_dlog(avr, "[copy] %s to output\n", converted->name);
         output->nb_samples = 0;
-        ret = ff_audio_data_copy(output, converted);
+        ret = ff_audio_data_copy(output, converted,
+                                 avr->remap_point == REMAP_OUT_COPY ?
+                                 &avr->ch_map_info : NULL);
         if (ret < 0)
             return ret;
         av_dlog(avr, "[end conversion]\n");
@@ -306,11 +376,24 @@ int attribute_align_arg avresample_convert(AVAudioResampleContext *avr,
             /* in some rare cases we can copy input to output and upmix
                directly in the output buffer */
             av_dlog(avr, "[copy] %s to output\n", current_buffer->name);
-            ret = ff_audio_data_copy(&output_buffer, current_buffer);
+            ret = ff_audio_data_copy(&output_buffer, current_buffer,
+                                     avr->remap_point == REMAP_OUT_COPY ?
+                                     &avr->ch_map_info : NULL);
             if (ret < 0)
                 return ret;
             current_buffer = &output_buffer;
-        } else if (avr->mixing_needed || avr->in_convert_needed) {
+        } else if (avr->remap_point == REMAP_OUT_COPY &&
+                   (!direct_output || out_samples < in_samples)) {
+            /* if remapping channels during output copy, we may need to
+             * use an intermediate buffer in order to remap before adding
+             * samples to the output fifo */
+            av_dlog(avr, "[copy] %s to out_buffer\n", current_buffer->name);
+            ret = ff_audio_data_copy(avr->out_buffer, current_buffer,
+                                     &avr->ch_map_info);
+            if (ret < 0)
+                return ret;
+            current_buffer = avr->out_buffer;
+        } else if (avr->in_copy_needed || avr->in_convert_needed) {
             /* if needed, copy or convert input to in_buffer, and downmix if
                applicable */
             if (avr->in_convert_needed) {
@@ -325,7 +408,9 @@ int attribute_align_arg avresample_convert(AVAudioResampleContext *avr,
                     return ret;
             } else {
                 av_dlog(avr, "[copy] %s to in_buffer\n", current_buffer->name);
-                ret = ff_audio_data_copy(avr->in_buffer, current_buffer);
+                ret = ff_audio_data_copy(avr->in_buffer, current_buffer,
+                                         avr->remap_point == REMAP_IN_COPY ?
+                                         &avr->ch_map_info : NULL);
                 if (ret < 0)
                     return ret;
             }
@@ -470,6 +555,57 @@ int avresample_set_matrix(AVAudioResampleContext *avr, const double *matrix,
     return 0;
 }
 
+int avresample_set_channel_mapping(AVAudioResampleContext *avr,
+                                   const int *channel_map)
+{
+    ChannelMapInfo *info = &avr->ch_map_info;
+    int in_channels, ch, i;
+
+    in_channels = av_get_channel_layout_nb_channels(avr->in_channel_layout);
+    if (in_channels <= 0 ||  in_channels > AVRESAMPLE_MAX_CHANNELS) {
+        av_log(avr, AV_LOG_ERROR, "Invalid input channel layout\n");
+        return AVERROR(EINVAL);
+    }
+
+    memset(info, 0, sizeof(*info));
+    memset(info->input_map, -1, sizeof(info->input_map));
+
+    for (ch = 0; ch < in_channels; ch++) {
+        if (channel_map[ch] >= in_channels) {
+            av_log(avr, AV_LOG_ERROR, "Invalid channel map\n");
+            return AVERROR(EINVAL);
+        }
+        if (channel_map[ch] < 0) {
+            info->channel_zero[ch] =  1;
+            info->channel_map[ch]  = -1;
+            info->do_zero          =  1;
+        } else if (info->input_map[channel_map[ch]] >= 0) {
+            info->channel_copy[ch] = info->input_map[channel_map[ch]];
+            info->channel_map[ch]  = -1;
+            info->do_copy          =  1;
+        } else {
+            info->channel_map[ch]            = channel_map[ch];
+            info->input_map[channel_map[ch]] = ch;
+            info->do_remap                   =  1;
+        }
+    }
+    /* Fill-in unmapped input channels with unmapped output channels.
+       This is used when remapping during conversion from interleaved to
+       planar format. */
+    for (ch = 0, i = 0; ch < in_channels && i < in_channels; ch++, i++) {
+        while (ch < in_channels && info->input_map[ch] >= 0)
+            ch++;
+        while (i < in_channels && info->channel_map[i] >= 0)
+            i++;
+        if (ch >= in_channels || i >= in_channels)
+            break;
+        info->input_map[ch] = i;
+    }
+
+    avr->use_channel_map = 1;
+    return 0;
+}
+
 int avresample_available(AVAudioResampleContext *avr)
 {
     return av_audio_fifo_size(avr->out_fifo);
index ebcd07f..387d097 100644 (file)
@@ -20,8 +20,8 @@
 #define AVRESAMPLE_VERSION_H
 
 #define LIBAVRESAMPLE_VERSION_MAJOR  1
-#define LIBAVRESAMPLE_VERSION_MINOR  0
-#define LIBAVRESAMPLE_VERSION_MICRO  1
+#define LIBAVRESAMPLE_VERSION_MINOR  1
+#define LIBAVRESAMPLE_VERSION_MICRO  0
 
 #define LIBAVRESAMPLE_VERSION_INT  AV_VERSION_INT(LIBAVRESAMPLE_VERSION_MAJOR, \
                                                   LIBAVRESAMPLE_VERSION_MINOR, \