diff --git a/gst/klv/CMakeLists.txt b/gst/klv/CMakeLists.txt index 52b8755..314e798 100644 --- a/gst/klv/CMakeLists.txt +++ b/gst/klv/CMakeLists.txt @@ -1,10 +1,12 @@ set (SOURCES gstklv.c gstklvinject.c + gstklvtimestamp.c gstklvinspect.c) set (HEADERS gstklvinject.h + gstklvtimestamp.h gstklvinspect.h) include_directories (AFTER diff --git a/gst/klv/gstklv.c b/gst/klv/gstklv.c index b80ad97..083f956 100644 --- a/gst/klv/gstklv.c +++ b/gst/klv/gstklv.c @@ -25,14 +25,18 @@ #include "gstklvinject.h" #include "gstklvinspect.h" +#include "gstklvtimestamp.h" static gboolean plugin_init (GstPlugin * plugin) { - return gst_element_register (plugin, "klvinspect", GST_RANK_NONE, - GST_TYPE_KLVINSPECT) - && gst_element_register (plugin, "klvinject", GST_RANK_NONE, - GST_TYPE_KLVINJECT); + return + gst_element_register (plugin, "klvinspect", + GST_RANK_NONE, GST_TYPE_KLVINSPECT) && + gst_element_register (plugin, "klvinject", + GST_RANK_NONE, GST_TYPE_KLVINJECT) && + gst_element_register (plugin, "klvtimestamp", + GST_RANK_NONE, GST_TYPE_KLVTIMESTAMP); } GST_PLUGIN_DEFINE (GST_VERSION_MAJOR, diff --git a/gst/klv/gstklvtimestamp.c b/gst/klv/gstklvtimestamp.c new file mode 100644 index 0000000..401aeee --- /dev/null +++ b/gst/klv/gstklvtimestamp.c @@ -0,0 +1,205 @@ +/* GStreamer + * Copyright (C) 2016 William Manley + * + * 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 Street, Suite 500, + * Boston, MA 02110-1335, USA. + */ +/** + * SECTION:element-gstklvtimestamp + * + * The klvtimestamp element parses KLV to place timestamps on passing buffers. + * + * + * Example launch line + * |[ + * gst-launch -v fakesrc ! klvtimestamp ! fakesink + * ]| + * FIXME Describe what the pipeline does. + * + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include +#include "gstklvtimestamp.h" +#include "klv.h" + +GST_DEBUG_CATEGORY_STATIC (gst_klvtimestamp_debug_category); +#define GST_CAT_DEFAULT gst_klvtimestamp_debug_category + +/* prototypes */ +static GstFlowReturn gst_klvtimestamp_transform_ip (GstBaseTransform * trans, + GstBuffer * buf); + +enum +{ + PROP_0 +}; + +/* pad templates */ + +#define SRC_CAPS "ANY" +#define SINK_CAPS "ANY" + + +/* class initialization */ + +G_DEFINE_TYPE_WITH_CODE (GstKlvTimestamp, gst_klvtimestamp, + GST_TYPE_BASE_TRANSFORM, + GST_DEBUG_CATEGORY_INIT (gst_klvtimestamp_debug_category, "klvtimestamp", 0, + "debug category for klvtimestamp element")); + +static void +gst_klvtimestamp_class_init (GstKlvTimestampClass * klass) +{ + GstBaseTransformClass *base_transform_class = + GST_BASE_TRANSFORM_CLASS (klass); + + /* Setting up pads and setting metadata should be moved to + base_class_init if you intend to subclass this class. */ + gst_element_class_add_pad_template (GST_ELEMENT_CLASS (klass), + gst_pad_template_new ("src", GST_PAD_SRC, GST_PAD_ALWAYS, + gst_caps_from_string (SRC_CAPS))); + gst_element_class_add_pad_template (GST_ELEMENT_CLASS (klass), + gst_pad_template_new ("sink", GST_PAD_SINK, GST_PAD_ALWAYS, + gst_caps_from_string (SINK_CAPS))); + + gst_element_class_set_static_metadata (GST_ELEMENT_CLASS (klass), + "KLV Timestamp", "Filter", "KLV Timestamp Conversion", + "Joshua M. Doe "); + + base_transform_class->transform_ip = + GST_DEBUG_FUNCPTR (gst_klvtimestamp_transform_ip); + + base_transform_class->passthrough_on_same_caps = TRUE; + base_transform_class->transform_ip_on_passthrough = TRUE; +} + +static void +gst_klvtimestamp_init (GstKlvTimestamp * filt) +{ +} + +static void +gst_klvtimestamp_dispose (GstKlvTimestamp * filt) +{ +} + +static GstStaticCaps unix_reference = GST_STATIC_CAPS ("timestamp/x-unix"); + +static void +gst_klvtimestamp_parse_klv_timestamp (GstKlvTimestamp * filt, GstBuffer * buf) +{ + /* Add KLV meta for testing, here: Motion Imagery Standards Board (MISB) + * Engineering Guideline MISB EG 0902 - MISB Minimum Metadata Set. + * Also see: SMPTE S336M for KLV specification, also ITU-R BT.1563-1 */ + const guint8 klv_header[16] = { 0x06, 0x0e, 0x2b, 0x34, 0x02, 0x0b, 0x01, + 0x01, 0x0e, 0x01, 0x03, 0x01, 0x01, 0x00, 0x00, 0x00 + }; + + GstKLVMeta *klv_meta; + gsize klv_size; + const guint8 *klv_data; + GstByteReader br; + guint8 *header_data; + + /* FIXME: MISB defines MISP time, which is NOT UTC, but use UTC for now */ + guint64 utc_us = -1; + + + klv_meta = gst_buffer_get_klv_meta (buf); + if (!klv_meta) { + GST_DEBUG_OBJECT (filt, "Buffer has no KLV metadata"); + return; + } + + klv_data = gst_klv_meta_get_data (klv_meta, &klv_size); + + if (!klv_data || klv_size == 0) { + GST_WARNING_OBJECT (filt, "KLV metadata appears to be empty"); + return; + } + + gst_byte_reader_init (&br, klv_data, (guint) klv_size); + + if (!gst_byte_reader_get_data (&br, sizeof (klv_header), &header_data)) { + GST_WARNING_OBJECT (filt, "KLV data too small to contain header"); + return; + } + + if (memcmp (header_data, klv_header, sizeof (klv_header)) != 0) { + GST_WARNING_OBJECT (filt, "KLV header doesn't match"); + GST_MEMDUMP_OBJECT (filt, "KLV header found", header_data, + (guint) sizeof (klv_header)); + return; + } + + guint8 klv_len; + if (!gst_byte_reader_get_uint8 (&br, &klv_len)) + goto not_enough_data; + + guint8 tag; + if (!gst_byte_reader_get_uint8 (&br, &tag)) + goto not_enough_data; + if (tag != 2) { + GST_WARNING_OBJECT (filt, + "First tag is %d, not 2 as expected for timestamp", tag); + return; + } + + guint8 data_len; + if (!gst_byte_reader_get_uint8 (&br, &data_len)) + goto not_enough_data; + if (data_len != 8) { + GST_WARNING_OBJECT (filt, + "Data length for time tag is %d, not 8 as expected", data_len); + } + + if (!gst_byte_reader_get_uint64_be (&br, &utc_us)) + goto not_enough_data; + + GST_LOG_OBJECT (filt, "Found timestamp of %d.%06d s", utc_us / 1000000, + utc_us % 1000000); + + GstReferenceTimestampMeta *time_meta; + time_meta = + gst_buffer_add_reference_timestamp_meta (buf, + gst_static_caps_get (&unix_reference), utc_us * 1000, + GST_CLOCK_TIME_NONE); + if (!time_meta) { + GST_WARNING_OBJECT (filt, "Failed to generate or attach timestamp meta"); + } + + return; + +not_enough_data: + GST_WARNING_OBJECT (filt, "Not enough data to continue parsing KLV"); + return; +} + +static GstFlowReturn +gst_klvtimestamp_transform_ip (GstBaseTransform * trans, GstBuffer * buf) +{ + GstKlvTimestamp *filt = GST_KLVTIMESTAMP (trans); + + GST_LOG_OBJECT (filt, "Parsing KLV metadata for timestamp"); + gst_klvtimestamp_parse_klv_timestamp (filt, buf); + + return GST_FLOW_OK; +} diff --git a/gst/klv/gstklvtimestamp.h b/gst/klv/gstklvtimestamp.h new file mode 100644 index 0000000..0ca6d42 --- /dev/null +++ b/gst/klv/gstklvtimestamp.h @@ -0,0 +1,50 @@ +/* GStreamer + * Copyright (C) 2016 William Manley + * + * 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_KLVTIMESTAMP_H_ +#define _GST_KLVTIMESTAMP_H_ + +#include + +G_BEGIN_DECLS + +#define GST_TYPE_KLVTIMESTAMP (gst_klvtimestamp_get_type()) +#define GST_KLVTIMESTAMP(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_KLVTIMESTAMP,GstKlvTimestamp)) +#define GST_KLVTIMESTAMP_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_KLVTIMESTAMP,GstKlvTimestampClass)) +#define GST_IS_KLVTIMESTAMP(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_KLVTIMESTAMP)) +#define GST_IS_KLVTIMESTAMP_CLASS(obj) (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_KLVTIMESTAMP)) + +typedef struct _GstKlvTimestamp GstKlvTimestamp; +typedef struct _GstKlvTimestampClass GstKlvTimestampClass; + +struct _GstKlvTimestamp +{ + GstBaseTransform base_klvtimestamp; +}; + +struct _GstKlvTimestampClass +{ + GstBaseTransformClass base_klvtimestamp_class; +}; + +GType gst_klvtimestamp_get_type (void); + +G_END_DECLS + +#endif /* _GST_KLVTIMESTAMP_H_ */