Commit 33078aae authored by Tim-Philipp Müller's avatar Tim-Philipp Müller

buffer: add gst_buffer_{set,get}_qdata()

Allows people/us to attach arbitrary metadata to buffers.

https://bugzilla.gnome.org/show_bug.cgi?id=664720

API: gst_buffer_set_qdata()
API: get_buffer_get_qdata()
parent 0280a3c2
......@@ -200,6 +200,9 @@ gst_buffer_is_metadata_writable
gst_buffer_make_metadata_writable
gst_buffer_replace
gst_buffer_set_qdata
gst_buffer_get_qdata
gst_buffer_get_caps
gst_buffer_set_caps
......
......@@ -128,6 +128,12 @@
#include "gstminiobject.h"
#include "gstversion.h"
struct _GstBufferPrivate
{
GList *qdata;
/* think about locking buffer->priv etc. when adding more fields */
};
static void gst_buffer_finalize (GstBuffer * buffer);
static GstBuffer *_gst_buffer_copy (GstBuffer * buffer);
......@@ -185,6 +191,8 @@ gst_buffer_class_init (GstBufferClass * klass)
klass->mini_object_class.copy = (GstMiniObjectCopyFunction) _gst_buffer_copy;
klass->mini_object_class.finalize =
(GstMiniObjectFinalizeFunction) gst_buffer_finalize;
g_type_class_add_private (klass, sizeof (GstBufferPrivate));
}
static void
......@@ -203,10 +211,61 @@ gst_buffer_finalize (GstBuffer * buffer)
if (buffer->parent)
gst_buffer_unref (buffer->parent);
if (G_UNLIKELY (buffer->priv != NULL)) {
GstBufferPrivate *priv = buffer->priv;
while (priv->qdata != NULL) {
GstStructure *s = priv->qdata->data;
gst_structure_set_parent_refcount (s, NULL);
gst_structure_free (s);
priv->qdata = g_list_delete_link (priv->qdata, priv->qdata);
}
priv->qdata = NULL;
}
/* ((GstMiniObjectClass *) */
/* gst_buffer_parent_class)->finalize (GST_MINI_OBJECT_CAST (buffer)); */
}
static inline GstBufferPrivate *
gst_buffer_ensure_priv (GstBuffer * buf)
{
GstBufferPrivate *priv = buf->priv;
if (priv != NULL)
return priv;
priv = buf->priv =
G_TYPE_INSTANCE_GET_PRIVATE (buf, GST_TYPE_BUFFER, GstBufferPrivate);
return priv;
}
static void
gst_buffer_copy_qdata (GstBuffer * dest, const GstBuffer * src)
{
GstBufferPrivate *priv;
GQueue qdata_copy = G_QUEUE_INIT;
GList *l;
if (G_LIKELY (src->priv == NULL))
return;
for (l = src->priv->qdata; l != NULL; l = l->next) {
GstStructure *s = gst_structure_copy (l->data);
gst_structure_set_parent_refcount (s, &dest->mini_object.refcount);
g_queue_push_tail (&qdata_copy, s);
GST_CAT_TRACE (GST_CAT_BUFFER, "copying qdata '%s' from buffer %p to %p",
g_quark_to_string (s->name), src, dest);
}
priv = gst_buffer_ensure_priv (dest);
priv->qdata = qdata_copy.head;
}
/**
* gst_buffer_copy_metadata:
* @dest: a destination #GstBuffer
......@@ -263,6 +322,116 @@ gst_buffer_copy_metadata (GstBuffer * dest, const GstBuffer * src,
if (flags & GST_BUFFER_COPY_CAPS) {
gst_caps_replace (&GST_BUFFER_CAPS (dest), GST_BUFFER_CAPS (src));
}
if ((flags & GST_BUFFER_COPY_QDATA)) {
GST_CAT_TRACE (GST_CAT_BUFFER, "copying qdata from %p to %p", src, dest);
gst_buffer_copy_qdata (dest, src);
}
}
/**
* gst_buffer_set_qdata:
* @buffer: a #GstBuffer
* @quark: name quark of data structure to set or replace
* @data: (transfer full) (allow-none): a #GstStructure to store with the
* buffer, name must match @quark. Can be NULL to remove an existing
* structure. This function takes ownership of the structure passed.
*
* Set metadata structure for name quark @quark to @data, or remove the
* existing metadata structure by that name in case @data is NULL.
*
* Takes ownership of @data.
*
* Since: 0.10.36
*/
void
gst_buffer_set_qdata (GstBuffer * buffer, GQuark quark, GstStructure * data)
{
GstBufferPrivate *priv;
GList *l;
g_return_if_fail (GST_IS_BUFFER (buffer));
g_return_if_fail (gst_buffer_is_metadata_writable (buffer));
g_return_if_fail (data == NULL || quark == gst_structure_get_name_id (data));
/* locking should not really be required, since the metadata_writable
* check ensures that the caller is the only one holding a ref, so as
* as a second ref is added everything turns read-only */
priv = gst_buffer_ensure_priv (buffer);
if (data) {
gst_structure_set_parent_refcount (data, &buffer->mini_object.refcount);
}
for (l = priv->qdata; l != NULL; l = l->next) {
GstStructure *s = l->data;
if (s->name == quark) {
GST_CAT_LOG (GST_CAT_BUFFER, "Replacing qdata '%s' on buffer %p: "
"%" GST_PTR_FORMAT " => %" GST_PTR_FORMAT, g_quark_to_string (quark),
buffer, s, data);
gst_structure_set_parent_refcount (s, NULL);
gst_structure_free (s);
if (data == NULL)
priv->qdata = g_list_delete_link (priv->qdata, l);
else
l->data = data;
goto done;
}
}
GST_CAT_LOG (GST_CAT_BUFFER, "Set qdata '%s' on buffer %p: %" GST_PTR_FORMAT,
g_quark_to_string (quark), buffer, data);
priv->qdata = g_list_prepend (priv->qdata, data);
done:
return;
}
/**
* gst_buffer_get_qdata:
* @buffer: a #GstBuffer
* @quark: name quark of data structure to find
*
* Get metadata structure for name quark @quark.
*
* Returns: (transfer none): a #GstStructure, or NULL if not found
*
* Since: 0.10.36
*/
const GstStructure *
gst_buffer_get_qdata (GstBuffer * buffer, GQuark quark)
{
GstStructure *ret = NULL;
/* no need for locking: if the caller has the only ref, we're safe, and
* if the buffer has multiple refs, it's not metadata-writable any longer
* and the data can't change */
GST_CAT_LOG (GST_CAT_BUFFER, "Looking for qdata '%s' on buffer %p",
g_quark_to_string (quark), buffer);
if (buffer->priv != NULL) {
GList *l;
for (l = buffer->priv->qdata; l != NULL; l = l->next) {
GstStructure *s = l->data;
GST_CAT_LOG (GST_CAT_BUFFER, "checking qdata '%s' on buffer %p",
g_quark_to_string (s->name), buffer);
if (s->name == quark) {
ret = s;
break;
}
}
}
return ret;
}
static GstBuffer *
......@@ -632,6 +801,9 @@ gst_buffer_create_sub (GstBuffer * buffer, guint offset, guint size)
if ((caps = GST_BUFFER_CAPS (buffer)))
gst_caps_ref (caps);
GST_BUFFER_CAPS (subbuffer) = caps;
/* and also the attached qdata */
gst_buffer_copy_qdata (subbuffer, buffer);
} else {
GST_BUFFER_DURATION (subbuffer) = GST_CLOCK_TIME_NONE;
GST_BUFFER_OFFSET_END (subbuffer) = GST_BUFFER_OFFSET_NONE;
......
......@@ -32,6 +32,7 @@ G_BEGIN_DECLS
typedef struct _GstBuffer GstBuffer;
typedef struct _GstBufferClass GstBufferClass;
typedef struct _GstBufferPrivate GstBufferPrivate;
/**
* GST_BUFFER_TRACE_NAME:
......@@ -288,7 +289,8 @@ struct _GstBuffer {
GstBuffer *parent;
/*< private >*/
gpointer _gst_reserved[GST_PADDING - 2];
GstBufferPrivate *priv;
gpointer _gst_reserved[GST_PADDING - 3];
};
struct _GstBufferClass {
......@@ -392,6 +394,8 @@ gst_buffer_copy (const GstBuffer * buf)
* @GST_BUFFER_COPY_TIMESTAMPS: flag indicating that buffer timestamp, duration,
* offset and offset_end should be copied
* @GST_BUFFER_COPY_CAPS: flag indicating that buffer caps should be copied
* @GST_BUFFER_COPY_QDATA: flag indicating that buffer qdata should be copied
* (Since 0.10.36)
*
* A set of flags that can be provided to the gst_buffer_copy_metadata()
* function to specify which metadata fields should be copied.
......@@ -401,7 +405,8 @@ gst_buffer_copy (const GstBuffer * buf)
typedef enum {
GST_BUFFER_COPY_FLAGS = (1 << 0),
GST_BUFFER_COPY_TIMESTAMPS = (1 << 1),
GST_BUFFER_COPY_CAPS = (1 << 2)
GST_BUFFER_COPY_CAPS = (1 << 2),
GST_BUFFER_COPY_QDATA = (1 << 3)
} GstBufferCopyFlags;
/**
......@@ -412,7 +417,7 @@ typedef enum {
*
* Since: 0.10.13
*/
#define GST_BUFFER_COPY_ALL ((GstBufferCopyFlags) (GST_BUFFER_COPY_FLAGS | GST_BUFFER_COPY_TIMESTAMPS | GST_BUFFER_COPY_CAPS))
#define GST_BUFFER_COPY_ALL ((GstBufferCopyFlags) (GST_BUFFER_COPY_FLAGS | GST_BUFFER_COPY_TIMESTAMPS | GST_BUFFER_COPY_CAPS | GST_BUFFER_COPY_QDATA))
/* copies metadata into newly allocated buffer */
void gst_buffer_copy_metadata (GstBuffer *dest, const GstBuffer *src,
......@@ -446,6 +451,17 @@ void gst_buffer_copy_metadata (GstBuffer *dest, const GstBuffe
gboolean gst_buffer_is_metadata_writable (GstBuffer *buf);
GstBuffer* gst_buffer_make_metadata_writable (GstBuffer *buf);
/* per-buffer user data */
void gst_buffer_set_qdata (GstBuffer * buffer,
GQuark quark,
GstStructure * data);
const GstStructure * gst_buffer_get_qdata (GstBuffer * buffer,
GQuark quark);
/**
* gst_buffer_replace:
* @obuf: (inout) (transfer full): pointer to a pointer to a #GstBuffer to be
......
......@@ -451,6 +451,59 @@ GST_START_TEST (test_try_new_and_alloc)
GST_END_TEST;
GST_START_TEST (test_qdata)
{
GstStructure *s;
GstBuffer *buf, *buf2, *buf3;
GQuark q1, q2, q3;
q1 = g_quark_from_static_string ("GstFooBar");
q2 = g_quark_from_static_string ("MyBorkData");
q3 = g_quark_from_static_string ("DoNotExist");
buf = gst_buffer_new ();
ASSERT_CRITICAL (gst_buffer_set_qdata (buf, q1, (s =
gst_structure_id_empty_new (q2))));
gst_structure_free (s);
gst_buffer_set_qdata (buf, q1, gst_structure_id_empty_new (q1));
gst_buffer_set_qdata (buf, q2, gst_structure_id_empty_new (q2));
fail_unless (gst_buffer_get_qdata (buf, q3) == NULL);
fail_unless (gst_buffer_get_qdata (buf, q1) != NULL);
fail_unless (gst_buffer_get_qdata (buf, q2) != NULL);
/* full copy */
buf2 = gst_buffer_copy (buf);
/* now back to the original buffer... */
gst_buffer_set_qdata (buf, q1, NULL);
fail_unless (gst_buffer_get_qdata (buf, q1) == NULL);
/* force creation of sub-buffer with writable metadata */
gst_buffer_ref (buf);
buf3 = gst_buffer_make_metadata_writable (buf);
/* and check the copies/subbuffers.. */
fail_unless (gst_buffer_get_qdata (buf2, q3) == NULL);
fail_unless (gst_buffer_get_qdata (buf2, q1) != NULL);
fail_unless (gst_buffer_get_qdata (buf2, q2) != NULL);
fail_unless (gst_buffer_get_qdata (buf3, q3) == NULL);
fail_unless (gst_buffer_get_qdata (buf3, q1) == NULL);
fail_unless (gst_buffer_get_qdata (buf3, q2) != NULL);
gst_buffer_set_qdata (buf3, q1, gst_structure_id_empty_new (q1));
fail_unless (gst_buffer_get_qdata (buf3, q1) != NULL);
/* original buffer shouldn't have changed */
fail_unless (gst_buffer_get_qdata (buf, q1) == NULL);
gst_buffer_unref (buf);
gst_buffer_unref (buf2);
gst_buffer_unref (buf3);
}
GST_END_TEST;
static Suite *
gst_buffer_suite (void)
{
......@@ -467,6 +520,7 @@ gst_buffer_suite (void)
tcase_add_test (tc_chain, test_metadata_writable);
tcase_add_test (tc_chain, test_copy);
tcase_add_test (tc_chain, test_try_new_and_alloc);
tcase_add_test (tc_chain, test_qdata);
return s;
}
......
......@@ -94,6 +94,7 @@ EXPORTS
gst_buffer_create_sub
gst_buffer_flag_get_type
gst_buffer_get_caps
gst_buffer_get_qdata
gst_buffer_get_type
gst_buffer_is_metadata_writable
gst_buffer_is_span_fast
......@@ -123,6 +124,7 @@ EXPORTS
gst_buffer_new
gst_buffer_new_and_alloc
gst_buffer_set_caps
gst_buffer_set_qdata
gst_buffer_span
gst_buffer_stamp
gst_buffer_try_new_and_alloc
......
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