This allows reordering, duplication, and silencing of input channels.
Unified · Split
| @@ -13,6 +13,10 @@ libavutil: 2012-10-22 · 12 unmodified lines | |||
| 13 | 13 | ||
| 14 | API changes, most recent first: | 14 | API changes, most recent first: |
| 15 | 15 | ||
| 16 | 2013-xx-xx - xxxxxxx - lavr 1.1.0 | ||
| 17 | Add avresample_set_channel_mapping() for input channel reordering, | ||
| 18 | duplication, and silencing. | ||
| 19 | |||
| 16 | 2012-xx-xx - xxxxxxx - lavu 52.2.1 - avstring.h | 20 | 2012-xx-xx - xxxxxxx - lavu 52.2.1 - avstring.h |
| 17 | Add av_basename() and av_dirname(). | 21 | Add av_basename() and av_dirname(). |
| 18 | 22 | ||
| @@ -50,6 +50,7 @@ struct AudioConvert { · 49 unmodified lines | |||
| 50 | DitherContext *dc; | 50 | DitherContext *dc; |
| 51 | enum AVSampleFormat in_fmt; | 51 | enum AVSampleFormat in_fmt; |
| 52 | enum AVSampleFormat out_fmt; | 52 | enum AVSampleFormat out_fmt; |
| 53 | int apply_map; | ||
| 53 | int channels; | 54 | int channels; |
| 54 | int planes; | 55 | int planes; |
| 55 | int ptr_align; | 56 | int ptr_align; |
| @@ -259,7 +260,8 @@ void ff_audio_convert_free(AudioConvert **ac) · 203 unmodified lines | |||
| 259 | AudioConvert *ff_audio_convert_alloc(AVAudioResampleContext *avr, | 260 | AudioConvert *ff_audio_convert_alloc(AVAudioResampleContext *avr, |
| 260 | enum AVSampleFormat out_fmt, | 261 | enum AVSampleFormat out_fmt, |
| 261 | enum AVSampleFormat in_fmt, | 262 | enum AVSampleFormat in_fmt, |
| 262 | int channels, int sample_rate) | 263 | int channels, int sample_rate, |
| 264 | int apply_map) | ||
| 263 | { | 265 | { |
| 264 | AudioConvert *ac; | 266 | AudioConvert *ac; |
| 265 | int in_planar, out_planar; | 267 | int in_planar, out_planar; |
| @@ -272,11 +274,13 @@ AudioConvert *ff_audio_convert_alloc(AVAudioResampleContext *avr, · 6 unmodified lines | |||
| 272 | ac->out_fmt = out_fmt; | 274 | ac->out_fmt = out_fmt; |
| 273 | ac->in_fmt = in_fmt; | 275 | ac->in_fmt = in_fmt; |
| 274 | ac->channels = channels; | 276 | ac->channels = channels; |
| 277 | ac->apply_map = apply_map; | ||
| 275 | 278 | ||
| 276 | if (avr->dither_method != AV_RESAMPLE_DITHER_NONE && | 279 | if (avr->dither_method != AV_RESAMPLE_DITHER_NONE && |
| 277 | av_get_packed_sample_fmt(out_fmt) == AV_SAMPLE_FMT_S16 && | 280 | av_get_packed_sample_fmt(out_fmt) == AV_SAMPLE_FMT_S16 && |
| 278 | av_get_bytes_per_sample(in_fmt) > 2) { | 281 | av_get_bytes_per_sample(in_fmt) > 2) { |
| 279 | ac->dc = ff_dither_alloc(avr, out_fmt, in_fmt, channels, sample_rate); | 282 | ac->dc = ff_dither_alloc(avr, out_fmt, in_fmt, channels, sample_rate, |
| 283 | apply_map); | ||
| 280 | if (!ac->dc) { | 284 | if (!ac->dc) { |
| 281 | av_free(ac); | 285 | av_free(ac); |
| 282 | return NULL; | 286 | return NULL; |
| @@ -309,6 +313,7 @@ int ff_audio_convert(AudioConvert *ac, AudioData *out, AudioData *in) · 26 unmodified lines | |||
| 309 | { | 313 | { |
| 310 | int use_generic = 1; | 314 | int use_generic = 1; |
| 311 | int len = in->nb_samples; | 315 | int len = in->nb_samples; |
| 316 | int p; | ||
| 312 | 317 | ||
| 313 | if (ac->dc) { | 318 | if (ac->dc) { |
| 314 | /* dithered conversion */ | 319 | /* dithered conversion */ |
| @@ -335,9 +340,46 @@ int ff_audio_convert(AudioConvert *ac, AudioData *out, AudioData *in) · 20 unmodified lines | |||
| 335 | av_get_sample_fmt_name(ac->out_fmt), | 340 | av_get_sample_fmt_name(ac->out_fmt), |
| 336 | use_generic ? ac->func_descr_generic : ac->func_descr); | 341 | use_generic ? ac->func_descr_generic : ac->func_descr); |
| 337 | 342 | ||
| 343 | if (ac->apply_map) { | ||
| 344 | ChannelMapInfo *map = &ac->avr->ch_map_info; | ||
| 345 | |||
| 346 | if (!av_sample_fmt_is_planar(ac->out_fmt)) { | ||
| 347 | av_log(ac->avr, AV_LOG_ERROR, "cannot remap packed format during conversion\n"); | ||
| 348 | return AVERROR(EINVAL); | ||
| 349 | } | ||
| 350 | |||
| 351 | if (map->do_remap) { | ||
| 352 | if (av_sample_fmt_is_planar(ac->in_fmt)) { | ||
| 353 | conv_func_flat *convert = use_generic ? ac->conv_flat_generic : | ||
| 354 | ac->conv_flat; | ||
| 355 | |||
| 356 | for (p = 0; p < ac->planes; p++) | ||
| 357 | if (map->channel_map[p] >= 0) | ||
| 358 | convert(out->data[p], in->data[map->channel_map[p]], len); | ||
| 359 | } else { | ||
| 360 | uint8_t *data[AVRESAMPLE_MAX_CHANNELS]; | ||
| 361 | conv_func_deinterleave *convert = use_generic ? | ||
| 362 | ac->conv_deinterleave_generic : | ||
| 363 | ac->conv_deinterleave; | ||
| 364 | |||
| 365 | for (p = 0; p < ac->channels; p++) | ||
| 366 | data[map->input_map[p]] = out->data[p]; | ||
| 367 | |||
| 368 | convert(data, in->data[0], len, ac->channels); | ||
| 369 | } | ||
| 370 | } | ||
| 371 | if (map->do_copy || map->do_zero) { | ||
| 372 | for (p = 0; p < ac->planes; p++) { | ||
| 373 | if (map->channel_copy[p]) | ||
| 374 | memcpy(out->data[p], out->data[map->channel_copy[p]], | ||
| 375 | len * out->stride); | ||
| 376 | else if (map->channel_zero[p]) | ||
| 377 | av_samples_set_silence(&out->data[p], 0, len, 1, ac->out_fmt); | ||
| 378 | } | ||
| 379 | } | ||
| 380 | } else { | ||
| 338 | switch (ac->func_type) { | 381 | switch (ac->func_type) { |
| 339 | case CONV_FUNC_TYPE_FLAT: { | 382 | case CONV_FUNC_TYPE_FLAT: { |
| 340 | int p; | ||
| 341 | if (!in->is_planar) | 383 | if (!in->is_planar) |
| 342 | len *= in->channels; | 384 | len *= in->channels; |
| 343 | if (use_generic) { | 385 | if (use_generic) { |
| @@ -362,6 +404,7 @@ int ff_audio_convert(AudioConvert *ac, AudioData *out, AudioData *in) · 18 unmodified lines | |||
| 362 | ac->conv_deinterleave(out->data, in->data[0], len, ac->channels); | 404 | ac->conv_deinterleave(out->data, in->data[0], len, ac->channels); |
| 363 | break; | 405 | break; |
| 364 | } | 406 | } |
| 407 | } | ||
| 365 | 408 | ||
| 366 | out->nb_samples = in->nb_samples; | 409 | out->nb_samples = in->nb_samples; |
| 367 | return 0; | 410 | return 0; |
| @@ -58,12 +58,14 @@ void ff_audio_convert_set_func(AudioConvert *ac, enum AVSampleFormat out_fmt, · 57 unmodified lines | |||
| 58 | * @param in_fmt input sample format | 58 | * @param in_fmt input sample format |
| 59 | * @param channels number of channels | 59 | * @param channels number of channels |
| 60 | * @param sample_rate sample rate (used for dithering) | 60 | * @param sample_rate sample rate (used for dithering) |
| 61 | * @param apply_map apply channel map during conversion | ||
| 61 | * @return newly-allocated AudioConvert context | 62 | * @return newly-allocated AudioConvert context |
| 62 | */ | 63 | */ |
| 63 | AudioConvert *ff_audio_convert_alloc(AVAudioResampleContext *avr, | 64 | AudioConvert *ff_audio_convert_alloc(AVAudioResampleContext *avr, |
| 64 | enum AVSampleFormat out_fmt, | 65 | enum AVSampleFormat out_fmt, |
| 65 | enum AVSampleFormat in_fmt, | 66 | enum AVSampleFormat in_fmt, |
| 66 | int channels, int sample_rate); | 67 | int channels, int sample_rate, |
| 68 | int apply_map); | ||
| 67 | 69 | ||
| 68 | /** | 70 | /** |
| 69 | * Free AudioConvert. | 71 | * Free AudioConvert. |
| @@ -213,7 +213,7 @@ void ff_audio_data_free(AudioData **a) · 212 unmodified lines | |||
| 213 | av_freep(a); | 213 | av_freep(a); |
| 214 | } | 214 | } |
| 215 | 215 | ||
| 216 | int ff_audio_data_copy(AudioData *dst, AudioData *src) | 216 | int ff_audio_data_copy(AudioData *dst, AudioData *src, ChannelMapInfo *map) |
| 217 | { | 217 | { |
| 218 | int ret, p; | 218 | int ret, p; |
| 219 | 219 | ||
| @@ -221,6 +221,11 @@ int ff_audio_data_copy(AudioData *dst, AudioData *src) · 1 unmodified line | |||
| 221 | if (dst->sample_fmt != src->sample_fmt || dst->channels < src->channels) | 221 | if (dst->sample_fmt != src->sample_fmt || dst->channels < src->channels) |
| 222 | return AVERROR(EINVAL); | 222 | return AVERROR(EINVAL); |
| 223 | 223 | ||
| 224 | if (map && !src->is_planar) { | ||
| 225 | av_log(src, AV_LOG_ERROR, "cannot remap packed format during copy\n"); | ||
| 226 | return AVERROR(EINVAL); | ||
| 227 | } | ||
| 228 | |||
| 224 | /* if the input is empty, just empty the output */ | 229 | /* if the input is empty, just empty the output */ |
| 225 | if (!src->nb_samples) { | 230 | if (!src->nb_samples) { |
| 226 | dst->nb_samples = 0; | 231 | dst->nb_samples = 0; |
| @@ -233,8 +238,29 @@ int ff_audio_data_copy(AudioData *dst, AudioData *src) · 6 unmodified lines | |||
| 233 | return ret; | 238 | return ret; |
| 234 | 239 | ||
| 235 | /* copy data */ | 240 | /* copy data */ |
| 236 | for (p = 0; p < src->planes; p++) | 241 | if (map) { |
| 237 | memcpy(dst->data[p], src->data[p], src->nb_samples * src->stride); | 242 | if (map->do_remap) { |
| 243 | for (p = 0; p < src->planes; p++) { | ||
| 244 | if (map->channel_map[p] >= 0) | ||
| 245 | memcpy(dst->data[p], src->data[map->channel_map[p]], | ||
| 246 | src->nb_samples * src->stride); | ||
| 247 | } | ||
| 248 | } | ||
| 249 | if (map->do_copy || map->do_zero) { | ||
| 250 | for (p = 0; p < src->planes; p++) { | ||
| 251 | if (map->channel_copy[p]) | ||
| 252 | memcpy(dst->data[p], dst->data[map->channel_copy[p]], | ||
| 253 | src->nb_samples * src->stride); | ||
| 254 | else if (map->channel_zero[p]) | ||
| 255 | av_samples_set_silence(&dst->data[p], 0, src->nb_samples, | ||
| 256 | 1, dst->sample_fmt); | ||
| 257 | } | ||
| 258 | } | ||
| 259 | } else { | ||
| 260 | for (p = 0; p < src->planes; p++) | ||
| 261 | memcpy(dst->data[p], src->data[p], src->nb_samples * src->stride); | ||
| 262 | } | ||
| 263 | |||
| 238 | dst->nb_samples = src->nb_samples; | 264 | dst->nb_samples = src->nb_samples; |
| 239 | 265 | ||
| 240 | return 0; | 266 | return 0; |
| @@ -118,9 +118,10 @@ void ff_audio_data_free(AudioData **a); · 117 unmodified lines | |||
| 118 | * | 118 | * |
| 119 | * @param out output AudioData | 119 | * @param out output AudioData |
| 120 | * @param in input AudioData | 120 | * @param in input AudioData |
| 121 | * @param map channel map, NULL if not remapping | ||
| 121 | * @return 0 on success, negative AVERROR value on error | 122 | * @return 0 on success, negative AVERROR value on error |
| 122 | */ | 123 | */ |
| 123 | int ff_audio_data_copy(AudioData *out, AudioData *in); | 124 | int ff_audio_data_copy(AudioData *out, AudioData *in, ChannelMapInfo *map); |
| 124 | 125 | ||
| 125 | /** | 126 | /** |
| 126 | * Append data from one AudioData to the end of another. | 127 | * Append data from one AudioData to the end of another. |
| @@ -258,6 +258,36 @@ int avresample_get_matrix(AVAudioResampleContext *avr, double *matrix, · 257 unmodified lines | |||
| 258 | int avresample_set_matrix(AVAudioResampleContext *avr, const double *matrix, | 258 | int avresample_set_matrix(AVAudioResampleContext *avr, const double *matrix, |
| 259 | int stride); | 259 | int stride); |
| 260 | 260 | ||
| 261 | /** | ||
| 262 | * Set a customized input channel mapping. | ||
| 263 | * | ||
| 264 | * This function can only be called when the allocated context is not open. | ||
| 265 | * Also, the input channel layout must have already been set. | ||
| 266 | * | ||
| 267 | * Calling avresample_close() on the context will clear the channel mapping. | ||
| 268 | * | ||
| 269 | * The map for each input channel specifies the channel index in the source to | ||
| 270 | * use for that particular channel, or -1 to mute the channel. Source channels | ||
| 271 | * can be duplicated by using the same index for multiple input channels. | ||
| 272 | * | ||
| 273 | * Examples: | ||
| 274 | * | ||
| 275 | * Reordering 5.1 AAC order (C,L,R,Ls,Rs,LFE) to Libav order (L,R,C,LFE,Ls,Rs): | ||
| 276 | * { 1, 2, 0, 5, 3, 4 } | ||
| 277 | * | ||
| 278 | * Muting the 3rd channel in 4-channel input: | ||
| 279 | * { 0, 1, -1, 3 } | ||
| 280 | * | ||
| 281 | * Duplicating the left channel of stereo input: | ||
| 282 | * { 0, 0 } | ||
| 283 | * | ||
| 284 | * @param avr audio resample context | ||
| 285 | * @param channel_map customized input channel mapping | ||
| 286 | * @return 0 on success, negative AVERROR code on failure | ||
| 287 | */ | ||
| 288 | int avresample_set_channel_mapping(AVAudioResampleContext *avr, | ||
| 289 | const int *channel_map); | ||
| 290 | |||
| 261 | /** | 291 | /** |
| 262 | * Set compensation for resampling. | 292 | * Set compensation for resampling. |
| 263 | * | 293 | * |
| @@ -53,6 +53,8 @@ typedef struct DitherState { · 52 unmodified lines | |||
| 53 | struct DitherContext { | 53 | struct DitherContext { |
| 54 | DitherDSPContext ddsp; | 54 | DitherDSPContext ddsp; |
| 55 | enum AVResampleDitherMethod method; | 55 | enum AVResampleDitherMethod method; |
| 56 | int apply_map; | ||
| 57 | ChannelMapInfo *ch_map_info; | ||
| 56 | 58 | ||
| 57 | int mute_dither_threshold; // threshold for disabling dither | 59 | int mute_dither_threshold; // threshold for disabling dither |
| 58 | int mute_reset_threshold; // threshold for resetting noise shaping | 60 | int mute_reset_threshold; // threshold for resetting noise shaping |
| @@ -251,17 +253,23 @@ int ff_convert_dither(DitherContext *c, AudioData *dst, AudioData *src) · 192 unmodified lines | |||
| 251 | return ret; | 253 | return ret; |
| 252 | } | 254 | } |
| 253 | 255 | ||
| 254 | if (src->sample_fmt != AV_SAMPLE_FMT_FLTP) { | 256 | if (src->sample_fmt != AV_SAMPLE_FMT_FLTP || c->apply_map) { |
| 255 | /* make sure flt_data is large enough for the input */ | 257 | /* make sure flt_data is large enough for the input */ |
| 256 | ret = ff_audio_data_realloc(c->flt_data, src->nb_samples); | 258 | ret = ff_audio_data_realloc(c->flt_data, src->nb_samples); |
| 257 | if (ret < 0) | 259 | if (ret < 0) |
| 258 | return ret; | 260 | return ret; |
| 259 | flt_data = c->flt_data; | 261 | flt_data = c->flt_data; |
| 262 | } | ||
| 260 | 263 | ||
| 264 | if (src->sample_fmt != AV_SAMPLE_FMT_FLTP) { | ||
| 261 | /* convert input samples to fltp and scale to s16 range */ | 265 | /* convert input samples to fltp and scale to s16 range */ |
| 262 | ret = ff_audio_convert(c->ac_in, flt_data, src); | 266 | ret = ff_audio_convert(c->ac_in, flt_data, src); |
| 263 | if (ret < 0) | 267 | if (ret < 0) |
| 264 | return ret; | 268 | return ret; |
| 269 | } else if (c->apply_map) { | ||
| 270 | ret = ff_audio_data_copy(flt_data, src, c->ch_map_info); | ||
| 271 | if (ret < 0) | ||
| 272 | return ret; | ||
| 265 | } else { | 273 | } else { |
| 266 | flt_data = src; | 274 | flt_data = src; |
| 267 | } | 275 | } |
| @@ -333,7 +341,7 @@ static void dither_init(DitherDSPContext *ddsp, · 65 unmodified lines | |||
| 333 | DitherContext *ff_dither_alloc(AVAudioResampleContext *avr, | 341 | DitherContext *ff_dither_alloc(AVAudioResampleContext *avr, |
| 334 | enum AVSampleFormat out_fmt, | 342 | enum AVSampleFormat out_fmt, |
| 335 | enum AVSampleFormat in_fmt, | 343 | enum AVSampleFormat in_fmt, |
| 336 | int channels, int sample_rate) | 344 | int channels, int sample_rate, int apply_map) |
| 337 | { | 345 | { |
| 338 | AVLFG seed_gen; | 346 | AVLFG seed_gen; |
| 339 | DitherContext *c; | 347 | DitherContext *c; |
| @@ -350,6 +358,10 @@ DitherContext *ff_dither_alloc(AVAudioResampleContext *avr, · 10 unmodified lines | |||
| 350 | if (!c) | 358 | if (!c) |
| 351 | return NULL; | 359 | return NULL; |
| 352 | 360 | ||
| 361 | c->apply_map = apply_map; | ||
| 362 | if (apply_map) | ||
| 363 | c->ch_map_info = &avr->ch_map_info; | ||
| 364 | |||
| 353 | if (avr->dither_method == AV_RESAMPLE_DITHER_TRIANGULAR_NS && | 365 | if (avr->dither_method == AV_RESAMPLE_DITHER_TRIANGULAR_NS && |
| 354 | sample_rate != 48000 && sample_rate != 44100) { | 366 | sample_rate != 48000 && sample_rate != 44100) { |
| 355 | av_log(avr, AV_LOG_WARNING, "sample rate must be 48000 or 44100 Hz " | 367 | av_log(avr, AV_LOG_WARNING, "sample rate must be 48000 or 44100 Hz " |
| @@ -379,19 +391,20 @@ DitherContext *ff_dither_alloc(AVAudioResampleContext *avr, · 23 unmodified lines | |||
| 379 | goto fail; | 391 | goto fail; |
| 380 | 392 | ||
| 381 | c->ac_out = ff_audio_convert_alloc(avr, out_fmt, AV_SAMPLE_FMT_S16P, | 393 | c->ac_out = ff_audio_convert_alloc(avr, out_fmt, AV_SAMPLE_FMT_S16P, |
| 382 | channels, sample_rate); | 394 | channels, sample_rate, 0); |
| 383 | if (!c->ac_out) | 395 | if (!c->ac_out) |
| 384 | goto fail; | 396 | goto fail; |
| 385 | } | 397 | } |
| 386 | 398 | ||
| 387 | if (in_fmt != AV_SAMPLE_FMT_FLTP) { | 399 | if (in_fmt != AV_SAMPLE_FMT_FLTP || c->apply_map) { |
| 388 | c->flt_data = ff_audio_data_alloc(channels, 1024, AV_SAMPLE_FMT_FLTP, | 400 | c->flt_data = ff_audio_data_alloc(channels, 1024, AV_SAMPLE_FMT_FLTP, |
| 389 | "dither flt buffer"); | 401 | "dither flt buffer"); |
| 390 | if (!c->flt_data) | 402 | if (!c->flt_data) |
| 391 | goto fail; | 403 | goto fail; |
| 392 | 404 | } | |
| 405 | if (in_fmt != AV_SAMPLE_FMT_FLTP) { | ||
| 393 | c->ac_in = ff_audio_convert_alloc(avr, AV_SAMPLE_FMT_FLTP, in_fmt, | 406 | c->ac_in = ff_audio_convert_alloc(avr, AV_SAMPLE_FMT_FLTP, in_fmt, |
| 394 | channels, sample_rate); | 407 | channels, sample_rate, c->apply_map); |
| 395 | if (!c->ac_in) | 408 | if (!c->ac_in) |
| 396 | goto fail; | 409 | goto fail; |
| 397 | } | 410 | } |
| @@ -66,7 +66,7 @@ typedef struct DitherDSPContext { · 65 unmodified lines | |||
| 66 | DitherContext *ff_dither_alloc(AVAudioResampleContext *avr, | 66 | DitherContext *ff_dither_alloc(AVAudioResampleContext *avr, |
| 67 | enum AVSampleFormat out_fmt, | 67 | enum AVSampleFormat out_fmt, |
| 68 | enum AVSampleFormat in_fmt, | 68 | enum AVSampleFormat in_fmt, |
| 69 | int channels, int sample_rate); | 69 | int channels, int sample_rate, int apply_map); |
| 70 | 70 | ||
| 71 | /** | 71 | /** |
| 72 | * Free a DitherContext. | 72 | * Free a DitherContext. |
| @@ -32,6 +32,24 @@ typedef struct AudioConvert AudioConvert; · 31 unmodified lines | |||
| 32 | typedef struct AudioMix AudioMix; | 32 | typedef struct AudioMix AudioMix; |
| 33 | typedef struct ResampleContext ResampleContext; | 33 | typedef struct ResampleContext ResampleContext; |
| 34 | 34 | ||
| 35 | enum RemapPoint { | ||
| 36 | REMAP_NONE, | ||
| 37 | REMAP_IN_COPY, | ||
| 38 | REMAP_IN_CONVERT, | ||
| 39 | REMAP_OUT_COPY, | ||
| 40 | REMAP_OUT_CONVERT, | ||
| 41 | }; | ||
| 42 | |||
| 43 | typedef struct ChannelMapInfo { | ||
| 44 | int channel_map[AVRESAMPLE_MAX_CHANNELS]; /**< source index of each output channel, -1 if not remapped */ | ||
| 45 | int do_remap; /**< remap needed */ | ||
| 46 | int channel_copy[AVRESAMPLE_MAX_CHANNELS]; /**< dest index to copy from */ | ||
| 47 | int do_copy; /**< copy needed */ | ||
| 48 | int channel_zero[AVRESAMPLE_MAX_CHANNELS]; /**< dest index to zero */ | ||
| 49 | int do_zero; /**< zeroing needed */ | ||
| 50 | int input_map[AVRESAMPLE_MAX_CHANNELS]; /**< dest index of each input channel */ | ||
| 51 | } ChannelMapInfo; | ||
| 52 | |||
| 35 | struct AVAudioResampleContext { | 53 | struct AVAudioResampleContext { |
| 36 | const AVClass *av_class; /**< AVClass for logging and AVOptions */ | 54 | const AVClass *av_class; /**< AVClass for logging and AVOptions */ |
| 37 | 55 | ||
| @@ -65,6 +83,7 @@ struct AVAudioResampleContext { · 27 unmodified lines | |||
| 65 | int resample_needed; /**< resampling is needed */ | 83 | int resample_needed; /**< resampling is needed */ |
| 66 | int in_convert_needed; /**< input sample format conversion is needed */ | 84 | int in_convert_needed; /**< input sample format conversion is needed */ |
| 67 | int out_convert_needed; /**< output sample format conversion is needed */ | 85 | int out_convert_needed; /**< output sample format conversion is needed */ |
| 86 | int in_copy_needed; /**< input data copy is needed */ | ||
| 68 | 87 | ||
| 69 | AudioData *in_buffer; /**< buffer for converted input */ | 88 | AudioData *in_buffer; /**< buffer for converted input */ |
| 70 | AudioData *resample_out_buffer; /**< buffer for output from resampler */ | 89 | AudioData *resample_out_buffer; /**< buffer for output from resampler */ |
| @@ -82,6 +101,10 @@ struct AVAudioResampleContext { · 11 unmodified lines | |||
| 82 | * only used if avresample_set_matrix() is called before avresample_open() | 101 | * only used if avresample_set_matrix() is called before avresample_open() |
| 83 | */ | 102 | */ |
| 84 | double *mix_matrix; | 103 | double *mix_matrix; |
| 104 | |||
| 105 | int use_channel_map; | ||
| 106 | enum RemapPoint remap_point; | ||
| 107 | ChannelMapInfo ch_map_info; | ||
| 85 | }; | 108 | }; |
| 86 | 109 | ||
| 87 | #endif /* AVRESAMPLE_INTERNAL_H */ | 110 | #endif /* AVRESAMPLE_INTERNAL_H */ |
| @@ -96,20 +96,84 @@ int avresample_open(AVAudioResampleContext *avr) · 95 unmodified lines | |||
| 96 | av_get_sample_fmt_name(avr->internal_sample_fmt)); | 96 | av_get_sample_fmt_name(avr->internal_sample_fmt)); |
| 97 | } | 97 | } |
| 98 | 98 | ||
| 99 | /* set sample format conversion parameters */ | 99 | /* treat all mono as planar for easier comparison */ |
| 100 | if (avr->in_channels == 1) | 100 | if (avr->in_channels == 1) |
| 101 | avr->in_sample_fmt = av_get_planar_sample_fmt(avr->in_sample_fmt); | 101 | avr->in_sample_fmt = av_get_planar_sample_fmt(avr->in_sample_fmt); |
| 102 | if (avr->out_channels == 1) | 102 | if (avr->out_channels == 1) |
| 103 | avr->out_sample_fmt = av_get_planar_sample_fmt(avr->out_sample_fmt); | 103 | avr->out_sample_fmt = av_get_planar_sample_fmt(avr->out_sample_fmt); |
| 104 | avr->in_convert_needed = (avr->resample_needed || avr->mixing_needed) && | 104 | |
| 105 | avr->in_sample_fmt != avr->internal_sample_fmt; | 105 | /* we may need to add an extra conversion in order to remap channels if |
| 106 | the output format is not planar */ | ||
| 107 | if (avr->use_channel_map && !avr->mixing_needed && !avr->resample_needed && | ||
| 108 | !av_sample_fmt_is_planar(avr->out_sample_fmt)) { | ||
| 109 | avr->internal_sample_fmt = av_get_planar_sample_fmt(avr->out_sample_fmt); | ||
| 110 | } | ||
| 111 | |||
| 112 | /* set sample format conversion parameters */ | ||
| 106 | if (avr->resample_needed || avr->mixing_needed) | 113 | if (avr->resample_needed || avr->mixing_needed) |
| 114 | avr->in_convert_needed = avr->in_sample_fmt != avr->internal_sample_fmt; | ||
| 115 | else | ||
| 116 | avr->in_convert_needed = avr->use_channel_map && | ||
| 117 | !av_sample_fmt_is_planar(avr->out_sample_fmt); | ||
| 118 | |||
| 119 | if (avr->resample_needed || avr->mixing_needed || avr->in_convert_needed) | ||
| 107 | avr->out_convert_needed = avr->internal_sample_fmt != avr->out_sample_fmt; | 120 | avr->out_convert_needed = avr->internal_sample_fmt != avr->out_sample_fmt; |
| 108 | else | 121 | else |
| 109 | avr->out_convert_needed = avr->in_sample_fmt != avr->out_sample_fmt; | 122 | avr->out_convert_needed = avr->in_sample_fmt != avr->out_sample_fmt; |
| 110 | 123 | ||
| 124 | avr->in_copy_needed = !avr->in_convert_needed && (avr->mixing_needed || | ||
| 125 | (avr->use_channel_map && avr->resample_needed)); | ||
| 126 | |||
| 127 | if (avr->use_channel_map) { | ||
| 128 | if (avr->in_copy_needed) { | ||
| 129 | avr->remap_point = REMAP_IN_COPY; | ||
| 130 | av_dlog(avr, "remap channels during in_copy\n"); | ||
| 131 | } else if (avr->in_convert_needed) { | ||
| 132 | avr->remap_point = REMAP_IN_CONVERT; | ||
| 133 | av_dlog(avr, "remap channels during in_convert\n"); | ||
| 134 | } else if (avr->out_convert_needed) { | ||
| 135 | avr->remap_point = REMAP_OUT_CONVERT; | ||
| 136 | av_dlog(avr, "remap channels during out_convert\n"); | ||
| 137 | } else { | ||
| 138 | avr->remap_point = REMAP_OUT_COPY; | ||
| 139 | av_dlog(avr, "remap channels during out_copy\n"); | ||
| 140 | } | ||
| 141 | |||
| 142 | #ifdef DEBUG | ||
| 143 | { | ||
| 144 | int ch; | ||
| 145 | av_dlog(avr, "output map: "); | ||
| 146 | if (avr->ch_map_info.do_remap) | ||
| 147 | for (ch = 0; ch < avr->in_channels; ch++) | ||
| 148 | av_dlog(avr, " % 2d", avr->ch_map_info.channel_map[ch]); | ||
| 149 | else | ||
| 150 | av_dlog(avr, "n/a"); | ||
| 151 | av_dlog(avr, "\n"); | ||
| 152 | av_dlog(avr, "copy map: "); | ||
| 153 | if (avr->ch_map_info.do_copy) | ||
| 154 | for (ch = 0; ch < avr->in_channels; ch++) | ||
| 155 | av_dlog(avr, " % 2d", avr->ch_map_info.channel_copy[ch]); | ||
| 156 | else | ||
| 157 | av_dlog(avr, "n/a"); | ||
| 158 | av_dlog(avr, "\n"); | ||
| 159 | av_dlog(avr, "zero map: "); | ||
| 160 | if (avr->ch_map_info.do_zero) | ||
| 161 | for (ch = 0; ch < avr->in_channels; ch++) | ||
| 162 | av_dlog(avr, " % 2d", avr->ch_map_info.channel_zero[ch]); | ||
| 163 | else | ||
| 164 | av_dlog(avr, "n/a"); | ||
| 165 | av_dlog(avr, "\n"); | ||
| 166 | av_dlog(avr, "input map: "); | ||
| 167 | for (ch = 0; ch < avr->in_channels; ch++) | ||
| 168 | av_dlog(avr, " % 2d", avr->ch_map_info.input_map[ch]); | ||
| 169 | av_dlog(avr, "\n"); | ||
| 170 | } | ||
| 171 | #endif | ||
| 172 | } else | ||
| 173 | avr->remap_point = REMAP_NONE; | ||
| 174 | |||
| 111 | /* allocate buffers */ | 175 | /* allocate buffers */ |
| 112 | if (avr->mixing_needed || avr->in_convert_needed) { | 176 | if (avr->in_copy_needed || avr->in_convert_needed) { |
| 113 | avr->in_buffer = ff_audio_data_alloc(FFMAX(avr->in_channels, avr->out_channels), | 177 | avr->in_buffer = ff_audio_data_alloc(FFMAX(avr->in_channels, avr->out_channels), |
| 114 | 0, avr->internal_sample_fmt, | 178 | 0, avr->internal_sample_fmt, |
| 115 | "in_buffer"); | 179 | "in_buffer"); |
| @@ -146,7 +210,8 @@ int avresample_open(AVAudioResampleContext *avr) · 30 unmodified lines | |||
| 146 | if (avr->in_convert_needed) { | 210 | if (avr->in_convert_needed) { |
| 147 | avr->ac_in = ff_audio_convert_alloc(avr, avr->internal_sample_fmt, | 211 | avr->ac_in = ff_audio_convert_alloc(avr, avr->internal_sample_fmt, |
| 148 | avr->in_sample_fmt, avr->in_channels, | 212 | avr->in_sample_fmt, avr->in_channels, |
| 149 | avr->in_sample_rate); | 213 | avr->in_sample_rate, |
| 214 | avr->remap_point == REMAP_IN_CONVERT); | ||
| 150 | if (!avr->ac_in) { | 215 | if (!avr->ac_in) { |
| 151 | ret = AVERROR(ENOMEM); | 216 | ret = AVERROR(ENOMEM); |
| 152 | goto error; | 217 | goto error; |
| @@ -160,7 +225,8 @@ int avresample_open(AVAudioResampleContext *avr) · 7 unmodified lines | |||
| 160 | src_fmt = avr->in_sample_fmt; | 225 | src_fmt = avr->in_sample_fmt; |
| 161 | avr->ac_out = ff_audio_convert_alloc(avr, avr->out_sample_fmt, src_fmt, | 226 | avr->ac_out = ff_audio_convert_alloc(avr, avr->out_sample_fmt, src_fmt, |
| 162 | avr->out_channels, | 227 | avr->out_channels, |
| 163 | avr->out_sample_rate); | 228 | avr->out_sample_rate, |
| 229 | avr->remap_point == REMAP_OUT_CONVERT); | ||
| 164 | if (!avr->ac_out) { | 230 | if (!avr->ac_out) { |
| 165 | ret = AVERROR(ENOMEM); | 231 | ret = AVERROR(ENOMEM); |
| 166 | goto error; | 232 | goto error; |
| @@ -200,6 +266,8 @@ void avresample_close(AVAudioResampleContext *avr) · 33 unmodified lines | |||
| 200 | ff_audio_resample_free(&avr->resample); | 266 | ff_audio_resample_free(&avr->resample); |
| 201 | ff_audio_mix_free(&avr->am); | 267 | ff_audio_mix_free(&avr->am); |
| 202 | av_freep(&avr->mix_matrix); | 268 | av_freep(&avr->mix_matrix); |
| 269 | |||
| 270 | avr->use_channel_map = 0; | ||
| 203 | } | 271 | } |
| 204 | 272 | ||
| 205 | void avresample_free(AVAudioResampleContext **avr) | 273 | void avresample_free(AVAudioResampleContext **avr) |
| @@ -242,7 +310,9 @@ static int handle_buffered_output(AVAudioResampleContext *avr, · 36 unmodified lines | |||
| 242 | data in the output FIFO */ | 310 | data in the output FIFO */ |
| 243 | av_dlog(avr, "[copy] %s to output\n", converted->name); | 311 | av_dlog(avr, "[copy] %s to output\n", converted->name); |
| 244 | output->nb_samples = 0; | 312 | output->nb_samples = 0; |
| 245 | ret = ff_audio_data_copy(output, converted); | 313 | ret = ff_audio_data_copy(output, converted, |
| 314 | avr->remap_point == REMAP_OUT_COPY ? | ||
| 315 | &avr->ch_map_info : NULL); | ||
| 246 | if (ret < 0) | 316 | if (ret < 0) |
| 247 | return ret; | 317 | return ret; |
| 248 | av_dlog(avr, "[end conversion]\n"); | 318 | av_dlog(avr, "[end conversion]\n"); |
| @@ -306,11 +376,24 @@ int attribute_align_arg avresample_convert(AVAudioResampleContext *avr, · 57 unmodified lines | |||
| 306 | /* in some rare cases we can copy input to output and upmix | 376 | /* in some rare cases we can copy input to output and upmix |
| 307 | directly in the output buffer */ | 377 | directly in the output buffer */ |
| 308 | av_dlog(avr, "[copy] %s to output\n", current_buffer->name); | 378 | av_dlog(avr, "[copy] %s to output\n", current_buffer->name); |
| 309 | ret = ff_audio_data_copy(&output_buffer, current_buffer); | 379 | ret = ff_audio_data_copy(&output_buffer, current_buffer, |
| 380 | avr->remap_point == REMAP_OUT_COPY ? | ||
| 381 | &avr->ch_map_info : NULL); | ||
| 310 | if (ret < 0) | 382 | if (ret < 0) |
| 311 | return ret; | 383 | return ret; |
| 312 | current_buffer = &output_buffer; | 384 | current_buffer = &output_buffer; |
| 313 | } else if (avr->mixing_needed || avr->in_convert_needed) { | 385 | } else if (avr->remap_point == REMAP_OUT_COPY && |
| 386 | (!direct_output || out_samples < in_samples)) { | ||
| 387 | /* if remapping channels during output copy, we may need to | ||
| 388 | * use an intermediate buffer in order to remap before adding | ||
| 389 | * samples to the output fifo */ | ||
| 390 | av_dlog(avr, "[copy] %s to out_buffer\n", current_buffer->name); | ||
| 391 | ret = ff_audio_data_copy(avr->out_buffer, current_buffer, | ||
| 392 | &avr->ch_map_info); | ||
| 393 | if (ret < 0) | ||
| 394 | return ret; | ||
| 395 | current_buffer = avr->out_buffer; | ||
| 396 | } else if (avr->in_copy_needed || avr->in_convert_needed) { | ||
| 314 | /* if needed, copy or convert input to in_buffer, and downmix if | 397 | /* if needed, copy or convert input to in_buffer, and downmix if |
| 315 | applicable */ | 398 | applicable */ |
| 316 | if (avr->in_convert_needed) { | 399 | if (avr->in_convert_needed) { |
| @@ -325,7 +408,9 @@ int attribute_align_arg avresample_convert(AVAudioResampleContext *avr, · 8 unmodified lines | |||
| 325 | return ret; | 408 | return ret; |
| 326 | } else { | 409 | } else { |
| 327 | av_dlog(avr, "[copy] %s to in_buffer\n", current_buffer->name); | 410 | av_dlog(avr, "[copy] %s to in_buffer\n", current_buffer->name); |
| 328 | ret = ff_audio_data_copy(avr->in_buffer, current_buffer); | 411 | ret = ff_audio_data_copy(avr->in_buffer, current_buffer, |
| 412 | avr->remap_point == REMAP_IN_COPY ? | ||
| 413 | &avr->ch_map_info : NULL); | ||
| 329 | if (ret < 0) | 414 | if (ret < 0) |
| 330 | return ret; | 415 | return ret; |
| 331 | } | 416 | } |
| @@ -470,6 +555,57 @@ int avresample_set_matrix(AVAudioResampleContext *avr, const double *matrix, · 138 unmodified lines | |||
| 470 | return 0; | 555 | return 0; |
| 471 | } | 556 | } |
| 472 | 557 | ||
| 558 | int avresample_set_channel_mapping(AVAudioResampleContext *avr, | ||
| 559 | const int *channel_map) | ||
| 560 | { | ||
| 561 | ChannelMapInfo *info = &avr->ch_map_info; | ||
| 562 | int in_channels, ch, i; | ||
| 563 | |||
| 564 | in_channels = av_get_channel_layout_nb_channels(avr->in_channel_layout); | ||
| 565 | if (in_channels <= 0 || in_channels > AVRESAMPLE_MAX_CHANNELS) { | ||
| 566 | av_log(avr, AV_LOG_ERROR, "Invalid input channel layout\n"); | ||
| 567 | return AVERROR(EINVAL); | ||
| 568 | } | ||
| 569 | |||
| 570 | memset(info, 0, sizeof(*info)); | ||
| 571 | memset(info->input_map, -1, sizeof(info->input_map)); | ||
| 572 | |||
| 573 | for (ch = 0; ch < in_channels; ch++) { | ||
| 574 | if (channel_map[ch] >= in_channels) { | ||
| 575 | av_log(avr, AV_LOG_ERROR, "Invalid channel map\n"); | ||
| 576 | return AVERROR(EINVAL); | ||
| 577 | } | ||
| 578 | if (channel_map[ch] < 0) { | ||
| 579 | info->channel_zero[ch] = 1; | ||
| 580 | info->channel_map[ch] = -1; | ||
| 581 | info->do_zero = 1; | ||
| 582 | } else if (info->input_map[channel_map[ch]] >= 0) { | ||
| 583 | info->channel_copy[ch] = info->input_map[channel_map[ch]]; | ||
| 584 | info->channel_map[ch] = -1; | ||
| 585 | info->do_copy = 1; | ||
| 586 | } else { | ||
| 587 | info->channel_map[ch] = channel_map[ch]; | ||
| 588 | info->input_map[channel_map[ch]] = ch; | ||
| 589 | info->do_remap = 1; | ||
| 590 | } | ||
| 591 | } | ||
| 592 | /* Fill-in unmapped input channels with unmapped output channels. | ||
| 593 | This is used when remapping during conversion from interleaved to | ||
| 594 | planar format. */ | ||
| 595 | for (ch = 0, i = 0; ch < in_channels && i < in_channels; ch++, i++) { | ||
| 596 | while (ch < in_channels && info->input_map[ch] >= 0) | ||
| 597 | ch++; | ||
| 598 | while (i < in_channels && info->channel_map[i] >= 0) | ||
| 599 | i++; | ||
| 600 | if (ch >= in_channels || i >= in_channels) | ||
| 601 | break; | ||
| 602 | info->input_map[ch] = i; | ||
| 603 | } | ||
| 604 | |||
| 605 | avr->use_channel_map = 1; | ||
| 606 | return 0; | ||
| 607 | } | ||
| 608 | |||
| 473 | int avresample_available(AVAudioResampleContext *avr) | 609 | int avresample_available(AVAudioResampleContext *avr) |
| 474 | { | 610 | { |
| 475 | return av_audio_fifo_size(avr->out_fifo); | 611 | return av_audio_fifo_size(avr->out_fifo); |
| @@ -20,8 +20,8 @@ · 19 unmodified lines | |||
| 20 | #define AVRESAMPLE_VERSION_H | 20 | #define AVRESAMPLE_VERSION_H |
| 21 | 21 | ||
| 22 | #define LIBAVRESAMPLE_VERSION_MAJOR 1 | 22 | #define LIBAVRESAMPLE_VERSION_MAJOR 1 |
| 23 | #define LIBAVRESAMPLE_VERSION_MINOR 0 | 23 | #define LIBAVRESAMPLE_VERSION_MINOR 1 |
| 24 | #define LIBAVRESAMPLE_VERSION_MICRO 1 | 24 | #define LIBAVRESAMPLE_VERSION_MICRO 0 |
| 25 | 25 | ||
| 26 | #define LIBAVRESAMPLE_VERSION_INT AV_VERSION_INT(LIBAVRESAMPLE_VERSION_MAJOR, \ | 26 | #define LIBAVRESAMPLE_VERSION_INT AV_VERSION_INT(LIBAVRESAMPLE_VERSION_MAJOR, \ |
| 27 | LIBAVRESAMPLE_VERSION_MINOR, \ | 27 | LIBAVRESAMPLE_VERSION_MINOR, \ |