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