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