diff --git a/libavresample/audio_mix.c b/libavresample/audio_mix.c index dd2f33d27f..3b39ceeca5 100644 --- a/libavresample/audio_mix.c +++ b/libavresample/audio_mix.c @@ -302,27 +302,37 @@ static int mix_function_init(AudioMix *am) return 0; } -int ff_audio_mix_init(AVAudioResampleContext *avr) +AudioMix *ff_audio_mix_alloc(AVAudioResampleContext *avr) { + AudioMix *am; int ret; + am = av_mallocz(sizeof(*am)); + if (!am) + return NULL; + am->avr = avr; + if (avr->internal_sample_fmt != AV_SAMPLE_FMT_S16P && avr->internal_sample_fmt != AV_SAMPLE_FMT_FLTP) { av_log(avr, AV_LOG_ERROR, "Unsupported internal format for " "mixing: %s\n", av_get_sample_fmt_name(avr->internal_sample_fmt)); - return AVERROR(EINVAL); + goto error; } + 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; + /* build matrix if the user did not already set one */ - if (avr->am->matrix) { - if (avr->am->coeff_type != avr->mix_coeff_type || - avr->am->in_layout != avr->in_channel_layout || - avr->am->out_layout != avr->out_channel_layout) { - av_log(avr, AV_LOG_ERROR, - "Custom matrix does not match current parameters\n"); - return AVERROR(EINVAL); - } + if (avr->mix_matrix) { + ret = ff_audio_mix_set_matrix(am, avr->mix_matrix, avr->in_channels); + if (ret < 0) + goto error; + av_freep(&avr->mix_matrix); } else { int i, j; char in_layout_name[128]; @@ -330,7 +340,7 @@ int ff_audio_mix_init(AVAudioResampleContext *avr) double *matrix_dbl = av_mallocz(avr->out_channels * avr->in_channels * sizeof(*matrix_dbl)); if (!matrix_dbl) - return AVERROR(ENOMEM); + goto error; ret = avresample_build_matrix(avr->in_channel_layout, avr->out_channel_layout, @@ -343,7 +353,7 @@ int ff_audio_mix_init(AVAudioResampleContext *avr) avr->matrix_encoding); if (ret < 0) { av_free(matrix_dbl); - return ret; + goto error; } av_get_channel_layout_string(in_layout_name, sizeof(in_layout_name), @@ -360,32 +370,33 @@ int ff_audio_mix_init(AVAudioResampleContext *avr) av_log(avr, AV_LOG_DEBUG, "\n"); } - ret = avresample_set_matrix(avr, matrix_dbl, avr->in_channels); + ret = ff_audio_mix_set_matrix(am, matrix_dbl, avr->in_channels); if (ret < 0) { av_free(matrix_dbl); - return ret; + goto error; } av_free(matrix_dbl); } - avr->am->fmt = avr->internal_sample_fmt; - avr->am->coeff_type = avr->mix_coeff_type; - avr->am->in_layout = avr->in_channel_layout; - avr->am->out_layout = avr->out_channel_layout; - avr->am->in_channels = avr->in_channels; - avr->am->out_channels = avr->out_channels; - - ret = mix_function_init(avr->am); + ret = mix_function_init(am); if (ret < 0) - return ret; + goto error; - return 0; + return am; + +error: + av_free(am); + return NULL; } -void ff_audio_mix_close(AudioMix *am) +void ff_audio_mix_free(AudioMix **am_p) { - if (!am) + AudioMix *am; + + if (!*am_p) return; + am = *am_p; + if (am->matrix) { av_free(am->matrix[0]); am->matrix = NULL; @@ -393,6 +404,8 @@ void ff_audio_mix_close(AudioMix *am) memset(am->matrix_q8, 0, sizeof(am->matrix_q8 )); memset(am->matrix_q15, 0, sizeof(am->matrix_q15)); memset(am->matrix_flt, 0, sizeof(am->matrix_flt)); + + av_freep(am_p); } int ff_audio_mix(AudioMix *am, AudioData *src) @@ -424,3 +437,92 @@ int ff_audio_mix(AudioMix *am, AudioData *src) return 0; } + +int ff_audio_mix_get_matrix(AudioMix *am, double *matrix, int stride) +{ + int i, o; + + if ( am->in_channels <= 0 || am->in_channels > AVRESAMPLE_MAX_CHANNELS || + am->out_channels <= 0 || am->out_channels > AVRESAMPLE_MAX_CHANNELS) { + av_log(am, AV_LOG_ERROR, "Invalid channel counts\n"); + return AVERROR(EINVAL); + } + +#define GET_MATRIX_CONVERT(suffix, scale) \ + if (!am->matrix_ ## suffix[0]) { \ + av_log(am, AV_LOG_ERROR, "matrix is not set\n"); \ + return AVERROR(EINVAL); \ + } \ + for (o = 0; o < am->out_channels; o++) \ + for (i = 0; i < am->in_channels; i++) \ + matrix[o * stride + i] = am->matrix_ ## suffix[o][i] * (scale); + + switch (am->coeff_type) { + case AV_MIX_COEFF_TYPE_Q8: + GET_MATRIX_CONVERT(q8, 1.0 / 256.0); + break; + case AV_MIX_COEFF_TYPE_Q15: + GET_MATRIX_CONVERT(q15, 1.0 / 32768.0); + break; + case AV_MIX_COEFF_TYPE_FLT: + GET_MATRIX_CONVERT(flt, 1.0); + break; + default: + av_log(am, AV_LOG_ERROR, "Invalid mix coeff type\n"); + return AVERROR(EINVAL); + } + + return 0; +} + +int ff_audio_mix_set_matrix(AudioMix *am, const double *matrix, int stride) +{ + int i, o; + + if ( am->in_channels <= 0 || am->in_channels > AVRESAMPLE_MAX_CHANNELS || + am->out_channels <= 0 || am->out_channels > AVRESAMPLE_MAX_CHANNELS) { + av_log(am, AV_LOG_ERROR, "Invalid channel counts\n"); + return AVERROR(EINVAL); + } + + if (am->matrix) { + av_free(am->matrix[0]); + am->matrix = NULL; + } + +#define CONVERT_MATRIX(type, expr) \ + am->matrix_## type[0] = av_mallocz(am->out_channels * am->in_channels * \ + sizeof(*am->matrix_## type[0])); \ + if (!am->matrix_## type[0]) \ + return AVERROR(ENOMEM); \ + for (o = 0; o < am->out_channels; o++) { \ + if (o > 0) \ + am->matrix_## type[o] = am->matrix_## type[o - 1] + \ + am->in_channels; \ + for (i = 0; i < am->in_channels; i++) { \ + double v = matrix[o * stride + i]; \ + am->matrix_## type[o][i] = expr; \ + } \ + } \ + am->matrix = (void **)am->matrix_## type; + + switch (am->coeff_type) { + case AV_MIX_COEFF_TYPE_Q8: + CONVERT_MATRIX(q8, av_clip_int16(lrint(256.0 * v))) + break; + case AV_MIX_COEFF_TYPE_Q15: + CONVERT_MATRIX(q15, av_clipl_int32(llrint(32768.0 * v))) + break; + case AV_MIX_COEFF_TYPE_FLT: + CONVERT_MATRIX(flt, v) + break; + default: + av_log(am, AV_LOG_ERROR, "Invalid mix coeff type\n"); + return AVERROR(EINVAL); + } + + /* TODO: detect situations where we can just swap around pointers + instead of doing matrix multiplications with 0.0 and 1.0 */ + + return 0; +} diff --git a/libavresample/audio_mix.h b/libavresample/audio_mix.h index 2199fffe0e..22faea7c1b 100644 --- a/libavresample/audio_mix.h +++ b/libavresample/audio_mix.h @@ -79,28 +79,36 @@ void ff_audio_mix_set_func(AudioMix *am, enum AVSampleFormat fmt, const char *descr, void *mix_func); /** - * Initialize the AudioMix context in the AVAudioResampleContext. + * Allocate and initialize an AudioMix context. * * The parameters in the AVAudioResampleContext are used to initialize the - * AudioMix context and set the mixing matrix. + * AudioMix context. * * @param avr AVAudioResampleContext - * @return 0 on success, negative AVERROR code on failure + * @return newly-allocated AudioMix context. */ -int ff_audio_mix_init(AVAudioResampleContext *avr); +AudioMix *ff_audio_mix_alloc(AVAudioResampleContext *avr); /** - * Close an AudioMix context. - * - * This clears and frees the mixing matrix arrays. + * Free an AudioMix context. */ -void ff_audio_mix_close(AudioMix *am); +void ff_audio_mix_free(AudioMix **am); /** * Apply channel mixing to audio data using the current mixing matrix. */ int ff_audio_mix(AudioMix *am, AudioData *src); +/** + * Get the current mixing matrix. + */ +int ff_audio_mix_get_matrix(AudioMix *am, double *matrix, int stride); + +/** + * Set the current mixing matrix. + */ +int ff_audio_mix_set_matrix(AudioMix *am, const double *matrix, int stride); + /* arch-specific initialization functions */ void ff_audio_mix_init_x86(AudioMix *am); diff --git a/libavresample/audio_mix_matrix.c b/libavresample/audio_mix_matrix.c index 01a93367ef..8da1b487a4 100644 --- a/libavresample/audio_mix_matrix.c +++ b/libavresample/audio_mix_matrix.c @@ -287,115 +287,3 @@ int avresample_build_matrix(uint64_t in_layout, uint64_t out_layout, return 0; } - -int avresample_get_matrix(AVAudioResampleContext *avr, double *matrix, - int stride) -{ - int in_channels, out_channels, i, o; - - in_channels = av_get_channel_layout_nb_channels(avr->in_channel_layout); - out_channels = av_get_channel_layout_nb_channels(avr->out_channel_layout); - - if ( in_channels <= 0 || in_channels > AVRESAMPLE_MAX_CHANNELS || - out_channels <= 0 || out_channels > AVRESAMPLE_MAX_CHANNELS) { - av_log(avr, AV_LOG_ERROR, "Invalid channel layouts\n"); - return AVERROR(EINVAL); - } - - switch (avr->mix_coeff_type) { - case AV_MIX_COEFF_TYPE_Q8: - if (!avr->am->matrix_q8[0]) { - av_log(avr, AV_LOG_ERROR, "matrix is not set\n"); - return AVERROR(EINVAL); - } - for (o = 0; o < out_channels; o++) - for (i = 0; i < in_channels; i++) - matrix[o * stride + i] = avr->am->matrix_q8[o][i] / 256.0; - break; - case AV_MIX_COEFF_TYPE_Q15: - if (!avr->am->matrix_q15[0]) { - av_log(avr, AV_LOG_ERROR, "matrix is not set\n"); - return AVERROR(EINVAL); - } - for (o = 0; o < out_channels; o++) - for (i = 0; i < in_channels; i++) - matrix[o * stride + i] = avr->am->matrix_q15[o][i] / 32768.0; - break; - case AV_MIX_COEFF_TYPE_FLT: - if (!avr->am->matrix_flt[0]) { - av_log(avr, AV_LOG_ERROR, "matrix is not set\n"); - return AVERROR(EINVAL); - } - for (o = 0; o < out_channels; o++) - for (i = 0; i < in_channels; i++) - matrix[o * stride + i] = avr->am->matrix_flt[o][i]; - break; - default: - av_log(avr, AV_LOG_ERROR, "Invalid mix coeff type\n"); - return AVERROR(EINVAL); - } - - return 0; -} - -int avresample_set_matrix(AVAudioResampleContext *avr, const double *matrix, - int stride) -{ - int in_channels, out_channels, i, o; - - in_channels = av_get_channel_layout_nb_channels(avr->in_channel_layout); - out_channels = av_get_channel_layout_nb_channels(avr->out_channel_layout); - - if ( in_channels <= 0 || in_channels > AVRESAMPLE_MAX_CHANNELS || - out_channels <= 0 || out_channels > AVRESAMPLE_MAX_CHANNELS) { - av_log(avr, AV_LOG_ERROR, "Invalid channel layouts\n"); - return AVERROR(EINVAL); - } - - if (avr->am->matrix) { - av_free(avr->am->matrix[0]); - avr->am->matrix = NULL; - } - -#define CONVERT_MATRIX(type, expr) \ - avr->am->matrix_## type[0] = av_mallocz(out_channels * in_channels * \ - sizeof(*avr->am->matrix_## type[0])); \ - if (!avr->am->matrix_## type[0]) \ - return AVERROR(ENOMEM); \ - for (o = 0; o < out_channels; o++) { \ - if (o > 0) \ - avr->am->matrix_## type[o] = avr->am->matrix_## type[o - 1] + \ - in_channels; \ - for (i = 0; i < in_channels; i++) { \ - double v = matrix[o * stride + i]; \ - avr->am->matrix_## type[o][i] = expr; \ - } \ - } \ - avr->am->matrix = (void **)avr->am->matrix_## type; - - switch (avr->mix_coeff_type) { - case AV_MIX_COEFF_TYPE_Q8: - CONVERT_MATRIX(q8, av_clip_int16(lrint(256.0 * v))) - break; - case AV_MIX_COEFF_TYPE_Q15: - CONVERT_MATRIX(q15, av_clipl_int32(llrint(32768.0 * v))) - break; - case AV_MIX_COEFF_TYPE_FLT: - CONVERT_MATRIX(flt, v) - break; - default: - av_log(avr, AV_LOG_ERROR, "Invalid mix coeff type\n"); - return AVERROR(EINVAL); - } - - /* TODO: detect situations where we can just swap around pointers - instead of doing matrix multiplications with 0.0 and 1.0 */ - - /* set AudioMix params */ - avr->am->in_layout = avr->in_channel_layout; - avr->am->out_layout = avr->out_channel_layout; - avr->am->in_channels = in_channels; - avr->am->out_channels = out_channels; - - return 0; -} diff --git a/libavresample/avresample.h b/libavresample/avresample.h index affeeeb73b..a73d6865ac 100644 --- a/libavresample/avresample.h +++ b/libavresample/avresample.h @@ -216,6 +216,9 @@ int avresample_build_matrix(uint64_t in_layout, uint64_t out_layout, /** * Get the current channel mixing matrix. * + * If no custom matrix has been previously set or the AVAudioResampleContext is + * not open, an error is returned. + * * @param avr audio resample context * @param matrix mixing coefficients; matrix[i + stride * o] is the weight of * input channel i in output channel o. @@ -231,7 +234,8 @@ int avresample_get_matrix(AVAudioResampleContext *avr, double *matrix, * Allows for setting a custom mixing matrix, overriding the default matrix * generated internally during avresample_open(). This function can be called * anytime on an allocated context, either before or after calling - * avresample_open(). avresample_convert() always uses the current matrix. + * avresample_open(), as long as the channel layouts have been set. + * avresample_convert() always uses the current matrix. * Calling avresample_close() on the context will clear the current matrix. * * @see avresample_close() diff --git a/libavresample/internal.h b/libavresample/internal.h index 006b6fd14a..3fd33fed6a 100644 --- a/libavresample/internal.h +++ b/libavresample/internal.h @@ -74,6 +74,12 @@ struct AVAudioResampleContext { ResampleContext *resample; /**< resampling context */ AudioMix *am; /**< channel mixing context */ enum AVMatrixEncoding matrix_encoding; /**< matrixed stereo encoding */ + + /** + * mix matrix + * only used if avresample_set_matrix() is called before avresample_open() + */ + double *mix_matrix; }; #endif /* AVRESAMPLE_INTERNAL_H */ diff --git a/libavresample/options.c b/libavresample/options.c index 8f643700cb..824f5e3bc3 100644 --- a/libavresample/options.c +++ b/libavresample/options.c @@ -84,13 +84,6 @@ AVAudioResampleContext *avresample_alloc_context(void) avr->av_class = &av_resample_context_class; av_opt_set_defaults(avr); - avr->am = av_mallocz(sizeof(*avr->am)); - if (!avr->am) { - av_free(avr); - return NULL; - } - avr->am->avr = avr; - return avr; } diff --git a/libavresample/utils.c b/libavresample/utils.c index 5591f1575e..c0ddf222ef 100644 --- a/libavresample/utils.c +++ b/libavresample/utils.c @@ -169,9 +169,11 @@ int avresample_open(AVAudioResampleContext *avr) } } if (avr->mixing_needed) { - ret = ff_audio_mix_init(avr); - if (ret < 0) + avr->am = ff_audio_mix_alloc(avr); + if (!avr->am) { + ret = AVERROR(ENOMEM); goto error; + } } return 0; @@ -191,8 +193,8 @@ void avresample_close(AVAudioResampleContext *avr) av_freep(&avr->ac_in); av_freep(&avr->ac_out); ff_audio_resample_free(&avr->resample); - ff_audio_mix_close(avr->am); - return; + ff_audio_mix_free(&avr->am); + av_freep(&avr->mix_matrix); } void avresample_free(AVAudioResampleContext **avr) @@ -200,7 +202,6 @@ void avresample_free(AVAudioResampleContext **avr) if (!*avr) return; avresample_close(*avr); - av_freep(&(*avr)->am); av_opt_free(*avr); av_freep(avr); } @@ -404,6 +405,66 @@ int attribute_align_arg avresample_convert(AVAudioResampleContext *avr, current_buffer); } +int avresample_get_matrix(AVAudioResampleContext *avr, double *matrix, + int stride) +{ + int in_channels, out_channels, i, o; + + 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); + + if ( in_channels <= 0 || in_channels > AVRESAMPLE_MAX_CHANNELS || + out_channels <= 0 || out_channels > AVRESAMPLE_MAX_CHANNELS) { + av_log(avr, AV_LOG_ERROR, "Invalid channel layouts\n"); + return AVERROR(EINVAL); + } + + if (!avr->mix_matrix) { + av_log(avr, AV_LOG_ERROR, "matrix is not set\n"); + return AVERROR(EINVAL); + } + + for (o = 0; o < out_channels; o++) + for (i = 0; i < in_channels; i++) + matrix[o * stride + i] = avr->mix_matrix[o * in_channels + i]; + + return 0; +} + +int avresample_set_matrix(AVAudioResampleContext *avr, const double *matrix, + int stride) +{ + int in_channels, out_channels, i, o; + + 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); + + if ( in_channels <= 0 || in_channels > AVRESAMPLE_MAX_CHANNELS || + out_channels <= 0 || out_channels > AVRESAMPLE_MAX_CHANNELS) { + av_log(avr, AV_LOG_ERROR, "Invalid channel layouts\n"); + return AVERROR(EINVAL); + } + + if (avr->mix_matrix) + av_freep(&avr->mix_matrix); + avr->mix_matrix = av_malloc(in_channels * out_channels * + sizeof(*avr->mix_matrix)); + if (!avr->mix_matrix) + return AVERROR(ENOMEM); + + for (o = 0; o < out_channels; o++) + for (i = 0; i < in_channels; i++) + avr->mix_matrix[o * in_channels + i] = matrix[o * stride + i]; + + return 0; +} + int avresample_available(AVAudioResampleContext *avr) { return av_audio_fifo_size(avr->out_fifo);