mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2025-02-17 03:35:21 +00:00
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()
This commit is contained in:
parent
0280a3c29f
commit
33078aaeb7
5 changed files with 250 additions and 3 deletions
|
@ -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
|
||||
|
||||
|
|
172
gst/gstbuffer.c
172
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;
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in a new issue