...
 
Commits (21)
This diff is collapsed.
...@@ -196,6 +196,8 @@ check_valid_channel_positions (const GstAudioChannelPosition * position, ...@@ -196,6 +196,8 @@ check_valid_channel_positions (const GstAudioChannelPosition * position,
* positions @to. @from and @to must contain the same number of * positions @to. @from and @to must contain the same number of
* positions and the same positions, only in a different order. * 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. * Returns: %TRUE if the reordering was possible.
*/ */
gboolean gboolean
...@@ -248,6 +250,31 @@ gst_audio_reorder_channels (gpointer data, gsize size, GstAudioFormat format, ...@@ -248,6 +250,31 @@ gst_audio_reorder_channels (gpointer data, gsize size, GstAudioFormat format,
return TRUE; 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: * gst_audio_buffer_reorder_channels:
* @buffer: The buffer to reorder. * @buffer: The buffer to reorder.
...@@ -269,7 +296,8 @@ gst_audio_buffer_reorder_channels (GstBuffer * buffer, ...@@ -269,7 +296,8 @@ gst_audio_buffer_reorder_channels (GstBuffer * buffer,
const GstAudioChannelPosition * from, const GstAudioChannelPosition * to) const GstAudioChannelPosition * from, const GstAudioChannelPosition * to)
{ {
GstMapInfo info; 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_IS_BUFFER (buffer), FALSE);
g_return_val_if_fail (gst_buffer_is_writable (buffer), FALSE); g_return_val_if_fail (gst_buffer_is_writable (buffer), FALSE);
...@@ -277,15 +305,20 @@ gst_audio_buffer_reorder_channels (GstBuffer * buffer, ...@@ -277,15 +305,20 @@ gst_audio_buffer_reorder_channels (GstBuffer * buffer,
if (gst_audio_channel_positions_equal (from, to, channels)) if (gst_audio_channel_positions_equal (from, to, channels))
return TRUE; return TRUE;
if (!gst_buffer_map (buffer, &info, GST_MAP_READWRITE)) meta = gst_buffer_get_audio_meta (buffer);
return FALSE; if (meta && meta->layout == GST_AUDIO_LAYOUT_NON_INTERLEAVED) {
g_return_val_if_fail (channels == meta->channels, FALSE);
ret = ret = gst_audio_meta_reorder_channels (meta, from, to);
gst_audio_reorder_channels (info.data, info.size, format, channels, from, } else {
to); 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; return ret;
} }
......
...@@ -131,6 +131,11 @@ struct _GstAudioConverter ...@@ -131,6 +131,11 @@ struct _GstAudioConverter
/* quant */ /* quant */
GstAudioQuantize *quant; GstAudioQuantize *quant;
/* change layout */
GstAudioFormat chlayout_format;
GstAudioLayout chlayout_target;
gint chlayout_channels;
/* pack */ /* pack */
gboolean out_default; gboolean out_default;
AudioChain *chain_end; /* NULL for empty chain or points to the last element in the chain */ 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) ...@@ -567,6 +572,110 @@ do_quantize (AudioChain * chain, gpointer user_data)
return TRUE; 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 static gboolean
is_intermediate_format (GstAudioFormat format) is_intermediate_format (GstAudioFormat format)
{ {
...@@ -704,9 +813,16 @@ chain_mix (GstAudioConverter * convert, AudioChain * prev) ...@@ -704,9 +813,16 @@ chain_mix (GstAudioConverter * convert, AudioChain * prev)
GstAudioInfo *out = &convert->out; GstAudioInfo *out = &convert->out;
GstAudioFormat format = convert->current_format; GstAudioFormat format = convert->current_format;
const GValue *opt_matrix = GET_OPT_MIX_MATRIX (convert); const GValue *opt_matrix = GET_OPT_MIX_MATRIX (convert);
GstAudioChannelMixerFlags flags = 0;
convert->current_channels = out->channels; 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) { if (opt_matrix) {
gfloat **matrix = NULL; gfloat **matrix = NULL;
...@@ -715,12 +831,10 @@ chain_mix (GstAudioConverter * convert, AudioChain * prev) ...@@ -715,12 +831,10 @@ chain_mix (GstAudioConverter * convert, AudioChain * prev)
mix_matrix_from_g_value (in->channels, out->channels, opt_matrix); mix_matrix_from_g_value (in->channels, out->channels, opt_matrix);
convert->mix = 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); out->channels, matrix);
} else { } else {
GstAudioChannelMixerFlags flags; flags |=
flags =
GST_AUDIO_INFO_IS_UNPOSITIONED (in) ? GST_AUDIO_INFO_IS_UNPOSITIONED (in) ?
GST_AUDIO_CHANNEL_MIXER_FLAGS_UNPOSITIONED_IN : 0; GST_AUDIO_CHANNEL_MIXER_FLAGS_UNPOSITIONED_IN : 0;
flags |= flags |=
...@@ -766,8 +880,13 @@ chain_resample (GstAudioConverter * convert, AudioChain * prev) ...@@ -766,8 +880,13 @@ chain_resample (GstAudioConverter * convert, AudioChain * prev)
flags = 0; flags = 0;
if (convert->current_layout == GST_AUDIO_LAYOUT_NON_INTERLEAVED) { if (convert->current_layout == GST_AUDIO_LAYOUT_NON_INTERLEAVED) {
flags |= GST_AUDIO_RESAMPLER_FLAG_NON_INTERLEAVED_IN; 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; flags |= GST_AUDIO_RESAMPLER_FLAG_NON_INTERLEAVED_OUT;
} }
convert->current_layout = out->layout;
if (variable_rate) if (variable_rate)
flags |= GST_AUDIO_RESAMPLER_FLAG_VARIABLE_RATE; flags |= GST_AUDIO_RESAMPLER_FLAG_VARIABLE_RATE;
...@@ -860,6 +979,29 @@ chain_quantize (GstAudioConverter * convert, AudioChain * prev) ...@@ -860,6 +979,29 @@ chain_quantize (GstAudioConverter * convert, AudioChain * prev)
return 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 * static AudioChain *
chain_pack (GstAudioConverter * convert, AudioChain * prev) chain_pack (GstAudioConverter * convert, AudioChain * prev)
{ {
...@@ -1169,8 +1311,6 @@ gst_audio_converter_new (GstAudioConverterFlags flags, GstAudioInfo * in_info, ...@@ -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 (in_info != NULL, FALSE);
g_return_val_if_fail (out_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) if (config)
opt_matrix = opt_matrix =
...@@ -1211,7 +1351,9 @@ gst_audio_converter_new (GstAudioConverterFlags flags, GstAudioInfo * in_info, ...@@ -1211,7 +1351,9 @@ gst_audio_converter_new (GstAudioConverterFlags flags, GstAudioInfo * in_info,
prev = chain_convert_out (convert, prev); prev = chain_convert_out (convert, prev);
/* step 6, optional quantize */ /* step 6, optional quantize */
prev = chain_quantize (convert, prev); 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->chain_end = chain_pack (convert, prev);
convert->convert = converter_generic; convert->convert = converter_generic;
...@@ -1221,10 +1363,12 @@ gst_audio_converter_new (GstAudioConverterFlags flags, GstAudioInfo * in_info, ...@@ -1221,10 +1363,12 @@ gst_audio_converter_new (GstAudioConverterFlags flags, GstAudioInfo * in_info,
if (convert->mix_passthrough) { if (convert->mix_passthrough) {
if (out_info->finfo->format == in_info->finfo->format) { if (out_info->finfo->format == in_info->finfo->format) {
if (convert->resampler == NULL) { if (convert->resampler == NULL) {
GST_INFO if (out_info->layout == in_info->layout) {
("same formats, no resampler and passthrough mixing -> passthrough"); GST_INFO ("same formats, same layout, no resampler and "
convert->convert = converter_passthrough; "passthrough mixing -> passthrough");
convert->in_place = TRUE; convert->convert = converter_passthrough;
convert->in_place = TRUE;
}
} else { } else {
if (is_intermediate_format (in_info->finfo->format)) { if (is_intermediate_format (in_info->finfo->format)) {
GST_INFO ("same formats, and passthrough mixing -> only resampling"); GST_INFO ("same formats, and passthrough mixing -> only resampling");
...@@ -1233,7 +1377,7 @@ gst_audio_converter_new (GstAudioConverterFlags flags, GstAudioInfo * in_info, ...@@ -1233,7 +1377,7 @@ gst_audio_converter_new (GstAudioConverterFlags flags, GstAudioInfo * in_info,
} }
} else if (GST_AUDIO_FORMAT_IS_ENDIAN_CONVERSION (out_info->finfo, } else if (GST_AUDIO_FORMAT_IS_ENDIAN_CONVERSION (out_info->finfo,
in_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"); GST_INFO ("no resampler, passthrough mixing -> only endian conversion");
convert->convert = converter_endian; convert->convert = converter_endian;
convert->in_place = TRUE; convert->in_place = TRUE;
......
...@@ -911,6 +911,23 @@ static DeinterleaveFunc deinterleave_funcs[] = { ...@@ -911,6 +911,23 @@ static DeinterleaveFunc deinterleave_funcs[] = {
deinterleave_gdouble 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 static void
calculate_kaiser_params (GstAudioResampler * resampler) calculate_kaiser_params (GstAudioResampler * resampler)
{ {
...@@ -1332,7 +1349,7 @@ gst_audio_resampler_new (GstAudioResamplerMethod method, ...@@ -1332,7 +1349,7 @@ gst_audio_resampler_new (GstAudioResamplerMethod method,
GstAudioFormat format, gint channels, GstAudioFormat format, gint channels,
gint in_rate, gint out_rate, GstStructure * options) gint in_rate, gint out_rate, GstStructure * options)
{ {
gboolean non_interleaved; gboolean non_interleaved_in, non_interleaved_out;
GstAudioResampler *resampler; GstAudioResampler *resampler;
const GstAudioFormatInfo *info; const GstAudioFormatInfo *info;
GstStructure *def_options = NULL; GstStructure *def_options = NULL;
...@@ -1376,14 +1393,17 @@ gst_audio_resampler_new (GstAudioResamplerMethod method, ...@@ -1376,14 +1393,17 @@ gst_audio_resampler_new (GstAudioResamplerMethod method,
resampler->bps = GST_AUDIO_FORMAT_INFO_WIDTH (info) / 8; resampler->bps = GST_AUDIO_FORMAT_INFO_WIDTH (info) / 8;
resampler->sbuf = g_malloc0 (sizeof (gpointer) * channels); 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); (resampler->flags & GST_AUDIO_RESAMPLER_FLAG_NON_INTERLEAVED_OUT);
/* we resample each channel separately */ /* we resample each channel separately */
resampler->blocks = resampler->channels; resampler->blocks = resampler->channels;
resampler->inc = 1; resampler->inc = 1;
resampler->ostride = non_interleaved ? 1 : resampler->channels; resampler->ostride = non_interleaved_out ? 1 : resampler->channels;
resampler->deinterleave = deinterleave_funcs[resampler->format_index]; resampler->deinterleave = non_interleaved_in ?
copy_func : deinterleave_funcs[resampler->format_index];
resampler->convert_taps = convert_taps_funcs[resampler->format_index]; resampler->convert_taps = convert_taps_funcs[resampler->format_index];
GST_DEBUG ("method %d, bps %d, channels %d", method, resampler->bps, GST_DEBUG ("method %d, bps %d, channels %d", method, resampler->bps,
......
...@@ -80,6 +80,7 @@ gst_audio_buffer_clip (GstBuffer * buffer, const GstSegment * segment, ...@@ -80,6 +80,7 @@ gst_audio_buffer_clip (GstBuffer * buffer, const GstSegment * segment,
gint rate, gint bpf) gint rate, gint bpf)
{ {
GstBuffer *ret; GstBuffer *ret;
GstAudioMeta *meta;
GstClockTime timestamp = GST_CLOCK_TIME_NONE, duration = GST_CLOCK_TIME_NONE; GstClockTime timestamp = GST_CLOCK_TIME_NONE, duration = GST_CLOCK_TIME_NONE;
guint64 offset = GST_BUFFER_OFFSET_NONE, offset_end = GST_BUFFER_OFFSET_NONE; guint64 offset = GST_BUFFER_OFFSET_NONE, offset_end = GST_BUFFER_OFFSET_NONE;
gsize trim, size, osize; gsize trim, size, osize;
...@@ -98,8 +99,11 @@ gst_audio_buffer_clip (GstBuffer * buffer, const GstSegment * segment, ...@@ -98,8 +99,11 @@ gst_audio_buffer_clip (GstBuffer * buffer, const GstSegment * segment,
* Calculate the missing values for the calculations, * Calculate the missing values for the calculations,
* they won't be changed later though. */ * they won't be changed later though. */
meta = gst_buffer_get_audio_meta (buffer);
/* these variables measure samples */
trim = 0; 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 */ /* no data, nothing to clip */
if (!size) if (!size)
...@@ -111,7 +115,7 @@ gst_audio_buffer_clip (GstBuffer * buffer, const GstSegment * segment, ...@@ -111,7 +115,7 @@ gst_audio_buffer_clip (GstBuffer * buffer, const GstSegment * segment,
duration = GST_BUFFER_DURATION (buffer); duration = GST_BUFFER_DURATION (buffer);
} else { } else {
change_duration = FALSE; 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)) { if (GST_BUFFER_OFFSET_IS_VALID (buffer)) {
...@@ -125,7 +129,7 @@ gst_audio_buffer_clip (GstBuffer * buffer, const GstSegment * segment, ...@@ -125,7 +129,7 @@ gst_audio_buffer_clip (GstBuffer * buffer, const GstSegment * segment,
offset_end = GST_BUFFER_OFFSET_END (buffer); offset_end = GST_BUFFER_OFFSET_END (buffer);
} else { } else {
change_offset_end = FALSE; change_offset_end = FALSE;
offset_end = offset + size / bpf; offset_end = offset + size;
} }
if (segment->format == GST_FORMAT_TIME) { if (segment->format == GST_FORMAT_TIME) {
...@@ -149,8 +153,8 @@ gst_audio_buffer_clip (GstBuffer * buffer, const GstSegment * segment, ...@@ -149,8 +153,8 @@ gst_audio_buffer_clip (GstBuffer * buffer, const GstSegment * segment,
diff = gst_util_uint64_scale (diff, rate, GST_SECOND); diff = gst_util_uint64_scale (diff, rate, GST_SECOND);
if (change_offset) if (change_offset)
offset += diff; offset += diff;
trim += diff * bpf; trim += diff;
size -= diff * bpf; size -= diff;
} }
diff = stop - cstop; diff = stop - cstop;
...@@ -161,7 +165,7 @@ gst_audio_buffer_clip (GstBuffer * buffer, const GstSegment * segment, ...@@ -161,7 +165,7 @@ gst_audio_buffer_clip (GstBuffer * buffer, const GstSegment * segment,
diff = gst_util_uint64_scale (diff, rate, GST_SECOND); diff = gst_util_uint64_scale (diff, rate, GST_SECOND);
if (change_offset_end) if (change_offset_end)
offset_end -= diff; offset_end -= diff;
size -= diff * bpf; size -= diff;
} }
} else { } else {
gst_buffer_unref (buffer); gst_buffer_unref (buffer);
...@@ -188,8 +192,8 @@ gst_audio_buffer_clip (GstBuffer * buffer, const GstSegment * segment, ...@@ -188,8 +192,8 @@ gst_audio_buffer_clip (GstBuffer * buffer, const GstSegment * segment,
if (change_duration) if (change_duration)
duration -= gst_util_uint64_scale (diff, GST_SECOND, rate); duration -= gst_util_uint64_scale (diff, GST_SECOND, rate);
trim += diff * bpf; trim += diff;
size -= diff * bpf; size -= diff;
} }
diff = stop - cstop; diff = stop - cstop;
...@@ -199,7 +203,7 @@ gst_audio_buffer_clip (GstBuffer * buffer, const GstSegment * segment, ...@@ -199,7 +203,7 @@ gst_audio_buffer_clip (GstBuffer * buffer, const GstSegment * segment,
if (change_duration) if (change_duration)
duration -= gst_util_uint64_scale (diff, GST_SECOND, rate); duration -= gst_util_uint64_scale (diff, GST_SECOND, rate);
size -= diff * bpf; size -= diff;
} }
} else { } else {
gst_buffer_unref (buffer); gst_buffer_unref (buffer);
...@@ -219,10 +223,9 @@ gst_audio_buffer_clip (GstBuffer * buffer, const GstSegment * segment, ...@@ -219,10 +223,9 @@ gst_audio_buffer_clip (GstBuffer * buffer, const GstSegment * segment,
GST_BUFFER_DURATION (ret) = duration; GST_BUFFER_DURATION (ret) = duration;
} }
} else { } 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); GST_DEBUG ("trim %" G_GSIZE_FORMAT " size %" G_GSIZE_FORMAT, trim, size);
ret = gst_buffer_copy_region (buffer, GST_BUFFER_COPY_ALL, trim, size); ret = gst_audio_buffer_truncate (buffer, bpf, trim, size);
gst_buffer_unref (buffer);
GST_DEBUG ("timestamp %" GST_TIME_FORMAT, GST_TIME_ARGS (timestamp)); GST_DEBUG ("timestamp %" GST_TIME_FORMAT, GST_TIME_ARGS (timestamp));
if (ret) { if (ret) {
...@@ -235,8 +238,76 @@ gst_audio_buffer_clip (GstBuffer * buffer, const GstSegment * segment, ...@@ -235,8 +238,76 @@ gst_audio_buffer_clip (GstBuffer * buffer, const GstSegment * segment,
if (change_offset_end) if (change_offset_end)
GST_BUFFER_OFFSET_END (ret) = offset_end; GST_BUFFER_OFFSET_END (ret) = offset_end;
} else { } else {
GST_ERROR ("copy_region failed"); GST_ERROR ("gst_audio_buffer_truncate failed");
} }
} }
return ret; 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, ...@@ -94,6 +94,9 @@ GstBuffer * gst_audio_buffer_clip (GstBuffer *buffer,
const GstSegment *segment, const GstSegment *segment,
gint rate, gint bpf); gint rate, gint bpf);
GST_EXPORT
GstBuffer * gst_audio_buffer_truncate (GstBuffer *buffer,
gint bpf, gsize trim, gsize samples);
G_END_DECLS G_END_DECLS
......
...@@ -93,7 +93,10 @@ ...@@ -93,7 +93,10 @@
* *
* In non-live pipelines, baseclass can also (configurably) arrange for * In non-live pipelines, baseclass can also (configurably) arrange for
* output buffer aggregation which may help to redue large(r) numbers of * 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 * On the other hand, it should be noted that baseclass only provides limited
* seeking support (upon explicit subclass request), as full-fledged support * seeking support (upon explicit subclass request), as full-fledged support
...@@ -985,7 +988,8 @@ gst_audio_decoder_output (GstAudioDecoder * dec, GstBuffer * buf) ...@@ -985,7 +988,8 @@ gst_audio_decoder_output (GstAudioDecoder * dec, GstBuffer * buf)
again: again:
inbuf = NULL; 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; gint av;
gboolean assemble = FALSE; gboolean assemble = FALSE;
const GstClockTimeDiff tol = 10 * GST_MSECOND; const GstClockTimeDiff tol = 10 * GST_MSECOND;
...@@ -1202,9 +1206,9 @@ gst_audio_decoder_finish_frame (GstAudioDecoder * dec, GstBuffer * buf, ...@@ -1202,9 +1206,9 @@ gst_audio_decoder_finish_frame (GstAudioDecoder * dec, GstBuffer * buf,
GstAudioDecoderPrivate *priv; GstAudioDecoderPrivate *priv;
GstAudioDecoderContext *ctx; GstAudioDecoderContext *ctx;
GstAudioDecoderClass *klass = GST_AUDIO_DECODER_GET_CLASS (dec); GstAudioDecoderClass *klass = GST_AUDIO_DECODER_GET_CLASS (dec);
gint samples = 0; GstAudioMeta *meta;
GstClockTime ts, next_ts; GstClockTime ts, next_ts;
gsize size; gsize size, samples = 0;
GstFlowReturn ret = GST_FLOW_OK; GstFlowReturn ret = GST_FLOW_OK;
GQueue inbufs = G_QUEUE_INIT; GQueue inbufs = G_QUEUE_INIT;
...@@ -1216,7 +1220,9 @@ gst_audio_decoder_finish_frame (GstAudioDecoder * dec, GstBuffer * buf, ...@@ -1216,7 +1220,9 @@ gst_audio_decoder_finish_frame (GstAudioDecoder * dec, GstBuffer * buf,
priv = dec->priv; priv = dec->priv;
ctx = &dec->priv->ctx; ctx = &dec->priv->ctx;
meta = buf ? gst_buffer_get_audio_meta (buf) : NULL;
size = buf ? gst_buffer_get_size (buf) : 0; 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 */ /* must know the output format by now */
g_return_val_if_fail (buf == NULL || GST_AUDIO_INFO_IS_VALID (&ctx->info), 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, ...@@ -1224,8 +1230,7 @@ gst_audio_decoder_finish_frame (GstAudioDecoder * dec, GstBuffer * buf,
GST_LOG_OBJECT (dec, GST_LOG_OBJECT (dec,
"accepting %" G_GSIZE_FORMAT " bytes == %" G_GSIZE_FORMAT "accepting %" G_GSIZE_FORMAT " bytes == %" G_GSIZE_FORMAT
" samples for %d frames", buf ? size : -1, " samples for %d frames", buf ? size : 0, samples, frames);
buf ? size / ctx->info.bpf : -1, frames);
GST_AUDIO_DECODER_STREAM_LOCK (dec); GST_AUDIO_DECODER_STREAM_LOCK (dec);
...@@ -1240,12 +1245,20 @@ gst_audio_decoder_finish_frame (GstAudioDecoder * dec, GstBuffer * buf, ...@@ -1240,12 +1245,20 @@ gst_audio_decoder_finish_frame (GstAudioDecoder * dec, GstBuffer * buf,
send_pending_events (dec); send_pending_events (dec);
} }
/* output shoud be whole number of sample frames */ /* sanity checking */
if (G_LIKELY (buf && ctx->info.bpf)) { if (G_LIKELY (buf && ctx->info.bpf)) {
if (size % ctx->info.bpf) if (!meta || meta->layout == GST_AUDIO_LAYOUT_INTERLEAVED) {
goto wrong_buffer; /* output shoud be whole number of sample frames */
/* per channel least */ if (size % ctx->info.bpf)
samples = 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 */ /* frame and ts book-keeping */
...@@ -1397,6 +1410,16 @@ wrong_buffer: ...@@ -1397,6 +1410,16 @@ wrong_buffer:
ret = GST_FLOW_ERROR; ret = GST_FLOW_ERROR;
goto exit; 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 static GstFlowReturn
......
...@@ -1181,9 +1181,10 @@ gst_audio_encoder_chain (GstPad * pad, GstObject * parent, GstBuffer * buffer) ...@@ -1181,9 +1181,10 @@ gst_audio_encoder_chain (GstPad * pad, GstObject * parent, GstBuffer * buffer)
GstAudioEncoder *enc; GstAudioEncoder *enc;
GstAudioEncoderPrivate *priv; GstAudioEncoderPrivate *priv;
GstAudioEncoderContext *ctx; GstAudioEncoderContext *ctx;
GstAudioMeta *meta;
GstFlowReturn ret = GST_FLOW_OK; GstFlowReturn ret = GST_FLOW_OK;
gboolean discont; gboolean discont;
gsize size; gsize size, samples;
enc = GST_AUDIO_ENCODER (parent); enc = GST_AUDIO_ENCODER (parent);
...@@ -1208,6 +1209,7 @@ gst_audio_encoder_chain (GstPad * pad, GstObject * parent, GstBuffer * buffer) ...@@ -1208,6 +1209,7 @@ gst_audio_encoder_chain (GstPad * pad, GstObject * parent, GstBuffer * buffer)
if (!ctx->info.bpf) if (!ctx->info.bpf)
goto not_negotiated; goto not_negotiated;
meta = gst_buffer_get_audio_meta (buffer);
size = gst_buffer_get_size (buffer); size = gst_buffer_get_size (buffer);
GST_LOG_OBJECT (enc, GST_LOG_OBJECT (enc,
...@@ -1216,9 +1218,17 @@ gst_audio_encoder_chain (GstPad * pad, GstObject * parent, GstBuffer * buffer) ...@@ -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_TIMESTAMP (buffer)),
GST_TIME_ARGS (GST_BUFFER_DURATION (buffer))); GST_TIME_ARGS (GST_BUFFER_DURATION (buffer)));
/* input shoud be whole number of sample frames */ if (!meta || meta->layout == GST_AUDIO_LAYOUT_INTERLEAVED) {
if (size % ctx->info.bpf) /* input should be whole number of sample frames */
goto wrong_buffer; 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 #ifndef GST_DISABLE_GST_DEBUG
{ {
...@@ -1226,8 +1236,7 @@ gst_audio_encoder_chain (GstPad * pad, GstObject * parent, GstBuffer * buffer) ...@@ -1226,8 +1236,7 @@ gst_audio_encoder_chain (GstPad * pad, GstObject * parent, GstBuffer * buffer)
GstClockTimeDiff diff; GstClockTimeDiff diff;
/* verify buffer duration */ /* verify buffer duration */
duration = gst_util_uint64_scale (size, GST_SECOND, duration = gst_util_uint64_scale (samples, GST_SECOND, ctx->info.rate);
ctx->info.rate * ctx->info.bpf);
diff = GST_CLOCK_DIFF (duration, GST_BUFFER_DURATION (buffer)); diff = GST_CLOCK_DIFF (duration, GST_BUFFER_DURATION (buffer));
if (GST_BUFFER_DURATION (buffer) != GST_CLOCK_TIME_NONE && if (GST_BUFFER_DURATION (buffer) != GST_CLOCK_TIME_NONE &&
(diff > GST_SECOND / ctx->info.rate / 2 || (diff > GST_SECOND / ctx->info.rate / 2 ||
...@@ -1254,7 +1263,9 @@ gst_audio_encoder_chain (GstPad * pad, GstObject * parent, GstBuffer * buffer) ...@@ -1254,7 +1263,9 @@ gst_audio_encoder_chain (GstPad * pad, GstObject * parent, GstBuffer * buffer)
goto done; goto done;
} }
meta = gst_buffer_get_audio_meta (buffer);
size = gst_buffer_get_size (buffer); size = gst_buffer_get_size (buffer);
samples = meta ? meta->samples : size / ctx->info.bpf;
GST_LOG_OBJECT (enc, GST_LOG_OBJECT (enc,
"buffer after segment clipping has size %" G_GSIZE_FORMAT " with ts %" "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) ...@@ -1302,19 +1313,18 @@ gst_audio_encoder_chain (GstPad * pad, GstObject * parent, GstBuffer * buffer)
/* do some fancy tweaking in hard resync case */ /* do some fancy tweaking in hard resync case */
if (discont && enc->priv->hard_resync) { if (discont && enc->priv->hard_resync) {
if (diff < 0) { if (diff < 0) {
guint64 diff_bytes; guint64 diff_samples;
GST_WARNING_OBJECT (enc, "Buffer is older than expected ts %" GST_WARNING_OBJECT (enc, "Buffer is older than expected ts %"
GST_TIME_FORMAT ". Clipping buffer", GST_TIME_ARGS (next_ts)); GST_TIME_FORMAT ". Clipping buffer", GST_TIME_ARGS (next_ts));
diff_bytes = diff_samples = GST_CLOCK_TIME_TO_FRAMES (-diff, ctx->info.rate);
GST_CLOCK_TIME_TO_FRAMES (-diff, ctx->info.rate) * ctx->info.bpf; if (diff_samples >= samples) {
if (diff_bytes >= size) {
gst_buffer_unref (buffer); gst_buffer_unref (buffer);
goto done; goto done;
} }
buffer = gst_buffer_make_writable (buffer); buffer = gst_audio_buffer_truncate (buffer, ctx->info.bpf, diff_samples,
gst_buffer_resize (buffer, diff_bytes, size - diff_bytes); samples - diff_samples);
GST_BUFFER_TIMESTAMP (buffer) += diff; GST_BUFFER_TIMESTAMP (buffer) += diff;
/* care even less about duration after this */ /* care even less about duration after this */
...@@ -1364,6 +1374,16 @@ not_negotiated: ...@@ -1364,6 +1374,16 @@ not_negotiated:
ret = GST_FLOW_NOT_NEGOTIATED; ret = GST_FLOW_NOT_NEGOTIATED;
goto done; 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: wrong_buffer:
{ {
GST_ELEMENT_ERROR (enc, STREAM, ENCODE, (NULL), GST_ELEMENT_ERROR (enc, STREAM, ENCODE, (NULL),
......
...@@ -304,3 +304,226 @@ gst_audio_clipping_meta_get_info (void) ...@@ -304,3 +304,226 @@ gst_audio_clipping_meta_get_info (void)
} }
return audio_clipping_meta_info; 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, ...@@ -125,6 +125,104 @@ GstAudioClippingMeta * gst_buffer_add_audio_clipping_meta (GstBuffer *buffer,
guint64 start, guint64 start,
guint64 end); 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 G_END_DECLS
#endif /* __GST_AUDIO_META_H__ */ #endif /* __GST_AUDIO_META_H__ */
...@@ -167,11 +167,11 @@ enum ...@@ -167,11 +167,11 @@ enum
#if G_BYTE_ORDER == G_LITTLE_ENDIAN #if G_BYTE_ORDER == G_LITTLE_ENDIAN
#define CAPS \ #define CAPS \
GST_AUDIO_CAPS_MAKE ("{ S32LE, U32LE, S16LE, U16LE, S8, U8, F32LE, F64LE }") \ GST_AUDIO_CAPS_MAKE ("{ S32LE, U32LE, S16LE, U16LE, S8, U8, F32LE, F64LE }") \
", layout = (string) { interleaved, non-interleaved }" ", layout = (string) { interleaved }"
#else #else
#define CAPS \ #define CAPS \
GST_AUDIO_CAPS_MAKE ("{ S32BE, U32BE, S16BE, U16BE, S8, U8, F32BE, F64BE }") \ GST_AUDIO_CAPS_MAKE ("{ S32BE, U32BE, S16BE, U16BE, S8, U8, F32BE, F64BE }") \
", layout = (string) { interleaved, non-interleaved }" ", layout = (string) { interleaved }"
#endif #endif
static GstStaticPadTemplate gst_adder_src_template = static GstStaticPadTemplate gst_adder_src_template =
......
...@@ -169,7 +169,7 @@ G_DEFINE_TYPE_WITH_CODE (GstAudioConvert, gst_audio_convert, ...@@ -169,7 +169,7 @@ G_DEFINE_TYPE_WITH_CODE (GstAudioConvert, gst_audio_convert,
#define STATIC_CAPS \ #define STATIC_CAPS \
GST_STATIC_CAPS (GST_AUDIO_CAPS_MAKE (GST_AUDIO_FORMATS_ALL) \ 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 = static GstStaticPadTemplate gst_audio_convert_src_template =
GST_STATIC_PAD_TEMPLATE ("src", GST_STATIC_PAD_TEMPLATE ("src",
...@@ -307,6 +307,14 @@ remove_format_from_structure (GstCapsFeatures * features, ...@@ -307,6 +307,14 @@ remove_format_from_structure (GstCapsFeatures * features,
return TRUE; 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 static gboolean
remove_channels_from_structure (GstCapsFeatures * features, GstStructure * s, remove_channels_from_structure (GstCapsFeatures * features, GstStructure * s,
gpointer user_data) gpointer user_data)
...@@ -352,6 +360,7 @@ gst_audio_convert_transform_caps (GstBaseTransform * btrans, ...@@ -352,6 +360,7 @@ gst_audio_convert_transform_caps (GstBaseTransform * btrans,
tmp = gst_caps_copy (caps); tmp = gst_caps_copy (caps);
gst_caps_map_in_place (tmp, remove_format_from_structure, NULL); 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); gst_caps_map_in_place (tmp, remove_channels_from_structure, btrans);
/* We can infer the required input / output channels based on the /* We can infer the required input / output channels based on the
...@@ -792,14 +801,17 @@ gst_audio_convert_transform (GstBaseTransform * base, GstBuffer * inbuf, ...@@ -792,14 +801,17 @@ gst_audio_convert_transform (GstBaseTransform * base, GstBuffer * inbuf,
{ {
GstFlowReturn ret; GstFlowReturn ret;
GstAudioConvert *this = GST_AUDIO_CONVERT (base); GstAudioConvert *this = GST_AUDIO_CONVERT (base);
GstMapInfo srcmap = { NULL, }, dstmap; GstAudioMeta *meta;
GstAudioMapInfo srcmap, dstmap;
gint insize, outsize; gint insize, outsize;
gboolean inbuf_writable; gboolean inbuf_writable;
GstAudioConverterFlags flags; GstAudioConverterFlags flags;
gsize samples; gsize samples;
/* get amount of samples to convert. */ /* 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 /* get in/output sizes, to see if the buffers we got are of correct
* sizes */ * sizes */
...@@ -811,27 +823,32 @@ gst_audio_convert_transform (GstBaseTransform * base, GstBuffer * inbuf, ...@@ -811,27 +823,32 @@ gst_audio_convert_transform (GstBaseTransform * base, GstBuffer * inbuf,
gst_buffer_resize (outbuf, 0, outsize); 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 */ /* get src and dst data */
if (inbuf != outbuf) { if (inbuf != outbuf) {
inbuf_writable = gst_buffer_is_writable (inbuf) inbuf_writable = gst_buffer_is_writable (inbuf)
&& gst_buffer_n_memory (inbuf) == 1 && gst_buffer_n_memory (inbuf) == 1
&& gst_memory_is_writable (gst_buffer_peek_memory (inbuf, 0)); && 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)) inbuf_writable ? GST_MAP_READWRITE : GST_MAP_READ))
goto inmap_error; goto inmap_error;
} else { } else {
inbuf_writable = TRUE; 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; goto outmap_error;
/* check in and outsize */ /* check in and outsize */
if (inbuf != outbuf) { if (inbuf != outbuf) {
if (srcmap.size < insize) if (srcmap.n_planes * srcmap.plane_size < insize)
goto wrong_size; goto wrong_size;
} }
if (dstmap.size < outsize) if (dstmap.n_planes * dstmap.plane_size < outsize)
goto wrong_size; goto wrong_size;
/* and convert the samples */ /* and convert the samples */
...@@ -840,22 +857,24 @@ gst_audio_convert_transform (GstBaseTransform * base, GstBuffer * inbuf, ...@@ -840,22 +857,24 @@ gst_audio_convert_transform (GstBaseTransform * base, GstBuffer * inbuf,
flags |= GST_AUDIO_CONVERTER_FLAG_IN_WRITABLE; flags |= GST_AUDIO_CONVERTER_FLAG_IN_WRITABLE;
if (!GST_BUFFER_FLAG_IS_SET (inbuf, GST_BUFFER_FLAG_GAP)) { 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, 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; goto convert_error;
} else { } else {
/* Create silence buffer */ /* 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; ret = GST_FLOW_OK;
done: done:
gst_buffer_unmap (outbuf, &dstmap); gst_audio_buffer_unmap (outbuf, &dstmap);
if (inbuf != outbuf) if (inbuf != outbuf)
gst_buffer_unmap (inbuf, &srcmap); gst_audio_buffer_unmap (inbuf, &srcmap);
return ret; return ret;
...@@ -866,7 +885,8 @@ wrong_size: ...@@ -866,7 +885,8 @@ wrong_size:
(NULL), (NULL),
("input/output buffers are of wrong size in: %" G_GSIZE_FORMAT " < %d" ("input/output buffers are of wrong size in: %" G_GSIZE_FORMAT " < %d"
" or out: %" 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; ret = GST_FLOW_ERROR;
goto done; goto done;
} }
...@@ -888,7 +908,7 @@ outmap_error: ...@@ -888,7 +908,7 @@ outmap_error:
GST_ELEMENT_ERROR (this, STREAM, FORMAT, GST_ELEMENT_ERROR (this, STREAM, FORMAT,
(NULL), ("failed to map output buffer")); (NULL), ("failed to map output buffer"));
if (inbuf != outbuf) if (inbuf != outbuf)
gst_buffer_unmap (inbuf, &srcmap); gst_audio_buffer_unmap (inbuf, &srcmap);
return GST_FLOW_ERROR; return GST_FLOW_ERROR;
} }
} }
......
...@@ -439,6 +439,7 @@ gst_audio_rate_chain (GstPad * pad, GstObject * parent, GstBuffer * buf) ...@@ -439,6 +439,7 @@ gst_audio_rate_chain (GstPad * pad, GstObject * parent, GstBuffer * buf)
GstFlowReturn ret = GST_FLOW_OK; GstFlowReturn ret = GST_FLOW_OK;
GstClockTimeDiff diff; GstClockTimeDiff diff;
gint rate, bpf; gint rate, bpf;
GstAudioMeta *meta;
audiorate = GST_AUDIO_RATE (parent); audiorate = GST_AUDIO_RATE (parent);
...@@ -492,8 +493,9 @@ gst_audio_rate_chain (GstPad * pad, GstObject * parent, GstBuffer * buf) ...@@ -492,8 +493,9 @@ gst_audio_rate_chain (GstPad * pad, GstObject * parent, GstBuffer * buf)
in_time = audiorate->next_ts; in_time = audiorate->next_ts;
} }
meta = gst_buffer_get_audio_meta (buf);
in_size = gst_buffer_get_size (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; audiorate->in += in_samples;
/* calculate the buffer offset */ /* calculate the buffer offset */
...@@ -549,6 +551,11 @@ gst_audio_rate_chain (GstPad * pad, GstObject * parent, GstBuffer * buf) ...@@ -549,6 +551,11 @@ gst_audio_rate_chain (GstPad * pad, GstObject * parent, GstBuffer * buf)
fillmap.size); fillmap.size);
gst_buffer_unmap (fill, &fillmap); 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", GST_DEBUG_OBJECT (audiorate, "inserting %" G_GUINT64_FORMAT " samples",
cursamples); cursamples);
...@@ -591,7 +598,7 @@ gst_audio_rate_chain (GstPad * pad, GstObject * parent, GstBuffer * buf) ...@@ -591,7 +598,7 @@ gst_audio_rate_chain (GstPad * pad, GstObject * parent, GstBuffer * buf)
} else if (in_offset < audiorate->next_offset) { } else if (in_offset < audiorate->next_offset) {
/* need to remove samples */ /* need to remove samples */
if (in_offset_end <= audiorate->next_offset) { if (in_offset_end <= audiorate->next_offset) {
guint64 drop = in_size / bpf; guint64 drop = in_samples;
audiorate->drop += drop; audiorate->drop += drop;
...@@ -607,24 +614,16 @@ gst_audio_rate_chain (GstPad * pad, GstObject * parent, GstBuffer * buf) ...@@ -607,24 +614,16 @@ gst_audio_rate_chain (GstPad * pad, GstObject * parent, GstBuffer * buf)
goto beach; goto beach;
} else { } else {
guint64 truncsamples; guint64 truncsamples, leftsamples;
guint truncsize, leftsize;
GstBuffer *trunc;
/* truncate buffer */ /* truncate buffer */
truncsamples = audiorate->next_offset - in_offset; truncsamples = audiorate->next_offset - in_offset;
truncsize = truncsamples * bpf; leftsamples = in_samples - truncsamples;
leftsize = in_size - truncsize;
trunc = buf = gst_audio_buffer_truncate (buf, bpf, truncsamples, leftsamples);
gst_buffer_copy_region (buf, GST_BUFFER_COPY_ALL, truncsize,
leftsize);
gst_buffer_unref (buf);
buf = trunc;
audiorate->drop += truncsamples; audiorate->drop += truncsamples;
audiorate->out += (leftsize / bpf); audiorate->out += leftsamples;
GST_DEBUG_OBJECT (audiorate, "truncating %" G_GUINT64_FORMAT " samples", GST_DEBUG_OBJECT (audiorate, "truncating %" G_GUINT64_FORMAT " samples",
truncsamples); truncsamples);
......
...@@ -548,8 +548,7 @@ gst_audio_resample_push_drain (GstAudioResample * resample, guint history_len) ...@@ -548,8 +548,7 @@ gst_audio_resample_push_drain (GstAudioResample * resample, guint history_len)
GstFlowReturn res; GstFlowReturn res;
gint outsize; gint outsize;
gsize out_len; gsize out_len;
GstMapInfo map; GstAudioMapInfo map;
gpointer out[1];
g_assert (resample->converter != NULL); g_assert (resample->converter != NULL);
...@@ -565,13 +564,15 @@ gst_audio_resample_push_drain (GstAudioResample * resample, guint history_len) ...@@ -565,13 +564,15 @@ gst_audio_resample_push_drain (GstAudioResample * resample, guint history_len)
outsize = out_len * resample->in.bpf; outsize = out_len * resample->in.bpf;
outbuf = gst_buffer_new_and_alloc (outsize); 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, 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 */ /* time */
if (GST_CLOCK_TIME_IS_VALID (resample->t0)) { if (GST_CLOCK_TIME_IS_VALID (resample->t0)) {
...@@ -703,7 +704,7 @@ static GstFlowReturn ...@@ -703,7 +704,7 @@ static GstFlowReturn
gst_audio_resample_process (GstAudioResample * resample, GstBuffer * inbuf, gst_audio_resample_process (GstAudioResample * resample, GstBuffer * inbuf,
GstBuffer * outbuf) GstBuffer * outbuf)
{ {
GstMapInfo in_map, out_map; GstAudioMapInfo in_map, out_map;
gsize outsize; gsize outsize;
guint32 in_len; guint32 in_len;
guint32 out_len; guint32 out_len;
...@@ -711,16 +712,19 @@ gst_audio_resample_process (GstAudioResample * resample, GstBuffer * inbuf, ...@@ -711,16 +712,19 @@ gst_audio_resample_process (GstAudioResample * resample, GstBuffer * inbuf,
gst_audio_converter_get_max_latency (resample->converter) * 2; gst_audio_converter_get_max_latency (resample->converter) * 2;
gboolean inbuf_writable; gboolean inbuf_writable;
in_len = gst_buffer_get_size (inbuf) / resample->in.bpf;
out_len = gst_buffer_get_size (outbuf) / resample->out.bpf;