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