mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-11-14 05:12:09 +00:00
meta: Add serialize/deserialize API
This allows metas to be serialized to be transmitted or stored. This is intended to be used for example by gdppay or unixfdsink. Implemented on GstCustomMeta, GstVideoMeta, GstReferenceTimestampMeta, and GstAudioMeta. Sponsored-by: Netflix Inc. Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/5355>
This commit is contained in:
parent
9501d64ccd
commit
06d9d934f9
15 changed files with 1039 additions and 37 deletions
|
@ -30,10 +30,11 @@
|
|||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "gstaudiometa.h"
|
||||
|
||||
#include <string.h>
|
||||
#include <gst/base/base.h>
|
||||
|
||||
static gboolean
|
||||
gst_audio_downmix_meta_init (GstMeta * meta, gpointer params,
|
||||
GstBuffer * buffer)
|
||||
|
@ -351,6 +352,111 @@ gst_audio_meta_transform (GstBuffer * dest, GstMeta * meta,
|
|||
return TRUE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gst_audio_meta_serialize (const GstMeta * meta, GstByteArrayInterface * data,
|
||||
guint8 * version)
|
||||
{
|
||||
GstAudioMeta *ameta = (GstAudioMeta *) meta;
|
||||
|
||||
/* Position is limited to 64 */
|
||||
gint n_position = ameta->info.channels > 64 ? 0 : ameta->info.channels;
|
||||
|
||||
gsize size = 28 + n_position * 4 + ameta->info.channels * 8;
|
||||
guint8 *ptr = gst_byte_array_interface_append (data, size);
|
||||
if (ptr == NULL)
|
||||
return FALSE;
|
||||
|
||||
GstByteWriter bw;
|
||||
gboolean success = TRUE;
|
||||
gst_byte_writer_init_with_data (&bw, ptr, size, FALSE);
|
||||
success &= gst_byte_writer_put_int32_le (&bw, ameta->info.finfo->format);
|
||||
success &= gst_byte_writer_put_int32_le (&bw, ameta->info.flags);
|
||||
success &= gst_byte_writer_put_int32_le (&bw, ameta->info.layout);
|
||||
success &= gst_byte_writer_put_int32_le (&bw, ameta->info.rate);
|
||||
success &= gst_byte_writer_put_int32_le (&bw, ameta->info.channels);
|
||||
for (int i = 0; i < n_position; i++)
|
||||
success &= gst_byte_writer_put_int32_le (&bw, ameta->info.position[i]);
|
||||
success &= gst_byte_writer_put_uint64_le (&bw, ameta->samples);
|
||||
for (int i = 0; i < ameta->info.channels; i++)
|
||||
success &= gst_byte_writer_put_uint64_le (&bw, ameta->offsets[i]);
|
||||
g_assert (success);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static GstMeta *
|
||||
gst_audio_meta_deserialize (const GstMetaInfo * info, GstBuffer * buffer,
|
||||
const guint8 * data, gsize size, guint8 version)
|
||||
{
|
||||
GstAudioMeta *ameta = NULL;
|
||||
gint32 format;
|
||||
gint32 flags;
|
||||
gint32 layout;
|
||||
gint32 rate;
|
||||
gint32 channels;
|
||||
|
||||
if (version != 0)
|
||||
return NULL;
|
||||
|
||||
GstByteReader br;
|
||||
gboolean success = TRUE;
|
||||
gst_byte_reader_init (&br, data, size);
|
||||
success &= gst_byte_reader_get_int32_le (&br, &format);
|
||||
success &= gst_byte_reader_get_int32_le (&br, &flags);
|
||||
success &= gst_byte_reader_get_int32_le (&br, &layout);
|
||||
success &= gst_byte_reader_get_int32_le (&br, &rate);
|
||||
success &= gst_byte_reader_get_int32_le (&br, &channels);
|
||||
|
||||
if (!success)
|
||||
return NULL;
|
||||
|
||||
/* Position is limited to 64 */
|
||||
gint n_position = channels > 64 ? 0 : channels;
|
||||
gint32 *position = g_new (gint32, n_position);
|
||||
guint64 *offsets64 = g_new (guint64, channels);
|
||||
guint64 samples = 0;
|
||||
|
||||
for (int i = 0; i < n_position; i++)
|
||||
success &= gst_byte_reader_get_int32_le (&br, &position[i]);
|
||||
success &= gst_byte_reader_get_uint64_le (&br, &samples);
|
||||
for (int i = 0; i < channels; i++)
|
||||
success &= gst_byte_reader_get_uint64_le (&br, &offsets64[i]);
|
||||
|
||||
if (!success) {
|
||||
g_free (position);
|
||||
g_free (offsets64);
|
||||
return NULL;
|
||||
}
|
||||
#if GLIB_SIZEOF_SIZE_T != 8
|
||||
gsize *offsets = g_new (gsize, channels);
|
||||
for (int i = 0; i < channels; i++) {
|
||||
if (offsets64[i] > G_MAXSIZE) {
|
||||
g_free (offsets64);
|
||||
g_free (offsets);
|
||||
g_free (position);
|
||||
return NULL;
|
||||
}
|
||||
offsets[i] = offsets64[i];
|
||||
}
|
||||
g_free (offsets64);
|
||||
#else
|
||||
gsize *offsets = (gsize *) offsets64;
|
||||
#endif
|
||||
|
||||
GstAudioInfo audio_info;
|
||||
gst_audio_info_set_format (&audio_info, format, rate, channels,
|
||||
(channels > 64) ? NULL : position);
|
||||
audio_info.flags = flags;
|
||||
audio_info.layout = layout;
|
||||
|
||||
ameta = gst_buffer_add_audio_meta (buffer, &audio_info, samples, offsets);
|
||||
|
||||
g_free (offsets);
|
||||
g_free (position);
|
||||
|
||||
return (GstMeta *) ameta;
|
||||
}
|
||||
|
||||
/**
|
||||
* gst_buffer_add_audio_meta:
|
||||
* @buffer: a #GstBuffer
|
||||
|
@ -482,11 +588,14 @@ gst_audio_meta_get_info (void)
|
|||
static const GstMetaInfo *audio_meta_info = NULL;
|
||||
|
||||
if (g_once_init_enter ((GstMetaInfo **) & audio_meta_info)) {
|
||||
const GstMetaInfo *meta = gst_meta_register (GST_AUDIO_META_API_TYPE,
|
||||
const GstMetaInfo *meta =
|
||||
gst_meta_register_serializable (GST_AUDIO_META_API_TYPE,
|
||||
"GstAudioMeta", sizeof (GstAudioMeta),
|
||||
gst_audio_meta_init,
|
||||
gst_audio_meta_free,
|
||||
gst_audio_meta_transform);
|
||||
gst_audio_meta_transform,
|
||||
gst_audio_meta_serialize,
|
||||
gst_audio_meta_deserialize);
|
||||
g_once_init_leave ((GstMetaInfo **) & audio_meta_info,
|
||||
(GstMetaInfo *) meta);
|
||||
}
|
||||
|
|
|
@ -170,6 +170,9 @@ typedef struct _GstAudioMeta GstAudioMeta;
|
|||
* case to do so. It is, however, allowed to attach it, for some potential
|
||||
* future use case.
|
||||
*
|
||||
* Since 1.24 it can be serialized using gst_meta_serialize() and
|
||||
* gst_meta_deserialize().
|
||||
*
|
||||
* Since: 1.16
|
||||
*/
|
||||
struct _GstAudioMeta {
|
||||
|
|
|
@ -23,6 +23,7 @@
|
|||
#include "gstvideometa.h"
|
||||
|
||||
#include <string.h>
|
||||
#include <gst/base/base.h>
|
||||
|
||||
/**
|
||||
* SECTION:gstvideometa
|
||||
|
@ -31,6 +32,12 @@
|
|||
*
|
||||
*/
|
||||
|
||||
static gboolean
|
||||
default_map (GstVideoMeta * meta, guint plane, GstMapInfo * info,
|
||||
gpointer * data, gint * stride, GstMapFlags flags);
|
||||
static gboolean
|
||||
default_unmap (GstVideoMeta * meta, guint plane, GstMapInfo * info);
|
||||
|
||||
#ifndef GST_DISABLE_GST_DEBUG
|
||||
#define GST_CAT_DEFAULT ensure_debug_category()
|
||||
static GstDebugCategory *
|
||||
|
@ -134,6 +141,110 @@ gst_video_meta_api_get_type (void)
|
|||
return type;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
video_meta_serialize (const GstMeta * meta, GstByteArrayInterface * data,
|
||||
guint8 * version)
|
||||
{
|
||||
GstVideoMeta *vmeta = (GstVideoMeta *) meta;
|
||||
|
||||
if (vmeta->map != default_map || vmeta->unmap != default_unmap) {
|
||||
GST_WARNING ("Cannot serialize video meta with custom map/unmap functions");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
gsize size = 36 + vmeta->n_planes * 16;
|
||||
guint8 *ptr = gst_byte_array_interface_append (data, size);
|
||||
if (ptr == NULL)
|
||||
return FALSE;
|
||||
|
||||
GstByteWriter bw;
|
||||
gboolean success = TRUE;
|
||||
gst_byte_writer_init_with_data (&bw, ptr, size, FALSE);
|
||||
success &= gst_byte_writer_put_int32_le (&bw, vmeta->flags);
|
||||
success &= gst_byte_writer_put_int32_le (&bw, vmeta->format);
|
||||
success &= gst_byte_writer_put_uint32_le (&bw, vmeta->width);
|
||||
success &= gst_byte_writer_put_uint32_le (&bw, vmeta->height);
|
||||
success &= gst_byte_writer_put_uint32_le (&bw, vmeta->n_planes);
|
||||
for (int n = 0; n < vmeta->n_planes; n++)
|
||||
success &= gst_byte_writer_put_uint64_le (&bw, vmeta->offset[n]);
|
||||
for (int n = 0; n < vmeta->n_planes; n++)
|
||||
success &= gst_byte_writer_put_int32_le (&bw, vmeta->stride[n]);
|
||||
success &= gst_byte_writer_put_uint32_le (&bw, vmeta->alignment.padding_top);
|
||||
success &=
|
||||
gst_byte_writer_put_uint32_le (&bw, vmeta->alignment.padding_bottom);
|
||||
success &= gst_byte_writer_put_uint32_le (&bw, vmeta->alignment.padding_left);
|
||||
success &=
|
||||
gst_byte_writer_put_uint32_le (&bw, vmeta->alignment.padding_right);
|
||||
for (int n = 0; n < vmeta->n_planes; n++)
|
||||
success &=
|
||||
gst_byte_writer_put_uint32_le (&bw, vmeta->alignment.stride_align[n]);
|
||||
g_assert (success);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static GstMeta *
|
||||
video_meta_deserialize (const GstMetaInfo * info, GstBuffer * buffer,
|
||||
const guint8 * data, gsize size, guint8 version)
|
||||
{
|
||||
GstVideoMeta *vmeta = NULL;
|
||||
gint32 flags;
|
||||
gint32 format;
|
||||
guint width;
|
||||
guint height;
|
||||
guint n_planes;
|
||||
GstVideoAlignment align;
|
||||
guint64 offset64[GST_VIDEO_MAX_PLANES];
|
||||
gint32 stride[GST_VIDEO_MAX_PLANES];
|
||||
|
||||
if (version != 0)
|
||||
return NULL;
|
||||
|
||||
GstByteReader br;
|
||||
gboolean success = TRUE;
|
||||
gst_byte_reader_init (&br, data, size);
|
||||
success &= gst_byte_reader_get_int32_le (&br, &flags);
|
||||
success &= gst_byte_reader_get_int32_le (&br, &format);
|
||||
success &= gst_byte_reader_get_uint32_le (&br, &width);
|
||||
success &= gst_byte_reader_get_uint32_le (&br, &height);
|
||||
success &= gst_byte_reader_get_uint32_le (&br, &n_planes);
|
||||
|
||||
if (!success || n_planes > GST_VIDEO_MAX_PLANES)
|
||||
return NULL;
|
||||
|
||||
for (int n = 0; n < n_planes; n++)
|
||||
success &= gst_byte_reader_get_uint64_le (&br, &offset64[n]);
|
||||
for (int n = 0; n < n_planes; n++)
|
||||
success &= gst_byte_reader_get_int32_le (&br, &stride[n]);
|
||||
success &= gst_byte_reader_get_uint32_le (&br, &align.padding_top);
|
||||
success &= gst_byte_reader_get_uint32_le (&br, &align.padding_bottom);
|
||||
success &= gst_byte_reader_get_uint32_le (&br, &align.padding_left);
|
||||
success &= gst_byte_reader_get_uint32_le (&br, &align.padding_right);
|
||||
for (int n = 0; n < n_planes; n++)
|
||||
success &= gst_byte_reader_get_uint32_le (&br, &align.stride_align[n]);
|
||||
|
||||
if (!success)
|
||||
return NULL;
|
||||
|
||||
#if GLIB_SIZEOF_SIZE_T != 8
|
||||
gsize offset[GST_VIDEO_MAX_PLANES];
|
||||
for (int i = 0; i < n_planes; i++) {
|
||||
if (offset64[i] > G_MAXSIZE)
|
||||
return NULL;
|
||||
offset[i] = offset64[i];
|
||||
}
|
||||
#else
|
||||
gsize *offset = (gsize *) offset64;
|
||||
#endif
|
||||
|
||||
vmeta =
|
||||
gst_buffer_add_video_meta_full (buffer, flags, format, width, height,
|
||||
n_planes, offset, stride);
|
||||
gst_video_meta_set_alignment (vmeta, align);
|
||||
|
||||
return (GstMeta *) vmeta;
|
||||
}
|
||||
|
||||
/* video metadata */
|
||||
const GstMetaInfo *
|
||||
gst_video_meta_get_info (void)
|
||||
|
@ -142,9 +253,10 @@ gst_video_meta_get_info (void)
|
|||
|
||||
if (g_once_init_enter ((GstMetaInfo **) & video_meta_info)) {
|
||||
const GstMetaInfo *meta =
|
||||
gst_meta_register (GST_VIDEO_META_API_TYPE, "GstVideoMeta",
|
||||
gst_meta_register_serializable (GST_VIDEO_META_API_TYPE, "GstVideoMeta",
|
||||
sizeof (GstVideoMeta), (GstMetaInitFunction) gst_video_meta_init,
|
||||
(GstMetaFreeFunction) NULL, gst_video_meta_transform);
|
||||
(GstMetaFreeFunction) NULL, gst_video_meta_transform,
|
||||
video_meta_serialize, video_meta_deserialize);
|
||||
g_once_init_leave ((GstMetaInfo **) & video_meta_info,
|
||||
(GstMetaInfo *) meta);
|
||||
}
|
||||
|
@ -310,8 +422,8 @@ gst_buffer_add_video_meta (GstBuffer * buffer,
|
|||
GstVideoMeta *
|
||||
gst_buffer_add_video_meta_full (GstBuffer * buffer,
|
||||
GstVideoFrameFlags flags, GstVideoFormat format, guint width,
|
||||
guint height, guint n_planes, gsize offset[GST_VIDEO_MAX_PLANES],
|
||||
gint stride[GST_VIDEO_MAX_PLANES])
|
||||
guint height, guint n_planes, const gsize offset[GST_VIDEO_MAX_PLANES],
|
||||
const gint stride[GST_VIDEO_MAX_PLANES])
|
||||
{
|
||||
GstVideoMeta *meta;
|
||||
guint i;
|
||||
|
|
|
@ -74,6 +74,9 @@ typedef struct _GstVideoCropMeta GstVideoCropMeta;
|
|||
* - padding-right (uint): extra pixels on the right side
|
||||
* The padding fields have the same semantic as #GstVideoMeta.alignment
|
||||
* and so represent the paddings requested on produced video buffers.
|
||||
*
|
||||
* Since 1.24 it can be serialized using gst_meta_serialize() and
|
||||
* gst_meta_deserialize().
|
||||
*/
|
||||
struct _GstVideoMeta {
|
||||
GstMeta meta;
|
||||
|
@ -116,8 +119,8 @@ GstVideoMeta * gst_buffer_add_video_meta (GstBuffer *buffer, GstVideoFrame
|
|||
GST_VIDEO_API
|
||||
GstVideoMeta * gst_buffer_add_video_meta_full (GstBuffer *buffer, GstVideoFrameFlags flags,
|
||||
GstVideoFormat format, guint width, guint height,
|
||||
guint n_planes, gsize offset[GST_VIDEO_MAX_PLANES],
|
||||
gint stride[GST_VIDEO_MAX_PLANES]);
|
||||
guint n_planes, const gsize offset[GST_VIDEO_MAX_PLANES],
|
||||
const gint stride[GST_VIDEO_MAX_PLANES]);
|
||||
|
||||
GST_VIDEO_API
|
||||
gboolean gst_video_meta_map (GstVideoMeta *meta, guint plane, GstMapInfo *info,
|
||||
|
|
|
@ -1578,6 +1578,106 @@ GST_START_TEST (test_audio_make_raw_caps)
|
|||
|
||||
GST_END_TEST;
|
||||
|
||||
GST_START_TEST (test_audio_meta_serialize)
|
||||
{
|
||||
GstBuffer *buf;
|
||||
GstAudioInfo info;
|
||||
GstAudioMeta *meta;
|
||||
|
||||
GstAudioChannelPosition position[] = { GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT,
|
||||
GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT
|
||||
};
|
||||
gst_audio_info_init (&info);
|
||||
gst_audio_info_set_format (&info, GST_AUDIO_FORMAT_S32, 44100, 2, position);
|
||||
info.layout = GST_AUDIO_LAYOUT_NON_INTERLEAVED;
|
||||
|
||||
/* Add audio meta */
|
||||
gsize samples = 1;
|
||||
gsize buf_size = 4 * 2 * samples;
|
||||
buf = gst_buffer_new_and_alloc (buf_size);
|
||||
gsize offsets[] = { 0, buf_size / 2 };
|
||||
meta = gst_buffer_add_audio_meta (buf, &info, samples, offsets);
|
||||
|
||||
/* Serialize */
|
||||
GByteArray *data = g_byte_array_new ();
|
||||
fail_unless (gst_meta_serialize_simple ((GstMeta *) meta, data));
|
||||
gst_buffer_unref (buf);
|
||||
|
||||
/* Create a new buffer */
|
||||
buf = gst_buffer_new_and_alloc (buf_size);
|
||||
guint32 consumed;
|
||||
meta = (GstAudioMeta *) gst_meta_deserialize (buf, data->data, data->len,
|
||||
&consumed);
|
||||
fail_unless (meta);
|
||||
fail_unless (consumed == data->len);
|
||||
g_byte_array_unref (data);
|
||||
|
||||
/* Check meta's content */
|
||||
g_assert_cmpint (meta->info.finfo->format, ==, GST_AUDIO_FORMAT_S32);
|
||||
g_assert_cmpint (meta->info.layout, ==, GST_AUDIO_LAYOUT_NON_INTERLEAVED);
|
||||
g_assert_cmpint (meta->info.rate, ==, 44100);
|
||||
g_assert_cmpint (meta->info.channels, ==, 2);
|
||||
g_assert_cmpint (meta->info.position[0], ==,
|
||||
GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT);
|
||||
g_assert_cmpint (meta->info.position[1], ==,
|
||||
GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT);
|
||||
g_assert_cmpuint (meta->samples, ==, samples);
|
||||
g_assert_cmpuint (meta->offsets[0], ==, 0);
|
||||
g_assert_cmpuint (meta->offsets[1], ==, buf_size / 2);
|
||||
|
||||
gst_buffer_unref (buf);
|
||||
}
|
||||
|
||||
GST_END_TEST;
|
||||
|
||||
GST_START_TEST (test_audio_meta_serialize_65_chans)
|
||||
{
|
||||
GstBuffer *buf;
|
||||
GstAudioInfo info;
|
||||
GstAudioMeta *meta;
|
||||
|
||||
/* With more than 64 channels we cannot have positions */
|
||||
gst_audio_info_init (&info);
|
||||
gst_audio_info_set_format (&info, GST_AUDIO_FORMAT_S32, 44100, 65, NULL);
|
||||
info.layout = GST_AUDIO_LAYOUT_NON_INTERLEAVED;
|
||||
|
||||
/* Add audio meta */
|
||||
gsize samples = 1;
|
||||
gsize buf_size = 4 * 65 * samples;
|
||||
buf = gst_buffer_new_and_alloc (buf_size);
|
||||
gsize offsets[65];
|
||||
for (int i = 0; i < 65; i++)
|
||||
offsets[i] = i * samples * 4;
|
||||
meta = gst_buffer_add_audio_meta (buf, &info, samples, offsets);
|
||||
|
||||
/* Serialize */
|
||||
GByteArray *data = g_byte_array_new ();
|
||||
fail_unless (gst_meta_serialize_simple ((GstMeta *) meta, data));
|
||||
gst_buffer_unref (buf);
|
||||
|
||||
/* Create a new buffer */
|
||||
buf = gst_buffer_new_and_alloc (buf_size);
|
||||
guint32 consumed;
|
||||
meta = (GstAudioMeta *) gst_meta_deserialize (buf, data->data, data->len,
|
||||
&consumed);
|
||||
fail_unless (meta);
|
||||
fail_unless (consumed == data->len);
|
||||
g_byte_array_unref (data);
|
||||
|
||||
/* Check meta's content */
|
||||
g_assert_cmpint (meta->info.finfo->format, ==, GST_AUDIO_FORMAT_S32);
|
||||
g_assert_cmpint (meta->info.layout, ==, GST_AUDIO_LAYOUT_NON_INTERLEAVED);
|
||||
g_assert_cmpint (meta->info.rate, ==, 44100);
|
||||
g_assert_cmpint (meta->info.channels, ==, 65);
|
||||
g_assert_cmpuint (meta->samples, ==, samples);
|
||||
for (int i = 0; i < 65; i++)
|
||||
g_assert_cmpuint (meta->offsets[i], ==, i * samples * 4);
|
||||
|
||||
gst_buffer_unref (buf);
|
||||
}
|
||||
|
||||
GST_END_TEST;
|
||||
|
||||
static Suite *
|
||||
audio_suite (void)
|
||||
{
|
||||
|
@ -1616,6 +1716,8 @@ audio_suite (void)
|
|||
tcase_add_test (tc_chain, test_audio_buffer_and_audio_meta);
|
||||
tcase_add_test (tc_chain, test_audio_info_from_caps);
|
||||
tcase_add_test (tc_chain, test_audio_make_raw_caps);
|
||||
tcase_add_test (tc_chain, test_audio_meta_serialize);
|
||||
tcase_add_test (tc_chain, test_audio_meta_serialize_65_chans);
|
||||
|
||||
return s;
|
||||
}
|
||||
|
|
|
@ -4215,6 +4215,70 @@ GST_START_TEST (test_info_dma_drm)
|
|||
|
||||
GST_END_TEST;
|
||||
|
||||
GST_START_TEST (test_video_meta_serialize)
|
||||
{
|
||||
GstBuffer *buf;
|
||||
GstVideoInfo info;
|
||||
GstVideoMeta *meta;
|
||||
GstVideoAlignment alig;
|
||||
|
||||
gst_video_alignment_reset (&alig);
|
||||
alig.padding_left = 2;
|
||||
alig.padding_right = 6;
|
||||
alig.padding_top = 2;
|
||||
alig.padding_bottom = 6;
|
||||
|
||||
gst_video_info_init (&info);
|
||||
gst_video_info_set_format (&info, GST_VIDEO_FORMAT_NV12, 1920, 1080);
|
||||
g_assert (gst_video_info_align (&info, &alig));
|
||||
|
||||
/* Add video meta */
|
||||
buf = gst_buffer_new ();
|
||||
meta = gst_buffer_add_video_meta_full (buf, GST_VIDEO_FRAME_FLAG_NONE,
|
||||
GST_VIDEO_INFO_FORMAT (&info), GST_VIDEO_INFO_WIDTH (&info),
|
||||
GST_VIDEO_INFO_HEIGHT (&info), GST_VIDEO_INFO_N_PLANES (&info),
|
||||
info.offset, info.stride);
|
||||
g_assert (gst_video_meta_set_alignment (meta, alig));
|
||||
|
||||
/* Serialize */
|
||||
GByteArray *data = g_byte_array_new ();
|
||||
fail_unless (gst_meta_serialize_simple ((GstMeta *) meta, data));
|
||||
gst_buffer_unref (buf);
|
||||
|
||||
/* Create a new buffer */
|
||||
buf = gst_buffer_new ();
|
||||
guint32 consumed;
|
||||
meta = (GstVideoMeta *) gst_meta_deserialize (buf, data->data, data->len,
|
||||
&consumed);
|
||||
fail_unless (meta);
|
||||
fail_unless (consumed == data->len);
|
||||
g_byte_array_unref (data);
|
||||
|
||||
/* Check meta's content */
|
||||
g_assert_cmpuint (meta->flags, ==, GST_VIDEO_FRAME_FLAG_NONE);
|
||||
g_assert_cmpuint (meta->format, ==, GST_VIDEO_FORMAT_NV12);
|
||||
g_assert_cmpuint (meta->id, ==, 0);
|
||||
g_assert_cmpuint (meta->width, ==, 1920);
|
||||
g_assert_cmpuint (meta->height, ==, 1080);
|
||||
g_assert_cmpuint (meta->alignment.padding_top, ==, 2);
|
||||
g_assert_cmpuint (meta->alignment.padding_bottom, ==, 6);
|
||||
g_assert_cmpuint (meta->alignment.padding_left, ==, 2);
|
||||
g_assert_cmpuint (meta->alignment.padding_right, ==, 6);
|
||||
|
||||
g_assert_cmpuint (meta->n_planes, ==, GST_VIDEO_INFO_N_PLANES (&info));
|
||||
for (int i = 0; i < meta->n_planes; i++) {
|
||||
g_assert_cmpuint (meta->stride[i], ==, GST_VIDEO_INFO_PLANE_STRIDE (&info,
|
||||
i));
|
||||
g_assert_cmpuint (meta->offset[i], ==, GST_VIDEO_INFO_PLANE_OFFSET (&info,
|
||||
i));
|
||||
g_assert_cmpuint (meta->alignment.stride_align[i], ==, 0);
|
||||
}
|
||||
|
||||
gst_buffer_unref (buf);
|
||||
}
|
||||
|
||||
GST_END_TEST;
|
||||
|
||||
static Suite *
|
||||
video_suite (void)
|
||||
{
|
||||
|
@ -4273,6 +4337,7 @@ video_suite (void)
|
|||
tcase_add_test (tc_chain, test_auto_video_frame_unmap);
|
||||
tcase_add_test (tc_chain, test_video_color_primaries_equivalent);
|
||||
tcase_add_test (tc_chain, test_info_dma_drm);
|
||||
tcase_add_test (tc_chain, test_video_meta_serialize);
|
||||
|
||||
return s;
|
||||
}
|
||||
|
|
|
@ -36,6 +36,7 @@
|
|||
#include <gst/gstbuffer.h>
|
||||
#include <gst/gstbufferlist.h>
|
||||
#include <gst/gstbufferpool.h>
|
||||
#include <gst/gstbytearrayinterface.h>
|
||||
#include <gst/gstcaps.h>
|
||||
#include <gst/gstcapsfeatures.h>
|
||||
#include <gst/gstchildproxy.h>
|
||||
|
|
|
@ -2860,6 +2860,49 @@ gst_reference_timestamp_meta_api_get_type (void)
|
|||
return type;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
timestamp_meta_serialize (const GstMeta * meta, GstByteArrayInterface * data,
|
||||
guint8 * version)
|
||||
{
|
||||
const GstReferenceTimestampMeta *rtmeta =
|
||||
(const GstReferenceTimestampMeta *) meta;
|
||||
gchar *caps_str = gst_caps_to_string (rtmeta->reference);
|
||||
gsize caps_str_len = strlen (caps_str);
|
||||
|
||||
gsize size = 16 + caps_str_len + 1;
|
||||
guint8 *ptr = gst_byte_array_interface_append (data, size);
|
||||
if (ptr == NULL) {
|
||||
g_free (caps_str);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
GST_WRITE_UINT64_LE (ptr, rtmeta->timestamp);
|
||||
GST_WRITE_UINT64_LE (ptr + 8, rtmeta->duration);
|
||||
memcpy (ptr + 16, caps_str, caps_str_len + 1);
|
||||
g_free (caps_str);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static GstMeta *
|
||||
timestamp_meta_deserialize (const GstMetaInfo * info, GstBuffer * buffer,
|
||||
const guint8 * data, gsize size, guint8 version)
|
||||
{
|
||||
/* Sanity check: caps_str must be 0-terminated. */
|
||||
if (version != 0 || size < 2 * sizeof (guint64) + 1 || data[size - 1] != '\0')
|
||||
return NULL;
|
||||
|
||||
guint64 timestamp = GST_READ_UINT64_LE (data);
|
||||
guint64 duration = GST_READ_UINT64_LE (data + 8);
|
||||
const gchar *caps_str = (const gchar *) data + 16;
|
||||
GstCaps *reference = gst_caps_from_string (caps_str);
|
||||
GstMeta *meta = (GstMeta *) gst_buffer_add_reference_timestamp_meta (buffer,
|
||||
reference, timestamp, duration);
|
||||
gst_caps_unref (reference);
|
||||
|
||||
return meta;
|
||||
}
|
||||
|
||||
/**
|
||||
* gst_reference_timestamp_meta_get_info:
|
||||
*
|
||||
|
@ -2876,12 +2919,15 @@ gst_reference_timestamp_meta_get_info (void)
|
|||
|
||||
if (g_once_init_enter ((GstMetaInfo **) & meta_info)) {
|
||||
const GstMetaInfo *meta =
|
||||
gst_meta_register (gst_reference_timestamp_meta_api_get_type (),
|
||||
gst_meta_register_serializable
|
||||
(gst_reference_timestamp_meta_api_get_type (),
|
||||
"GstReferenceTimestampMeta",
|
||||
sizeof (GstReferenceTimestampMeta),
|
||||
(GstMetaInitFunction) _gst_reference_timestamp_meta_init,
|
||||
(GstMetaFreeFunction) _gst_reference_timestamp_meta_free,
|
||||
_gst_reference_timestamp_meta_transform);
|
||||
_gst_reference_timestamp_meta_transform,
|
||||
timestamp_meta_serialize,
|
||||
timestamp_meta_deserialize);
|
||||
g_once_init_leave ((GstMetaInfo **) & meta_info, (GstMetaInfo *) meta);
|
||||
}
|
||||
|
||||
|
|
|
@ -763,6 +763,9 @@ typedef struct _GstReferenceTimestampMeta GstReferenceTimestampMeta;
|
|||
* * `timestamp/x-unix`: for timestamps based on the UNIX epoch according to
|
||||
* the local clock.
|
||||
*
|
||||
* Since 1.24 it can be serialized using gst_meta_serialize() and
|
||||
* gst_meta_deserialize().
|
||||
*
|
||||
* Since: 1.14
|
||||
*/
|
||||
struct _GstReferenceTimestampMeta
|
||||
|
|
129
subprojects/gstreamer/gst/gstbytearrayinterface.h
Normal file
129
subprojects/gstreamer/gst/gstbytearrayinterface.h
Normal file
|
@ -0,0 +1,129 @@
|
|||
/* Copyright (C) 2023 Netflix Inc.
|
||||
* Author: Xavier Claessens <xavier.claessens@collabora.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License along with this library; if not, write to the
|
||||
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
|
||||
* Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <glib.h>
|
||||
#include <gst/gstconfig.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
/**
|
||||
* GstByteArrayInterface:
|
||||
* @data: A pointer to an array of bytes.
|
||||
* @len: Number of bytes in @data.
|
||||
* @resize: Reallocate @data.
|
||||
*
|
||||
* Interface for an array of bytes. It is expected to be subclassed to implement
|
||||
* @resize virtual method using language native array implementation, such as
|
||||
* GLib's #GByteArray, C++'s `std::vector<uint8_t>` or Rust's `Vec<u8>`.
|
||||
*
|
||||
* @resize implementation could allocate more than requested to avoid repeated
|
||||
* reallocations. It can return %FALSE, or be set to %NULL, in the case the
|
||||
* array cannot grow.
|
||||
*
|
||||
* Since: 1.24
|
||||
*/
|
||||
typedef struct _GstByteArrayInterface GstByteArrayInterface;
|
||||
struct _GstByteArrayInterface
|
||||
{
|
||||
guint8 *data;
|
||||
gsize len;
|
||||
gboolean (*resize) (GstByteArrayInterface *self, gsize length);
|
||||
|
||||
/* < private > */
|
||||
gpointer _gst_reserved[GST_PADDING];
|
||||
};
|
||||
|
||||
/**
|
||||
* gst_byte_array_interface_init:
|
||||
* @self: A #GstByteArrayInterface.
|
||||
* @length: New size.
|
||||
*
|
||||
* Initialize #GstByteArrayInterface structure.
|
||||
*
|
||||
* Since: 1.24
|
||||
*/
|
||||
static inline void
|
||||
gst_byte_array_interface_init (GstByteArrayInterface *self)
|
||||
{
|
||||
memset (self, 0, sizeof (GstByteArrayInterface));
|
||||
}
|
||||
|
||||
/**
|
||||
* gst_byte_array_interface_set_size:
|
||||
* @self: A #GstByteArrayInterface.
|
||||
* @length: New size.
|
||||
*
|
||||
* Reallocate data pointer to fit at least @length bytes. @self->len is updated
|
||||
* to @length.
|
||||
*
|
||||
* Returns: %TRUE on success, %FALSE otherwise.
|
||||
* Since: 1.24
|
||||
*/
|
||||
static inline gboolean
|
||||
gst_byte_array_interface_set_size (GstByteArrayInterface *self, gsize length)
|
||||
{
|
||||
if (self->resize == NULL || !self->resize (self, length))
|
||||
return FALSE;
|
||||
self->len = length;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* gst_byte_array_interface_append:
|
||||
* @self: A #GstByteArrayInterface.
|
||||
* @size: Number of bytes to append to the array.
|
||||
*
|
||||
* Grow the array by @size bytes and return a pointer to the newly added memory.
|
||||
*
|
||||
* Returns: Pointer to added memory, or %NULL if reallocation failed.
|
||||
* Since: 1.24
|
||||
*/
|
||||
static inline guint8 *
|
||||
gst_byte_array_interface_append (GstByteArrayInterface *self, gsize size)
|
||||
{
|
||||
gsize orig = self->len;
|
||||
if (!gst_byte_array_interface_set_size (self, self->len + size))
|
||||
return NULL;
|
||||
return self->data + orig;
|
||||
}
|
||||
|
||||
/**
|
||||
* gst_byte_array_interface_append_data:
|
||||
* @self: A #GstByteArrayInterface.
|
||||
* @data: Source data.
|
||||
* @size: Size of @data.
|
||||
*
|
||||
* Append @size bytes from @data, reallocating @self->data pointer if necessary.
|
||||
*
|
||||
* Returns: %TRUE on success, %FALSE otherwise.
|
||||
* Since: 1.24
|
||||
*/
|
||||
static inline gboolean
|
||||
gst_byte_array_interface_append_data (GstByteArrayInterface *self, const guint8 *data, gsize size)
|
||||
{
|
||||
guint8 *ptr = gst_byte_array_interface_append (self, size);
|
||||
if (ptr == NULL)
|
||||
return FALSE;
|
||||
memcpy (ptr, data, size);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
G_END_DECLS
|
|
@ -193,6 +193,46 @@ custom_transform_func (GstBuffer * transbuf, GstMeta * meta,
|
|||
return TRUE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
custom_serialize_func (const GstMeta * meta, GstByteArrayInterface * data,
|
||||
guint8 * version)
|
||||
{
|
||||
const GstCustomMeta *cmeta = (const GstCustomMeta *) meta;
|
||||
gchar *str =
|
||||
gst_structure_serialize (cmeta->structure, GST_SERIALIZE_FLAG_STRICT);
|
||||
if (str == NULL)
|
||||
return FALSE;
|
||||
|
||||
gboolean ret = gst_byte_array_interface_append_data (data, (guint8 *) str,
|
||||
strlen (str) + 1);
|
||||
g_free (str);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static GstMeta *
|
||||
custom_deserialize_func (const GstMetaInfo * info, GstBuffer * buffer,
|
||||
const guint8 * data, gsize size, guint8 version)
|
||||
{
|
||||
if (version != 0 || size < 1 || data[size - 1] != '\0')
|
||||
return NULL;
|
||||
|
||||
GstStructure *structure =
|
||||
gst_structure_new_from_string ((const gchar *) data);
|
||||
if (structure == NULL)
|
||||
return NULL;
|
||||
|
||||
GstMeta *meta = gst_buffer_add_meta (buffer, info, NULL);
|
||||
GstCustomMeta *cmeta = (GstCustomMeta *) meta;
|
||||
|
||||
gst_structure_set_parent_refcount (cmeta->structure, NULL);
|
||||
gst_structure_take (&cmeta->structure, structure);
|
||||
gst_structure_set_parent_refcount (cmeta->structure,
|
||||
&GST_MINI_OBJECT_REFCOUNT (buffer));
|
||||
|
||||
return meta;
|
||||
}
|
||||
|
||||
/**
|
||||
* gst_custom_meta_get_structure:
|
||||
*
|
||||
|
@ -274,9 +314,10 @@ gst_meta_register_custom (const gchar * name, const gchar ** tags,
|
|||
if (api == G_TYPE_INVALID)
|
||||
goto done;
|
||||
|
||||
info = (GstMetaInfoImpl *) gst_meta_register (api, name,
|
||||
info = (GstMetaInfoImpl *) gst_meta_register_serializable (api, name,
|
||||
sizeof (GstCustomMeta),
|
||||
custom_init_func, custom_free_func, custom_transform_func);
|
||||
custom_init_func, custom_free_func, custom_transform_func,
|
||||
custom_serialize_func, custom_deserialize_func);
|
||||
|
||||
if (!info)
|
||||
goto done;
|
||||
|
@ -364,28 +405,12 @@ gst_meta_api_type_get_tags (GType api)
|
|||
return (const gchar * const *) tags;
|
||||
}
|
||||
|
||||
/**
|
||||
* gst_meta_register:
|
||||
* @api: the type of the #GstMeta API
|
||||
* @impl: the name of the #GstMeta implementation
|
||||
* @size: the size of the #GstMeta structure
|
||||
* @init_func: (scope async): a #GstMetaInitFunction
|
||||
* @free_func: (scope async): a #GstMetaFreeFunction
|
||||
* @transform_func: (scope async): a #GstMetaTransformFunction
|
||||
*
|
||||
* Register a new #GstMeta implementation.
|
||||
*
|
||||
* The same @info can be retrieved later with gst_meta_get_info() by using
|
||||
* @impl as the key.
|
||||
*
|
||||
* Returns: (transfer none): a #GstMetaInfo that can be used to
|
||||
* access metadata.
|
||||
*/
|
||||
|
||||
const GstMetaInfo *
|
||||
gst_meta_register (GType api, const gchar * impl, gsize size,
|
||||
static const GstMetaInfo *
|
||||
gst_meta_register_internal (GType api, const gchar * impl, gsize size,
|
||||
GstMetaInitFunction init_func, GstMetaFreeFunction free_func,
|
||||
GstMetaTransformFunction transform_func)
|
||||
GstMetaTransformFunction transform_func,
|
||||
GstMetaSerializeFunction serialize_func,
|
||||
GstMetaDeserializeFunction deserialize_func)
|
||||
{
|
||||
GstMetaInfo *info;
|
||||
GType type;
|
||||
|
@ -412,6 +437,8 @@ gst_meta_register (GType api, const gchar * impl, gsize size,
|
|||
info->init_func = init_func;
|
||||
info->free_func = free_func;
|
||||
info->transform_func = transform_func;
|
||||
info->serialize_func = serialize_func;
|
||||
info->deserialize_func = deserialize_func;
|
||||
((GstMetaInfoImpl *) info)->is_custom = FALSE;
|
||||
|
||||
GST_CAT_DEBUG (GST_CAT_META,
|
||||
|
@ -426,6 +453,62 @@ gst_meta_register (GType api, const gchar * impl, gsize size,
|
|||
return info;
|
||||
}
|
||||
|
||||
/**
|
||||
* gst_meta_register:
|
||||
* @api: the type of the #GstMeta API
|
||||
* @impl: the name of the #GstMeta implementation
|
||||
* @size: the size of the #GstMeta structure
|
||||
* @init_func: (scope async): a #GstMetaInitFunction
|
||||
* @free_func: (scope async): a #GstMetaFreeFunction
|
||||
* @transform_func: (scope async): a #GstMetaTransformFunction
|
||||
*
|
||||
* Register a new #GstMeta implementation.
|
||||
*
|
||||
* The same @info can be retrieved later with gst_meta_get_info() by using
|
||||
* @impl as the key.
|
||||
*
|
||||
* Returns: (transfer none): a #GstMetaInfo that can be used to
|
||||
* access metadata.
|
||||
*/
|
||||
const GstMetaInfo *
|
||||
gst_meta_register (GType api, const gchar * impl, gsize size,
|
||||
GstMetaInitFunction init_func, GstMetaFreeFunction free_func,
|
||||
GstMetaTransformFunction transform_func)
|
||||
{
|
||||
return gst_meta_register_internal (api, impl, size, init_func, free_func,
|
||||
transform_func, NULL, NULL);
|
||||
}
|
||||
|
||||
/**
|
||||
* gst_meta_register_serializable:
|
||||
* @api: the type of the #GstMeta API
|
||||
* @impl: the name of the #GstMeta implementation
|
||||
* @size: the size of the #GstMeta structure
|
||||
* @init_func: (scope async): a #GstMetaInitFunction
|
||||
* @free_func: (scope async): a #GstMetaFreeFunction
|
||||
* @transform_func: (scope async): a #GstMetaTransformFunction
|
||||
* @serialize_func: (scope async): a #GstMetaSerializeFunction
|
||||
* @deserialize_func: (scope async): a #GstMetaDeserializeFunction
|
||||
*
|
||||
* Same as gst_meta_register() but also set serialize/deserialize functions.
|
||||
*
|
||||
* Returns: (transfer none): a #GstMetaInfo that can be used to access metadata.
|
||||
*
|
||||
* Since: 1.24
|
||||
*/
|
||||
const GstMetaInfo *
|
||||
gst_meta_register_serializable (GType api, const gchar * impl, gsize size,
|
||||
GstMetaInitFunction init_func, GstMetaFreeFunction free_func,
|
||||
GstMetaTransformFunction transform_func,
|
||||
GstMetaSerializeFunction serialize_func,
|
||||
GstMetaDeserializeFunction deserialize_func)
|
||||
{
|
||||
g_return_val_if_fail (serialize_func != NULL, NULL);
|
||||
g_return_val_if_fail (deserialize_func != NULL, NULL);
|
||||
return gst_meta_register_internal (api, impl, size, init_func, free_func,
|
||||
transform_func, serialize_func, deserialize_func);
|
||||
}
|
||||
|
||||
/**
|
||||
* gst_meta_get_info:
|
||||
* @impl: the name
|
||||
|
@ -497,3 +580,180 @@ gst_meta_compare_seqnum (const GstMeta * meta1, const GstMeta * meta2)
|
|||
|
||||
return (seqnum1 < seqnum2) ? -1 : 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* gst_meta_serialize:
|
||||
* @meta: a #GstMeta
|
||||
* @data: #GstByteArrayInterface to append serialization data
|
||||
*
|
||||
* Serialize @meta into a format that can be stored or transmitted and later
|
||||
* deserialized by gst_meta_deserialize().
|
||||
*
|
||||
* This is only supported for meta that implements #GstMetaInfo.serialize_func,
|
||||
* %FALSE is returned otherwise.
|
||||
*
|
||||
* Upon failure, @data->data pointer could have been reallocated, but @data->len
|
||||
* won't be modified. This is intended to be able to append multiple metas
|
||||
* into the same #GByteArray.
|
||||
*
|
||||
* Since serialization size is often the same for every buffer, caller may want
|
||||
* to remember the size of previous data to preallocate the next.
|
||||
*
|
||||
* Returns: %TRUE on success, %FALSE otherwise.
|
||||
*
|
||||
* Since: 1.24
|
||||
*/
|
||||
gboolean
|
||||
gst_meta_serialize (const GstMeta * meta, GstByteArrayInterface * data)
|
||||
{
|
||||
g_return_val_if_fail (meta != NULL, FALSE);
|
||||
g_return_val_if_fail (data != NULL, FALSE);
|
||||
|
||||
if (meta->info->serialize_func != NULL) {
|
||||
const gchar *name = g_type_name (meta->info->type);
|
||||
guint32 name_len = strlen (name);
|
||||
guint32 orig_len = data->len;
|
||||
guint8 version = 0;
|
||||
|
||||
/* Format: [total size][name_len][name][\0][version][payload]
|
||||
* Preallocate space for header but only write it on success because we
|
||||
* don't have every info yet.
|
||||
*/
|
||||
guint8 header_size = 2 * sizeof (guint32) + name_len + 2;
|
||||
if (!gst_byte_array_interface_set_size (data, data->len + header_size))
|
||||
return FALSE;
|
||||
if (meta->info->serialize_func (meta, data, &version)) {
|
||||
guint8 *header = data->data + orig_len;
|
||||
GST_WRITE_UINT32_LE (header + 0, data->len - orig_len);
|
||||
GST_WRITE_UINT32_LE (header + 4, name_len);
|
||||
memcpy (header + 8, name, name_len + 1);
|
||||
header[header_size - 1] = version;
|
||||
return TRUE;
|
||||
}
|
||||
// Serialization failed, rollback.
|
||||
gst_byte_array_interface_set_size (data, orig_len);
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
typedef struct
|
||||
{
|
||||
GstByteArrayInterface parent;
|
||||
GByteArray *data;
|
||||
} ByteArrayImpl;
|
||||
|
||||
static gboolean
|
||||
byte_array_impl_resize (GstByteArrayInterface * parent, gsize length)
|
||||
{
|
||||
ByteArrayImpl *self = (ByteArrayImpl *) parent;
|
||||
|
||||
g_byte_array_set_size (self->data, length);
|
||||
parent->data = self->data->data;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* gst_meta_serialize_simple:
|
||||
* @meta: a #GstMeta
|
||||
* @data: #GByteArray to append serialization data
|
||||
*
|
||||
* Same as gst_meta_serialize() but with a #GByteArray instead of
|
||||
* #GstByteArrayInterface.
|
||||
*
|
||||
* Returns: %TRUE on success, %FALSE otherwise.
|
||||
*
|
||||
* Since: 1.24
|
||||
*/
|
||||
gboolean
|
||||
gst_meta_serialize_simple (const GstMeta * meta, GByteArray * data)
|
||||
{
|
||||
ByteArrayImpl impl;
|
||||
|
||||
gst_byte_array_interface_init (&impl.parent);
|
||||
impl.parent.data = data->data;
|
||||
impl.parent.len = data->len;
|
||||
impl.parent.resize = byte_array_impl_resize;
|
||||
impl.data = data;
|
||||
return gst_meta_serialize (meta, (GstByteArrayInterface *) & impl);
|
||||
}
|
||||
|
||||
/**
|
||||
* gst_meta_deserialize:
|
||||
* @buffer: a #GstBuffer
|
||||
* @data: serialization data obtained from gst_meta_serialize()
|
||||
* @size: size of @data
|
||||
* @consumed: (out): total size used by this meta, could be less than @size
|
||||
*
|
||||
* Recreate a #GstMeta from serialized data returned by
|
||||
* gst_meta_serialize() and add it to @buffer.
|
||||
*
|
||||
* Note that the meta must have been previously registered by calling one of
|
||||
* `gst_*_meta_get_info ()` functions.
|
||||
*
|
||||
* @consumed is set to the number of bytes that can be skipped from @data to
|
||||
* find the next meta serialization, if any. In case of parsing error that does
|
||||
* not allow to determine that size, @consumed is set to 0.
|
||||
*
|
||||
* Returns: (transfer none) (nullable): the metadata owned by @buffer, or %NULL.
|
||||
*
|
||||
* Since: 1.24
|
||||
*/
|
||||
GstMeta *
|
||||
gst_meta_deserialize (GstBuffer * buffer, const guint8 * data, gsize size,
|
||||
guint32 * consumed)
|
||||
{
|
||||
g_return_val_if_fail (GST_IS_BUFFER (buffer), NULL);
|
||||
g_return_val_if_fail (data != NULL, NULL);
|
||||
g_return_val_if_fail (consumed != NULL, NULL);
|
||||
|
||||
*consumed = 0;
|
||||
|
||||
/* Format: [total size][name_len][name][\0][version][payload] */
|
||||
if (size < 2 * sizeof (guint32))
|
||||
goto bad_header;
|
||||
|
||||
guint32 total_size = GST_READ_UINT32_LE (data + 0);
|
||||
guint32 name_len = GST_READ_UINT32_LE (data + 4);
|
||||
guint32 header_size = 2 * sizeof (guint32) + name_len + 2;
|
||||
if (size < total_size || total_size < header_size)
|
||||
goto bad_header;
|
||||
|
||||
guint8 version = data[header_size - 1];
|
||||
const gchar *name = (const gchar *) (data + 2 * sizeof (guint32));
|
||||
if (name[name_len] != '\0')
|
||||
goto bad_header;
|
||||
|
||||
*consumed = total_size;
|
||||
|
||||
const GstMetaInfo *info = gst_meta_get_info (name);
|
||||
if (info == NULL) {
|
||||
GST_CAT_WARNING (GST_CAT_META,
|
||||
"%s does not correspond to a registered meta", name);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (info->deserialize_func == NULL) {
|
||||
GST_CAT_WARNING (GST_CAT_META, "Meta %s does not support deserialization",
|
||||
name);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
const guint8 *payload = data + header_size;
|
||||
guint32 payload_size = total_size - header_size;
|
||||
GstMeta *meta =
|
||||
info->deserialize_func (info, buffer, payload, payload_size, version);
|
||||
if (meta == NULL) {
|
||||
GST_CAT_WARNING (GST_CAT_META, "Failed to deserialize %s payload", name);
|
||||
GST_CAT_MEMDUMP (GST_CAT_META, "Meta serialization payload", payload,
|
||||
payload_size);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return meta;
|
||||
|
||||
bad_header:
|
||||
GST_CAT_WARNING (GST_CAT_META, "Could not parse meta serialization header");
|
||||
GST_CAT_MEMDUMP (GST_CAT_META, "Meta serialization data", data, size);
|
||||
return NULL;
|
||||
}
|
||||
|
|
|
@ -25,6 +25,9 @@
|
|||
|
||||
#include <glib.h>
|
||||
|
||||
#include <gst/gstbytearrayinterface.h>
|
||||
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
typedef struct _GstMeta GstMeta;
|
||||
|
@ -132,6 +135,10 @@ struct _GstMeta {
|
|||
* Extra custom metadata. The @structure field is the same as returned by
|
||||
* gst_custom_meta_get_structure().
|
||||
*
|
||||
* Since 1.24 it can be serialized using gst_meta_serialize() and
|
||||
* gst_meta_deserialize(), but only if the #GstStructure does not contain any
|
||||
* fields that cannot be serialized, see %GST_SERIALIZE_FLAG_STRICT.
|
||||
*
|
||||
* Since: 1.20
|
||||
*/
|
||||
typedef struct {
|
||||
|
@ -211,7 +218,6 @@ GST_API GQuark _gst_meta_transform_clear;
|
|||
*/
|
||||
#define GST_META_TRANSFORM_IS_CLEAR(type) ((type) == _gst_meta_transform_clear)
|
||||
|
||||
|
||||
/**
|
||||
* GstMetaTransformFunction:
|
||||
* @transbuf: a #GstBuffer
|
||||
|
@ -258,6 +264,61 @@ typedef gboolean (*GstCustomMetaTransformFunction) (GstBuffer *transbuf,
|
|||
GstCustomMeta *meta, GstBuffer *buffer,
|
||||
GQuark type, gpointer data, gpointer user_data);
|
||||
|
||||
/**
|
||||
* GstMetaSerializeFunction:
|
||||
* @meta: a #GstMeta
|
||||
* @data: #GstByteArrayInterface to append serialization data
|
||||
* @version: (out): version of the serialization format
|
||||
*
|
||||
* Serialize @meta into a format that can be stored or transmitted and later
|
||||
* deserialized by #GstMetaDeserializeFunction.
|
||||
*
|
||||
* By default version is set to 0, it should be bumped if incompatible changes
|
||||
* are made to the format so %GstMetaDeserializeFunction can deserialize each
|
||||
* version.
|
||||
*
|
||||
* Returns: %TRUE on success, %FALSE otherwise.
|
||||
*
|
||||
* Since: 1.24
|
||||
*/
|
||||
typedef gboolean (*GstMetaSerializeFunction) (const GstMeta *meta,
|
||||
GstByteArrayInterface *data, guint8 *version);
|
||||
|
||||
/**
|
||||
* GstMetaDeserializeFunction:
|
||||
* @info: #GstMetaInfo of the meta
|
||||
* @buffer: a #GstBuffer
|
||||
* @data: data obtained from #GstMetaSerializeFunction
|
||||
* @size: size of data to avoid buffer overflow
|
||||
*
|
||||
* Recreate a #GstMeta from serialized data returned by
|
||||
* #GstMetaSerializeFunction and add it to @buffer.
|
||||
*
|
||||
* Returns: (transfer none) (nullable): the metadata owned by @buffer, or %NULL.
|
||||
*
|
||||
* Since: 1.24
|
||||
*/
|
||||
typedef GstMeta *(*GstMetaDeserializeFunction) (const GstMetaInfo *info,
|
||||
GstBuffer *buffer, const guint8 *data, gsize size, guint8 version);
|
||||
|
||||
/**
|
||||
* GstMetaInfo.serialize_func:
|
||||
*
|
||||
* Function for serializing the metadata, or %NULL if not supported by this
|
||||
* meta.
|
||||
*
|
||||
* Since: 1.24
|
||||
*/
|
||||
|
||||
/**
|
||||
* GstMetaInfo.deserialize_func:
|
||||
*
|
||||
* Function for deserializing the metadata, or %NULL if not supported by this
|
||||
* meta.
|
||||
*
|
||||
* Since: 1.24
|
||||
*/
|
||||
|
||||
/**
|
||||
* GstMetaInfo:
|
||||
* @api: tag identifying the metadata structure and api
|
||||
|
@ -266,6 +327,10 @@ typedef gboolean (*GstCustomMetaTransformFunction) (GstBuffer *transbuf,
|
|||
* @init_func: function for initializing the metadata
|
||||
* @free_func: function for freeing the metadata
|
||||
* @transform_func: function for transforming the metadata
|
||||
* @serialize_func: function for serializing the metadata into a #GstStructure,
|
||||
* or %NULL if not supported by this meta. (Since 1.24)
|
||||
* @deserialize_func: function for deserializing the metadata from a
|
||||
* #GstStructure, or %NULL if not supported by this meta. (Since 1.24)
|
||||
*
|
||||
* The #GstMetaInfo provides information about a specific metadata
|
||||
* structure.
|
||||
|
@ -278,6 +343,8 @@ struct _GstMetaInfo {
|
|||
GstMetaInitFunction init_func;
|
||||
GstMetaFreeFunction free_func;
|
||||
GstMetaTransformFunction transform_func;
|
||||
GstMetaSerializeFunction serialize_func;
|
||||
GstMetaDeserializeFunction deserialize_func;
|
||||
|
||||
/* No padding needed, GstMetaInfo is always allocated by GStreamer and is
|
||||
* not subclassable or stack-allocatable, so we can extend it as we please
|
||||
|
@ -297,6 +364,15 @@ const GstMetaInfo * gst_meta_register (GType api, const gchar *impl,
|
|||
GstMetaFreeFunction free_func,
|
||||
GstMetaTransformFunction transform_func);
|
||||
|
||||
GST_API
|
||||
const GstMetaInfo * gst_meta_register_serializable (GType api, const gchar *impl,
|
||||
gsize size,
|
||||
GstMetaInitFunction init_func,
|
||||
GstMetaFreeFunction free_func,
|
||||
GstMetaTransformFunction transform_func,
|
||||
GstMetaSerializeFunction serialize_func,
|
||||
GstMetaDeserializeFunction deserialize_func);
|
||||
|
||||
GST_API
|
||||
const GstMetaInfo * gst_meta_register_custom (const gchar *name, const gchar **tags,
|
||||
GstCustomMetaTransformFunction transform_func,
|
||||
|
@ -327,6 +403,18 @@ GST_API
|
|||
gint gst_meta_compare_seqnum (const GstMeta * meta1,
|
||||
const GstMeta * meta2);
|
||||
|
||||
GST_API
|
||||
gboolean gst_meta_serialize (const GstMeta *meta,
|
||||
GstByteArrayInterface *data);
|
||||
GST_API
|
||||
gboolean gst_meta_serialize_simple (const GstMeta *meta,
|
||||
GByteArray *data);
|
||||
GST_API
|
||||
GstMeta * gst_meta_deserialize (GstBuffer *buffer,
|
||||
const guint8 *data,
|
||||
gsize size,
|
||||
guint32 *consumed);
|
||||
|
||||
/* some default tags */
|
||||
|
||||
GST_API GQuark _gst_meta_tag_memory;
|
||||
|
|
|
@ -82,6 +82,7 @@ gst_headers = files(
|
|||
'gstbufferlist.h',
|
||||
'gstbufferpool.h',
|
||||
'gstbus.h',
|
||||
'gstbytearrayinterface.h',
|
||||
'gstcaps.h',
|
||||
'gstcapsfeatures.h',
|
||||
'gstchildproxy.h',
|
||||
|
|
|
@ -258,7 +258,7 @@ GST_START_TEST (test_metadata_writable)
|
|||
ASSERT_BUFFER_REFCOUNT (buffer, "buffer", 2);
|
||||
fail_unless (gst_buffer_is_writable (buffer) == FALSE);
|
||||
|
||||
/* Check that make_metadata_writable produces a new sub-buffer with
|
||||
/* Check that make_metadata_writable produces a new sub-buffer with
|
||||
* writable metadata. */
|
||||
sub1 = gst_buffer_make_writable (buffer);
|
||||
fail_if (sub1 == buffer);
|
||||
|
@ -967,6 +967,37 @@ GST_START_TEST (test_auto_unmap)
|
|||
|
||||
GST_END_TEST;
|
||||
|
||||
GST_START_TEST (test_reference_timestamp_meta_serialization)
|
||||
{
|
||||
GstCaps *reference =
|
||||
gst_caps_new_simple ("timestamp/x-unix", NULL, NULL, NULL);
|
||||
|
||||
/* Serialize */
|
||||
GstBuffer *buffer = gst_buffer_new ();
|
||||
GstReferenceTimestampMeta *meta =
|
||||
gst_buffer_add_reference_timestamp_meta (buffer, reference, 1, 2);
|
||||
GByteArray *data = g_byte_array_new ();
|
||||
fail_unless (gst_meta_serialize_simple ((GstMeta *) meta, data));
|
||||
gst_buffer_unref (buffer);
|
||||
|
||||
/* Deserialize */
|
||||
buffer = gst_buffer_new ();
|
||||
guint32 consumed;
|
||||
meta = (GstReferenceTimestampMeta *) gst_meta_deserialize (buffer, data->data,
|
||||
data->len, &consumed);
|
||||
fail_unless (meta);
|
||||
fail_unless (consumed == data->len);
|
||||
fail_unless (gst_caps_is_equal (meta->reference, reference));
|
||||
fail_unless_equals_uint64 (meta->timestamp, 1);
|
||||
fail_unless_equals_uint64 (meta->duration, 2);
|
||||
gst_buffer_unref (buffer);
|
||||
|
||||
gst_caps_unref (reference);
|
||||
g_byte_array_unref (data);
|
||||
}
|
||||
|
||||
GST_END_TEST;
|
||||
|
||||
static Suite *
|
||||
gst_buffer_suite (void)
|
||||
{
|
||||
|
@ -994,6 +1025,7 @@ gst_buffer_suite (void)
|
|||
tcase_add_test (tc_chain, test_wrapped_bytes);
|
||||
tcase_add_test (tc_chain, test_new_memdup);
|
||||
tcase_add_test (tc_chain, test_auto_unmap);
|
||||
tcase_add_test (tc_chain, test_reference_timestamp_meta_serialization);
|
||||
|
||||
return s;
|
||||
}
|
||||
|
|
|
@ -808,6 +808,53 @@ GST_START_TEST (test_meta_custom_transform)
|
|||
|
||||
GST_END_TEST;
|
||||
|
||||
GST_START_TEST (test_meta_custom_serialize)
|
||||
{
|
||||
const GstMetaInfo *info;
|
||||
GstCustomMeta *meta;
|
||||
GstBuffer *buffer;
|
||||
|
||||
info = gst_meta_register_custom_simple ("test-custom-serialize");
|
||||
fail_unless (info != NULL);
|
||||
|
||||
/* add some metadata */
|
||||
buffer = gst_buffer_new ();
|
||||
meta = gst_buffer_add_custom_meta (buffer, "test-custom-serialize");
|
||||
gst_structure_set (meta->structure, "test-field", G_TYPE_INT, 42, NULL);
|
||||
|
||||
/* Serialize */
|
||||
GByteArray *data = g_byte_array_new ();
|
||||
fail_unless (gst_meta_serialize_simple ((GstMeta *) meta, data));
|
||||
gst_buffer_unref (buffer);
|
||||
|
||||
/* Create a new buffer */
|
||||
buffer = gst_buffer_new ();
|
||||
guint32 consumed;
|
||||
meta = (GstCustomMeta *) gst_meta_deserialize (buffer, data->data, data->len,
|
||||
&consumed);
|
||||
fail_unless (meta);
|
||||
fail_unless (consumed == data->len);
|
||||
|
||||
/* Check meta's content */
|
||||
fail_unless (gst_custom_meta_has_name (meta, "test-custom-serialize"));
|
||||
gint val;
|
||||
fail_unless (gst_structure_get_int (meta->structure, "test-field", &val));
|
||||
fail_unless_equals_int (val, 42);
|
||||
|
||||
/* Add field that cannot be serialized */
|
||||
GstElement *bin = gst_bin_new ("mybin");
|
||||
gst_structure_set (meta->structure, "test-field-obj", GST_TYPE_BIN, bin,
|
||||
NULL);
|
||||
g_byte_array_set_size (data, 0);
|
||||
fail_if (gst_meta_serialize_simple ((GstMeta *) meta, data));
|
||||
fail_if (data->len != 0);
|
||||
gst_object_unref (bin);
|
||||
gst_buffer_unref (buffer);
|
||||
g_byte_array_unref (data);
|
||||
}
|
||||
|
||||
GST_END_TEST;
|
||||
|
||||
static Suite *
|
||||
gst_buffermeta_suite (void)
|
||||
{
|
||||
|
@ -827,6 +874,7 @@ gst_buffermeta_suite (void)
|
|||
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);
|
||||
tcase_add_test (tc_chain, test_meta_custom_serialize);
|
||||
|
||||
return s;
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue