From a92281c9651a2fbcd47d7caeeb182ac3cd6df62e Mon Sep 17 00:00:00 2001 From: "Joshua M. Doe" Date: Thu, 21 Nov 2019 11:47:42 -0500 Subject: [PATCH] pleora: add support for sending/receiving KLV metadata as chunk data 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. --- sys/pleora/CMakeLists.txt | 2 + sys/pleora/gstpleorasink.cpp | 18 +- sys/pleora/gstpleorasink.h | 1 + sys/pleora/gstpleorasrc.cpp | 83 ++++++- sys/pleora/gstpleorasrc.h | 1 + sys/pleora/klv.c | 330 ++++++++++++++++++++++++++ sys/pleora/klv.h | 82 +++++++ sys/pleora/streamingchannelsource.cpp | 150 +++++++++++- sys/pleora/streamingchannelsource.h | 17 ++ 9 files changed, 674 insertions(+), 10 deletions(-) create mode 100644 sys/pleora/klv.c create mode 100644 sys/pleora/klv.h diff --git a/sys/pleora/CMakeLists.txt b/sys/pleora/CMakeLists.txt index bfbcee1..428bbf3 100644 --- a/sys/pleora/CMakeLists.txt +++ b/sys/pleora/CMakeLists.txt @@ -1,10 +1,12 @@ add_definitions(-D_XKEYCHECK_H) set (SOURCES + klv.c gstpleora.cpp gstpleorasrc.cpp) set (HEADERS + klv.h gstpleorasrc.h) if (Pleora_VERSION_MAJOR GREATER 5) diff --git a/sys/pleora/gstpleorasink.cpp b/sys/pleora/gstpleorasink.cpp index 805161c..cb5e996 100644 --- a/sys/pleora/gstpleorasink.cpp +++ b/sys/pleora/gstpleorasink.cpp @@ -75,7 +75,8 @@ enum PROP_VERSION, PROP_INFO, PROP_SERIAL, - PROP_MAC + PROP_MAC, + PROP_OUTPUT_KLV }; #define DEFAULT_PROP_NUM_INTERNAL_BUFFERS 3 @@ -86,6 +87,7 @@ enum #define DEFAULT_PROP_INFO "Pleora eBUS GStreamer Sink" #define DEFAULT_PROP_SERIAL "0001" #define DEFAULT_PROP_MAC "" +#define DEFAULT_PROP_OUTPUT_KLV TRUE /* pad templates */ @@ -184,6 +186,12 @@ gst_pleorasink_class_init (GstPleoraSinkClass * klass) DEFAULT_PROP_MAC, (GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | GST_PARAM_MUTABLE_READY))); + g_object_class_install_property (gobject_class, PROP_OUTPUT_KLV, + g_param_spec_boolean ("output-klv", "Output KLV", + "Whether to output KLV as chunk data according to MISB ST1608", + DEFAULT_PROP_OUTPUT_KLV, + (GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | + GST_PARAM_MUTABLE_READY))); } static void @@ -198,6 +206,7 @@ gst_pleorasink_init (GstPleoraSink * sink) sink->info = g_strdup (DEFAULT_PROP_INFO); sink->serial = g_strdup (DEFAULT_PROP_SERIAL); sink->mac = g_strdup (DEFAULT_PROP_MAC); + sink->output_klv = DEFAULT_PROP_OUTPUT_KLV; sink->camera_connected = FALSE; @@ -253,6 +262,10 @@ gst_pleorasink_set_property (GObject * object, guint property_id, g_free (sink->mac); sink->mac = g_strdup (g_value_get_string (value)); break; + case PROP_OUTPUT_KLV: + sink->output_klv = g_value_get_boolean (value); + sink->source->SetKlvEnabled (sink->output_klv); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; @@ -293,6 +306,9 @@ gst_pleorasink_get_property (GObject * object, guint property_id, case PROP_MAC: g_value_set_string (value, sink->mac); break; + case PROP_OUTPUT_KLV: + g_value_set_boolean (value, sink->output_klv); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; diff --git a/sys/pleora/gstpleorasink.h b/sys/pleora/gstpleorasink.h index 94821ad..bc48e7c 100644 --- a/sys/pleora/gstpleorasink.h +++ b/sys/pleora/gstpleorasink.h @@ -52,6 +52,7 @@ struct _GstPleoraSink gchar *info; gchar *serial; gchar *mac; + gboolean output_klv; gboolean camera_connected; GstVideoInfo vinfo; diff --git a/sys/pleora/gstpleorasrc.cpp b/sys/pleora/gstpleorasrc.cpp index 480fba0..d59b56a 100644 --- a/sys/pleora/gstpleorasrc.cpp +++ b/sys/pleora/gstpleorasrc.cpp @@ -48,6 +48,9 @@ #include #include +/* FIXME: include this for now until gst-plugins-base MR124 is accepted */ +#include "klv.h" + GST_DEBUG_CATEGORY_STATIC (gst_pleorasrc_debug); #define GST_CAT_DEFAULT gst_pleorasrc_debug @@ -83,7 +86,8 @@ enum PROP_RECEIVER_ONLY, PROP_PACKET_SIZE, PROP_CONFIG_FILE, - PROP_CONFIG_FILE_CONNECT + PROP_CONFIG_FILE_CONNECT, + PROP_OUTPUT_KLV }; #define DEFAULT_PROP_DEVICE "" @@ -97,6 +101,7 @@ enum #define DEFAULT_PROP_PACKET_SIZE 0 #define DEFAULT_PROP_CONFIG_FILE "" #define DEFAULT_PROP_CONFIG_FILE_CONNECT TRUE +#define DEFAULT_PROP_OUTPUT_KLV TRUE #define VIDEO_CAPS_MAKE_BAYER8(format) \ "video/x-bayer, " \ @@ -221,6 +226,12 @@ gst_pleorasrc_class_init (GstPleoraSrcClass * klass) "connects using properties and then restores configuration", DEFAULT_PROP_CONFIG_FILE_CONNECT, (GParamFlags) (G_PARAM_STATIC_STRINGS | G_PARAM_READWRITE))); + g_object_class_install_property (gobject_class, PROP_OUTPUT_KLV, + g_param_spec_boolean ("output-klv", "Output KLV", + "Whether to output MISB ST1608 KLV as buffer meta", + DEFAULT_PROP_OUTPUT_KLV, + (GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | + GST_PARAM_MUTABLE_READY))); } static void @@ -263,6 +274,7 @@ gst_pleorasrc_init (GstPleoraSrc * src) src->receiver_only = DEFAULT_PROP_RECEIVER_ONLY; src->config_file = g_strdup (DEFAULT_PROP_CONFIG_FILE); src->config_file_connect = DEFAULT_PROP_CONFIG_FILE_CONNECT; + src->output_klv = DEFAULT_PROP_OUTPUT_KLV; src->stop_requested = FALSE; src->caps = NULL; @@ -317,6 +329,9 @@ gst_pleorasrc_set_property (GObject * object, guint property_id, case PROP_CONFIG_FILE_CONNECT: src->config_file_connect = g_value_get_boolean (value); break; + case PROP_OUTPUT_KLV: + src->output_klv= g_value_get_boolean (value); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; @@ -366,6 +381,9 @@ gst_pleorasrc_get_property (GObject * object, guint property_id, case PROP_CONFIG_FILE_CONNECT: g_value_set_boolean (value, src->config_file_connect); break; + case PROP_OUTPUT_KLV: + g_value_set_boolean (value, src->output_klv); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; @@ -1587,8 +1605,8 @@ gst_pleorasrc_create (GstPushSrc * psrc, GstBuffer ** buf) return GST_FLOW_ERROR; } + /* wrap or copy image data to buffer */ pvimage = pvbuffer->GetImage (); - gpointer data = pvimage->GetDataPointer (); if (src->pleora_stride == src->gst_stride) { VideoFrame *vf = g_new0 (VideoFrame, 1); @@ -1621,13 +1639,70 @@ gst_pleorasrc_create (GstPushSrc * psrc, GstBuffer ** buf) memcpy (d + i * src->gst_stride, s + i * src->pleora_stride, src->pleora_stride); gst_buffer_unmap (*buf, &minfo); - - src->pipeline->ReleaseBuffer (pvbuffer); } + + /* TODO: use PvBuffer timestamps */ clock = gst_element_get_clock (GST_ELEMENT (src)); clock_time = gst_clock_get_time (clock); gst_object_unref (clock); + if (src->output_klv && pvbuffer->HasChunks ()) { + guint32 num_chunks; + num_chunks = pvbuffer->GetChunkCount (); + GST_LOG_OBJECT (src, "Buffer has %d chunk(s) with layout ID %d", num_chunks, + pvbuffer->GetChunkLayoutID ()); + + /* TODO: spec says "Image must be the first chunk", but that doesn't seem + to be true, so check every chunk */ + for (guint i = 0; i < num_chunks; ++i) { + guint32 chunk_id, chunk_size; + const guint8 *chunk_data; + + pvRes = pvbuffer->GetChunkIDByIndex (i, chunk_id); + if (!pvRes.IsOK ()) { + GST_WARNING_OBJECT (src, "Failed to get chunk ID for index %d: '%s'", i, + pvRes.GetDescription ().GetAscii ()); + continue; + } + + chunk_size = pvbuffer->GetChunkSizeByIndex (i); + if (chunk_size == 0) { + GST_WARNING_OBJECT (src, "Chunk size reported as zero for index %d", i); + continue; + } + + chunk_data = pvbuffer->GetChunkRawDataByIndex (i); + if (chunk_data == NULL) { + GST_WARNING_OBJECT (src, "Chunk data is NULL for index %d", i); + continue; + } + + GST_LOG_OBJECT (src, + "Found chunk at index %d with ID %04x of size %d bytes", i, chunk_id, + chunk_size); + GST_MEMDUMP_OBJECT (src, "Chunk data", chunk_data, chunk_size); + + if (chunk_size < 17) { + GST_LOG_OBJECT (src, "Chunk data is too small to contain KLV"); + continue; + } + + if (GST_READ_UINT32_BE (chunk_data) != 0x060E2B34) { + GST_LOG_OBJECT (src, "Chunk doesn't contain KLV data"); + continue; + } + + GST_LOG_OBJECT (src, "Adding KLV meta to buffer"); + /* TODO: do we need to exclude padding that may be present? */ + gst_buffer_add_klv_meta_from_data (*buf, chunk_data, chunk_size); + } + } + + if (src->pleora_stride != src->gst_stride) { + src->pipeline->ReleaseBuffer (pvbuffer); + pvbuffer = NULL; + } + /* check for dropped frames and disrupted signal */ //dropped_frames = (circ_handle.FrameCount - src->last_frame_count) - 1; //if (dropped_frames > 0) { diff --git a/sys/pleora/gstpleorasrc.h b/sys/pleora/gstpleorasrc.h index 83be28b..1aa19bd 100644 --- a/sys/pleora/gstpleorasrc.h +++ b/sys/pleora/gstpleorasrc.h @@ -60,6 +60,7 @@ struct _GstPleoraSrc gint packet_size; gchar *config_file; gboolean config_file_connect; + gboolean output_klv; guint32 last_frame_count; guint32 total_dropped_frames; diff --git a/sys/pleora/klv.c b/sys/pleora/klv.c new file mode 100644 index 0000000..aaebe9d --- /dev/null +++ b/sys/pleora/klv.c @@ -0,0 +1,330 @@ +/* GStreamer KLV Metadata Support Library + * Copyright (C) 2016-2019 Tim-Philipp Müller + * + * 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 + * + * + * + * Utility functions around KLV metadata support in GStreamer. + * + * + * 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.). + * + * + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#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); diff --git a/sys/pleora/klv.h b/sys/pleora/klv.h new file mode 100644 index 0000000..8ce4f36 --- /dev/null +++ b/sys/pleora/klv.h @@ -0,0 +1,82 @@ +/* GStreamer KLV Metadata Support Library + * Copyright (C) 2016-2019 Tim-Philipp Müller + * + * 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. + */ + +#ifndef __GST_TAG_KLV_H__ +#define __GST_TAG_KLV_H__ + +#include + +// FIXME: include this for now until gst-plugins-base MR124 is accepted +#define GST_TAG_API + +G_BEGIN_DECLS + +/** + * GstKLVMeta: + * + * An opaque #GstMeta structure representing a self-contained KLV metadata + * block that can be attached to buffers. + * + * Since: 1.16 + */ +typedef struct { + /*< private >*/ + GstMeta meta; +} GstKLVMeta; + +GST_TAG_API +GType gst_klv_meta_get_type (void); + +#define GST_KLV_META_API_TYPE (gst_klv_meta_api_get_type()) +#define GST_KLV_META_INFO (gst_klv_meta_get_info()) + +GST_TAG_API +GType gst_klv_meta_api_get_type (void); + +GST_TAG_API +const GstMetaInfo * gst_klv_meta_get_info (void); + +/* Add KLV meta data to a buffer */ + +GST_TAG_API +GstKLVMeta * gst_buffer_add_klv_meta_from_data (GstBuffer * buffer, const guint8 * data, gsize size); + +GST_TAG_API +GstKLVMeta * gst_buffer_add_klv_meta_take_data (GstBuffer * buffer, guint8 * data, gsize size); + +GST_TAG_API +GstKLVMeta * gst_buffer_add_klv_meta_from_bytes (GstBuffer * buffer, GBytes * bytes); + +GST_TAG_API +GstKLVMeta * gst_buffer_add_klv_meta_take_bytes (GstBuffer * buffer, GBytes * bytes); + +/* Get KLV meta data from a buffer */ + +GST_TAG_API +GstKLVMeta * gst_buffer_get_klv_meta (GstBuffer * buffer); + +GST_TAG_API +const guint8 * gst_klv_meta_get_data (GstKLVMeta * klv_meta, gsize * size); + +GST_TAG_API +GBytes * gst_klv_meta_get_bytes (GstKLVMeta * klv_meta); + +G_END_DECLS + +#endif /* __GST_TAG_KLV_H__ */ diff --git a/sys/pleora/streamingchannelsource.cpp b/sys/pleora/streamingchannelsource.cpp index d142ad1..8247a94 100644 --- a/sys/pleora/streamingchannelsource.cpp +++ b/sys/pleora/streamingchannelsource.cpp @@ -18,13 +18,19 @@ */ #include "streamingchannelsource.h" +#include "klv.h" /* setup debug */ GST_DEBUG_CATEGORY_EXTERN (pleorasink_debug); #define GST_CAT_DEFAULT pleorasink_debug +/* these seem to be arbitrary */ +#define CHUNKLAYOUTID 0xABCD +#define KLV_CHUNKID 0xFEDC + GstStreamingChannelSource::GstStreamingChannelSource () -: mAcquisitionBuffer (NULL), mBufferCount (0), mBufferValid (FALSE) +: mAcquisitionBuffer (NULL), mBufferCount (0), mBufferValid (FALSE), + mChunkModeActive(TRUE), mChunkKlvEnabled(TRUE), mKlvChunkSize(0) { } @@ -58,6 +64,67 @@ PvResult GstStreamingChannelSource::GetSupportedPixelType (int aIndex, return PvResult::Code::OK; } +PvResult GstStreamingChannelSource::GetSupportedChunk (int aIndex, uint32_t &aID, PvString &aName) const +{ + switch (aIndex) { + case 0: + aID = KLV_CHUNKID; + aName = "KLV"; + return PvResult::Code::OK; + default: + break; + } + + return PvResult::Code::INVALID_PARAMETER; +} + +bool GstStreamingChannelSource::GetChunkEnable (uint32_t aChunkID) const +{ + switch (aChunkID) { + case KLV_CHUNKID: + return mChunkKlvEnabled; + default: + break; + } + + return false; +} + +PvResult GstStreamingChannelSource::SetChunkEnable (uint32_t aChunkID, bool aEnabled) +{ + switch (aChunkID) { + case KLV_CHUNKID: + mChunkKlvEnabled = aEnabled; + mSink->output_klv = mChunkKlvEnabled; + return PvResult::Code::OK; + default: + break; + } + + return PvResult::Code::INVALID_PARAMETER; +} + +uint32_t GstStreamingChannelSource::GetRequiredChunkSize() const +{ + if (mChunkModeActive && mChunkKlvEnabled) { + /* chunk data must be multiple of 4 bytes, and 16 bytes extra seem + to be needed for chunk ID and length */ + return GST_ROUND_UP_4 (mKlvChunkSize) + 16; + } else { + return 0; + } +} + +void GstStreamingChannelSource::SetKlvEnabled (gboolean enable) +{ + SetChunkEnable (KLV_CHUNKID, enable); +} + +gboolean GstStreamingChannelSource::GetKlvEnabled() +{ + return GetChunkEnable (KLV_CHUNKID); +} + PvBuffer * GstStreamingChannelSource::AllocBuffer () { if (mBufferCount < mSink->num_internal_buffers) { @@ -154,12 +221,13 @@ GstStreamingChannelSource::SetCaps (GstCaps * caps) void GstStreamingChannelSource::ResizeBufferIfNeeded (PvBuffer * aBuffer) { - uint32_t lRequiredChunkSize = 0; + uint32_t lRequiredChunkSize = GetRequiredChunkSize(); PvImage *lImage = aBuffer->GetImage (); if ((lImage->GetWidth () != mWidth) || (lImage->GetHeight () != mHeight) || (lImage->GetPixelType () != mPixelType) || (lImage->GetMaximumChunkLength () != lRequiredChunkSize)) { + GST_LOG_OBJECT (mSink, "Width=%d, Height=%d, PixelType=%d, and/or ChunkLength=%d changed, reallocating buffer", mWidth, mHeight, mPixelType, lRequiredChunkSize); lImage->Alloc (mWidth, mHeight, mPixelType, 0, 0, lRequiredChunkSize); } } @@ -167,7 +235,9 @@ GstStreamingChannelSource::ResizeBufferIfNeeded (PvBuffer * aBuffer) void GstStreamingChannelSource::SetBuffer (GstBuffer * buf) { - GST_LOG ("SetBuffer"); + GByteArray * klv_byte_array = NULL; + + GST_LOG_OBJECT (mSink, "SetBuffer"); g_mutex_lock (&mSink->mutex); @@ -184,15 +254,25 @@ GstStreamingChannelSource::SetBuffer (GstBuffer * buf) return; } + if (mChunkKlvEnabled) { + klv_byte_array = GetKlvByteArray (buf); + if (klv_byte_array) { + mKlvChunkSize = klv_byte_array->len; + } else { + mKlvChunkSize = 0; + } + } + + ResizeBufferIfNeeded (mAcquisitionBuffer); + /* TODO: avoid memcpy (when strides align) by attaching to PvBuffer */ GstMapInfo minfo; gst_buffer_map (buf, &minfo, GST_MAP_READ); - ResizeBufferIfNeeded (mAcquisitionBuffer); guint8 *dst = mAcquisitionBuffer->GetDataPointer (); if (!dst) { - GST_ERROR ("Have buffer to fill, but data pointer is invalid"); + GST_ERROR_OBJECT (mSink, "Have buffer to fill, but data pointer is invalid"); g_mutex_unlock (&mSink->mutex); return; } @@ -202,8 +282,68 @@ GstStreamingChannelSource::SetBuffer (GstBuffer * buf) gst_buffer_unmap (buf, &minfo); + mAcquisitionBuffer->ResetChunks(); + mAcquisitionBuffer->SetChunkLayoutID(CHUNKLAYOUTID); + + if (mChunkKlvEnabled && klv_byte_array && klv_byte_array->len > 0) { + PvResult pvRes; + pvRes = mAcquisitionBuffer->AddChunk (KLV_CHUNKID, (uint8_t*)klv_byte_array->data, klv_byte_array->len); + if (pvRes.IsOK ()) { + GST_LOG_OBJECT (mSink, "Added KLV as chunk data (len=%d)", klv_byte_array->len); + } else { + GST_WARNING_OBJECT (mSink, "Failed to add KLV as chunk data (len=%d): %s", + klv_byte_array->len, + pvRes.GetDescription ().GetAscii ()); + } + } + + if (klv_byte_array) { + g_byte_array_unref (klv_byte_array); + } + mBufferValid = TRUE; g_cond_signal (&mSink->cond); g_mutex_unlock (&mSink->mutex); } + +GByteArray * GstStreamingChannelSource::GetKlvByteArray (GstBuffer * buf) +{ + GstKLVMeta *klv_meta; + gpointer iter = NULL; + GByteArray *byte_array; + + { + //FIXME put fake data for testing + const guint8 klv_header[18] = + { 0x06, 0x0e, 0x2b, 0x34, 0x02, 0x0b, 0x01, 0x01, 0x0e, 0x01, 0x03, + 0x01, 0x01, 0x00, 0x00, 0x00, 0x01, 0xFF }; + buf = gst_buffer_make_writable (buf); + gst_buffer_add_klv_meta_from_data (buf, klv_header, sizeof (klv_header)); + gst_buffer_add_klv_meta_from_data (buf, klv_header, sizeof (klv_header)); + } + + /* spec says KLV can all be in one chunk, or multiple chunks, we do one chunk */ + byte_array = g_byte_array_new (); + while ((klv_meta = (GstKLVMeta *) gst_buffer_iterate_meta_filtered (buf, + &iter, GST_KLV_META_API_TYPE))) { + gsize klv_size; + const guint8 *klv_data; + klv_data = gst_klv_meta_get_data (klv_meta, &klv_size); + if (!klv_data) { + GST_WARNING_OBJECT (mSink, "Failed to get KLV data from meta"); + break; + } + + g_byte_array_append (byte_array, klv_data, klv_size); + } + + /* chunk length must be multiple of 4 bytes */ + if (byte_array->len % 4 != 0) { + const guint8 padding[4] = {0}; + const guint padding_len = GST_ROUND_UP_4 (byte_array->len) - byte_array->len; + g_byte_array_append (byte_array, padding, padding_len); + } + + return byte_array; +} \ No newline at end of file diff --git a/sys/pleora/streamingchannelsource.h b/sys/pleora/streamingchannelsource.h index f0ed619..1f0c81a 100644 --- a/sys/pleora/streamingchannelsource.h +++ b/sys/pleora/streamingchannelsource.h @@ -41,6 +41,18 @@ public: void GetHeightInfo (uint32_t & aMin, uint32_t & aMax, uint32_t & aInc) const; PvResult GetSupportedPixelType (int aIndex, PvPixelType & aPixelType) const; + PvResult GetSupportedChunk (int aIndex, uint32_t &aID, PvString &aName) const; + bool GetChunkEnable (uint32_t aChunkID) const; + PvResult SetChunkEnable (uint32_t aChunkID, bool aEnabled); + bool GetChunkModeActive() const { return mChunkModeActive; } + PvResult SetChunkModeActive( bool aEnabled ) { mChunkModeActive = aEnabled; return PvResult::Code::OK; } + uint32_t GetChunksSize() const { return GetRequiredChunkSize(); } + + uint32_t GetRequiredChunkSize () const; + void SetKlvEnabled (gboolean enable = TRUE); + gboolean GetKlvEnabled (); + GByteArray * GetKlvByteArray (GstBuffer * buf); + private: GstPleoraSink * mSink; PvBuffer *mAcquisitionBuffer; @@ -50,4 +62,9 @@ private: gint mWidth; gint mHeight; PvPixelType mPixelType; + + bool mChunkModeActive; + bool mChunkKlvEnabled; + + gint mKlvChunkSize; }; \ No newline at end of file