3 * Copyright (C) 2007 Sebastian Dröge <slomo@circular-chaos.org>
5 * gstinterpolationcontrolsource.c: Control source that provides several
6 * interpolation methods
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Library General Public
10 * License as published by the Free Software Foundation; either
11 * version 2 of the License, or (at your option) any later version.
13 * This library is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Library General Public License for more details.
18 * You should have received a copy of the GNU Library General Public
19 * License along with this library; if not, write to the
20 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
21 * Boston, MA 02111-1307, USA.
25 * SECTION:gstinterpolationcontrolsource
26 * @short_description: interpolation control source
28 * #GstInterpolationControlSource is a #GstControlSource, that interpolates values between user-given
29 * control points. It supports several interpolation modes and property types.
31 * To use #GstInterpolationControlSource get a new instance by calling
32 * gst_interpolation_control_source_new(), bind it to a #GParamSpec, select a interpolation mode with
33 * gst_interpolation_control_source_set_interpolation_mode() and set some control points by calling
34 * gst_interpolation_control_source_set().
36 * All functions are MT-safe.
40 #include <glib-object.h>
43 #include "gstcontrolsource.h"
44 #include "gstinterpolationcontrolsource.h"
45 #include "gstinterpolationcontrolsourceprivate.h"
47 static void gst_interpolation_control_source_init (GstInterpolationControlSource
50 gst_interpolation_control_source_class_init (GstInterpolationControlSourceClass
53 G_DEFINE_TYPE (GstInterpolationControlSource, gst_interpolation_control_source,
54 GST_TYPE_CONTROL_SOURCE);
56 static GObjectClass *parent_class = NULL;
59 * gst_control_point_free:
60 * @prop: the object to free
62 * Private method which frees all data allocated by a #GstControlPoint
66 gst_control_point_free (GstControlPoint * cp)
68 g_return_if_fail (cp);
70 g_value_unset (&cp->value);
71 g_slice_free (GstControlPoint, cp);
75 gst_interpolation_control_source_reset (GstInterpolationControlSource * self)
77 GstControlSource *csource = GST_CONTROL_SOURCE (self);
79 csource->get_value = NULL;
80 csource->get_value_array = NULL;
82 self->priv->type = self->priv->base = G_TYPE_INVALID;
84 if (G_IS_VALUE (&self->priv->default_value))
85 g_value_unset (&self->priv->default_value);
86 if (G_IS_VALUE (&self->priv->minimum_value))
87 g_value_unset (&self->priv->minimum_value);
88 if (G_IS_VALUE (&self->priv->maximum_value))
89 g_value_unset (&self->priv->maximum_value);
91 if (self->priv->values) {
92 g_list_foreach (self->priv->values, (GFunc) gst_control_point_free, NULL);
93 g_list_free (self->priv->values);
94 self->priv->values = NULL;
97 self->priv->nvalues = 0;
98 self->priv->last_requested_value = NULL;
99 self->priv->valid_cache = FALSE;
103 * gst_interpolation_control_source_new:
105 * This returns a new, unbound #GstInterpolationControlSource.
107 * Returns: a new, unbound #GstInterpolationControlSource.
109 GstInterpolationControlSource *
110 gst_interpolation_control_source_new (void)
112 return g_object_new (GST_TYPE_INTERPOLATION_CONTROL_SOURCE, NULL);
116 * gst_interpolation_control_source_set_interpolation_mode:
117 * @self: the #GstInterpolationControlSource object
118 * @mode: interpolation mode
120 * Sets the given interpolation mode.
122 * <note><para>User interpolation is not yet available and quadratic interpolation
123 * is deprecated and maps to cubic interpolation.</para></note>
125 * Returns: %TRUE if the interpolation mode could be set, %FALSE otherwise
128 gst_interpolation_control_source_set_interpolation_mode
129 (GstInterpolationControlSource * self, GstInterpolateMode mode) {
131 GstControlSource *csource = GST_CONTROL_SOURCE (self);
133 if (mode >= priv_gst_num_interpolation_methods
134 || priv_gst_interpolation_methods[mode] == NULL) {
135 GST_WARNING ("interpolation mode %d invalid or not implemented yet", mode);
139 if (mode == GST_INTERPOLATE_QUADRATIC) {
140 GST_WARNING ("Quadratic interpolation mode is deprecated, using cubic"
141 "interpolation mode");
144 if (mode == GST_INTERPOLATE_USER) {
145 GST_WARNING ("User interpolation mode is not implemented yet");
149 g_mutex_lock (self->lock);
150 switch (self->priv->base) {
152 csource->get_value = priv_gst_interpolation_methods[mode]->get_int;
153 csource->get_value_array =
154 priv_gst_interpolation_methods[mode]->get_int_value_array;
157 csource->get_value = priv_gst_interpolation_methods[mode]->get_uint;
158 csource->get_value_array =
159 priv_gst_interpolation_methods[mode]->get_uint_value_array;
163 csource->get_value = priv_gst_interpolation_methods[mode]->get_long;
164 csource->get_value_array =
165 priv_gst_interpolation_methods[mode]->get_long_value_array;
169 csource->get_value = priv_gst_interpolation_methods[mode]->get_ulong;
170 csource->get_value_array =
171 priv_gst_interpolation_methods[mode]->get_ulong_value_array;
175 csource->get_value = priv_gst_interpolation_methods[mode]->get_int64;
176 csource->get_value_array =
177 priv_gst_interpolation_methods[mode]->get_int64_value_array;
181 csource->get_value = priv_gst_interpolation_methods[mode]->get_uint64;
182 csource->get_value_array =
183 priv_gst_interpolation_methods[mode]->get_uint64_value_array;
187 csource->get_value = priv_gst_interpolation_methods[mode]->get_float;
188 csource->get_value_array =
189 priv_gst_interpolation_methods[mode]->get_float_value_array;
193 csource->get_value = priv_gst_interpolation_methods[mode]->get_double;
194 csource->get_value_array =
195 priv_gst_interpolation_methods[mode]->get_double_value_array;
198 case G_TYPE_BOOLEAN:{
199 csource->get_value = priv_gst_interpolation_methods[mode]->get_boolean;
200 csource->get_value_array =
201 priv_gst_interpolation_methods[mode]->get_boolean_value_array;
205 csource->get_value = priv_gst_interpolation_methods[mode]->get_enum;
206 csource->get_value_array =
207 priv_gst_interpolation_methods[mode]->get_enum_value_array;
211 csource->get_value = priv_gst_interpolation_methods[mode]->get_string;
212 csource->get_value_array =
213 priv_gst_interpolation_methods[mode]->get_string_value_array;
221 /* Incomplete implementation */
222 if (!ret || !csource->get_value || !csource->get_value_array) {
223 gst_interpolation_control_source_reset (self);
227 self->priv->valid_cache = FALSE;
228 self->priv->interpolation_mode = mode;
230 g_mutex_unlock (self->lock);
236 gst_interpolation_control_source_bind (GstControlSource * source,
240 GstInterpolationControlSource *self =
241 GST_INTERPOLATION_CONTROL_SOURCE (source);
244 /* get the fundamental base type */
245 self->priv->type = base = type = G_PARAM_SPEC_VALUE_TYPE (pspec);
246 while ((type = g_type_parent (type)))
249 self->priv->base = base;
251 type = self->priv->type;
253 if (!gst_interpolation_control_source_set_interpolation_mode (self,
254 self->priv->interpolation_mode))
259 GParamSpecInt *tpspec = G_PARAM_SPEC_INT (pspec);
261 g_value_init (&self->priv->default_value, type);
262 g_value_set_int (&self->priv->default_value, tpspec->default_value);
263 g_value_init (&self->priv->minimum_value, type);
264 g_value_set_int (&self->priv->minimum_value, tpspec->minimum);
265 g_value_init (&self->priv->maximum_value, type);
266 g_value_set_int (&self->priv->maximum_value, tpspec->maximum);
270 GParamSpecUInt *tpspec = G_PARAM_SPEC_UINT (pspec);
272 g_value_init (&self->priv->default_value, type);
273 g_value_set_uint (&self->priv->default_value, tpspec->default_value);
274 g_value_init (&self->priv->minimum_value, type);
275 g_value_set_uint (&self->priv->minimum_value, tpspec->minimum);
276 g_value_init (&self->priv->maximum_value, type);
277 g_value_set_uint (&self->priv->maximum_value, tpspec->maximum);
281 GParamSpecLong *tpspec = G_PARAM_SPEC_LONG (pspec);
283 g_value_init (&self->priv->default_value, type);
284 g_value_set_long (&self->priv->default_value, tpspec->default_value);
285 g_value_init (&self->priv->minimum_value, type);
286 g_value_set_long (&self->priv->minimum_value, tpspec->minimum);
287 g_value_init (&self->priv->maximum_value, type);
288 g_value_set_long (&self->priv->maximum_value, tpspec->maximum);
292 GParamSpecULong *tpspec = G_PARAM_SPEC_ULONG (pspec);
294 g_value_init (&self->priv->default_value, type);
295 g_value_set_ulong (&self->priv->default_value, tpspec->default_value);
296 g_value_init (&self->priv->minimum_value, type);
297 g_value_set_ulong (&self->priv->minimum_value, tpspec->minimum);
298 g_value_init (&self->priv->maximum_value, type);
299 g_value_set_ulong (&self->priv->maximum_value, tpspec->maximum);
303 GParamSpecInt64 *tpspec = G_PARAM_SPEC_INT64 (pspec);
305 g_value_init (&self->priv->default_value, type);
306 g_value_set_int64 (&self->priv->default_value, tpspec->default_value);
307 g_value_init (&self->priv->minimum_value, type);
308 g_value_set_int64 (&self->priv->minimum_value, tpspec->minimum);
309 g_value_init (&self->priv->maximum_value, type);
310 g_value_set_int64 (&self->priv->maximum_value, tpspec->maximum);
314 GParamSpecUInt64 *tpspec = G_PARAM_SPEC_UINT64 (pspec);
316 g_value_init (&self->priv->default_value, type);
317 g_value_set_uint64 (&self->priv->default_value, tpspec->default_value);
318 g_value_init (&self->priv->minimum_value, type);
319 g_value_set_uint64 (&self->priv->minimum_value, tpspec->minimum);
320 g_value_init (&self->priv->maximum_value, type);
321 g_value_set_uint64 (&self->priv->maximum_value, tpspec->maximum);
325 GParamSpecFloat *tpspec = G_PARAM_SPEC_FLOAT (pspec);
327 g_value_init (&self->priv->default_value, type);
328 g_value_set_float (&self->priv->default_value, tpspec->default_value);
329 g_value_init (&self->priv->minimum_value, type);
330 g_value_set_float (&self->priv->minimum_value, tpspec->minimum);
331 g_value_init (&self->priv->maximum_value, type);
332 g_value_set_float (&self->priv->maximum_value, tpspec->maximum);
336 GParamSpecDouble *tpspec = G_PARAM_SPEC_DOUBLE (pspec);
338 g_value_init (&self->priv->default_value, type);
339 g_value_set_double (&self->priv->default_value, tpspec->default_value);
340 g_value_init (&self->priv->minimum_value, type);
341 g_value_set_double (&self->priv->minimum_value, tpspec->minimum);
342 g_value_init (&self->priv->maximum_value, type);
343 g_value_set_double (&self->priv->maximum_value, tpspec->maximum);
346 case G_TYPE_BOOLEAN:{
347 GParamSpecBoolean *tpspec = G_PARAM_SPEC_BOOLEAN (pspec);
349 g_value_init (&self->priv->default_value, type);
350 g_value_set_boolean (&self->priv->default_value, tpspec->default_value);
354 GParamSpecEnum *tpspec = G_PARAM_SPEC_ENUM (pspec);
356 g_value_init (&self->priv->default_value, type);
357 g_value_set_enum (&self->priv->default_value, tpspec->default_value);
361 GParamSpecString *tpspec = G_PARAM_SPEC_STRING (pspec);
363 g_value_init (&self->priv->default_value, type);
364 g_value_set_string (&self->priv->default_value, tpspec->default_value);
368 GST_WARNING ("incomplete implementation for paramspec type '%s'",
369 G_PARAM_SPEC_TYPE_NAME (pspec));
375 self->priv->valid_cache = FALSE;
376 self->priv->nvalues = 0;
378 gst_interpolation_control_source_reset (self);
385 * gst_control_point_compare:
386 * @p1: a pointer to a #GstControlPoint
387 * @p2: a pointer to a #GstControlPoint
389 * Compare function for g_list operations that operates on two #GstControlPoint
393 gst_control_point_compare (gconstpointer p1, gconstpointer p2)
395 GstClockTime ct1 = ((GstControlPoint *) p1)->timestamp;
396 GstClockTime ct2 = ((GstControlPoint *) p2)->timestamp;
398 return ((ct1 < ct2) ? -1 : ((ct1 == ct2) ? 0 : 1));
402 * gst_control_point_find:
403 * @p1: a pointer to a #GstControlPoint
404 * @p2: a pointer to a #GstClockTime
406 * Compare function for g_list operations that operates on a #GstControlPoint and
410 gst_control_point_find (gconstpointer p1, gconstpointer p2)
412 GstClockTime ct1 = ((GstControlPoint *) p1)->timestamp;
413 GstClockTime ct2 = *(GstClockTime *) p2;
415 return ((ct1 < ct2) ? -1 : ((ct1 == ct2) ? 0 : 1));
419 * _list_find_sorted_custom:
421 * This works like g_list_find_custom() with the difference that it expects the
422 * list to be sorted in ascending order (0->MAX), stops when it the list-values
423 * are bigger that what is searched for and optionaly delivers the last node
424 * back in the @prev_node argument. This can be used to quickly insert a new
425 * node at the correct position.
428 _list_find_sorted_custom (GList * list, gconstpointer data, GCompareFunc func,
434 g_return_val_if_fail (func != NULL, list);
437 cmp = func (list->data, data);
456 static GstControlPoint *
457 _make_new_cp (GstInterpolationControlSource * self, GstClockTime timestamp,
462 /* create a new GstControlPoint */
463 cp = g_slice_new0 (GstControlPoint);
464 cp->timestamp = timestamp;
465 g_value_init (&cp->value, self->priv->type);
466 g_value_copy (value, &cp->value);
472 gst_interpolation_control_source_set_internal (GstInterpolationControlSource *
473 self, GstClockTime timestamp, GValue * value)
475 GList *node, *prev = self->priv->values;
477 /* check if we can shortcut and append */
478 if ((node = g_list_last (self->priv->values))) {
479 GstControlPoint *last_cp = node->data;
481 if (timestamp > last_cp->timestamp) {
482 /* pass 'node' instead of list, and also deliberately ignore the result */
483 node = g_list_append (node, _make_new_cp (self, timestamp, value));
484 self->priv->nvalues++;
489 /* check if a control point for the timestamp already exists */
490 if ((node = _list_find_sorted_custom (self->priv->values, ×tamp,
491 gst_control_point_find, &prev))) {
492 /* update control point */
493 GstControlPoint *cp = node->data;
494 g_value_reset (&cp->value);
495 g_value_copy (value, &cp->value);
497 /* sort new cp into the prop->values list */
498 if (self->priv->values) {
501 /* pass 'prev' instead of list */
502 new_list = g_list_insert_sorted (prev,
503 _make_new_cp (self, timestamp, value), gst_control_point_compare);
504 if (self->priv->values == prev)
505 self->priv->values = new_list;
507 self->priv->values = g_list_prepend (NULL,
508 _make_new_cp (self, timestamp, value));
510 self->priv->nvalues++;
513 self->priv->valid_cache = FALSE;
518 * gst_interpolation_control_source_set:
519 * @self: the #GstInterpolationControlSource object
520 * @timestamp: the time the control-change is scheduled for
521 * @value: the control-value
523 * Set the value of given controller-handled property at a certain time.
525 * Returns: FALSE if the values couldn't be set, TRUE otherwise.
528 gst_interpolation_control_source_set (GstInterpolationControlSource * self,
529 GstClockTime timestamp, GValue * value)
531 g_return_val_if_fail (GST_IS_INTERPOLATION_CONTROL_SOURCE (self), FALSE);
532 g_return_val_if_fail (GST_CLOCK_TIME_IS_VALID (timestamp), FALSE);
533 g_return_val_if_fail (G_IS_VALUE (value), FALSE);
534 g_return_val_if_fail (G_VALUE_TYPE (value) == self->priv->type, FALSE);
536 g_mutex_lock (self->lock);
537 gst_interpolation_control_source_set_internal (self, timestamp, value);
538 g_mutex_unlock (self->lock);
544 * gst_interpolation_control_source_set_from_list:
545 * @self: the #GstInterpolationControlSource object
546 * @timedvalues: a list with #GstTimedValue items
548 * Sets multiple timed values at once.
550 * Returns: FALSE if the values couldn't be set, TRUE otherwise.
553 gst_interpolation_control_source_set_from_list (GstInterpolationControlSource *
554 self, GSList * timedvalues)
558 gboolean res = FALSE;
560 g_return_val_if_fail (GST_IS_INTERPOLATION_CONTROL_SOURCE (self), FALSE);
562 for (node = timedvalues; node; node = g_slist_next (node)) {
564 if (!GST_CLOCK_TIME_IS_VALID (tv->timestamp)) {
565 GST_WARNING ("GstTimedValued with invalid timestamp passed to %s",
567 } else if (!G_IS_VALUE (&tv->value)) {
568 GST_WARNING ("GstTimedValued with invalid value passed to %s",
570 } else if (G_VALUE_TYPE (&tv->value) != self->priv->type) {
571 GST_WARNING ("incompatible value type for property");
573 g_mutex_lock (self->lock);
574 gst_interpolation_control_source_set_internal (self, tv->timestamp,
576 g_mutex_unlock (self->lock);
584 * gst_interpolation_control_source_unset:
585 * @self: the #GstInterpolationControlSource object
586 * @timestamp: the time the control-change should be removed from
588 * Used to remove the value of given controller-handled property at a certain
591 * Returns: FALSE if the value couldn't be unset (i.e. not found, TRUE otherwise.
594 gst_interpolation_control_source_unset (GstInterpolationControlSource * self,
595 GstClockTime timestamp)
598 gboolean res = FALSE;
600 g_return_val_if_fail (GST_IS_INTERPOLATION_CONTROL_SOURCE (self), FALSE);
601 g_return_val_if_fail (GST_CLOCK_TIME_IS_VALID (timestamp), FALSE);
603 g_mutex_lock (self->lock);
604 /* check if a control point for the timestamp exists */
605 if ((node = g_list_find_custom (self->priv->values, ×tamp,
606 gst_control_point_find))) {
607 if (node == self->priv->last_requested_value)
608 self->priv->last_requested_value = NULL;
609 gst_control_point_free (node->data); /* free GstControlPoint */
610 self->priv->values = g_list_delete_link (self->priv->values, node);
611 self->priv->nvalues--;
612 self->priv->valid_cache = FALSE;
615 g_mutex_unlock (self->lock);
621 * gst_interpolation_control_source_unset_all:
622 * @self: the #GstInterpolationControlSource object
624 * Used to remove all time-stamped values of given controller-handled property
628 gst_interpolation_control_source_unset_all (GstInterpolationControlSource *
631 g_return_if_fail (GST_IS_INTERPOLATION_CONTROL_SOURCE (self));
633 g_mutex_lock (self->lock);
634 /* free GstControlPoint structures */
635 g_list_foreach (self->priv->values, (GFunc) gst_control_point_free, NULL);
636 g_list_free (self->priv->values);
637 self->priv->last_requested_value = NULL;
638 self->priv->values = NULL;
639 self->priv->nvalues = 0;
640 self->priv->valid_cache = FALSE;
642 g_mutex_unlock (self->lock);
646 * gst_interpolation_control_source_get_all:
647 * @self: the #GstInterpolationControlSource to get the list from
649 * Returns a read-only copy of the list of #GstTimedValue for the given property.
650 * Free the list after done with it.
652 * Returns: a copy of the list, or %NULL if the property isn't handled by the controller
655 gst_interpolation_control_source_get_all (GstInterpolationControlSource * self)
659 g_return_val_if_fail (GST_IS_INTERPOLATION_CONTROL_SOURCE (self), NULL);
661 g_mutex_lock (self->lock);
662 if (self->priv->values)
663 res = g_list_copy (self->priv->values);
664 g_mutex_unlock (self->lock);
670 * gst_interpolation_control_source_get_count:
671 * @self: the #GstInterpolationControlSource to get the number of values from
673 * Returns the number of control points that are set.
675 * Returns: the number of control points that are set.
679 gst_interpolation_control_source_get_count (GstInterpolationControlSource *
682 g_return_val_if_fail (GST_IS_INTERPOLATION_CONTROL_SOURCE (self), 0);
683 return self->priv->nvalues;
687 gst_interpolation_control_source_init (GstInterpolationControlSource * self)
689 self->lock = g_mutex_new ();
691 G_TYPE_INSTANCE_GET_PRIVATE (self, GST_TYPE_INTERPOLATION_CONTROL_SOURCE,
692 GstInterpolationControlSourcePrivate);
693 self->priv->interpolation_mode = GST_INTERPOLATE_NONE;
697 gst_interpolation_control_source_finalize (GObject * obj)
699 GstInterpolationControlSource *self = GST_INTERPOLATION_CONTROL_SOURCE (obj);
701 g_mutex_lock (self->lock);
702 gst_interpolation_control_source_reset (self);
703 g_mutex_unlock (self->lock);
704 g_mutex_free (self->lock);
705 G_OBJECT_CLASS (parent_class)->finalize (obj);
709 gst_interpolation_control_source_dispose (GObject * obj)
711 G_OBJECT_CLASS (parent_class)->dispose (obj);
715 gst_interpolation_control_source_class_init (GstInterpolationControlSourceClass
718 GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
719 GstControlSourceClass *csource_class = GST_CONTROL_SOURCE_CLASS (klass);
721 parent_class = g_type_class_peek_parent (klass);
722 g_type_class_add_private (klass,
723 sizeof (GstInterpolationControlSourcePrivate));
725 gobject_class->finalize = gst_interpolation_control_source_finalize;
726 gobject_class->dispose = gst_interpolation_control_source_dispose;
727 csource_class->bind = gst_interpolation_control_source_bind;