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