Commit e18bcef7 authored by George Kiagiadakis's avatar George Kiagiadakis

libs: audio: Implement GstAudioMeta, a meta to describe mapping properties of audio buffers

parent eacb7a77
......@@ -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;
  • Isn't all of this duplicative of the caps ? Maybe you could just embed a GstAudioInfo ?

  • format, layout and channels are duplicative, yes. The number of samples is not. I could add a GstAudioInfo, but that also contains other information that is useless here. I could also not add this information at all here and require the code that uses a GstAudioMeta to also have a GstAudioInfo around, but I feel this is bad practice because the number of channels, for example, is also the size of the offsets array, so they should be together. This last option would also require changing the signature of some methods like gst_audio_buffer_clip().

Please register or sign in to reply
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__ */
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment