+
+GST_DEBUG_CATEGORY_STATIC (gst_parent_buffer_meta_debug);
+
+/**
+ * gst_buffer_add_parent_buffer_meta:
+ * @buffer: (transfer none): a #GstBuffer
+ * @ref: (transfer none): a #GstBuffer to ref
+ *
+ * Add a #GstParentBufferMeta to @buffer that holds a reference on
+ * @ref until the buffer is freed.
+ *
+ * Returns: (transfer none): The #GstParentBufferMeta that was added to the buffer
+ *
+ * Since: 1.6
+ */
+GstParentBufferMeta *
+gst_buffer_add_parent_buffer_meta (GstBuffer * buffer, GstBuffer * ref)
+{
+ GstParentBufferMeta *meta;
+
+ g_return_val_if_fail (GST_IS_BUFFER (ref), NULL);
+
+ meta =
+ (GstParentBufferMeta *) gst_buffer_add_meta (buffer,
+ GST_PARENT_BUFFER_META_INFO, NULL);
+
+ if (!meta)
+ return NULL;
+
+ meta->buffer = gst_buffer_ref (ref);
+
+ return meta;
+}
+
+static gboolean
+_gst_parent_buffer_meta_transform (GstBuffer * dest, GstMeta * meta,
+ GstBuffer * buffer, GQuark type, gpointer data)
+{
+ GstParentBufferMeta *dmeta, *smeta;
+
+ smeta = (GstParentBufferMeta *) meta;
+
+ if (GST_META_TRANSFORM_IS_COPY (type)) {
+ /* copy over the reference to the parent buffer.
+ * Usually, this meta means we need to keep the parent buffer
+ * alive because one of the child memories is in use, which
+ * might not be the case if memory is deep copied or sub-regioned,
+ * but we can't tell, so keep the meta */
+ dmeta = gst_buffer_add_parent_buffer_meta (dest, smeta->buffer);
+ if (!dmeta)
+ return FALSE;
+
+ GST_CAT_DEBUG (gst_parent_buffer_meta_debug,
+ "copy buffer reference metadata");
+ } else {
+ /* return FALSE, if transform type is not supported */
+ return FALSE;
+ }
+ return TRUE;
+}
+
+static void
+_gst_parent_buffer_meta_free (GstParentBufferMeta * parent_meta,
+ GstBuffer * buffer)
+{
+ GST_CAT_DEBUG (gst_parent_buffer_meta_debug,
+ "Dropping reference on buffer %p", parent_meta->buffer);
+ gst_buffer_unref (parent_meta->buffer);
+}
+
+static gboolean
+_gst_parent_buffer_meta_init (GstParentBufferMeta * parent_meta,
+ gpointer params, GstBuffer * buffer)
+{
+ static volatile gsize _init;
+
+ if (g_once_init_enter (&_init)) {
+ GST_DEBUG_CATEGORY_INIT (gst_parent_buffer_meta_debug, "parentbuffermeta",
+ 0, "parentbuffermeta");
+ g_once_init_leave (&_init, 1);
+ }
+
+ parent_meta->buffer = NULL;
+
+ return TRUE;
+}
+
+GType
+gst_parent_buffer_meta_api_get_type (void)
+{
+ static volatile GType type = 0;
+ static const gchar *tags[] = { NULL };
+
+ if (g_once_init_enter (&type)) {
+ GType _type = gst_meta_api_type_register ("GstParentBufferMetaAPI", tags);
+ g_once_init_leave (&type, _type);
+ }
+
+ return type;
+}
+
+/**
+ * gst_parent_buffer_meta_get_info:
+ *
+ * Get the global #GstMetaInfo describing the #GstParentBufferMeta meta.
+ *
+ * Returns: (transfer none): The #GstMetaInfo
+ *
+ * Since: 1.6
+ */
+const GstMetaInfo *
+gst_parent_buffer_meta_get_info (void)
+{
+ static const GstMetaInfo *meta_info = NULL;
+
+ if (g_once_init_enter ((GstMetaInfo **) & meta_info)) {
+ const GstMetaInfo *meta =
+ gst_meta_register (gst_parent_buffer_meta_api_get_type (),
+ "GstParentBufferMeta",
+ sizeof (GstParentBufferMeta),
+ (GstMetaInitFunction) _gst_parent_buffer_meta_init,
+ (GstMetaFreeFunction) _gst_parent_buffer_meta_free,
+ _gst_parent_buffer_meta_transform);
+ g_once_init_leave ((GstMetaInfo **) & meta_info, (GstMetaInfo *) meta);
+ }
+
+ return meta_info;
+}