_priv_gst_caps_features_cleanup ();
_priv_gst_caps_cleanup ();
+ _priv_gst_meta_cleanup ();
g_type_class_unref (g_type_class_peek (gst_object_get_type ()));
g_type_class_unref (g_type_class_peek (gst_pad_get_type ()));
G_GNUC_INTERNAL void _priv_gst_caps_features_cleanup (void);
G_GNUC_INTERNAL void _priv_gst_caps_cleanup (void);
G_GNUC_INTERNAL void _priv_gst_debug_cleanup (void);
+G_GNUC_INTERNAL void _priv_gst_meta_cleanup (void);
/* called from gst_task_cleanup_all(). */
G_GNUC_INTERNAL void _priv_gst_element_cleanup (void);
return meta_info;
}
+
+/**
+ * gst_buffer_add_custom_meta:
+ * @buffer: (transfer none): a #GstBuffer
+ * @name: the registered name of the desired custom meta
+ *
+ * Creates and adds a #GstCustomMeta for the desired @name. @name must have
+ * been successfully registered with gst_meta_register_custom().
+ *
+ * Returns: (transfer none) (nullable): The #GstCustomMeta that was added to the buffer
+ *
+ * Since: 1.20
+ */
+GstCustomMeta *
+gst_buffer_add_custom_meta (GstBuffer * buffer, const gchar * name)
+{
+ GstCustomMeta *meta;
+ const GstMetaInfo *info;
+
+ g_return_val_if_fail (name != NULL, NULL);
+ g_return_val_if_fail (GST_IS_BUFFER (buffer), NULL);
+
+ info = gst_meta_get_info (name);
+
+ if (info == NULL || !gst_meta_info_is_custom (info))
+ return NULL;
+
+ meta = (GstCustomMeta *) gst_buffer_add_meta (buffer, info, NULL);
+
+ return meta;
+}
+
+/**
+ * gst_buffer_get_custom_meta:
+ * @buffer: a #GstBuffer
+ * @name: the registered name of the custom meta to retrieve.
+ *
+ * Find the first #GstCustomMeta on @buffer for the desired @name.
+ *
+ * Returns: (transfer none) (nullable): the #GstCustomMeta or %NULL when there
+ * is no such metadata on @buffer.
+ *
+ * Since: 1.20
+ */
+GstCustomMeta *
+gst_buffer_get_custom_meta (GstBuffer * buffer, const gchar * name)
+{
+ const GstMetaInfo *info;
+
+ g_return_val_if_fail (buffer != NULL, NULL);
+ g_return_val_if_fail (name != NULL, NULL);
+
+ info = gst_meta_get_info (name);
+
+ if (!info)
+ return NULL;
+
+ if (!gst_meta_info_is_custom (info))
+ return NULL;
+
+ return (GstCustomMeta *) gst_buffer_get_meta (buffer, info->api);
+}
GstBufferForeachMetaFunc func,
gpointer user_data);
+GST_API
+GstCustomMeta * gst_buffer_add_custom_meta (GstBuffer *buffer,
+ const gchar *name);
+
+GST_API
+GstCustomMeta * gst_buffer_get_custom_meta (GstBuffer *buffer,
+ const gchar *name);
+
/**
* gst_value_set_buffer:
* @v: a #GValue to receive the data
GQuark _gst_meta_transform_copy;
GQuark _gst_meta_tag_memory;
+typedef struct
+{
+ GstCustomMeta meta;
+
+ GstStructure *structure;
+} GstCustomMetaImpl;
+
+typedef struct
+{
+ GstMetaInfo info;
+ GstCustomMetaTransformFunction custom_transform_func;
+ gpointer custom_transform_user_data;
+ GDestroyNotify custom_transform_destroy_notify;
+ gboolean is_custom;
+} GstMetaInfoImpl;
+
+static void
+free_info (gpointer data)
+{
+ g_slice_free (GstMetaInfoImpl, data);
+}
+
void
_priv_gst_meta_initialize (void)
{
g_rw_lock_init (&lock);
- metainfo = g_hash_table_new (g_str_hash, g_str_equal);
+ metainfo = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, free_info);
_gst_meta_transform_copy = g_quark_from_static_string ("gst-copy");
_gst_meta_tag_memory = g_quark_from_static_string ("memory");
}
+static gboolean
+notify_custom (gchar * key, GstMetaInfo * info, gpointer unused)
+{
+ GstMetaInfoImpl *impl = (GstMetaInfoImpl *) info;
+
+ if (impl->is_custom) {
+ if (impl->custom_transform_destroy_notify)
+ impl->custom_transform_destroy_notify (impl->custom_transform_user_data);
+ }
+ return TRUE;
+}
+
+void
+_priv_gst_meta_cleanup (void)
+{
+ if (metainfo != NULL) {
+ g_hash_table_foreach_remove (metainfo, (GHRFunc) notify_custom, NULL);
+ g_hash_table_unref (metainfo);
+ metainfo = NULL;
+ }
+}
+
/**
* gst_meta_api_type_register:
* @api: an API to register
return type;
}
+static gboolean
+custom_init_func (GstMeta * meta, gpointer params, GstBuffer * buffer)
+{
+ GstCustomMetaImpl *cmeta = (GstCustomMetaImpl *) meta;
+
+ cmeta->structure = gst_structure_new_empty (g_type_name (meta->info->type));
+
+ gst_structure_set_parent_refcount (cmeta->structure,
+ &GST_MINI_OBJECT_REFCOUNT (buffer));
+
+ return TRUE;
+}
+
+static void
+custom_free_func (GstMeta * meta, GstBuffer * buffer)
+{
+ GstCustomMetaImpl *cmeta = (GstCustomMetaImpl *) meta;
+
+ gst_structure_set_parent_refcount (cmeta->structure, NULL);
+ gst_structure_free (cmeta->structure);
+}
+
+static gboolean
+custom_transform_func (GstBuffer * transbuf, GstMeta * meta,
+ GstBuffer * buffer, GQuark type, gpointer data)
+{
+ GstCustomMetaImpl *custom, *cmeta = (GstCustomMetaImpl *) meta;
+ GstMetaInfoImpl *info = (GstMetaInfoImpl *) meta->info;
+
+ if (info->custom_transform_func)
+ return info->custom_transform_func (transbuf, (GstCustomMeta *) meta,
+ buffer, type, data, info->custom_transform_user_data);
+
+ if (GST_META_TRANSFORM_IS_COPY (type)) {
+ custom =
+ (GstCustomMetaImpl *) gst_buffer_add_meta (transbuf, meta->info, NULL);
+ gst_structure_set_parent_refcount (custom->structure, NULL);
+ gst_structure_take (&custom->structure,
+ gst_structure_copy (cmeta->structure));
+ gst_structure_set_parent_refcount (custom->structure,
+ &GST_MINI_OBJECT_REFCOUNT (buffer));
+ } else {
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+/**
+ * gst_custom_meta_get_structure:
+ *
+ * Retrieve the #GstStructure backing a custom meta, the structure's mutability
+ * is conditioned to the writability of the #GstBuffer @meta is attached to.
+ *
+ * Returns: (transfer none): the #GstStructure backing @meta
+ * Since: 1.20
+ */
+GstStructure *
+gst_custom_meta_get_structure (GstCustomMeta * meta)
+{
+ g_return_val_if_fail (meta != NULL, NULL);
+ g_return_val_if_fail (gst_meta_info_is_custom (((GstMeta *) meta)->info),
+ NULL);
+
+ return ((GstCustomMetaImpl *) meta)->structure;
+}
+
+/**
+ * gst_custom_meta_has_name:
+ *
+ * Checks whether the name of the custom meta is @name
+ *
+ * Returns: Whether @name is the name of the custom meta
+ * Since: 1.20
+ */
+gboolean
+gst_custom_meta_has_name (GstCustomMeta * meta, const gchar * name)
+{
+ g_return_val_if_fail (meta != NULL, FALSE);
+ g_return_val_if_fail (gst_meta_info_is_custom (((GstMeta *) meta)->info),
+ FALSE);
+
+ return gst_structure_has_name (((GstCustomMetaImpl *) meta)->structure, name);
+}
+
+/**
+ * gst_meta_register_custom:
+ * @name: the name of the #GstMeta implementation
+ * @tags: (array zero-terminated=1): tags for @api
+ * @transform_func: (scope notified) (nullable): a #GstMetaTransformFunction
+ * @user_data: (closure): user data passed to @transform_func
+ * @destroy_data: #GDestroyNotify for user_data
+ *
+ * Register a new custom #GstMeta implementation, backed by an opaque
+ * structure holding a #GstStructure.
+ *
+ * The registered info can be retrieved later with gst_meta_get_info() by using
+ * @name as the key.
+ *
+ * The backing #GstStructure can be retrieved with
+ * gst_custom_meta_get_structure(), its mutability is conditioned by the
+ * writability of the buffer the meta is attached to.
+ *
+ * When @transform_func is %NULL, the meta and its backing #GstStructure
+ * will always be copied when the transform operation is copy, other operations
+ * are discarded, copy regions are ignored.
+ *
+ * Returns: (transfer none): a #GstMetaInfo that can be used to
+ * access metadata.
+ * Since: 1.20
+ */
+const GstMetaInfo *
+gst_meta_register_custom (const gchar * name, const gchar ** tags,
+ GstCustomMetaTransformFunction transform_func,
+ gpointer user_data, GDestroyNotify destroy_data)
+{
+ gchar *api_name = g_strdup_printf ("%s-api", name);
+ GType api;
+ GstMetaInfoImpl *info;
+ GstMetaInfo *ret = NULL;
+
+ g_return_val_if_fail (tags != NULL, NULL);
+ g_return_val_if_fail (name != NULL, NULL);
+
+ api = gst_meta_api_type_register (api_name, tags);
+ g_free (api_name);
+ if (api == G_TYPE_INVALID)
+ goto done;
+
+ info = (GstMetaInfoImpl *) gst_meta_register (api, name,
+ sizeof (GstCustomMetaImpl),
+ custom_init_func, custom_free_func, custom_transform_func);
+
+ if (!info)
+ goto done;
+
+ info->is_custom = TRUE;
+ info->custom_transform_func = transform_func;
+ info->custom_transform_user_data = user_data;
+ info->custom_transform_destroy_notify = destroy_data;
+
+ ret = (GstMetaInfo *) info;
+
+done:
+ return ret;
+}
+
+/**
+ * gst_meta_info_is_custom:
+ *
+ * Returns: whether @info was registered as a #GstCustomMeta with
+ * gst_meta_register_custom()
+ * Since:1.20
+ */
+gboolean
+gst_meta_info_is_custom (const GstMetaInfo * info)
+{
+ g_return_val_if_fail (info != NULL, FALSE);
+
+ return ((GstMetaInfoImpl *) info)->is_custom;
+}
+
/**
* gst_meta_api_type_has_tag:
* @api: an API
* The same @info can be retrieved later with gst_meta_get_info() by using
* @impl as the key.
*
- * Returns: (transfer none) (nullable): a #GstMetaInfo that can be used to
+ * Returns: (transfer none): a #GstMetaInfo that can be used to
* access metadata.
*/
if (type == 0)
return NULL;
- info = g_slice_new (GstMetaInfo);
+ info = (GstMetaInfo *) g_slice_new (GstMetaInfoImpl);
info->api = api;
info->type = type;
info->size = size;
info->init_func = init_func;
info->free_func = free_func;
info->transform_func = transform_func;
+ ((GstMetaInfoImpl *) info)->is_custom = FALSE;
GST_CAT_DEBUG (GST_CAT_META,
"register \"%s\" implementing \"%s\" of size %" G_GSIZE_FORMAT, impl,
const GstMetaInfo *info;
};
+/**
+ * GstCustomMeta:
+ *
+ * Simple typing wrapper around #GstMeta
+ *
+ * Since: 1.20
+ */
+typedef struct {
+ GstMeta meta;
+} GstCustomMeta;
+
#include <gst/gstbuffer.h>
/**
GQuark type, gpointer data);
/**
+ * GstCustomMetaTransformFunction:
+ * @transbuf: a #GstBuffer
+ * @meta: a #GstCustomMeta
+ * @buffer: a #GstBuffer
+ * @type: the transform type
+ * @data: transform specific data.
+ * @user_data: user data passed when registering the meta
+ *
+ * Function called for each @meta in @buffer as a result of performing a
+ * transformation on @transbuf. Additional @type specific transform data
+ * is passed to the function as @data.
+ *
+ * Implementations should check the @type of the transform and parse
+ * additional type specific fields in @data that should be used to update
+ * the metadata on @transbuf.
+ *
+ * Returns: %TRUE if the transform could be performed
+ * Since: 1.20
+ */
+typedef gboolean (*GstCustomMetaTransformFunction) (GstBuffer *transbuf,
+ GstCustomMeta *meta, GstBuffer *buffer,
+ GQuark type, gpointer data, gpointer user_data);
+
+/**
* GstMetaInfo:
* @api: tag identifying the metadata structure and api
* @type: type identifying the implementor of the api
GstMetaInitFunction init_func,
GstMetaFreeFunction free_func,
GstMetaTransformFunction transform_func);
+
+GST_API
+const GstMetaInfo * gst_meta_register_custom (const gchar *name, const gchar **tags,
+ GstCustomMetaTransformFunction transform_func,
+ gpointer user_data, GDestroyNotify destroy_data);
+
+GST_API
+gboolean gst_meta_info_is_custom (const GstMetaInfo *info);
+
+GST_API
+GstStructure * gst_custom_meta_get_structure (GstCustomMeta *meta);
+
+GST_API
+gboolean gst_custom_meta_has_name (GstCustomMeta *meta, const gchar * name);
+
GST_API
const GstMetaInfo * gst_meta_get_info (const gchar * impl);
GST_END_TEST;
+GST_START_TEST (test_meta_custom)
+{
+ GstBuffer *buffer;
+ const GstMetaInfo *info;
+ GstCustomMeta *meta;
+ GstMeta *it;
+ GstStructure *s, *expected;
+ gpointer state = NULL;
+ const gchar *tags[] = { "test-tag", NULL };
+
+ info = gst_meta_register_custom ("test-custom", tags, NULL, NULL, NULL);
+
+ fail_unless (info != NULL);
+
+ buffer = gst_buffer_new_and_alloc (4);
+ fail_if (buffer == NULL);
+
+ /* add some metadata */
+ meta = gst_buffer_add_custom_meta (buffer, "test-custom");
+ fail_if (meta == NULL);
+
+ fail_unless (gst_custom_meta_has_name ((GstCustomMeta *) meta,
+ "test-custom"));
+
+ expected = gst_structure_new_empty ("test-custom");
+ s = gst_custom_meta_get_structure (meta);
+ fail_unless (gst_structure_is_equal (s, expected));
+ gst_structure_free (expected);
+
+ gst_structure_set (s, "test-field", G_TYPE_INT, 42, NULL);
+ gst_buffer_ref (buffer);
+ ASSERT_CRITICAL (gst_structure_set (s, "test-field", G_TYPE_INT, 43, NULL));
+ gst_buffer_unref (buffer);
+ expected = gst_structure_new ("test-custom",
+ "test-field", G_TYPE_INT, 42, NULL);
+ fail_unless (gst_structure_is_equal (s, expected));
+ gst_structure_free (expected);
+
+ it = gst_buffer_iterate_meta (buffer, &state);
+
+ fail_unless ((GstCustomMeta *) it == meta);
+
+ fail_unless (it->info == info);
+
+ /* clean up */
+ gst_buffer_unref (buffer);
+}
+
+GST_END_TEST;
+
+static gboolean
+transform_custom (GstBuffer * transbuf, GstMeta * meta, GstBuffer * buffer,
+ GQuark type, gpointer data, gint * user_data)
+{
+ if (GST_META_TRANSFORM_IS_COPY (type)) {
+ GstStructure *s;
+ GstCustomMeta *custom;
+
+ custom = (GstCustomMeta *) gst_buffer_add_meta (transbuf, meta->info, NULL);
+ s = gst_custom_meta_get_structure (custom);
+ gst_structure_set (s, "test-field", G_TYPE_INT, *user_data, NULL);
+ } else {
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+GST_START_TEST (test_meta_custom_transform)
+{
+ GstBuffer *buffer, *buffer_copy;
+ const GstMetaInfo *info;
+ GstCustomMeta *meta;
+ GstStructure *s, *expected;
+ const gchar *tags[] = { "test-tag", NULL };
+ gint *user_data;
+
+ /* That memory should be deallocated at gst_deinit time */
+ user_data = g_malloc (sizeof (gint));
+ *user_data = 42;
+ info =
+ gst_meta_register_custom ("test-custom", tags,
+ (GstCustomMetaTransformFunction) transform_custom, user_data, g_free);
+
+ fail_unless (info != NULL);
+
+ buffer = gst_buffer_new_and_alloc (4);
+ fail_if (buffer == NULL);
+
+ /* add some metadata */
+ meta = gst_buffer_add_custom_meta (buffer, "test-custom");
+ fail_if (meta == NULL);
+
+ buffer_copy = gst_buffer_copy (buffer);
+ meta = gst_buffer_get_custom_meta (buffer_copy, "test-custom");
+ fail_unless (meta != NULL);
+ expected =
+ gst_structure_new ("test-custom", "test-field", G_TYPE_INT, 42, NULL);
+ s = gst_custom_meta_get_structure (meta);
+ fail_unless (gst_structure_is_equal (s, expected));
+ gst_structure_free (expected);
+
+ /* clean up */
+ gst_buffer_unref (buffer_copy);
+ gst_buffer_unref (buffer);
+}
+
+GST_END_TEST;
+
static Suite *
gst_buffermeta_suite (void)
{
tcase_add_test (tc_chain, test_meta_foreach_remove_several);
tcase_add_test (tc_chain, test_meta_iterate);
tcase_add_test (tc_chain, test_meta_seqnum);
+ tcase_add_test (tc_chain, test_meta_custom);
+ tcase_add_test (tc_chain, test_meta_custom_transform);
return s;
}