aboutsummaryrefslogtreecommitdiff
path: root/libs/gst/controller/gsttriggercontrolsource.c
diff options
context:
space:
mode:
Diffstat (limited to 'libs/gst/controller/gsttriggercontrolsource.c')
-rw-r--r--libs/gst/controller/gsttriggercontrolsource.c256
1 files changed, 256 insertions, 0 deletions
diff --git a/libs/gst/controller/gsttriggercontrolsource.c b/libs/gst/controller/gsttriggercontrolsource.c
new file mode 100644
index 0000000..94636be
--- /dev/null
+++ b/libs/gst/controller/gsttriggercontrolsource.c
@@ -0,0 +1,256 @@
+/* GStreamer
+ *
+ * Copyright (C) 2007,2009 Sebastian Dröge <sebastian.droege@collabora.co.uk>
+ * 2011 Stefan Sauer <ensonic@users.sf.net>
+ *
+ * gsttriggercontrolsource.c: Control source that provides some values at time-
+ * stamps
+ *
+ * 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., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+ /**
+ * SECTION:gsttriggercontrolsource
+ * @short_description: interpolation control source
+ *
+ * #GstTriggerControlSource is a #GstControlSource, that returns values from user-given
+ * control points. It allows for a tolerance on the time-stamps.
+ *
+ * To use #GstTriggerControlSource get a new instance by calling
+ * gst_trigger_control_source_new(), bind it to a #GParamSpec and set some
+ * control points by calling gst_timed_value_control_source_set().
+ *
+ * All functions are MT-safe.
+ */
+
+#include <glib-object.h>
+#include <gst/gst.h>
+
+#include "gsttriggercontrolsource.h"
+#include "gst/glib-compat-private.h"
+#include "gst/math-compat.h"
+
+#define GST_CAT_DEFAULT controller_debug
+GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFAULT);
+
+struct _GstTriggerControlSourcePrivate
+{
+ gint64 tolerance;
+};
+
+/* control point accessors */
+
+/* returns the default value of the property, except for times with specific values */
+/* needed for one-shot events, such as notes and triggers */
+
+static inline gdouble
+_interpolate_trigger (GstTimedValueControlSource * self, GSequenceIter * iter,
+ GstClockTime timestamp)
+{
+ GstControlPoint *cp;
+ gint64 tolerance = ((GstTriggerControlSource *) self)->priv->tolerance;
+ gboolean found = FALSE;
+
+ cp = g_sequence_get (iter);
+ if (GST_CLOCK_DIFF (cp->timestamp, timestamp) <= tolerance) {
+ found = TRUE;
+ } else {
+ if ((iter = g_sequence_iter_next (iter))) {
+ cp = g_sequence_get (iter);
+ if (GST_CLOCK_DIFF (timestamp, cp->timestamp) <= tolerance) {
+ found = TRUE;
+ }
+ }
+ }
+ if (found) {
+ return cp->value;
+ }
+ return NAN;
+}
+
+static gboolean
+interpolate_trigger_get (GstTimedValueControlSource * self,
+ GstClockTime timestamp, gdouble * value)
+{
+ gboolean ret = FALSE;
+ GSequenceIter *iter;
+
+ g_mutex_lock (&self->lock);
+
+ iter =
+ gst_timed_value_control_source_find_control_point_iter (self, timestamp);
+ if (iter) {
+ *value = _interpolate_trigger (self, iter, timestamp);
+ if (!isnan (*value))
+ ret = TRUE;
+ }
+ g_mutex_unlock (&self->lock);
+ return ret;
+}
+
+static gboolean
+interpolate_trigger_get_value_array (GstTimedValueControlSource * self,
+ GstClockTime timestamp, GstClockTime interval, guint n_values,
+ gdouble * values)
+{
+ gboolean ret = FALSE;
+ guint i;
+ GstClockTime ts = timestamp;
+ GstClockTime next_ts = 0;
+ gdouble val;
+ GSequenceIter *iter1 = NULL, *iter2 = NULL;
+ gboolean triggered = FALSE;
+
+ g_mutex_lock (&self->lock);
+ for (i = 0; i < n_values; i++) {
+ val = NAN;
+ if (ts >= next_ts) {
+ iter1 = gst_timed_value_control_source_find_control_point_iter (self, ts);
+ if (!iter1) {
+ if (G_LIKELY (self->values))
+ iter2 = g_sequence_get_begin_iter (self->values);
+ else
+ iter2 = NULL;
+ } else {
+ iter2 = g_sequence_iter_next (iter1);
+ }
+
+ if (iter2 && !g_sequence_iter_is_end (iter2)) {
+ GstControlPoint *cp;
+
+ cp = g_sequence_get (iter2);
+ next_ts = cp->timestamp;
+ } else {
+ next_ts = GST_CLOCK_TIME_NONE;
+ }
+
+ if (iter1) {
+ val = _interpolate_trigger (self, iter1, ts);
+ if (!isnan (val))
+ ret = TRUE;
+ } else {
+ g_mutex_unlock (&self->lock);
+ return FALSE;
+ }
+ triggered = TRUE;
+ } else if (triggered) {
+ if (iter1) {
+ val = _interpolate_trigger (self, iter1, ts);
+ if (!isnan (val))
+ ret = TRUE;
+ } else {
+ g_mutex_unlock (&self->lock);
+ return FALSE;
+ }
+ triggered = FALSE;
+ }
+ *values = val;
+ ts += interval;
+ values++;
+ }
+ g_mutex_unlock (&self->lock);
+ return ret;
+}
+
+enum
+{
+ PROP_TOLERANCE = 1,
+};
+
+#define _do_init \
+ GST_DEBUG_CATEGORY_INIT (GST_CAT_DEFAULT, "trigger control source", 0, \
+ "timeline value trigger control source")
+
+G_DEFINE_TYPE_WITH_CODE (GstTriggerControlSource, gst_trigger_control_source,
+ GST_TYPE_TIMED_VALUE_CONTROL_SOURCE, _do_init);
+
+/**
+ * gst_trigger_control_source_new:
+ *
+ * This returns a new, unbound #GstTriggerControlSource.
+ *
+ * Returns: (transfer full): a new, unbound #GstTriggerControlSource.
+ */
+GstControlSource *
+gst_trigger_control_source_new (void)
+{
+ return g_object_newv (GST_TYPE_TRIGGER_CONTROL_SOURCE, 0, NULL);
+}
+
+static void
+gst_trigger_control_source_init (GstTriggerControlSource * self)
+{
+ GstControlSource *csource = (GstControlSource *) self;
+
+ self->priv =
+ G_TYPE_INSTANCE_GET_PRIVATE (self, GST_TYPE_TRIGGER_CONTROL_SOURCE,
+ GstTriggerControlSourcePrivate);
+
+ csource->get_value = (GstControlSourceGetValue) interpolate_trigger_get;
+ csource->get_value_array = (GstControlSourceGetValueArray)
+ interpolate_trigger_get_value_array;
+}
+
+static void
+gst_trigger_control_source_set_property (GObject * object, guint prop_id,
+ const GValue * value, GParamSpec * pspec)
+{
+ GstTriggerControlSource *self = GST_TRIGGER_CONTROL_SOURCE (object);
+
+ switch (prop_id) {
+ case PROP_TOLERANCE:
+ GST_TIMED_VALUE_CONTROL_SOURCE_LOCK (self);
+ self->priv->tolerance = g_value_get_int64 (value);
+ GST_TIMED_VALUE_CONTROL_SOURCE_UNLOCK (self);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+gst_trigger_control_source_get_property (GObject * object, guint prop_id,
+ GValue * value, GParamSpec * pspec)
+{
+ GstTriggerControlSource *self = GST_TRIGGER_CONTROL_SOURCE (object);
+
+ switch (prop_id) {
+ case PROP_TOLERANCE:
+ g_value_set_int64 (value, self->priv->tolerance);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+gst_trigger_control_source_class_init (GstTriggerControlSourceClass * klass)
+{
+ GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+
+ g_type_class_add_private (klass, sizeof (GstTriggerControlSourcePrivate));
+
+ gobject_class->set_property = gst_trigger_control_source_set_property;
+ gobject_class->get_property = gst_trigger_control_source_get_property;
+
+ g_object_class_install_property (gobject_class, PROP_TOLERANCE,
+ g_param_spec_int64 ("tolerance", "Tolerance",
+ "Amount of ns a control time can be off to still trigger",
+ 0, G_MAXINT64, 0, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
+}