* 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
Unified · Split
| @@ -132,6 +132,10 @@ API changes, most recent first: · 131 unmodified lines | ||
| 132 | 132 | 2012-03-26 - a67d9cf - lavfi 2.66.100 |
| 133 | 133 | Add avfilter_fill_frame_from_{audio_,}buffer_ref() functions. |
| 134 | 134 | |
| 135 | +2013-xx-xx - xxxxxxx - lavr 1.1.0 | |
| 136 | + Add avresample_set_channel_mapping() for input channel reordering, | |
| 137 | + duplication, and silencing. | |
| 138 | + | |
| 135 | 139 | 2012-xx-xx - xxxxxxx - lavu 52.2.1 - avstring.h |
| 136 | 140 | Add av_basename() and av_dirname(). |
| 137 | 141 | |
| @@ -228,6 +228,13 @@ For Emacs, add these roughly equivalent lines to your @file{.emacs.d/init.el}: · 227 unmodified lines | ||
| 228 | 228 | You can commit unfinished stuff (for testing etc), but it must be disabled |
| 229 | 229 | (#ifdef etc) by default so it does not interfere with other developers' |
| 230 | 230 | work. |
| 231 | +@item | |
| 232 | + The commit message should have a short first line in the form of | |
| 233 | + a @samp{topic: short description} as a header, separated by a newline | |
| 234 | + from the body consisting of an explanation of why the change is necessary. | |
| 235 | + If the commit fixes a known bug on the bug tracker, the commit message | |
| 236 | + should include its bug ID. Referring to the issue on the bug tracker does | |
| 237 | + not exempt you from writing an excerpt of the bug in the commit message. | |
| 231 | 238 | @item |
| 232 | 239 | You do not have to over-test things. If it works for you, and you think it |
| 233 | 240 | should work for others, then commit. If your code has problems |
| @@ -30,7 +30,6 @@ · 29 unmodified lines | ||
| 30 | 30 | #include "audio_convert.h" |
| 31 | 31 | #include "audio_data.h" |
| 32 | 32 | #include "dither.h" |
| 33 | −#include "internal.h" | |
| 34 | 33 | |
| 35 | 34 | enum ConvFuncType { |
| 36 | 35 | CONV_FUNC_TYPE_FLAT, |
| @@ -51,6 +50,7 @@ struct AudioConvert { · 14 unmodified lines | ||
| 51 | 50 | DitherContext *dc; |
| 52 | 51 | enum AVSampleFormat in_fmt; |
| 53 | 52 | enum AVSampleFormat out_fmt; |
| 53 | + int apply_map; | |
| 54 | 54 | int channels; |
| 55 | 55 | int planes; |
| 56 | 56 | int ptr_align; |
| @@ -260,7 +260,8 @@ void ff_audio_convert_free(AudioConvert **ac) · 203 unmodified lines | ||
| 260 | 260 | AudioConvert *ff_audio_convert_alloc(AVAudioResampleContext *avr, |
| 261 | 261 | enum AVSampleFormat out_fmt, |
| 262 | 262 | enum AVSampleFormat in_fmt, |
| 263 | − int channels, int sample_rate) | |
| 263 | + int channels, int sample_rate, | |
| 264 | + int apply_map) | |
| 264 | 265 | { |
| 265 | 266 | AudioConvert *ac; |
| 266 | 267 | int in_planar, out_planar; |
| @@ -273,11 +274,13 @@ AudioConvert *ff_audio_convert_alloc(AVAudioResampleContext *avr, · 6 unmodified lines | ||
| 273 | 274 | ac->out_fmt = out_fmt; |
| 274 | 275 | ac->in_fmt = in_fmt; |
| 275 | 276 | ac->channels = channels; |
| 277 | + ac->apply_map = apply_map; | |
| 276 | 278 | |
| 277 | 279 | if (avr->dither_method != AV_RESAMPLE_DITHER_NONE && |
| 278 | 280 | av_get_packed_sample_fmt(out_fmt) == AV_SAMPLE_FMT_S16 && |
| 279 | 281 | av_get_bytes_per_sample(in_fmt) > 2) { |
| 280 | − 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); | |
| 281 | 284 | if (!ac->dc) { |
| 282 | 285 | av_free(ac); |
| 283 | 286 | return NULL; |
| @@ -310,6 +313,7 @@ int ff_audio_convert(AudioConvert *ac, AudioData *out, AudioData *in) · 26 unmodified lines | ||
| 310 | 313 | { |
| 311 | 314 | int use_generic = 1; |
| 312 | 315 | int len = in->nb_samples; |
| 316 | + int p; | |
| 313 | 317 | |
| 314 | 318 | if (ac->dc) { |
| 315 | 319 | /* dithered conversion */ |
| @@ -336,9 +340,46 @@ int ff_audio_convert(AudioConvert *ac, AudioData *out, AudioData *in) · 20 unmodified lines | ||
| 336 | 340 | av_get_sample_fmt_name(ac->out_fmt), |
| 337 | 341 | use_generic ? ac->func_descr_generic : ac->func_descr); |
| 338 | 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 { | |
| 339 | 381 | switch (ac->func_type) { |
| 340 | 382 | case CONV_FUNC_TYPE_FLAT: { |
| 341 | − int p; | |
| 342 | 383 | if (!in->is_planar) |
| 343 | 384 | len *= in->channels; |
| 344 | 385 | if (use_generic) { |
| @@ -363,6 +404,7 @@ int ff_audio_convert(AudioConvert *ac, AudioData *out, AudioData *in) · 18 unmodified lines | ||
| 363 | 404 | ac->conv_deinterleave(out->data, in->data[0], len, ac->channels); |
| 364 | 405 | break; |
| 365 | 406 | } |
| 407 | + } | |
| 366 | 408 | |
| 367 | 409 | out->nb_samples = in->nb_samples; |
| 368 | 410 | return 0; |
| @@ -23,10 +23,9 @@ · 22 unmodified lines | ||
| 23 | 23 | |
| 24 | 24 | #include "libavutil/samplefmt.h" |
| 25 | 25 | #include "avresample.h" |
| 26 | +#include "internal.h" | |
| 26 | 27 | #include "audio_data.h" |
| 27 | 28 | |
| 28 | −typedef struct AudioConvert AudioConvert; | |
| 29 | − | |
| 30 | 29 | /** |
| 31 | 30 | * Set conversion function if the parameters match. |
| 32 | 31 | * |
| @@ -59,12 +58,14 @@ void ff_audio_convert_set_func(AudioConvert *ac, enum AVSampleFormat out_fmt, · 26 unmodified lines | ||
| 59 | 58 | * @param in_fmt input sample format |
| 60 | 59 | * @param channels number of channels |
| 61 | 60 | * @param sample_rate sample rate (used for dithering) |
| 61 | + * @param apply_map apply channel map during conversion | |
| 62 | 62 | * @return newly-allocated AudioConvert context |
| 63 | 63 | */ |
| 64 | 64 | AudioConvert *ff_audio_convert_alloc(AVAudioResampleContext *avr, |
| 65 | 65 | enum AVSampleFormat out_fmt, |
| 66 | 66 | enum AVSampleFormat in_fmt, |
| 67 | − int channels, int sample_rate); | |
| 67 | + int channels, int sample_rate, | |
| 68 | + int apply_map); | |
| 68 | 69 | |
| 69 | 70 | /** |
| 70 | 71 | * Free AudioConvert. |
| @@ -213,7 +213,7 @@ void ff_audio_data_free(AudioData **a) · 212 unmodified lines | ||
| 213 | 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 | 218 | int ret, p; |
| 219 | 219 | |
| @@ -221,6 +221,11 @@ int ff_audio_data_copy(AudioData *dst, AudioData *src) · 1 unmodified line | ||
| 221 | 221 | if (dst->sample_fmt != src->sample_fmt || dst->channels < src->channels) |
| 222 | 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 | 229 | /* if the input is empty, just empty the output */ |
| 225 | 230 | if (!src->nb_samples) { |
| 226 | 231 | dst->nb_samples = 0; |
| @@ -233,8 +238,29 @@ int ff_audio_data_copy(AudioData *dst, AudioData *src) · 6 unmodified lines | ||
| 233 | 238 | return ret; |
| 234 | 239 | |
| 235 | 240 | /* copy data */ |
| 236 | − for (p = 0; p < src->planes; p++) | |
| 237 | − memcpy(dst->data[p], src->data[p], src->nb_samples * src->stride); | |
| 241 | + if (map) { | |
| 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 | 264 | dst->nb_samples = src->nb_samples; |
| 239 | 265 | |
| 240 | 266 | return 0; |
| @@ -27,11 +27,12 @@ · 26 unmodified lines | ||
| 27 | 27 | #include "libavutil/log.h" |
| 28 | 28 | #include "libavutil/samplefmt.h" |
| 29 | 29 | #include "avresample.h" |
| 30 | +#include "internal.h" | |
| 30 | 31 | |
| 31 | 32 | /** |
| 32 | 33 | * Audio buffer used for intermediate storage between conversion phases. |
| 33 | 34 | */ |
| 34 | −typedef struct AudioData { | |
| 35 | +struct AudioData { | |
| 35 | 36 | const AVClass *class; /**< AVClass for logging */ |
| 36 | 37 | uint8_t *data[AVRESAMPLE_MAX_CHANNELS]; /**< data plane pointers */ |
| 37 | 38 | uint8_t *buffer; /**< data buffer */ |
| @@ -50,7 +51,7 @@ typedef struct AudioData { · 12 unmodified lines | ||
| 50 | 51 | int ptr_align; /**< minimum data pointer alignment */ |
| 51 | 52 | int samples_align; /**< allocated samples alignment */ |
| 52 | 53 | const char *name; /**< name for debug logging */ |
| 53 | −} AudioData; | |
| 54 | +}; | |
| 54 | 55 | |
| 55 | 56 | int ff_audio_data_set_channels(AudioData *a, int channels); |
| 56 | 57 | |
| @@ -117,9 +118,10 @@ void ff_audio_data_free(AudioData **a); · 60 unmodified lines | ||
| 117 | 118 | * |
| 118 | 119 | * @param out output AudioData |
| 119 | 120 | * @param in input AudioData |
| 121 | + * @param map channel map, NULL if not remapping | |
| 120 | 122 | * @return 0 on success, negative AVERROR value on error |
| 121 | 123 | */ |
| 122 | −int ff_audio_data_copy(AudioData *out, AudioData *in); | |
| 124 | +int ff_audio_data_copy(AudioData *out, AudioData *in, ChannelMapInfo *map); | |
| 123 | 125 | |
| 124 | 126 | /** |
| 125 | 127 | * Append data from one AudioData to the end of another. |
| @@ -25,13 +25,12 @@ · 24 unmodified lines | ||
| 25 | 25 | |
| 26 | 26 | #include "libavutil/samplefmt.h" |
| 27 | 27 | #include "avresample.h" |
| 28 | +#include "internal.h" | |
| 28 | 29 | #include "audio_data.h" |
| 29 | 30 | |
| 30 | 31 | typedef void (mix_func)(uint8_t **src, void **matrix, int len, int out_ch, |
| 31 | 32 | int in_ch); |
| 32 | 33 | |
| 33 | −typedef struct AudioMix AudioMix; | |
| 34 | − | |
| 35 | 34 | /** |
| 36 | 35 | * Set mixing function if the parameters match. |
| 37 | 36 | * |
| @@ -258,6 +258,36 @@ int avresample_get_matrix(AVAudioResampleContext *avr, double *matrix, · 257 unmodified lines | ||
| 258 | 258 | int avresample_set_matrix(AVAudioResampleContext *avr, const double *matrix, |
| 259 | 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 | 292 | * Set compensation for resampling. |
| 263 | 293 | * |
| @@ -53,6 +53,8 @@ typedef struct DitherState { · 52 unmodified lines | ||
| 53 | 53 | struct DitherContext { |
| 54 | 54 | DitherDSPContext ddsp; |
| 55 | 55 | enum AVResampleDitherMethod method; |
| 56 | + int apply_map; | |
| 57 | + ChannelMapInfo *ch_map_info; | |
| 56 | 58 | |
| 57 | 59 | int mute_dither_threshold; // threshold for disabling dither |
| 58 | 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 | 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 | 257 | /* make sure flt_data is large enough for the input */ |
| 256 | 258 | ret = ff_audio_data_realloc(c->flt_data, src->nb_samples); |
| 257 | 259 | if (ret < 0) |
| 258 | 260 | return ret; |
| 259 | 261 | flt_data = c->flt_data; |
| 262 | + } | |
| 260 | 263 | |
| 264 | + if (src->sample_fmt != AV_SAMPLE_FMT_FLTP) { | |
| 261 | 265 | /* convert input samples to fltp and scale to s16 range */ |
| 262 | 266 | ret = ff_audio_convert(c->ac_in, flt_data, src); |
| 263 | 267 | if (ret < 0) |
| 264 | 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 | 273 | } else { |
| 266 | 274 | flt_data = src; |
| 267 | 275 | } |
| @@ -333,7 +341,7 @@ static void dither_init(DitherDSPContext *ddsp, · 65 unmodified lines | ||
| 333 | 341 | DitherContext *ff_dither_alloc(AVAudioResampleContext *avr, |
| 334 | 342 | enum AVSampleFormat out_fmt, |
| 335 | 343 | enum AVSampleFormat in_fmt, |
| 336 | − int channels, int sample_rate) | |
| 344 | + int channels, int sample_rate, int apply_map) | |
| 337 | 345 | { |
| 338 | 346 | AVLFG seed_gen; |
| 339 | 347 | DitherContext *c; |
| @@ -350,6 +358,10 @@ DitherContext *ff_dither_alloc(AVAudioResampleContext *avr, · 10 unmodified lines | ||
| 350 | 358 | if (!c) |
| 351 | 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 | 365 | if (avr->dither_method == AV_RESAMPLE_DITHER_TRIANGULAR_NS && |
| 354 | 366 | sample_rate != 48000 && sample_rate != 44100) { |
| 355 | 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 | 391 | goto fail; |
| 380 | 392 | |
| 381 | 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 | 395 | if (!c->ac_out) |
| 384 | 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 | 400 | c->flt_data = ff_audio_data_alloc(channels, 1024, AV_SAMPLE_FMT_FLTP, |
| 389 | 401 | "dither flt buffer"); |
| 390 | 402 | if (!c->flt_data) |
| 391 | 403 | goto fail; |
| 392 | − | |
| 404 | + } | |
| 405 | + if (in_fmt != AV_SAMPLE_FMT_FLTP) { | |
| 393 | 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 | 408 | if (!c->ac_in) |
| 396 | 409 | goto fail; |
| 397 | 410 | } |
| @@ -66,7 +66,7 @@ typedef struct DitherDSPContext { · 65 unmodified lines | ||
| 66 | 66 | DitherContext *ff_dither_alloc(AVAudioResampleContext *avr, |
| 67 | 67 | enum AVSampleFormat out_fmt, |
| 68 | 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 | 72 | * Free a DitherContext. |
| @@ -26,10 +26,29 @@ · 25 unmodified lines | ||
| 26 | 26 | #include "libavutil/opt.h" |
| 27 | 27 | #include "libavutil/samplefmt.h" |
| 28 | 28 | #include "avresample.h" |
| 29 | −#include "audio_convert.h" | |
| 30 | −#include "audio_data.h" | |
| 31 | −#include "audio_mix.h" | |
| 32 | −#include "resample.h" | |
| 29 | + | |
| 30 | +typedef struct AudioData AudioData; | |
| 31 | +typedef struct AudioConvert AudioConvert; | |
| 32 | +typedef struct AudioMix AudioMix; | |
| 33 | +typedef struct ResampleContext ResampleContext; | |
| 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; | |
| 33 | 52 | |
| 34 | 53 | struct AVAudioResampleContext { |
| 35 | 54 | const AVClass *av_class; /**< AVClass for logging and AVOptions */ |
| @@ -64,6 +83,7 @@ struct AVAudioResampleContext { · 28 unmodified lines | ||
| 64 | 83 | int resample_needed; /**< resampling is needed */ |
| 65 | 84 | int in_convert_needed; /**< input sample format conversion is needed */ |
| 66 | 85 | int out_convert_needed; /**< output sample format conversion is needed */ |
| 86 | + int in_copy_needed; /**< input data copy is needed */ | |
| 67 | 87 | |
| 68 | 88 | AudioData *in_buffer; /**< buffer for converted input */ |
| 69 | 89 | AudioData *resample_out_buffer; /**< buffer for output from resampler */ |
| @@ -81,6 +101,10 @@ struct AVAudioResampleContext { · 11 unmodified lines | ||
| 81 | 101 | * only used if avresample_set_matrix() is called before avresample_open() |
| 82 | 102 | */ |
| 83 | 103 | double *mix_matrix; |
| 104 | + | |
| 105 | + int use_channel_map; | |
| 106 | + enum RemapPoint remap_point; | |
| 107 | + ChannelMapInfo ch_map_info; | |
| 84 | 108 | }; |
| 85 | 109 | |
| 86 | 110 | #endif /* AVRESAMPLE_INTERNAL_H */ |
| @@ -23,6 +23,7 @@ · 22 unmodified lines | ||
| 23 | 23 | #include "libavutil/libm.h" |
| 24 | 24 | #include "libavutil/log.h" |
| 25 | 25 | #include "internal.h" |
| 26 | +#include "resample.h" | |
| 26 | 27 | #include "audio_data.h" |
| 27 | 28 | |
| 28 | 29 | struct ResampleContext { |
| @@ -22,10 +22,9 @@ · 21 unmodified lines | ||
| 22 | 22 | #define AVRESAMPLE_RESAMPLE_H |
| 23 | 23 | |
| 24 | 24 | #include "avresample.h" |
| 25 | +#include "internal.h" | |
| 25 | 26 | #include "audio_data.h" |
| 26 | 27 | |
| 27 | −typedef struct ResampleContext ResampleContext; | |
| 28 | − | |
| 29 | 28 | /** |
| 30 | 29 | * Allocate and initialize a ResampleContext. |
| 31 | 30 | * |
| @@ -26,8 +26,11 @@ · 25 unmodified lines | ||
| 26 | 26 | #include "libavutil/opt.h" |
| 27 | 27 | |
| 28 | 28 | #include "avresample.h" |
| 29 | −#include "audio_data.h" | |
| 30 | 29 | #include "internal.h" |
| 30 | +#include "audio_data.h" | |
| 31 | +#include "audio_convert.h" | |
| 32 | +#include "audio_mix.h" | |
| 33 | +#include "resample.h" | |
| 31 | 34 | |
| 32 | 35 | int avresample_open(AVAudioResampleContext *avr) |
| 33 | 36 | { |
| @@ -93,20 +96,84 @@ int avresample_open(AVAudioResampleContext *avr) · 59 unmodified lines | ||
| 93 | 96 | av_get_sample_fmt_name(avr->internal_sample_fmt)); |
| 94 | 97 | } |
| 95 | 98 | |
| 96 | − /* set sample format conversion parameters */ | |
| 99 | + /* treat all mono as planar for easier comparison */ | |
| 97 | 100 | if (avr->in_channels == 1) |
| 98 | 101 | avr->in_sample_fmt = av_get_planar_sample_fmt(avr->in_sample_fmt); |
| 99 | 102 | if (avr->out_channels == 1) |
| 100 | 103 | avr->out_sample_fmt = av_get_planar_sample_fmt(avr->out_sample_fmt); |
| 101 | − avr->in_convert_needed = (avr->resample_needed || avr->mixing_needed) && | |
| 102 | − avr->in_sample_fmt != avr->internal_sample_fmt; | |
| 104 | + | |
| 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 */ | |
| 103 | 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) | |
| 104 | 120 | avr->out_convert_needed = avr->internal_sample_fmt != avr->out_sample_fmt; |
| 105 | 121 | else |
| 106 | 122 | avr->out_convert_needed = avr->in_sample_fmt != avr->out_sample_fmt; |
| 107 | 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 | + | |
| 108 | 175 | /* allocate buffers */ |
| 109 | − if (avr->mixing_needed || avr->in_convert_needed) { | |
| 176 | + if (avr->in_copy_needed || avr->in_convert_needed) { | |
| 110 | 177 | avr->in_buffer = ff_audio_data_alloc(FFMAX(avr->in_channels, avr->out_channels), |
| 111 | 178 | 0, avr->internal_sample_fmt, |
| 112 | 179 | "in_buffer"); |
| @@ -143,7 +210,8 @@ int avresample_open(AVAudioResampleContext *avr) · 30 unmodified lines | ||
| 143 | 210 | if (avr->in_convert_needed) { |
| 144 | 211 | avr->ac_in = ff_audio_convert_alloc(avr, avr->internal_sample_fmt, |
| 145 | 212 | avr->in_sample_fmt, avr->in_channels, |
| 146 | − avr->in_sample_rate); | |
| 213 | + avr->in_sample_rate, | |
| 214 | + avr->remap_point == REMAP_IN_CONVERT); | |
| 147 | 215 | if (!avr->ac_in) { |
| 148 | 216 | ret = AVERROR(ENOMEM); |
| 149 | 217 | goto error; |
| @@ -157,7 +225,8 @@ int avresample_open(AVAudioResampleContext *avr) · 7 unmodified lines | ||
| 157 | 225 | src_fmt = avr->in_sample_fmt; |
| 158 | 226 | avr->ac_out = ff_audio_convert_alloc(avr, avr->out_sample_fmt, src_fmt, |
| 159 | 227 | avr->out_channels, |
| 160 | − avr->out_sample_rate); | |
| 228 | + avr->out_sample_rate, | |
| 229 | + avr->remap_point == REMAP_OUT_CONVERT); | |
| 161 | 230 | if (!avr->ac_out) { |
| 162 | 231 | ret = AVERROR(ENOMEM); |
| 163 | 232 | goto error; |
| @@ -197,6 +266,8 @@ void avresample_close(AVAudioResampleContext *avr) · 33 unmodified lines | ||
| 197 | 266 | ff_audio_resample_free(&avr->resample); |
| 198 | 267 | ff_audio_mix_free(&avr->am); |
| 199 | 268 | av_freep(&avr->mix_matrix); |
| 269 | + | |
| 270 | + avr->use_channel_map = 0; | |
| 200 | 271 | } |
| 201 | 272 | |
| 202 | 273 | void avresample_free(AVAudioResampleContext **avr) |
| @@ -239,7 +310,9 @@ static int handle_buffered_output(AVAudioResampleContext *avr, · 36 unmodified lines | ||
| 239 | 310 | data in the output FIFO */ |
| 240 | 311 | av_dlog(avr, "[copy] %s to output\n", converted->name); |
| 241 | 312 | output->nb_samples = 0; |
| 242 | − 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); | |
| 243 | 316 | if (ret < 0) |
| 244 | 317 | return ret; |
| 245 | 318 | av_dlog(avr, "[end conversion]\n"); |
| @@ -303,11 +376,24 @@ int attribute_align_arg avresample_convert(AVAudioResampleContext *avr, · 57 unmodified lines | ||
| 303 | 376 | /* in some rare cases we can copy input to output and upmix |
| 304 | 377 | directly in the output buffer */ |
| 305 | 378 | av_dlog(avr, "[copy] %s to output\n", current_buffer->name); |
| 306 | − 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); | |
| 307 | 382 | if (ret < 0) |
| 308 | 383 | return ret; |
| 309 | 384 | current_buffer = &output_buffer; |
| 310 | − } 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) { | |
| 311 | 397 | /* if needed, copy or convert input to in_buffer, and downmix if |
| 312 | 398 | applicable */ |
| 313 | 399 | if (avr->in_convert_needed) { |
| @@ -322,7 +408,9 @@ int attribute_align_arg avresample_convert(AVAudioResampleContext *avr, · 8 unmodified lines | ||
| 322 | 408 | return ret; |
| 323 | 409 | } else { |
| 324 | 410 | av_dlog(avr, "[copy] %s to in_buffer\n", current_buffer->name); |
| 325 | − 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); | |
| 326 | 414 | if (ret < 0) |
| 327 | 415 | return ret; |
| 328 | 416 | } |
| @@ -467,6 +555,57 @@ int avresample_set_matrix(AVAudioResampleContext *avr, const double *matrix, · 138 unmodified lines | ||
| 467 | 555 | return 0; |
| 468 | 556 | } |
| 469 | 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 | + | |
| 470 | 609 | int avresample_available(AVAudioResampleContext *avr) |
| 471 | 610 | { |
| 472 | 611 | return av_audio_fifo_size(avr->out_fifo); |
| @@ -20,8 +20,8 @@ · 19 unmodified lines | ||
| 20 | 20 | #define AVRESAMPLE_VERSION_H |
| 21 | 21 | |
| 22 | 22 | #define LIBAVRESAMPLE_VERSION_MAJOR 1 |
| 23 | −#define LIBAVRESAMPLE_VERSION_MINOR 0 | |
| 24 | −#define LIBAVRESAMPLE_VERSION_MICRO 1 | |
| 23 | +#define LIBAVRESAMPLE_VERSION_MINOR 1 | |
| 24 | +#define LIBAVRESAMPLE_VERSION_MICRO 0 | |
| 25 | 25 | |
| 26 | 26 | #define LIBAVRESAMPLE_VERSION_INT AV_VERSION_INT(LIBAVRESAMPLE_VERSION_MAJOR, \ |
| 27 | 27 | LIBAVRESAMPLE_VERSION_MINOR, \ |