4 * New dynamic properties
8 /* What needs to be done in plugins?
9 Very little - it is just two steps to make a plugin controllable!
11 1) Just mark gobject-properties that make sense to be controlled,
12 by GST_PARAM_CONTROLLABLE for a start.
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.
20 /* What needs to be done in applications?
22 1) First put some properties under control, by calling
23 controller=g_object_control_properties(object, "prop1", "prop2",...);
25 2) Set how the controller will smooth inbetween values.
26 gst_controller_set_interpolation_mode(controller,"prop1",mode);
29 gst_controller_set(controller,"prop1",0*GST_SECOND,value1);
30 gst_controller_set(controller,"prop1",1*GST_SECOND,value2);
32 4) Start your pipeline ;-)
34 5) Live control params from the GUI
35 g_object_set_live_value(object, "prop1", timestamp, value);
39 #include "gst-controller.h"
41 #define GST_CAT_DEFAULT gst_controller_debug
42 GST_DEBUG_CATEGORY_EXTERN (GST_CAT_DEFAULT);
44 static GObjectClass *parent_class = NULL;
45 GQuark controller_key;
48 /* imports from gst-interpolation.c */
51 *gst_controlled_property_find_timed_value_node (GstControlledProperty *
52 prop, GstClockTime timestamp);
53 extern GstInterpolateMethod *interpolation_methods[];
58 on_object_controlled_property_changed (const GObject * object, GParamSpec * arg,
61 GstControlledProperty *prop = GST_CONTROLLED_PROPERTY (user_data);
64 GST_INFO ("notify for '%s'", prop->name);
66 ctrl = g_object_get_qdata (G_OBJECT (object), controller_key);
67 g_return_if_fail (ctrl);
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);
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);
82 //GST_DEBUG ("-> is control change");
89 * gst_timed_value_compare:
90 * @p1: a pointer to a #GstTimedValue
91 * @p2: a pointer to a #GstTimedValue
93 * Compare function for g_list operations that operates on two #GstTimedValue
97 gst_timed_value_compare (gconstpointer p1, gconstpointer p2)
99 GstClockTime ct1 = ((GstTimedValue *) p1)->timestamp;
100 GstClockTime ct2 = ((GstTimedValue *) p2)->timestamp;
102 return ((ct1 < ct2) ? -1 : ((ct1 == ct2) ? 0 : 1));
103 /* this does not produce an gint :(
104 return ((ct1 - ct2));
109 * gst_timed_value_find:
110 * @p1: a pointer to a #GstTimedValue
111 * @p2: a pointer to a #GstClockTime
113 * Compare function for g_list operations that operates on a #GstTimedValue and
117 gst_timed_value_find (gconstpointer p1, gconstpointer p2)
119 GstClockTime ct1 = ((GstTimedValue *) p1)->timestamp;
120 GstClockTime ct2 = *(GstClockTime *) p2;
122 return ((ct1 < ct2) ? -1 : ((ct1 == ct2) ? 0 : 1));
123 /* this does not produce an gint :(
124 return ((ct1 - ct2));
129 * gst_controlled_property_set_interpolation_mode:
130 * @self: the controlled property object to change
131 * @mode: the new interpolation mode
133 * Sets the given Interpolation mode for the controlled property and activates
134 * the respective interpolation hooks.
137 gst_controlled_property_set_interpolation_mode (GstControlledProperty * self,
138 GstInterpolateMode mode)
140 self->interpolation = mode;
141 if (mode != GST_INTERPOLATE_USER) {
142 switch (self->type) {
145 self->get = interpolation_methods[mode]->get_int;
146 self->get_value_array =
147 interpolation_methods[mode]->get_int_value_array;
151 self->get = interpolation_methods[mode]->get_long;
152 self->get_value_array =
153 interpolation_methods[mode]->get_long_value_array;
156 self->get = interpolation_methods[mode]->get_float;
157 self->get_value_array =
158 interpolation_methods[mode]->get_float_value_array;
161 self->get = interpolation_methods[mode]->get_double;
162 self->get_value_array =
163 interpolation_methods[mode]->get_double_value_array;
167 self->get_value_array = NULL;
168 GST_WARNING ("incomplete implementation for type '%d'", self->type);
171 /* TODO shouldn't this also get a GstInterpolateMethod *user_method
172 for the case mode==GST_INTERPOLATE_USER
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
183 * Private method which initializes the fields of a new controlled property
186 * Returns: a freshly allocated structure or %NULL
188 static GstControlledProperty *
189 gst_controlled_property_new (GObject * object, const gchar * name)
191 GstControlledProperty *prop = NULL;
194 GST_INFO ("trying to put property '%s' under control", name);
196 // check if the object has a property of that name
198 g_object_class_find_property (G_OBJECT_GET_CLASS (object), name))) {
199 GST_DEBUG (" psec->flags : 0x%08x", pspec->flags);
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:
216 if ((prop = g_new0 (GstControlledProperty, 1))) {
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) {
230 GParamSpecInt *tpspec = G_PARAM_SPEC_INT (pspec);
232 g_value_set_int (&prop->default_value, tpspec->default_value);
236 GParamSpecUInt *tpspec = G_PARAM_SPEC_UINT (pspec);
238 g_value_set_uint (&prop->default_value, tpspec->default_value);
242 GParamSpecLong *tpspec = G_PARAM_SPEC_LONG (pspec);
244 g_value_set_long (&prop->default_value, tpspec->default_value);
248 GParamSpecULong *tpspec = G_PARAM_SPEC_ULONG (pspec);
250 g_value_set_ulong (&prop->default_value, tpspec->default_value);
254 GParamSpecFloat *tpspec = G_PARAM_SPEC_FLOAT (pspec);
256 g_value_set_float (&prop->default_value, tpspec->default_value);
259 GParamSpecDouble *tpspec = G_PARAM_SPEC_DOUBLE (pspec);
261 g_value_set_double (&prop->default_value, tpspec->default_value);
265 GST_WARNING ("incomplete implementation for paramspec type '%s'",
266 G_PARAM_SPEC_TYPE_NAME (pspec));
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
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);
282 GST_WARNING ("class '%s' has no property '%s'", G_OBJECT_TYPE_NAME (object),
290 * gst_controlled_property_free:
291 * @prop: the object to free
293 * Private method which frees all data allocated by a #GstControlledProperty
297 gst_controlled_property_free (GstControlledProperty * prop)
301 g_signal_handler_disconnect (prop->object, prop->notify_handler_id);
302 for (node = prop->values; node; node = g_list_next (node)) {
305 g_list_free (prop->values);
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
314 * Searches the list of properties under control.
316 * Returns: a #GstControlledProperty object of %NULL if the property is not
319 static GstControlledProperty *
320 gst_controller_find_controlled_property (GstController * self,
323 GstControlledProperty *prop;
326 for (node = self->properties; node; node = g_list_next (node)) {
328 if (!strcmp (prop->name, name)) {
332 GST_WARNING ("controller does not manage property '%s'", name);
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
344 * Creates a new GstController for the given object's properties
346 * Returns: the new controller.
349 gst_controller_new_valist (GObject * object, va_list var_args)
352 GstControlledProperty *prop;
355 g_return_val_if_fail (G_IS_OBJECT (object), NULL);
357 GST_INFO ("setting up a new controller");
359 /* TODO should this method check if the given object implements GstParent and
360 if so instantiate a GstParentController ?
362 BilboEd: This is too specific to be put here, don't we want
363 GstController to be as generic as possible ?
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.
370 self = g_object_get_qdata (object, controller_key);
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);
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);
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
393 * Creates a new GstController for the given object's properties
395 * Returns: the new controller.
398 gst_controller_new (GObject * object, ...)
403 g_return_val_if_fail (G_IS_OBJECT (object), NULL);
405 va_start (var_args, object);
406 self = gst_controller_new_valist (object, var_args);
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
417 * Removes the given object properties from the controller
419 * Returns: %FALSE if one of the given property isn't handled by the controller, %TRUE otherwise
422 gst_controller_remove_properties_valist (GstController * self, va_list var_args)
425 GstControlledProperty *prop;
428 g_return_val_if_fail (GST_IS_CONTROLLER (self), FALSE);
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);
439 g_mutex_unlock (self->lock);
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
450 * Removes the given object properties from the controller
452 * Returns: %FALSE if one of the given property isn't handled by the controller, %TRUE otherwise
455 gst_controller_remove_properties (GstController * self, ...)
460 g_return_val_if_fail (GST_IS_CONTROLLER (self), FALSE);
462 va_start (var_args, self);
463 res = gst_controller_remove_properties_valist (self, var_args);
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
476 * Set the value of given controller-handled property at a certain time.
478 * Returns: FALSE if the values couldn't be set (ex : properties not handled by controller), TRUE otherwise
481 gst_controller_set (GstController * self, gchar * property_name,
482 GstClockTime timestamp, GValue * value)
484 gboolean res = FALSE;
485 GstControlledProperty *prop;
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);
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) {
498 // check if a timed_value for the timestamp already exists
499 if ((node = g_list_find_custom (prop->values, ×tamp,
500 gst_timed_value_find))) {
502 memcpy (&tv->value, value, sizeof (GValue));
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
510 g_list_insert_sorted (prop->values, tv, gst_timed_value_compare);
514 GST_WARNING ("incompatible value type for property '%s'", prop->name);
517 g_mutex_unlock (self->lock);
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
528 * Sets multiple timed values at once.
530 * Returns: %FALSE if the values couldn't be set (ex : properties not handled by controller), %TRUE otherwise
534 gst_controller_set_from_list (GstController * self, gchar * property_name,
535 GSList * timedvalues)
537 gboolean res = FALSE;
538 GstControlledProperty *prop;
542 g_return_val_if_fail (GST_IS_CONTROLLER (self), FALSE);
543 g_return_val_if_fail (property_name, FALSE);
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)) {
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? */
553 g_list_insert_sorted (prop->values, tv, gst_timed_value_compare);
556 GST_WARNING ("incompatible value type for property '%s'", prop->name);
560 g_mutex_unlock (self->lock);
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
571 * Used to remove the value of given controller-handled property at a certain
574 * Returns: %FALSE if the values couldn't be unset (ex : properties not handled by controller), %TRUE otherwise
577 gst_controller_unset (GstController * self, gchar * property_name,
578 GstClockTime timestamp)
580 gboolean res = FALSE;
581 GstControlledProperty *prop;
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);
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);
592 g_mutex_unlock (self->lock);
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
603 * Gets the value for the given controller-handled property at the requested
606 * Returns: the GValue of the property at the given time, or %NULL if the property isn't handled by the controller
610 gst_controller_get (GstController * self, gchar * property_name,
611 GstClockTime timestamp)
613 GstControlledProperty *prop;
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);
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);
625 g_mutex_unlock (self->lock);
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
635 * Returns a read-only copy of the list of GstTimedValue for the given property.
636 * Free the list after done with it.
638 * Returns: a copy of the list, or %NULL if the property isn't handled by the controller
641 gst_controller_get_all (GstController * self, gchar * property_name)
644 GstControlledProperty *prop;
646 g_return_val_if_fail (GST_IS_CONTROLLER (self), NULL);
647 g_return_val_if_fail (property_name, NULL);
649 g_mutex_lock (self->lock);
650 if ((prop = gst_controller_find_controlled_property (self, property_name))) {
651 res = g_list_copy (prop->values);
653 g_mutex_unlock (self->lock);
659 * gst_controller_sink_values:
660 * @self: the controller that handles the values
661 * @timestamp: the time that should be processed
663 * Sets the properties of the element, according to the controller that (maybe)
664 * handles them and for the given timestamp.
666 * Returns: %TRUE if the controller values could be applied to the object
667 * properties, %FALSE otherwise
670 gst_controller_sink_values (GstController * self, GstClockTime timestamp)
672 GstControlledProperty *prop;
677 g_return_val_if_fail (GST_IS_CONTROLLER (self), FALSE);
678 g_return_val_if_fail (GST_CLOCK_TIME_IS_VALID (timestamp), FALSE);
680 GST_INFO ("sink_values");
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)) {
686 GST_DEBUG (" property '%s' at ts=%" G_GUINT64_FORMAT, prop->name,
690 if (G_IS_VALUE (&prop->live_value.value)) {
692 gst_controlled_property_find_timed_value_node (prop, timestamp);
694 GST_DEBUG (" no control changes in the queue");
697 GstTimedValue *tv = lnode->data;
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) {
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);
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...
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
731 * Function to be able to get an array of values for one or more given element
734 * If the GstValueArray->values array in list nodes is NULL, it will be created
736 * The type of the values in the array are the same as the property's type.
738 * Returns: %TRUE if the given array(s) could be filled, %FALSE otherwise
741 gst_controller_get_value_arrays (GstController * self,
742 GstClockTime timestamp, GSList * value_arrays)
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);
751 for (node = value_arrays; (res && node); node = g_slist_next (node)) {
752 res = gst_controller_get_value_array (self, timestamp, node->data);
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
764 * Function to be able to get an array of values for one element properties
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.
769 * Returns: %TRUE if the given array(s) could be filled, %FALSE otherwise
772 gst_controller_get_value_array (GstController * self, GstClockTime timestamp,
773 GstValueArray * value_array)
775 gboolean res = FALSE;
776 GstControlledProperty *prop;
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);
783 /* TODO and if GstValueArray->values is not NULL, the caller is resposible that
784 is is big enough for nbsamples values, right?
787 g_mutex_lock (self->lock);
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);
796 //get current value_array via interpolator
797 res = prop->get_value_array (prop, timestamp, value_array);
799 g_mutex_unlock (self->lock);
804 * gst_controller_set_interpolation_mode:
807 * @mode: interpolation mode
809 * Sets the given interpolation mode on the given property.
811 * Returns: %TRUE if the property is handled by the controller, %FALSE otherwise
814 gst_controller_set_interpolation_mode (GstController * self,
815 gchar * property_name, GstInterpolateMode mode)
817 gboolean res = FALSE;
818 GstControlledProperty *prop;
820 g_return_val_if_fail (GST_IS_CONTROLLER (self), FALSE);
821 g_return_val_if_fail (property_name, FALSE);
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
828 gst_controlled_property_set_interpolation_mode (prop, mode);
831 g_mutex_unlock (self->lock);
838 gst_controller_set_live_value(GstController * self, gchar *property_name,
839 GstClockTime timestamp, GValue *value)
841 GstControlledProperty *prop;
843 g_return_if_fail (GST_IS_CONTROLLER (self));
844 g_return_if_fail (property_name);
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;
853 g_mutex_unlock (self->lock);
858 /* gobject handling */
861 _gst_controller_finalize (GObject * object)
863 GstController *self = GST_CONTROLLER (object);
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);
871 g_list_free (self->properties);
872 self->properties = NULL;
874 g_mutex_free (self->lock);
876 if (G_OBJECT_CLASS (parent_class)->finalize)
877 (G_OBJECT_CLASS (parent_class)->finalize) (object);
881 _gst_controller_init (GTypeInstance * instance, gpointer g_class)
883 GstController *self = GST_CONTROLLER (instance);
885 self->lock = g_mutex_new ();
890 _gst_controller_class_init (GstControllerClass * klass)
892 GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
894 parent_class = g_type_class_ref (G_TYPE_OBJECT);
896 gobject_class->finalize = _gst_controller_finalize;
898 controller_key = g_quark_from_string ("gst::controller");
900 // register properties
902 // set defaults for overridable methods
903 /* TODO which of theses do we need ?
909 gst_controller_get_type (void)
911 static GType type = 0;
914 static const GTypeInfo info = {
915 sizeof (GstControllerClass),
917 NULL, // base_finalize
918 (GClassInitFunc) _gst_controller_class_init, // class_init
919 NULL, // class_finalize
921 sizeof (GstController),
923 (GInstanceInitFunc) _gst_controller_init, // instance_init
926 type = g_type_register_static (G_TYPE_OBJECT, "GstController", &info, 0);