summaryrefslogtreecommitdiff
path: root/gst
diff options
context:
space:
mode:
authorVivia Nikolaidou <vivia@toolsonair.com>2016-05-20 18:17:52 +0300
committerSebastian Dröge <sebastian@centricular.com>2016-08-04 19:08:27 +0300
commita2c6e2b64a59b4e18c23e93bc50e06f32104ea29 (patch)
treee433af9ee69d001cc5daa69c91ccab7df4b935dc /gst
parentde1f42a28462ab71cc38830fd14c3e4d6fc4c85b (diff)
downloadgstreamer-plugins-bad-a2c6e2b64a59b4e18c23e93bc50e06f32104ea29.tar.gz
timecodestamper: New element to attach SMPTE timecode to buffers
The timecodestamper element attaches a SMPTE timecode to each video buffer. This timecode corresponds to the current stream time. https://bugzilla.gnome.org/show_bug.cgi?id=766419
Diffstat (limited to 'gst')
-rw-r--r--gst/timecode/Makefile.am10
-rw-r--r--gst/timecode/gsttimecodestamper.c419
-rw-r--r--gst/timecode/gsttimecodestamper.h64
-rw-r--r--gst/timecode/plugin.c40
4 files changed, 533 insertions, 0 deletions
diff --git a/gst/timecode/Makefile.am b/gst/timecode/Makefile.am
new file mode 100644
index 000000000..dea26ff40
--- /dev/null
+++ b/gst/timecode/Makefile.am
@@ -0,0 +1,10 @@
+plugin_LTLIBRARIES = libgsttimecode.la
+
+libgsttimecode_la_SOURCES = plugin.c gsttimecodestamper.c
+
+libgsttimecode_la_CFLAGS = $(GST_PLUGINS_BASE_CFLAGS) $(GST_CFLAGS)
+libgsttimecode_la_LIBADD = $(GST_PLUGINS_BASE_LIBS) -lgstvideo-1.0 $(GST_LIBS)
+libgsttimecode_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS)
+libgsttimecode_la_LIBTOOLFLAGS = $(GST_PLUGIN_LIBTOOLFLAGS)
+
+noinst_HEADERS = gsttimecodestamper.h
diff --git a/gst/timecode/gsttimecodestamper.c b/gst/timecode/gsttimecodestamper.c
new file mode 100644
index 000000000..d9302895b
--- /dev/null
+++ b/gst/timecode/gsttimecodestamper.c
@@ -0,0 +1,419 @@
+/*
+ * GStreamer
+ * Copyright (C) 2016 Vivia Nikolaidou <vivia@toolsonair.com>
+ *
+ * gsttimecodestamper.c
+ *
+ * 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:element-timecodestamper
+ * @short_description: Attach a timecode into incoming video frames
+ *
+ * This element attaches a timecode into every incoming video frame. It starts
+ * counting from the stream time of each segment start, which it converts into
+ * a timecode.
+ *
+ * <refsect2>
+ * <title>Example launch line</title>
+ * |[
+ * gst-launch-1.0 videotestsrc ! timecodestamper ! autovideosink
+ * ]|
+ * </refsect2>
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "gsttimecodestamper.h"
+
+#include <gst/gst.h>
+#include <gst/video/video.h>
+#include <stdlib.h>
+#include <string.h>
+
+GST_DEBUG_CATEGORY_STATIC (timecodestamper_debug);
+#define GST_CAT_DEFAULT timecodestamper_debug
+
+/* GstTimeCodeStamper properties */
+enum
+{
+ PROP_0,
+ PROP_OVERRIDE_EXISTING,
+ PROP_DROP_FRAME,
+ PROP_SOURCE_CLOCK,
+ PROP_DAILY_JAM
+};
+
+#define DEFAULT_OVERRIDE_EXISTING FALSE
+#define DEFAULT_DROP_FRAME FALSE
+#define DEFAULT_SOURCE_CLOCK NULL
+#define DEFAULT_DAILY_JAM NULL
+
+static GstStaticPadTemplate gst_timecodestamper_src_template =
+GST_STATIC_PAD_TEMPLATE ("src",
+ GST_PAD_SRC,
+ GST_PAD_ALWAYS,
+ GST_STATIC_CAPS ("video/x-raw")
+ );
+
+static GstStaticPadTemplate gst_timecodestamper_sink_template =
+GST_STATIC_PAD_TEMPLATE ("sink",
+ GST_PAD_SINK,
+ GST_PAD_ALWAYS,
+ GST_STATIC_CAPS ("video/x-raw")
+ );
+
+static void gst_timecodestamper_set_property (GObject * object, guint prop_id,
+ const GValue * value, GParamSpec * pspec);
+static void gst_timecodestamper_get_property (GObject * object, guint prop_id,
+ GValue * value, GParamSpec * pspec);
+static void gst_timecodestamper_dispose (GObject * object);
+static gboolean gst_timecodestamper_sink_event (GstBaseTransform * trans,
+ GstEvent * event);
+static GstFlowReturn gst_timecodestamper_transform_ip (GstBaseTransform *
+ vfilter, GstBuffer * buffer);
+static gboolean gst_timecodestamper_stop (GstBaseTransform * trans);
+
+G_DEFINE_TYPE (GstTimeCodeStamper, gst_timecodestamper,
+ GST_TYPE_BASE_TRANSFORM);
+
+static void
+gst_timecodestamper_class_init (GstTimeCodeStamperClass * klass)
+{
+ GObjectClass *gobject_class = (GObjectClass *) klass;
+ GstElementClass *element_class = (GstElementClass *) klass;
+ GstBaseTransformClass *trans_class = (GstBaseTransformClass *) klass;
+
+ GST_DEBUG_CATEGORY_INIT (timecodestamper_debug, "timecodestamper", 0,
+ "timecodestamper");
+ gst_element_class_set_static_metadata (element_class, "Timecode stamper",
+ "Filter/Video", "Attaches a timecode meta into each video frame",
+ "Vivia Nikolaidou <vivia@toolsonair.com");
+
+ gobject_class->set_property = gst_timecodestamper_set_property;
+ gobject_class->get_property = gst_timecodestamper_get_property;
+ gobject_class->dispose = gst_timecodestamper_dispose;
+
+ g_object_class_install_property (gobject_class, PROP_OVERRIDE_EXISTING,
+ g_param_spec_boolean ("override-existing", "Override existing timecode",
+ "If set to true, any existing timecode will be overridden",
+ DEFAULT_OVERRIDE_EXISTING,
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+ g_object_class_install_property (gobject_class, PROP_DROP_FRAME,
+ g_param_spec_boolean ("drop-frame", "Override existing timecode",
+ "Use drop-frame timecodes for 29.97 and 59.94 FPS",
+ DEFAULT_DROP_FRAME, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+ g_object_class_install_property (gobject_class, PROP_SOURCE_CLOCK,
+ g_param_spec_object ("source-clock",
+ "Source clock to use for first timecode",
+ "If unset, the timecode will refer to the stream time",
+ GST_TYPE_CLOCK, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+ g_object_class_install_property (gobject_class, PROP_DAILY_JAM,
+ g_param_spec_boxed ("daily-jam",
+ "Daily jam",
+ "The daily jam of the timecode",
+ G_TYPE_DATE_TIME, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
+ gst_element_class_add_pad_template (element_class,
+ gst_static_pad_template_get (&gst_timecodestamper_sink_template));
+ gst_element_class_add_pad_template (element_class,
+ gst_static_pad_template_get (&gst_timecodestamper_src_template));
+
+ trans_class->sink_event = GST_DEBUG_FUNCPTR (gst_timecodestamper_sink_event);
+ trans_class->stop = GST_DEBUG_FUNCPTR (gst_timecodestamper_stop);
+
+ trans_class->transform_ip =
+ GST_DEBUG_FUNCPTR (gst_timecodestamper_transform_ip);
+}
+
+static void
+gst_timecodestamper_init (GstTimeCodeStamper * timecodestamper)
+{
+ timecodestamper->override_existing = DEFAULT_OVERRIDE_EXISTING;
+ timecodestamper->drop_frame = DEFAULT_DROP_FRAME;
+ timecodestamper->source_clock = DEFAULT_SOURCE_CLOCK;
+ timecodestamper->current_tc = gst_video_time_code_new_empty ();
+ timecodestamper->current_tc->config.latest_daily_jam = DEFAULT_DAILY_JAM;
+}
+
+static void
+gst_timecodestamper_dispose (GObject * object)
+{
+ GstTimeCodeStamper *timecodestamper = GST_TIME_CODE_STAMPER (object);
+
+ if (timecodestamper->current_tc != NULL) {
+ gst_video_time_code_free (timecodestamper->current_tc);
+ timecodestamper->current_tc = NULL;
+ }
+
+ if (timecodestamper->source_clock) {
+ gst_object_unref (timecodestamper->source_clock);
+ timecodestamper->source_clock = NULL;
+ }
+
+ G_OBJECT_CLASS (gst_timecodestamper_parent_class)->dispose (object);
+}
+
+static void
+gst_timecodestamper_set_property (GObject * object, guint prop_id,
+ const GValue * value, GParamSpec * pspec)
+{
+ GstTimeCodeStamper *timecodestamper = GST_TIME_CODE_STAMPER (object);
+
+ switch (prop_id) {
+ case PROP_OVERRIDE_EXISTING:
+ timecodestamper->override_existing = g_value_get_boolean (value);
+ break;
+ case PROP_DROP_FRAME:
+ timecodestamper->drop_frame = g_value_get_boolean (value);
+ break;
+ case PROP_SOURCE_CLOCK:
+ if (timecodestamper->source_clock)
+ gst_object_unref (timecodestamper->source_clock);
+ timecodestamper->source_clock = g_value_dup_object (value);
+ break;
+ case PROP_DAILY_JAM:
+ if (timecodestamper->current_tc->config.latest_daily_jam)
+ g_date_time_unref (timecodestamper->current_tc->
+ config.latest_daily_jam);
+ timecodestamper->current_tc->config.latest_daily_jam =
+ g_value_dup_boxed (value);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+gst_timecodestamper_get_property (GObject * object, guint prop_id,
+ GValue * value, GParamSpec * pspec)
+{
+ GstTimeCodeStamper *timecodestamper = GST_TIME_CODE_STAMPER (object);
+
+ switch (prop_id) {
+ case PROP_OVERRIDE_EXISTING:
+ g_value_set_boolean (value, timecodestamper->override_existing);
+ break;
+ case PROP_DROP_FRAME:
+ g_value_set_boolean (value, timecodestamper->drop_frame);
+ break;
+ case PROP_SOURCE_CLOCK:
+ g_value_set_object (value, timecodestamper->source_clock);
+ break;
+ case PROP_DAILY_JAM:
+ g_value_set_boxed (value,
+ timecodestamper->current_tc->config.latest_daily_jam);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+gst_timecodestamper_set_drop_frame (GstTimeCodeStamper * timecodestamper)
+{
+ if (timecodestamper->drop_frame && timecodestamper->vinfo.fps_d == 1001 &&
+ (timecodestamper->vinfo.fps_n == 30000 ||
+ timecodestamper->vinfo.fps_d == 60000))
+ timecodestamper->current_tc->config.flags |=
+ GST_VIDEO_TIME_CODE_FLAGS_DROP_FRAME;
+ else
+ timecodestamper->current_tc->config.flags &=
+ ~GST_VIDEO_TIME_CODE_FLAGS_DROP_FRAME;
+}
+
+static gboolean
+gst_timecodestamper_stop (GstBaseTransform * trans)
+{
+ GstTimeCodeStamper *timecodestamper = GST_TIME_CODE_STAMPER (trans);
+
+ gst_video_info_init (&timecodestamper->vinfo);
+
+ return TRUE;
+}
+
+/* Must be called with object lock */
+static void
+gst_timecodestamper_reset_timecode (GstTimeCodeStamper * timecodestamper)
+{
+ GDateTime *jam = NULL;
+
+ if (timecodestamper->current_tc->config.latest_daily_jam)
+ jam =
+ g_date_time_ref (timecodestamper->current_tc->config.latest_daily_jam);
+ gst_video_time_code_clear (timecodestamper->current_tc);
+ /* FIXME: What if the buffer doesn't contain both top and bottom fields? */
+ gst_video_time_code_init (timecodestamper->current_tc,
+ timecodestamper->vinfo.fps_n,
+ timecodestamper->vinfo.fps_d,
+ jam,
+ timecodestamper->vinfo.interlace_mode ==
+ GST_VIDEO_INTERLACE_MODE_PROGRESSIVE ? 0 :
+ GST_VIDEO_TIME_CODE_FLAGS_INTERLACED, 0, 0, 0, 0, 0);
+ gst_timecodestamper_set_drop_frame (timecodestamper);
+}
+
+static gboolean
+gst_timecodestamper_sink_event (GstBaseTransform * trans, GstEvent * event)
+{
+ gboolean ret = FALSE;
+ GstTimeCodeStamper *timecodestamper = GST_TIME_CODE_STAMPER (trans);
+
+ GST_DEBUG_OBJECT (trans, "received event %" GST_PTR_FORMAT, event);
+ switch (GST_EVENT_TYPE (event)) {
+ case GST_EVENT_SEGMENT:
+ {
+ GstSegment segment;
+ guint64 frames;
+ gchar *tc_str;
+
+ GST_OBJECT_LOCK (timecodestamper);
+ if (timecodestamper->source_clock != NULL) {
+ GST_OBJECT_UNLOCK (timecodestamper);
+ break;
+ }
+
+ gst_event_copy_segment (event, &segment);
+ if (segment.format != GST_FORMAT_TIME) {
+ GST_OBJECT_UNLOCK (timecodestamper);
+ GST_ERROR_OBJECT (timecodestamper, "Invalid segment format");
+ return FALSE;
+ }
+ if (GST_VIDEO_INFO_FORMAT (&timecodestamper->vinfo) ==
+ GST_VIDEO_FORMAT_UNKNOWN) {
+ GST_ERROR_OBJECT (timecodestamper,
+ "Received segment event without caps");
+ GST_OBJECT_UNLOCK (timecodestamper);
+ return FALSE;
+ }
+ frames =
+ gst_util_uint64_scale (segment.time, timecodestamper->vinfo.fps_n,
+ timecodestamper->vinfo.fps_d * GST_SECOND);
+ gst_timecodestamper_reset_timecode (timecodestamper);
+ gst_video_time_code_add_frames (timecodestamper->current_tc, frames);
+ GST_DEBUG_OBJECT (timecodestamper,
+ "Got %" G_GUINT64_FORMAT " frames when segment time is %"
+ GST_TIME_FORMAT, frames, GST_TIME_ARGS (segment.time));
+ tc_str = gst_video_time_code_to_string (timecodestamper->current_tc);
+ GST_DEBUG_OBJECT (timecodestamper, "New timecode is %s", tc_str);
+ g_free (tc_str);
+ GST_OBJECT_UNLOCK (timecodestamper);
+ break;
+ }
+ case GST_EVENT_CAPS:
+ {
+ GstCaps *caps;
+
+ GST_OBJECT_LOCK (timecodestamper);
+ gst_event_parse_caps (event, &caps);
+ if (!gst_video_info_from_caps (&timecodestamper->vinfo, caps)) {
+ GST_OBJECT_UNLOCK (timecodestamper);
+ return FALSE;
+ }
+ gst_timecodestamper_reset_timecode (timecodestamper);
+ GST_OBJECT_UNLOCK (timecodestamper);
+ break;
+ }
+ default:
+ break;
+ }
+ ret =
+ GST_BASE_TRANSFORM_CLASS (gst_timecodestamper_parent_class)->sink_event
+ (trans, event);
+ return ret;
+}
+
+static GstFlowReturn
+gst_timecodestamper_transform_ip (GstBaseTransform * vfilter,
+ GstBuffer * buffer)
+{
+ GstTimeCodeStamper *timecodestamper = GST_TIME_CODE_STAMPER (vfilter);
+ GstClockTime ref_time;
+
+ GST_OBJECT_LOCK (timecodestamper);
+ if (gst_buffer_get_video_time_code_meta (buffer)
+ && timecodestamper->override_existing == FALSE) {
+ GST_OBJECT_UNLOCK (timecodestamper);
+ return GST_FLOW_OK;
+ }
+ if (timecodestamper->source_clock != NULL) {
+ if (timecodestamper->current_tc->hours == 0
+ && timecodestamper->current_tc->minutes == 0
+ && timecodestamper->current_tc->seconds == 0
+ && timecodestamper->current_tc->frames == 0) {
+ guint64 hours, minutes, seconds, frames;
+ /* Daily jam time */
+
+ ref_time = gst_clock_get_time (timecodestamper->source_clock);
+ ref_time = ref_time % (24 * 60 * 60 * GST_SECOND);
+ hours = ref_time / (GST_SECOND * 60 * 60);
+ ref_time -= hours * GST_SECOND * 60 * 60;
+ minutes = ref_time / (GST_SECOND * 60);
+ ref_time -= minutes * GST_SECOND * 60;
+ seconds = ref_time / GST_SECOND;
+ ref_time -= seconds * GST_SECOND;
+ /* Converting to frames for the whole ref_time might be inaccurate in case
+ * we have a drop frame timecode */
+ frames = gst_util_uint64_scale (ref_time, timecodestamper->vinfo.fps_n,
+ timecodestamper->vinfo.fps_d * GST_SECOND);
+
+ GST_DEBUG_OBJECT (timecodestamper,
+ "Initializing with %" G_GUINT64_FORMAT ":%" G_GUINT64_FORMAT ":%"
+ G_GUINT64_FORMAT ":%" G_GUINT64_FORMAT "", hours, minutes, seconds,
+ frames);
+ gst_video_time_code_init (timecodestamper->current_tc,
+ timecodestamper->vinfo.fps_n,
+ timecodestamper->vinfo.fps_d,
+ NULL,
+ timecodestamper->vinfo.interlace_mode ==
+ GST_VIDEO_INTERLACE_MODE_PROGRESSIVE ? 0 :
+ GST_VIDEO_TIME_CODE_FLAGS_INTERLACED, hours, minutes, seconds, 0, 0);
+ gst_timecodestamper_set_drop_frame (timecodestamper);
+ /* Do not use frames when initializing because maybe we have drop frame */
+ gst_video_time_code_add_frames (timecodestamper->current_tc, frames);
+ }
+ } else if (timecodestamper->source_clock == NULL) {
+ GstClockTime timecode_time;
+
+ timecode_time =
+ gst_video_time_code_nsec_since_daily_jam (timecodestamper->current_tc);
+ ref_time =
+ gst_segment_to_stream_time (&vfilter->segment, GST_FORMAT_TIME,
+ buffer->pts);
+ if (timecode_time != GST_CLOCK_TIME_NONE && ref_time != GST_CLOCK_TIME_NONE
+ && ((timecode_time > ref_time && timecode_time - ref_time > GST_SECOND)
+ || (ref_time > timecode_time
+ && ref_time - timecode_time > GST_SECOND))) {
+ gchar *tc_str =
+ gst_video_time_code_to_string (timecodestamper->current_tc);
+ GST_WARNING_OBJECT (timecodestamper,
+ "Time code %s (stream time %" GST_TIME_FORMAT
+ ") has drifted more than one second from stream time %"
+ GST_TIME_FORMAT, tc_str, GST_TIME_ARGS (timecode_time),
+ GST_TIME_ARGS (ref_time));
+ g_free (tc_str);
+ }
+ }
+ gst_buffer_add_video_time_code_meta (buffer, timecodestamper->current_tc);
+ gst_video_time_code_increment_frame (timecodestamper->current_tc);
+ GST_OBJECT_UNLOCK (timecodestamper);
+ return GST_FLOW_OK;
+}
diff --git a/gst/timecode/gsttimecodestamper.h b/gst/timecode/gsttimecodestamper.h
new file mode 100644
index 000000000..b06912efd
--- /dev/null
+++ b/gst/timecode/gsttimecodestamper.h
@@ -0,0 +1,64 @@
+/*
+ * GStreamer
+ * Copyright (C) 2016 Vivia Nikolaidou <vivia@toolsonair.com>
+ *
+ * gsttimecodestamper.h
+ *
+ * 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_TIME_CODE_STAMPER_H__
+#define __GST_TIME_CODE_STAMPER_H__
+
+#include <gst/gst.h>
+#include <gst/video/video.h>
+
+#define GST_TYPE_TIME_CODE_STAMPER (gst_timecodestamper_get_type())
+#define GST_TIME_CODE_STAMPER(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_TIME_CODE_STAMPER,GstTimeCodeStamper))
+#define GST_TIME_CODE_STAMPER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), GST_TYPE_TIME_CODE_STAMPER,GstTimeCodeStamperClass))
+#define GST_TIME_CODE_STAMPER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), GST_TYPE_TIME_CODE_STAMPER,GstTimeCodeStamperClass))
+#define GST_IS_TIME_CODE_STAMPER(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_TIME_CODE_STAMPER))
+#define GST_IS_TIME_CODE_STAMPER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), GST_TYPE_TIME_CODE_STAMPER))
+
+typedef struct _GstTimeCodeStamper GstTimeCodeStamper;
+typedef struct _GstTimeCodeStamperClass GstTimeCodeStamperClass;
+
+/**
+ * GstTimeCodeStamper:
+ *
+ * Opaque data structure.
+ */
+struct _GstTimeCodeStamper
+{
+ GstBaseTransform videofilter;
+
+ /* < private > */
+ gboolean override_existing;
+ gboolean drop_frame;
+ GstVideoTimeCode *current_tc;
+ GstVideoInfo vinfo;
+ GstClock *source_clock;
+};
+
+struct _GstTimeCodeStamperClass
+{
+ GstBaseTransformClass parent_class;
+};
+
+GType gst_timecodestamper_get_type (void);
+
+G_END_DECLS
+#endif /* __GST_TIME_CODE_STAMPER_H__ */
diff --git a/gst/timecode/plugin.c b/gst/timecode/plugin.c
new file mode 100644
index 000000000..6faad77e7
--- /dev/null
+++ b/gst/timecode/plugin.c
@@ -0,0 +1,40 @@
+/*
+ * GStreamer
+ * Copyright (C) 2016 Vivia Nikolaidou <vivia@toolsonair.com>
+ *
+ * plugin.c
+ *
+ * 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.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "gsttimecodestamper.h"
+
+static gboolean
+plugin_init (GstPlugin * plugin)
+{
+ return gst_element_register (plugin, "timecodestamper", GST_RANK_NONE,
+ GST_TYPE_TIME_CODE_STAMPER);
+}
+
+GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
+ GST_VERSION_MINOR,
+ timecode,
+ "Timecode-related elements",
+ plugin_init, VERSION, GST_LICENSE, GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN);