aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMichael Niedermayer <michaelni@gmx.at>2013-01-08 12:56:41 +0100
committerMichael Niedermayer <michaelni@gmx.at>2013-01-08 12:56:41 +0100
commit249fca3df9f9790369d38aaa63c828c3e2a30936 (patch)
treefe8f3784cc3160505617bf76b32534c4b5367658
parent3a0bac27b3c851924a5fbde5301a3ad6bf951aba (diff)
parent074a00d192c0e749d677b008b337da42597e780f (diff)
downloadffmpeg-249fca3df9f9790369d38aaa63c828c3e2a30936.tar.gz
Merge commit '074a00d192c0e749d677b008b337da42597e780f'
* commit '074a00d192c0e749d677b008b337da42597e780f': lavr: add a public function for setting a custom channel map lavr: typedef internal structs in internal.h doc: Extend commit message section Conflicts: doc/APIchanges doc/developer.texi Merged-by: Michael Niedermayer <michaelni@gmx.at>
-rw-r--r--doc/APIchanges4
-rw-r--r--doc/developer.texi7
-rw-r--r--libavresample/audio_convert.c50
-rw-r--r--libavresample/audio_convert.h7
-rw-r--r--libavresample/audio_data.c32
-rw-r--r--libavresample/audio_data.h8
-rw-r--r--libavresample/audio_mix.h3
-rw-r--r--libavresample/avresample.h30
-rw-r--r--libavresample/dither.c25
-rw-r--r--libavresample/dither.h2
-rw-r--r--libavresample/internal.h32
-rw-r--r--libavresample/resample.c1
-rw-r--r--libavresample/resample.h3
-rw-r--r--libavresample/utils.c161
-rw-r--r--libavresample/version.h4
15 files changed, 328 insertions, 41 deletions
diff --git a/doc/APIchanges b/doc/APIchanges
index a79dd2df58..fc5c3f2cb7 100644
--- a/doc/APIchanges
+++ b/doc/APIchanges
@@ -132,6 +132,10 @@ API changes, most recent first:
2012-03-26 - a67d9cf - lavfi 2.66.100
Add avfilter_fill_frame_from_{audio_,}buffer_ref() functions.
+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().
diff --git a/doc/developer.texi b/doc/developer.texi
index fce842a006..7e4b12c528 100644
--- a/doc/developer.texi
+++ b/doc/developer.texi
@@ -229,6 +229,13 @@ For Emacs, add these roughly equivalent lines to your @file{.emacs.d/init.el}:
(#ifdef etc) by default so it does not interfere with other developers'
work.
@item
+ The commit message should have a short first line in the form of
+ a @samp{topic: short description} as a header, separated by a newline
+ from the body consisting of an explanation of why the change is necessary.
+ If the commit fixes a known bug on the bug tracker, the commit message
+ should include its bug ID. Referring to the issue on the bug tracker does
+ not exempt you from writing an excerpt of the bug in the commit message.
+@item
You do not have to over-test things. If it works for you, and you think it
should work for others, then commit. If your code has problems
(portability, triggers compiler bugs, unusual environment etc) they will be
diff --git a/libavresample/audio_convert.c b/libavresample/audio_convert.c
index eb3bc1f1de..b57d2fa650 100644
--- a/libavresample/audio_convert.c
+++ b/libavresample/audio_convert.c
@@ -30,7 +30,6 @@
#include "audio_convert.h"
#include "audio_data.h"
#include "dither.h"
-#include "internal.h"
enum ConvFuncType {
CONV_FUNC_TYPE_FLAT,
@@ -51,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;
@@ -260,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;
@@ -273,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;
@@ -310,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 */
@@ -336,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) {
@@ -363,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;
diff --git a/libavresample/audio_convert.h b/libavresample/audio_convert.h
index b8808f176d..6a3089d4fb 100644
--- a/libavresample/audio_convert.h
+++ b/libavresample/audio_convert.h
@@ -23,10 +23,9 @@
#include "libavutil/samplefmt.h"
#include "avresample.h"
+#include "internal.h"
#include "audio_data.h"
-typedef struct AudioConvert AudioConvert;
-
/**
* Set conversion function if the parameters match.
*
@@ -59,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.
diff --git a/libavresample/audio_data.c b/libavresample/audio_data.c
index 199a68cb11..c52f518e9a 100644
--- a/libavresample/audio_data.c
+++ b/libavresample/audio_data.c
@@ -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;
diff --git a/libavresample/audio_data.h b/libavresample/audio_data.h
index 558e7e67ec..97236bb5de 100644
--- a/libavresample/audio_data.h
+++ b/libavresample/audio_data.h
@@ -27,11 +27,12 @@
#include "libavutil/log.h"
#include "libavutil/samplefmt.h"
#include "avresample.h"
+#include "internal.h"
/**
* Audio buffer used for intermediate storage between conversion phases.
*/
-typedef struct AudioData {
+struct AudioData {
const AVClass *class; /**< AVClass for logging */
uint8_t *data[AVRESAMPLE_MAX_CHANNELS]; /**< data plane pointers */
uint8_t *buffer; /**< data buffer */
@@ -50,7 +51,7 @@ typedef struct AudioData {
int ptr_align; /**< minimum data pointer alignment */
int samples_align; /**< allocated samples alignment */
const char *name; /**< name for debug logging */
-} AudioData;
+};
int ff_audio_data_set_channels(AudioData *a, int channels);
@@ -117,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.
diff --git a/libavresample/audio_mix.h b/libavresample/audio_mix.h
index 4ccfc1fd28..5bae5ab6da 100644
--- a/libavresample/audio_mix.h
+++ b/libavresample/audio_mix.h
@@ -25,13 +25,12 @@
#include "libavutil/samplefmt.h"
#include "avresample.h"
+#include "internal.h"
#include "audio_data.h"
typedef void (mix_func)(uint8_t **src, void **matrix, int len, int out_ch,
int in_ch);
-typedef struct AudioMix AudioMix;
-
/**
* Set mixing function if the parameters match.
*
diff --git a/libavresample/avresample.h b/libavresample/avresample.h
index 0012787404..d26f2ca223 100644
--- a/libavresample/avresample.h
+++ b/libavresample/avresample.h
@@ -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
diff --git a/libavresample/dither.c b/libavresample/dither.c
index 9c1e1c1101..dfff03e756 100644
--- a/libavresample/dither.c
+++ b/libavresample/dither.c
@@ -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;
}
diff --git a/libavresample/dither.h b/libavresample/dither.h
index 8b30dd23e0..d6a7d3ea8d 100644
--- a/libavresample/dither.h
+++ b/libavresample/dither.h
@@ -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.
diff --git a/libavresample/internal.h b/libavresample/internal.h
index 2e139abf2b..057f89a49c 100644
--- a/libavresample/internal.h
+++ b/libavresample/internal.h
@@ -26,10 +26,29 @@
#include "libavutil/opt.h"
#include "libavutil/samplefmt.h"
#include "avresample.h"
-#include "audio_convert.h"
-#include "audio_data.h"
-#include "audio_mix.h"
-#include "resample.h"
+
+typedef struct AudioData AudioData;
+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 */
@@ -64,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 */
@@ -81,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 */
diff --git a/libavresample/resample.c b/libavresample/resample.c
index dc121fe56d..69c9bab893 100644
--- a/libavresample/resample.c
+++ b/libavresample/resample.c
@@ -23,6 +23,7 @@
#include "libavutil/libm.h"
#include "libavutil/log.h"
#include "internal.h"
+#include "resample.h"
#include "audio_data.h"
struct ResampleContext {
diff --git a/libavresample/resample.h b/libavresample/resample.h
index 7534e26ad4..4544dab92f 100644
--- a/libavresample/resample.h
+++ b/libavresample/resample.h
@@ -22,10 +22,9 @@
#define AVRESAMPLE_RESAMPLE_H
#include "avresample.h"
+#include "internal.h"
#include "audio_data.h"
-typedef struct ResampleContext ResampleContext;
-
/**
* Allocate and initialize a ResampleContext.
*
diff --git a/libavresample/utils.c b/libavresample/utils.c
index 8f6583d6e8..c159e33206 100644
--- a/libavresample/utils.c
+++ b/libavresample/utils.c
@@ -26,8 +26,11 @@
#include "libavutil/opt.h"
#include "avresample.h"
-#include "audio_data.h"
#include "internal.h"
+#include "audio_data.h"
+#include "audio_convert.h"
+#include "audio_mix.h"
+#include "resample.h"
int avresample_open(AVAudioResampleContext *avr)
{
@@ -93,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");
@@ -143,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;
@@ -157,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;
@@ -197,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)
@@ -239,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");
@@ -303,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) {
@@ -322,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;
}
@@ -467,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);
diff --git a/libavresample/version.h b/libavresample/version.h
index ebcd07f57c..387d097d3a 100644
--- a/libavresample/version.h
+++ b/libavresample/version.h
@@ -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, \