This attemps to partially implement MISB ST1608.1, "Transport of Motion Imagery and Metadata over GigE Vision". This relies on GstKLVMeta, which is currently a merge request 124 for gst-plugins-base. For now we include it here. Currently all KLVMeta is packed into one chunk, no special handling of timestamps is done. Testing has only been done between pleorasink and pleorasrc, no other MISB-compliant stream.
331 lines
8.6 KiB
C
331 lines
8.6 KiB
C
/* GStreamer KLV Metadata Support Library
|
|
* Copyright (C) 2016-2019 Tim-Philipp Müller <tim@centricular.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.
|
|
*/
|
|
|
|
/**
|
|
* SECTION:gsttagklv
|
|
* @short_description: KLV metadata support
|
|
* @title: KLV metadata support
|
|
*
|
|
* <refsect2>
|
|
* <para>
|
|
* Utility functions around KLV metadata support in GStreamer.
|
|
* </para>
|
|
* <para>
|
|
* See ITU Recommendation BT.1563-1 or SMPTE 336M for the KLV standard;
|
|
* see MISB EG 0902 (MISB Minimum Metadata Set) and other MISB standards
|
|
* and recommended practices for examples of KLV metadata local data sets
|
|
* for a variety of use cases (timestamps, GPS coordinates, speed, etc.).
|
|
* </para>
|
|
* </refsect2>
|
|
*/
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
#include "config.h"
|
|
#endif
|
|
|
|
#include <gst/tag/tag.h>
|
|
#include "klv.h"
|
|
|
|
/* We hide the implementation details, so that we have the option to implement
|
|
* different/more efficient storage in future (unfortunately we can't put the
|
|
* data inline with the meta allocation though since it's registered as fixed
|
|
* size, although that could probably be fixed in a backwards compatible way).
|
|
* GBytes means effectively two allocations, one for the GBytes, one for the
|
|
* data. Plus one for the KLV meta struct.
|
|
*
|
|
* For now we also assume that KLV data is always self-contained and one single
|
|
* chunk of data, but in future we may have use cases where we might want to
|
|
* relax that requirement. */
|
|
typedef struct
|
|
{
|
|
GstKLVMeta klv_meta;
|
|
GBytes *bytes;
|
|
} GstKLVMetaImpl;
|
|
|
|
GType
|
|
gst_klv_meta_api_get_type (void)
|
|
{
|
|
static volatile GType type;
|
|
static const gchar *tags[] = { NULL };
|
|
|
|
if (g_once_init_enter (&type)) {
|
|
GType _type = gst_meta_api_type_register ("GstKLVMetaAPI", tags);
|
|
g_once_init_leave (&type, _type);
|
|
}
|
|
return type;
|
|
}
|
|
|
|
static gboolean
|
|
gst_klv_meta_init (GstMeta * meta, gpointer params, GstBuffer * buffer)
|
|
{
|
|
GstKLVMetaImpl *impl = (GstKLVMetaImpl *) meta;
|
|
|
|
impl->bytes = NULL;
|
|
return TRUE;
|
|
}
|
|
|
|
static void
|
|
gst_klv_meta_clear (GstMeta * meta, GstBuffer * buffer)
|
|
{
|
|
GstKLVMetaImpl *impl = (GstKLVMetaImpl *) meta;
|
|
|
|
if (impl->bytes != NULL)
|
|
g_bytes_unref (impl->bytes);
|
|
}
|
|
|
|
static gboolean
|
|
gst_klv_meta_transform (GstBuffer * dest, GstMeta * meta,
|
|
GstBuffer * buffer, GQuark type, gpointer data)
|
|
{
|
|
GstKLVMetaImpl *smeta;
|
|
GstKLVMeta *dmeta;
|
|
|
|
smeta = (GstKLVMetaImpl *) meta;
|
|
|
|
if (GST_META_TRANSFORM_IS_COPY (type)) {
|
|
dmeta = gst_buffer_add_klv_meta_from_bytes (dest, smeta->bytes);
|
|
if (!dmeta)
|
|
return FALSE;
|
|
} else {
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
const GstMetaInfo *
|
|
gst_klv_meta_get_info (void)
|
|
{
|
|
static const GstMetaInfo *klv_meta_info = NULL;
|
|
|
|
if (g_once_init_enter ((GstMetaInfo **) & klv_meta_info)) {
|
|
const GstMetaInfo *meta =
|
|
gst_meta_register (GST_KLV_META_API_TYPE, "GstKLVMeta",
|
|
sizeof (GstKLVMetaImpl), gst_klv_meta_init, gst_klv_meta_clear,
|
|
gst_klv_meta_transform);
|
|
g_once_init_leave ((GstMetaInfo **) & klv_meta_info, (GstMetaInfo *) meta);
|
|
}
|
|
return klv_meta_info;
|
|
}
|
|
|
|
/* Add KLV meta data to a buffer */
|
|
|
|
static GstKLVMeta *
|
|
gst_buffer_add_klv_meta_internal (GstBuffer * buffer, GBytes * bytes)
|
|
{
|
|
GstKLVMetaImpl *impl;
|
|
GstKLVMeta *meta;
|
|
gconstpointer data;
|
|
gsize size;
|
|
|
|
/* KLV coding shall use and only use a fixed 16-byte SMPTE-administered
|
|
* Universal Label, according to SMPTE 298M as Key (Rec. ITU R-BT.1653-1) */
|
|
data = g_bytes_get_data (bytes, &size);
|
|
if (size < 16 || GST_READ_UINT32_BE (data) != 0x060E2B34) {
|
|
GST_ERROR ("Trying to attach a invalid KLV meta data to buffer");
|
|
g_bytes_unref (bytes);
|
|
return NULL;
|
|
}
|
|
|
|
meta = (GstKLVMeta *) gst_buffer_add_meta (buffer, GST_KLV_META_INFO, NULL);
|
|
|
|
GST_TRACE ("Adding %u bytes of KLV data to buffer %p", (guint) size, buffer);
|
|
|
|
impl = (GstKLVMetaImpl *) meta;
|
|
impl->bytes = bytes;
|
|
|
|
return meta;
|
|
}
|
|
|
|
/**
|
|
* gst_buffer_add_klv_meta_from_data: (skip)
|
|
* @buffer: a #GstBuffer
|
|
* @data: (array length=size) (transfer none): KLV data with 16-byte KLV
|
|
* Universal Label prefix
|
|
* @size: size of @data in bytes
|
|
*
|
|
* Attaches #GstKLVMeta metadata to @buffer with the given parameters,
|
|
* Does not take ownership of @data.
|
|
*
|
|
* Returns: (transfer none): the #GstKLVMeta on @buffer.
|
|
*
|
|
* Since: 1.16
|
|
*/
|
|
GstKLVMeta *
|
|
gst_buffer_add_klv_meta_from_data (GstBuffer * buffer, const guint8 * data,
|
|
gsize size)
|
|
{
|
|
g_return_val_if_fail (buffer != NULL, NULL);
|
|
g_return_val_if_fail (data != NULL && size > 16, NULL);
|
|
|
|
return gst_buffer_add_klv_meta_internal (buffer, g_bytes_new (data, size));
|
|
}
|
|
|
|
/**
|
|
* gst_buffer_add_klv_meta_take_data: (skip)
|
|
* @buffer: a #GstBuffer
|
|
* @data: (array length=size) (transfer full): KLV data with 16-byte KLV
|
|
* Universal Label prefix
|
|
* @size: size of @data in bytes
|
|
*
|
|
* Attaches #GstKLVMeta metadata to @buffer with the given parameters,
|
|
* Take ownership of @data.
|
|
*
|
|
* Returns: (transfer none): the #GstKLVMeta on @buffer.
|
|
*
|
|
* Since: 1.16
|
|
*/
|
|
GstKLVMeta *
|
|
gst_buffer_add_klv_meta_take_data (GstBuffer * buffer, guint8 * data,
|
|
gsize size)
|
|
{
|
|
g_return_val_if_fail (buffer != NULL, NULL);
|
|
g_return_val_if_fail (data != NULL && size > 16, NULL);
|
|
|
|
return gst_buffer_add_klv_meta_internal (buffer, g_bytes_new_take (data,
|
|
size));
|
|
}
|
|
|
|
/**
|
|
* gst_buffer_add_klv_meta_from_bytes:
|
|
* @buffer: a #GstBuffer
|
|
* @bytes: (transfer none): KLV data with 16-byte KLV Universal Label prefix
|
|
*
|
|
* Attaches #GstKLVMeta metadata to @buffer with the given parameters,
|
|
* Does not take ownership of @bytes, you will need to unref @bytes.
|
|
*
|
|
* Returns: (transfer none): the #GstKLVMeta on @buffer.
|
|
*
|
|
* Since: 1.16
|
|
*/
|
|
GstKLVMeta *
|
|
gst_buffer_add_klv_meta_from_bytes (GstBuffer * buffer, GBytes * bytes)
|
|
{
|
|
g_return_val_if_fail (buffer != NULL, NULL);
|
|
g_return_val_if_fail (bytes != NULL, NULL);
|
|
|
|
return gst_buffer_add_klv_meta_internal (buffer, g_bytes_ref (bytes));
|
|
}
|
|
|
|
/**
|
|
* gst_buffer_add_klv_meta_take_bytes:
|
|
* @buffer: a #GstBuffer
|
|
* @bytes: (transfer full): KLV data with 16-byte KLV Universal Label prefix
|
|
*
|
|
* Attaches #GstKLVMeta metadata to @buffer with the given parameters,
|
|
* Takes ownership of @bytes.
|
|
*
|
|
* Returns: (transfer none): the #GstKLVMeta on @buffer.
|
|
*
|
|
* Since: 1.16
|
|
*/
|
|
GstKLVMeta *
|
|
gst_buffer_add_klv_meta_take_bytes (GstBuffer * buffer, GBytes * bytes)
|
|
{
|
|
g_return_val_if_fail (buffer != NULL, NULL);
|
|
g_return_val_if_fail (bytes != NULL, NULL);
|
|
|
|
return gst_buffer_add_klv_meta_internal (buffer, bytes);
|
|
}
|
|
|
|
/* Get KLV meta data from a buffer */
|
|
|
|
/**
|
|
* gst_buffer_get_klv_meta:
|
|
* @buffer: a #GstBuffer
|
|
*
|
|
* Returns: a #GstKLVMeta on the buffer, or %NULL if the buffer has none.
|
|
*
|
|
* Since: 1.16
|
|
*/
|
|
GstKLVMeta *
|
|
gst_buffer_get_klv_meta (GstBuffer * buffer)
|
|
{
|
|
return (GstKLVMeta *) gst_buffer_get_meta (buffer, GST_KLV_META_API_TYPE);
|
|
}
|
|
|
|
/**
|
|
* gst_klv_meta_get_data: (skip)
|
|
* @klv_meta: a #GstKLVMeta
|
|
* @size: the size of the returned data in bytes.
|
|
*
|
|
* Returns: (transfer none): the KLV data
|
|
*
|
|
* Since: 1.16
|
|
*/
|
|
const guint8 *
|
|
gst_klv_meta_get_data (GstKLVMeta * klv_meta, gsize * size)
|
|
{
|
|
GstKLVMetaImpl *impl;
|
|
|
|
g_return_val_if_fail (klv_meta != NULL, NULL);
|
|
g_return_val_if_fail (size != NULL, NULL);
|
|
|
|
impl = (GstKLVMetaImpl *) klv_meta;
|
|
|
|
return g_bytes_get_data (impl->bytes, size);
|
|
}
|
|
|
|
/**
|
|
* gst_klv_meta_get_bytes:
|
|
* @klv_meta: a #GstKLVMeta
|
|
*
|
|
* Returns: (transfer none): the KLV data as a #GBytes
|
|
*
|
|
* Since: 1.16
|
|
*/
|
|
GBytes *
|
|
gst_klv_meta_get_bytes (GstKLVMeta * klv_meta)
|
|
{
|
|
GstKLVMetaImpl *impl;
|
|
|
|
g_return_val_if_fail (klv_meta != NULL, NULL);
|
|
|
|
impl = (GstKLVMetaImpl *) klv_meta;
|
|
|
|
return impl->bytes;
|
|
}
|
|
|
|
/* Boxed type, so bindings can use the API */
|
|
|
|
static gpointer
|
|
gst_klv_meta_copy_boxed (gpointer boxed)
|
|
{
|
|
GstKLVMetaImpl *impl = boxed;
|
|
GstKLVMetaImpl *copy;
|
|
|
|
copy = g_new (GstKLVMetaImpl, 1);
|
|
copy->bytes = impl->bytes ? g_bytes_ref (impl->bytes) : NULL;
|
|
return copy;
|
|
}
|
|
|
|
static void
|
|
gst_klv_meta_free_boxed (gpointer boxed)
|
|
{
|
|
GstKLVMetaImpl *impl = boxed;
|
|
|
|
if (impl->bytes)
|
|
g_bytes_unref (impl->bytes);
|
|
|
|
g_free (impl);
|
|
}
|
|
|
|
G_DEFINE_BOXED_TYPE (GstKLVMeta, gst_klv_meta, gst_klv_meta_copy_boxed,
|
|
gst_klv_meta_free_boxed);
|