3 * Copyright (C) <2005> Stefan Kost <ensonic at users dot sf dot net>
5 * gstcontroller.c: dynamic parameter control subsystem
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.
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.
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.
24 * SECTION:gstcontroller
25 * @short_description: dynamic parameter control subsystem
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.
32 * What needs to be changed in a #GstElement?
33 * Very little - it is just two steps to make a plugin controllable!
36 * mark gobject-properties paramspecs that make sense to be controlled,
37 * by GST_PARAM_CONTROLLABLE.
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.
47 * What needs to be done in applications?
48 * Again its not a lot to change.
51 * first put some properties under control, by calling
52 * controller = g_object_control_properties(object, "prop1", "prop2",...);
55 * set how the controller will smooth inbetween values.
56 * gst_controller_set_interpolation_mode(controller,"prop1",mode);
60 * gst_controller_set (controller, "prop1" ,0 * GST_SECOND, value1);
61 * gst_controller_set (controller, "prop1" ,1 * GST_SECOND, value2);
70 #include "gstcontroller.h"
72 #define GST_CAT_DEFAULT gst_controller_debug
73 GST_DEBUG_CATEGORY_EXTERN (GST_CAT_DEFAULT);
75 static GObjectClass *parent_class = NULL;
76 GQuark __gst_controller_key;
79 /* imports from gst-interpolation.c */
82 * gst_controlled_property_find_timed_value_node (GstControlledProperty *
83 prop, GstClockTime timestamp);
84 extern GstInterpolateMethod *interpolation_methods[];
89 on_object_controlled_property_changed (const GObject * object, GParamSpec * arg,
92 GstControlledProperty *prop = GST_CONTROLLED_PROPERTY (user_data);
95 GST_INFO ("notify for '%s'", prop->name);
97 ctrl = g_object_get_qdata (G_OBJECT (object), __gst_controller_key);
98 g_return_if_fail (ctrl);
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);
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);
116 * gst_timed_value_compare:
117 * @p1: a pointer to a #GstTimedValue
118 * @p2: a pointer to a #GstTimedValue
120 * Compare function for g_list operations that operates on two #GstTimedValue
124 gst_timed_value_compare (gconstpointer p1, gconstpointer p2)
126 GstClockTime ct1 = ((GstTimedValue *) p1)->timestamp;
127 GstClockTime ct2 = ((GstTimedValue *) p2)->timestamp;
129 return ((ct1 < ct2) ? -1 : ((ct1 == ct2) ? 0 : 1));
130 /* this does not produce an gint :(
131 return ((ct1 - ct2));
136 * gst_timed_value_find:
137 * @p1: a pointer to a #GstTimedValue
138 * @p2: a pointer to a #GstClockTime
140 * Compare function for g_list operations that operates on a #GstTimedValue and
144 gst_timed_value_find (gconstpointer p1, gconstpointer p2)
146 GstClockTime ct1 = ((GstTimedValue *) p1)->timestamp;
147 GstClockTime ct2 = *(GstClockTime *) p2;
149 return ((ct1 < ct2) ? -1 : ((ct1 == ct2) ? 0 : 1));
150 /* this does not produce an gint :(
151 return ((ct1 - ct2));
156 * gst_controlled_property_set_interpolation_mode:
157 * @self: the controlled property object to change
158 * @mode: the new interpolation mode
160 * Sets the given Interpolation mode for the controlled property and activates
161 * the respective interpolation hooks.
163 * Returns: %TRUE for success
166 gst_controlled_property_set_interpolation_mode (GstControlledProperty * self,
167 GstInterpolateMode mode)
171 self->interpolation = mode;
172 if (mode != GST_INTERPOLATE_USER) {
173 switch (self->type) {
176 self->get = interpolation_methods[mode]->get_int;
177 self->get_value_array =
178 interpolation_methods[mode]->get_int_value_array;
182 self->get = interpolation_methods[mode]->get_long;
183 self->get_value_array =
184 interpolation_methods[mode]->get_long_value_array;
187 self->get = interpolation_methods[mode]->get_float;
188 self->get_value_array =
189 interpolation_methods[mode]->get_float_value_array;
192 self->get = interpolation_methods[mode]->get_double;
193 self->get_value_array =
194 interpolation_methods[mode]->get_double_value_array;
197 self->get = interpolation_methods[mode]->get_boolean;
198 self->get_value_array =
199 interpolation_methods[mode]->get_boolean_value_array;
204 self->get_value_array = NULL;
206 if (!self->get) { /* || !self->get_value_array) */
207 GST_WARNING ("incomplete implementation for type '%d'", self->type);
211 /* TODO shouldn't this also get a GstInterpolateMethod *user_method
212 for the case mode==GST_INTERPOLATE_USER
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
224 * Private method which initializes the fields of a new controlled property
227 * Returns: a freshly allocated structure or %NULL
229 static GstControlledProperty *
230 gst_controlled_property_new (GObject * object, const gchar * name)
232 GstControlledProperty *prop = NULL;
235 GST_INFO ("trying to put property '%s' under control", name);
237 /* check if the object has a property of that name */
239 g_object_class_find_property (G_OBJECT_GET_CLASS (object), name))) {
240 GST_DEBUG (" psec->flags : 0x%08x", pspec->flags);
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);
249 /* TODO do sanity checks
250 we don't control some pspec->value_type:
260 if ((prop = g_new0 (GstControlledProperty, 1))) {
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) {
273 GParamSpecInt *tpspec = G_PARAM_SPEC_INT (pspec);
275 g_value_set_int (&prop->default_value, tpspec->default_value);
279 GParamSpecUInt *tpspec = G_PARAM_SPEC_UINT (pspec);
281 g_value_set_uint (&prop->default_value, tpspec->default_value);
285 GParamSpecLong *tpspec = G_PARAM_SPEC_LONG (pspec);
287 g_value_set_long (&prop->default_value, tpspec->default_value);
291 GParamSpecULong *tpspec = G_PARAM_SPEC_ULONG (pspec);
293 g_value_set_ulong (&prop->default_value, tpspec->default_value);
297 GParamSpecFloat *tpspec = G_PARAM_SPEC_FLOAT (pspec);
299 g_value_set_float (&prop->default_value, tpspec->default_value);
302 GParamSpecDouble *tpspec = G_PARAM_SPEC_DOUBLE (pspec);
304 g_value_set_double (&prop->default_value, tpspec->default_value);
307 case G_TYPE_BOOLEAN:{
308 GParamSpecBoolean *tpspec = G_PARAM_SPEC_BOOLEAN (pspec);
310 g_value_set_boolean (&prop->default_value, tpspec->default_value);
314 GST_WARNING ("incomplete implementation for paramspec type '%s'",
315 G_PARAM_SPEC_TYPE_NAME (pspec));
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
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);
331 GST_WARNING ("class '%s' has no property '%s'", G_OBJECT_TYPE_NAME (object),
339 * gst_controlled_property_free:
340 * @prop: the object to free
342 * Private method which frees all data allocated by a #GstControlledProperty
346 gst_controlled_property_free (GstControlledProperty * prop)
350 for (node = prop->values; node; node = g_list_next (node)) {
353 g_list_free (prop->values);
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
362 * Searches the list of properties under control.
364 * Returns: a #GstControlledProperty object of %NULL if the property is not
367 static GstControlledProperty *
368 gst_controller_find_controlled_property (GstController * self,
371 GstControlledProperty *prop;
374 for (node = self->properties; node; node = g_list_next (node)) {
376 if (!strcmp (prop->name, name)) {
380 GST_DEBUG ("controller does not (yet) manage property '%s'", name);
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
392 * Creates a new GstController for the given object's properties
394 * Returns: the new controller.
398 gst_controller_new_valist (GObject * object, va_list var_args)
401 GstControlledProperty *prop;
404 g_return_val_if_fail (G_IS_OBJECT (object), NULL);
406 GST_INFO ("setting up a new controller");
408 /* TODO should this method check if the given object implements GstParent and
409 if so instantiate a GstParentController ?
411 BilboEd: This is too specific to be put here, don't we want
412 GstController to be as generic as possible ?
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.
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 */
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);
433 GST_INFO ("returning existing controller");
435 self->properties = g_list_prepend (self->properties, prop);
438 GST_WARNING ("trying to control property again");
444 GST_INFO ("controller->ref_count=%d", G_OBJECT (self)->ref_count);
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
453 * Creates a new GstController for the given object's properties
455 * Returns: the new controller.
459 gst_controller_new_list (GObject * object, GList * list)
462 GstControlledProperty *prop;
466 g_return_val_if_fail (G_IS_OBJECT (object), NULL);
468 GST_INFO ("setting up a new controller");
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 */
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);
485 GST_INFO ("returning existing controller");
487 self->properties = g_list_prepend (self->properties, prop);
490 GST_WARNING ("trying to control property again");
495 GST_INFO ("controller->ref_count=%d", G_OBJECT (self)->ref_count);
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
504 * Creates a new GstController for the given object's properties
506 * Returns: the new controller.
510 gst_controller_new (GObject * object, ...)
515 g_return_val_if_fail (G_IS_OBJECT (object), NULL);
517 va_start (var_args, object);
518 self = gst_controller_new_valist (object, var_args);
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
529 * Removes the given object properties from the controller
531 * Returns: %FALSE if one of the given property isn't handled by the controller, %TRUE otherwise
535 gst_controller_remove_properties_valist (GstController * self, va_list var_args)
538 GstControlledProperty *prop;
541 g_return_val_if_fail (GST_IS_CONTROLLER (self), FALSE);
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);
553 g_mutex_unlock (self->lock);
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
564 * Removes the given object properties from the controller
566 * Returns: %FALSE if one of the given property isn't handled by the controller, %TRUE otherwise
570 gst_controller_remove_properties_list (GstController * self, GList * list)
573 GstControlledProperty *prop;
577 g_return_val_if_fail (GST_IS_CONTROLLER (self), FALSE);
579 for (tmp = list; tmp; tmp = g_list_next (tmp)) {
580 name = (gchar *) tmp->data;
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);
591 g_mutex_unlock (self->lock);
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
602 * Removes the given object properties from the controller
604 * Returns: %FALSE if one of the given property isn't handled by the controller, %TRUE otherwise
608 gst_controller_remove_properties (GstController * self, ...)
613 g_return_val_if_fail (GST_IS_CONTROLLER (self), FALSE);
615 va_start (var_args, self);
616 res = gst_controller_remove_properties_valist (self, var_args);
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
629 * Set the value of given controller-handled property at a certain time.
631 * Returns: FALSE if the values couldn't be set (ex : properties not handled by controller), TRUE otherwise
635 gst_controller_set (GstController * self, gchar * property_name,
636 GstClockTime timestamp, GValue * value)
638 gboolean res = FALSE;
639 GstControlledProperty *prop;
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);
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) {
652 /* check if a timed_value for the timestamp already exists */
653 if ((node = g_list_find_custom (prop->values, ×tamp,
654 gst_timed_value_find))) {
656 memcpy (&tv->value, value, sizeof (GValue));
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 */
664 g_list_insert_sorted (prop->values, tv, gst_timed_value_compare);
668 GST_WARNING ("incompatible value type for property '%s'", prop->name);
671 g_mutex_unlock (self->lock);
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
682 * Sets multiple timed values at once.
684 * Returns: %FALSE if the values couldn't be set (ex : properties not handled by controller), %TRUE otherwise
689 gst_controller_set_from_list (GstController * self, gchar * property_name,
690 GSList * timedvalues)
692 gboolean res = FALSE;
693 GstControlledProperty *prop;
697 g_return_val_if_fail (GST_IS_CONTROLLER (self), FALSE);
698 g_return_val_if_fail (property_name, FALSE);
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)) {
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? */
708 g_list_insert_sorted (prop->values, tv, gst_timed_value_compare);
711 GST_WARNING ("incompatible value type for property '%s'", prop->name);
715 g_mutex_unlock (self->lock);
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
726 * Used to remove the value of given controller-handled property at a certain
729 * Returns: %FALSE if the values couldn't be unset (ex : properties not handled by controller), %TRUE otherwise
733 gst_controller_unset (GstController * self, gchar * property_name,
734 GstClockTime timestamp)
736 gboolean res = FALSE;
737 GstControlledProperty *prop;
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);
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);
748 g_mutex_unlock (self->lock);
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
759 * Gets the value for the given controller-handled property at the requested
762 * Returns: the GValue of the property at the given time, or %NULL if the property isn't handled by the controller
766 gst_controller_get (GstController * self, gchar * property_name,
767 GstClockTime timestamp)
769 GstControlledProperty *prop;
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);
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);
781 g_mutex_unlock (self->lock);
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
791 * Returns a read-only copy of the list of GstTimedValue for the given property.
792 * Free the list after done with it.
794 * Returns: a copy of the list, or %NULL if the property isn't handled by the controller
798 gst_controller_get_all (GstController * self, gchar * property_name)
801 GstControlledProperty *prop;
803 g_return_val_if_fail (GST_IS_CONTROLLER (self), NULL);
804 g_return_val_if_fail (property_name, NULL);
806 g_mutex_lock (self->lock);
807 if ((prop = gst_controller_find_controlled_property (self, property_name))) {
808 res = g_list_copy (prop->values);
810 g_mutex_unlock (self->lock);
816 * gst_controller_sync_values:
817 * @self: the controller that handles the values
818 * @timestamp: the time that should be processed
820 * Sets the properties of the element, according to the controller that (maybe)
821 * handles them and for the given timestamp.
823 * Returns: %TRUE if the controller values could be applied to the object
824 * properties, %FALSE otherwise
828 gst_controller_sync_values (GstController * self, GstClockTime timestamp)
830 GstControlledProperty *prop;
835 g_return_val_if_fail (GST_IS_CONTROLLER (self), FALSE);
836 g_return_val_if_fail (GST_CLOCK_TIME_IS_VALID (timestamp), FALSE);
838 GST_INFO ("sync_values");
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)) {
844 GST_DEBUG (" property '%s' at ts=%" G_GUINT64_FORMAT, prop->name,
848 if (G_IS_VALUE (&prop->live_value.value)) {
850 gst_controlled_property_find_timed_value_node (prop, timestamp);
852 GST_DEBUG (" no control changes in the queue");
855 GstTimedValue *tv = lnode->data;
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) {
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);
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...
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
888 * Function to be able to get an array of values for one or more given element
891 * If the GstValueArray->values array in list nodes is NULL, it will be created
893 * The type of the values in the array are the same as the property's type.
895 * Returns: %TRUE if the given array(s) could be filled, %FALSE otherwise
899 gst_controller_get_value_arrays (GstController * self,
900 GstClockTime timestamp, GSList * value_arrays)
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);
909 for (node = value_arrays; (res && node); node = g_slist_next (node)) {
910 res = gst_controller_get_value_array (self, timestamp, node->data);
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
922 * Function to be able to get an array of values for one element properties
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.
927 * Returns: %TRUE if the given array(s) could be filled, %FALSE otherwise
931 gst_controller_get_value_array (GstController * self, GstClockTime timestamp,
932 GstValueArray * value_array)
934 gboolean res = FALSE;
935 GstControlledProperty *prop;
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);
942 /* TODO and if GstValueArray->values is not NULL, the caller is resposible that
943 is is big enough for nbsamples values, right?
946 g_mutex_lock (self->lock);
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);
955 /* get current value_array via interpolator */
956 res = prop->get_value_array (prop, timestamp, value_array);
958 g_mutex_unlock (self->lock);
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
968 * Sets the given interpolation mode on the given property.
970 * Returns: %TRUE if the property is handled by the controller, %FALSE otherwise
974 gst_controller_set_interpolation_mode (GstController * self,
975 gchar * property_name, GstInterpolateMode mode)
977 gboolean res = FALSE;
978 GstControlledProperty *prop;
980 g_return_val_if_fail (GST_IS_CONTROLLER (self), FALSE);
981 g_return_val_if_fail (property_name, FALSE);
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
988 gst_controlled_property_set_interpolation_mode (prop, mode);
991 g_mutex_unlock (self->lock);
998 gst_controller_set_live_value(GstController * self, gchar *property_name,
999 GstClockTime timestamp, GValue *value)
1001 GstControlledProperty *prop;
1003 g_return_if_fail (GST_IS_CONTROLLER (self));
1004 g_return_if_fail (property_name);
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;
1013 g_mutex_unlock (self->lock);
1018 /* gobject handling */
1021 _gst_controller_finalize (GObject * object)
1023 GstController *self = GST_CONTROLLER (object);
1025 GstControlledProperty *prop;
1027 /* free list of properties */
1028 if (self->properties) {
1029 for (node = self->properties; node; node = g_list_next (node)) {
1031 g_signal_handler_disconnect (self->object, prop->notify_handler_id);
1032 gst_controlled_property_free (prop);
1034 g_list_free (self->properties);
1035 self->properties = NULL;
1037 g_mutex_free (self->lock);
1038 /* remove controller from objects qdata list */
1039 g_object_set_qdata (self->object, __gst_controller_key, NULL);
1041 if (G_OBJECT_CLASS (parent_class)->finalize)
1042 (G_OBJECT_CLASS (parent_class)->finalize) (object);
1046 _gst_controller_init (GTypeInstance * instance, gpointer g_class)
1048 GstController *self = GST_CONTROLLER (instance);
1050 self->lock = g_mutex_new ();
1055 _gst_controller_class_init (GstControllerClass * klass)
1057 GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
1059 parent_class = g_type_class_ref (G_TYPE_OBJECT);
1061 gobject_class->finalize = _gst_controller_finalize;
1063 __gst_controller_key = g_quark_from_string ("gst::controller");
1065 /* register properties */
1066 /* register signals */
1067 /* set defaults for overridable methods */
1068 /* TODO which of theses do we need ?
1074 gst_controller_get_type (void)
1076 static GType 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 */
1091 type = g_type_register_static (G_TYPE_OBJECT, "GstController", &info, 0);