From: Anton Khirnov <an...@khirnov.net> Signed-off-by: Vittorio Giovara <vittorio.giov...@gmail.com> --- libavresample/audio_mix.c | 148 ++++++------ libavresample/audio_mix_matrix.c | 477 ++++++++++++++++++++++----------------- libavresample/avresample.h | 42 +++- libavresample/internal.h | 10 +- libavresample/options.c | 8 + libavresample/tests/avresample.c | 26 +-- libavresample/utils.c | 127 +++++++---- 7 files changed, 504 insertions(+), 334 deletions(-)
diff --git a/libavresample/audio_mix.c b/libavresample/audio_mix.c index 89ecc6ba71..36dff2b979 100644 --- a/libavresample/audio_mix.c +++ b/libavresample/audio_mix.c @@ -20,6 +20,7 @@ #include <stdint.h> +#include "libavutil/channel_layout.h" #include "libavutil/common.h" #include "libavutil/libm.h" #include "libavutil/samplefmt.h" @@ -34,10 +35,8 @@ struct AudioMix { AVAudioResampleContext *avr; enum AVSampleFormat fmt; enum AVMixCoeffType coeff_type; - uint64_t in_layout; - uint64_t out_layout; - int in_channels; - int out_channels; + AVChannelLayout in_layout; + AVChannelLayout out_layout; int ptr_align; int samples_align; @@ -331,8 +330,8 @@ static av_cold int mix_function_init(AudioMix *am) if (!am->mix) { av_log(am->avr, AV_LOG_ERROR, "audio_mix: NO FUNCTION FOUND: [fmt=%s] " "[c=%s] [%d to %d]\n", av_get_sample_fmt_name(am->fmt), - coeff_type_names[am->coeff_type], am->in_channels, - am->out_channels); + coeff_type_names[am->coeff_type], am->in_layout.nb_channels, + am->out_layout.nb_channels); return AVERROR_PATCHWELCOME; } return 0; @@ -358,38 +357,42 @@ AudioMix *ff_audio_mix_alloc(AVAudioResampleContext *avr) am->fmt = avr->internal_sample_fmt; am->coeff_type = avr->mix_coeff_type; - am->in_layout = avr->in_channel_layout; - am->out_layout = avr->out_channel_layout; - am->in_channels = avr->in_channels; - am->out_channels = avr->out_channels; + + ret = av_channel_layout_copy(&am->in_layout, &avr->in_ch_layout); + if (ret < 0) + goto error; + ret = av_channel_layout_copy(&am->out_layout, &avr->out_ch_layout); + if (ret < 0) + goto error; /* build matrix if the user did not already set one */ if (avr->mix_matrix) { - ret = ff_audio_mix_set_matrix(am, avr->mix_matrix, avr->in_channels); + ret = ff_audio_mix_set_matrix(am, avr->mix_matrix, avr->in_ch_layout.nb_channels); if (ret < 0) goto error; av_freep(&avr->mix_matrix); } else { - double *matrix_dbl = av_mallocz(avr->out_channels * avr->in_channels * + double *matrix_dbl = av_mallocz(avr->out_ch_layout.nb_channels * + avr->in_ch_layout.nb_channels * sizeof(*matrix_dbl)); if (!matrix_dbl) goto error; - ret = avresample_build_matrix(avr->in_channel_layout, - avr->out_channel_layout, - avr->center_mix_level, - avr->surround_mix_level, - avr->lfe_mix_level, - avr->normalize_mix_level, - matrix_dbl, - avr->in_channels, - avr->matrix_encoding); + ret = avresample_build_matrix2(&avr->in_ch_layout, + &avr->out_ch_layout, + avr->center_mix_level, + avr->surround_mix_level, + avr->lfe_mix_level, + avr->normalize_mix_level, + matrix_dbl, + avr->in_ch_layout.nb_channels, + avr->matrix_encoding); if (ret < 0) { av_free(matrix_dbl); goto error; } - ret = ff_audio_mix_set_matrix(am, matrix_dbl, avr->in_channels); + ret = ff_audio_mix_set_matrix(am, matrix_dbl, avr->in_ch_layout.nb_channels); if (ret < 0) { av_log(avr, AV_LOG_ERROR, "error setting mix matrix\n"); av_free(matrix_dbl); @@ -402,7 +405,7 @@ AudioMix *ff_audio_mix_alloc(AVAudioResampleContext *avr) return am; error: - av_free(am); + ff_audio_mix_free(&am); return NULL; } @@ -422,11 +425,16 @@ void ff_audio_mix_free(AudioMix **am_p) memset(am->matrix_q15, 0, sizeof(am->matrix_q15)); memset(am->matrix_flt, 0, sizeof(am->matrix_flt)); + av_channel_layout_uninit(&am->in_layout); + av_channel_layout_uninit(&am->out_layout); + av_freep(am_p); } int ff_audio_mix(AudioMix *am, AudioData *src) { + int in_channels = am->in_layout.nb_channels; + int out_channels = am->out_layout.nb_channels; int use_generic = 1; int len = src->nb_samples; int i, j; @@ -442,16 +450,16 @@ int ff_audio_mix(AudioMix *am, AudioData *src) } } av_log(am->avr, AV_LOG_TRACE, "audio_mix: %d samples - %d to %d channels (%s)\n", - src->nb_samples, am->in_channels, am->out_channels, + src->nb_samples, in_channels, out_channels, use_generic ? am->func_descr_generic : am->func_descr); if (am->in_matrix_channels && am->out_matrix_channels) { uint8_t **data; uint8_t *data0[AVRESAMPLE_MAX_CHANNELS] = { NULL }; - if (am->out_matrix_channels < am->out_channels || - am->in_matrix_channels < am->in_channels) { - for (i = 0, j = 0; i < FFMAX(am->in_channels, am->out_channels); i++) { + if (am->out_matrix_channels < out_channels || + am->in_matrix_channels < in_channels) { + for (i = 0, j = 0; i < FFMAX(in_channels, out_channels); i++) { if (am->input_skip[i] || am->output_skip[i] || am->output_zero[i]) continue; data0[j++] = src->data[i]; @@ -469,23 +477,25 @@ int ff_audio_mix(AudioMix *am, AudioData *src) am->in_matrix_channels); } - if (am->out_matrix_channels < am->out_channels) { - for (i = 0; i < am->out_channels; i++) + if (am->out_matrix_channels < out_channels) { + for (i = 0; i < out_channels; i++) if (am->output_zero[i]) av_samples_set_silence(&src->data[i], 0, len, 1, am->fmt); } - ff_audio_data_set_channels(src, am->out_channels); + ff_audio_data_set_channels(src, out_channels); return 0; } int ff_audio_mix_get_matrix(AudioMix *am, double *matrix, int stride) { + int in_channels = am->in_layout.nb_channels; + int out_channels = am->out_layout.nb_channels; int i, o, i0, o0; - if ( am->in_channels <= 0 || am->in_channels > AVRESAMPLE_MAX_CHANNELS || - am->out_channels <= 0 || am->out_channels > AVRESAMPLE_MAX_CHANNELS) { + if (in_channels <= 0 || in_channels > AVRESAMPLE_MAX_CHANNELS || + out_channels <= 0 || out_channels > AVRESAMPLE_MAX_CHANNELS) { av_log(am->avr, AV_LOG_ERROR, "Invalid channel counts\n"); return AVERROR(EINVAL); } @@ -495,8 +505,8 @@ int ff_audio_mix_get_matrix(AudioMix *am, double *matrix, int stride) av_log(am->avr, AV_LOG_ERROR, "matrix is not set\n"); \ return AVERROR(EINVAL); \ } \ - for (o = 0, o0 = 0; o < am->out_channels; o++) { \ - for (i = 0, i0 = 0; i < am->in_channels; i++) { \ + for (o = 0, o0 = 0; o < out_channels; o++) { \ + for (i = 0, i0 = 0; i < in_channels; i++) { \ if (am->input_skip[i] || am->output_zero[o]) \ matrix[o * stride + i] = 0.0; \ else \ @@ -529,6 +539,8 @@ int ff_audio_mix_get_matrix(AudioMix *am, double *matrix, int stride) static void reduce_matrix(AudioMix *am, const double *matrix, int stride) { + int in_channels = am->in_layout.nb_channels; + int out_channels = am->out_layout.nb_channels; int i, o; memset(am->output_zero, 0, sizeof(am->output_zero)); @@ -536,11 +548,11 @@ static void reduce_matrix(AudioMix *am, const double *matrix, int stride) memset(am->output_skip, 0, sizeof(am->output_skip)); /* exclude output channels if they can be zeroed instead of mixed */ - for (o = 0; o < am->out_channels; o++) { + for (o = 0; o < out_channels; o++) { int zero = 1; /* check if the output is always silent */ - for (i = 0; i < am->in_channels; i++) { + for (i = 0; i < in_channels; i++) { if (matrix[o * stride + i] != 0.0) { zero = 0; break; @@ -548,8 +560,8 @@ static void reduce_matrix(AudioMix *am, const double *matrix, int stride) } /* check if the corresponding input channel makes a contribution to any output channel */ - if (o < am->in_channels) { - for (i = 0; i < am->out_channels; i++) { + if (o < in_channels) { + for (i = 0; i < out_channels; i++) { if (matrix[i * stride + o] != 0.0) { zero = 0; break; @@ -559,7 +571,7 @@ static void reduce_matrix(AudioMix *am, const double *matrix, int stride) if (zero) { am->output_zero[o] = 1; am->out_matrix_channels--; - if (o < am->in_channels) + if (o < in_channels) am->in_matrix_channels--; } } @@ -571,10 +583,10 @@ static void reduce_matrix(AudioMix *am, const double *matrix, int stride) /* skip input channels that contribute fully only to the corresponding output channel */ - for (i = 0; i < FFMIN(am->in_channels, am->out_channels); i++) { + for (i = 0; i < FFMIN(in_channels, out_channels); i++) { int skip = 1; - for (o = 0; o < am->out_channels; o++) { + for (o = 0; o < out_channels; o++) { int i0; if ((o != i && matrix[o * stride + i] != 0.0) || (o == i && matrix[o * stride + i] != 1.0)) { @@ -584,7 +596,7 @@ static void reduce_matrix(AudioMix *am, const double *matrix, int stride) /* if the input contributes fully to the output, also check that no other inputs contribute to this output */ if (o == i) { - for (i0 = 0; i0 < am->in_channels; i0++) { + for (i0 = 0; i0 < in_channels; i0++) { if (i0 != i && matrix[o * stride + i0] != 0.0) { skip = 0; break; @@ -598,10 +610,10 @@ static void reduce_matrix(AudioMix *am, const double *matrix, int stride) } } /* skip input channels that do not contribute to any output channel */ - for (; i < am->in_channels; i++) { + for (; i < in_channels; i++) { int contrib = 0; - for (o = 0; o < am->out_channels; o++) { + for (o = 0; o < out_channels; o++) { if (matrix[o * stride + i] != 0.0) { contrib = 1; break; @@ -619,11 +631,11 @@ static void reduce_matrix(AudioMix *am, const double *matrix, int stride) /* skip output channels that only get full contribution from the corresponding input channel */ - for (o = 0; o < FFMIN(am->in_channels, am->out_channels); o++) { + for (o = 0; o < FFMIN(in_channels, out_channels); o++) { int skip = 1; int o0; - for (i = 0; i < am->in_channels; i++) { + for (i = 0; i < in_channels; i++) { if ((o != i && matrix[o * stride + i] != 0.0) || (o == i && matrix[o * stride + i] != 1.0)) { skip = 0; @@ -633,7 +645,7 @@ static void reduce_matrix(AudioMix *am, const double *matrix, int stride) /* check if the corresponding input channel makes a contribution to any other output channel */ i = o; - for (o0 = 0; o0 < am->out_channels; o0++) { + for (o0 = 0; o0 < out_channels; o0++) { if (o0 != i && matrix[o0 * stride + i] != 0.0) { skip = 0; break; @@ -653,11 +665,15 @@ static void reduce_matrix(AudioMix *am, const double *matrix, int stride) int ff_audio_mix_set_matrix(AudioMix *am, const double *matrix, int stride) { int i, o, i0, o0, ret; - char in_layout_name[128]; - char out_layout_name[128]; - - if ( am->in_channels <= 0 || am->in_channels > AVRESAMPLE_MAX_CHANNELS || - am->out_channels <= 0 || am->out_channels > AVRESAMPLE_MAX_CHANNELS) { + int in_channels = am->in_layout.nb_channels; + int out_channels = am->out_layout.nb_channels; + char *in_layout_name; + char *out_layout_name; + + if (!av_channel_layout_check(&am->in_layout) || + !av_channel_layout_check(&am->out_layout) || + am->in_layout.nb_channels > AVRESAMPLE_MAX_CHANNELS || + am->out_layout.nb_channels > AVRESAMPLE_MAX_CHANNELS) { av_log(am->avr, AV_LOG_ERROR, "Invalid channel counts\n"); return AVERROR(EINVAL); } @@ -667,8 +683,8 @@ int ff_audio_mix_set_matrix(AudioMix *am, const double *matrix, int stride) am->matrix = NULL; } - am->in_matrix_channels = am->in_channels; - am->out_matrix_channels = am->out_channels; + am->in_matrix_channels = am->in_layout.nb_channels; + am->out_matrix_channels = am->out_layout.nb_channels; reduce_matrix(am, matrix, stride); @@ -678,13 +694,13 @@ int ff_audio_mix_set_matrix(AudioMix *am, const double *matrix, int stride) sizeof(*am->matrix_## type[0])); \ if (!am->matrix_## type[0]) \ return AVERROR(ENOMEM); \ - for (o = 0, o0 = 0; o < am->out_channels; o++) { \ + for (o = 0, o0 = 0; o < out_channels; o++) { \ if (am->output_zero[o] || am->output_skip[o]) \ continue; \ if (o0 > 0) \ am->matrix_## type[o0] = am->matrix_## type[o0 - 1] + \ am->in_matrix_channels; \ - for (i = 0, i0 = 0; i < am->in_channels; i++) { \ + for (i = 0, i0 = 0; i < in_channels; i++) { \ double v; \ if (am->input_skip[i] || am->output_zero[i]) \ continue; \ @@ -717,26 +733,28 @@ int ff_audio_mix_set_matrix(AudioMix *am, const double *matrix, int stride) if (ret < 0) return ret; - av_get_channel_layout_string(in_layout_name, sizeof(in_layout_name), - am->in_channels, am->in_layout); - av_get_channel_layout_string(out_layout_name, sizeof(out_layout_name), - am->out_channels, am->out_layout); - av_log(am->avr, AV_LOG_DEBUG, "audio_mix: %s to %s\n", - in_layout_name, out_layout_name); + in_layout_name = av_channel_layout_describe(&am->in_layout); + out_layout_name = av_channel_layout_describe(&am->out_layout); + if (in_layout_name && out_layout_name) + av_log(am->avr, AV_LOG_DEBUG, "audio_mix: %s to %s\n", + in_layout_name, out_layout_name); av_log(am->avr, AV_LOG_DEBUG, "matrix size: %d x %d\n", am->in_matrix_channels, am->out_matrix_channels); - for (o = 0; o < am->out_channels; o++) { - for (i = 0; i < am->in_channels; i++) { + for (o = 0; o < out_channels; o++) { + for (i = 0; i < in_channels; i++) { if (am->output_zero[o]) av_log(am->avr, AV_LOG_DEBUG, " (ZERO)"); else if (am->input_skip[i] || am->output_zero[i] || am->output_skip[o]) av_log(am->avr, AV_LOG_DEBUG, " (SKIP)"); else av_log(am->avr, AV_LOG_DEBUG, " %0.3f ", - matrix[o * am->in_channels + i]); + matrix[o * in_channels + i]); } av_log(am->avr, AV_LOG_DEBUG, "\n"); } + av_freep(&in_layout_name); + av_freep(&out_layout_name); + return 0; } diff --git a/libavresample/audio_mix_matrix.c b/libavresample/audio_mix_matrix.c index 5182ae1bf9..a5a16edca1 100644 --- a/libavresample/audio_mix_matrix.c +++ b/libavresample/audio_mix_matrix.c @@ -21,6 +21,7 @@ #include <stdint.h> +#include "libavutil/channel_layout.h" #include "libavutil/common.h" #include "libavutil/libm.h" #include "libavutil/samplefmt.h" @@ -29,33 +30,6 @@ #include "audio_data.h" #include "audio_mix.h" -/* channel positions */ -#define FRONT_LEFT 0 -#define FRONT_RIGHT 1 -#define FRONT_CENTER 2 -#define LOW_FREQUENCY 3 -#define BACK_LEFT 4 -#define BACK_RIGHT 5 -#define FRONT_LEFT_OF_CENTER 6 -#define FRONT_RIGHT_OF_CENTER 7 -#define BACK_CENTER 8 -#define SIDE_LEFT 9 -#define SIDE_RIGHT 10 -#define TOP_CENTER 11 -#define TOP_FRONT_LEFT 12 -#define TOP_FRONT_CENTER 13 -#define TOP_FRONT_RIGHT 14 -#define TOP_BACK_LEFT 15 -#define TOP_BACK_CENTER 16 -#define TOP_BACK_RIGHT 17 -#define STEREO_LEFT 29 -#define STEREO_RIGHT 30 -#define WIDE_LEFT 31 -#define WIDE_RIGHT 32 -#define SURROUND_DIRECT_LEFT 33 -#define SURROUND_DIRECT_RIGHT 34 -#define LOW_FREQUENCY_2 35 - #define SQRT3_2 1.22474487139158904909 /* sqrt(3/2) */ static av_always_inline int even(uint64_t layout) @@ -63,232 +37,313 @@ static av_always_inline int even(uint64_t layout) return (!layout || !!(layout & (layout - 1))); } -static int sane_layout(uint64_t layout) +static int sane_layout(const AVChannelLayout *layout) { /* check that there is at least 1 front speaker */ - if (!(layout & AV_CH_LAYOUT_SURROUND)) + if (!av_channel_layout_subset(layout, AV_CH_LAYOUT_SURROUND)) return 0; /* check for left/right symmetry */ - if (!even(layout & (AV_CH_FRONT_LEFT | AV_CH_FRONT_RIGHT)) || - !even(layout & (AV_CH_SIDE_LEFT | AV_CH_SIDE_RIGHT)) || - !even(layout & (AV_CH_BACK_LEFT | AV_CH_BACK_RIGHT)) || - !even(layout & (AV_CH_FRONT_LEFT_OF_CENTER | AV_CH_FRONT_RIGHT_OF_CENTER)) || - !even(layout & (AV_CH_TOP_FRONT_LEFT | AV_CH_TOP_FRONT_RIGHT)) || - !even(layout & (AV_CH_TOP_BACK_LEFT | AV_CH_TOP_BACK_RIGHT)) || - !even(layout & (AV_CH_STEREO_LEFT | AV_CH_STEREO_RIGHT)) || - !even(layout & (AV_CH_WIDE_LEFT | AV_CH_WIDE_RIGHT)) || - !even(layout & (AV_CH_SURROUND_DIRECT_LEFT | AV_CH_SURROUND_DIRECT_RIGHT))) + if (!even(av_channel_layout_subset(layout, (AV_CH_FRONT_LEFT | AV_CH_FRONT_RIGHT))) || + !even(av_channel_layout_subset(layout, (AV_CH_SIDE_LEFT | AV_CH_SIDE_RIGHT))) || + !even(av_channel_layout_subset(layout, (AV_CH_BACK_LEFT | AV_CH_BACK_RIGHT))) || + !even(av_channel_layout_subset(layout, (AV_CH_FRONT_LEFT_OF_CENTER | AV_CH_FRONT_RIGHT_OF_CENTER))) || + !even(av_channel_layout_subset(layout, (AV_CH_TOP_FRONT_LEFT | AV_CH_TOP_FRONT_RIGHT))) || + !even(av_channel_layout_subset(layout, (AV_CH_TOP_BACK_LEFT | AV_CH_TOP_BACK_RIGHT))) || + !even(av_channel_layout_subset(layout, (AV_CH_STEREO_LEFT | AV_CH_STEREO_RIGHT))) || + !even(av_channel_layout_subset(layout, (AV_CH_WIDE_LEFT | AV_CH_WIDE_RIGHT))) || + !even(av_channel_layout_subset(layout, (AV_CH_SURROUND_DIRECT_LEFT | AV_CH_SURROUND_DIRECT_RIGHT)))) return 0; return 1; } -int avresample_build_matrix(uint64_t in_layout, uint64_t out_layout, - double center_mix_level, double surround_mix_level, - double lfe_mix_level, int normalize, - double *matrix_out, int stride, - enum AVMatrixEncoding matrix_encoding) +#define IDX_OUT(ch) (av_channel_layout_channel_index(out_layout, ch)) + +int avresample_build_matrix2(const AVChannelLayout *in_layout, + const AVChannelLayout *out_layout, + double center_mix_level, double surround_mix_level, + double lfe_mix_level, int normalize, + double *matrix, int stride, + enum AVMatrixEncoding matrix_encoding) { - int i, j, out_i, out_j; - double matrix[64][64] = {{0}}; - int64_t unaccounted; + static const AVChannelLayout stereo = AV_CHANNEL_LAYOUT_STEREO; + int i, j; double maxcoef = 0; - int in_channels, out_channels; - - if ((out_layout & AV_CH_LAYOUT_STEREO_DOWNMIX) == AV_CH_LAYOUT_STEREO_DOWNMIX) { - out_layout = AV_CH_LAYOUT_STEREO; - } - - unaccounted = in_layout & ~out_layout; - - in_channels = av_get_channel_layout_nb_channels( in_layout); - out_channels = av_get_channel_layout_nb_channels(out_layout); - - memset(matrix_out, 0, out_channels * stride * sizeof(*matrix_out)); + int idx_in, idx_out, idx_r, idx_l; + int in_channels = in_layout->nb_channels; + int out_channels = out_layout->nb_channels;; /* check if layouts are supported */ - if (!in_layout || in_channels > AVRESAMPLE_MAX_CHANNELS) + if (!av_channel_layout_check(in_layout) || + in_channels > AVRESAMPLE_MAX_CHANNELS) return AVERROR(EINVAL); - if (!out_layout || out_channels > AVRESAMPLE_MAX_CHANNELS) + if (!av_channel_layout_check(out_layout) || + out_channels > AVRESAMPLE_MAX_CHANNELS) return AVERROR(EINVAL); /* check if layouts are unbalanced or abnormal */ if (!sane_layout(in_layout) || !sane_layout(out_layout)) return AVERROR_PATCHWELCOME; - /* route matching input/output channels */ - for (i = 0; i < 64; i++) { - if (in_layout & out_layout & (1ULL << i)) - matrix[i][i] = 1.0; - } + if (out_channels == 2 && + IDX_OUT(AV_CHAN_STEREO_LEFT) >= 0 && + IDX_OUT(AV_CHAN_STEREO_RIGHT) >= 0) + out_layout = &stereo; + + memset(matrix, 0, out_channels * stride * sizeof(*matrix)); - /* mix front center to front left/right */ - if (unaccounted & AV_CH_FRONT_CENTER) { - if ((out_layout & AV_CH_LAYOUT_STEREO) == AV_CH_LAYOUT_STEREO) { - if ((in_layout & AV_CH_LAYOUT_STEREO) == AV_CH_LAYOUT_STEREO) { - matrix[FRONT_LEFT ][FRONT_CENTER] += center_mix_level; - matrix[FRONT_RIGHT][FRONT_CENTER] += center_mix_level; + for (idx_in = 0; idx_in < in_channels; idx_in++) { + enum AVChannel in_ch = av_channel_layout_get_channel(in_layout, idx_in); + + idx_out = IDX_OUT(in_ch); + + /* check if the input channel is also present in output */ + if (idx_out >= 0) { + if (in_ch == AV_CHAN_FRONT_CENTER && + av_channel_layout_subset(in_layout, AV_CH_LAYOUT_STEREO) == AV_CH_LAYOUT_STEREO && + !av_channel_layout_subset(out_layout, AV_CH_LAYOUT_STEREO)) { + /* mix left/right/center to center */ + matrix[idx_out * stride + idx_in] = center_mix_level * M_SQRT2; } else { - matrix[FRONT_LEFT ][FRONT_CENTER] += M_SQRT1_2; - matrix[FRONT_RIGHT][FRONT_CENTER] += M_SQRT1_2; + /* just copy it */ + matrix[idx_out * stride + idx_in] = 1.0; } - } else - return AVERROR_PATCHWELCOME; - } - /* mix front left/right to center */ - if (unaccounted & AV_CH_LAYOUT_STEREO) { - if (out_layout & AV_CH_FRONT_CENTER) { - matrix[FRONT_CENTER][FRONT_LEFT ] += M_SQRT1_2; - matrix[FRONT_CENTER][FRONT_RIGHT] += M_SQRT1_2; - /* mix left/right/center to center */ - if (in_layout & AV_CH_FRONT_CENTER) - matrix[FRONT_CENTER][FRONT_CENTER] = center_mix_level * M_SQRT2; - } else - return AVERROR_PATCHWELCOME; - } - /* mix back center to back, side, or front */ - if (unaccounted & AV_CH_BACK_CENTER) { - if (out_layout & AV_CH_BACK_LEFT) { - matrix[BACK_LEFT ][BACK_CENTER] += M_SQRT1_2; - matrix[BACK_RIGHT][BACK_CENTER] += M_SQRT1_2; - } else if (out_layout & AV_CH_SIDE_LEFT) { - matrix[SIDE_LEFT ][BACK_CENTER] += M_SQRT1_2; - matrix[SIDE_RIGHT][BACK_CENTER] += M_SQRT1_2; - } else if (out_layout & AV_CH_FRONT_LEFT) { - if (matrix_encoding == AV_MATRIX_ENCODING_DOLBY || - matrix_encoding == AV_MATRIX_ENCODING_DPLII) { - if (unaccounted & (AV_CH_BACK_LEFT | AV_CH_SIDE_LEFT)) { - matrix[FRONT_LEFT ][BACK_CENTER] -= surround_mix_level * M_SQRT1_2; - matrix[FRONT_RIGHT][BACK_CENTER] += surround_mix_level * M_SQRT1_2; + + continue; + } + + /* the input channel is not present in the output layout */ + + /* mix front center to front left/right */ + if (in_ch == AV_CHAN_FRONT_CENTER) { + int idx_l = IDX_OUT(AV_CHAN_FRONT_LEFT); + int idx_r = IDX_OUT(AV_CHAN_FRONT_RIGHT); + if (idx_l >= 0 && idx_r >= 0) { + matrix[idx_l * stride + idx_in] += M_SQRT1_2; + matrix[idx_r * stride + idx_in] += M_SQRT1_2; + } + } + + /* mix front left/right to center */ + if (in_ch == AV_CHAN_FRONT_LEFT || in_ch == AV_CHAN_FRONT_RIGHT) { + idx_out = IDX_OUT(AV_CHAN_FRONT_CENTER); + if (idx_out >= 0) + matrix[idx_out * stride + idx_in] += M_SQRT1_2; + } + + /* mix back center to back, side, or front */ + if (in_ch == AV_CHAN_BACK_CENTER) { + int idx_l = IDX_OUT(AV_CHAN_BACK_LEFT); + int idx_r = IDX_OUT(AV_CHAN_BACK_RIGHT); + if (idx_l >= 0 && idx_r >= 0) { + matrix[idx_l * stride + idx_in] += M_SQRT1_2; + matrix[idx_r * stride + idx_in] += M_SQRT1_2; + continue; + } + + idx_l = IDX_OUT(AV_CHAN_SIDE_LEFT); + idx_r = IDX_OUT(AV_CHAN_SIDE_RIGHT); + if (idx_l >= 0 && idx_r >= 0) { + matrix[idx_l * stride + idx_in] += M_SQRT1_2; + matrix[idx_r * stride + idx_in] += M_SQRT1_2; + continue; + } + + idx_l = IDX_OUT(AV_CHAN_FRONT_LEFT); + idx_r = IDX_OUT(AV_CHAN_FRONT_RIGHT); + if (idx_l >= 0 && idx_r >= 0) { + if (matrix_encoding == AV_MATRIX_ENCODING_DOLBY || + matrix_encoding == AV_MATRIX_ENCODING_DPLII) { + if (!av_channel_layout_subset(out_layout, AV_CH_BACK_LEFT | AV_CH_SIDE_LEFT) && + av_channel_layout_subset(in_layout, AV_CH_BACK_LEFT | AV_CH_SIDE_LEFT)) { + matrix[idx_l * stride + idx_in] -= surround_mix_level * M_SQRT1_2; + matrix[idx_r * stride + idx_in] += surround_mix_level * M_SQRT1_2; + } else { + matrix[idx_l * stride + idx_in] -= surround_mix_level; + matrix[idx_r * stride + idx_in] += surround_mix_level; + } } else { - matrix[FRONT_LEFT ][BACK_CENTER] -= surround_mix_level; - matrix[FRONT_RIGHT][BACK_CENTER] += surround_mix_level; + matrix[idx_l * stride + idx_in] += surround_mix_level * M_SQRT1_2; + matrix[idx_r * stride + idx_in] += surround_mix_level * M_SQRT1_2; } - } else { - matrix[FRONT_LEFT ][BACK_CENTER] += surround_mix_level * M_SQRT1_2; - matrix[FRONT_RIGHT][BACK_CENTER] += surround_mix_level * M_SQRT1_2; + continue; } - } else if (out_layout & AV_CH_FRONT_CENTER) { - matrix[FRONT_CENTER][BACK_CENTER] += surround_mix_level * M_SQRT1_2; - } else - return AVERROR_PATCHWELCOME; - } - /* mix back left/right to back center, side, or front */ - if (unaccounted & AV_CH_BACK_LEFT) { - if (out_layout & AV_CH_BACK_CENTER) { - matrix[BACK_CENTER][BACK_LEFT ] += M_SQRT1_2; - matrix[BACK_CENTER][BACK_RIGHT] += M_SQRT1_2; - } else if (out_layout & AV_CH_SIDE_LEFT) { - /* if side channels do not exist in the input, just copy back - channels to side channels, otherwise mix back into side */ - if (in_layout & AV_CH_SIDE_LEFT) { - matrix[SIDE_LEFT ][BACK_LEFT ] += M_SQRT1_2; - matrix[SIDE_RIGHT][BACK_RIGHT] += M_SQRT1_2; - } else { - matrix[SIDE_LEFT ][BACK_LEFT ] += 1.0; - matrix[SIDE_RIGHT][BACK_RIGHT] += 1.0; + + idx_out = IDX_OUT(AV_CHAN_FRONT_CENTER); + if (idx_out >= 0) { + matrix[idx_out * stride + idx_in] += surround_mix_level * M_SQRT1_2; + continue; } - } else if (out_layout & AV_CH_FRONT_LEFT) { - if (matrix_encoding == AV_MATRIX_ENCODING_DOLBY) { - matrix[FRONT_LEFT ][BACK_LEFT ] -= surround_mix_level * M_SQRT1_2; - matrix[FRONT_LEFT ][BACK_RIGHT] -= surround_mix_level * M_SQRT1_2; - matrix[FRONT_RIGHT][BACK_LEFT ] += surround_mix_level * M_SQRT1_2; - matrix[FRONT_RIGHT][BACK_RIGHT] += surround_mix_level * M_SQRT1_2; - } else if (matrix_encoding == AV_MATRIX_ENCODING_DPLII) { - matrix[FRONT_LEFT ][BACK_LEFT ] -= surround_mix_level * SQRT3_2; - matrix[FRONT_LEFT ][BACK_RIGHT] -= surround_mix_level * M_SQRT1_2; - matrix[FRONT_RIGHT][BACK_LEFT ] += surround_mix_level * M_SQRT1_2; - matrix[FRONT_RIGHT][BACK_RIGHT] += surround_mix_level * SQRT3_2; - } else { - matrix[FRONT_LEFT ][BACK_LEFT ] += surround_mix_level; - matrix[FRONT_RIGHT][BACK_RIGHT] += surround_mix_level; + } + + /* mix back left/right to back center, side, or front */ + if (in_ch == AV_CHAN_BACK_LEFT || in_ch == AV_CHAN_BACK_RIGHT) { + idx_out = IDX_OUT(AV_CHAN_BACK_CENTER); + if (idx_out >= 0) { + matrix[idx_out * stride + idx_in] += M_SQRT1_2; + continue; } - } else if (out_layout & AV_CH_FRONT_CENTER) { - matrix[FRONT_CENTER][BACK_LEFT ] += surround_mix_level * M_SQRT1_2; - matrix[FRONT_CENTER][BACK_RIGHT] += surround_mix_level * M_SQRT1_2; - } else - return AVERROR_PATCHWELCOME; - } - /* mix side left/right into back or front */ - if (unaccounted & AV_CH_SIDE_LEFT) { - if (out_layout & AV_CH_BACK_LEFT) { - /* if back channels do not exist in the input, just copy side - channels to back channels, otherwise mix side into back */ - if (in_layout & AV_CH_BACK_LEFT) { - matrix[BACK_LEFT ][SIDE_LEFT ] += M_SQRT1_2; - matrix[BACK_RIGHT][SIDE_RIGHT] += M_SQRT1_2; - } else { - matrix[BACK_LEFT ][SIDE_LEFT ] += 1.0; - matrix[BACK_RIGHT][SIDE_RIGHT] += 1.0; + + idx_out = IDX_OUT(in_ch == AV_CHAN_BACK_LEFT ? AV_CHAN_SIDE_LEFT : + AV_CHAN_SIDE_RIGHT); + if (idx_out >= 0) { + /* if side channels do not exist in the input, just copy back + channels to side channels, otherwise mix back into side */ + if (av_channel_layout_subset(in_layout, AV_CH_SIDE_LEFT)) + matrix[idx_out * stride + idx_in] += M_SQRT1_2; + else + matrix[idx_out * stride + idx_in] += 1.0; + + continue; } - } else if (out_layout & AV_CH_BACK_CENTER) { - matrix[BACK_CENTER][SIDE_LEFT ] += M_SQRT1_2; - matrix[BACK_CENTER][SIDE_RIGHT] += M_SQRT1_2; - } else if (out_layout & AV_CH_FRONT_LEFT) { - if (matrix_encoding == AV_MATRIX_ENCODING_DOLBY) { - matrix[FRONT_LEFT ][SIDE_LEFT ] -= surround_mix_level * M_SQRT1_2; - matrix[FRONT_LEFT ][SIDE_RIGHT] -= surround_mix_level * M_SQRT1_2; - matrix[FRONT_RIGHT][SIDE_LEFT ] += surround_mix_level * M_SQRT1_2; - matrix[FRONT_RIGHT][SIDE_RIGHT] += surround_mix_level * M_SQRT1_2; - } else if (matrix_encoding == AV_MATRIX_ENCODING_DPLII) { - matrix[FRONT_LEFT ][SIDE_LEFT ] -= surround_mix_level * SQRT3_2; - matrix[FRONT_LEFT ][SIDE_RIGHT] -= surround_mix_level * M_SQRT1_2; - matrix[FRONT_RIGHT][SIDE_LEFT ] += surround_mix_level * M_SQRT1_2; - matrix[FRONT_RIGHT][SIDE_RIGHT] += surround_mix_level * SQRT3_2; - } else { - matrix[FRONT_LEFT ][SIDE_LEFT ] += surround_mix_level; - matrix[FRONT_RIGHT][SIDE_RIGHT] += surround_mix_level; + + idx_l = IDX_OUT(AV_CHAN_FRONT_LEFT); + idx_r = IDX_OUT(AV_CHAN_FRONT_RIGHT); + if (idx_l >= 0 && idx_r >= 0) { + if (matrix_encoding == AV_MATRIX_ENCODING_DOLBY) { + matrix[idx_l * stride + idx_in] -= surround_mix_level * M_SQRT1_2; + matrix[idx_r * stride + idx_in] += surround_mix_level * M_SQRT1_2; + } else if (matrix_encoding == AV_MATRIX_ENCODING_DPLII) { + if (in_ch == AV_CHAN_BACK_LEFT) { + matrix[idx_l * stride + idx_in] -= surround_mix_level * SQRT3_2; + matrix[idx_r * stride + idx_in] += surround_mix_level * M_SQRT1_2; + } else { + matrix[idx_l * stride + idx_in] -= surround_mix_level * M_SQRT1_2; + matrix[idx_r * stride + idx_in] += surround_mix_level * SQRT3_2; + } + } else { + idx_out = (in_ch == AV_CHAN_BACK_LEFT) ? idx_l : idx_r; + matrix[idx_out * stride + idx_in] += surround_mix_level; + } + + continue; } - } else if (out_layout & AV_CH_FRONT_CENTER) { - matrix[FRONT_CENTER][SIDE_LEFT ] += surround_mix_level * M_SQRT1_2; - matrix[FRONT_CENTER][SIDE_RIGHT] += surround_mix_level * M_SQRT1_2; - } else - return AVERROR_PATCHWELCOME; - } - /* mix left-of-center/right-of-center into front left/right or center */ - if (unaccounted & AV_CH_FRONT_LEFT_OF_CENTER) { - if (out_layout & AV_CH_FRONT_LEFT) { - matrix[FRONT_LEFT ][FRONT_LEFT_OF_CENTER ] += 1.0; - matrix[FRONT_RIGHT][FRONT_RIGHT_OF_CENTER] += 1.0; - } else if (out_layout & AV_CH_FRONT_CENTER) { - matrix[FRONT_CENTER][FRONT_LEFT_OF_CENTER ] += M_SQRT1_2; - matrix[FRONT_CENTER][FRONT_RIGHT_OF_CENTER] += M_SQRT1_2; - } else - return AVERROR_PATCHWELCOME; - } - /* mix LFE into front left/right or center */ - if (unaccounted & AV_CH_LOW_FREQUENCY) { - if (out_layout & AV_CH_FRONT_CENTER) { - matrix[FRONT_CENTER][LOW_FREQUENCY] += lfe_mix_level; - } else if (out_layout & AV_CH_FRONT_LEFT) { - matrix[FRONT_LEFT ][LOW_FREQUENCY] += lfe_mix_level * M_SQRT1_2; - matrix[FRONT_RIGHT][LOW_FREQUENCY] += lfe_mix_level * M_SQRT1_2; - } else - return AVERROR_PATCHWELCOME; + + idx_out = IDX_OUT(AV_CHAN_FRONT_CENTER); + if (idx_out >= 0) { + matrix[idx_out * stride + idx_in] += surround_mix_level * M_SQRT1_2; + continue; + } + } + + /* mix side left/right into back or front */ + if (in_ch == AV_CHAN_SIDE_LEFT || in_ch == AV_CHAN_SIDE_RIGHT) { + idx_out = IDX_OUT(in_ch == AV_CHAN_SIDE_LEFT ? AV_CHAN_BACK_LEFT : + AV_CHAN_BACK_RIGHT); + if (idx_out >= 0) { + /* if back channels do not exist in the input, just copy side + channels to back channels, otherwise mix side into back */ + if (av_channel_layout_subset(in_layout, AV_CH_BACK_LEFT)) + matrix[idx_out * stride + idx_in] += M_SQRT1_2; + else + matrix[idx_out * stride + idx_in] += 1.0; + + continue; + } + + idx_out = IDX_OUT(AV_CHAN_BACK_CENTER); + if (idx_out >= 0) { + matrix[idx_out * stride + idx_in] += M_SQRT1_2; + continue; + } + + + idx_l = IDX_OUT(AV_CHAN_FRONT_LEFT); + idx_r = IDX_OUT(AV_CHAN_FRONT_RIGHT); + if (idx_l >= 0 && idx_r >= 0) { + if (matrix_encoding == AV_MATRIX_ENCODING_DOLBY) { + matrix[idx_l * stride + idx_in] -= surround_mix_level * M_SQRT1_2; + matrix[idx_r * stride + idx_in] += surround_mix_level * M_SQRT1_2; + } else if (matrix_encoding == AV_MATRIX_ENCODING_DPLII) { + if (in_ch == AV_CHAN_SIDE_LEFT) { + matrix[idx_l * stride + idx_in] -= surround_mix_level * SQRT3_2; + matrix[idx_r * stride + idx_in] += surround_mix_level * M_SQRT1_2; + } else { + matrix[idx_l * stride + idx_in] -= surround_mix_level * M_SQRT1_2; + matrix[idx_r * stride + idx_in] += surround_mix_level * SQRT3_2; + } + } else { + idx_out = (in_ch == AV_CHAN_SIDE_LEFT) ? idx_l : idx_r; + matrix[idx_out * stride + idx_in] += surround_mix_level; + } + + continue; + } + + idx_out = IDX_OUT(AV_CHAN_FRONT_CENTER); + if (idx_out >= 0) { + matrix[idx_out * stride + idx_in] += surround_mix_level * M_SQRT1_2; + continue; + } + } + + /* mix left-of-center/right-of-center into front left/right or center */ + if (in_ch == AV_CHAN_FRONT_LEFT_OF_CENTER || + in_ch == AV_CHAN_FRONT_RIGHT_OF_CENTER) { + idx_out = IDX_OUT(in_ch == AV_CHAN_FRONT_LEFT_OF_CENTER ? + AV_CHAN_FRONT_LEFT : AV_CHAN_FRONT_RIGHT); + if (idx_out >= 0) { + matrix[idx_out * stride + idx_in] += 1.0; + continue; + } + + idx_out = IDX_OUT(AV_CHAN_FRONT_CENTER); + if (idx_out >= 0) { + matrix[idx_out * stride + idx_in] += M_SQRT1_2; + continue; + } + } + + /* mix LFE into front left/right or center */ + if (in_ch == AV_CHAN_LOW_FREQUENCY) { + idx_out = IDX_OUT(AV_CHAN_FRONT_CENTER); + if (idx_out >= 0) { + matrix[idx_out * stride + idx_in] += lfe_mix_level; + continue; + } + + idx_l = IDX_OUT(AV_CHAN_FRONT_LEFT); + idx_r = IDX_OUT(AV_CHAN_FRONT_RIGHT); + if (idx_l >= 0 && idx_r >= 0) { + matrix[idx_l * stride + idx_in] += lfe_mix_level * M_SQRT1_2; + matrix[idx_r * stride + idx_in] += lfe_mix_level * M_SQRT1_2; + continue; + } + } } - /* transfer internal matrix to output matrix and calculate maximum - per-channel coefficient sum */ - for (out_i = i = 0; out_i < out_channels && i < 64; i++) { + /* calculate maximum per-channel coefficient sum */ + for (idx_out = 0; idx_out < out_channels; idx_out++) { double sum = 0; - for (out_j = j = 0; out_j < in_channels && j < 64; j++) { - matrix_out[out_i * stride + out_j] = matrix[i][j]; - sum += fabs(matrix[i][j]); - if (in_layout & (1ULL << j)) - out_j++; - } + for (idx_in = 0; idx_in < in_channels; idx_in++) + sum += fabs(matrix[idx_out * stride + idx_in]); maxcoef = FFMAX(maxcoef, sum); - if (out_layout & (1ULL << i)) - out_i++; } /* normalize */ if (normalize && maxcoef > 1.0) { for (i = 0; i < out_channels; i++) for (j = 0; j < in_channels; j++) - matrix_out[i * stride + j] /= maxcoef; + matrix[i * stride + j] /= maxcoef; } return 0; } + +#if FF_API_OLD_CHANNEL_LAYOUT +int avresample_build_matrix(uint64_t in_layout, uint64_t out_layout, + double center_mix_level, double surround_mix_level, + double lfe_mix_level, int normalize, + double *matrix_out, int stride, + enum AVMatrixEncoding matrix_encoding) +{ + AVChannelLayout in_chl, out_chl; + + av_channel_layout_from_mask(&in_chl, in_layout); + av_channel_layout_from_mask(&out_chl, out_layout); + return avresample_build_matrix2(&in_chl, &out_chl, center_mix_level, + surround_mix_level, lfe_mix_level, normalize, + matrix_out, stride, matrix_encoding); +} +#endif diff --git a/libavresample/avresample.h b/libavresample/avresample.h index 3f9b9433c1..5a60423470 100644 --- a/libavresample/avresample.h +++ b/libavresample/avresample.h @@ -44,8 +44,10 @@ * matrix): * @code * AVAudioResampleContext *avr = avresample_alloc_context(); - * av_opt_set_int(avr, "in_channel_layout", AV_CH_LAYOUT_5POINT1, 0); - * av_opt_set_int(avr, "out_channel_layout", AV_CH_LAYOUT_STEREO, 0); + * AVChannelLayout in_ch_layout = (AVChannelLayout)AV_CHANNEL_LAYOUT_5POINT1; + * AVChannelLayout out_ch_layout = (AVChannelLayout)AV_CHANNEL_LAYOUT_STEREO; + * av_opt_set_channel_layout(avr, "in_ch_layout", &in_ch_layout, 0); + * av_opt_set_channel_layout(avr, "out_ch_layout", &out_ch_layout, 0); * av_opt_set_int(avr, "in_sample_rate", 48000, 0); * av_opt_set_int(avr, "out_sample_rate", 44100, 0); * av_opt_set_int(avr, "in_sample_fmt", AV_SAMPLE_FMT_FLTP, 0); @@ -92,6 +94,7 @@ * avresample_free(). */ +#include "libavutil/attributes.h" #include "libavutil/avutil.h" #include "libavutil/channel_layout.h" #include "libavutil/dict.h" @@ -167,7 +170,7 @@ AVAudioResampleContext *avresample_alloc_context(void); /** * Initialize AVAudioResampleContext. * @note The context must be configured using the AVOption API. - * @note The fields "in_channel_layout", "out_channel_layout", + * @note The fields "in_ch_layout", "out_ch_layout", * "in_sample_rate", "out_sample_rate", "in_sample_fmt", * "out_sample_fmt" must be set. * @@ -212,6 +215,7 @@ void avresample_close(AVAudioResampleContext *avr); */ void avresample_free(AVAudioResampleContext **avr); +#if FF_API_OLD_CHANNEL_LAYOUT /** * Generate a channel mixing matrix. * @@ -233,11 +237,43 @@ void avresample_free(AVAudioResampleContext **avr); * matrix array * @param matrix_encoding matrixed stereo downmix mode (e.g. dplii) * @return 0 on success, negative AVERROR code on failure + * + * @deprecated use avresample_build_matrix2() */ +attribute_deprecated int avresample_build_matrix(uint64_t in_layout, uint64_t out_layout, double center_mix_level, double surround_mix_level, double lfe_mix_level, int normalize, double *matrix, int stride, enum AVMatrixEncoding matrix_encoding); +#endif + +/** + * Generate a channel mixing matrix. + * + * This function is the one used internally by libavresample for building the + * default mixing matrix. It is made public just as a utility function for + * building custom matrices. + * + * @param in_layout input channel layout + * @param out_layout output channel layout + * @param center_mix_level mix level for the center channel + * @param surround_mix_level mix level for the surround channel(s) + * @param lfe_mix_level mix level for the low-frequency effects channel + * @param normalize if 1, coefficients will be normalized to prevent + * overflow. if 0, coefficients will not be + * normalized. + * @param[out] matrix mixing coefficients; matrix[i + stride * o] is + * the weight of input channel i in output channel o. + * @param stride distance between adjacent input channels in the + * matrix array + * @param matrix_encoding matrixed stereo downmix mode (e.g. dplii) + * @return 0 on success, negative AVERROR code on failure + */ +int avresample_build_matrix2(const AVChannelLayout *in_layout, + const AVChannelLayout *out_layout, + double center_mix_level, double surround_mix_level, + double lfe_mix_level, int normalize, double *matrix, + int stride, enum AVMatrixEncoding matrix_encoding); /** * Get the current channel mixing matrix. diff --git a/libavresample/internal.h b/libavresample/internal.h index b88b7587aa..a6bf2d400e 100644 --- a/libavresample/internal.h +++ b/libavresample/internal.h @@ -22,6 +22,7 @@ #define AVRESAMPLE_INTERNAL_H #include "libavutil/audio_fifo.h" +#include "libavutil/channel_layout.h" #include "libavutil/log.h" #include "libavutil/opt.h" #include "libavutil/samplefmt.h" @@ -53,10 +54,17 @@ typedef struct ChannelMapInfo { struct AVAudioResampleContext { const AVClass *av_class; /**< AVClass for logging and AVOptions */ +#if FF_API_OLD_CHANNEL_LAYOUT uint64_t in_channel_layout; /**< input channel layout */ +#endif + AVChannelLayout in_ch_layout; /**< input channel layout */ + enum AVSampleFormat in_sample_fmt; /**< input sample format */ int in_sample_rate; /**< input sample rate */ +#if FF_API_OLD_CHANNEL_LAYOUT uint64_t out_channel_layout; /**< output channel layout */ +#endif + AVChannelLayout out_ch_layout; /**< output channel layout */ enum AVSampleFormat out_sample_fmt; /**< output sample format */ int out_sample_rate; /**< output sample rate */ enum AVSampleFormat internal_sample_fmt; /**< internal sample format */ @@ -74,8 +82,6 @@ struct AVAudioResampleContext { int kaiser_beta; /**< beta value for Kaiser window (only applicable if filter_type == AV_FILTER_TYPE_KAISER) */ enum AVResampleDitherMethod dither_method; /**< dither method */ - int in_channels; /**< number of input channels */ - int out_channels; /**< number of output channels */ int resample_channels; /**< number of channels used for resampling */ int downmix_needed; /**< downmixing is needed */ int upmix_needed; /**< upmixing is needed */ diff --git a/libavresample/options.c b/libavresample/options.c index 6249f90115..78af61a6e1 100644 --- a/libavresample/options.c +++ b/libavresample/options.c @@ -36,10 +36,18 @@ #define PARAM AV_OPT_FLAG_AUDIO_PARAM static const AVOption avresample_options[] = { +#if FF_API_OLD_CHANNEL_LAYOUT { "in_channel_layout", "Input Channel Layout", OFFSET(in_channel_layout), AV_OPT_TYPE_INT64, { .i64 = 0 }, INT64_MIN, INT64_MAX, PARAM }, +#endif + { "in_ch_layout", "Input Channel Layout", OFFSET(in_ch_layout), AV_OPT_TYPE_CHANNEL_LAYOUT, + { .str = NULL }, .flags = PARAM }, { "in_sample_fmt", "Input Sample Format", OFFSET(in_sample_fmt), AV_OPT_TYPE_INT, { .i64 = AV_SAMPLE_FMT_S16 }, AV_SAMPLE_FMT_U8, AV_SAMPLE_FMT_NB-1, PARAM }, { "in_sample_rate", "Input Sample Rate", OFFSET(in_sample_rate), AV_OPT_TYPE_INT, { .i64 = 48000 }, 1, INT_MAX, PARAM }, +#if FF_API_OLD_CHANNEL_LAYOUT { "out_channel_layout", "Output Channel Layout", OFFSET(out_channel_layout), AV_OPT_TYPE_INT64, { .i64 = 0 }, INT64_MIN, INT64_MAX, PARAM }, +#endif + { "out_ch_layout", "Output Channel Layout", OFFSET(out_ch_layout), AV_OPT_TYPE_CHANNEL_LAYOUT, + { .str = NULL }, .flags = PARAM }, { "out_sample_fmt", "Output Sample Format", OFFSET(out_sample_fmt), AV_OPT_TYPE_INT, { .i64 = AV_SAMPLE_FMT_S16 }, AV_SAMPLE_FMT_U8, AV_SAMPLE_FMT_NB-1, PARAM }, { "out_sample_rate", "Output Sample Rate", OFFSET(out_sample_rate), AV_OPT_TYPE_INT, { .i64 = 48000 }, 1, INT_MAX, PARAM }, { "internal_sample_fmt", "Internal Sample Format", OFFSET(internal_sample_fmt), AV_OPT_TYPE_INT, { .i64 = AV_SAMPLE_FMT_NONE }, AV_SAMPLE_FMT_NONE, AV_SAMPLE_FMT_NB-1, PARAM, "internal_sample_fmt" }, diff --git a/libavresample/tests/avresample.c b/libavresample/tests/avresample.c index 77599960f8..19027b2df1 100644 --- a/libavresample/tests/avresample.c +++ b/libavresample/tests/avresample.c @@ -179,11 +179,11 @@ static const int rates[] = { 16000 }; -static const uint64_t layouts[] = { - AV_CH_LAYOUT_STEREO, - AV_CH_LAYOUT_MONO, - AV_CH_LAYOUT_5POINT1, - AV_CH_LAYOUT_7POINT1, +static const AVChannelLayout layouts[] = { + AV_CHANNEL_LAYOUT_STEREO, + AV_CHANNEL_LAYOUT_MONO, + AV_CHANNEL_LAYOUT_5POINT1, + AV_CHANNEL_LAYOUT_7POINT1, }; int main(int argc, char **argv) @@ -199,12 +199,8 @@ int main(int argc, char **argv) uint8_t *out_data[AVRESAMPLE_MAX_CHANNELS] = { 0 }; int in_linesize; int out_linesize; - uint64_t in_ch_layout; - int in_channels; enum AVSampleFormat in_fmt; int in_rate; - uint64_t out_ch_layout; - int out_channels; enum AVSampleFormat out_fmt; int out_rate; int num_formats, num_rates, num_layouts; @@ -257,8 +253,8 @@ int main(int argc, char **argv) for (i = 0; i < num_formats; i++) { in_fmt = formats[i]; for (k = 0; k < num_layouts; k++) { - in_ch_layout = layouts[k]; - in_channels = av_get_channel_layout_nb_channels(in_ch_layout); + AVChannelLayout in_ch_layout = layouts[k]; + int in_channels = in_ch_layout.nb_channels; for (m = 0; m < num_rates; m++) { in_rate = rates[m]; @@ -274,8 +270,8 @@ int main(int argc, char **argv) for (j = 0; j < num_formats; j++) { out_fmt = formats[j]; for (l = 0; l < num_layouts; l++) { - out_ch_layout = layouts[l]; - out_channels = av_get_channel_layout_nb_channels(out_ch_layout); + AVChannelLayout out_ch_layout = layouts[l]; + int out_channels = out_ch_layout.nb_channels; for (n = 0; n < num_rates; n++) { out_rate = rates[n]; @@ -291,10 +287,10 @@ int main(int argc, char **argv) goto end; } - av_opt_set_int(s, "in_channel_layout", in_ch_layout, 0); + av_opt_set_channel_layout(s, "in_ch_layout", &in_ch_layout, 0); + av_opt_set_channel_layout(s, "out_ch_layout", &out_ch_layout, 0); av_opt_set_int(s, "in_sample_fmt", in_fmt, 0); av_opt_set_int(s, "in_sample_rate", in_rate, 0); - av_opt_set_int(s, "out_channel_layout", out_ch_layout, 0); av_opt_set_int(s, "out_sample_fmt", out_fmt, 0); av_opt_set_int(s, "out_sample_rate", out_rate, 0); diff --git a/libavresample/utils.c b/libavresample/utils.c index bab2153b4b..15c827efbe 100644 --- a/libavresample/utils.c +++ b/libavresample/utils.c @@ -18,6 +18,7 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ +#include "libavutil/channel_layout.h" #include "libavutil/common.h" #include "libavutil/dict.h" #include "libavutil/error.h" @@ -35,6 +36,7 @@ int avresample_open(AVAudioResampleContext *avr) { + int in_ch, out_ch; int ret; if (avresample_is_open(avr)) { @@ -42,24 +44,67 @@ int avresample_open(AVAudioResampleContext *avr) return AVERROR(EINVAL); } + if ( avr->in_ch_layout.order == AV_CHANNEL_ORDER_CUSTOM || + avr->out_ch_layout.order == AV_CHANNEL_ORDER_CUSTOM) { + av_log(avr, AV_LOG_ERROR, + "Resampling a custom channel layout order is not supported.\n"); + return AVERROR(ENOSYS); + } + + if (avr->in_ch_layout.order == AV_CHANNEL_ORDER_UNSPEC) { + if (avr->in_ch_layout.nb_channels > 63) { + av_log(avr, AV_LOG_ERROR, + "Unspecified channel layout order is supported only for up " + "to 63 channels (got %d).\n", avr->in_ch_layout.nb_channels); + return AVERROR(ENOSYS); + } + av_channel_layout_default(&avr->in_ch_layout, avr->in_ch_layout.nb_channels); + } + if (avr->out_ch_layout.order == AV_CHANNEL_ORDER_UNSPEC) { + if (avr->out_ch_layout.nb_channels > 63) { + av_log(avr, AV_LOG_ERROR, + "Unspecified channel layout order is supported only for up " + "to 63 channels (got %d).\n", avr->out_ch_layout.nb_channels); + return AVERROR(ENOSYS); + } + av_channel_layout_default(&avr->out_ch_layout, avr->out_ch_layout.nb_channels); + } + /* set channel mixing parameters */ - avr->in_channels = av_get_channel_layout_nb_channels(avr->in_channel_layout); - if (avr->in_channels <= 0 || avr->in_channels > AVRESAMPLE_MAX_CHANNELS) { - av_log(avr, AV_LOG_ERROR, "Invalid input channel layout: %"PRIu64"\n", - avr->in_channel_layout); +#if FF_API_OLD_CHANNEL_LAYOUT + if (avr->in_channel_layout) { + av_log(avr, AV_LOG_WARNING, "Setting the input channel layout with the " + "'in_channel_layout' option is deprecated, use the " + "'in_ch_layout' option instead].\n"); + av_channel_layout_uninit(&avr->in_ch_layout); + av_channel_layout_from_mask(&avr->in_ch_layout, avr->in_channel_layout); + } + if (avr->out_channel_layout) { + av_log(avr, AV_LOG_WARNING, "Setting the output channel layout with the " + "'out_channel_layout' option is deprecated, use the " + "'out_ch_layout' option instead].\n"); + av_channel_layout_uninit(&avr->out_ch_layout); + av_channel_layout_from_mask(&avr->out_ch_layout, avr->out_channel_layout); + } +#endif + in_ch = avr->in_ch_layout.nb_channels; + out_ch = avr->out_ch_layout.nb_channels; + if (!av_channel_layout_check(&avr->in_ch_layout) || + in_ch > AVRESAMPLE_MAX_CHANNELS) { + av_log(avr, AV_LOG_ERROR, "Invalid input channel layout.\n"); return AVERROR(EINVAL); } - avr->out_channels = av_get_channel_layout_nb_channels(avr->out_channel_layout); - if (avr->out_channels <= 0 || avr->out_channels > AVRESAMPLE_MAX_CHANNELS) { - av_log(avr, AV_LOG_ERROR, "Invalid output channel layout: %"PRIu64"\n", - avr->out_channel_layout); + if (!av_channel_layout_check(&avr->out_ch_layout) || + out_ch > AVRESAMPLE_MAX_CHANNELS) { + av_log(avr, AV_LOG_ERROR, "Invalid output channel layout.\n"); return AVERROR(EINVAL); } - avr->resample_channels = FFMIN(avr->in_channels, avr->out_channels); - avr->downmix_needed = avr->in_channels > avr->out_channels; - avr->upmix_needed = avr->out_channels > avr->in_channels || + avr->resample_channels = FFMIN(in_ch, out_ch); + avr->downmix_needed = in_ch > out_ch; + avr->upmix_needed = out_ch > in_ch || (!avr->downmix_needed && (avr->mix_matrix || - avr->in_channel_layout != avr->out_channel_layout)); + av_channel_layout_compare(&avr->in_ch_layout, + &avr->out_ch_layout))); avr->mixing_needed = avr->downmix_needed || avr->upmix_needed; /* set resampling parameters */ @@ -105,7 +150,7 @@ int avresample_open(AVAudioResampleContext *avr) /* 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 && - !ff_sample_fmt_is_planar(avr->out_sample_fmt, avr->out_channels)) { + !ff_sample_fmt_is_planar(avr->out_sample_fmt, avr->out_ch_layout.nb_channels)) { avr->internal_sample_fmt = av_get_planar_sample_fmt(avr->out_sample_fmt); } @@ -114,7 +159,8 @@ int avresample_open(AVAudioResampleContext *avr) avr->in_convert_needed = avr->in_sample_fmt != avr->internal_sample_fmt; else avr->in_convert_needed = avr->use_channel_map && - !ff_sample_fmt_is_planar(avr->out_sample_fmt, avr->out_channels); + !ff_sample_fmt_is_planar(avr->out_sample_fmt, + avr->out_ch_layout.nb_channels); if (avr->resample_needed || avr->mixing_needed || avr->in_convert_needed) avr->out_convert_needed = avr->internal_sample_fmt != avr->out_sample_fmt; @@ -144,27 +190,27 @@ int avresample_open(AVAudioResampleContext *avr) int ch; av_log(avr, AV_LOG_TRACE, "output map: "); if (avr->ch_map_info.do_remap) - for (ch = 0; ch < avr->in_channels; ch++) + for (ch = 0; ch < in_ch; ch++) av_log(avr, AV_LOG_TRACE, " % 2d", avr->ch_map_info.channel_map[ch]); else av_log(avr, AV_LOG_TRACE, "n/a"); av_log(avr, AV_LOG_TRACE, "\n"); av_log(avr, AV_LOG_TRACE, "copy map: "); if (avr->ch_map_info.do_copy) - for (ch = 0; ch < avr->in_channels; ch++) + for (ch = 0; ch < in_ch; ch++) av_log(avr, AV_LOG_TRACE, " % 2d", avr->ch_map_info.channel_copy[ch]); else av_log(avr, AV_LOG_TRACE, "n/a"); av_log(avr, AV_LOG_TRACE, "\n"); av_log(avr, AV_LOG_TRACE, "zero map: "); if (avr->ch_map_info.do_zero) - for (ch = 0; ch < avr->in_channels; ch++) + for (ch = 0; ch < in_ch; ch++) av_log(avr, AV_LOG_TRACE, " % 2d", avr->ch_map_info.channel_zero[ch]); else av_log(avr, AV_LOG_TRACE, "n/a"); av_log(avr, AV_LOG_TRACE, "\n"); av_log(avr, AV_LOG_TRACE, "input map: "); - for (ch = 0; ch < avr->in_channels; ch++) + for (ch = 0; ch < in_ch; ch++) av_log(avr, AV_LOG_TRACE, " % 2d", avr->ch_map_info.input_map[ch]); av_log(avr, AV_LOG_TRACE, "\n"); } @@ -174,7 +220,7 @@ int avresample_open(AVAudioResampleContext *avr) /* allocate buffers */ if (avr->in_copy_needed || avr->in_convert_needed) { - avr->in_buffer = ff_audio_data_alloc(FFMAX(avr->in_channels, avr->out_channels), + avr->in_buffer = ff_audio_data_alloc(FFMAX(in_ch, out_ch), 0, avr->internal_sample_fmt, "in_buffer"); if (!avr->in_buffer) { @@ -183,7 +229,7 @@ int avresample_open(AVAudioResampleContext *avr) } } if (avr->resample_needed) { - avr->resample_out_buffer = ff_audio_data_alloc(avr->out_channels, + avr->resample_out_buffer = ff_audio_data_alloc(out_ch, 1024, avr->internal_sample_fmt, "resample_out_buffer"); if (!avr->resample_out_buffer) { @@ -192,15 +238,14 @@ int avresample_open(AVAudioResampleContext *avr) } } if (avr->out_convert_needed) { - avr->out_buffer = ff_audio_data_alloc(avr->out_channels, 0, + avr->out_buffer = ff_audio_data_alloc(out_ch, 0, avr->out_sample_fmt, "out_buffer"); if (!avr->out_buffer) { ret = AVERROR(EINVAL); goto error; } } - avr->out_fifo = av_audio_fifo_alloc(avr->out_sample_fmt, avr->out_channels, - 1024); + avr->out_fifo = av_audio_fifo_alloc(avr->out_sample_fmt, out_ch, 1024); if (!avr->out_fifo) { ret = AVERROR(ENOMEM); goto error; @@ -209,7 +254,7 @@ int avresample_open(AVAudioResampleContext *avr) /* setup contexts */ 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_fmt, in_ch, avr->in_sample_rate, avr->remap_point == REMAP_IN_CONVERT); if (!avr->ac_in) { @@ -224,7 +269,7 @@ int avresample_open(AVAudioResampleContext *avr) else src_fmt = avr->in_sample_fmt; avr->ac_out = ff_audio_convert_alloc(avr, avr->out_sample_fmt, src_fmt, - avr->out_channels, + out_ch, avr->out_sample_rate, avr->remap_point == REMAP_OUT_CONVERT); if (!avr->ac_out) { @@ -361,7 +406,7 @@ int attribute_align_arg avresample_convert(AVAudioResampleContext *avr, direct_output = output && av_audio_fifo_size(avr->out_fifo) == 0; if (output) { ret = ff_audio_data_init(&output_buffer, output, out_plane_size, - avr->out_channels, out_samples, + avr->out_ch_layout.nb_channels, out_samples, avr->out_sample_fmt, 0, "output"); if (ret < 0) return ret; @@ -371,7 +416,7 @@ int attribute_align_arg avresample_convert(AVAudioResampleContext *avr, if (input) { /* initialize input_buffer with input data */ ret = ff_audio_data_init(&input_buffer, input, in_plane_size, - avr->in_channels, in_samples, + avr->in_ch_layout.nb_channels, in_samples, avr->in_sample_fmt, 1, "input"); if (ret < 0) return ret; @@ -420,7 +465,7 @@ int attribute_align_arg avresample_convert(AVAudioResampleContext *avr, if (ret < 0) return ret; } - ff_audio_data_set_channels(avr->in_buffer, avr->in_channels); + ff_audio_data_set_channels(avr->in_buffer, avr->in_ch_layout.nb_channels); if (avr->downmix_needed) { av_log(avr, AV_LOG_TRACE, "[downmix] in_buffer\n"); ret = ff_audio_mix(avr->am, avr->in_buffer); @@ -504,18 +549,24 @@ int attribute_align_arg avresample_convert(AVAudioResampleContext *avr, int avresample_config(AVAudioResampleContext *avr, AVFrame *out, AVFrame *in) { + int ret; + if (avresample_is_open(avr)) { avresample_close(avr); } if (in) { - avr->in_channel_layout = in->channel_layout; + ret = av_channel_layout_copy(&avr->in_ch_layout, &in->ch_layout); + if (ret < 0) + return ret; avr->in_sample_rate = in->sample_rate; avr->in_sample_fmt = in->format; } if (out) { - avr->out_channel_layout = out->channel_layout; + ret = av_channel_layout_copy(&avr->out_ch_layout, &out->ch_layout); + if (ret < 0) + return ret; avr->out_sample_rate = out->sample_rate; avr->out_sample_fmt = out->format; } @@ -529,7 +580,7 @@ static int config_changed(AVAudioResampleContext *avr, int ret = 0; if (in) { - if (avr->in_channel_layout != in->channel_layout || + if (av_channel_layout_compare(&avr->in_ch_layout, &in->ch_layout) || avr->in_sample_rate != in->sample_rate || avr->in_sample_fmt != in->format) { ret |= AVERROR_INPUT_CHANGED; @@ -537,7 +588,7 @@ static int config_changed(AVAudioResampleContext *avr, } if (out) { - if (avr->out_channel_layout != out->channel_layout || + if (av_channel_layout_compare(&avr->out_ch_layout, &out->ch_layout) || avr->out_sample_rate != out->sample_rate || avr->out_sample_fmt != out->format) { ret |= AVERROR_OUTPUT_CHANGED; @@ -595,7 +646,7 @@ static inline int available_samples(AVFrame *out) if (av_sample_fmt_is_planar(out->format)) { return samples; } else { - int channels = av_get_channel_layout_nb_channels(out->channel_layout); + int channels = out->ch_layout.nb_channels; return samples / channels; } } @@ -642,8 +693,8 @@ int avresample_get_matrix(AVAudioResampleContext *avr, double *matrix, if (avr->am) return ff_audio_mix_get_matrix(avr->am, matrix, stride); - in_channels = av_get_channel_layout_nb_channels(avr->in_channel_layout); - out_channels = av_get_channel_layout_nb_channels(avr->out_channel_layout); + in_channels = avr->in_ch_layout.nb_channels; + out_channels = avr->out_ch_layout.nb_channels; if ( in_channels <= 0 || in_channels > AVRESAMPLE_MAX_CHANNELS || out_channels <= 0 || out_channels > AVRESAMPLE_MAX_CHANNELS) { @@ -671,8 +722,8 @@ int avresample_set_matrix(AVAudioResampleContext *avr, const double *matrix, if (avr->am) return ff_audio_mix_set_matrix(avr->am, matrix, stride); - in_channels = av_get_channel_layout_nb_channels(avr->in_channel_layout); - out_channels = av_get_channel_layout_nb_channels(avr->out_channel_layout); + in_channels = avr->in_ch_layout.nb_channels; + out_channels = avr->out_ch_layout.nb_channels; if ( in_channels <= 0 || in_channels > AVRESAMPLE_MAX_CHANNELS || out_channels <= 0 || out_channels > AVRESAMPLE_MAX_CHANNELS) { @@ -700,7 +751,7 @@ int avresample_set_channel_mapping(AVAudioResampleContext *avr, ChannelMapInfo *info = &avr->ch_map_info; int in_channels, ch, i; - in_channels = av_get_channel_layout_nb_channels(avr->in_channel_layout); + in_channels = avr->in_ch_layout.nb_channels; if (in_channels <= 0 || in_channels > AVRESAMPLE_MAX_CHANNELS) { av_log(avr, AV_LOG_ERROR, "Invalid input channel layout\n"); return AVERROR(EINVAL); -- 2.13.1 _______________________________________________ libav-devel mailing list libav-devel@libav.org https://lists.libav.org/mailman/listinfo/libav-devel