From: Tim-Philipp Müller Date: Wed, 16 Nov 2011 01:04:45 +0000 (+0000) Subject: buffer: add gst_buffer_{set,get}_qdata() X-Git-Tag: RELEASE-0.10.36~63 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=33078aaeb70a9218242e41fc4b110bd1a1ac7a56;p=platform%2Fupstream%2Fgstreamer.git 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() --- diff --git a/docs/gst/gstreamer-sections.txt b/docs/gst/gstreamer-sections.txt index b9500b7..c0902e5 100644 --- a/docs/gst/gstreamer-sections.txt +++ b/docs/gst/gstreamer-sections.txt @@ -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 diff --git a/gst/gstbuffer.c b/gst/gstbuffer.c index 5a12663..edc0a6e 100644 --- a/gst/gstbuffer.c +++ b/gst/gstbuffer.c @@ -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; diff --git a/gst/gstbuffer.h b/gst/gstbuffer.h index d0893a2..3125f7f 100644 --- a/gst/gstbuffer.h +++ b/gst/gstbuffer.h @@ -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 diff --git a/tests/check/gst/gstbuffer.c b/tests/check/gst/gstbuffer.c index 06cac4c..be608ec 100644 --- a/tests/check/gst/gstbuffer.c +++ b/tests/check/gst/gstbuffer.c @@ -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; } diff --git a/win32/common/libgstreamer.def b/win32/common/libgstreamer.def index 5f2847e..f7a7974 100644 --- a/win32/common/libgstreamer.def +++ b/win32/common/libgstreamer.def @@ -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