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