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  * gsttimedvaluecontrolsource.c: Base class for timeed value based control
7  *                               sources
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:gsttimedvaluecontrolsource
27  * @title: GstTimedValueControlSource
28  * @short_description: timed value control source base class
29  *
30  * Base class for #GstControlSource that use time-stamped values.
31  *
32  * When overriding bind, chain up first to give this bind implementation a
33  * chance to setup things.
34  *
35  * All functions are MT-safe.
36  *
37  */
38 #ifdef HAVE_CONFIG_H
39 #include "config.h"
40 #endif
41 
42 #include <glib-object.h>
43 #include <gst/gst.h>
44 
45 #include "gstinterpolationcontrolsource.h"
46 #include "gst/glib-compat-private.h"
47 
48 #define GST_CAT_DEFAULT controller_debug
49 GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFAULT);
50 
51 #define _do_init \
52   GST_DEBUG_CATEGORY_INIT (GST_CAT_DEFAULT, "timed value control source", 0, \
53     "timed value control source base class")
54 
55 #define gst_timed_value_control_source_parent_class parent_class
56 G_DEFINE_ABSTRACT_TYPE_WITH_CODE (GstTimedValueControlSource,
57     gst_timed_value_control_source, GST_TYPE_CONTROL_SOURCE, _do_init);
58 
59 
60 enum
61 {
62   VALUE_CHANGED_SIGNAL,
63   VALUE_ADDED_SIGNAL,
64   VALUE_REMOVED_SIGNAL,
65   LAST_SIGNAL
66 };
67 
68 static guint gst_timed_value_control_source_signals[LAST_SIGNAL] = { 0 };
69 
70 /**
71  * gst_control_point_free:
72  * @cp: the object to free
73  *
74  * Frees all data allocated by a #GstControlPoint instance.
75  */
76 void
gst_control_point_free(GstControlPoint * cp)77 gst_control_point_free (GstControlPoint * cp)
78 {
79   g_return_if_fail (cp);
80 
81   g_slice_free (GstControlPoint, cp);
82 }
83 
84 GstControlPoint *
gst_control_point_copy(GstControlPoint * cp)85 gst_control_point_copy (GstControlPoint * cp)
86 {
87   return g_slice_dup (GstControlPoint, cp);
88 }
89 
90 GType
gst_control_point_get_type(void)91 gst_control_point_get_type (void)
92 {
93   static volatile gsize type_id = 0;
94 
95   if (g_once_init_enter (&type_id)) {
96     GType tmp =
97         g_boxed_type_register_static (g_intern_static_string
98         ("GstControlPoint"),
99         (GBoxedCopyFunc) gst_control_point_copy,
100         (GBoxedFreeFunc) gst_control_point_free);
101     g_once_init_leave (&type_id, tmp);
102   }
103 
104   return type_id;
105 }
106 
107 static void
gst_timed_value_control_source_reset(GstTimedValueControlSource * self)108 gst_timed_value_control_source_reset (GstTimedValueControlSource * self)
109 {
110   GstControlSource *csource = (GstControlSource *) self;
111 
112   csource->get_value = NULL;
113   csource->get_value_array = NULL;
114 
115   if (self->values) {
116     g_sequence_free (self->values);
117     self->values = NULL;
118   }
119 
120   self->nvalues = 0;
121   self->valid_cache = FALSE;
122 }
123 
124 /*
125  * gst_control_point_compare:
126  * @p1: a pointer to a #GstControlPoint
127  * @p2: a pointer to a #GstControlPoint
128  *
129  * Compare function for g_list operations that operates on two #GstControlPoint
130  * parameters.
131  */
132 static gint
gst_control_point_compare(gconstpointer p1,gconstpointer p2)133 gst_control_point_compare (gconstpointer p1, gconstpointer p2)
134 {
135   GstClockTime ct1 = ((GstControlPoint *) p1)->timestamp;
136   GstClockTime ct2 = ((GstControlPoint *) p2)->timestamp;
137 
138   return ((ct1 < ct2) ? -1 : ((ct1 == ct2) ? 0 : 1));
139 }
140 
141 /*
142  * gst_control_point_find:
143  * @p1: a pointer to a #GstControlPoint
144  * @p2: a pointer to a #GstClockTime
145  * @user_data: supplied user data
146  *
147  * Compare function for g_sequence operations that operates on a #GstControlPoint and
148  * a #GstClockTime.
149  */
150 static gint
gst_control_point_find(gconstpointer p1,gconstpointer p2,gpointer user_data)151 gst_control_point_find (gconstpointer p1, gconstpointer p2, gpointer user_data)
152 {
153   GstClockTime ct1 = ((GstControlPoint *) p1)->timestamp;
154   GstClockTime ct2 = *(GstClockTime *) p2;
155 
156   return ((ct1 < ct2) ? -1 : ((ct1 == ct2) ? 0 : 1));
157 }
158 
159 static GstControlPoint *
_make_new_cp(GstTimedValueControlSource * self,GstClockTime timestamp,const gdouble value)160 _make_new_cp (GstTimedValueControlSource * self, GstClockTime timestamp,
161     const gdouble value)
162 {
163   GstControlPoint *cp;
164 
165   /* create a new GstControlPoint */
166   cp = g_slice_new0 (GstControlPoint);
167   cp->timestamp = timestamp;
168   cp->value = value;
169 
170   return cp;
171 }
172 
173 static void
gst_timed_value_control_source_set_internal(GstTimedValueControlSource * self,GstClockTime timestamp,const gdouble value)174 gst_timed_value_control_source_set_internal (GstTimedValueControlSource *
175     self, GstClockTime timestamp, const gdouble value)
176 {
177   GstControlPoint *cp;
178 
179   g_mutex_lock (&self->lock);
180 
181   /* check if a control point for the timestamp already exists */
182   if (G_LIKELY (self->values)) {
183     GSequenceIter *iter = g_sequence_lookup (self->values, &timestamp,
184         (GCompareDataFunc) gst_control_point_find, NULL);
185 
186     if (iter) {
187       GstControlPoint *cp = g_sequence_get (iter);
188 
189       /* update control point */
190       cp->value = value;
191       g_mutex_unlock (&self->lock);
192 
193       g_signal_emit (self,
194           gst_timed_value_control_source_signals[VALUE_CHANGED_SIGNAL], 0, cp);
195       goto done;
196     }
197   } else {
198     self->values = g_sequence_new ((GDestroyNotify) gst_control_point_free);
199     GST_INFO ("create new timed value sequence");
200   }
201 
202   /* sort new cp into the prop->values list */
203   cp = _make_new_cp (self, timestamp, value);
204   g_sequence_insert_sorted (self->values, cp,
205       (GCompareDataFunc) gst_control_point_compare, NULL);
206   self->nvalues++;
207   g_mutex_unlock (&self->lock);
208 
209   g_signal_emit (self,
210       gst_timed_value_control_source_signals[VALUE_ADDED_SIGNAL], 0, cp);
211 
212 done:
213   self->valid_cache = FALSE;
214 }
215 
216 /**
217  * gst_timed_value_control_source_find_control_point_iter:
218  * @self: the control source to search in
219  * @timestamp: the search key
220  *
221  * Find last value before given timestamp in control point list.
222  * If all values in the control point list come after the given
223  * timestamp or no values exist, %NULL is returned.
224  *
225  * For use in control source implementations.
226  *
227  * Returns: (transfer none): the found #GSequenceIter or %NULL
228  */
gst_timed_value_control_source_find_control_point_iter(GstTimedValueControlSource * self,GstClockTime timestamp)229 GSequenceIter *gst_timed_value_control_source_find_control_point_iter
230     (GstTimedValueControlSource * self, GstClockTime timestamp)
231 {
232   GSequenceIter *iter;
233 
234   if (!self->values)
235     return NULL;
236 
237   iter =
238       g_sequence_search (self->values, &timestamp,
239       (GCompareDataFunc) gst_control_point_find, NULL);
240 
241   /* g_sequence_search() returns the iter where timestamp
242    * would be inserted, i.e. the iter > timestamp, so
243    * we need to get the previous one. And of course, if
244    * there is no previous one, we return NULL. */
245   if (g_sequence_iter_is_begin (iter))
246     return NULL;
247 
248   return g_sequence_iter_prev (iter);
249 }
250 
251 
252 /**
253  * gst_timed_value_control_source_set:
254  * @self: the #GstTimedValueControlSource object
255  * @timestamp: the time the control-change is scheduled for
256  * @value: the control-value
257  *
258  * Set the value of given controller-handled property at a certain time.
259  *
260  * Returns: FALSE if the values couldn't be set, TRUE otherwise.
261  */
262 gboolean
gst_timed_value_control_source_set(GstTimedValueControlSource * self,GstClockTime timestamp,const gdouble value)263 gst_timed_value_control_source_set (GstTimedValueControlSource * self,
264     GstClockTime timestamp, const gdouble value)
265 {
266   g_return_val_if_fail (GST_IS_TIMED_VALUE_CONTROL_SOURCE (self), FALSE);
267   g_return_val_if_fail (GST_CLOCK_TIME_IS_VALID (timestamp), FALSE);
268 
269   gst_timed_value_control_source_set_internal (self, timestamp, value);
270 
271   return TRUE;
272 }
273 
274 /**
275  * gst_timed_value_control_source_set_from_list:
276  * @self: the #GstTimedValueControlSource object
277  * @timedvalues: (transfer none) (element-type GstTimedValue): a list
278  * with #GstTimedValue items
279  *
280  * Sets multiple timed values at once.
281  *
282  * Returns: FALSE if the values couldn't be set, TRUE otherwise.
283  */
284 gboolean
gst_timed_value_control_source_set_from_list(GstTimedValueControlSource * self,const GSList * timedvalues)285 gst_timed_value_control_source_set_from_list (GstTimedValueControlSource *
286     self, const GSList * timedvalues)
287 {
288   const GSList *node;
289   GstTimedValue *tv;
290   gboolean res = FALSE;
291 
292   g_return_val_if_fail (GST_IS_TIMED_VALUE_CONTROL_SOURCE (self), FALSE);
293 
294   for (node = timedvalues; node; node = g_slist_next (node)) {
295     tv = node->data;
296     if (!GST_CLOCK_TIME_IS_VALID (tv->timestamp)) {
297       GST_WARNING ("GstTimedValued with invalid timestamp passed to %s",
298           GST_FUNCTION);
299     } else {
300       gst_timed_value_control_source_set_internal (self, tv->timestamp,
301           tv->value);
302       res = TRUE;
303     }
304   }
305   return res;
306 }
307 
308 /**
309  * gst_timed_value_control_source_unset:
310  * @self: the #GstTimedValueControlSource object
311  * @timestamp: the time the control-change should be removed from
312  *
313  * Used to remove the value of given controller-handled property at a certain
314  * time.
315  *
316  * Returns: FALSE if the value couldn't be unset (i.e. not found, TRUE otherwise.
317  */
318 gboolean
gst_timed_value_control_source_unset(GstTimedValueControlSource * self,GstClockTime timestamp)319 gst_timed_value_control_source_unset (GstTimedValueControlSource * self,
320     GstClockTime timestamp)
321 {
322   GSequenceIter *iter;
323   gboolean res = FALSE;
324   GstControlPoint *cp = NULL;
325 
326   g_return_val_if_fail (GST_IS_TIMED_VALUE_CONTROL_SOURCE (self), FALSE);
327   g_return_val_if_fail (GST_CLOCK_TIME_IS_VALID (timestamp), FALSE);
328 
329   g_mutex_lock (&self->lock);
330   /* check if a control point for the timestamp exists */
331   if (G_LIKELY (self->values) && (iter =
332           g_sequence_lookup (self->values, &timestamp,
333               (GCompareDataFunc) gst_control_point_find, NULL))) {
334 
335     /* Iter contains the iter right after timestamp, i.e.
336      * we need to get the previous one and check the timestamp
337      */
338     cp = g_slice_dup (GstControlPoint, g_sequence_get (iter));
339     g_sequence_remove (iter);
340     self->nvalues--;
341     self->valid_cache = FALSE;
342     res = TRUE;
343   }
344   g_mutex_unlock (&self->lock);
345 
346   if (cp) {
347     g_signal_emit (self,
348         gst_timed_value_control_source_signals[VALUE_REMOVED_SIGNAL], 0, cp);
349     g_slice_free (GstControlPoint, cp);
350   }
351 
352   return res;
353 }
354 
355 /**
356  * gst_timed_value_control_source_unset_all:
357  * @self: the #GstTimedValueControlSource object
358  *
359  * Used to remove all time-stamped values of given controller-handled property
360  *
361  */
362 void
gst_timed_value_control_source_unset_all(GstTimedValueControlSource * self)363 gst_timed_value_control_source_unset_all (GstTimedValueControlSource * self)
364 {
365   g_return_if_fail (GST_IS_TIMED_VALUE_CONTROL_SOURCE (self));
366 
367   g_mutex_lock (&self->lock);
368   /* free GstControlPoint structures */
369   if (self->values) {
370     g_sequence_free (self->values);
371     self->values = NULL;
372   }
373   self->nvalues = 0;
374   self->valid_cache = FALSE;
375 
376   g_mutex_unlock (&self->lock);
377 }
378 
379 static void
_append_control_point(GstControlPoint * cp,GQueue * res)380 _append_control_point (GstControlPoint * cp, GQueue * res)
381 {
382   g_queue_push_tail (res, cp);
383 }
384 
385 /**
386  * gst_timed_value_control_source_get_all:
387  * @self: the #GstTimedValueControlSource to get the list from
388  *
389  * Returns a read-only copy of the list of #GstTimedValue for the given property.
390  * Free the list after done with it.
391  *
392  * Returns: (transfer container) (element-type GstTimedValue): a copy
393  * of the list, or %NULL if the property isn't handled by the controller
394  */
395 GList *
gst_timed_value_control_source_get_all(GstTimedValueControlSource * self)396 gst_timed_value_control_source_get_all (GstTimedValueControlSource * self)
397 {
398   GQueue res = G_QUEUE_INIT;
399 
400   g_return_val_if_fail (GST_IS_TIMED_VALUE_CONTROL_SOURCE (self), NULL);
401 
402   g_mutex_lock (&self->lock);
403   if (G_LIKELY (self->values))
404     g_sequence_foreach (self->values, (GFunc) _append_control_point, &res);
405   g_mutex_unlock (&self->lock);
406 
407   return res.head;
408 }
409 
410 /**
411  * gst_timed_value_control_source_get_count:
412  * @self: the #GstTimedValueControlSource to get the number of values from
413  *
414  * Get the number of control points that are set.
415  *
416  * Returns: the number of control points that are set.
417  */
418 gint
gst_timed_value_control_source_get_count(GstTimedValueControlSource * self)419 gst_timed_value_control_source_get_count (GstTimedValueControlSource * self)
420 {
421   g_return_val_if_fail (GST_IS_TIMED_VALUE_CONTROL_SOURCE (self), 0);
422   return self->nvalues;
423 }
424 
425 /**
426  * gst_timed_value_control_invalidate_cache:
427  * @self: the #GstTimedValueControlSource
428  *
429  * Reset the controlled value cache.
430  */
431 void
gst_timed_value_control_invalidate_cache(GstTimedValueControlSource * self)432 gst_timed_value_control_invalidate_cache (GstTimedValueControlSource * self)
433 {
434   g_return_if_fail (GST_IS_TIMED_VALUE_CONTROL_SOURCE (self));
435   self->valid_cache = FALSE;
436 }
437 
438 static void
gst_timed_value_control_source_init(GstTimedValueControlSource * self)439 gst_timed_value_control_source_init (GstTimedValueControlSource * self)
440 {
441   g_mutex_init (&self->lock);
442 }
443 
444 static void
gst_timed_value_control_source_finalize(GObject * obj)445 gst_timed_value_control_source_finalize (GObject * obj)
446 {
447   GstTimedValueControlSource *self = GST_TIMED_VALUE_CONTROL_SOURCE (obj);
448 
449   g_mutex_lock (&self->lock);
450   gst_timed_value_control_source_reset (self);
451   g_mutex_unlock (&self->lock);
452   g_mutex_clear (&self->lock);
453 
454   G_OBJECT_CLASS (parent_class)->finalize (obj);
455 }
456 
457 static void
gst_timed_value_control_source_class_init(GstTimedValueControlSourceClass * klass)458 gst_timed_value_control_source_class_init (GstTimedValueControlSourceClass
459     * klass)
460 {
461   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
462   //GstControlSourceClass *csource_class = GST_CONTROL_SOURCE_CLASS (klass);
463 
464   /**
465    * GstTimedValueControlSource::value-changed
466    * @self: The #GstTimedValueControlSource on which a #GstTimedValue has changed
467    * @timed_value: The #GstTimedValue where the value changed
468    *
469    * Emitted right after the new value has been set on @timed_signals
470    *
471    * Since: 1.6
472    */
473   gst_timed_value_control_source_signals[VALUE_CHANGED_SIGNAL] =
474       g_signal_new ("value-changed", G_TYPE_FROM_CLASS (klass),
475       G_SIGNAL_RUN_FIRST, 0, NULL,
476       NULL, g_cclosure_marshal_generic, G_TYPE_NONE, 1,
477       gst_control_point_get_type ());
478 
479   /**
480    * GstTimedValueControlSource::value-added
481    * @self: The #GstTimedValueControlSource into which a #GstTimedValue has been
482    *        added
483    * @timed_value: The newly added #GstTimedValue
484    *
485    * Emitted right after the new value has been added to @self
486    *
487    * Since: 1.6
488    */
489   gst_timed_value_control_source_signals[VALUE_ADDED_SIGNAL] =
490       g_signal_new ("value-added", G_TYPE_FROM_CLASS (klass),
491       G_SIGNAL_RUN_FIRST, 0, NULL,
492       NULL, g_cclosure_marshal_generic, G_TYPE_NONE, 1,
493       gst_control_point_get_type ());
494 
495   /**
496    * GstTimedValueControlSource::value-removed
497    * @self: The #GstTimedValueControlSource from which a #GstTimedValue has been
498    *        removed
499    * @timed_value: The removed #GstTimedValue
500    *
501    * Emitted when @timed_value is removed from @self
502    *
503    * Since: 1.6
504    */
505   gst_timed_value_control_source_signals[VALUE_REMOVED_SIGNAL] =
506       g_signal_new ("value-removed", G_TYPE_FROM_CLASS (klass),
507       G_SIGNAL_RUN_FIRST, 0, NULL,
508       NULL, g_cclosure_marshal_generic, G_TYPE_NONE, 1,
509       gst_control_point_get_type ());
510 
511 
512   gobject_class->finalize = gst_timed_value_control_source_finalize;
513 }
514