457d7196df1a54dc2588a0272cd6ce083686babe
[platform/upstream/gstreamer.git] / libs / gst / controller / gstcontroller.c
1 /* GStreamer
2  *
3  * Copyright (C) <2005> Stefan Kost <ensonic at users dot sf dot net>
4  *
5  * gstcontroller.c: dynamic parameter control subsystem
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Library General Public
9  * License as published by the Free Software Foundation; either
10  * version 2 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Library General Public License for more details.
16  *
17  * You should have received a copy of the GNU Library General Public
18  * License along with this library; if not, write to the
19  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
20  * Boston, MA 02111-1307, USA.
21  */
22
23 /**
24  * SECTION:gstcontroller
25  * @short_description: dynamic parameter control subsystem
26  *
27  * The controller subsystem offers a lightweight way to adjust gobject
28  * properties over stream-time. It works by using time-stampled value pairs that
29  * are queued for element-properties. At run-time the elements continously pulls
30  * values changes for the current stream-time.
31  *
32  * What needs to be changed in a #GstElement?
33  * Very little - it is just two steps to make a plugin controllable!
34  * <orderedlist>
35  *   <listitem><para>
36  *     mark gobject-properties paramspecs that make sense to be controlled,
37  *     by GST_PARAM_CONTROLLABLE.
38  *   </para></listitem>
39  *   <listitem><para>
40  *     when processing data (get, chain, loop function) at the beginning call
41  *     gst_object_sync_values(element,timestamp).
42  *     This will made the controller to update all gobject properties that are under
43  *     control with the current values based on timestamp.
44  *   </para></listitem>
45  * </orderedlist>
46  *
47  * What needs to be done in applications?
48  * Again its not a lot to change.
49  * <orderedlist>
50  *   <listitem><para>
51  *     first put some properties under control, by calling
52  *     controller = g_object_control_properties(object, "prop1", "prop2",...);
53  *   </para></listitem>
54  *   <listitem><para>
55  *     set how the controller will smooth inbetween values.
56  *     gst_controller_set_interpolation_mode(controller,"prop1",mode);
57  *   </para></listitem>
58  *   <listitem><para>
59  *     set key values
60  *     gst_controller_set (controller, "prop1" ,0 * GST_SECOND, value1);
61  *     gst_controller_set (controller, "prop1" ,1 * GST_SECOND, value2);
62  *   </para></listitem>
63  *   <listitem><para>
64  *     start your pipeline
65  *   </para></listitem>
66  * </orderedlist>
67  */
68
69 #include "config.h"
70 #include "gstcontroller.h"
71
72 #define GST_CAT_DEFAULT gst_controller_debug
73 GST_DEBUG_CATEGORY_EXTERN (GST_CAT_DEFAULT);
74
75 static GObjectClass *parent_class = NULL;
76 GQuark __gst_controller_key;
77
78
79 /* imports from gst-interpolation.c */
80
81 extern GList
82     * gst_controlled_property_find_timed_value_node (GstControlledProperty *
83     prop, GstClockTime timestamp);
84 extern GstInterpolateMethod *interpolation_methods[];
85
86 /* callbacks */
87
88 void
89 on_object_controlled_property_changed (const GObject * object, GParamSpec * arg,
90     gpointer user_data)
91 {
92   GstControlledProperty *prop = GST_CONTROLLED_PROPERTY (user_data);
93   GstController *ctrl;
94
95   GST_INFO ("notify for '%s'", prop->name);
96
97   ctrl = g_object_get_qdata (G_OBJECT (object), __gst_controller_key);
98   g_return_if_fail (ctrl);
99
100   if (g_mutex_trylock (ctrl->lock)) {
101     if (!G_IS_VALUE (&prop->live_value.value)) {
102       g_value_init (&prop->live_value.value, prop->type);
103     }
104     g_object_get_property (G_OBJECT (object), prop->name,
105         &prop->live_value.value);
106     prop->live_value.timestamp = prop->last_value.timestamp;
107     g_mutex_unlock (ctrl->lock);
108     GST_DEBUG ("-> is live update : ts=%" G_GUINT64_FORMAT,
109         prop->live_value.timestamp);
110   }
111 }
112
113 /* helper */
114
115 /*
116  * gst_timed_value_compare:
117  * @p1: a pointer to a #GstTimedValue
118  * @p2: a pointer to a #GstTimedValue
119  *
120  * Compare function for g_list operations that operates on two #GstTimedValue
121  * parameters.
122  */
123 static gint
124 gst_timed_value_compare (gconstpointer p1, gconstpointer p2)
125 {
126   GstClockTime ct1 = ((GstTimedValue *) p1)->timestamp;
127   GstClockTime ct2 = ((GstTimedValue *) p2)->timestamp;
128
129   return ((ct1 < ct2) ? -1 : ((ct1 == ct2) ? 0 : 1));
130 /* this does not produce an gint :(
131   return ((ct1 - ct2));
132 */
133 }
134
135 /*
136  * gst_timed_value_find:
137  * @p1: a pointer to a #GstTimedValue
138  * @p2: a pointer to a #GstClockTime
139  *
140  * Compare function for g_list operations that operates on a #GstTimedValue and
141  * a #GstClockTime.
142  */
143 static gint
144 gst_timed_value_find (gconstpointer p1, gconstpointer p2)
145 {
146   GstClockTime ct1 = ((GstTimedValue *) p1)->timestamp;
147   GstClockTime ct2 = *(GstClockTime *) p2;
148
149   return ((ct1 < ct2) ? -1 : ((ct1 == ct2) ? 0 : 1));
150 /* this does not produce an gint :(
151   return ((ct1 - ct2));
152 */
153 }
154
155 /*
156  * gst_controlled_property_set_interpolation_mode:
157  * @self: the controlled property object to change
158  * @mode: the new interpolation mode
159  *
160  * Sets the given Interpolation mode for the controlled property and activates
161  * the respective interpolation hooks.
162  *
163  * Returns: %TRUE for success
164  */
165 static gboolean
166 gst_controlled_property_set_interpolation_mode (GstControlledProperty * self,
167     GstInterpolateMode mode)
168 {
169   gboolean res = TRUE;
170
171   self->interpolation = mode;
172   if (mode != GST_INTERPOLATE_USER) {
173     switch (self->type) {
174       case G_TYPE_INT:
175         self->get = interpolation_methods[mode]->get_int;
176         self->get_value_array =
177             interpolation_methods[mode]->get_int_value_array;
178         break;
179       case G_TYPE_UINT:
180         self->get = interpolation_methods[mode]->get_uint;
181         self->get_value_array =
182             interpolation_methods[mode]->get_uint_value_array;
183         break;
184       case G_TYPE_LONG:
185         self->get = interpolation_methods[mode]->get_long;
186         self->get_value_array =
187             interpolation_methods[mode]->get_long_value_array;
188         break;
189       case G_TYPE_ULONG:
190         self->get = interpolation_methods[mode]->get_ulong;
191         self->get_value_array =
192             interpolation_methods[mode]->get_ulong_value_array;
193         break;
194       case G_TYPE_FLOAT:
195         self->get = interpolation_methods[mode]->get_float;
196         self->get_value_array =
197             interpolation_methods[mode]->get_float_value_array;
198         break;
199       case G_TYPE_DOUBLE:
200         self->get = interpolation_methods[mode]->get_double;
201         self->get_value_array =
202             interpolation_methods[mode]->get_double_value_array;
203         break;
204       case G_TYPE_BOOLEAN:
205         self->get = interpolation_methods[mode]->get_boolean;
206         self->get_value_array =
207             interpolation_methods[mode]->get_boolean_value_array;
208         break;
209         break;
210       default:
211         self->get = NULL;
212         self->get_value_array = NULL;
213     }
214     if (!self->get) {           /* || !self->get_value_array) */
215       GST_WARNING ("incomplete implementation for type '%d'", self->type);
216       res = FALSE;
217     }
218   } else {
219     /* TODO shouldn't this also get a GstInterpolateMethod *user_method
220        for the case mode==GST_INTERPOLATE_USER
221      */
222     res = FALSE;
223   }
224   return (res);
225 }
226
227 /*
228  * gst_controlled_property_new:
229  * @object: for which object the controlled property should be set up
230  * @name: the name of the property to be controlled
231  *
232  * Private method which initializes the fields of a new controlled property
233  * structure.
234  *
235  * Returns: a freshly allocated structure or %NULL
236  */
237 static GstControlledProperty *
238 gst_controlled_property_new (GObject * object, const gchar * name)
239 {
240   GstControlledProperty *prop = NULL;
241   GParamSpec *pspec;
242
243   GST_INFO ("trying to put property '%s' under control", name);
244
245   /* check if the object has a property of that name */
246   if ((pspec =
247           g_object_class_find_property (G_OBJECT_GET_CLASS (object), name))) {
248     GST_DEBUG ("  psec->flags : 0x%08x", pspec->flags);
249
250     /* check if this param is witable */
251     g_return_val_if_fail ((pspec->flags & G_PARAM_WRITABLE), NULL);
252     /* check if property is controlable */
253     g_return_val_if_fail ((pspec->flags & GST_PARAM_CONTROLLABLE), NULL);
254     /* check if this param is not construct-only */
255     g_return_val_if_fail (!(pspec->flags & G_PARAM_CONSTRUCT_ONLY), NULL);
256
257     /* TODO do sanity checks
258        we don't control some pspec->value_type:
259        G_TYPE_PARAM_BOXED
260        G_TYPE_PARAM_ENUM
261        G_TYPE_PARAM_FLAGS
262        G_TYPE_PARAM_OBJECT
263        G_TYPE_PARAM_PARAM
264        G_TYPE_PARAM_POINTER
265        G_TYPE_PARAM_STRING
266      */
267
268     if ((prop = g_new0 (GstControlledProperty, 1))) {
269       gchar *signal_name;
270
271       prop->name = pspec->name; /* so we don't use the same mem twice */
272       prop->type = G_PARAM_SPEC_VALUE_TYPE (pspec);
273       gst_controlled_property_set_interpolation_mode (prop,
274           GST_INTERPOLATE_NONE);
275       /* prepare our gvalues */
276       g_value_init (&prop->default_value, prop->type);
277       g_value_init (&prop->result_value, prop->type);
278       g_value_init (&prop->last_value.value, prop->type);
279       switch (prop->type) {
280         case G_TYPE_INT:{
281           GParamSpecInt *tpspec = G_PARAM_SPEC_INT (pspec);
282
283           g_value_set_int (&prop->default_value, tpspec->default_value);
284         }
285           break;
286         case G_TYPE_UINT:{
287           GParamSpecUInt *tpspec = G_PARAM_SPEC_UINT (pspec);
288
289           g_value_set_uint (&prop->default_value, tpspec->default_value);
290         }
291           break;
292         case G_TYPE_LONG:{
293           GParamSpecLong *tpspec = G_PARAM_SPEC_LONG (pspec);
294
295           g_value_set_long (&prop->default_value, tpspec->default_value);
296         }
297           break;
298         case G_TYPE_ULONG:{
299           GParamSpecULong *tpspec = G_PARAM_SPEC_ULONG (pspec);
300
301           g_value_set_ulong (&prop->default_value, tpspec->default_value);
302         }
303           break;
304         case G_TYPE_FLOAT:{
305           GParamSpecFloat *tpspec = G_PARAM_SPEC_FLOAT (pspec);
306
307           g_value_set_float (&prop->default_value, tpspec->default_value);
308         }
309         case G_TYPE_DOUBLE:{
310           GParamSpecDouble *tpspec = G_PARAM_SPEC_DOUBLE (pspec);
311
312           g_value_set_double (&prop->default_value, tpspec->default_value);
313         }
314           break;
315         case G_TYPE_BOOLEAN:{
316           GParamSpecBoolean *tpspec = G_PARAM_SPEC_BOOLEAN (pspec);
317
318           g_value_set_boolean (&prop->default_value, tpspec->default_value);
319         }
320           break;
321         default:
322           GST_WARNING ("incomplete implementation for paramspec type '%s'",
323               G_PARAM_SPEC_TYPE_NAME (pspec));
324       }
325       /* TODO what about adding a timedval with timestamp=0 and value=default
326        * a bit easier for interpolators, example:
327        * first timestamp is at 5
328        * requested value if for timestamp=3
329        * LINEAR and Co. would need to interpolate from default value to value
330        * at timestamp 5
331        */
332       signal_name = g_alloca (8 + 1 + strlen (name));
333       g_sprintf (signal_name, "notify::%s", name);
334       prop->notify_handler_id =
335           g_signal_connect (object, signal_name,
336           G_CALLBACK (on_object_controlled_property_changed), (gpointer) prop);
337     }
338   } else {
339     GST_WARNING ("class '%s' has no property '%s'", G_OBJECT_TYPE_NAME (object),
340         name);
341   }
342
343   return (prop);
344 }
345
346 /*
347  * gst_controlled_property_free:
348  * @prop: the object to free
349  *
350  * Private method which frees all data allocated by a #GstControlledProperty
351  * instance.
352  */
353 static void
354 gst_controlled_property_free (GstControlledProperty * prop)
355 {
356   GList *node;
357
358   for (node = prop->values; node; node = g_list_next (node)) {
359     g_free (node->data);
360   }
361   g_list_free (prop->values);
362   g_free (prop);
363 }
364
365 /*
366  * gst_controller_find_controlled_property:
367  * @self: the controller object to search for a property in
368  * @name: the gobject property name to look for
369  *
370  * Searches the list of properties under control.
371  *
372  * Returns: a #GstControlledProperty object of %NULL if the property is not
373  * being controlled.
374  */
375 static GstControlledProperty *
376 gst_controller_find_controlled_property (GstController * self,
377     const gchar * name)
378 {
379   GstControlledProperty *prop;
380   GList *node;
381
382   for (node = self->properties; node; node = g_list_next (node)) {
383     prop = node->data;
384     if (!strcmp (prop->name, name)) {
385       return (prop);
386     }
387   }
388   GST_DEBUG ("controller does not (yet) manage property '%s'", name);
389
390   return (NULL);
391 }
392
393 /* methods */
394
395 /**
396  * gst_controller_new_valist:
397  * @object: the object of which some properties should be controlled
398  * @var_args: %NULL terminated list of property names that should be controlled
399  *
400  * Creates a new GstController for the given object's properties
401  *
402  * Returns: the new controller.
403  * Since: 0.9
404  */
405 GstController *
406 gst_controller_new_valist (GObject * object, va_list var_args)
407 {
408   GstController *self;
409   GstControlledProperty *prop;
410   gchar *name;
411
412   g_return_val_if_fail (G_IS_OBJECT (object), NULL);
413
414   GST_INFO ("setting up a new controller");
415
416   /* TODO should this method check if the given object implements GstParent and
417      if so instantiate a GstParentController ?
418
419      BilboEd: This is too specific to be put here, don't we want
420      GstController to be as generic as possible ?
421
422      Ensonic: So we will have gst_parent_controller_new as well and maybe a
423      convinience function that automatically chooses the right one (how to name it)?
424      GstParent will be in core after all.
425    */
426
427   self = g_object_get_qdata (object, __gst_controller_key);
428   /* create GstControlledProperty for each property */
429   while ((name = va_arg (var_args, gchar *))) {
430     /* test if this property isn't yet controlled */
431     if (!self || !(prop = gst_controller_find_controlled_property (self, name))) {
432       /* create GstControlledProperty and add to self->propeties List */
433       if ((prop = gst_controlled_property_new (object, name))) {
434         /* if we don't have a controller object yet, now is the time to create one */
435         if (!self) {
436           self = g_object_new (GST_TYPE_CONTROLLER, NULL);
437           self->object = object;
438           /* store the controller */
439           g_object_set_qdata (object, __gst_controller_key, self);
440         } else {
441           GST_INFO ("returning existing controller");
442         }
443         self->properties = g_list_prepend (self->properties, prop);
444       }
445     } else {
446       GST_WARNING ("trying to control property again");
447     }
448   }
449   va_end (var_args);
450
451   if (self)
452     GST_INFO ("controller->ref_count=%d", G_OBJECT (self)->ref_count);
453   return (self);
454 }
455
456 /**
457  * gst_controller_new_list:
458  * @object: the object of which some properties should be controlled
459  * @list: list of property names that should be controlled
460  *
461  * Creates a new GstController for the given object's properties
462  *
463  * Returns: the new controller.
464  * Since: 0.9
465  */
466 GstController *
467 gst_controller_new_list (GObject * object, GList * list)
468 {
469   GstController *self;
470   GstControlledProperty *prop;
471   gchar *name;
472   GList *node;
473
474   g_return_val_if_fail (G_IS_OBJECT (object), NULL);
475
476   GST_INFO ("setting up a new controller");
477
478   self = g_object_get_qdata (object, __gst_controller_key);
479   /* create GstControlledProperty for each property */
480   for (node = list; node; node = g_list_next (node)) {
481     name = (gchar *) node->data;
482     /* test if this property isn't yet controlled */
483     if (!self || !(prop = gst_controller_find_controlled_property (self, name))) {
484       /* create GstControlledProperty and add to self->propeties List */
485       if ((prop = gst_controlled_property_new (object, name))) {
486         /* if we don't have a controller object yet, now is the time to create one */
487         if (!self) {
488           self = g_object_new (GST_TYPE_CONTROLLER, NULL);
489           self->object = object;
490           /* store the controller */
491           g_object_set_qdata (object, __gst_controller_key, self);
492         } else {
493           GST_INFO ("returning existing controller");
494         }
495         self->properties = g_list_prepend (self->properties, prop);
496       }
497     } else {
498       GST_WARNING ("trying to control property again");
499     }
500   }
501
502   if (self)
503     GST_INFO ("controller->ref_count=%d", G_OBJECT (self)->ref_count);
504   return (self);
505 }
506
507 /**
508  * gst_controller_new:
509  * @object: the object of which some properties should be controlled
510  * @...: %NULL terminated list of property names that should be controlled
511  *
512  * Creates a new GstController for the given object's properties
513  *
514  * Returns: the new controller.
515  * Since: 0.9
516  */
517 GstController *
518 gst_controller_new (GObject * object, ...)
519 {
520   GstController *self;
521   va_list var_args;
522
523   g_return_val_if_fail (G_IS_OBJECT (object), NULL);
524
525   va_start (var_args, object);
526   self = gst_controller_new_valist (object, var_args);
527   va_end (var_args);
528
529   return (self);
530 }
531
532 /**
533  * gst_controller_remove_properties_valist:
534  * @self: the controller object from which some properties should be removed
535  * @var_args: %NULL terminated list of property names that should be removed
536  *
537  * Removes the given object properties from the controller
538  *
539  * Returns: %FALSE if one of the given property isn't handled by the controller, %TRUE otherwise
540  * Since: 0.9
541  */
542 gboolean
543 gst_controller_remove_properties_valist (GstController * self, va_list var_args)
544 {
545   gboolean res = TRUE;
546   GstControlledProperty *prop;
547   gchar *name;
548
549   g_return_val_if_fail (GST_IS_CONTROLLER (self), FALSE);
550
551   while ((name = va_arg (var_args, gchar *))) {
552     /* find the property in the properties list of the controller, remove and free it */
553     g_mutex_lock (self->lock);
554     if ((prop = gst_controller_find_controlled_property (self, name))) {
555       self->properties = g_list_remove (self->properties, prop);
556       g_signal_handler_disconnect (self->object, prop->notify_handler_id);
557       gst_controlled_property_free (prop);
558     } else {
559       res = FALSE;
560     }
561     g_mutex_unlock (self->lock);
562   }
563
564   return (res);
565 }
566
567 /**
568  * gst_controller_remove_properties_list:
569  * @self: the controller object from which some properties should be removed
570  * @list: #GList of property names that should be removed
571  *
572  * Removes the given object properties from the controller
573  *
574  * Returns: %FALSE if one of the given property isn't handled by the controller, %TRUE otherwise
575  * Since: 0.9
576  */
577 gboolean
578 gst_controller_remove_properties_list (GstController * self, GList * list)
579 {
580   gboolean res = TRUE;
581   GstControlledProperty *prop;
582   gchar *name;
583   GList *tmp;
584
585   g_return_val_if_fail (GST_IS_CONTROLLER (self), FALSE);
586
587   for (tmp = list; tmp; tmp = g_list_next (tmp)) {
588     name = (gchar *) tmp->data;
589
590     /* find the property in the properties list of the controller, remove and free it */
591     g_mutex_lock (self->lock);
592     if ((prop = gst_controller_find_controlled_property (self, name))) {
593       self->properties = g_list_remove (self->properties, prop);
594       g_signal_handler_disconnect (self->object, prop->notify_handler_id);
595       gst_controlled_property_free (prop);
596     } else {
597       res = FALSE;
598     }
599     g_mutex_unlock (self->lock);
600   }
601
602   return (res);
603 }
604
605 /**
606  * gst_controller_remove_properties:
607  * @self: the controller object from which some properties should be removed
608  * @...: %NULL terminated list of property names that should be removed
609  *
610  * Removes the given object properties from the controller
611  *
612  * Returns: %FALSE if one of the given property isn't handled by the controller, %TRUE otherwise
613  * Since: 0.9
614  */
615 gboolean
616 gst_controller_remove_properties (GstController * self, ...)
617 {
618   gboolean res;
619   va_list var_args;
620
621   g_return_val_if_fail (GST_IS_CONTROLLER (self), FALSE);
622
623   va_start (var_args, self);
624   res = gst_controller_remove_properties_valist (self, var_args);
625   va_end (var_args);
626
627   return (res);
628 }
629
630 /**
631  * gst_controller_set:
632  * @self: the controller object which handles the properties
633  * @property_name: the name of the property to set
634  * @timestamp: the time the control-change is schedules for
635  * @value: the control-value
636  *
637  * Set the value of given controller-handled property at a certain time.
638  *
639  * Returns: FALSE if the values couldn't be set (ex : properties not handled by controller), TRUE otherwise
640  * Since: 0.9
641  */
642 gboolean
643 gst_controller_set (GstController * self, gchar * property_name,
644     GstClockTime timestamp, GValue * value)
645 {
646   gboolean res = FALSE;
647   GstControlledProperty *prop;
648
649   g_return_val_if_fail (GST_IS_CONTROLLER (self), FALSE);
650   g_return_val_if_fail (property_name, FALSE);
651   g_return_val_if_fail (GST_CLOCK_TIME_IS_VALID (timestamp), FALSE);
652   g_return_val_if_fail (G_IS_VALUE (value), FALSE);
653
654   g_mutex_lock (self->lock);
655   if ((prop = gst_controller_find_controlled_property (self, property_name))) {
656     if (G_VALUE_TYPE (value) == prop->type) {
657       GstTimedValue *tv;
658       GList *node;
659
660       /* check if a timed_value for the timestamp already exists */
661       if ((node = g_list_find_custom (prop->values, &timestamp,
662                   gst_timed_value_find))) {
663         tv = node->data;
664         memcpy (&tv->value, value, sizeof (GValue));
665       } else {
666         /* create a new GstTimedValue */
667         tv = g_new (GstTimedValue, 1);
668         tv->timestamp = timestamp;
669         memcpy (&tv->value, value, sizeof (GValue));
670         /* and sort it into the prop->values list */
671         prop->values =
672             g_list_insert_sorted (prop->values, tv, gst_timed_value_compare);
673       }
674       res = TRUE;
675     } else {
676       GST_WARNING ("incompatible value type for property '%s'", prop->name);
677     }
678   }
679   g_mutex_unlock (self->lock);
680
681   return (res);
682 }
683
684 /**
685  * gst_controller_set_from_list:
686  * @self: the controller object which handles the properties
687  * @property_name: the name of the property to set
688  * @timedvalues: a list with #GstTimedValue items
689  *
690  * Sets multiple timed values at once.
691  *
692  * Returns: %FALSE if the values couldn't be set (ex : properties not handled by controller), %TRUE otherwise
693  * Since: 0.9
694  */
695
696 gboolean
697 gst_controller_set_from_list (GstController * self, gchar * property_name,
698     GSList * timedvalues)
699 {
700   gboolean res = FALSE;
701   GstControlledProperty *prop;
702   GSList *node;
703   GstTimedValue *tv;
704
705   g_return_val_if_fail (GST_IS_CONTROLLER (self), FALSE);
706   g_return_val_if_fail (property_name, FALSE);
707
708   g_mutex_lock (self->lock);
709   if ((prop = gst_controller_find_controlled_property (self, property_name))) {
710     for (node = timedvalues; node; node = g_slist_next (node)) {
711       tv = node->data;
712       if (G_VALUE_TYPE (&tv->value) == prop->type) {
713         g_return_val_if_fail (GST_CLOCK_TIME_IS_VALID (tv->timestamp), FALSE);
714         /* TODO copy the timed value or just link in? */
715         prop->values =
716             g_list_insert_sorted (prop->values, tv, gst_timed_value_compare);
717         res = TRUE;
718       } else {
719         GST_WARNING ("incompatible value type for property '%s'", prop->name);
720       }
721     }
722   }
723   g_mutex_unlock (self->lock);
724
725   return (res);
726 }
727
728 /**
729  * gst_controller_unset:
730  * @self: the controller object which handles the properties
731  * @property_name: the name of the property to unset
732  * @timestamp: the time the control-change should be removed from
733  *
734  * Used to remove the value of given controller-handled property at a certain
735  * time.
736  *
737  * Returns: %FALSE if the values couldn't be unset (ex : properties not handled by controller), %TRUE otherwise
738  * Since: 0.9
739  */
740 gboolean
741 gst_controller_unset (GstController * self, gchar * property_name,
742     GstClockTime timestamp)
743 {
744   gboolean res = FALSE;
745   GstControlledProperty *prop;
746
747   g_return_val_if_fail (GST_IS_CONTROLLER (self), FALSE);
748   g_return_val_if_fail (property_name, FALSE);
749   g_return_val_if_fail (GST_CLOCK_TIME_IS_VALID (timestamp), FALSE);
750
751   g_mutex_lock (self->lock);
752   if ((prop = gst_controller_find_controlled_property (self, property_name))) {
753     prop->values = g_list_remove (prop->values, prop);
754     res = TRUE;
755   }
756   g_mutex_unlock (self->lock);
757
758   return (res);
759 }
760
761 /**
762  * gst_controller_get:
763  * @self: the controller object which handles the properties
764  * @property_name: the name of the property to get
765  * @timestamp: the time the control-change should be read from
766  *
767  * Gets the value for the given controller-handled property at the requested
768  * time.
769  *
770  * Returns: the GValue of the property at the given time, or %NULL if the property isn't handled by the controller
771  * Since: 0.9
772  */
773 GValue *
774 gst_controller_get (GstController * self, gchar * property_name,
775     GstClockTime timestamp)
776 {
777   GstControlledProperty *prop;
778   GValue *val = NULL;
779
780   g_return_val_if_fail (GST_IS_CONTROLLER (self), NULL);
781   g_return_val_if_fail (property_name, NULL);
782   g_return_val_if_fail (GST_CLOCK_TIME_IS_VALID (timestamp), NULL);
783
784   g_mutex_lock (self->lock);
785   if ((prop = gst_controller_find_controlled_property (self, property_name))) {
786     /* get current value via interpolator */
787     val = prop->get (prop, timestamp);
788   }
789   g_mutex_unlock (self->lock);
790
791   return (val);
792 }
793
794 /**
795  * gst_controller_get_all:
796  * @self: the controller to get the list from
797  * @property_name: the name of the property to get the list for
798  *
799  * Returns a read-only copy of the list of GstTimedValue for the given property.
800  * Free the list after done with it.
801  *
802  * Returns: a copy of the list, or %NULL if the property isn't handled by the controller
803  * Since: 0.9
804  */
805 const GList *
806 gst_controller_get_all (GstController * self, gchar * property_name)
807 {
808   GList *res = NULL;
809   GstControlledProperty *prop;
810
811   g_return_val_if_fail (GST_IS_CONTROLLER (self), NULL);
812   g_return_val_if_fail (property_name, NULL);
813
814   g_mutex_lock (self->lock);
815   if ((prop = gst_controller_find_controlled_property (self, property_name))) {
816     res = g_list_copy (prop->values);
817   }
818   g_mutex_unlock (self->lock);
819
820   return (res);
821 }
822
823 /**
824  * gst_controller_sync_values:
825  * @self: the controller that handles the values
826  * @timestamp: the time that should be processed
827  *
828  * Sets the properties of the element, according to the controller that (maybe)
829  * handles them and for the given timestamp.
830  *
831  * Returns: %TRUE if the controller values could be applied to the object
832  * properties, %FALSE otherwise
833  * Since: 0.9
834  */
835 gboolean
836 gst_controller_sync_values (GstController * self, GstClockTime timestamp)
837 {
838   GstControlledProperty *prop;
839   GList *node;
840   GValue *value;
841   gboolean live;
842
843   g_return_val_if_fail (GST_IS_CONTROLLER (self), FALSE);
844   g_return_val_if_fail (GST_CLOCK_TIME_IS_VALID (timestamp), FALSE);
845
846   GST_INFO ("sync_values");
847
848   g_mutex_lock (self->lock);
849   /* go over the controlled properties of the controller */
850   for (node = self->properties; node; node = g_list_next (node)) {
851     prop = node->data;
852     GST_DEBUG ("  property '%s' at ts=%" G_GUINT64_FORMAT, prop->name,
853         timestamp);
854
855     live = FALSE;
856     if (G_IS_VALUE (&prop->live_value.value)) {
857       GList *lnode =
858           gst_controlled_property_find_timed_value_node (prop, timestamp);
859       if (!lnode) {
860         GST_DEBUG ("    no control changes in the queue");
861         live = TRUE;
862       } else {
863         GstTimedValue *tv = lnode->data;
864
865         if (prop->live_value.timestamp < tv->timestamp) {
866           g_value_unset (&prop->live_value.value);
867           GST_DEBUG ("    live value resetted");
868         } else if (prop->live_value.timestamp < timestamp) {
869           live = TRUE;
870         }
871       }
872     }
873     if (!live) {
874       /* get current value via interpolator */
875       value = prop->get (prop, timestamp);
876       prop->last_value.timestamp = timestamp;
877       g_value_copy (value, &prop->last_value.value);
878       g_object_set_property (self->object, prop->name, value);
879     }
880   }
881   g_mutex_unlock (self->lock);
882   /* TODO what can here go wrong, to return FALSE ?
883      BilboEd : Nothing I guess, as long as all the checks are made when creating the controller,
884      adding/removing controlled properties, etc...
885    */
886
887   return (TRUE);
888 }
889
890 /**
891  * gst_controller_get_value_arrays:
892  * @self: the controller that handles the values
893  * @timestamp: the time that should be processed
894  * @value_arrays: list to return the control-values in
895  *
896  * Function to be able to get an array of values for one or more given element
897  * properties.
898  *
899  * If the GstValueArray->values array in list nodes is NULL, it will be created
900  * by the function.
901  * The type of the values in the array are the same as the property's type.
902  *
903  * Returns: %TRUE if the given array(s) could be filled, %FALSE otherwise
904  * Since: 0.9
905  */
906 gboolean
907 gst_controller_get_value_arrays (GstController * self,
908     GstClockTime timestamp, GSList * value_arrays)
909 {
910   gboolean res = TRUE;
911   GSList *node;
912
913   g_return_val_if_fail (GST_IS_CONTROLLER (self), FALSE);
914   g_return_val_if_fail (GST_CLOCK_TIME_IS_VALID (timestamp), FALSE);
915   g_return_val_if_fail (value_arrays, FALSE);
916
917   for (node = value_arrays; (res && node); node = g_slist_next (node)) {
918     res = gst_controller_get_value_array (self, timestamp, node->data);
919   }
920
921   return (res);
922 }
923
924 /**
925  * gst_controller_get_value_array:
926  * @self: the controller that handles the values
927  * @timestamp: the time that should be processed
928  * @value_array: array to put control-values in
929  *
930  * Function to be able to get an array of values for one element properties
931  *
932  * If the GstValueArray->values array is NULL, it will be created by the function.
933  * The type of the values in the array are the same as the property's type.
934  *
935  * Returns: %TRUE if the given array(s) could be filled, %FALSE otherwise
936  * Since: 0.9
937  */
938 gboolean
939 gst_controller_get_value_array (GstController * self, GstClockTime timestamp,
940     GstValueArray * value_array)
941 {
942   gboolean res = FALSE;
943   GstControlledProperty *prop;
944
945   g_return_val_if_fail (GST_IS_CONTROLLER (self), FALSE);
946   g_return_val_if_fail (GST_CLOCK_TIME_IS_VALID (timestamp), FALSE);
947   g_return_val_if_fail (value_array, FALSE);
948   g_return_val_if_fail (value_array->property_name, FALSE);
949
950   /* TODO and if GstValueArray->values is not NULL, the caller is resposible that
951      is is big enough for nbsamples values, right?
952    */
953
954   g_mutex_lock (self->lock);
955   if ((prop =
956           gst_controller_find_controlled_property (self,
957               value_array->property_name))) {
958     if (!value_array->values) {
959       /* TODO from where to get the base size
960          value_array->values=g_new(sizeof(???),nbsamples);
961        */
962     }
963     /* get current value_array via interpolator */
964     res = prop->get_value_array (prop, timestamp, value_array);
965   }
966   g_mutex_unlock (self->lock);
967   return (res);
968 }
969
970 /**
971  * gst_controller_set_interpolation_mode:
972  * @self: the controller object
973  * @property_name: the name of the property for which to change the interpolation
974  * @mode: interpolation mode
975  *
976  * Sets the given interpolation mode on the given property.
977  *
978  * Returns: %TRUE if the property is handled by the controller, %FALSE otherwise
979  * Since: 0.9
980  */
981 gboolean
982 gst_controller_set_interpolation_mode (GstController * self,
983     gchar * property_name, GstInterpolateMode mode)
984 {
985   gboolean res = FALSE;
986   GstControlledProperty *prop;
987
988   g_return_val_if_fail (GST_IS_CONTROLLER (self), FALSE);
989   g_return_val_if_fail (property_name, FALSE);
990
991   g_mutex_lock (self->lock);
992   if ((prop = gst_controller_find_controlled_property (self, property_name))) {
993     /* TODO shouldn't this also get a GstInterpolateMethod *user_method
994        for the case mode==GST_INTERPOLATE_USER
995      */
996     gst_controlled_property_set_interpolation_mode (prop, mode);
997     res = TRUE;
998   }
999   g_mutex_unlock (self->lock);
1000
1001   return (res);
1002 }
1003
1004 /*
1005 void
1006 gst_controller_set_live_value(GstController * self, gchar *property_name,
1007     GstClockTime timestamp, GValue *value)
1008 {
1009   GstControlledProperty *prop;
1010
1011   g_return_if_fail (GST_IS_CONTROLLER (self));
1012   g_return_if_fail (property_name);
1013
1014   g_mutex_lock (self->lock);
1015   if ((prop = gst_controller_find_controlled_property (self, property_name))) {
1016     g_value_unset (&prop->live_value.value);
1017     g_value_init (&prop->live_value.value, prop->type);
1018     g_value_copy (value, &prop->live_value.value);
1019     prop->live_value.timestamp = timestamp;
1020   }
1021   g_mutex_unlock (self->lock);
1022 }
1023
1024 */
1025
1026 /* gobject handling */
1027
1028 static void
1029 _gst_controller_finalize (GObject * object)
1030 {
1031   GstController *self = GST_CONTROLLER (object);
1032   GList *node;
1033   GstControlledProperty *prop;
1034
1035   /* free list of properties */
1036   if (self->properties) {
1037     for (node = self->properties; node; node = g_list_next (node)) {
1038       prop = node->data;
1039       g_signal_handler_disconnect (self->object, prop->notify_handler_id);
1040       gst_controlled_property_free (prop);
1041     }
1042     g_list_free (self->properties);
1043     self->properties = NULL;
1044   }
1045   g_mutex_free (self->lock);
1046   /* remove controller from objects qdata list */
1047   g_object_set_qdata (self->object, __gst_controller_key, NULL);
1048
1049   if (G_OBJECT_CLASS (parent_class)->finalize)
1050     (G_OBJECT_CLASS (parent_class)->finalize) (object);
1051 }
1052
1053 static void
1054 _gst_controller_init (GTypeInstance * instance, gpointer g_class)
1055 {
1056   GstController *self = GST_CONTROLLER (instance);
1057
1058   self->lock = g_mutex_new ();
1059
1060 }
1061
1062 static void
1063 _gst_controller_class_init (GstControllerClass * klass)
1064 {
1065   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
1066
1067   parent_class = g_type_class_ref (G_TYPE_OBJECT);
1068
1069   gobject_class->finalize = _gst_controller_finalize;
1070
1071   __gst_controller_key = g_quark_from_string ("gst::controller");
1072
1073   /* register properties */
1074   /* register signals */
1075   /* set defaults for overridable methods */
1076   /* TODO which of theses do we need ?
1077      BilboEd : none :)
1078    */
1079 }
1080
1081 GType
1082 gst_controller_get_type (void)
1083 {
1084   static GType type = 0;
1085
1086   if (type == 0) {
1087     static const GTypeInfo info = {
1088       sizeof (GstControllerClass),
1089       NULL,                     /* base_init */
1090       NULL,                     /* base_finalize */
1091       (GClassInitFunc) _gst_controller_class_init,      /* class_init */
1092       NULL,                     /* class_finalize */
1093       NULL,                     /* class_data */
1094       sizeof (GstController),
1095       0,                        /* n_preallocs */
1096       (GInstanceInitFunc) _gst_controller_init, /* instance_init */
1097       NULL                      /* value_table */
1098     };
1099     type = g_type_register_static (G_TYPE_OBJECT, "GstController", &info, 0);
1100   }
1101   return type;
1102 }