1 /* GStreamer
2  *
3  * Copyright (C) 2007,2009 Sebastian Dröge <sebastian.droege@collabora.co.uk>
4  *               2011 Stefan Sauer <ensonic@users.sf.net>
5  *
6  * gsttriggercontrolsource.c: Control source that provides some values at time-
7  *                            stamps
8  *
9  * This library is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU Library General Public
11  * License as published by the Free Software Foundation; either
12  * version 2 of the License, or (at your option) any later version.
13  *
14  * This library is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17  * Library General Public License for more details.
18  *
19  * You should have received a copy of the GNU Library General Public
20  * License along with this library; if not, write to the
21  * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
22  * Boston, MA 02110-1301, USA.
23  */
24 
25  /**
26  * SECTION:gsttriggercontrolsource
27  * @title: GstTriggerControlSource
28  * @short_description: trigger control source
29  *
30  * #GstTriggerControlSource is a #GstControlSource, that returns values from user-given
31  * control points. It allows for a tolerance on the time-stamps.
32  *
33  * To use #GstTriggerControlSource get a new instance by calling
34  * gst_trigger_control_source_new(), bind it to a #GParamSpec and set some
35  * control points by calling gst_timed_value_control_source_set().
36  *
37  * All functions are MT-safe.
38  */
39 #ifdef HAVE_CONFIG_H
40 #include "config.h"
41 #endif
42 
43 #include <glib-object.h>
44 #include <gst/gst.h>
45 
46 #include "gsttriggercontrolsource.h"
47 #include "gst/glib-compat-private.h"
48 #include "gst/math-compat.h"
49 
50 #define GST_CAT_DEFAULT controller_debug
51 GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFAULT);
52 
53 struct _GstTriggerControlSourcePrivate
54 {
55   gint64 tolerance;
56 };
57 
58 /* control point accessors */
59 
60 /*  returns the default value of the property, except for times with specific values */
61 /*  needed for one-shot events, such as notes and triggers */
62 
63 static inline gdouble
_interpolate_trigger(GstTimedValueControlSource * self,GSequenceIter * iter,GstClockTime timestamp)64 _interpolate_trigger (GstTimedValueControlSource * self, GSequenceIter * iter,
65     GstClockTime timestamp)
66 {
67   GstControlPoint *cp;
68   gint64 tolerance = ((GstTriggerControlSource *) self)->priv->tolerance;
69   gboolean found = FALSE;
70 
71   cp = g_sequence_get (iter);
72   if (GST_CLOCK_DIFF (cp->timestamp, timestamp) <= tolerance) {
73     found = TRUE;
74   } else {
75     if ((iter = g_sequence_iter_next (iter)) && !g_sequence_iter_is_end (iter)) {
76       cp = g_sequence_get (iter);
77       if (GST_CLOCK_DIFF (timestamp, cp->timestamp) <= tolerance) {
78         found = TRUE;
79       }
80     }
81   }
82   if (found) {
83     return cp->value;
84   }
85   return NAN;
86 }
87 
88 static gboolean
interpolate_trigger_get(GstTimedValueControlSource * self,GstClockTime timestamp,gdouble * value)89 interpolate_trigger_get (GstTimedValueControlSource * self,
90     GstClockTime timestamp, gdouble * value)
91 {
92   gboolean ret = FALSE;
93   GSequenceIter *iter;
94 
95   g_mutex_lock (&self->lock);
96 
97   iter =
98       gst_timed_value_control_source_find_control_point_iter (self, timestamp);
99   if (iter) {
100     *value = _interpolate_trigger (self, iter, timestamp);
101     if (!isnan (*value))
102       ret = TRUE;
103   }
104   g_mutex_unlock (&self->lock);
105   return ret;
106 }
107 
108 static gboolean
interpolate_trigger_get_value_array(GstTimedValueControlSource * self,GstClockTime timestamp,GstClockTime interval,guint n_values,gdouble * values)109 interpolate_trigger_get_value_array (GstTimedValueControlSource * self,
110     GstClockTime timestamp, GstClockTime interval, guint n_values,
111     gdouble * values)
112 {
113   gboolean ret = FALSE;
114   guint i;
115   GstClockTime ts = timestamp;
116   GstClockTime next_ts = 0;
117   gdouble val;
118   GSequenceIter *iter1 = NULL, *iter2 = NULL;
119   gboolean triggered = FALSE;
120 
121   g_mutex_lock (&self->lock);
122   for (i = 0; i < n_values; i++) {
123     val = NAN;
124     if (ts >= next_ts) {
125       iter1 = gst_timed_value_control_source_find_control_point_iter (self, ts);
126       if (!iter1) {
127         if (G_LIKELY (self->values))
128           iter2 = g_sequence_get_begin_iter (self->values);
129         else
130           iter2 = NULL;
131       } else {
132         iter2 = g_sequence_iter_next (iter1);
133       }
134 
135       if (iter2 && !g_sequence_iter_is_end (iter2)) {
136         GstControlPoint *cp;
137 
138         cp = g_sequence_get (iter2);
139         next_ts = cp->timestamp;
140       } else {
141         next_ts = GST_CLOCK_TIME_NONE;
142       }
143 
144       if (iter1) {
145         val = _interpolate_trigger (self, iter1, ts);
146         if (!isnan (val))
147           ret = TRUE;
148       } else {
149         g_mutex_unlock (&self->lock);
150         return FALSE;
151       }
152       triggered = TRUE;
153     } else if (triggered) {
154       if (iter1) {
155         val = _interpolate_trigger (self, iter1, ts);
156         if (!isnan (val))
157           ret = TRUE;
158       } else {
159         g_mutex_unlock (&self->lock);
160         return FALSE;
161       }
162       triggered = FALSE;
163     }
164     *values = val;
165     ts += interval;
166     values++;
167   }
168   g_mutex_unlock (&self->lock);
169   return ret;
170 }
171 
172 enum
173 {
174   PROP_TOLERANCE = 1,
175 };
176 
177 #define _do_init \
178   GST_DEBUG_CATEGORY_INIT (GST_CAT_DEFAULT, "trigger control source", 0, \
179     "timeline value trigger control source")
180 
181 G_DEFINE_TYPE_WITH_CODE (GstTriggerControlSource, gst_trigger_control_source,
182     GST_TYPE_TIMED_VALUE_CONTROL_SOURCE, G_ADD_PRIVATE (GstTriggerControlSource)
183     _do_init);
184 
185 /**
186  * gst_trigger_control_source_new:
187  *
188  * This returns a new, unbound #GstTriggerControlSource.
189  *
190  * Returns: (transfer full): a new, unbound #GstTriggerControlSource.
191  */
192 GstControlSource *
gst_trigger_control_source_new(void)193 gst_trigger_control_source_new (void)
194 {
195   GstControlSource *csource =
196       g_object_new (GST_TYPE_TRIGGER_CONTROL_SOURCE, NULL);
197 
198   /* Clear floating flag */
199   gst_object_ref_sink (csource);
200 
201   return csource;
202 }
203 
204 static void
gst_trigger_control_source_init(GstTriggerControlSource * self)205 gst_trigger_control_source_init (GstTriggerControlSource * self)
206 {
207   GstControlSource *csource = (GstControlSource *) self;
208 
209   self->priv = gst_trigger_control_source_get_instance_private (self);
210 
211   csource->get_value = (GstControlSourceGetValue) interpolate_trigger_get;
212   csource->get_value_array = (GstControlSourceGetValueArray)
213       interpolate_trigger_get_value_array;
214 }
215 
216 static void
gst_trigger_control_source_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)217 gst_trigger_control_source_set_property (GObject * object, guint prop_id,
218     const GValue * value, GParamSpec * pspec)
219 {
220   GstTriggerControlSource *self = GST_TRIGGER_CONTROL_SOURCE (object);
221 
222   switch (prop_id) {
223     case PROP_TOLERANCE:
224       GST_TIMED_VALUE_CONTROL_SOURCE_LOCK (self);
225       self->priv->tolerance = g_value_get_int64 (value);
226       GST_TIMED_VALUE_CONTROL_SOURCE_UNLOCK (self);
227       break;
228     default:
229       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
230       break;
231   }
232 }
233 
234 static void
gst_trigger_control_source_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)235 gst_trigger_control_source_get_property (GObject * object, guint prop_id,
236     GValue * value, GParamSpec * pspec)
237 {
238   GstTriggerControlSource *self = GST_TRIGGER_CONTROL_SOURCE (object);
239 
240   switch (prop_id) {
241     case PROP_TOLERANCE:
242       g_value_set_int64 (value, self->priv->tolerance);
243       break;
244     default:
245       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
246       break;
247   }
248 }
249 
250 static void
gst_trigger_control_source_class_init(GstTriggerControlSourceClass * klass)251 gst_trigger_control_source_class_init (GstTriggerControlSourceClass * klass)
252 {
253   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
254 
255   gobject_class->set_property = gst_trigger_control_source_set_property;
256   gobject_class->get_property = gst_trigger_control_source_get_property;
257 
258   g_object_class_install_property (gobject_class, PROP_TOLERANCE,
259       g_param_spec_int64 ("tolerance", "Tolerance",
260           "Amount of ns a control time can be off to still trigger",
261           0, G_MAXINT64, 0, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
262 
263 }
264