audio-converter: simplify API

Remove the consumed/produced output fields from the resampler and
converter. Let the caler specify the right number of input/output
samples so we can be more optimal.
Use just one function to update the converter configuration.
Simplify some things internally.
Make it possible to use writable input as temp space in audioconvert.
This commit is contained in:
Wim Taymans 2016-01-05 16:06:22 +01:00
parent 1d9a793545
commit de37491662
5 changed files with 79 additions and 164 deletions

View file

@ -217,19 +217,16 @@ audio_chain_free (AudioChain * chain)
}
static gpointer *
audio_chain_alloc_samples (AudioChain * chain, gsize num_samples, gsize * avail)
audio_chain_alloc_samples (AudioChain * chain, gsize num_samples)
{
return chain->alloc_func (chain, num_samples, avail, chain->alloc_data);
return chain->alloc_func (chain, num_samples, chain->alloc_data);
}
static void
audio_chain_set_samples (AudioChain * chain, gpointer * samples,
gsize num_samples)
{
if (num_samples == 0)
return;
GST_LOG ("set samples %" G_GSIZE_FORMAT, num_samples);
GST_LOG ("set samples %p %" G_GSIZE_FORMAT, samples, num_samples);
chain->samples = samples;
chain->num_samples = num_samples;
@ -307,7 +304,7 @@ copy_config (GQuark field_id, const GValue * value, gpointer user_data)
*
* Set @in_rate, @out_rate and @config as extra configuration for @convert.
*
* in_rate and @out_rate specify the new sample rates of input and output
* @in_rate and @out_rate specify the new sample rates of input and output
* formats. A value of 0 leaves the sample rate unchanged.
*
* @config can be %NULL, in which case, the current configuration is not
@ -345,6 +342,10 @@ gst_audio_converter_update_config (GstAudioConverter * convert,
gst_structure_free (config);
}
if (convert->resampler)
gst_audio_resampler_update (convert->resampler, in_rate, out_rate,
convert->config);
return TRUE;
}
@ -459,7 +460,6 @@ do_unpack (AudioChain * chain, gpointer user_data)
}
} else {
tmp = convert->in_data;
num_samples = convert->in_samples;
GST_LOG ("get in samples %p", tmp);
}
audio_chain_set_samples (chain, tmp, num_samples);
@ -470,8 +470,8 @@ do_unpack (AudioChain * chain, gpointer user_data)
static gboolean
do_convert_in (AudioChain * chain, gpointer user_data)
{
GstAudioConverter *convert = user_data;
gsize num_samples;
GstAudioConverter *convert = user_data;
gpointer *in, *out;
gint i;
@ -490,8 +490,8 @@ do_convert_in (AudioChain * chain, gpointer user_data)
static gboolean
do_mix (AudioChain * chain, gpointer user_data)
{
GstAudioConverter *convert = user_data;
gsize num_samples;
GstAudioConverter *convert = user_data;
gpointer *in, *out;
in = audio_chain_get_samples (chain->prev, &num_samples);
@ -510,23 +510,19 @@ do_resample (AudioChain * chain, gpointer user_data)
{
GstAudioConverter *convert = user_data;
gpointer *in, *out;
gsize in_frames, out_frames, produced, consumed;
gsize in_frames, out_frames;
in = audio_chain_get_samples (chain->prev, &in_frames);
out_frames = convert->out_samples;
out = (chain->allow_ip ? in : audio_chain_alloc_samples (chain, out_frames));
out_frames =
gst_audio_resampler_get_out_frames (convert->resampler, in_frames);
out =
(chain->allow_ip ? in : audio_chain_alloc_samples (chain, out_frames,
&out_frames));
GST_LOG ("resample %p %p,%" G_GSIZE_FORMAT " %" G_GSIZE_FORMAT " %"
G_GSIZE_FORMAT, in, out, in_frames, out_frames, num_samples);
GST_LOG ("resample %p %p,%" G_GSIZE_FORMAT " %" G_GSIZE_FORMAT, in,
out, in_frames, out_frames);
gst_audio_resampler_resample (convert->resampler, in, in_frames, out,
out_frames, &consumed, &produced);
out_frames);
audio_chain_set_samples (chain, out, produced);
audio_chain_set_samples (chain, out, out_frames);
return TRUE;
}
@ -541,7 +537,7 @@ do_convert_out (AudioChain * chain, gpointer user_data)
in = audio_chain_get_samples (chain->prev, &num_samples);
out = (chain->allow_ip ? in : audio_chain_alloc_samples (chain, num_samples));
GST_LOG ("convert out %p, %p, %" G_GSIZE_FORMAT, in, out, num_samples);
GST_LOG ("convert out %p, %p %" G_GSIZE_FORMAT, in, out, num_samples);
for (i = 0; i < chain->blocks; i++)
convert->convert_out (out[i], in[i], num_samples * chain->inc);
@ -560,7 +556,7 @@ do_quantize (AudioChain * chain, gpointer user_data)
in = audio_chain_get_samples (chain->prev, &num_samples);
out = (chain->allow_ip ? in : audio_chain_alloc_samples (chain, num_samples));
GST_LOG ("quantize %p, %p, %" G_GSIZE_FORMAT, in, out, num_samples);
GST_LOG ("quantize %p, %p %" G_GSIZE_FORMAT, in, out, num_samples);
gst_audio_quantize_samples (convert->quant, in, out, num_samples);
@ -679,7 +675,8 @@ chain_resample (GstAudioConverter * convert, AudioChain * prev)
GstAudioFormat format = convert->current_format;
gint channels = convert->current_channels;
if (in->rate != out->rate) {
if (in->rate != out->rate
|| convert->flags & GST_AUDIO_CONVERTER_FLAG_VARIABLE_RATE) {
method = GET_OPT_RESAMPLER_METHOD (convert);
flags = 0;
@ -885,7 +882,7 @@ converter_generic (GstAudioConverter * convert,
/**
* gst_audio_converter_new: (skip)
* @flags: #GstAudioConverterFlags
* @flags: extra #GstAudioConverterFlags
* @in_info: a source #GstAudioInfo
* @out_info: a destination #GstAudioInfo
* @config: (transfer full): a #GstStructure with configuration options
@ -1062,52 +1059,6 @@ gst_audio_converter_get_max_latency (GstAudioConverter * convert)
return 0;
}
/**
* gst_audio_converter_update_rates:
* @convert: a #GstAudioConverter
* @in_rate: input rate
* @out_rate: output rate
* @options: resampler options
*
* Update the input and output rates, passing @options to the resampler.
*
* Returns: %TRUE on success.
*/
gboolean
gst_audio_converter_update_rates (GstAudioConverter * convert,
gint in_rate, gint out_rate, GstStructure * options)
{
g_return_val_if_fail (convert != NULL, FALSE);
g_return_val_if_fail (in_rate > 0, FALSE);
g_return_val_if_fail (out_rate > 0, FALSE);
convert->in.rate = in_rate;
convert->out.rate = out_rate;
if (options)
gst_structure_free (options);
return TRUE;
}
/**
* gst_audio_converter_get_rates:
* @convert: a #GstAudioConverter
* @in_rate: input rate
* @out_rate: output rate
*
* Get the current input and output rates.
*/
void
gst_audio_converter_get_rates (GstAudioConverter * convert,
gint * in_rate, gint * out_rate)
{
if (in_rate)
*in_rate = convert->in.rate;
if (out_rate)
*out_rate = convert->out.rate;
}
/**
* gst_audio_converter_reset:
* @convert: a #GstAudioConverter
@ -1158,8 +1109,6 @@ gst_audio_converter_samples (GstAudioConverter * convert,
g_return_val_if_fail (convert != NULL, FALSE);
g_return_val_if_fail (out != NULL, FALSE);
in_frames = MIN (in_frames, out_frames);
if (in_frames == 0) {
GST_LOG ("skipping empty buffer");
return TRUE;

View file

@ -103,12 +103,6 @@ gsize gst_audio_converter_get_in_frames (GstAudioConverter *con
gsize gst_audio_converter_get_max_latency (GstAudioConverter *convert);
gboolean gst_audio_converter_update_rates (GstAudioConverter *convert,
gint in_rate, gint out_rate,
GstStructure *options);
void gst_audio_converter_get_rates (GstAudioConverter *convert,
gint *in_rate, gint *out_rate);
gboolean gst_audio_converter_samples (GstAudioConverter * convert,
GstAudioConverterFlags flags,
gpointer in[], gsize in_frames,

View file

@ -39,7 +39,7 @@ typedef struct _Tap
typedef void (*MakeTapsFunc) (GstAudioResampler * resampler, Tap * t, gint j);
typedef void (*ResampleFunc) (GstAudioResampler * resampler, gpointer in[],
gsize in_len, gpointer out[], gsize out_len, gsize * consumed,
gsize * produced, gboolean move);
gboolean move);
typedef void (*DeinterleaveFunc) (GstAudioResampler * resampler,
gpointer * sbuf, gpointer in[], gsize in_frames);
typedef void (*MirrorFunc) (GstAudioResampler * resampler, gpointer * sbuf);
@ -377,8 +377,7 @@ make_taps (GstAudioResampler * resampler, Tap * t, gint j)
#define MAKE_RESAMPLE_FUNC(type) \
static void \
resample_ ##type (GstAudioResampler * resampler, gpointer in[], gsize in_len, \
gpointer out[], gsize out_len, gsize * consumed, gsize * produced, \
gboolean move) \
gpointer out[], gsize out_len, gsize * consumed, gboolean move) \
{ \
gint c, di = 0; \
gint n_taps = resampler->n_taps; \
@ -411,7 +410,6 @@ resample_ ##type (GstAudioResampler * resampler, gpointer in[], gsize in_len,
memmove (ip, &ip[samp_index], (in_len - samp_index) * sizeof(type)); \
} \
*consumed = samp_index - resampler->samp_index; \
*produced = di; \
\
resampler->samp_index = move ? 0 : samp_index; \
resampler->samp_phase = samp_phase; \
@ -425,8 +423,7 @@ MAKE_RESAMPLE_FUNC (gint16);
#define MAKE_RESAMPLE_INTERLEAVED_FUNC(type,channels) \
static void \
resample_interleaved_ ##type##_##channels (GstAudioResampler * resampler, gpointer in[],\
gsize in_len, gpointer out[], gsize out_len, gsize * consumed, gsize * produced, \
gboolean move) \
gsize in_len, gpointer out[], gsize out_len, gsize * consumed, gboolean move) \
{ \
gint di = 0; \
gint n_taps = resampler->n_taps; \
@ -459,7 +456,6 @@ resample_interleaved_ ##type##_##channels (GstAudioResampler * resampler, gpoint
(in_len - samp_index) * sizeof(type) * channels); \
} \
*consumed = samp_index - resampler->samp_index; \
*produced = di; \
\
resampler->samp_index = move ? 0 : samp_index; \
resampler->samp_phase = samp_phase; \
@ -861,6 +857,8 @@ gst_audio_resampler_new (GstAudioResamplerMethod method,
* Update the resampler parameters for @resampler. This function should
* not be called concurrently with any other function on @resampler.
*
* When @in_rate or @out_rate is 0, its value is unchanged.
*
* Returns: %TRUE if the new parameters could be set
*/
gboolean
@ -870,8 +868,11 @@ gst_audio_resampler_update (GstAudioResampler * resampler,
gint gcd;
g_return_val_if_fail (resampler != NULL, FALSE);
g_return_val_if_fail (in_rate != 0, FALSE);
g_return_val_if_fail (out_rate != 0, FALSE);
if (in_rate == 0)
in_rate = resampler->in_rate;
if (out_rate == 0)
out_rate = resampler->out_rate;
gcd = gst_util_greatest_common_divisor (in_rate, out_rate);
in_rate /= gcd;
@ -1034,12 +1035,9 @@ get_sample_bufs (GstAudioResampler * resampler, gsize need)
* @in: input samples
* @in_frames: number of input frames
* @out: output samples
* @out_frames: maximum output frames
* @in_consumed: number of frames consumed
* @out_produced: number of frames produced
* @out_frames: number of output frames
*
* Perform resampling on @in_frames frames in @in and write at most
* @out_frames of frames to @out.
* Perform resampling on @in_frames frames in @in and write @out_frames to @out.
*
* In case the samples are interleaved, @in and @out must point to an
* array with a single element pointing to a block of interleaved samples.
@ -1047,30 +1045,26 @@ get_sample_bufs (GstAudioResampler * resampler, gsize need)
* If non-interleaved samples are used, @in and @out must point to an
* array with pointers to memory blocks, one for each channel.
*
* @in may be %NULL, in which case @in_frames of 0 samples are pushed
* @in may be %NULL, in which case @in_frames of silence samples are pushed
* into the resampler.
*
* The number of frames consumed is returned in @consumed and can be
* less than @in_frames due to latency of the resampler or because
* the number of samples produced equals @out_frames.
*
* The number of frames produced is returned in @produced.
* This function always produces @out_frames of output and consumes @in_frames of
* input. Use gst_audio_resampler_get_out_frames() and
* gst_audio_resampler_get_in_frames() to make sure @in_frames and @out_frames
* are matching and @in and @out point to enough memory.
*/
void
gst_audio_resampler_resample (GstAudioResampler * resampler,
gpointer in[], gsize in_frames, gpointer out[], gsize out_frames,
gsize * in_consumed, gsize * out_produced)
gpointer in[], gsize in_frames, gpointer out[], gsize out_frames)
{
gsize samples_avail;
gsize out2, need;
gsize need, consumed;
gpointer *sbuf;
/* do sample skipping */
if (resampler->skip >= in_frames) {
/* we need tp skip all input */
resampler->skip -= in_frames;
*in_consumed = in_frames;
*out_produced = 0;
return;
}
/* skip the last samples by advancing the sample index */
@ -1090,8 +1084,6 @@ gst_audio_resampler_resample (GstAudioResampler * resampler,
need = resampler->n_taps + resampler->samp_index;
if (samples_avail < need) {
/* not enough samples to start */
*in_consumed = in_frames;
*out_produced = 0;
return;
}
@ -1101,21 +1093,16 @@ gst_audio_resampler_resample (GstAudioResampler * resampler,
resampler->filling = FALSE;
}
/* calculate maximum number of available output samples */
out2 = calc_out (resampler, samples_avail - need);
out_frames = MIN (out2, out_frames);
/* resample all channels */
resampler->resample (resampler, sbuf, samples_avail, out, out_frames,
in_consumed, out_produced, TRUE);
&consumed, TRUE);
GST_LOG ("in %" G_GSIZE_FORMAT ", used %" G_GSIZE_FORMAT ", consumed %"
G_GSIZE_FORMAT ", produced %" G_GSIZE_FORMAT, in_frames, samples_avail,
*in_consumed, *out_produced);
G_GSIZE_FORMAT, in_frames, samples_avail, consumed);
/* update pointers */
if (*in_consumed > 0) {
gssize left = samples_avail - *in_consumed;
if (consumed > 0) {
gssize left = samples_avail - consumed;
if (left > 0) {
/* we consumed part of our samples */
resampler->samples_avail = left;
@ -1124,7 +1111,5 @@ gst_audio_resampler_resample (GstAudioResampler * resampler,
resampler->samples_avail = 0;
resampler->skip = -left;
}
/* we always consume everything */
*in_consumed = in_frames;
}
}

View file

@ -178,8 +178,7 @@ gsize gst_audio_resampler_get_max_latency (GstAudioResampler *res
void gst_audio_resampler_resample (GstAudioResampler * resampler,
gpointer in[], gsize in_frames,
gpointer out[], gsize out_frames,
gsize *in_consumed, gsize *out_produced);
gpointer out[], gsize out_frames);
G_END_DECLS

View file

@ -386,14 +386,16 @@ gst_audio_resample_update_state (GstAudioResample * resample, GstAudioInfo * in,
resample->converter = NULL;
}
if (resample->converter == NULL) {
resample->converter = gst_audio_converter_new (0, in, out, options);
resample->converter =
gst_audio_converter_new (GST_AUDIO_CONVERTER_FLAG_VARIABLE_RATE, in,
out, options);
if (resample->converter == NULL)
goto resampler_failed;
} else if (in && out) {
gboolean ret;
ret =
gst_audio_converter_update_rates (resample->converter, in->rate,
gst_audio_converter_update_config (resample->converter, in->rate,
out->rate, options);
if (!ret)
goto update_failed;
@ -568,8 +570,7 @@ gst_audio_resample_push_drain (GstAudioResample * resample, guint history_len)
GstBuffer *outbuf;
GstFlowReturn res;
gint outsize;
gsize in_processed;
gsize out_len, out_processed;
gsize out_len;
GstMapInfo map;
gpointer out[1];
@ -591,11 +592,8 @@ gst_audio_resample_push_drain (GstAudioResample * resample, guint history_len)
out[0] = map.data;
gst_audio_converter_samples (resample->converter, 0, NULL, history_len,
out, out_len, &in_processed, &out_processed);
out, out_len);
/* If we wrote more than allocated something is really wrong now
* and we should better abort immediately */
g_assert (out_len == out_processed);
gst_buffer_unmap (outbuf, &map);
/* time */
@ -604,7 +602,7 @@ gst_audio_resample_push_drain (GstAudioResample * resample, guint history_len)
gst_util_uint64_scale_int_round (resample->samples_out, GST_SECOND,
resample->out.rate);
GST_BUFFER_DURATION (outbuf) = resample->t0 +
gst_util_uint64_scale_int_round (resample->samples_out + out_processed,
gst_util_uint64_scale_int_round (resample->samples_out + out_len,
GST_SECOND, resample->out.rate) - GST_BUFFER_TIMESTAMP (outbuf);
} else {
GST_BUFFER_TIMESTAMP (outbuf) = GST_CLOCK_TIME_NONE;
@ -613,13 +611,13 @@ gst_audio_resample_push_drain (GstAudioResample * resample, guint history_len)
/* offset */
if (resample->out_offset0 != GST_BUFFER_OFFSET_NONE) {
GST_BUFFER_OFFSET (outbuf) = resample->out_offset0 + resample->samples_out;
GST_BUFFER_OFFSET_END (outbuf) = GST_BUFFER_OFFSET (outbuf) + out_processed;
GST_BUFFER_OFFSET_END (outbuf) = GST_BUFFER_OFFSET (outbuf) + out_len;
} else {
GST_BUFFER_OFFSET (outbuf) = GST_BUFFER_OFFSET_NONE;
GST_BUFFER_OFFSET_END (outbuf) = GST_BUFFER_OFFSET_NONE;
}
/* move along */
resample->samples_out += out_processed;
resample->samples_out += out_len;
resample->samples_in += history_len;
GST_LOG_OBJECT (resample,
@ -742,20 +740,23 @@ gst_audio_resample_process (GstAudioResample * resample, GstBuffer * inbuf,
{
GstMapInfo in_map, out_map;
gsize outsize;
guint32 in_len, in_processed;
guint32 out_len, out_processed;
guint32 in_len;
guint32 out_len;
guint filt_len =
gst_audio_converter_get_max_latency (resample->converter) * 2;
gboolean inbuf_writable;
gst_buffer_map (inbuf, &in_map, GST_MAP_READ);
inbuf_writable = gst_buffer_is_writable (inbuf)
&& gst_buffer_n_memory (inbuf) == 1
&& gst_memory_is_writable (gst_buffer_peek_memory (inbuf, 0));
gst_buffer_map (inbuf, &in_map,
inbuf_writable ? GST_MAP_READWRITE : GST_MAP_READ);
gst_buffer_map (outbuf, &out_map, GST_MAP_WRITE);
in_len = in_map.size / resample->in.bpf;
out_len = out_map.size / resample->out.bpf;
in_processed = in_len;
out_processed = out_len;
if (GST_BUFFER_FLAG_IS_SET (inbuf, GST_BUFFER_FLAG_GAP)) {
resample->num_nongap_samples = 0;
if (resample->num_gap_samples < filt_len) {
@ -777,16 +778,15 @@ gst_audio_resample_process (GstAudioResample * resample, GstBuffer * inbuf,
den = resample->out.rate;
if (resample->samples_in + in_len >= filt_len / 2)
out_processed =
out_len =
gst_util_uint64_scale_int_ceil (resample->samples_in + in_len -
filt_len / 2, den, num) - resample->samples_out;
else
out_processed = 0;
out_len = 0;
memset (out_map.data, 0, out_map.size);
GST_BUFFER_FLAG_SET (outbuf, GST_BUFFER_FLAG_GAP);
resample->num_gap_samples += in_len;
in_processed = in_len;
}
} else { /* not a gap */
if (resample->num_gap_samples > filt_len) {
@ -807,43 +807,31 @@ gst_audio_resample_process (GstAudioResample * resample, GstBuffer * inbuf,
{
/* process */
{
gsize in_proc, out_proc, out_test;
gpointer in[1], out[1];
GstAudioConverterFlags flags;
out_test =
out_len =
gst_audio_converter_get_out_frames (resample->converter, in_len);
out_test = MIN (out_test, out_len);
flags = 0;
if (inbuf_writable)
flags |= GST_AUDIO_CONVERTER_FLAG_IN_WRITABLE;
in[0] = in_map.data;
out[0] = out_map.data;
gst_audio_converter_samples (resample->converter, 0, in, in_len,
out, out_len, &in_proc, &out_proc);
in_processed = in_proc;
out_processed = out_proc;
//g_printerr ("in %d, test %d, %d, real %d (%d)\n", (gint) in_len, (gint) out_test, (gint) out_len, (gint) out_proc, (gint) (out_test - out_proc));
g_assert (out_test == out_proc);
gst_audio_converter_samples (resample->converter, flags, in, in_len,
out, out_len);
}
}
}
/* If we wrote more than allocated something is really wrong now and we
* should better abort immediately */
g_assert (out_len >= out_processed);
if (G_UNLIKELY (in_len != in_processed)) {
GST_WARNING_OBJECT (resample, "converted %d of %d input samples",
in_processed, in_len);
}
/* time */
if (GST_CLOCK_TIME_IS_VALID (resample->t0)) {
GST_BUFFER_TIMESTAMP (outbuf) = resample->t0 +
gst_util_uint64_scale_int_round (resample->samples_out, GST_SECOND,
resample->out.rate);
GST_BUFFER_DURATION (outbuf) = resample->t0 +
gst_util_uint64_scale_int_round (resample->samples_out + out_processed,
gst_util_uint64_scale_int_round (resample->samples_out + out_len,
GST_SECOND, resample->out.rate) - GST_BUFFER_TIMESTAMP (outbuf);
} else {
GST_BUFFER_TIMESTAMP (outbuf) = GST_CLOCK_TIME_NONE;
@ -852,26 +840,26 @@ gst_audio_resample_process (GstAudioResample * resample, GstBuffer * inbuf,
/* offset */
if (resample->out_offset0 != GST_BUFFER_OFFSET_NONE) {
GST_BUFFER_OFFSET (outbuf) = resample->out_offset0 + resample->samples_out;
GST_BUFFER_OFFSET_END (outbuf) = GST_BUFFER_OFFSET (outbuf) + out_processed;
GST_BUFFER_OFFSET_END (outbuf) = GST_BUFFER_OFFSET (outbuf) + out_len;
} else {
GST_BUFFER_OFFSET (outbuf) = GST_BUFFER_OFFSET_NONE;
GST_BUFFER_OFFSET_END (outbuf) = GST_BUFFER_OFFSET_NONE;
}
/* move along */
resample->samples_out += out_processed;
resample->samples_out += out_len;
resample->samples_in += in_len;
gst_buffer_unmap (inbuf, &in_map);
gst_buffer_unmap (outbuf, &out_map);
outsize = out_processed * resample->in.bpf;
outsize = out_len * resample->in.bpf;
gst_buffer_resize (outbuf, 0, outsize);
GST_LOG_OBJECT (resample,
"Converted to buffer of %" G_GUINT32_FORMAT
" samples (%" G_GSIZE_FORMAT " bytes) with timestamp %" GST_TIME_FORMAT
", duration %" GST_TIME_FORMAT ", offset %" G_GUINT64_FORMAT
", offset_end %" G_GUINT64_FORMAT, out_processed, outsize,
", offset_end %" G_GUINT64_FORMAT, out_len, outsize,
GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (outbuf)),
GST_TIME_ARGS (GST_BUFFER_DURATION (outbuf)),
GST_BUFFER_OFFSET (outbuf), GST_BUFFER_OFFSET_END (outbuf));