3 * Copyright (C) 2007,2009 Sebastian Dröge <sebastian.droege@collabora.co.uk>
4 * 2011 Stefan Sauer <ensonic@users.sf.net>
6 * gsttimedvaluecontrolsource.c: Base class for timeed value based control
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.
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.
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.
26 * SECTION:gsttimedvaluecontrolsource
27 * @short_description: timed value control source base class
29 * Base class for #GstControlSource that use time-stamped values.
31 * When overriding bind, chain up first to give this bind implementation a
32 * chance to setup things.
34 * All functions are MT-safe.
38 #include <glib-object.h>
41 #include "gstinterpolationcontrolsource.h"
42 #include "gst/glib-compat-private.h"
44 #define GST_CAT_DEFAULT controller_debug
45 GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFAULT);
48 GST_DEBUG_CATEGORY_INIT (GST_CAT_DEFAULT, "timed value control source", 0, \
49 "timed value control source base class")
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);
64 static guint gst_timed_value_control_source_signals[LAST_SIGNAL] = { 0 };
67 * gst_control_point_free:
68 * @prop: the object to free
70 * Private method which frees all data allocated by a #GstControlPoint
74 gst_control_point_free (GstControlPoint * cp)
76 g_return_if_fail (cp);
78 g_slice_free (GstControlPoint, cp);
82 gst_control_point_copy (GstControlPoint * boxed)
84 return g_slice_dup (GstControlPoint, boxed);
88 gst_control_point_get_type (void)
90 static volatile gsize type_id = 0;
92 if (g_once_init_enter (&type_id)) {
94 g_boxed_type_register_static (g_intern_static_string
96 (GBoxedCopyFunc) gst_control_point_copy,
97 (GBoxedFreeFunc) gst_control_point_free);
98 g_once_init_leave (&type_id, tmp);
105 gst_timed_value_control_source_reset (GstTimedValueControlSource * self)
107 GstControlSource *csource = (GstControlSource *) self;
109 csource->get_value = NULL;
110 csource->get_value_array = NULL;
113 g_sequence_free (self->values);
118 self->valid_cache = FALSE;
122 * gst_control_point_compare:
123 * @p1: a pointer to a #GstControlPoint
124 * @p2: a pointer to a #GstControlPoint
126 * Compare function for g_list operations that operates on two #GstControlPoint
130 gst_control_point_compare (gconstpointer p1, gconstpointer p2)
132 GstClockTime ct1 = ((GstControlPoint *) p1)->timestamp;
133 GstClockTime ct2 = ((GstControlPoint *) p2)->timestamp;
135 return ((ct1 < ct2) ? -1 : ((ct1 == ct2) ? 0 : 1));
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
144 * Compare function for g_sequence operations that operates on a #GstControlPoint and
148 gst_control_point_find (gconstpointer p1, gconstpointer p2, gpointer user_data)
150 GstClockTime ct1 = ((GstControlPoint *) p1)->timestamp;
151 GstClockTime ct2 = *(GstClockTime *) p2;
153 return ((ct1 < ct2) ? -1 : ((ct1 == ct2) ? 0 : 1));
156 static GstControlPoint *
157 _make_new_cp (GstTimedValueControlSource * self, GstClockTime timestamp,
162 /* create a new GstControlPoint */
163 cp = g_slice_new0 (GstControlPoint);
164 cp->timestamp = timestamp;
171 gst_timed_value_control_source_set_internal (GstTimedValueControlSource *
172 self, GstClockTime timestamp, const gdouble value)
177 g_mutex_lock (&self->lock);
179 /* check if a control point for the timestamp already exists */
180 /* iter contains the iter right *after* timestamp */
181 if (G_LIKELY (self->values)) {
183 g_sequence_search (self->values, ×tamp,
184 (GCompareDataFunc) gst_control_point_find, NULL);
186 GSequenceIter *prev = g_sequence_iter_prev (iter);
188 if (!g_sequence_iter_is_end (prev)) {
189 GstControlPoint *cp = g_sequence_get (prev);
191 /* If the timestamp is the same just update the control point value */
192 if (cp->timestamp == timestamp) {
194 /* update control point */
196 g_mutex_unlock (&self->lock);
199 gst_timed_value_control_source_signals[VALUE_CHANGED_SIGNAL], 0,
206 self->values = g_sequence_new ((GDestroyNotify) gst_control_point_free);
207 GST_INFO ("create new timed value sequence");
210 /* sort new cp into the prop->values list */
211 cp = _make_new_cp (self, timestamp, value);
212 g_sequence_insert_sorted (self->values, cp,
213 (GCompareDataFunc) gst_control_point_compare, NULL);
215 g_mutex_unlock (&self->lock);
218 gst_timed_value_control_source_signals[VALUE_ADDED_SIGNAL], 0, cp);
221 self->valid_cache = FALSE;
225 * gst_timed_value_control_source_find_control_point_iter:
226 * @self: the control source to search in
227 * @timestamp: the search key
229 * Find last value before given timestamp in control point list.
230 * If all values in the control point list come after the given
231 * timestamp or no values exist, %NULL is returned.
233 * For use in control source implementations.
235 * Returns: (transfer none): the found #GSequenceIter or %NULL
237 GSequenceIter *gst_timed_value_control_source_find_control_point_iter
238 (GstTimedValueControlSource * self, GstClockTime timestamp)
246 g_sequence_search (self->values, ×tamp,
247 (GCompareDataFunc) gst_control_point_find, NULL);
249 /* g_sequence_search() returns the iter where timestamp
250 * would be inserted, i.e. the iter > timestamp, so
251 * we need to get the previous one. And of course, if
252 * there is no previous one, we return NULL. */
253 if (g_sequence_iter_is_begin (iter))
256 return g_sequence_iter_prev (iter);
261 * gst_timed_value_control_source_set:
262 * @self: the #GstTimedValueControlSource object
263 * @timestamp: the time the control-change is scheduled for
264 * @value: the control-value
266 * Set the value of given controller-handled property at a certain time.
268 * Returns: FALSE if the values couldn't be set, TRUE otherwise.
271 gst_timed_value_control_source_set (GstTimedValueControlSource * self,
272 GstClockTime timestamp, const gdouble value)
274 g_return_val_if_fail (GST_IS_TIMED_VALUE_CONTROL_SOURCE (self), FALSE);
275 g_return_val_if_fail (GST_CLOCK_TIME_IS_VALID (timestamp), FALSE);
277 gst_timed_value_control_source_set_internal (self, timestamp, value);
283 * gst_timed_value_control_source_set_from_list:
284 * @self: the #GstTimedValueControlSource object
285 * @timedvalues: (transfer none) (element-type GstTimedValue): a list
286 * with #GstTimedValue items
288 * Sets multiple timed values at once.
290 * Returns: FALSE if the values couldn't be set, TRUE otherwise.
293 gst_timed_value_control_source_set_from_list (GstTimedValueControlSource *
294 self, const GSList * timedvalues)
298 gboolean res = FALSE;
300 g_return_val_if_fail (GST_IS_TIMED_VALUE_CONTROL_SOURCE (self), FALSE);
302 for (node = timedvalues; node; node = g_slist_next (node)) {
304 if (!GST_CLOCK_TIME_IS_VALID (tv->timestamp)) {
305 GST_WARNING ("GstTimedValued with invalid timestamp passed to %s",
308 gst_timed_value_control_source_set_internal (self, tv->timestamp,
317 * gst_timed_value_control_source_unset:
318 * @self: the #GstTimedValueControlSource object
319 * @timestamp: the time the control-change should be removed from
321 * Used to remove the value of given controller-handled property at a certain
324 * Returns: FALSE if the value couldn't be unset (i.e. not found, TRUE otherwise.
327 gst_timed_value_control_source_unset (GstTimedValueControlSource * self,
328 GstClockTime timestamp)
331 gboolean res = FALSE;
332 GstControlPoint *cp = NULL;
334 g_return_val_if_fail (GST_IS_TIMED_VALUE_CONTROL_SOURCE (self), FALSE);
335 g_return_val_if_fail (GST_CLOCK_TIME_IS_VALID (timestamp), FALSE);
337 g_mutex_lock (&self->lock);
338 /* check if a control point for the timestamp exists */
339 if (G_LIKELY (self->values) && (iter =
340 g_sequence_search (self->values, ×tamp,
341 (GCompareDataFunc) gst_control_point_find, NULL))) {
343 /* Iter contains the iter right after timestamp, i.e.
344 * we need to get the previous one and check the timestamp
346 iter = g_sequence_iter_prev (iter);
347 cp = g_sequence_get (iter);
348 if (cp->timestamp == timestamp) {
349 cp = g_slice_dup (GstControlPoint, cp);
350 g_sequence_remove (iter);
352 self->valid_cache = FALSE;
359 g_mutex_unlock (&self->lock);
363 gst_timed_value_control_source_signals[VALUE_REMOVED_SIGNAL], 0, cp);
364 g_slice_free (GstControlPoint, cp);
371 * gst_timed_value_control_source_unset_all:
372 * @self: the #GstTimedValueControlSource object
374 * Used to remove all time-stamped values of given controller-handled property
378 gst_timed_value_control_source_unset_all (GstTimedValueControlSource * self)
380 g_return_if_fail (GST_IS_TIMED_VALUE_CONTROL_SOURCE (self));
382 g_mutex_lock (&self->lock);
383 /* free GstControlPoint structures */
385 g_sequence_free (self->values);
389 self->valid_cache = FALSE;
391 g_mutex_unlock (&self->lock);
395 _append_control_point (GstControlPoint * cp, GQueue * res)
397 g_queue_push_tail (res, cp);
401 * gst_timed_value_control_source_get_all:
402 * @self: the #GstTimedValueControlSource to get the list from
404 * Returns a read-only copy of the list of #GstTimedValue for the given property.
405 * Free the list after done with it.
407 * Returns: (transfer container) (element-type GstTimedValue): a copy
408 * of the list, or %NULL if the property isn't handled by the controller
411 gst_timed_value_control_source_get_all (GstTimedValueControlSource * self)
413 GQueue res = G_QUEUE_INIT;
415 g_return_val_if_fail (GST_IS_TIMED_VALUE_CONTROL_SOURCE (self), NULL);
417 g_mutex_lock (&self->lock);
418 if (G_LIKELY (self->values))
419 g_sequence_foreach (self->values, (GFunc) _append_control_point, &res);
420 g_mutex_unlock (&self->lock);
426 * gst_timed_value_control_source_get_count:
427 * @self: the #GstTimedValueControlSource to get the number of values from
429 * Get the number of control points that are set.
431 * Returns: the number of control points that are set.
434 gst_timed_value_control_source_get_count (GstTimedValueControlSource * self)
436 g_return_val_if_fail (GST_IS_TIMED_VALUE_CONTROL_SOURCE (self), 0);
437 return self->nvalues;
441 * gst_timed_value_control_invalidate_cache:
442 * @self: the #GstTimedValueControlSource
444 * Reset the controlled value cache.
447 gst_timed_value_control_invalidate_cache (GstTimedValueControlSource * self)
449 g_return_if_fail (GST_IS_TIMED_VALUE_CONTROL_SOURCE (self));
450 self->valid_cache = FALSE;
454 gst_timed_value_control_source_init (GstTimedValueControlSource * self)
456 g_mutex_init (&self->lock);
460 gst_timed_value_control_source_finalize (GObject * obj)
462 GstTimedValueControlSource *self = GST_TIMED_VALUE_CONTROL_SOURCE (obj);
464 g_mutex_lock (&self->lock);
465 gst_timed_value_control_source_reset (self);
466 g_mutex_unlock (&self->lock);
467 g_mutex_clear (&self->lock);
469 G_OBJECT_CLASS (parent_class)->finalize (obj);
473 gst_timed_value_control_source_class_init (GstTimedValueControlSourceClass
476 GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
477 //GstControlSourceClass *csource_class = GST_CONTROL_SOURCE_CLASS (klass);
480 * GstTimedValueControlSource::value-changed
481 * @self: The #GstTimedValueControlSource on which a #GstTimedValue has changed
482 * @timed_value: The #GstTimedValue where the value changed
484 * Emited right after the new value has been set on @timed_signals
488 gst_timed_value_control_source_signals[VALUE_CHANGED_SIGNAL] =
489 g_signal_new ("value-changed", G_TYPE_FROM_CLASS (klass),
490 G_SIGNAL_RUN_FIRST, 0, NULL,
491 NULL, g_cclosure_marshal_generic, G_TYPE_NONE, 1,
492 gst_control_point_get_type ());
495 * GstTimedValueControlSource::value-added
496 * @self: The #GstTimedValueControlSource into which a #GstTimedValue has been
498 * @timed_value: The newly added #GstTimedValue
500 * Emited right after the new value has been added to @self
504 gst_timed_value_control_source_signals[VALUE_ADDED_SIGNAL] =
505 g_signal_new ("value-added", G_TYPE_FROM_CLASS (klass),
506 G_SIGNAL_RUN_FIRST, 0, NULL,
507 NULL, g_cclosure_marshal_generic, G_TYPE_NONE, 1,
508 gst_control_point_get_type ());
511 * GstTimedValueControlSource::value-removed
512 * @self: The #GstTimedValueControlSource from which a #GstTimedValue has been
514 * @timed_value: The removed #GstTimedValue
516 * Emited when @timed_value is removed from @self
520 gst_timed_value_control_source_signals[VALUE_REMOVED_SIGNAL] =
521 g_signal_new ("value-removed", G_TYPE_FROM_CLASS (klass),
522 G_SIGNAL_RUN_FIRST, 0, NULL,
523 NULL, g_cclosure_marshal_generic, G_TYPE_NONE, 1,
524 gst_control_point_get_type ());
527 gobject_class->finalize = gst_timed_value_control_source_finalize;