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