...
 
Commits (21)
This diff is collapsed.
......@@ -196,6 +196,8 @@ check_valid_channel_positions (const GstAudioChannelPosition * position,
* positions @to. @from and @to must contain the same number of
* positions and the same positions, only in a different order.
*
* Note: this function assumes the audio data is in interleaved layout
*
* Returns: %TRUE if the reordering was possible.
*/
gboolean
......@@ -248,6 +250,31 @@ gst_audio_reorder_channels (gpointer data, gsize size, GstAudioFormat format,
return TRUE;
}
static gboolean
gst_audio_meta_reorder_channels (GstAudioMeta * meta,
const GstAudioChannelPosition * from, const GstAudioChannelPosition * to)
{
gint reorder_map[64] = { 0, };
gsize tmp_offsets[64] = { 0, };
gint i;
g_return_val_if_fail (meta, FALSE);
g_return_val_if_fail (meta->channels > 0, FALSE);
g_return_val_if_fail (meta->channels <= 64, FALSE);
g_return_val_if_fail (meta->offsets != NULL, FALSE);
if (!gst_audio_get_channel_reorder_map (meta->channels, from, to,
reorder_map))
return FALSE;
memcpy (tmp_offsets, meta->offsets, meta->channels * sizeof (gsize));
for (i = 0; i < meta->channels; i++) {
meta->offsets[reorder_map[i]] = tmp_offsets[i];
}
return TRUE;
}
/**
* gst_audio_buffer_reorder_channels:
* @buffer: The buffer to reorder.
......@@ -269,7 +296,8 @@ gst_audio_buffer_reorder_channels (GstBuffer * buffer,
const GstAudioChannelPosition * from, const GstAudioChannelPosition * to)
{
GstMapInfo info;
gboolean ret;
GstAudioMeta *meta;
gboolean ret = TRUE;
g_return_val_if_fail (GST_IS_BUFFER (buffer), FALSE);
g_return_val_if_fail (gst_buffer_is_writable (buffer), FALSE);
......@@ -277,15 +305,20 @@ gst_audio_buffer_reorder_channels (GstBuffer * buffer,
if (gst_audio_channel_positions_equal (from, to, channels))
return TRUE;
if (!gst_buffer_map (buffer, &info, GST_MAP_READWRITE))
return FALSE;
meta = gst_buffer_get_audio_meta (buffer);
if (meta && meta->layout == GST_AUDIO_LAYOUT_NON_INTERLEAVED) {
g_return_val_if_fail (channels == meta->channels, FALSE);
ret =
gst_audio_reorder_channels (info.data, info.size, format, channels, from,
to);
ret = gst_audio_meta_reorder_channels (meta, from, to);
} else {
if (!gst_buffer_map (buffer, &info, GST_MAP_READWRITE))
return FALSE;
gst_buffer_unmap (buffer, &info);
ret = gst_audio_reorder_channels (info.data, info.size, format, channels,
from, to);
gst_buffer_unmap (buffer, &info);
}
return ret;
}
......
......@@ -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 */
......@@ -567,6 +572,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)
{
......@@ -704,9 +813,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;
......@@ -715,12 +831,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 |=
......@@ -766,8 +880,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;
......@@ -860,6 +979,29 @@ chain_quantize (GstAudioConverter * convert, AudioChain * prev)
return 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)
{
......@@ -1169,8 +1311,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 =
......@@ -1211,7 +1351,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;
......@@ -1221,10 +1363,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");
......@@ -1233,7 +1377,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;
......
......@@ -911,6 +911,23 @@ static DeinterleaveFunc deinterleave_funcs[] = {
deinterleave_gdouble
};
static void
copy_func (GstAudioResampler * resampler, gpointer sbuf[],
gpointer in[], gsize in_frames)
{
gint i, c, channels = resampler->channels;
gsize samples_avail = resampler->samples_avail;
for (c = 0; c < channels; c++) {
gpointer *s = sbuf[c] + (samples_avail * resampler->bps);
if (G_UNLIKELY (in == NULL)) {
for (i = 0; i < in_frames; i++)
s[i] = 0;
} else {
memcpy (s, in[c], in_frames * resampler->bps);
}
}
}
static void
calculate_kaiser_params (GstAudioResampler * resampler)
{
......@@ -1332,7 +1349,7 @@ gst_audio_resampler_new (GstAudioResamplerMethod method,
GstAudioFormat format, gint channels,
gint in_rate, gint out_rate, GstStructure * options)
{
gboolean non_interleaved;
gboolean non_interleaved_in, non_interleaved_out;
GstAudioResampler *resampler;
const GstAudioFormatInfo *info;
GstStructure *def_options = NULL;
......@@ -1376,14 +1393,17 @@ gst_audio_resampler_new (GstAudioResamplerMethod method,
resampler->bps = GST_AUDIO_FORMAT_INFO_WIDTH (info) / 8;
resampler->sbuf = g_malloc0 (sizeof (gpointer) * channels);
non_interleaved =
non_interleaved_in =
(resampler->flags & GST_AUDIO_RESAMPLER_FLAG_NON_INTERLEAVED_IN);
non_interleaved_out =
(resampler->flags & GST_AUDIO_RESAMPLER_FLAG_NON_INTERLEAVED_OUT);
/* we resample each channel separately */
resampler->blocks = resampler->channels;
resampler->inc = 1;
resampler->ostride = non_interleaved ? 1 : resampler->channels;
resampler->deinterleave = deinterleave_funcs[resampler->format_index];
resampler->ostride = non_interleaved_out ? 1 : resampler->channels;
resampler->deinterleave = non_interleaved_in ?
copy_func : deinterleave_funcs[resampler->format_index];
resampler->convert_taps = convert_taps_funcs[resampler->format_index];
GST_DEBUG ("method %d, bps %d, channels %d", method, resampler->bps,
......
......@@ -80,6 +80,7 @@ gst_audio_buffer_clip (GstBuffer * buffer, const GstSegment * segment,
gint rate, gint bpf)
{
GstBuffer *ret;
GstAudioMeta *meta;
GstClockTime timestamp = GST_CLOCK_TIME_NONE, duration = GST_CLOCK_TIME_NONE;
guint64 offset = GST_BUFFER_OFFSET_NONE, offset_end = GST_BUFFER_OFFSET_NONE;
gsize trim, size, osize;
......@@ -98,8 +99,11 @@ gst_audio_buffer_clip (GstBuffer * buffer, const GstSegment * segment,
* Calculate the missing values for the calculations,
* they won't be changed later though. */
meta = gst_buffer_get_audio_meta (buffer);
/* these variables measure samples */
trim = 0;
osize = size = gst_buffer_get_size (buffer);
osize = size = meta ? meta->samples : (gst_buffer_get_size (buffer) / bpf);
/* no data, nothing to clip */
if (!size)
......@@ -111,7 +115,7 @@ gst_audio_buffer_clip (GstBuffer * buffer, const GstSegment * segment,
duration = GST_BUFFER_DURATION (buffer);
} else {
change_duration = FALSE;
duration = gst_util_uint64_scale (size / bpf, GST_SECOND, rate);
duration = gst_util_uint64_scale (size, GST_SECOND, rate);
}
if (GST_BUFFER_OFFSET_IS_VALID (buffer)) {
......@@ -125,7 +129,7 @@ gst_audio_buffer_clip (GstBuffer * buffer, const GstSegment * segment,
offset_end = GST_BUFFER_OFFSET_END (buffer);
} else {
change_offset_end = FALSE;
offset_end = offset + size / bpf;
offset_end = offset + size;
}
if (segment->format == GST_FORMAT_TIME) {
......@@ -149,8 +153,8 @@ gst_audio_buffer_clip (GstBuffer * buffer, const GstSegment * segment,
diff = gst_util_uint64_scale (diff, rate, GST_SECOND);
if (change_offset)
offset += diff;
trim += diff * bpf;
size -= diff * bpf;
trim += diff;
size -= diff;
}
diff = stop - cstop;
......@@ -161,7 +165,7 @@ gst_audio_buffer_clip (GstBuffer * buffer, const GstSegment * segment,
diff = gst_util_uint64_scale (diff, rate, GST_SECOND);
if (change_offset_end)
offset_end -= diff;
size -= diff * bpf;
size -= diff;
}
} else {
gst_buffer_unref (buffer);
......@@ -188,8 +192,8 @@ gst_audio_buffer_clip (GstBuffer * buffer, const GstSegment * segment,
if (change_duration)
duration -= gst_util_uint64_scale (diff, GST_SECOND, rate);
trim += diff * bpf;
size -= diff * bpf;
trim += diff;
size -= diff;
}
diff = stop - cstop;
......@@ -199,7 +203,7 @@ gst_audio_buffer_clip (GstBuffer * buffer, const GstSegment * segment,
if (change_duration)
duration -= gst_util_uint64_scale (diff, GST_SECOND, rate);
size -= diff * bpf;
size -= diff;
}
} else {
gst_buffer_unref (buffer);
......@@ -219,10 +223,9 @@ gst_audio_buffer_clip (GstBuffer * buffer, const GstSegment * segment,
GST_BUFFER_DURATION (ret) = duration;
}
} else {
/* Get a writable buffer and apply all changes */
/* cut out all the samples that are no longer relevant */
GST_DEBUG ("trim %" G_GSIZE_FORMAT " size %" G_GSIZE_FORMAT, trim, size);
ret = gst_buffer_copy_region (buffer, GST_BUFFER_COPY_ALL, trim, size);
gst_buffer_unref (buffer);
ret = gst_audio_buffer_truncate (buffer, bpf, trim, size);
GST_DEBUG ("timestamp %" GST_TIME_FORMAT, GST_TIME_ARGS (timestamp));
if (ret) {
......@@ -235,8 +238,76 @@ gst_audio_buffer_clip (GstBuffer * buffer, const GstSegment * segment,
if (change_offset_end)
GST_BUFFER_OFFSET_END (ret) = offset_end;
} else {
GST_ERROR ("copy_region failed");
GST_ERROR ("gst_audio_buffer_truncate failed");
}
}
return ret;
}
/**
* gst_audio_buffer_truncate:
* @buffer: (transfer full): The buffer to truncate.
* @bpf: size of one audio frame in bytes. This is the size of one sample *
* number of channels.
* @trim: the number of samples to remove from the beginning of the buffer
* @samples: the final number of samples that should exist in this buffer or -1
* to use all the remaining samples if you are only removing samples from the
* beginning.
*
* Truncate the buffer to finally have @samples number of samples, removing
* the necessary amount of samples from the end and @trim number of samples
* from the beginning.
*
* After calling this function the caller does not own a reference to
* @buffer anymore.
*
* Returns: (transfer full): the truncated buffer or %NULL if the arguments
* were invalid
*
* Since: UNRELEASED
*/
GstBuffer *
gst_audio_buffer_truncate (GstBuffer * buffer, gint bpf, gsize trim,
gsize samples)
{
GstAudioMeta *meta = NULL;
GstBuffer *ret = NULL;
gsize orig_samples;
gint i;
g_return_val_if_fail (GST_IS_BUFFER (buffer), NULL);
g_return_val_if_fail (trim >= 0, NULL);
meta = gst_buffer_get_audio_meta (buffer);
orig_samples = meta ? meta->samples : gst_buffer_get_size (buffer) / bpf;
g_return_val_if_fail (trim < orig_samples, NULL);
g_return_val_if_fail (samples == -1 || trim + samples <= orig_samples, NULL);
if (samples == -1)
samples = orig_samples - trim;
/* nothing to truncate */
if (samples == orig_samples)
return buffer;
if (!meta || meta->layout == GST_AUDIO_LAYOUT_INTERLEAVED) {
/* interleaved */
ret = gst_buffer_copy_region (buffer, GST_BUFFER_COPY_ALL, trim * bpf,
samples * bpf);
gst_buffer_unref (buffer);
if ((meta = gst_buffer_get_audio_meta (ret)))
meta->samples = samples;
} else {
/* non-interleaved */
ret = gst_buffer_make_writable (buffer);
meta = gst_buffer_get_audio_meta (buffer);
meta->samples = samples;
for (i = 0; i < meta->channels; i++) {
meta->offsets[i] += trim * bpf / meta->channels;
}
}
return ret;
}
......@@ -94,6 +94,9 @@ GstBuffer * gst_audio_buffer_clip (GstBuffer *buffer,
const GstSegment *segment,
gint rate, gint bpf);
GST_EXPORT
GstBuffer * gst_audio_buffer_truncate (GstBuffer *buffer,
gint bpf, gsize trim, gsize samples);
G_END_DECLS
......
......@@ -93,7 +93,10 @@
*
* In non-live pipelines, baseclass can also (configurably) arrange for
* output buffer aggregation which may help to redue large(r) numbers of
* small(er) buffers being pushed and processed downstream.
* small(er) buffers being pushed and processed downstream. Note that this
* feature is only available if the buffer layout is interleaved. For planar
* buffers, the decoder implementation is fully responsible for the output
* buffer size.
*
* On the other hand, it should be noted that baseclass only provides limited
* seeking support (upon explicit subclass request), as full-fledged support
......@@ -985,7 +988,8 @@ gst_audio_decoder_output (GstAudioDecoder * dec, GstBuffer * buf)
again:
inbuf = NULL;
if (priv->agg && dec->priv->latency > 0) {
if (priv->agg && dec->priv->latency > 0 &&
priv->ctx.info.layout == GST_AUDIO_LAYOUT_INTERLEAVED) {
gint av;
gboolean assemble = FALSE;
const GstClockTimeDiff tol = 10 * GST_MSECOND;
......@@ -1202,9 +1206,9 @@ gst_audio_decoder_finish_frame (GstAudioDecoder * dec, GstBuffer * buf,
GstAudioDecoderPrivate *priv;
GstAudioDecoderContext *ctx;
GstAudioDecoderClass *klass = GST_AUDIO_DECODER_GET_CLASS (dec);
gint samples = 0;
GstAudioMeta *meta;
GstClockTime ts, next_ts;
gsize size;
gsize size, samples = 0;
GstFlowReturn ret = GST_FLOW_OK;
GQueue inbufs = G_QUEUE_INIT;
......@@ -1216,7 +1220,9 @@ gst_audio_decoder_finish_frame (GstAudioDecoder * dec, GstBuffer * buf,
priv = dec->priv;
ctx = &dec->priv->ctx;
meta = buf ? gst_buffer_get_audio_meta (buf) : NULL;
size = buf ? gst_buffer_get_size (buf) : 0;
samples = buf ? (meta ? meta->samples : size / ctx->info.bpf) : 0;
/* must know the output format by now */
g_return_val_if_fail (buf == NULL || GST_AUDIO_INFO_IS_VALID (&ctx->info),
......@@ -1224,8 +1230,7 @@ gst_audio_decoder_finish_frame (GstAudioDecoder * dec, GstBuffer * buf,
GST_LOG_OBJECT (dec,
"accepting %" G_GSIZE_FORMAT " bytes == %" G_GSIZE_FORMAT
" samples for %d frames", buf ? size : -1,
buf ? size / ctx->info.bpf : -1, frames);
" samples for %d frames", buf ? size : 0, samples, frames);
GST_AUDIO_DECODER_STREAM_LOCK (dec);
......@@ -1240,12 +1245,20 @@ gst_audio_decoder_finish_frame (GstAudioDecoder * dec, GstBuffer * buf,
send_pending_events (dec);
}
/* output shoud be whole number of sample frames */
/* sanity checking */
if (G_LIKELY (buf && ctx->info.bpf)) {
if (size % ctx->info.bpf)
goto wrong_buffer;
/* per channel least */
samples = size / ctx->info.bpf;
if (!meta || meta->layout == GST_AUDIO_LAYOUT_INTERLEAVED) {
/* output shoud be whole number of sample frames */
if (size % ctx->info.bpf)
goto wrong_buffer;
/* output should have no additional padding */
if (samples != size / ctx->info.bpf)
goto wrong_samples;
} else {
/* can't have more samples than what the buffer fits */
if (samples > size / ctx->info.bpf)
goto wrong_samples;
}
}
/* frame and ts book-keeping */
......@@ -1397,6 +1410,16 @@ wrong_buffer:
ret = GST_FLOW_ERROR;
goto exit;
}
wrong_samples:
{
GST_ELEMENT_ERROR (dec, STREAM, DECODE, (NULL),
("GstAudioMeta samples (%" G_GSIZE_FORMAT ") are inconsistent with "
"the buffer size and layout (size/bpf = %" G_GSIZE_FORMAT ")",
meta->samples, size / ctx->info.bpf));
gst_buffer_unref (buf);
ret = GST_FLOW_ERROR;
goto exit;
}
}
static GstFlowReturn
......
......@@ -1181,9 +1181,10 @@ gst_audio_encoder_chain (GstPad * pad, GstObject * parent, GstBuffer * buffer)
GstAudioEncoder *enc;
GstAudioEncoderPrivate *priv;
GstAudioEncoderContext *ctx;
GstAudioMeta *meta;
GstFlowReturn ret = GST_FLOW_OK;
gboolean discont;
gsize size;
gsize size, samples;
enc = GST_AUDIO_ENCODER (parent);
......@@ -1208,6 +1209,7 @@ gst_audio_encoder_chain (GstPad * pad, GstObject * parent, GstBuffer * buffer)
if (!ctx->info.bpf)
goto not_negotiated;
meta = gst_buffer_get_audio_meta (buffer);
size = gst_buffer_get_size (buffer);
GST_LOG_OBJECT (enc,
......@@ -1216,9 +1218,17 @@ gst_audio_encoder_chain (GstPad * pad, GstObject * parent, GstBuffer * buffer)
GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buffer)),
GST_TIME_ARGS (GST_BUFFER_DURATION (buffer)));
/* input shoud be whole number of sample frames */
if (size % ctx->info.bpf)
goto wrong_buffer;
if (!meta || meta->layout == GST_AUDIO_LAYOUT_INTERLEAVED) {
/* input should be whole number of sample frames */
if (size % ctx->info.bpf)
goto wrong_buffer;
samples = size / ctx->info.bpf;
} else {
samples = meta->samples;
/* can't have more samples than what the buffer fits */
if (samples > size / ctx->info.bpf)
goto wrong_samples;
}
#ifndef GST_DISABLE_GST_DEBUG
{
......@@ -1226,8 +1236,7 @@ gst_audio_encoder_chain (GstPad * pad, GstObject * parent, GstBuffer * buffer)
GstClockTimeDiff diff;
/* verify buffer duration */
duration = gst_util_uint64_scale (size, GST_SECOND,
ctx->info.rate * ctx->info.bpf);
duration = gst_util_uint64_scale (samples, GST_SECOND, ctx->info.rate);
diff = GST_CLOCK_DIFF (duration, GST_BUFFER_DURATION (buffer));
if (GST_BUFFER_DURATION (buffer) != GST_CLOCK_TIME_NONE &&
(diff > GST_SECOND / ctx->info.rate / 2 ||
......@@ -1254,7 +1263,9 @@ gst_audio_encoder_chain (GstPad * pad, GstObject * parent, GstBuffer * buffer)
goto done;
}
meta = gst_buffer_get_audio_meta (buffer);
size = gst_buffer_get_size (buffer);
samples = meta ? meta->samples : size / ctx->info.bpf;
GST_LOG_OBJECT (enc,
"buffer after segment clipping has size %" G_GSIZE_FORMAT " with ts %"
......@@ -1302,19 +1313,18 @@ gst_audio_encoder_chain (GstPad * pad, GstObject * parent, GstBuffer * buffer)
/* do some fancy tweaking in hard resync case */
if (discont && enc->priv->hard_resync) {
if (diff < 0) {
guint64 diff_bytes;
guint64 diff_samples;
GST_WARNING_OBJECT (enc, "Buffer is older than expected ts %"
GST_TIME_FORMAT ". Clipping buffer", GST_TIME_ARGS (next_ts));
diff_bytes =
GST_CLOCK_TIME_TO_FRAMES (-diff, ctx->info.rate) * ctx->info.bpf;
if (diff_bytes >= size) {
diff_samples = GST_CLOCK_TIME_TO_FRAMES (-diff, ctx->info.rate);
if (diff_samples >= samples) {
gst_buffer_unref (buffer);
goto done;
}
buffer = gst_buffer_make_writable (buffer);
gst_buffer_resize (buffer, diff_bytes, size - diff_bytes);
buffer = gst_audio_buffer_truncate (buffer, ctx->info.bpf, diff_samples,
samples - diff_samples);
GST_BUFFER_TIMESTAMP (buffer) += diff;
/* care even less about duration after this */
......@@ -1364,6 +1374,16 @@ not_negotiated:
ret = GST_FLOW_NOT_NEGOTIATED;
goto done;
}
wrong_samples:
{
GST_ELEMENT_ERROR (enc, STREAM, ENCODE, (NULL),
("GstAudioMeta samples (%" G_GSIZE_FORMAT ") are more than what "
"the buffer fits: %" G_GSIZE_FORMAT, meta->samples,
size / ctx->info.bpf));
gst_buffer_unref (buffer);
ret = GST_FLOW_ERROR;
goto done;
}
wrong_buffer:
{
GST_ELEMENT_ERROR (enc, STREAM, ENCODE, (NULL),
......
......@@ -304,3 +304,226 @@ gst_audio_clipping_meta_get_info (void)
}
return audio_clipping_meta_info;
}
static gboolean
gst_audio_meta_init (GstMeta * meta, gpointer params, GstBuffer * buffer)
{
GstAudioMeta *ameta = (GstAudioMeta *) meta;
ameta->format = GST_AUDIO_FORMAT_UNKNOWN;
ameta->layout = GST_AUDIO_LAYOUT_INTERLEAVED;
ameta->channels = ameta->samples = 0;
ameta->offsets = NULL;
return TRUE;
}
static void
gst_audio_meta_free (GstMeta * meta, GstBuffer * buffer)
{
GstAudioMeta *ameta = (GstAudioMeta *) meta;
if (ameta->offsets && ameta->offsets != ameta->priv_offsets_arr)
g_slice_free1 (ameta->channels * sizeof (gsize), ameta->offsets);
}
static gboolean
gst_audio_meta_transform (GstBuffer * dest, GstMeta * meta,
GstBuffer * buffer, GQuark type, gpointer data)
{
GstAudioMeta *smeta, *dmeta;
smeta = (GstAudioMeta *) meta;
if (GST_META_TRANSFORM_IS_COPY (type)) {
dmeta = gst_buffer_add_audio_meta (dest, smeta->format, smeta->layout,
smeta->channels, smeta->samples, smeta->offsets);
if (!dmeta)
return FALSE;
} else {
/* return FALSE, if transform type is not supported */
return FALSE;
}
return TRUE;
}
/**
* gst_buffer_add_audio_meta:
* @buffer: (transfer none): a #GstBuffer
* @format: the format of the samples in the buffer
* @layout: the layout of the buffer
* @channels: the number of channels in the buffer
* @samples: the number of valid samples in the buffer
* @offsets: (nullable): the offsets (in bytes) where each channel plane starts
* in the buffer or %NULL to calculate it (see below); must be %NULL also
* when @layout is %GST_AUDIO_LAYOUT_INTERLEAVED
*
* Allocates and attaches a #GstAudioMeta on @buffer, which must be writable
* for that purpose. The fields of the #GstAudioMeta are directly populated
* from the arguments of this function.
*
* When @layout is %GST_AUDIO_LAYOUT_NON_INTERLEAVED and @offsets is %NULL,
* the offsets are calculated with a formula that assumes the planes are
* tightly packed and in sequence:
* offsets[channel] = channel * @samples * bytes_per_sample
*
* Returns: the #GstAudioMeta that was attached on the @buffer
*
* Since: UNRELEASED
*/
GstAudioMeta *
gst_buffer_add_audio_meta (GstBuffer * buffer, GstAudioFormat format,
GstAudioLayout layout, gint channels, gsize samples, gsize offsets[])
{
GstAudioMeta *meta;
gint i;
g_return_val_if_fail (format != GST_AUDIO_FORMAT_UNKNOWN, NULL);
g_return_val_if_fail (channels > 0, NULL);
meta =
(GstAudioMeta *) gst_buffer_add_meta (buffer, GST_AUDIO_META_INFO, NULL);
meta->format = format;
meta->layout = layout;
meta->channels = channels;
meta->samples = samples;
if (layout == GST_AUDIO_LAYOUT_NON_INTERLEAVED) {
if (G_UNLIKELY (channels > 8))
meta->offsets = g_slice_alloc (channels * sizeof (gsize));
else
meta->offsets = meta->priv_offsets_arr;
if (offsets) {
for (i = 0; i < channels; i++)
meta->offsets[i] = offsets[i];
} else {
/* default offsets assume channels are laid out sequentially in memory */
const GstAudioFormatInfo *finfo =
gst_audio_format_get_info (meta->format);
for (i = 0; i < channels; i++)
meta->offsets[i] = i * samples * finfo->width / 8;
}
}
return meta;
}
/**
* gst_audio_buffer_map:
* @buffer: (transfer none): a #GstBuffer
* @info: a #GstAudioMapInfo to store the result of the map
* @flags: the access mode for the memory
*
* Maps an audio buffer so that it can be read or written and stores the result
* of the map operation in @info, taking into account whether the buffer is
* in planar or interleaved format.
*
* Note: if a #GstAudioMeta is not attached on the @buffer, it is assumed
* to have interleaved layout.
*
* Returns: %TRUE if the map operation succeeded or %FALSE on failure
*
* Since: UNRELEASED
*/
gboolean
gst_audio_buffer_map (GstBuffer * buffer, GstAudioMapInfo * info,
GstMapFlags flags)
{
GstAudioMeta *meta = NULL;
const GstAudioFormatInfo *finfo;
gboolean ret = TRUE;
gint i;
meta = gst_buffer_get_audio_meta (buffer);
if (meta)
finfo = gst_audio_format_get_info (meta->format);
if (!gst_buffer_map (buffer, &info->map_info, flags))
return FALSE;
if (!meta || meta->layout == GST_AUDIO_LAYOUT_INTERLEAVED) {
/* interleaved */
info->n_planes = 1;
info->plane_size = meta ?
(meta->channels * meta->samples * finfo->width / 8) :
info->map_info.size;
info->planes = info->priv_planes_arr;
info->planes[0] = info->map_info.data;
} else {
/* non-interleaved */
info->n_planes = meta->channels;
info->plane_size = meta->samples * finfo->width / 8;
if (G_UNLIKELY (info->n_planes > 8))
info->planes = g_slice_alloc (info->n_planes * sizeof (gpointer));
else
info->planes = info->priv_planes_arr;
for (i = 0; i < meta->channels; i++)
info->planes[i] = info->map_info.data + meta->offsets[i];
}
if (info->n_planes * info->plane_size > info->map_info.size) {
GST_ERROR ("Invalid buffer size according to its attached GstAudioMeta");
gst_audio_buffer_unmap (buffer, info);
return FALSE;
}
return ret;
}
/**
* gst_audio_buffer_unmap:
* @buffer: (transfer none): a #GstBuffer
* @info: a #GstAudioMapInfo with the result of the previous map
*
* Unmaps an audio buffer that was previously mapped with
* gst_audio_buffer_map().
*
* Since: UNRELEASED
*/
void
gst_audio_buffer_unmap (GstBuffer * buffer, GstAudioMapInfo * info)
{
gst_buffer_unmap (buffer, &info->map_info);
if (info->planes != info->priv_planes_arr)
g_slice_free1 (info->n_planes * sizeof (gpointer), info->planes);
memset (info, 0, sizeof (GstAudioMapInfo));
}
GType
gst_audio_meta_api_get_type (void)
{
static volatile GType type;
static const gchar *tags[] = {
GST_META_TAG_AUDIO_STR, GST_META_TAG_AUDIO_CHANNELS_STR,
GST_META_TAG_AUDIO_RATE_STR, NULL
};
if (g_once_init_enter (&type)) {
GType _type = gst_meta_api_type_register ("GstAudioMetaAPI", tags);
g_once_init_leave (&type, _type);
}
return type;
}
const GstMetaInfo *
gst_audio_meta_get_info (void)
{
static const GstMetaInfo *audio_meta_info = NULL;
if (g_once_init_enter ((GstMetaInfo **) & audio_meta_info)) {
const GstMetaInfo *meta = gst_meta_register (GST_AUDIO_META_API_TYPE,
"GstAudioMeta", sizeof (GstAudioMeta),
gst_audio_meta_init,
gst_audio_meta_free,
gst_audio_meta_transform);
g_once_init_leave ((GstMetaInfo **) & audio_meta_info,
(GstMetaInfo *) meta);
}
return audio_meta_info;
}
......@@ -125,6 +125,104 @@ GstAudioClippingMeta * gst_buffer_add_audio_clipping_meta (GstBuffer *buffer,
guint64 start,
guint64 end);
#define GST_AUDIO_META_API_TYPE (gst_audio_meta_api_get_type())
#define GST_AUDIO_META_INFO (gst_audio_meta_get_info())
typedef struct _GstAudioMeta GstAudioMeta;
/**
* GstAudioMapInfo:
* @n_planes: the number of planes available
* @plane_size: the size (in bytes) of each plane
* @planes: an array of @n_planes pointers pointing to the start of each
* plane in the mapped buffer
*
* A structure containing the result of an audio buffer map operation,
* which is executed with gst_audio_buffer_map(). For non-interleaved (planar)
* buffers, the beginning of each plane in the buffer has its own pointer in
* the @planes array. For interleaved buffers, the @planes array only contains
* one item, which is the pointer to the beginning of the buffer, and @n_planes
* equals 1.
*
* Since: UNRELEASED
*/
typedef struct {
gint n_planes;
gsize plane_size;
gpointer *planes;
GstMapInfo map_info;
/*< private >*/
gpointer priv_planes_arr[8];
gpointer _gst_reserved[GST_PADDING];
} GstAudioMapInfo;
/**
* GstAudioMeta:
* @meta: parent #GstMeta
* @format: the format of the samples in the buffer
* @layout: the layout of the buffer
* @channels: the number of channels in the buffer
* @samples: the number of valid samples in the buffer
* @offsets: the offsets (in bytes) where each channel plane starts in the
* buffer or %NULL if the buffer has interleaved layout; if not %NULL, this
* is guaranteed to be an array of @channels elements
*
* Buffer metadata describing how data is layed out inside the buffer. This
* is useful for non-interleaved (planar) buffers, where it is necessary to
* have a place to store where each plane starts and how long each plane is.
*
* It is a requirement for non-interleaved buffers to have this metadata
* attached and to be mapped with gst_audio_buffer_map() in order to ensure
* correct handling of cliping and channel reordering (which is internally
* implemented by altering the @offsets and the @samples variables).
*
* Additionally, when this metadata is present, it is not safe to infer the
* number of valid samples from the size of the buffer. You should always
* use the @samples variable of this metadata.
*
* Since: UNRELEASED
*/
struct _GstAudioMeta {
GstMeta meta;
GstAudioFormat format;
GstAudioLayout layout;
gint channels;
gsize samples;
gsize *offsets;
/*< private >*/
gsize priv_offsets_arr[8];
gpointer _gst_reserved[GST_PADDING];
};
GST_EXPORT
GType gst_audio_meta_api_get_type (void);
GST_EXPORT
const GstMetaInfo * gst_audio_meta_get_info (void);
#define gst_buffer_get_audio_meta(b) \
((GstAudioMeta*)gst_buffer_get_meta((b), GST_AUDIO_META_API_TYPE))
GST_EXPORT
GstAudioMeta * gst_buffer_add_audio_meta (GstBuffer *buffer,
GstAudioFormat format,
GstAudioLayout layout,
gint channels, gsize samples,
gsize offsets[]);
GST_EXPORT
gboolean gst_audio_buffer_map (GstBuffer *buffer, GstAudioMapInfo *info,
GstMapFlags flags);
GST_EXPORT
void gst_audio_buffer_unmap (GstBuffer *buffer, GstAudioMapInfo *info);
G_END_DECLS
#endif /* __GST_AUDIO_META_H__ */
......@@ -167,11 +167,11 @@ enum
#if G_BYTE_ORDER == G_LITTLE_ENDIAN
#define CAPS \
GST_AUDIO_CAPS_MAKE ("{ S32LE, U32LE, S16LE, U16LE, S8, U8, F32LE, F64LE }") \
", layout = (string) { interleaved, non-interleaved }"
", layout = (string) { interleaved }"
#else
#define CAPS \
GST_AUDIO_CAPS_MAKE ("{ S32BE, U32BE, S16BE, U16BE, S8, U8, F32BE, F64BE }") \
", layout = (string) { interleaved, non-interleaved }"
", layout = (string) { interleaved }"
#endif
static GstStaticPadTemplate gst_adder_src_template =
......
......@@ -169,7 +169,7 @@ G_DEFINE_TYPE_WITH_CODE (GstAudioConvert, gst_audio_convert,
#define STATIC_CAPS \
GST_STATIC_CAPS (GST_AUDIO_CAPS_MAKE (GST_AUDIO_FORMATS_ALL) \
", layout = (string) interleaved")
", layout = (string) { interleaved, non-interleaved }")
static GstStaticPadTemplate gst_audio_convert_src_template =
GST_STATIC_PAD_TEMPLATE ("src",
......@@ -307,6 +307,14 @@ remove_format_from_structure (GstCapsFeatures * features,
return TRUE;
}
static gboolean
remove_layout_from_structure (GstCapsFeatures * features,
GstStructure * structure, gpointer user_data G_GNUC_UNUSED)
{
gst_structure_remove_field (structure, "layout");
return TRUE;
}
static gboolean
remove_channels_from_structure (GstCapsFeatures * features, GstStructure * s,
gpointer user_data)
......@@ -352,6 +360,7 @@ gst_audio_convert_transform_caps (GstBaseTransform * btrans,
tmp = gst_caps_copy (caps);
gst_caps_map_in_place (tmp, remove_format_from_structure, NULL);
gst_caps_map_in_place (tmp, remove_layout_from_structure, NULL);
gst_caps_map_in_place (tmp, remove_channels_from_structure, btrans);
/* We can infer the required input / output channels based on the
......@@ -792,14 +801,17 @@ gst_audio_convert_transform (GstBaseTransform * base, GstBuffer * inbuf,
{
GstFlowReturn ret;
GstAudioConvert *this = GST_AUDIO_CONVERT (base);
GstMapInfo srcmap = { NULL, }, dstmap;
GstAudioMeta *meta;
GstAudioMapInfo srcmap, dstmap;
gint insize, outsize;
gboolean inbuf_writable;
GstAudioConverterFlags flags;
gsize samples;
/* get amount of samples to convert. */
samples = gst_buffer_get_size (inbuf) / this->in_info.bpf;
meta = gst_buffer_get_audio_meta (inbuf);
samples =
meta ? meta->samples : (gst_buffer_get_size (inbuf) / this->in_info.bpf);
/* get in/output sizes, to see if the buffers we got are of correct
* sizes */
......@@ -811,27 +823,32 @@ gst_audio_convert_transform (GstBaseTransform * base, GstBuffer * inbuf,
gst_buffer_resize (outbuf, 0, outsize);
if (inbuf != outbuf) {
gst_buffer_add_audio_meta (outbuf, this->out_info.finfo->format,
this->out_info.layout, this->out_info.channels, samples, NULL);
}
/* get src and dst data */
if (inbuf != outbuf) {
inbuf_writable = gst_buffer_is_writable (inbuf)
&& gst_buffer_n_memory (inbuf) == 1
&& gst_memory_is_writable (gst_buffer_peek_memory (inbuf, 0));
if (!gst_buffer_map (inbuf, &srcmap,
if (!gst_audio_buffer_map (inbuf, &srcmap,
inbuf_writable ? GST_MAP_READWRITE : GST_MAP_READ))
goto inmap_error;
} else {
inbuf_writable = TRUE;
}
if (!gst_buffer_map (outbuf, &dstmap, GST_MAP_WRITE))
if (!gst_audio_buffer_map (outbuf, &dstmap, GST_MAP_WRITE))
goto outmap_error;
/* check in and outsize */
if (inbuf != outbuf) {
if (srcmap.size < insize)
if (srcmap.n_planes * srcmap.plane_size < insize)
goto wrong_size;
}
if (dstmap.size < outsize)
if (dstmap.n_planes * dstmap.plane_size < outsize)
goto wrong_size;
/* and convert the samples */
......@@ -840,22 +857,24 @@ gst_audio_convert_transform (GstBaseTransform * base, GstBuffer * inbuf,
flags |= GST_AUDIO_CONVERTER_FLAG_IN_WRITABLE;
if (!GST_BUFFER_FLAG_IS_SET (inbuf, GST_BUFFER_FLAG_GAP)) {
gpointer in[1] = { srcmap.data };
gpointer out[1] = { dstmap.data };
if (!gst_audio_converter_samples (this->convert, flags,
inbuf != outbuf ? in : out, samples, out, samples))
inbuf != outbuf ? srcmap.planes : dstmap.planes, samples,
dstmap.planes, samples))
goto convert_error;
} else {
/* Create silence buffer */
gst_audio_format_fill_silence (this->out_info.finfo, dstmap.data, outsize);
gint i;
for (i = 0; i < dstmap.n_planes; i++) {
gst_audio_format_fill_silence (this->out_info.finfo, dstmap.planes[i],
dstmap.plane_size);
}
}
ret = GST_FLOW_OK;
done:
gst_buffer_unmap (outbuf, &dstmap);
gst_audio_buffer_unmap (outbuf, &dstmap);
if (inbuf != outbuf)
gst_buffer_unmap (inbuf, &srcmap);
gst_audio_buffer_unmap (inbuf, &srcmap);
return ret;
......@@ -866,7 +885,8 @@ wrong_size:
(NULL),
("input/output buffers are of wrong size in: %" G_GSIZE_FORMAT " < %d"
" or out: %" G_GSIZE_FORMAT " < %d",
srcmap.size, insize, dstmap.size, outsize));
srcmap.n_planes * srcmap.plane_size, insize,
dstmap.n_planes * dstmap.plane_size, outsize));
ret = GST_FLOW_ERROR;
goto done;
}
......@@ -888,7 +908,7 @@ outmap_error:
GST_ELEMENT_ERROR (this, STREAM, FORMAT,
(NULL), ("failed to map output buffer"));
if (inbuf != outbuf)
gst_buffer_unmap (inbuf, &srcmap);
gst_audio_buffer_unmap (inbuf, &srcmap);
return GST_FLOW_ERROR;
}
}
......
......@@ -439,6 +439,7 @@ gst_audio_rate_chain (GstPad * pad, GstObject * parent, GstBuffer * buf)
GstFlowReturn ret = GST_FLOW_OK;
GstClockTimeDiff diff;
gint rate, bpf;
GstAudioMeta *meta;
audiorate = GST_AUDIO_RATE (parent);
......@@ -492,8 +493,9 @@ gst_audio_rate_chain (GstPad * pad, GstObject * parent, GstBuffer * buf)
in_time = audiorate->next_ts;
}
meta = gst_buffer_get_audio_meta (buf);
in_size = gst_buffer_get_size (buf);
in_samples = in_size / bpf;
in_samples = meta ? meta->samples : in_size / bpf;
audiorate->in += in_samples;
/* calculate the buffer offset */
......@@ -549,6 +551,11 @@ gst_audio_rate_chain (GstPad * pad, GstObject * parent, GstBuffer * buf)
fillmap.size);
gst_buffer_unmap (fill, &fillmap);
if (audiorate->info.layout == GST_AUDIO_LAYOUT_NON_INTERLEAVED) {
gst_buffer_add_audio_meta (fill, audiorate->info.finfo->format,
audiorate->info.layout, audiorate->info.channels, cursamples, NULL);
}
GST_DEBUG_OBJECT (audiorate, "inserting %" G_GUINT64_FORMAT " samples",
cursamples);
......@@ -591,7 +598,7 @@ gst_audio_rate_chain (GstPad * pad, GstObject * parent, GstBuffer * buf)
} else if (in_offset < audiorate->next_offset) {
/* need to remove samples */
if (in_offset_end <= audiorate->next_offset) {
guint64 drop = in_size / bpf;
guint64 drop = in_samples;
audiorate->drop += drop;
......@@ -607,24 +614,16 @@ gst_audio_rate_chain (GstPad * pad, GstObject * parent, GstBuffer * buf)
goto beach;
} else {
guint64 truncsamples;
guint truncsize, leftsize;
GstBuffer *trunc;
guint64 truncsamples, leftsamples;
/* truncate buffer */
truncsamples = audiorate->next_offset - in_offset;
truncsize = truncsamples * bpf;
leftsize = in_size - truncsize;
leftsamples = in_samples - truncsamples;
trunc =
gst_buffer_copy_region (buf, GST_BUFFER_COPY_ALL, truncsize,
leftsize);
gst_buffer_unref (buf);
buf = trunc;
buf = gst_audio_buffer_truncate (buf, bpf, truncsamples, leftsamples);
audiorate->drop += truncsamples;
audiorate->out += (leftsize / bpf);
audiorate->out += leftsamples;
GST_DEBUG_OBJECT (audiorate, "truncating %" G_GUINT64_FORMAT " samples",
truncsamples);
......
......@@ -548,8 +548,7 @@ gst_audio_resample_push_drain (GstAudioResample * resample, guint history_len)
GstFlowReturn res;
gint outsize;
gsize out_len;
GstMapInfo map;
gpointer out[1];
GstAudioMapInfo map;
g_assert (resample->converter != NULL);
......@@ -565,13 +564,15 @@ gst_audio_resample_push_drain (GstAudioResample * resample, guint history_len)
outsize = out_len * resample->in.bpf;
outbuf = gst_buffer_new_and_alloc (outsize);
gst_buffer_map (outbuf, &map, GST_MAP_WRITE);
gst_buffer_add_audio_meta (outbuf, resample->out.finfo->format,
resample->out.layout, resample->out.channels, out_len, NULL);
gst_audio_buffer_map (outbuf, &map, GST_MAP_WRITE);
out[0] = map.data;
gst_audio_converter_samples (resample->converter, 0, NULL, history_len,
out, out_len);
map.planes, out_len);
gst_buffer_unmap (outbuf, &map);
gst_audio_buffer_unmap (outbuf, &map);
/* time */
if (GST_CLOCK_TIME_IS_VALID (resample->t0)) {
......@@ -703,7 +704,7 @@ static GstFlowReturn
gst_audio_resample_process (GstAudioResample * resample, GstBuffer * inbuf,
GstBuffer * outbuf)
{
GstMapInfo in_map, out_map;
GstAudioMapInfo in_map, out_map;
gsize outsize;
guint32 in_len;
guint32 out_len;
......@@ -711,16 +712,19 @@ gst_audio_resample_process (GstAudioResample * resample, GstBuffer * inbuf,
gst_audio_converter_get_max_latency (resample->converter) * 2;
gboolean inbuf_writable;
in_len = gst_buffer_get_size (inbuf) / resample->in.bpf;
out_len = gst_buffer_get_size (outbuf) / resample->out.bpf;
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);
gst_buffer_add_audio_meta (outbuf, resample->out.finfo->format,
resample->out.layout, resample->out.channels, out_len, NULL);
in_len = in_map.size / resample->in.bpf;
out_len = out_map.size / resample->out.bpf;
gst_audio_buffer_map (inbuf, &in_map,
inbuf_writable ? GST_MAP_READWRITE : GST_MAP_READ);
gst_audio_buffer_map (outbuf, &out_map, GST_MAP_WRITE);
if (GST_BUFFER_FLAG_IS_SET (inbuf, GST_BUFFER_FLAG_GAP)) {
resample->num_nongap_samples = 0;
......@@ -749,7 +753,7 @@ gst_audio_resample_process (GstAudioResample * resample, GstBuffer * inbuf,
else
out_len = 0;
memset (out_map.data, 0, out_map.size);
memset (out_map.map_info.data, 0, out_map.map_info.size);
GST_BUFFER_FLAG_SET (outbuf, GST_BUFFER_FLAG_GAP);
resample->num_gap_samples += in_len;
}
......@@ -771,17 +775,14 @@ gst_audio_resample_process (GstAudioResample * resample, GstBuffer * inbuf,
}
{
/* process */
gpointer in[1], out[1];
GstAudioConverterFlags flags;
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, flags, in, in_len,
out, out_len);
gst_audio_converter_samples (resample->converter, flags, in_map.planes,
in_len, out_map.planes, out_len);
}
}
......@@ -809,8 +810,8 @@ gst_audio_resample_process (GstAudioResample * resample, GstBuffer * inbuf,
resample->samples_out += out_len;
resample->samples_in += in_len;
gst_buffer_unmap (inbuf, &in_map);
gst_buffer_unmap (outbuf, &out_map);
gst_audio_buffer_unmap (inbuf, &in_map);
gst_audio_buffer_unmap (outbuf, &out_map);
outsize = out_len * resample->in.bpf;
......
This diff is collapsed.
This diff is collapsed.
......@@ -25,6 +25,7 @@
#endif
#include <gst/check/gstcheck.h>
#include <gst/check/gstharness.h>
#include <gst/audio/audio.h>
/* For ease of programming we use globals to keep refs for our floating
......@@ -46,7 +47,7 @@ static GstStaticPadTemplate sinktemplate = GST_STATIC_PAD_TEMPLATE ("sink",
);
static GstElement *
setup_audiotestsrc (void)
setup_audiotestsrc ()
{
GstElement *audiotestsrc;
......@@ -114,6 +115,169 @@ GST_START_TEST (test_all_waves)
GST_END_TEST;
#define TEST_LAYOUT_CHANNELS 6
static GstStaticPadTemplate sinktemplate_interleaved =
GST_STATIC_PAD_TEMPLATE ("sink",
GST_PAD_SINK,
GST_PAD_ALWAYS,
GST_STATIC_CAPS ("audio/x-raw, "
"format = (string) " GST_AUDIO_NE (S16) ", "
"channels = (int) " G_STRINGIFY (TEST_LAYOUT_CHANNELS) ", "
"rate = (int) [ 1, MAX ], layout = (string) interleaved")
);
static GstStaticPadTemplate sinktemplate_plannar =
GST_STATIC_PAD_TEMPLATE ("sink",
GST_PAD_SINK,
GST_PAD_ALWAYS,
GST_STATIC_CAPS ("audio/x-raw, "
"format = (string) " GST_AUDIO_NE (S16) ", "
"channels = (int) " G_STRINGIFY (TEST_LAYOUT_CHANNELS) ", "
"rate = (int) [ 1, MAX ], layout = (string) non-interleaved")
);
typedef enum
{
GST_AUDIO_TEST_SRC_WAVE_SINE,
GST_AUDIO_TEST_SRC_WAVE_SQUARE,
GST_AUDIO_TEST_SRC_WAVE_SAW,
GST_AUDIO_TEST_SRC_WAVE_TRIANGLE,
GST_AUDIO_TEST_SRC_WAVE_SILENCE,
GST_AUDIO_TEST_SRC_WAVE_WHITE_NOISE,
GST_AUDIO_TEST_SRC_WAVE_PINK_NOISE,
GST_AUDIO_TEST_SRC_WAVE_SINE_TAB,
GST_AUDIO_TEST_SRC_WAVE_TICKS,
GST_AUDIO_TEST_SRC_WAVE_GAUSSIAN_WHITE_NOISE,
GST_AUDIO_TEST_SRC_WAVE_RED_NOISE,
GST_AUDIO_TEST_SRC_WAVE_BLUE_NOISE,
GST_AUDIO_TEST_SRC_WAVE_VIOLET_NOISE,
_GST_AUDIO_TEST_SRC_WAVE_LAST
} GstAudioTestSrcWave;
GST_START_TEST (test_layout)
{
GstHarness *interleavedsrc, *plannarsrc;
GObjectClass *oclass;
GParamSpec *property;
GEnumValue *values;
guint i, j;
interleavedsrc = gst_harness_new_with_templates ("audiotestsrc", NULL,
&sinktemplate_interleaved);
plannarsrc = gst_harness_new_with_templates ("audiotestsrc", NULL,
&sinktemplate_plannar);
gst_harness_use_testclock (interleavedsrc);
gst_harness_use_testclock (plannarsrc);
g_object_set (interleavedsrc->element, "is-live", TRUE, NULL);
g_object_set (plannarsrc->element, "is-live", TRUE, NULL);
oclass = G_OBJECT_GET_CLASS (interleavedsrc->element);
property = g_object_class_find_property (oclass, "wave");
fail_unless (G_IS_PARAM_SPEC_ENUM (property));
values = G_ENUM_CLASS (g_type_class_ref (property->value_type))->values;
for (j = 0; values[j].value_name; j++) {
/* these produce random values by definition,
* so we can't compare channels */
switch (j) {
case GST_AUDIO_TEST_SRC_WAVE_WHITE_NOISE:
case GST_AUDIO_TEST_SRC_WAVE_PINK_NOISE:
case GST_AUDIO_TEST_SRC_WAVE_GAUSSIAN_WHITE_NOISE:
case GST_AUDIO_TEST_SRC_WAVE_RED_NOISE:
case GST_AUDIO_TEST_SRC_WAVE_BLUE_NOISE:
case GST_AUDIO_TEST_SRC_WAVE_VIOLET_NOISE:
continue;
default:
break;
}
GST_DEBUG ("layout test with wave %s", values[j].value_name);
g_object_set (interleavedsrc->element, "wave", values[j].value, NULL);