1
0
mirror of https://github.com/FFmpeg/FFmpeg.git synced 2024-12-23 12:43:46 +02:00

Merge commit '14758e3211d34a97c42b07acae117ce5627d7f57'

* commit '14758e3211d34a97c42b07acae117ce5627d7f57':
  lavr: temporarily store custom matrix in AVAudioResampleContext
  lavr: clarify documentation for avresample_get/set_matrix()

Merged-by: Michael Niedermayer <michaelni@gmx.at>
This commit is contained in:
Michael Niedermayer 2012-12-12 10:31:49 +01:00
commit 82c0211213
7 changed files with 221 additions and 159 deletions

View File

@ -302,27 +302,37 @@ static int mix_function_init(AudioMix *am)
return 0; return 0;
} }
int ff_audio_mix_init(AVAudioResampleContext *avr) AudioMix *ff_audio_mix_alloc(AVAudioResampleContext *avr)
{ {
AudioMix *am;
int ret; int ret;
am = av_mallocz(sizeof(*am));
if (!am)
return NULL;
am->avr = avr;
if (avr->internal_sample_fmt != AV_SAMPLE_FMT_S16P && if (avr->internal_sample_fmt != AV_SAMPLE_FMT_S16P &&
avr->internal_sample_fmt != AV_SAMPLE_FMT_FLTP) { avr->internal_sample_fmt != AV_SAMPLE_FMT_FLTP) {
av_log(avr, AV_LOG_ERROR, "Unsupported internal format for " av_log(avr, AV_LOG_ERROR, "Unsupported internal format for "
"mixing: %s\n", "mixing: %s\n",
av_get_sample_fmt_name(avr->internal_sample_fmt)); 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 */ /* build matrix if the user did not already set one */
if (avr->am->matrix) { if (avr->mix_matrix) {
if (avr->am->coeff_type != avr->mix_coeff_type || ret = ff_audio_mix_set_matrix(am, avr->mix_matrix, avr->in_channels);
avr->am->in_layout != avr->in_channel_layout || if (ret < 0)
avr->am->out_layout != avr->out_channel_layout) { goto error;
av_log(avr, AV_LOG_ERROR, av_freep(&avr->mix_matrix);
"Custom matrix does not match current parameters\n");
return AVERROR(EINVAL);
}
} else { } else {
int i, j; int i, j;
char in_layout_name[128]; 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 * double *matrix_dbl = av_mallocz(avr->out_channels * avr->in_channels *
sizeof(*matrix_dbl)); sizeof(*matrix_dbl));
if (!matrix_dbl) if (!matrix_dbl)
return AVERROR(ENOMEM); goto error;
ret = avresample_build_matrix(avr->in_channel_layout, ret = avresample_build_matrix(avr->in_channel_layout,
avr->out_channel_layout, avr->out_channel_layout,
@ -343,7 +353,7 @@ int ff_audio_mix_init(AVAudioResampleContext *avr)
avr->matrix_encoding); avr->matrix_encoding);
if (ret < 0) { if (ret < 0) {
av_free(matrix_dbl); av_free(matrix_dbl);
return ret; goto error;
} }
av_get_channel_layout_string(in_layout_name, sizeof(in_layout_name), 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"); 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) { if (ret < 0) {
av_free(matrix_dbl); av_free(matrix_dbl);
return ret; goto error;
} }
av_free(matrix_dbl); av_free(matrix_dbl);
} }
avr->am->fmt = avr->internal_sample_fmt; ret = mix_function_init(am);
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);
if (ret < 0) 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; return;
am = *am_p;
if (am->matrix) { if (am->matrix) {
av_free(am->matrix[0]); av_free(am->matrix[0]);
am->matrix = NULL; 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_q8, 0, sizeof(am->matrix_q8 ));
memset(am->matrix_q15, 0, sizeof(am->matrix_q15)); memset(am->matrix_q15, 0, sizeof(am->matrix_q15));
memset(am->matrix_flt, 0, sizeof(am->matrix_flt)); memset(am->matrix_flt, 0, sizeof(am->matrix_flt));
av_freep(am_p);
} }
int ff_audio_mix(AudioMix *am, AudioData *src) int ff_audio_mix(AudioMix *am, AudioData *src)
@ -424,3 +437,92 @@ int ff_audio_mix(AudioMix *am, AudioData *src)
return 0; 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;
}

View File

@ -79,28 +79,36 @@ void ff_audio_mix_set_func(AudioMix *am, enum AVSampleFormat fmt,
const char *descr, void *mix_func); 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 * The parameters in the AVAudioResampleContext are used to initialize the
* AudioMix context and set the mixing matrix. * AudioMix context.
* *
* @param avr AVAudioResampleContext * @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. * Free an AudioMix context.
*
* This clears and frees the mixing matrix arrays.
*/ */
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. * Apply channel mixing to audio data using the current mixing matrix.
*/ */
int ff_audio_mix(AudioMix *am, AudioData *src); 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 */ /* arch-specific initialization functions */
void ff_audio_mix_init_x86(AudioMix *am); void ff_audio_mix_init_x86(AudioMix *am);

View File

@ -287,115 +287,3 @@ int avresample_build_matrix(uint64_t in_layout, uint64_t out_layout,
return 0; 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;
}

View File

@ -216,6 +216,9 @@ int avresample_build_matrix(uint64_t in_layout, uint64_t out_layout,
/** /**
* Get the current channel mixing matrix. * 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 avr audio resample context
* @param matrix mixing coefficients; matrix[i + stride * o] is the weight of * @param matrix mixing coefficients; matrix[i + stride * o] is the weight of
* input channel i in output channel o. * 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 * Allows for setting a custom mixing matrix, overriding the default matrix
* generated internally during avresample_open(). This function can be called * generated internally during avresample_open(). This function can be called
* anytime on an allocated context, either before or after calling * 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. * Calling avresample_close() on the context will clear the current matrix.
* *
* @see avresample_close() * @see avresample_close()

View File

@ -74,6 +74,12 @@ struct AVAudioResampleContext {
ResampleContext *resample; /**< resampling context */ ResampleContext *resample; /**< resampling context */
AudioMix *am; /**< channel mixing context */ AudioMix *am; /**< channel mixing context */
enum AVMatrixEncoding matrix_encoding; /**< matrixed stereo encoding */ 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 */ #endif /* AVRESAMPLE_INTERNAL_H */

View File

@ -84,13 +84,6 @@ AVAudioResampleContext *avresample_alloc_context(void)
avr->av_class = &av_resample_context_class; avr->av_class = &av_resample_context_class;
av_opt_set_defaults(avr); 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; return avr;
} }

View File

@ -169,9 +169,11 @@ int avresample_open(AVAudioResampleContext *avr)
} }
} }
if (avr->mixing_needed) { if (avr->mixing_needed) {
ret = ff_audio_mix_init(avr); avr->am = ff_audio_mix_alloc(avr);
if (ret < 0) if (!avr->am) {
ret = AVERROR(ENOMEM);
goto error; goto error;
}
} }
return 0; return 0;
@ -191,8 +193,8 @@ void avresample_close(AVAudioResampleContext *avr)
av_freep(&avr->ac_in); av_freep(&avr->ac_in);
av_freep(&avr->ac_out); av_freep(&avr->ac_out);
ff_audio_resample_free(&avr->resample); ff_audio_resample_free(&avr->resample);
ff_audio_mix_close(avr->am); ff_audio_mix_free(&avr->am);
return; av_freep(&avr->mix_matrix);
} }
void avresample_free(AVAudioResampleContext **avr) void avresample_free(AVAudioResampleContext **avr)
@ -200,7 +202,6 @@ void avresample_free(AVAudioResampleContext **avr)
if (!*avr) if (!*avr)
return; return;
avresample_close(*avr); avresample_close(*avr);
av_freep(&(*avr)->am);
av_opt_free(*avr); av_opt_free(*avr);
av_freep(avr); av_freep(avr);
} }
@ -404,6 +405,66 @@ int attribute_align_arg avresample_convert(AVAudioResampleContext *avr,
current_buffer); 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) int avresample_available(AVAudioResampleContext *avr)
{ {
return av_audio_fifo_size(avr->out_fifo); return av_audio_fifo_size(avr->out_fifo);