c40525fbbcf91d8663d23052a5e527adc682b7ee
[platform/upstream/gstreamer.git] / libs / gst / controller / gsttriggercontrolsource.c
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  * @short_description: trigger control source
28  *
29  * #GstTriggerControlSource is a #GstControlSource, that returns values from user-given
30  * control points. It allows for a tolerance on the time-stamps.
31  *
32  * To use #GstTriggerControlSource get a new instance by calling
33  * gst_trigger_control_source_new(), bind it to a #GParamSpec and set some
34  * control points by calling gst_timed_value_control_source_set().
35  *
36  * All functions are MT-safe.
37  */
38
39 #include <glib-object.h>
40 #include <gst/gst.h>
41
42 #include "gsttriggercontrolsource.h"
43 #include "gst/glib-compat-private.h"
44 #include "gst/math-compat.h"
45
46 #define GST_CAT_DEFAULT controller_debug
47 GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFAULT);
48
49 struct _GstTriggerControlSourcePrivate
50 {
51   gint64 tolerance;
52 };
53
54 /* control point accessors */
55
56 /*  returns the default value of the property, except for times with specific values */
57 /*  needed for one-shot events, such as notes and triggers */
58
59 static inline gdouble
60 _interpolate_trigger (GstTimedValueControlSource * self, GSequenceIter * iter,
61     GstClockTime timestamp)
62 {
63   GstControlPoint *cp;
64   gint64 tolerance = ((GstTriggerControlSource *) self)->priv->tolerance;
65   gboolean found = FALSE;
66
67   cp = g_sequence_get (iter);
68   if (GST_CLOCK_DIFF (cp->timestamp, timestamp) <= tolerance) {
69     found = TRUE;
70   } else {
71     if ((iter = g_sequence_iter_next (iter)) && !g_sequence_iter_is_end (iter)) {
72       cp = g_sequence_get (iter);
73       if (GST_CLOCK_DIFF (timestamp, cp->timestamp) <= tolerance) {
74         found = TRUE;
75       }
76     }
77   }
78   if (found) {
79     return cp->value;
80   }
81   return NAN;
82 }
83
84 static gboolean
85 interpolate_trigger_get (GstTimedValueControlSource * self,
86     GstClockTime timestamp, gdouble * value)
87 {
88   gboolean ret = FALSE;
89   GSequenceIter *iter;
90
91   g_mutex_lock (&self->lock);
92
93   iter =
94       gst_timed_value_control_source_find_control_point_iter (self, timestamp);
95   if (iter) {
96     *value = _interpolate_trigger (self, iter, timestamp);
97     if (!isnan (*value))
98       ret = TRUE;
99   }
100   g_mutex_unlock (&self->lock);
101   return ret;
102 }
103
104 static gboolean
105 interpolate_trigger_get_value_array (GstTimedValueControlSource * self,
106     GstClockTime timestamp, GstClockTime interval, guint n_values,
107     gdouble * values)
108 {
109   gboolean ret = FALSE;
110   guint i;
111   GstClockTime ts = timestamp;
112   GstClockTime next_ts = 0;
113   gdouble val;
114   GSequenceIter *iter1 = NULL, *iter2 = NULL;
115   gboolean triggered = FALSE;
116
117   g_mutex_lock (&self->lock);
118   for (i = 0; i < n_values; i++) {
119     val = NAN;
120     if (ts >= next_ts) {
121       iter1 = gst_timed_value_control_source_find_control_point_iter (self, ts);
122       if (!iter1) {
123         if (G_LIKELY (self->values))
124           iter2 = g_sequence_get_begin_iter (self->values);
125         else
126           iter2 = NULL;
127       } else {
128         iter2 = g_sequence_iter_next (iter1);
129       }
130
131       if (iter2 && !g_sequence_iter_is_end (iter2)) {
132         GstControlPoint *cp;
133
134         cp = g_sequence_get (iter2);
135         next_ts = cp->timestamp;
136       } else {
137         next_ts = GST_CLOCK_TIME_NONE;
138       }
139
140       if (iter1) {
141         val = _interpolate_trigger (self, iter1, ts);
142         if (!isnan (val))
143           ret = TRUE;
144       } else {
145         g_mutex_unlock (&self->lock);
146         return FALSE;
147       }
148       triggered = TRUE;
149     } else if (triggered) {
150       if (iter1) {
151         val = _interpolate_trigger (self, iter1, ts);
152         if (!isnan (val))
153           ret = TRUE;
154       } else {
155         g_mutex_unlock (&self->lock);
156         return FALSE;
157       }
158       triggered = FALSE;
159     }
160     *values = val;
161     ts += interval;
162     values++;
163   }
164   g_mutex_unlock (&self->lock);
165   return ret;
166 }
167
168 enum
169 {
170   PROP_TOLERANCE = 1,
171 };
172
173 #define _do_init \
174   GST_DEBUG_CATEGORY_INIT (GST_CAT_DEFAULT, "trigger control source", 0, \
175     "timeline value trigger control source")
176
177 G_DEFINE_TYPE_WITH_CODE (GstTriggerControlSource, gst_trigger_control_source,
178     GST_TYPE_TIMED_VALUE_CONTROL_SOURCE, _do_init);
179
180 /**
181  * gst_trigger_control_source_new:
182  *
183  * This returns a new, unbound #GstTriggerControlSource.
184  *
185  * Returns: (transfer full): a new, unbound #GstTriggerControlSource.
186  */
187 GstControlSource *
188 gst_trigger_control_source_new (void)
189 {
190   return g_object_newv (GST_TYPE_TRIGGER_CONTROL_SOURCE, 0, NULL);
191 }
192
193 static void
194 gst_trigger_control_source_init (GstTriggerControlSource * self)
195 {
196   GstControlSource *csource = (GstControlSource *) self;
197
198   self->priv =
199       G_TYPE_INSTANCE_GET_PRIVATE (self, GST_TYPE_TRIGGER_CONTROL_SOURCE,
200       GstTriggerControlSourcePrivate);
201
202   csource->get_value = (GstControlSourceGetValue) interpolate_trigger_get;
203   csource->get_value_array = (GstControlSourceGetValueArray)
204       interpolate_trigger_get_value_array;
205 }
206
207 static void
208 gst_trigger_control_source_set_property (GObject * object, guint prop_id,
209     const GValue * value, GParamSpec * pspec)
210 {
211   GstTriggerControlSource *self = GST_TRIGGER_CONTROL_SOURCE (object);
212
213   switch (prop_id) {
214     case PROP_TOLERANCE:
215       GST_TIMED_VALUE_CONTROL_SOURCE_LOCK (self);
216       self->priv->tolerance = g_value_get_int64 (value);
217       GST_TIMED_VALUE_CONTROL_SOURCE_UNLOCK (self);
218       break;
219     default:
220       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
221       break;
222   }
223 }
224
225 static void
226 gst_trigger_control_source_get_property (GObject * object, guint prop_id,
227     GValue * value, GParamSpec * pspec)
228 {
229   GstTriggerControlSource *self = GST_TRIGGER_CONTROL_SOURCE (object);
230
231   switch (prop_id) {
232     case PROP_TOLERANCE:
233       g_value_set_int64 (value, self->priv->tolerance);
234       break;
235     default:
236       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
237       break;
238   }
239 }
240
241 static void
242 gst_trigger_control_source_class_init (GstTriggerControlSourceClass * klass)
243 {
244   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
245
246   g_type_class_add_private (klass, sizeof (GstTriggerControlSourcePrivate));
247
248   gobject_class->set_property = gst_trigger_control_source_set_property;
249   gobject_class->get_property = gst_trigger_control_source_get_property;
250
251   g_object_class_install_property (gobject_class, PROP_TOLERANCE,
252       g_param_spec_int64 ("tolerance", "Tolerance",
253           "Amount of ns a control time can be off to still trigger",
254           0, G_MAXINT64, 0, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
255
256 }