From 060ecd16cddc7b106c831df7ab4448e121aab085 Mon Sep 17 00:00:00 2001 From: George Kiagiadakis Date: Thu, 1 Feb 2018 17:00:06 +0200 Subject: [PATCH] libs: audio-converter: complete code to support non-interleaved audio buffers https://bugzilla.gnome.org/show_bug.cgi?id=705986 --- gst-libs/gst/audio/audio-converter.c | 168 ++++++++++++++++++++++++++++++++--- 1 file changed, 156 insertions(+), 12 deletions(-) diff --git a/gst-libs/gst/audio/audio-converter.c b/gst-libs/gst/audio/audio-converter.c index c3bcb82..0812eff 100644 --- a/gst-libs/gst/audio/audio-converter.c +++ b/gst-libs/gst/audio/audio-converter.c @@ -131,6 +131,11 @@ struct _GstAudioConverter /* quant */ GstAudioQuantize *quant; + /* change layout */ + GstAudioFormat chlayout_format; + GstAudioLayout chlayout_target; + gint chlayout_channels; + /* pack */ gboolean out_default; AudioChain *chain_end; /* NULL for empty chain or points to the last element in the chain */ @@ -582,6 +587,110 @@ do_quantize (AudioChain * chain, gpointer user_data) return TRUE; } +#define MAKE_INTERLEAVE_FUNC(type) \ +static inline void \ +interleave_##type (const type * in[], type * out[], \ + gsize num_samples, gint channels) \ +{ \ + gsize s; \ + gint c; \ + for (s = 0; s < num_samples; s++) { \ + for (c = 0; c < channels; c++) { \ + out[0][s * channels + c] = in[c][s]; \ + } \ + } \ +} + +#define MAKE_DEINTERLEAVE_FUNC(type) \ +static inline void \ +deinterleave_##type (const type * in[], type * out[], \ + gsize num_samples, gint channels) \ +{ \ + gsize s; \ + gint c; \ + for (s = 0; s < num_samples; s++) { \ + for (c = 0; c < channels; c++) { \ + out[c][s] = in[0][s * channels + c]; \ + } \ + } \ +} + +MAKE_INTERLEAVE_FUNC (gint16); +MAKE_INTERLEAVE_FUNC (gint32); +MAKE_INTERLEAVE_FUNC (gfloat); +MAKE_INTERLEAVE_FUNC (gdouble); +MAKE_DEINTERLEAVE_FUNC (gint16); +MAKE_DEINTERLEAVE_FUNC (gint32); +MAKE_DEINTERLEAVE_FUNC (gfloat); +MAKE_DEINTERLEAVE_FUNC (gdouble); + +static gboolean +do_change_layout (AudioChain * chain, gpointer user_data) +{ + GstAudioConverter *convert = user_data; + GstAudioFormat format = convert->chlayout_format; + GstAudioLayout out_layout = convert->chlayout_target; + gint channels = convert->chlayout_channels; + gsize num_samples; + gpointer *in, *out; + + in = audio_chain_get_samples (chain->prev, &num_samples); + out = (chain->allow_ip ? in : audio_chain_alloc_samples (chain, num_samples)); + + if (out_layout == GST_AUDIO_LAYOUT_INTERLEAVED) { + /* interleave */ + GST_LOG ("interleaving %p, %p %" G_GSIZE_FORMAT, in, out, num_samples); + switch (format) { + case GST_AUDIO_FORMAT_S16: + interleave_gint16 ((const gint16 **) in, (gint16 **) out, + num_samples, channels); + break; + case GST_AUDIO_FORMAT_S32: + interleave_gint32 ((const gint32 **) in, (gint32 **) out, + num_samples, channels); + break; + case GST_AUDIO_FORMAT_F32: + interleave_gfloat ((const gfloat **) in, (gfloat **) out, + num_samples, channels); + break; + case GST_AUDIO_FORMAT_F64: + interleave_gdouble ((const gdouble **) in, (gdouble **) out, + num_samples, channels); + break; + default: + g_assert_not_reached (); + break; + } + } else { + /* deinterleave */ + GST_LOG ("deinterleaving %p, %p %" G_GSIZE_FORMAT, in, out, num_samples); + switch (format) { + case GST_AUDIO_FORMAT_S16: + deinterleave_gint16 ((const gint16 **) in, (gint16 **) out, + num_samples, channels); + break; + case GST_AUDIO_FORMAT_S32: + deinterleave_gint32 ((const gint32 **) in, (gint32 **) out, + num_samples, channels); + break; + case GST_AUDIO_FORMAT_F32: + deinterleave_gfloat ((const gfloat **) in, (gfloat **) out, + num_samples, channels); + break; + case GST_AUDIO_FORMAT_F64: + deinterleave_gdouble ((const gdouble **) in, (gdouble **) out, + num_samples, channels); + break; + default: + g_assert_not_reached (); + break; + } + } + + audio_chain_set_samples (chain, out, num_samples); + return TRUE; +} + static gboolean is_intermediate_format (GstAudioFormat format) { @@ -719,9 +828,16 @@ chain_mix (GstAudioConverter * convert, AudioChain * prev) GstAudioInfo *out = &convert->out; GstAudioFormat format = convert->current_format; const GValue *opt_matrix = GET_OPT_MIX_MATRIX (convert); + GstAudioChannelMixerFlags flags = 0; convert->current_channels = out->channels; + /* keep the input layout */ + if (convert->current_layout == GST_AUDIO_LAYOUT_NON_INTERLEAVED) { + flags |= GST_AUDIO_CHANNEL_MIXER_FLAGS_NON_INTERLEAVED_IN; + flags |= GST_AUDIO_CHANNEL_MIXER_FLAGS_NON_INTERLEAVED_OUT; + } + if (opt_matrix) { gfloat **matrix = NULL; @@ -730,12 +846,10 @@ chain_mix (GstAudioConverter * convert, AudioChain * prev) mix_matrix_from_g_value (in->channels, out->channels, opt_matrix); convert->mix = - gst_audio_channel_mixer_new_with_matrix (0, format, in->channels, + gst_audio_channel_mixer_new_with_matrix (flags, format, in->channels, out->channels, matrix); } else { - GstAudioChannelMixerFlags flags; - - flags = + flags |= GST_AUDIO_INFO_IS_UNPOSITIONED (in) ? GST_AUDIO_CHANNEL_MIXER_FLAGS_UNPOSITIONED_IN : 0; flags |= @@ -781,8 +895,13 @@ chain_resample (GstAudioConverter * convert, AudioChain * prev) flags = 0; if (convert->current_layout == GST_AUDIO_LAYOUT_NON_INTERLEAVED) { flags |= GST_AUDIO_RESAMPLER_FLAG_NON_INTERLEAVED_IN; + } + /* if the resampler is activated, it is optimal to change layout here */ + if (out->layout == GST_AUDIO_LAYOUT_NON_INTERLEAVED) { flags |= GST_AUDIO_RESAMPLER_FLAG_NON_INTERLEAVED_OUT; } + convert->current_layout = out->layout; + if (variable_rate) flags |= GST_AUDIO_RESAMPLER_FLAG_VARIABLE_RATE; @@ -876,6 +995,29 @@ chain_quantize (GstAudioConverter * convert, AudioChain * prev) } static AudioChain * +chain_change_layout (GstAudioConverter * convert, AudioChain * prev) +{ + GstAudioInfo *out = &convert->out; + + if (convert->current_layout != out->layout) { + convert->current_layout = out->layout; + + /* if there is only 1 channel, layouts are identical */ + if (convert->current_channels > 1) { + convert->chlayout_target = convert->current_layout; + convert->chlayout_format = convert->current_format; + convert->chlayout_channels = convert->current_channels; + + prev = audio_chain_new (prev, convert); + prev->allow_ip = FALSE; + prev->pass_alloc = FALSE; + audio_chain_set_make_func (prev, do_change_layout, convert, NULL); + } + } + return prev; +} + +static AudioChain * chain_pack (GstAudioConverter * convert, AudioChain * prev) { GstAudioInfo *out = &convert->out; @@ -1184,8 +1326,6 @@ gst_audio_converter_new (GstAudioConverterFlags flags, GstAudioInfo * in_info, g_return_val_if_fail (in_info != NULL, FALSE); g_return_val_if_fail (out_info != NULL, FALSE); - g_return_val_if_fail (in_info->layout == GST_AUDIO_LAYOUT_INTERLEAVED, FALSE); - g_return_val_if_fail (in_info->layout == out_info->layout, FALSE); if (config) opt_matrix = @@ -1226,7 +1366,9 @@ gst_audio_converter_new (GstAudioConverterFlags flags, GstAudioInfo * in_info, prev = chain_convert_out (convert, prev); /* step 6, optional quantize */ prev = chain_quantize (convert, prev); - /* step 7, pack */ + /* step 7, change layout */ + prev = chain_change_layout (convert, prev); + /* step 8, pack */ convert->chain_end = chain_pack (convert, prev); convert->convert = converter_generic; @@ -1236,10 +1378,12 @@ gst_audio_converter_new (GstAudioConverterFlags flags, GstAudioInfo * in_info, if (convert->mix_passthrough) { if (out_info->finfo->format == in_info->finfo->format) { if (convert->resampler == NULL) { - GST_INFO - ("same formats, no resampler and passthrough mixing -> passthrough"); - convert->convert = converter_passthrough; - convert->in_place = TRUE; + if (out_info->layout == in_info->layout) { + GST_INFO ("same formats, same layout, no resampler and " + "passthrough mixing -> passthrough"); + convert->convert = converter_passthrough; + convert->in_place = TRUE; + } } else { if (is_intermediate_format (in_info->finfo->format)) { GST_INFO ("same formats, and passthrough mixing -> only resampling"); @@ -1248,7 +1392,7 @@ gst_audio_converter_new (GstAudioConverterFlags flags, GstAudioInfo * in_info, } } else if (GST_AUDIO_FORMAT_IS_ENDIAN_CONVERSION (out_info->finfo, in_info->finfo)) { - if (convert->resampler == NULL) { + if (convert->resampler == NULL && out_info->layout == in_info->layout) { GST_INFO ("no resampler, passthrough mixing -> only endian conversion"); convert->convert = converter_endian; convert->in_place = TRUE; -- 2.7.4