...
 
......@@ -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;
}
......
......@@ -324,7 +324,7 @@ gst_audio_meta_free (GstMeta * meta, GstBuffer * buffer)
{
GstAudioMeta *ameta = (GstAudioMeta *) meta;
if (ameta->offsets)
if (ameta->offsets && ameta->offsets != ameta->priv_offsets_arr)
g_slice_free1 (ameta->channels * sizeof (gsize), ameta->offsets);
}
......@@ -349,6 +349,30 @@ gst_audio_meta_transform (GstBuffer * dest, GstMeta * meta,
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, gint samples, gsize offsets[])
......@@ -367,7 +391,10 @@ gst_buffer_add_audio_meta (GstBuffer * buffer, GstAudioFormat format,
meta->samples = samples;
if (layout == GST_AUDIO_LAYOUT_NON_INTERLEAVED) {
meta->offsets = g_slice_alloc (channels * sizeof (gsize));
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++)
......@@ -384,6 +411,23 @@ gst_buffer_add_audio_meta (GstBuffer * buffer, GstAudioFormat format,
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)
......@@ -400,7 +444,7 @@ gst_audio_buffer_map (GstBuffer * buffer, GstAudioMapInfo * info,
info->n_planes = 1;
info->plane_size = info->map_info.size;
info->planes = g_slice_alloc (info->n_planes * sizeof (gpointer));
info->planes = info->priv_planes_arr;
info->planes[0] = info->map_info.data;
} else {
/* non-interleaved */
......@@ -418,7 +462,11 @@ gst_audio_buffer_map (GstBuffer * buffer, GstAudioMapInfo * info,
return FALSE;
}
info->planes = g_slice_alloc (info->n_planes * sizeof (gpointer));
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];
}
......@@ -426,11 +474,22 @@ gst_audio_buffer_map (GstBuffer * buffer, GstAudioMapInfo * info,
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);
g_slice_free1 (info->n_planes * sizeof (gpointer), info->planes);
if (info->planes != info->priv_planes_arr)
g_slice_free1 (info->n_planes * sizeof (gpointer), info->planes);
memset (info, 0, sizeof (GstAudioMapInfo));
}
......
......@@ -131,6 +131,22 @@ GstAudioClippingMeta * gst_buffer_add_audio_clipping_meta (GstBuffer *buffer,
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;
......@@ -139,9 +155,36 @@ typedef struct {
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;
......@@ -153,6 +196,7 @@ struct _GstAudioMeta {
gsize *offsets;
/*< private >*/
gsize priv_offsets_arr[8];
gpointer _gst_reserved[GST_PADDING];
};
......
......@@ -847,6 +847,7 @@ typedef struct
gint channels;
GstAudioChannelPosition from[32], to[32];
gint32 in[32], out[32];
gint32 plane_offsets[32];
gboolean fail;
} MultichannelReorderData;
......@@ -858,12 +859,14 @@ GST_START_TEST (test_multichannel_reorder)
{GST_AUDIO_CHANNEL_POSITION_MONO},
{0, 1, 2, 3},
{0, 1, 2, 3},
{0},
FALSE},
{1,
{GST_AUDIO_CHANNEL_POSITION_MONO},
{GST_AUDIO_CHANNEL_POSITION_FRONT_CENTER},
{0, 1, 2, 3},
{0, 1, 2, 3},
{0},
TRUE},
{2,
{GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT,
......@@ -872,6 +875,7 @@ GST_START_TEST (test_multichannel_reorder)
GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT},
{0, 1, 2, 3},
{0, 1, 2, 3},
{0, 1},
FALSE},
{2,
{GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT,
......@@ -880,6 +884,7 @@ GST_START_TEST (test_multichannel_reorder)
GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT},
{0, 1, 2, 3},
{1, 0, 3, 2},
{1, 0},
FALSE},
{4,
{GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT,
......@@ -892,6 +897,7 @@ GST_START_TEST (test_multichannel_reorder)
GST_AUDIO_CHANNEL_POSITION_REAR_CENTER},
{0, 1, 2, 3},
{1, 2, 0, 3},
{1, 2, 0, 3},
FALSE},
{4,
{GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT,
......@@ -904,6 +910,7 @@ GST_START_TEST (test_multichannel_reorder)
GST_AUDIO_CHANNEL_POSITION_FRONT_CENTER},
{0, 1, 2, 3},
{3, 0, 1, 2},
{3, 0, 1, 2},
FALSE},
{4,
{GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT,
......@@ -916,11 +923,13 @@ GST_START_TEST (test_multichannel_reorder)
GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT},
{0, 1, 2, 3},
{3, 2, 1, 0},
{3, 2, 1, 0},
FALSE},
};
gint i;
gint i, j;
GstBuffer *buf;
GstMapInfo map;
GstAudioMapInfo amap;
for (i = 0; i < G_N_ELEMENTS (tests); i++) {
buf =
......@@ -931,6 +940,8 @@ GST_START_TEST (test_multichannel_reorder)
fail_if (gst_audio_buffer_reorder_channels (buf, GST_AUDIO_FORMAT_S32,
tests[i].channels, tests[i].from, tests[i].to));
} else {
/* first interpret as interleaved */
fail_unless (gst_audio_buffer_reorder_channels (buf, GST_AUDIO_FORMAT_S32,
tests[i].channels, tests[i].from, tests[i].to));
......@@ -938,6 +949,24 @@ GST_START_TEST (test_multichannel_reorder)
fail_unless_equals_int (map.size, sizeof (tests[i].in));
fail_unless (memcmp (tests[i].out, map.data, map.size) == 0);
gst_buffer_unmap (buf, &map);
/* now interpret as planar */
gst_buffer_add_audio_meta (buf, GST_AUDIO_FORMAT_S32,
GST_AUDIO_LAYOUT_NON_INTERLEAVED, tests[i].channels,
sizeof (tests[i].in) / (tests[i].channels * sizeof (gint32)), NULL);
fail_unless (gst_audio_buffer_reorder_channels (buf, GST_AUDIO_FORMAT_S32,
tests[i].channels, tests[i].from, tests[i].to));
fail_unless (gst_audio_buffer_map (buf, &amap, GST_MAP_READ));
fail_unless_equals_int (amap.n_planes, tests[i].channels);
fail_unless_equals_int (amap.plane_size,
sizeof (tests[i].in) / tests[i].channels);
for (j = 0; j < amap.n_planes; j++) {
fail_unless_equals_pointer (amap.planes[j],
amap.map_info.data + tests[i].plane_offsets[j] * amap.plane_size);
}
}
gst_buffer_unref (buf);
}
......