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_sink_values(element,timestamp).
42 * This will made the controller to update all gobject properties that are under
43 * control with the current values based on timestamp.
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 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), 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_unset (&prop->live_value.value);
103 g_value_init (&prop->live_value.value, prop->type);
105 g_object_get_property (G_OBJECT (object), prop->name,
106 &prop->live_value.value);
107 prop->live_value.timestamp = prop->last_value.timestamp;
108 g_mutex_unlock (ctrl->lock);
109 GST_DEBUG ("-> is live update : ts=%" G_GUINT64_FORMAT,
110 prop->live_value.timestamp);
113 //GST_DEBUG ("-> is control change");
120 * gst_timed_value_compare:
121 * @p1: a pointer to a #GstTimedValue
122 * @p2: a pointer to a #GstTimedValue
124 * Compare function for g_list operations that operates on two #GstTimedValue
128 gst_timed_value_compare (gconstpointer p1, gconstpointer p2)
130 GstClockTime ct1 = ((GstTimedValue *) p1)->timestamp;
131 GstClockTime ct2 = ((GstTimedValue *) p2)->timestamp;
133 return ((ct1 < ct2) ? -1 : ((ct1 == ct2) ? 0 : 1));
134 /* this does not produce an gint :(
135 return ((ct1 - ct2));
140 * gst_timed_value_find:
141 * @p1: a pointer to a #GstTimedValue
142 * @p2: a pointer to a #GstClockTime
144 * Compare function for g_list operations that operates on a #GstTimedValue and
148 gst_timed_value_find (gconstpointer p1, gconstpointer p2)
150 GstClockTime ct1 = ((GstTimedValue *) p1)->timestamp;
151 GstClockTime ct2 = *(GstClockTime *) p2;
153 return ((ct1 < ct2) ? -1 : ((ct1 == ct2) ? 0 : 1));
154 /* this does not produce an gint :(
155 return ((ct1 - ct2));
160 * gst_controlled_property_set_interpolation_mode:
161 * @self: the controlled property object to change
162 * @mode: the new interpolation mode
164 * Sets the given Interpolation mode for the controlled property and activates
165 * the respective interpolation hooks.
167 * Returns: %TRUE for success
170 gst_controlled_property_set_interpolation_mode (GstControlledProperty * self,
171 GstInterpolateMode mode)
175 self->interpolation = mode;
176 if (mode != GST_INTERPOLATE_USER) {
177 switch (self->type) {
180 self->get = interpolation_methods[mode]->get_int;
181 self->get_value_array =
182 interpolation_methods[mode]->get_int_value_array;
186 self->get = interpolation_methods[mode]->get_long;
187 self->get_value_array =
188 interpolation_methods[mode]->get_long_value_array;
191 self->get = interpolation_methods[mode]->get_float;
192 self->get_value_array =
193 interpolation_methods[mode]->get_float_value_array;
196 self->get = interpolation_methods[mode]->get_double;
197 self->get_value_array =
198 interpolation_methods[mode]->get_double_value_array;
201 self->get = interpolation_methods[mode]->get_boolean;
202 self->get_value_array =
203 interpolation_methods[mode]->get_boolean_value_array;
208 self->get_value_array = NULL;
210 if (!self->get) { /* || !self->get_value_array) */
211 GST_WARNING ("incomplete implementation for type '%d'", self->type);
215 /* TODO shouldn't this also get a GstInterpolateMethod *user_method
216 for the case mode==GST_INTERPOLATE_USER
224 * gst_controlled_property_new:
225 * @object: for which object the controlled property should be set up
226 * @name: the name of the property to be controlled
228 * Private method which initializes the fields of a new controlled property
231 * Returns: a freshly allocated structure or %NULL
233 static GstControlledProperty *
234 gst_controlled_property_new (GObject * object, const gchar * name)
236 GstControlledProperty *prop = NULL;
239 GST_INFO ("trying to put property '%s' under control", name);
241 // check if the object has a property of that name
243 g_object_class_find_property (G_OBJECT_GET_CLASS (object), name))) {
244 GST_DEBUG (" psec->flags : 0x%08x", pspec->flags);
246 // check if this param is witable
247 g_return_val_if_fail ((pspec->flags & G_PARAM_WRITABLE), NULL);
248 // check if property is controlable
249 g_return_val_if_fail ((pspec->flags & GST_PARAM_CONTROLLABLE), NULL);
250 // check if this param is not construct-only
251 g_return_val_if_fail (!(pspec->flags & G_PARAM_CONSTRUCT_ONLY), NULL);
253 /* TODO do sanity checks
254 we don't control some pspec->value_type:
264 if ((prop = g_new0 (GstControlledProperty, 1))) {
267 prop->name = pspec->name; // so we don't use the same mem twice
268 prop->type = G_PARAM_SPEC_VALUE_TYPE (pspec);
269 gst_controlled_property_set_interpolation_mode (prop,
270 GST_INTERPOLATE_NONE);
271 /* prepare our gvalues */
272 g_value_init (&prop->default_value, prop->type);
273 g_value_init (&prop->result_value, prop->type);
274 g_value_init (&prop->last_value.value, prop->type);
275 switch (prop->type) {
277 GParamSpecInt *tpspec = G_PARAM_SPEC_INT (pspec);
279 g_value_set_int (&prop->default_value, tpspec->default_value);
283 GParamSpecUInt *tpspec = G_PARAM_SPEC_UINT (pspec);
285 g_value_set_uint (&prop->default_value, tpspec->default_value);
289 GParamSpecLong *tpspec = G_PARAM_SPEC_LONG (pspec);
291 g_value_set_long (&prop->default_value, tpspec->default_value);
295 GParamSpecULong *tpspec = G_PARAM_SPEC_ULONG (pspec);
297 g_value_set_ulong (&prop->default_value, tpspec->default_value);
301 GParamSpecFloat *tpspec = G_PARAM_SPEC_FLOAT (pspec);
303 g_value_set_float (&prop->default_value, tpspec->default_value);
306 GParamSpecDouble *tpspec = G_PARAM_SPEC_DOUBLE (pspec);
308 g_value_set_double (&prop->default_value, tpspec->default_value);
311 case G_TYPE_BOOLEAN:{
312 GParamSpecBoolean *tpspec = G_PARAM_SPEC_BOOLEAN (pspec);
314 g_value_set_boolean (&prop->default_value, tpspec->default_value);
318 GST_WARNING ("incomplete implementation for paramspec type '%s'",
319 G_PARAM_SPEC_TYPE_NAME (pspec));
321 /* TODO what about adding a timedval with timestamp=0 and value=default
322 * a bit easier for interpolators, example:
323 * first timestamp is at 5
324 * requested value if for timestamp=3
325 * LINEAR and Co. would need to interpolate from default value to value
328 signal_name = g_alloca (8 + 1 + strlen (name));
329 g_sprintf (signal_name, "notify::%s", name);
330 prop->notify_handler_id =
331 g_signal_connect (object, signal_name,
332 G_CALLBACK (on_object_controlled_property_changed), (gpointer) prop);
335 GST_WARNING ("class '%s' has no property '%s'", G_OBJECT_TYPE_NAME (object),
343 * gst_controlled_property_free:
344 * @prop: the object to free
346 * Private method which frees all data allocated by a #GstControlledProperty
350 gst_controlled_property_free (GstControlledProperty * prop)
354 for (node = prop->values; node; node = g_list_next (node)) {
357 g_list_free (prop->values);
362 * gst_controller_find_controlled_property:
363 * @self: the controller object to search for a property in
364 * @name: the gobject property name to look for
366 * Searches the list of properties under control.
368 * Returns: a #GstControlledProperty object of %NULL if the property is not
371 static GstControlledProperty *
372 gst_controller_find_controlled_property (GstController * self,
375 GstControlledProperty *prop;
378 for (node = self->properties; node; node = g_list_next (node)) {
380 if (!strcmp (prop->name, name)) {
384 GST_DEBUG ("controller does not (yet) manage property '%s'", name);
392 * gst_controller_new_valist:
393 * @object: the object of which some properties should be controlled
394 * @var_args: %NULL terminated list of property names that should be controlled
396 * Creates a new GstController for the given object's properties
398 * Returns: the new controller.
402 gst_controller_new_valist (GObject * object, va_list var_args)
405 GstControlledProperty *prop;
408 g_return_val_if_fail (G_IS_OBJECT (object), NULL);
410 GST_INFO ("setting up a new controller");
412 /* TODO should this method check if the given object implements GstParent and
413 if so instantiate a GstParentController ?
415 BilboEd: This is too specific to be put here, don't we want
416 GstController to be as generic as possible ?
418 Ensonic: So we will have gst_parent_controller_new as well and maybe a
419 convinience function that automatically chooses the right one (how to name it)?
420 GstParent will be in core after all.
423 self = g_object_get_qdata (object, controller_key);
424 // create GstControlledProperty for each property
425 while ((name = va_arg (var_args, gchar *))) {
426 // test if this property isn't yet controlled
427 if (!self || !(prop = gst_controller_find_controlled_property (self, name))) {
428 // create GstControlledProperty and add to self->propeties List
429 if ((prop = gst_controlled_property_new (object, name))) {
430 // if we don't have a controller object yet, now is the time to create one
432 self = g_object_new (GST_TYPE_CONTROLLER, NULL);
433 self->lock = g_mutex_new ();
434 self->object = object;
435 // store the controller
436 g_object_set_qdata (object, controller_key, self);
438 // increment ref-count
439 self = g_object_ref (self);
441 self->properties = g_list_prepend (self->properties, prop);
444 GST_WARNING ("trying to control property again");
453 * gst_controller_new:
454 * @object: the object of which some properties should be controlled
455 * @...: %NULL terminated list of property names that should be controlled
457 * Creates a new GstController for the given object's properties
459 * Returns: the new controller.
463 gst_controller_new (GObject * object, ...)
468 g_return_val_if_fail (G_IS_OBJECT (object), NULL);
470 va_start (var_args, object);
471 self = gst_controller_new_valist (object, var_args);
478 * gst_controller_remove_properties_valist:
479 * @self: the controller object from which some properties should be removed
480 * @var_args: %NULL terminated list of property names that should be removed
482 * Removes the given object properties from the controller
484 * Returns: %FALSE if one of the given property isn't handled by the controller, %TRUE otherwise
488 gst_controller_remove_properties_valist (GstController * self, va_list var_args)
491 GstControlledProperty *prop;
494 g_return_val_if_fail (GST_IS_CONTROLLER (self), FALSE);
496 while ((name = va_arg (var_args, gchar *))) {
497 // find the property in the properties list of the controller, remove and free it
498 g_mutex_lock (self->lock);
499 if ((prop = gst_controller_find_controlled_property (self, name))) {
500 self->properties = g_list_remove (self->properties, prop);
501 g_signal_handler_disconnect (self->object, prop->notify_handler_id);
502 gst_controlled_property_free (prop);
506 g_mutex_unlock (self->lock);
513 * gst_controller_remove_properties:
514 * @self: the controller object from which some properties should be removed
515 * @...: %NULL terminated list of property names that should be removed
517 * Removes the given object properties from the controller
519 * Returns: %FALSE if one of the given property isn't handled by the controller, %TRUE otherwise
523 gst_controller_remove_properties (GstController * self, ...)
528 g_return_val_if_fail (GST_IS_CONTROLLER (self), FALSE);
530 va_start (var_args, self);
531 res = gst_controller_remove_properties_valist (self, var_args);
538 * gst_controller_set:
539 * @self: the controller object which handles the properties
540 * @property_name: the name of the property to set
541 * @timestamp: the time the control-change is schedules for
542 * @value: the control-value
544 * Set the value of given controller-handled property at a certain time.
546 * Returns: FALSE if the values couldn't be set (ex : properties not handled by controller), TRUE otherwise
550 gst_controller_set (GstController * self, gchar * property_name,
551 GstClockTime timestamp, GValue * value)
553 gboolean res = FALSE;
554 GstControlledProperty *prop;
556 g_return_val_if_fail (GST_IS_CONTROLLER (self), FALSE);
557 g_return_val_if_fail (property_name, FALSE);
558 g_return_val_if_fail (GST_CLOCK_TIME_IS_VALID (timestamp), FALSE);
559 g_return_val_if_fail (G_IS_VALUE (value), FALSE);
561 g_mutex_lock (self->lock);
562 if ((prop = gst_controller_find_controlled_property (self, property_name))) {
563 if (G_VALUE_TYPE (value) == prop->type) {
567 // check if a timed_value for the timestamp already exists
568 if ((node = g_list_find_custom (prop->values, ×tamp,
569 gst_timed_value_find))) {
571 memcpy (&tv->value, value, sizeof (GValue));
573 // create a new GstTimedValue
574 tv = g_new (GstTimedValue, 1);
575 tv->timestamp = timestamp;
576 memcpy (&tv->value, value, sizeof (GValue));
577 // and sort it into the prop->values list
579 g_list_insert_sorted (prop->values, tv, gst_timed_value_compare);
583 GST_WARNING ("incompatible value type for property '%s'", prop->name);
586 g_mutex_unlock (self->lock);
592 * gst_controller_set_from_list:
593 * @self: the controller object which handles the properties
594 * @property_name: the name of the property to set
595 * @timedvalues: a list with #GstTimedValue items
597 * Sets multiple timed values at once.
599 * Returns: %FALSE if the values couldn't be set (ex : properties not handled by controller), %TRUE otherwise
604 gst_controller_set_from_list (GstController * self, gchar * property_name,
605 GSList * timedvalues)
607 gboolean res = FALSE;
608 GstControlledProperty *prop;
612 g_return_val_if_fail (GST_IS_CONTROLLER (self), FALSE);
613 g_return_val_if_fail (property_name, FALSE);
615 g_mutex_lock (self->lock);
616 if ((prop = gst_controller_find_controlled_property (self, property_name))) {
617 for (node = timedvalues; node; node = g_slist_next (node)) {
619 if (G_VALUE_TYPE (&tv->value) == prop->type) {
620 g_return_val_if_fail (GST_CLOCK_TIME_IS_VALID (tv->timestamp), FALSE);
621 /* TODO copy the timed value or just link in? */
623 g_list_insert_sorted (prop->values, tv, gst_timed_value_compare);
626 GST_WARNING ("incompatible value type for property '%s'", prop->name);
630 g_mutex_unlock (self->lock);
636 * gst_controller_unset:
637 * @self: the controller object which handles the properties
638 * @property_name: the name of the property to unset
639 * @timestamp: the time the control-change should be removed from
641 * Used to remove the value of given controller-handled property at a certain
644 * Returns: %FALSE if the values couldn't be unset (ex : properties not handled by controller), %TRUE otherwise
648 gst_controller_unset (GstController * self, gchar * property_name,
649 GstClockTime timestamp)
651 gboolean res = FALSE;
652 GstControlledProperty *prop;
654 g_return_val_if_fail (GST_IS_CONTROLLER (self), FALSE);
655 g_return_val_if_fail (property_name, FALSE);
656 g_return_val_if_fail (GST_CLOCK_TIME_IS_VALID (timestamp), FALSE);
658 g_mutex_lock (self->lock);
659 if ((prop = gst_controller_find_controlled_property (self, property_name))) {
660 prop->values = g_list_remove (prop->values, prop);
663 g_mutex_unlock (self->lock);
669 * gst_controller_get:
670 * @self: the controller object which handles the properties
671 * @property_name: the name of the property to get
672 * @timestamp: the time the control-change should be read from
674 * Gets the value for the given controller-handled property at the requested
677 * Returns: the GValue of the property at the given time, or %NULL if the property isn't handled by the controller
681 gst_controller_get (GstController * self, gchar * property_name,
682 GstClockTime timestamp)
684 GstControlledProperty *prop;
687 g_return_val_if_fail (GST_IS_CONTROLLER (self), NULL);
688 g_return_val_if_fail (property_name, NULL);
689 g_return_val_if_fail (GST_CLOCK_TIME_IS_VALID (timestamp), NULL);
691 g_mutex_lock (self->lock);
692 if ((prop = gst_controller_find_controlled_property (self, property_name))) {
693 //get current value via interpolator
694 val = prop->get (prop, timestamp);
696 g_mutex_unlock (self->lock);
702 * gst_controller_get_all:
703 * @self: the controller to get the list from
704 * @property_name: the name of the property to get the list for
706 * Returns a read-only copy of the list of GstTimedValue for the given property.
707 * Free the list after done with it.
709 * Returns: a copy of the list, or %NULL if the property isn't handled by the controller
713 gst_controller_get_all (GstController * self, gchar * property_name)
716 GstControlledProperty *prop;
718 g_return_val_if_fail (GST_IS_CONTROLLER (self), NULL);
719 g_return_val_if_fail (property_name, NULL);
721 g_mutex_lock (self->lock);
722 if ((prop = gst_controller_find_controlled_property (self, property_name))) {
723 res = g_list_copy (prop->values);
725 g_mutex_unlock (self->lock);
731 * gst_controller_sink_values:
732 * @self: the controller that handles the values
733 * @timestamp: the time that should be processed
735 * Sets the properties of the element, according to the controller that (maybe)
736 * handles them and for the given timestamp.
738 * Returns: %TRUE if the controller values could be applied to the object
739 * properties, %FALSE otherwise
743 gst_controller_sink_values (GstController * self, GstClockTime timestamp)
745 GstControlledProperty *prop;
750 g_return_val_if_fail (GST_IS_CONTROLLER (self), FALSE);
751 g_return_val_if_fail (GST_CLOCK_TIME_IS_VALID (timestamp), FALSE);
753 GST_INFO ("sink_values");
755 g_mutex_lock (self->lock);
756 // go over the controlled properties of the controller
757 for (node = self->properties; node; node = g_list_next (node)) {
759 GST_DEBUG (" property '%s' at ts=%" G_GUINT64_FORMAT, prop->name,
763 if (G_IS_VALUE (&prop->live_value.value)) {
765 gst_controlled_property_find_timed_value_node (prop, timestamp);
767 GST_DEBUG (" no control changes in the queue");
770 GstTimedValue *tv = lnode->data;
772 //GST_DEBUG ("live.ts %"G_UINT64_FORMAT" <-> now %"G_UINT64_FORMAT, prop->live_value.timestamp, tv->timestamp);
773 if (prop->live_value.timestamp < tv->timestamp) {
774 g_value_unset (&prop->live_value.value);
775 GST_DEBUG (" live value resetted");
776 } else if (prop->live_value.timestamp < timestamp) {
782 //get current value via interpolator
783 value = prop->get (prop, timestamp);
784 prop->last_value.timestamp = timestamp;
785 g_value_copy (value, &prop->last_value.value);
786 g_object_set_property (self->object, prop->name, value);
789 g_mutex_unlock (self->lock);
790 /* TODO what can here go wrong, to return FALSE ?
791 BilboEd : Nothing I guess, as long as all the checks are made when creating the controller,
792 adding/removing controlled properties, etc...
799 * gst_controller_get_value_arrays:
800 * @self: the controller that handles the values
801 * @timestamp: the time that should be processed
802 * @value_arrays: list to return the control-values in
804 * Function to be able to get an array of values for one or more given element
807 * If the GstValueArray->values array in list nodes is NULL, it will be created
809 * The type of the values in the array are the same as the property's type.
811 * Returns: %TRUE if the given array(s) could be filled, %FALSE otherwise
815 gst_controller_get_value_arrays (GstController * self,
816 GstClockTime timestamp, GSList * value_arrays)
821 g_return_val_if_fail (GST_IS_CONTROLLER (self), FALSE);
822 g_return_val_if_fail (GST_CLOCK_TIME_IS_VALID (timestamp), FALSE);
823 g_return_val_if_fail (value_arrays, FALSE);
825 for (node = value_arrays; (res && node); node = g_slist_next (node)) {
826 res = gst_controller_get_value_array (self, timestamp, node->data);
833 * gst_controller_get_value_array:
834 * @self: the controller that handles the values
835 * @timestamp: the time that should be processed
836 * @value_array: array to put control-values in
838 * Function to be able to get an array of values for one element properties
840 * If the GstValueArray->values array is NULL, it will be created by the function.
841 * The type of the values in the array are the same as the property's type.
843 * Returns: %TRUE if the given array(s) could be filled, %FALSE otherwise
847 gst_controller_get_value_array (GstController * self, GstClockTime timestamp,
848 GstValueArray * value_array)
850 gboolean res = FALSE;
851 GstControlledProperty *prop;
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);
858 /* TODO and if GstValueArray->values is not NULL, the caller is resposible that
859 is is big enough for nbsamples values, right?
862 g_mutex_lock (self->lock);
864 gst_controller_find_controlled_property (self,
865 value_array->property_name))) {
866 if (!value_array->values) {
867 /* TODO from where to get the base size
868 value_array->values=g_new(sizeof(???),nbsamples);
871 //get current value_array via interpolator
872 res = prop->get_value_array (prop, timestamp, value_array);
874 g_mutex_unlock (self->lock);
879 * gst_controller_set_interpolation_mode:
880 * @self: the controller object
881 * @property_name: the name of the property for which to change the interpolation
882 * @mode: interpolation mode
884 * Sets the given interpolation mode on the given property.
886 * Returns: %TRUE if the property is handled by the controller, %FALSE otherwise
890 gst_controller_set_interpolation_mode (GstController * self,
891 gchar * property_name, GstInterpolateMode mode)
893 gboolean res = FALSE;
894 GstControlledProperty *prop;
896 g_return_val_if_fail (GST_IS_CONTROLLER (self), FALSE);
897 g_return_val_if_fail (property_name, FALSE);
899 g_mutex_lock (self->lock);
900 if ((prop = gst_controller_find_controlled_property (self, property_name))) {
901 /* TODO shouldn't this also get a GstInterpolateMethod *user_method
902 for the case mode==GST_INTERPOLATE_USER
904 gst_controlled_property_set_interpolation_mode (prop, mode);
907 g_mutex_unlock (self->lock);
914 gst_controller_set_live_value(GstController * self, gchar *property_name,
915 GstClockTime timestamp, GValue *value)
917 GstControlledProperty *prop;
919 g_return_if_fail (GST_IS_CONTROLLER (self));
920 g_return_if_fail (property_name);
922 g_mutex_lock (self->lock);
923 if ((prop = gst_controller_find_controlled_property (self, property_name))) {
924 g_value_unset (&prop->live_value.value);
925 g_value_init (&prop->live_value.value, prop->type);
926 g_value_copy (value, &prop->live_value.value);
927 prop->live_value.timestamp = timestamp;
929 g_mutex_unlock (self->lock);
934 /* gobject handling */
937 _gst_controller_finalize (GObject * object)
939 GstController *self = GST_CONTROLLER (object);
941 GstControlledProperty *prop;
943 /* free list of properties */
944 if (self->properties) {
945 for (node = self->properties; node; node = g_list_next (node)) {
947 g_signal_handler_disconnect (self->object, prop->notify_handler_id);
948 gst_controlled_property_free (prop);
950 g_list_free (self->properties);
951 self->properties = NULL;
953 g_mutex_free (self->lock);
954 /* remove controller from objects qdata list */
955 g_object_set_qdata (self->object, controller_key, NULL);
957 if (G_OBJECT_CLASS (parent_class)->finalize)
958 (G_OBJECT_CLASS (parent_class)->finalize) (object);
962 _gst_controller_init (GTypeInstance * instance, gpointer g_class)
964 GstController *self = GST_CONTROLLER (instance);
966 self->lock = g_mutex_new ();
971 _gst_controller_class_init (GstControllerClass * klass)
973 GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
975 parent_class = g_type_class_ref (G_TYPE_OBJECT);
977 gobject_class->finalize = _gst_controller_finalize;
979 controller_key = g_quark_from_string ("gst::controller");
981 // register properties
983 // set defaults for overridable methods
984 /* TODO which of theses do we need ?
990 gst_controller_get_type (void)
992 static GType type = 0;
995 static const GTypeInfo info = {
996 sizeof (GstControllerClass),
998 NULL, // base_finalize
999 (GClassInitFunc) _gst_controller_class_init, // class_init
1000 NULL, // class_finalize
1002 sizeof (GstController),
1004 (GInstanceInitFunc) _gst_controller_init, // instance_init
1007 type = g_type_register_static (G_TYPE_OBJECT, "GstController", &info, 0);