controller: add api to check for active controllers (needed for e.g. volume)
[platform/upstream/gstreamer.git] / gst / gstcontroller.c
1 /* GStreamer
2  *
3  * Copyright (C) 2005 Stefan Kost <ensonic at users dot sf dot net>
4  *               2007 Sebastian Dröge <slomo@circular-chaos.org>
5  *               2011 Stefan Sauer <ensonic at users dot sf dot net>
6  *
7  * gstcontroller.c: dynamic parameter control subsystem
8  *
9  * This library is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU Library General Public
11  * License as published by the Free Software Foundation; either
12  * version 2 of the License, or (at your option) any later version.
13  *
14  * This library is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17  * Library General Public License for more details.
18  *
19  * You should have received a copy of the GNU Library General Public
20  * License along with this library; if not, write to the
21  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
22  * Boston, MA 02111-1307, USA.
23  */
24
25 /**
26  * SECTION:gstcontroller
27  * @short_description: dynamic parameter control subsystem
28  *
29  * The controller subsystem offers a lightweight way to adjust gobject
30  * properties over stream-time. It works by using time-stamped value pairs that
31  * are queued for element-properties. At run-time the elements continously pull
32  * values changes for the current stream-time.
33  *
34  * What needs to be changed in a #GstElement?
35  * Very little - it is just two steps to make a plugin controllable!
36  * <orderedlist>
37  *   <listitem><para>
38  *     mark gobject-properties paramspecs that make sense to be controlled,
39  *     by GST_PARAM_CONTROLLABLE.
40  *   </para></listitem>
41  *   <listitem><para>
42  *     when processing data (get, chain, loop function) at the beginning call
43  *     gst_object_sync_values(element,timestamp).
44  *     This will made the controller to update all gobject properties that are under
45  *     control with the current values based on timestamp.
46  *   </para></listitem>
47  * </orderedlist>
48  *
49  * What needs to be done in applications?
50  * Again its not a lot to change.
51  * <orderedlist>
52  *   <listitem><para>
53  *     first put some properties under control, by calling
54  *     controller = gst_object_control_properties (object, "prop1", "prop2",...);
55  *   </para></listitem>
56  *   <listitem><para>
57  *     create a #GstControlSource.
58  *     csource = gst_interpolation_control_source_new ();
59  *     gst_interpolation_control_source_set_interpolation_mode(csource, mode);
60  *   </para></listitem>
61  *   <listitem><para>
62  *     Attach the #GstControlSource on the controller to a property.
63  *     gst_controller_set_control_source (controller, "prop1", csource);
64  *   </para></listitem>
65  *   <listitem><para>
66  *     Set the control values
67  *     gst_interpolation_control_source_set (csource,0 * GST_SECOND, value1);
68  *     gst_interpolation_control_source_set (csource,1 * GST_SECOND, value2);
69  *   </para></listitem>
70  *   <listitem><para>
71  *     start your pipeline
72  *   </para></listitem>
73  * </orderedlist>
74  */
75
76 #include "gst_private.h"
77
78 #include "gstobject.h"
79 #include "gstclock.h"
80 #include "gstinfo.h"
81 #include "gstcontroller.h"
82 #include "gstcontrolsource.h"
83 #include "gstparamspecs.h"
84
85 #define GST_CAT_DEFAULT controller_debug
86 GST_DEBUG_CATEGORY (GST_CAT_DEFAULT);
87
88 static GObjectClass *parent_class = NULL;
89
90 /* property ids */
91 enum
92 {
93   PROP_CONTROL_RATE = 1
94 };
95
96 struct _GstControllerPrivate
97 {
98   GstClockTime control_rate;
99   GstClockTime last_sync;
100 };
101
102 /* helper */
103
104 /*
105  * GstControlledProperty:
106  */
107 typedef struct _GstControlledProperty
108 {
109   GParamSpec *pspec;            /* GParamSpec for this property */
110   const gchar *name;            /* name of the property */
111   GstControlSource *csource;    /* GstControlSource for this property */
112   gboolean disabled;
113   GValue last_value;
114 } GstControlledProperty;
115
116 #define GST_CONTROLLED_PROPERTY(obj)    ((GstControlledProperty *)(obj))
117
118 /*
119  * gst_controlled_property_new:
120  * @object: for which object the controlled property should be set up
121  * @name: the name of the property to be controlled
122  *
123  * Private method which initializes the fields of a new controlled property
124  * structure.
125  *
126  * Returns: a freshly allocated structure or %NULL
127  */
128 static GstControlledProperty *
129 gst_controlled_property_new (GstObject * object, const gchar * name)
130 {
131   GstControlledProperty *prop = NULL;
132   GParamSpec *pspec;
133
134   GST_INFO ("trying to put property '%s' under control", name);
135
136   /* check if the object has a property of that name */
137   if ((pspec =
138           g_object_class_find_property (G_OBJECT_GET_CLASS (object), name))) {
139     GST_DEBUG ("  psec->flags : 0x%08x", pspec->flags);
140
141     /* check if this param is witable && controlable && !construct-only */
142     g_return_val_if_fail ((pspec->flags & (G_PARAM_WRITABLE |
143                 GST_PARAM_CONTROLLABLE | G_PARAM_CONSTRUCT_ONLY)) ==
144         (G_PARAM_WRITABLE | GST_PARAM_CONTROLLABLE), NULL);
145
146     if ((prop = g_slice_new (GstControlledProperty))) {
147       prop->pspec = pspec;
148       prop->name = pspec->name;
149       prop->csource = NULL;
150       prop->disabled = FALSE;
151       memset (&prop->last_value, 0, sizeof (GValue));
152       g_value_init (&prop->last_value, G_PARAM_SPEC_VALUE_TYPE (prop->pspec));
153     }
154   } else {
155     GST_WARNING ("class '%s' has no property '%s'", G_OBJECT_TYPE_NAME (object),
156         name);
157   }
158   return prop;
159 }
160
161 /*
162  * gst_controlled_property_free:
163  * @prop: the object to free
164  *
165  * Private method which frees all data allocated by a #GstControlledProperty
166  * instance.
167  */
168 static void
169 gst_controlled_property_free (GstControlledProperty * prop)
170 {
171   if (prop->csource)
172     g_object_unref (prop->csource);
173   g_value_unset (&prop->last_value);
174   g_slice_free (GstControlledProperty, prop);
175 }
176
177 /*
178  * gst_controller_find_controlled_property:
179  * @self: the controller object to search for a property in
180  * @name: the gobject property name to look for
181  *
182  * Searches the list of properties under control.
183  *
184  * Returns: a #GstControlledProperty object of %NULL if the property is not
185  * being controlled.
186  */
187 static GstControlledProperty *
188 gst_controller_find_controlled_property (GstController * self,
189     const gchar * name)
190 {
191   GstControlledProperty *prop;
192   GList *node;
193
194   for (node = self->properties; node; node = g_list_next (node)) {
195     prop = node->data;
196     /* FIXME: eventually use GQuark to speed it up */
197     if (!strcmp (prop->name, name)) {
198       return prop;
199     }
200   }
201   GST_DEBUG ("controller does not (yet) manage property '%s'", name);
202
203   return NULL;
204 }
205
206 /*
207  * gst_controller_add_property:
208  * @self: the controller object
209  * @name: name of projecty in @object
210  *
211  * Creates a new #GstControlledProperty if there is none for property @name yet.
212  *
213  * Returns: %TRUE if the property has been added to the controller
214  */
215 static gboolean
216 gst_controller_add_property (GstController * self, const gchar * name)
217 {
218   gboolean res = TRUE;
219
220   /* test if this property isn't yet controlled */
221   if (!gst_controller_find_controlled_property (self, name)) {
222     GstControlledProperty *prop;
223
224     /* create GstControlledProperty and add to self->properties list */
225     if ((prop = gst_controlled_property_new (self->object, name)))
226       self->properties = g_list_prepend (self->properties, prop);
227     else
228       res = FALSE;
229   } else {
230     GST_WARNING ("trying to control property %s again", name);
231   }
232   return res;
233 }
234
235 /*
236  * gst_controller_remove_property:
237  * @self: the controller object
238  * @name: name of projecty in @object
239  *
240  * Removes a #GstControlledProperty for property @name.
241  *
242  * Returns: %TRUE if the property has been removed from the controller
243  */
244 static gboolean
245 gst_controller_remove_property (GstController * self, const gchar * name)
246 {
247   gboolean res = TRUE;
248   GstControlledProperty *prop;
249
250   if ((prop = gst_controller_find_controlled_property (self, name))) {
251     self->properties = g_list_remove (self->properties, prop);
252     //g_signal_handler_disconnect (self->object, prop->notify_handler_id);
253     gst_controlled_property_free (prop);
254   } else {
255     res = FALSE;
256   }
257   return res;
258 }
259
260 /* methods */
261
262 /**
263  * gst_controller_new_valist:
264  * @object: the object of which some properties should be controlled
265  * @var_args: %NULL terminated list of property names that should be controlled
266  *
267  * Creates a new GstController for the given object's properties
268  *
269  * Returns: the new controller.
270  */
271 GstController *
272 gst_controller_new_valist (GstObject * object, va_list var_args)
273 {
274   GstController *self;
275   gchar *name;
276
277   g_return_val_if_fail (G_IS_OBJECT (object), NULL);
278
279   GST_INFO ("setting up a new controller");
280
281   self = g_object_newv (GST_TYPE_CONTROLLER, 0, NULL);
282   self->object = g_object_ref (object);
283
284   /* create GstControlledProperty for each property */
285   while ((name = va_arg (var_args, gchar *))) {
286     gst_controller_add_property (self, name);
287   }
288   va_end (var_args);
289
290   return self;
291 }
292
293 /**
294  * gst_controller_new_list:
295  * @object: the object of which some properties should be controlled
296  * @list: (transfer none) (element-type utf8): list of property names
297  *   that should be controlled
298  *
299  * Creates a new GstController for the given object's properties
300  *
301  * Rename to: gst_controller_new
302  *
303  * Returns: the new controller.
304  */
305 GstController *
306 gst_controller_new_list (GstObject * object, GList * list)
307 {
308   GstController *self;
309   gchar *name;
310   GList *node;
311
312   g_return_val_if_fail (G_IS_OBJECT (object), NULL);
313
314   GST_INFO ("setting up a new controller");
315
316   self = g_object_newv (GST_TYPE_CONTROLLER, 0, NULL);
317   self->object = g_object_ref (object);
318
319   /* create GstControlledProperty for each property */
320   for (node = list; node; node = g_list_next (node)) {
321     name = (gchar *) node->data;
322     gst_controller_add_property (self, name);
323   }
324
325   return self;
326 }
327
328 /**
329  * gst_controller_new:
330  * @object: the object of which some properties should be controlled
331  * @...: %NULL terminated list of property names that should be controlled
332  *
333  * Creates a new GstController for the given object's properties
334  *
335  * Returns: the new controller.
336  */
337 GstController *
338 gst_controller_new (GstObject * object, ...)
339 {
340   GstController *self;
341   va_list var_args;
342
343   g_return_val_if_fail (G_IS_OBJECT (object), NULL);
344
345   va_start (var_args, object);
346   self = gst_controller_new_valist (object, var_args);
347   va_end (var_args);
348
349   return self;
350 }
351
352 // FIXME: docs
353 gboolean
354 gst_controller_add_properties_valist (GstController * self, va_list var_args)
355 {
356   gboolean res = TRUE;
357   gchar *name;
358
359   g_return_val_if_fail (GST_IS_CONTROLLER (self), FALSE);
360
361   while ((name = va_arg (var_args, gchar *))) {
362     /* find the property in the properties list of the controller, remove and free it */
363     g_mutex_lock (self->lock);
364     res &= gst_controller_add_property (self, name);
365     g_mutex_unlock (self->lock);
366   }
367
368   return res;
369 }
370
371 // FIXME: docs
372 gboolean
373 gst_controller_add_properties_list (GstController * self, GList * list)
374 {
375   gboolean res = TRUE;
376   gchar *name;
377   GList *tmp;
378
379   g_return_val_if_fail (GST_IS_CONTROLLER (self), FALSE);
380
381   for (tmp = list; tmp; tmp = g_list_next (tmp)) {
382     name = (gchar *) tmp->data;
383
384     /* find the property in the properties list of the controller, remove and free it */
385     g_mutex_lock (self->lock);
386     res &= gst_controller_add_property (self, name);
387     g_mutex_unlock (self->lock);
388   }
389
390   return res;
391 }
392
393 // FIXME: docs
394 gboolean
395 gst_controller_add_properties (GstController * self, ...)
396 {
397   gboolean res;
398   va_list var_args;
399
400   g_return_val_if_fail (GST_IS_CONTROLLER (self), FALSE);
401
402   va_start (var_args, self);
403   res = gst_controller_add_properties_valist (self, var_args);
404   va_end (var_args);
405
406   return res;
407 }
408
409 /**
410  * gst_controller_remove_properties_valist:
411  * @self: the controller object from which some properties should be removed
412  * @var_args: %NULL terminated list of property names that should be removed
413  *
414  * Removes the given object properties from the controller
415  *
416  * Returns: %FALSE if one of the given property isn't handled by the controller, %TRUE otherwise
417  */
418 gboolean
419 gst_controller_remove_properties_valist (GstController * self, va_list var_args)
420 {
421   gboolean res = TRUE;
422   gchar *name;
423
424   g_return_val_if_fail (GST_IS_CONTROLLER (self), FALSE);
425
426   while ((name = va_arg (var_args, gchar *))) {
427     /* find the property in the properties list of the controller, remove and free it */
428     g_mutex_lock (self->lock);
429     res &= gst_controller_remove_property (self, name);
430     g_mutex_unlock (self->lock);
431   }
432
433   return res;
434 }
435
436 /**
437  * gst_controller_remove_properties_list:
438  * @self: the controller object from which some properties should be removed
439  * @list: (transfer none) (element-type utf8): #GList of property names that
440  *   should be removed
441  *
442  * Removes the given object properties from the controller
443  *
444  * Rename to: gst_controller_remove_properties
445  *
446  * Returns: %FALSE if one of the given property isn't handled by the controller, %TRUE otherwise
447  */
448 gboolean
449 gst_controller_remove_properties_list (GstController * self, GList * list)
450 {
451   gboolean res = TRUE;
452   gchar *name;
453   GList *tmp;
454
455   g_return_val_if_fail (GST_IS_CONTROLLER (self), FALSE);
456
457   for (tmp = list; tmp; tmp = g_list_next (tmp)) {
458     name = (gchar *) tmp->data;
459
460     /* find the property in the properties list of the controller, remove and free it */
461     g_mutex_lock (self->lock);
462     res &= gst_controller_remove_property (self, name);
463     g_mutex_unlock (self->lock);
464   }
465
466   return res;
467 }
468
469 /**
470  * gst_controller_remove_properties:
471  * @self: the controller object from which some properties should be removed
472  * @...: %NULL terminated list of property names that should be removed
473  *
474  * Removes the given object properties from the controller
475  *
476  * Returns: %FALSE if one of the given property isn't handled by the controller, %TRUE otherwise
477  */
478 gboolean
479 gst_controller_remove_properties (GstController * self, ...)
480 {
481   gboolean res;
482   va_list var_args;
483
484   g_return_val_if_fail (GST_IS_CONTROLLER (self), FALSE);
485
486   va_start (var_args, self);
487   res = gst_controller_remove_properties_valist (self, var_args);
488   va_end (var_args);
489
490   return res;
491 }
492
493 /**
494  * gst_controller_is_active:
495  * @self: the #GstController which should be disabled
496  *
497  * Check if the controller is active. It is active if it has at least one
498  * controlled property that is not disabled.
499  *
500  * Returns: %TRUE if the controller is active
501  */
502 gboolean
503 gst_controller_is_active (GstController * self)
504 {
505   gboolean active = FALSE;
506   GList *node;
507   GstControlledProperty *prop;
508
509   g_return_if_fail (GST_IS_CONTROLLER (self));
510
511   g_mutex_lock (self->lock);
512   for (node = self->properties; node; node = node->next) {
513     prop = node->data;
514     active |= !prop->disabled;
515   }
516   g_mutex_unlock (self->lock);
517
518   return active;
519 }
520
521 /**
522  * gst_controller_set_property_disabled:
523  * @self: the #GstController which should be disabled
524  * @property_name: property to disable
525  * @disabled: boolean that specifies whether to disable the controller
526  * or not.
527  *
528  * This function is used to disable the #GstController on a property for
529  * some time, i.e. gst_controller_sync_values() will do nothing for the
530  * property.
531  */
532 void
533 gst_controller_set_property_disabled (GstController * self,
534     const gchar * property_name, gboolean disabled)
535 {
536   GstControlledProperty *prop;
537
538   g_return_if_fail (GST_IS_CONTROLLER (self));
539   g_return_if_fail (property_name);
540
541   g_mutex_lock (self->lock);
542   if ((prop = gst_controller_find_controlled_property (self, property_name))) {
543     prop->disabled = disabled;
544   }
545   g_mutex_unlock (self->lock);
546 }
547
548 /**
549  * gst_controller_set_disabled:
550  * @self: the #GstController which should be disabled
551  * @disabled: boolean that specifies whether to disable the controller
552  * or not.
553  *
554  * This function is used to disable all properties of the #GstController
555  * for some time, i.e. gst_controller_sync_values() will do nothing.
556  */
557
558 void
559 gst_controller_set_disabled (GstController * self, gboolean disabled)
560 {
561   GList *node;
562   GstControlledProperty *prop;
563
564   g_return_if_fail (GST_IS_CONTROLLER (self));
565
566   g_mutex_lock (self->lock);
567   for (node = self->properties; node; node = node->next) {
568     prop = node->data;
569     prop->disabled = disabled;
570   }
571   g_mutex_unlock (self->lock);
572 }
573
574 /**
575  * gst_controller_set_control_source:
576  * @self: the controller object
577  * @property_name: name of the property for which the #GstControlSource should be set
578  * @csource: the #GstControlSource that should be used for the property
579  *
580  * Sets the #GstControlSource for @property_name. If there already was a #GstControlSource
581  * for this property it will be unreferenced.
582  *
583  * Returns: %FALSE if the given property isn't handled by the controller or the new #GstControlSource
584  * couldn't be bound to the property, %TRUE if everything worked as expected.
585  */
586 gboolean
587 gst_controller_set_control_source (GstController * self,
588     const gchar * property_name, GstControlSource * csource)
589 {
590   GstControlledProperty *prop;
591   gboolean ret = FALSE;
592
593   g_mutex_lock (self->lock);
594   if ((prop = gst_controller_find_controlled_property (self, property_name))) {
595     GstControlSource *old = prop->csource;
596
597     if (csource && (ret = gst_control_source_bind (csource, prop->pspec))) {
598       g_object_ref (csource);
599       prop->csource = csource;
600     } else if (!csource) {
601       ret = TRUE;
602       prop->csource = NULL;
603     }
604
605     if (ret && old)
606       g_object_unref (old);
607   }
608   g_mutex_unlock (self->lock);
609
610   return ret;
611 }
612
613 /**
614  * gst_controller_get_control_source:
615  * @self: the controller object
616  * @property_name: name of the property for which the #GstControlSource should be get
617  *
618  * Gets the corresponding #GstControlSource for the property. This should be unreferenced
619  * again after use.
620  *
621  * Returns: (transfer full): the #GstControlSource for @property_name or NULL if
622  * the property is not controlled by this controller or no #GstControlSource was
623  * assigned yet.
624  */
625 GstControlSource *
626 gst_controller_get_control_source (GstController * self,
627     const gchar * property_name)
628 {
629   GstControlledProperty *prop;
630   GstControlSource *ret = NULL;
631
632   g_return_val_if_fail (GST_IS_CONTROLLER (self), NULL);
633   g_return_val_if_fail (property_name, NULL);
634
635   g_mutex_lock (self->lock);
636   if ((prop = gst_controller_find_controlled_property (self, property_name))) {
637     ret = prop->csource;
638   }
639   g_mutex_unlock (self->lock);
640
641   if (ret)
642     g_object_ref (ret);
643
644   return ret;
645 }
646
647 /**
648  * gst_controller_get:
649  * @self: the controller object which handles the properties
650  * @property_name: the name of the property to get
651  * @timestamp: the time the control-change should be read from
652  *
653  * Gets the value for the given controller-handled property at the requested
654  * time.
655  *
656  * Returns: the GValue of the property at the given time, or %NULL if the
657  * property isn't handled by the controller
658  */
659 GValue *
660 gst_controller_get (GstController * self, const gchar * property_name,
661     GstClockTime timestamp)
662 {
663   GstControlledProperty *prop;
664   GValue *val = NULL;
665
666   g_return_val_if_fail (GST_IS_CONTROLLER (self), NULL);
667   g_return_val_if_fail (property_name, NULL);
668   g_return_val_if_fail (GST_CLOCK_TIME_IS_VALID (timestamp), NULL);
669
670   g_mutex_lock (self->lock);
671   if ((prop = gst_controller_find_controlled_property (self, property_name))) {
672     val = g_new0 (GValue, 1);
673     g_value_init (val, G_PARAM_SPEC_VALUE_TYPE (prop->pspec));
674     if (prop->csource) {
675       gboolean res;
676
677       /* get current value via control source */
678       res = gst_control_source_get_value (prop->csource, timestamp, val);
679       if (!res) {
680         g_free (val);
681         val = NULL;
682       }
683     } else {
684       g_object_get_property ((GObject *) self->object, prop->name, val);
685     }
686   }
687   g_mutex_unlock (self->lock);
688
689   return val;
690 }
691
692 /**
693  * gst_controller_suggest_next_sync:
694  * @self: the controller that handles the values
695  *
696  * Returns a suggestion for timestamps where buffers should be split
697  * to get best controller results.
698  *
699  * Returns: Returns the suggested timestamp or %GST_CLOCK_TIME_NONE
700  * if no control-rate was set.
701  */
702 GstClockTime
703 gst_controller_suggest_next_sync (GstController * self)
704 {
705   GstClockTime ret;
706
707   g_return_val_if_fail (GST_IS_CONTROLLER (self), GST_CLOCK_TIME_NONE);
708   g_return_val_if_fail (self->priv->control_rate != GST_CLOCK_TIME_NONE,
709       GST_CLOCK_TIME_NONE);
710
711   g_mutex_lock (self->lock);
712
713   /* TODO: Implement more logic, depending on interpolation mode
714    * and control points
715    * FIXME: we need playback direction
716    */
717   ret = self->priv->last_sync + self->priv->control_rate;
718
719   g_mutex_unlock (self->lock);
720
721   return ret;
722 }
723
724 /**
725  * gst_controller_sync_values:
726  * @self: the controller that handles the values
727  * @timestamp: the time that should be processed
728  *
729  * Sets the properties of the element, according to the controller that (maybe)
730  * handles them and for the given timestamp.
731  *
732  * If this function fails, it is most likely the application developers fault.
733  * Most probably the control sources are not setup correctly.
734  *
735  * Returns: %TRUE if the controller values could be applied to the object
736  * properties, %FALSE otherwise
737  */
738 gboolean
739 gst_controller_sync_values (GstController * self, GstClockTime timestamp)
740 {
741   GstControlledProperty *prop;
742   GList *node;
743   gboolean ret = TRUE, val_ret;
744   GValue value = { 0, };
745
746   g_return_val_if_fail (GST_IS_CONTROLLER (self), FALSE);
747   g_return_val_if_fail (GST_CLOCK_TIME_IS_VALID (timestamp), FALSE);
748
749   GST_LOG ("sync_values");
750
751   g_mutex_lock (self->lock);
752   g_object_freeze_notify ((GObject *) self->object);
753   /* go over the controlled properties of the controller */
754   for (node = self->properties; node; node = g_list_next (node)) {
755     prop = node->data;
756
757     if (!prop->csource || prop->disabled)
758       continue;
759
760     GST_LOG ("property '%s' at ts=%" G_GUINT64_FORMAT, prop->name, timestamp);
761
762     /* we can make this faster
763      * http://bugzilla.gnome.org/show_bug.cgi?id=536939
764      */
765     g_value_init (&value, G_PARAM_SPEC_VALUE_TYPE (prop->pspec));
766     val_ret = gst_control_source_get_value (prop->csource, timestamp, &value);
767     if (G_LIKELY (val_ret)) {
768       /* always set the value for first time, but then only if it changed
769        * this should limit g_object_notify invocations.
770        * FIXME: can we detect negative playback rates?
771        */
772       if ((timestamp < self->priv->last_sync) ||
773           gst_value_compare (&value, &prop->last_value) != GST_VALUE_EQUAL) {
774         g_object_set_property ((GObject *) self->object, prop->name, &value);
775         g_value_copy (&value, &prop->last_value);
776       }
777     } else {
778       GST_DEBUG ("no control value for param %s", prop->name);
779     }
780     g_value_unset (&value);
781     ret &= val_ret;
782   }
783   self->priv->last_sync = timestamp;
784   g_object_thaw_notify ((GObject *) self->object);
785
786   g_mutex_unlock (self->lock);
787
788   return ret;
789 }
790
791 /**
792  * gst_controller_get_value_arrays:
793  * @self: the controller that handles the values
794  * @timestamp: the time that should be processed
795  * @value_arrays: list to return the control-values in
796  *
797  * Function to be able to get an array of values for one or more given element
798  * properties.
799  *
800  * All fields of the %GstValueArray in the list must be filled correctly.
801  * Especially the GstValueArray->values arrays must be big enough to keep
802  * the requested amount of values.
803  *
804  * The types of the values in the array are the same as the property's type.
805  *
806  * <note><para>This doesn't modify the controlled GObject properties!</para></note>
807  *
808  * Returns: %TRUE if the given array(s) could be filled, %FALSE otherwise
809  */
810 gboolean
811 gst_controller_get_value_arrays (GstController * self,
812     GstClockTime timestamp, GSList * value_arrays)
813 {
814   gboolean res = TRUE;
815   GSList *node;
816
817   g_return_val_if_fail (GST_IS_CONTROLLER (self), FALSE);
818   g_return_val_if_fail (GST_CLOCK_TIME_IS_VALID (timestamp), FALSE);
819   g_return_val_if_fail (value_arrays, FALSE);
820
821   for (node = value_arrays; (res && node); node = g_slist_next (node)) {
822     res = gst_controller_get_value_array (self, timestamp, node->data);
823   }
824
825   return (res);
826 }
827
828 /**
829  * gst_controller_get_value_array:
830  * @self: the controller that handles the values
831  * @timestamp: the time that should be processed
832  * @value_array: array to put control-values in
833  *
834  * Function to be able to get an array of values for one element property.
835  *
836  * All fields of @value_array must be filled correctly. Especially the
837  * @value_array->values array must be big enough to keep the requested amount
838  * of values (as indicated by the nbsamples field).
839  *
840  * The type of the values in the array is the same as the property's type.
841  *
842  * <note><para>This doesn't modify the controlled GObject property!</para></note>
843  *
844  * Returns: %TRUE if the given array could be filled, %FALSE otherwise
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   g_return_val_if_fail (value_array->values, FALSE);
858
859   g_mutex_lock (self->lock);
860
861   if ((prop =
862           gst_controller_find_controlled_property (self,
863               value_array->property_name))) {
864     /* get current value_array via control source */
865
866     if (!prop->csource)
867       goto out;
868
869     res =
870         gst_control_source_get_value_array (prop->csource, timestamp,
871         value_array);
872   }
873
874 out:
875   g_mutex_unlock (self->lock);
876   return res;
877 }
878
879 /* gobject handling */
880
881 static void
882 _gst_controller_get_property (GObject * object, guint property_id,
883     GValue * value, GParamSpec * pspec)
884 {
885   GstController *self = GST_CONTROLLER (object);
886
887   switch (property_id) {
888     case PROP_CONTROL_RATE:{
889       /* FIXME: don't change if element is playing, controller works for GObject
890          so this wont work
891
892          GstState c_state, p_state;
893          GstStateChangeReturn ret;
894
895          ret = gst_element_get_state (self->object, &c_state, &p_state, 0);
896          if ((ret == GST_STATE_CHANGE_SUCCESS &&
897          (c_state == GST_STATE_NULL || c_state == GST_STATE_READY)) ||
898          (ret == GST_STATE_CHANGE_ASYNC &&
899          (p_state == GST_STATE_NULL || p_state == GST_STATE_READY))) {
900        */
901       g_value_set_uint64 (value, self->priv->control_rate);
902       /*
903          }
904          else {
905          GST_WARNING ("Changing the control rate is only allowed if the elemnt"
906          " is in NULL or READY");
907          }
908        */
909     }
910       break;
911     default:{
912       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
913     }
914       break;
915   }
916 }
917
918 /* sets the given properties for this object */
919 static void
920 _gst_controller_set_property (GObject * object, guint property_id,
921     const GValue * value, GParamSpec * pspec)
922 {
923   GstController *self = GST_CONTROLLER (object);
924
925   switch (property_id) {
926     case PROP_CONTROL_RATE:{
927       self->priv->control_rate = g_value_get_uint64 (value);
928     }
929       break;
930     default:{
931       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
932     }
933       break;
934   }
935 }
936
937 static void
938 _gst_controller_dispose (GObject * object)
939 {
940   GstController *self = GST_CONTROLLER (object);
941
942   if (self->object != NULL) {
943     g_mutex_lock (self->lock);
944     /* free list of properties */
945     if (self->properties) {
946       GList *node;
947
948       for (node = self->properties; node; node = g_list_next (node)) {
949         GstControlledProperty *prop = node->data;
950
951         gst_controlled_property_free (prop);
952       }
953       g_list_free (self->properties);
954       self->properties = NULL;
955     }
956
957     g_object_unref (self->object);
958     self->object = NULL;
959     g_mutex_unlock (self->lock);
960   }
961
962   if (G_OBJECT_CLASS (parent_class)->dispose)
963     (G_OBJECT_CLASS (parent_class)->dispose) (object);
964 }
965
966 static void
967 _gst_controller_finalize (GObject * object)
968 {
969   GstController *self = GST_CONTROLLER (object);
970
971   g_mutex_free (self->lock);
972
973   if (G_OBJECT_CLASS (parent_class)->finalize)
974     (G_OBJECT_CLASS (parent_class)->finalize) (object);
975 }
976
977 static void
978 _gst_controller_init (GTypeInstance * instance, gpointer g_class)
979 {
980   GstController *self = GST_CONTROLLER (instance);
981
982   self->lock = g_mutex_new ();
983   self->priv =
984       G_TYPE_INSTANCE_GET_PRIVATE (self, GST_TYPE_CONTROLLER,
985       GstControllerPrivate);
986   self->priv->last_sync = GST_CLOCK_TIME_NONE;
987   self->priv->control_rate = 100 * GST_MSECOND;
988 }
989
990 static void
991 _gst_controller_class_init (GstControllerClass * klass)
992 {
993   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
994
995   parent_class = g_type_class_peek_parent (klass);
996   g_type_class_add_private (klass, sizeof (GstControllerPrivate));
997
998   gobject_class->set_property = _gst_controller_set_property;
999   gobject_class->get_property = _gst_controller_get_property;
1000   gobject_class->dispose = _gst_controller_dispose;
1001   gobject_class->finalize = _gst_controller_finalize;
1002
1003   /* register properties */
1004   g_object_class_install_property (gobject_class, PROP_CONTROL_RATE,
1005       g_param_spec_uint64 ("control-rate",
1006           "control rate",
1007           "Controlled properties will be updated at least every control-rate nanoseconds",
1008           1, G_MAXUINT, 100 * GST_MSECOND,
1009           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1010
1011   GST_DEBUG_CATEGORY_INIT (GST_CAT_DEFAULT, "gstcontroller", 0,
1012       "dynamic parameter control for gstreamer elements");
1013 }
1014
1015 GType
1016 gst_controller_get_type (void)
1017 {
1018   static volatile gsize type = 0;
1019
1020   if (g_once_init_enter (&type)) {
1021     GType _type;
1022     static const GTypeInfo info = {
1023       sizeof (GstControllerClass),
1024       NULL,                     /* base_init */
1025       NULL,                     /* base_finalize */
1026       (GClassInitFunc) _gst_controller_class_init,      /* class_init */
1027       NULL,                     /* class_finalize */
1028       NULL,                     /* class_data */
1029       sizeof (GstController),
1030       0,                        /* n_preallocs */
1031       (GInstanceInitFunc) _gst_controller_init, /* instance_init */
1032       NULL                      /* value_table */
1033     };
1034     _type = g_type_register_static (G_TYPE_OBJECT, "GstController", &info, 0);
1035     g_once_init_leave (&type, _type);
1036   }
1037   return type;
1038 }