* commit '14758e3211d34a97c42b07acae117ce5627d7f57': lavr: temporarily store custom matrix in AVAudioResampleContext lavr: clarify documentation for avresample_get/set_matrix() Merged-by: Michael Niedermayer <michaelni@gmx.at>tags/n1.1
| @@ -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; | |||
| } | |||
| @@ -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); | |||
| @@ -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; | |||
| } | |||
| @@ -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() | |||
| @@ -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 */ | |||
| @@ -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; | |||
| } | |||
| @@ -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); | |||