From a80401b22c6f8693e733767aec3086296ac6ed05 Mon Sep 17 00:00:00 2001 From: Stefan Sauer Date: Tue, 20 Dec 2011 22:36:18 +0100 Subject: [PATCH] controller: move GstControlledProperty into a separate class Add a GstControlBinding class. This is a preparation for making the controlsources generate double valued control curves and do the gparamspec mapping in the control binding. Now the API in GstObject is again mostly for convenience. --- docs/gst/gstreamer-docs.sgml | 1 + docs/gst/gstreamer-sections.txt | 30 +- docs/gst/gstreamer.types.in | 1 + gst/Makefile.am | 2 + gst/gst.c | 3 + gst/gstcontrolbinding.c | 233 +++++++++++++ gst/gstcontrolbinding.h | 98 ++++++ gst/gstcontrolsource.c | 17 +- gst/gstobject.c | 319 ++++++++---------- gst/gstobject.h | 21 +- .../controller/gsttimedvaluecontrolsource.c | 4 + tests/check/libs/controller.c | 19 +- 12 files changed, 540 insertions(+), 208 deletions(-) create mode 100644 gst/gstcontrolbinding.c create mode 100644 gst/gstcontrolbinding.h diff --git a/docs/gst/gstreamer-docs.sgml b/docs/gst/gstreamer-docs.sgml index e03fbc38eb..95faaefe41 100644 --- a/docs/gst/gstreamer-docs.sgml +++ b/docs/gst/gstreamer-docs.sgml @@ -67,6 +67,7 @@ Windows. It is released under the GNU Library General Public License + diff --git a/docs/gst/gstreamer-sections.txt b/docs/gst/gstreamer-sections.txt index f51b1b35bd..164e98e7ff 100644 --- a/docs/gst/gstreamer-sections.txt +++ b/docs/gst/gstreamer-sections.txt @@ -557,6 +557,27 @@ GST_USING_PRINTF_EXTENSION +
+gstcontrolbinding +GstControlBinding +GstControlBinding +GstControlBindingClass +gst_control_binding_new +gst_control_binding_sync_values +gst_control_binding_get_control_source +gst_control_binding_set_disabled +gst_control_binding_is_disabled + +GST_CONTROL_BINDING +GST_IS_CONTROL_BINDING +GST_CONTROL_BINDING_CLASS +GST_IS_CONTROL_BINDING_CLASS +GST_CONTROL_BINDING_GET_CLASS +GST_TYPE_CONTROL_BINDING + +gst_control_binding_get_type +
+
gstcontrolsource GstControlSource @@ -566,7 +587,6 @@ GstControlSourceBind GstControlSourceGetValue GstControlSourceGetValueArray GstTimedValue -GstValueArray gst_control_source_bind gst_control_source_get_value gst_control_source_get_value_array @@ -1525,9 +1545,11 @@ gst_object_get_path_string gst_object_suggest_next_sync gst_object_sync_values -gst_object_has_active_controlled_properties -gst_object_set_controlled_properties_disabled -gst_object_set_controlled_property_disabled +gst_object_has_active_control_bindings +gst_object_set_control_bindings_disabled +gst_object_set_control_binding_disabled +gst_object_set_control_binding +gst_object_get_control_binding gst_object_get_control_source gst_object_set_control_source gst_object_get_value diff --git a/docs/gst/gstreamer.types.in b/docs/gst/gstreamer.types.in index 4bf91290c4..1dd867910d 100644 --- a/docs/gst/gstreamer.types.in +++ b/docs/gst/gstreamer.types.in @@ -12,6 +12,7 @@ gst_bin_get_type gst_bus_get_type gst_child_proxy_get_type gst_clock_get_type +gst_control_binding_get_type gst_control_source_get_type gst_element_factory_get_type gst_element_get_type diff --git a/gst/Makefile.am b/gst/Makefile.am index e797c63510..4a8df1d097 100644 --- a/gst/Makefile.am +++ b/gst/Makefile.am @@ -56,6 +56,7 @@ libgstreamer_@GST_MAJORMINOR@_la_SOURCES = \ gstcaps.c \ gstchildproxy.c \ gstclock.c \ + gstcontrolbinding.c \ gstcontrolsource.c \ gstdatetime.c \ gstdebugutils.c \ @@ -150,6 +151,7 @@ gst_headers = \ gstchildproxy.h \ gstclock.h \ gstcompat.h \ + gstcontrolbinding.h \ gstcontrolsource.h \ gstdatetime.h \ gstdebugutils.h \ diff --git a/gst/gst.c b/gst/gst.c index bcaae43ef7..4aeb6563ca 100644 --- a/gst/gst.c +++ b/gst/gst.c @@ -771,6 +771,9 @@ init_post (GOptionContext * context, GOptionGroup * group, gpointer data, g_type_class_ref (gst_segment_flags_get_type ()); g_type_class_ref (gst_scheduling_flags_get_type ()); + g_type_class_ref (gst_control_binding_get_type ()); + g_type_class_ref (gst_control_source_get_type ()); + _priv_gst_event_initialize (); _priv_gst_buffer_initialize (); _priv_gst_message_initialize (); diff --git a/gst/gstcontrolbinding.c b/gst/gstcontrolbinding.c new file mode 100644 index 0000000000..4bba851fd6 --- /dev/null +++ b/gst/gstcontrolbinding.c @@ -0,0 +1,233 @@ +/* GStreamer + * + * Copyright (C) 2011 Stefan Sauer + * + * gstcontrolbinding.c: Attachment for control sources + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ +/** + * SECTION:gstcontrolbinding + * @short_description: attachment for control source sources + * + * A value mapping object that attaches control sources to gobject properties. + */ + +#include "gst_private.h" + +#include +#include + +#include "gstcontrolbinding.h" + +#define GST_CAT_DEFAULT control_binding_debug +GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFAULT); + +static void gst_control_binding_dispose (GObject * object); +static void gst_control_binding_finalize (GObject * object); + +#define _do_init \ + GST_DEBUG_CATEGORY_INIT (GST_CAT_DEFAULT, "gstcontrolbinding", 0, \ + "dynamic parameter control source attachment"); + +G_DEFINE_TYPE_WITH_CODE (GstControlBinding, gst_control_binding, G_TYPE_OBJECT, + _do_init); + +static void +gst_control_binding_class_init (GstControlBindingClass * klass) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS (klass); + + gobject_class->dispose = gst_control_binding_dispose; + gobject_class->finalize = gst_control_binding_finalize; +} + +static void +gst_control_binding_init (GstControlBinding * self) +{ +} + +static void +gst_control_binding_dispose (GObject * object) +{ + GstControlBinding *self = GST_CONTROL_BINDING (object); + + if (self->csource) { + // FIXME: hack + self->csource->bound = FALSE; + g_object_unref (self->csource); + self->csource = NULL; + } +} + +static void +gst_control_binding_finalize (GObject * object) +{ + GstControlBinding *self = GST_CONTROL_BINDING (object); + + g_value_unset (&self->cur_value); + g_value_unset (&self->last_value); +} + +/** + * gst_control_binding_new: + * @object: the object of the property + * @property_name: the property-name to attach the control source + * @csource: the control source + * + * Create a new control-binding that attaches the #GstControlSource to the + * #GObject property. + * + * Returns: the new #GstControlBinding + */ +GstControlBinding * +gst_control_binding_new (GstObject * object, const gchar * property_name, + GstControlSource * csource) +{ + GstControlBinding *self = NULL; + GParamSpec *pspec; + + GST_INFO_OBJECT (object, "trying to put property '%s' under control", + property_name); + + /* check if the object has a property of that name */ + if ((pspec = g_object_class_find_property (G_OBJECT_GET_CLASS (object), + property_name))) { + GST_DEBUG_OBJECT (object, " psec->flags : 0x%08x", pspec->flags); + + /* check if this param is witable && controlable && !construct-only */ + g_return_val_if_fail ((pspec->flags & (G_PARAM_WRITABLE | + GST_PARAM_CONTROLLABLE | G_PARAM_CONSTRUCT_ONLY)) == + (G_PARAM_WRITABLE | GST_PARAM_CONTROLLABLE), NULL); + + if (gst_control_source_bind (csource, pspec)) { + if ((self = (GstControlBinding *) g_object_newv (GST_TYPE_CONTROL_BINDING, + 0, NULL))) { + self->pspec = pspec; + self->name = pspec->name; + self->csource = g_object_ref (csource); + self->disabled = FALSE; + + g_value_init (&self->cur_value, G_PARAM_SPEC_VALUE_TYPE (pspec)); + g_value_init (&self->last_value, G_PARAM_SPEC_VALUE_TYPE (pspec)); + } + } + } else { + GST_WARNING_OBJECT (object, "class '%s' has no property '%s'", + G_OBJECT_TYPE_NAME (object), property_name); + } + return self; +} + +/* functions */ + +/** + * gst_control_binding_sync_values: + * @self: the control binding + * @object: the object that has controlled properties + * @timestamp: the time that should be processed + * @last_sync: the last time this was called + * + * Sets the property of the @object, according to the #GstControlSources that + * handle them and for the given timestamp. + * + * If this function fails, it is most likely the application developers fault. + * Most probably the control sources are not setup correctly. + * + * Returns: %TRUE if the controller value could be applied to the object + * property, %FALSE otherwise + */ +gboolean +gst_control_binding_sync_values (GstControlBinding * self, GstObject * object, + GstClockTime timestamp, GstClockTime last_sync) +{ + GValue *value; + gboolean ret; + + g_return_val_if_fail (GST_IS_CONTROL_BINDING (self), FALSE); + + if (self->disabled) + return TRUE; + + GST_LOG_OBJECT (object, "property '%s' at ts=%" G_GUINT64_FORMAT, + self->name, timestamp); + + value = &self->cur_value; + ret = gst_control_source_get_value (self->csource, timestamp, value); + if (G_LIKELY (ret)) { + /* always set the value for first time, but then only if it changed + * this should limit g_object_notify invocations. + * FIXME: can we detect negative playback rates? + */ + if ((timestamp < last_sync) || + gst_value_compare (value, &self->last_value) != GST_VALUE_EQUAL) { + /* we can make this faster + * http://bugzilla.gnome.org/show_bug.cgi?id=536939 + */ + g_object_set_property ((GObject *) object, self->name, value); + g_value_copy (value, &self->last_value); + } + } else { + GST_DEBUG_OBJECT (object, "no control value for param %s", self->name); + } + return (ret); +} + +/** + * gst_control_binding_get_control_source: + * @self: the control binding + * + * Get the control source. + * + * Returns: the control source. Unref when done with it. + */ +GstControlSource * +gst_control_binding_get_control_source (GstControlBinding * self) +{ + g_return_val_if_fail (GST_IS_CONTROL_BINDING (self), NULL); + return g_object_ref (self->csource); +} + +/** + * gst_control_binding_set_disabled: + * @self: the control binding + * @disabled: boolean that specifies whether to disable the controller + * or not. + * + * This function is used to disable a control binding for some time, i.e. + * gst_object_sync_values() will do nothing. + */ +void +gst_control_binding_set_disabled (GstControlBinding * self, gboolean disabled) +{ + g_return_if_fail (GST_IS_CONTROL_BINDING (self)); + self->disabled = disabled; +} + +/** + * gst_control_binding_is_disabled: + * @self: the control binding + * + * Check if the control binding is disabled. + * + * Returns: %TRUE if the binding is inactive + */ +gboolean +gst_control_binding_is_disabled (GstControlBinding * self) +{ + g_return_val_if_fail (GST_IS_CONTROL_BINDING (self), TRUE); + return (self->disabled == TRUE); +} diff --git a/gst/gstcontrolbinding.h b/gst/gstcontrolbinding.h new file mode 100644 index 0000000000..e9ee23af74 --- /dev/null +++ b/gst/gstcontrolbinding.h @@ -0,0 +1,98 @@ +/* GStreamer + * + * Copyright (C) 2011 Stefan Sauer + * + * gstcontrolbinding.h: Attachment for control sources + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef __GST_CONTROL_BINDING_H__ +#define __GST_CONTROL_BINDING_H__ + +#include + +#include + +#include + +G_BEGIN_DECLS + +#define GST_TYPE_CONTROL_BINDING \ + (gst_control_binding_get_type()) +#define GST_CONTROL_BINDING(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_CONTROL_BINDING,GstControlBinding)) +#define GST_CONTROL_BINDING_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_CONTROL_BINDING,GstControlBindingClass)) +#define GST_IS_CONTROL_BINDING(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_CONTROL_BINDING)) +#define GST_IS_CONTROL_BINDING_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_CONTROL_BINDING)) +#define GST_CONTROL_BINDING_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS ((obj), GST_TYPE_CONTOL_SOURCE, GstControlBindingClass)) + +typedef struct _GstControlBinding GstControlBinding; +typedef struct _GstControlBindingClass GstControlBindingClass; + +/** + * GstControlBinding: + * + * The instance structure of #GstControlBinding. + */ +struct _GstControlBinding { + GObject parent; + + /*< public >*/ + GParamSpec *pspec; /* GParamSpec for this property */ + const gchar *name; /* name of the property */ + GstControlSource *csource; /* GstControlSource for this property */ + gboolean disabled; + GValue cur_value, last_value; + + /*< private >*/ + gpointer _gst_reserved[GST_PADDING]; +}; + +/** + * GstControlBindingClass: + * @parent_class: Parent class + * + * The class structure of #GstControlBinding. + */ + +struct _GstControlBindingClass +{ + GObjectClass parent_class; + + /*< private >*/ + gpointer _gst_reserved[GST_PADDING]; +}; + +GType gst_control_binding_get_type (void); + +/* Functions */ + +GstControlBinding * gst_control_binding_new (GstObject * object, const gchar * property_name, + GstControlSource * csource); + +gboolean gst_control_binding_sync_values (GstControlBinding * self, GstObject *object, + GstClockTime timestamp, GstClockTime last_sync); +GstControlSource * gst_control_binding_get_control_source (GstControlBinding * self); +void gst_control_binding_set_disabled (GstControlBinding * self, gboolean disabled); +gboolean gst_control_binding_is_disabled (GstControlBinding * self); +G_END_DECLS + +#endif /* __GST_CONTROL_BINDING_H__ */ diff --git a/gst/gstcontrolsource.c b/gst/gstcontrolsource.c index faffc47e3d..85f242d5fc 100644 --- a/gst/gstcontrolsource.c +++ b/gst/gstcontrolsource.c @@ -51,7 +51,12 @@ #define GST_CAT_DEFAULT control_source_debug GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFAULT); -G_DEFINE_ABSTRACT_TYPE (GstControlSource, gst_control_source, G_TYPE_OBJECT); +#define _do_init \ + GST_DEBUG_CATEGORY_INIT (GST_CAT_DEFAULT, "gstcontrolsource", 0, \ + "dynamic parameter control sources"); + +G_DEFINE_ABSTRACT_TYPE_WITH_CODE (GstControlSource, gst_control_source, + G_TYPE_OBJECT, _do_init); static void gst_control_source_class_init (GstControlSourceClass * klass) @@ -60,15 +65,11 @@ gst_control_source_class_init (GstControlSourceClass * klass) /* Has to be implemented by children */ klass->bind = NULL; - - GST_DEBUG_CATEGORY_INIT (GST_CAT_DEFAULT, "gstcontrolsource", 0, - "dynamic parameter control sources"); } static void gst_control_source_init (GstControlSource * self) { - /* Set default handlers that print a warning */ self->get_value = NULL; self->get_value_array = NULL; self->bound = FALSE; @@ -150,8 +151,12 @@ gst_control_source_bind (GstControlSource * self, GParamSpec * pspec) ret = GST_CONTROL_SOURCE_GET_CLASS (self)->bind (self, pspec); - if (ret) + if (ret) { + GST_DEBUG ("bound control source for param %s", pspec->name); self->bound = TRUE; + } else { + GST_DEBUG ("failed to bind control source for param %s", pspec->name); + } return ret; } diff --git a/gst/gstobject.c b/gst/gstobject.c index 4ed8d0f7db..5b098fe8ac 100644 --- a/gst/gstobject.c +++ b/gst/gstobject.c @@ -140,6 +140,7 @@ #include "gstobject.h" #include "gstmarshal.h" #include "gstclock.h" +#include "gstcontrolbinding.h" #include "gstcontrolsource.h" #include "gstinfo.h" #include "gstparamspecs.h" @@ -195,21 +196,6 @@ static guint gst_object_signals[LAST_SIGNAL] = { 0 }; static GParamSpec *properties[PROP_LAST]; -/* - * GstControlledProperty: - */ -typedef struct _GstControlledProperty -{ - GParamSpec *pspec; /* GParamSpec for this property */ - const gchar *name; /* name of the property */ - GstControlSource *csource; /* GstControlSource for this property */ - gboolean disabled; - GValue last_value; -} GstControlledProperty; - -static void gst_controlled_property_free (GstControlledProperty * prop); - - G_DEFINE_ABSTRACT_TYPE (GstObject, gst_object, G_TYPE_INITIALLY_UNOWNED); static void @@ -425,14 +411,14 @@ gst_object_dispose (GObject * object) GST_OBJECT_PARENT (object) = NULL; GST_OBJECT_UNLOCK (object); - if (self->properties) { + if (self->control_bindings) { GList *node; - for (node = self->properties; node; node = g_list_next (node)) { - gst_controlled_property_free ((GstControlledProperty *) node->data); + for (node = self->control_bindings; node; node = g_list_next (node)) { + g_object_unref (node->data); } - g_list_free (self->properties); - self->properties = NULL; + g_list_free (self->control_bindings); + self->control_bindings = NULL; } ((GObjectClass *) gst_object_parent_class)->dispose (object); @@ -1037,85 +1023,27 @@ gst_object_get_path_string (GstObject * object) /* controller helper functions */ /* - * gst_controlled_property_new: - * @object: for which object the controlled property should be set up - * @name: the name of the property to be controlled - * - * Private method which initializes the fields of a new controlled property - * structure. - * - * Returns: a freshly allocated structure or %NULL - */ -static GstControlledProperty * -gst_controlled_property_new (GstObject * object, const gchar * name) -{ - GstControlledProperty *prop = NULL; - GParamSpec *pspec; - - GST_INFO ("trying to put property '%s' under control", name); - - /* check if the object has a property of that name */ - if ((pspec = - g_object_class_find_property (G_OBJECT_GET_CLASS (object), name))) { - GST_DEBUG (" psec->flags : 0x%08x", pspec->flags); - - /* check if this param is witable && controlable && !construct-only */ - g_return_val_if_fail ((pspec->flags & (G_PARAM_WRITABLE | - GST_PARAM_CONTROLLABLE | G_PARAM_CONSTRUCT_ONLY)) == - (G_PARAM_WRITABLE | GST_PARAM_CONTROLLABLE), NULL); - - if ((prop = g_slice_new (GstControlledProperty))) { - prop->pspec = pspec; - prop->name = pspec->name; - prop->csource = NULL; - prop->disabled = FALSE; - memset (&prop->last_value, 0, sizeof (GValue)); - g_value_init (&prop->last_value, G_PARAM_SPEC_VALUE_TYPE (prop->pspec)); - } - } else { - GST_WARNING ("class '%s' has no property '%s'", G_OBJECT_TYPE_NAME (object), - name); - } - return prop; -} - -/* - * gst_controlled_property_free: - * @prop: the object to free - * - * Private method which frees all data allocated by a #GstControlledProperty - * instance. - */ -static void -gst_controlled_property_free (GstControlledProperty * prop) -{ - if (prop->csource) - g_object_unref (prop->csource); - g_value_unset (&prop->last_value); - g_slice_free (GstControlledProperty, prop); -} - -/* - * gst_object_find_controlled_property: + * gst_object_find_control_binding: * @self: the gobject to search for a property in * @name: the gobject property name to look for * * Searches the list of properties under control. * - * Returns: a #GstControlledProperty or %NULL if the property is not being + * Returns: a #GstControlBinding or %NULL if the property is not being * controlled. */ -static GstControlledProperty * -gst_object_find_controlled_property (GstObject * self, const gchar * name) +static GstControlBinding * +gst_object_find_control_binding (GstObject * self, const gchar * name) { - GstControlledProperty *prop; + GstControlBinding *binding; GList *node; - for (node = self->properties; node; node = g_list_next (node)) { - prop = node->data; + for (node = self->control_bindings; node; node = g_list_next (node)) { + binding = node->data; /* FIXME: eventually use GQuark to speed it up */ - if (!strcmp (prop->name, name)) { - return prop; + if (!strcmp (binding->name, name)) { + GST_DEBUG_OBJECT (self, "found control binding for property '%s'", name); + return binding; } } GST_DEBUG_OBJECT (self, "controller does not manage property '%s'", name); @@ -1174,10 +1102,8 @@ gst_object_suggest_next_sync (GstObject * object) gboolean gst_object_sync_values (GstObject * object, GstClockTime timestamp) { - GstControlledProperty *prop; GList *node; - gboolean ret = TRUE, val_ret; - GValue value = { 0, }; + gboolean ret = TRUE; g_return_val_if_fail (GST_IS_OBJECT (object), FALSE); g_return_val_if_fail (GST_CLOCK_TIME_IS_VALID (timestamp), FALSE); @@ -1187,36 +1113,9 @@ gst_object_sync_values (GstObject * object, GstClockTime timestamp) /* FIXME: this deadlocks */ /* GST_OBJECT_LOCK (object); */ g_object_freeze_notify ((GObject *) object); - /* go over the controlled properties of the controller */ - for (node = object->properties; node; node = g_list_next (node)) { - prop = node->data; - - if (prop->disabled) - continue; - - GST_LOG_OBJECT (object, "property '%s' at ts=%" G_GUINT64_FORMAT, - prop->name, timestamp); - - /* we can make this faster - * http://bugzilla.gnome.org/show_bug.cgi?id=536939 - */ - g_value_init (&value, G_PARAM_SPEC_VALUE_TYPE (prop->pspec)); - val_ret = gst_control_source_get_value (prop->csource, timestamp, &value); - if (G_LIKELY (val_ret)) { - /* always set the value for first time, but then only if it changed - * this should limit g_object_notify invocations. - * FIXME: can we detect negative playback rates? - */ - if ((timestamp < object->last_sync) || - gst_value_compare (&value, &prop->last_value) != GST_VALUE_EQUAL) { - g_object_set_property ((GObject *) object, prop->name, &value); - g_value_copy (&value, &prop->last_value); - } - } else { - GST_DEBUG_OBJECT (object, "no control value for param %s", prop->name); - } - g_value_unset (&value); - ret &= val_ret; + for (node = object->control_bindings; node; node = g_list_next (node)) { + ret &= gst_control_binding_sync_values ((GstControlBinding *) node->data, + object, timestamp, object->last_sync); } object->last_sync = timestamp; g_object_thaw_notify ((GObject *) object); @@ -1225,8 +1124,9 @@ gst_object_sync_values (GstObject * object, GstClockTime timestamp) return ret; } + /** - * gst_object_has_active_controlled_properties: + * gst_object_has_active_control_bindings: * @object: the object that has controlled properties * * Check if the @object has an active controlled properties. @@ -1234,51 +1134,47 @@ gst_object_sync_values (GstObject * object, GstClockTime timestamp) * Returns: %TRUE if the object has active controlled properties */ gboolean -gst_object_has_active_controlled_properties (GstObject * object) +gst_object_has_active_control_bindings (GstObject * object) { gboolean res = FALSE; GList *node; - GstControlledProperty *prop; g_return_val_if_fail (GST_IS_OBJECT (object), FALSE); GST_OBJECT_LOCK (object); - for (node = object->properties; node; node = node->next) { - prop = node->data; - res |= !prop->disabled; + for (node = object->control_bindings; node; node = g_list_next (node)) { + res |= !gst_control_binding_is_disabled ((GstControlBinding *) node->data); } GST_OBJECT_UNLOCK (object); return res; } /** - * gst_object_set_controlled_properties_disabled: + * gst_object_set_control_bindings_disabled: * @object: the object that has controlled properties * @disabled: boolean that specifies whether to disable the controller * or not. * * This function is used to disable all controlled properties of the @object for - * some time, i.e. gst_object_sync_values() will do nothing.. + * some time, i.e. gst_object_sync_values() will do nothing. */ void -gst_object_set_controlled_properties_disabled (GstObject * object, - gboolean disabled) +gst_object_set_control_bindings_disabled (GstObject * object, gboolean disabled) { GList *node; - GstControlledProperty *prop; g_return_if_fail (GST_IS_OBJECT (object)); GST_OBJECT_LOCK (object); - for (node = object->properties; node; node = node->next) { - prop = node->data; - prop->disabled = disabled; + for (node = object->control_bindings; node; node = g_list_next (node)) { + gst_control_binding_set_disabled ((GstControlBinding *) node->data, + disabled); } GST_OBJECT_UNLOCK (object); } /** - * gst_object_set_controlled_property_disabled: + * gst_object_set_control_binding_disabled: * @object: the object that has controlled properties * @property_name: property to disable * @disabled: boolean that specifies whether to disable the controller @@ -1289,21 +1185,88 @@ gst_object_set_controlled_properties_disabled (GstObject * object, * property. */ void -gst_object_set_controlled_property_disabled (GstObject * object, +gst_object_set_control_binding_disabled (GstObject * object, const gchar * property_name, gboolean disabled) { - GstControlledProperty *prop; + GstControlBinding *binding; g_return_if_fail (GST_IS_OBJECT (object)); g_return_if_fail (property_name); GST_OBJECT_LOCK (object); - if ((prop = gst_object_find_controlled_property (object, property_name))) { - prop->disabled = disabled; + if ((binding = gst_object_find_control_binding (object, property_name))) { + gst_control_binding_set_disabled (binding, disabled); } GST_OBJECT_UNLOCK (object); } + +/** + * gst_object_set_control_binding: + * @object: the controller object + * @binding: the #GstControlBinding that should be used for the property + * + * Sets the #GstControlBinding. If there already was a #GstControlBinding + * for this property it will be replaced. Use %NULL for @binding to unset it. + * + * Returns: %FALSE if the given property isn't handled by the controller or + * %TRUE if everything worked as expected. + */ +gboolean +gst_object_set_control_binding (GstObject * object, GstControlBinding * binding) +{ + GstControlBinding *old; + gboolean ret = FALSE; + + g_return_val_if_fail (GST_IS_OBJECT (object), FALSE); + g_return_val_if_fail ((!binding || GST_IS_CONTROL_BINDING (binding)), FALSE); + + GST_OBJECT_LOCK (object); + if ((old = gst_object_find_control_binding (object, binding->name))) { + object->control_bindings = g_list_remove (object->control_bindings, old); + g_object_unref (old); + GST_DEBUG_OBJECT (object, "controlled property %s removed", binding->name); + ret = TRUE; + } + if (binding) { + object->control_bindings = + g_list_prepend (object->control_bindings, g_object_ref (binding)); + GST_DEBUG_OBJECT (object, "controlled property %s added", binding->name); + ret = TRUE; + } + GST_OBJECT_UNLOCK (object); + + return ret; +} + +/** + * gst_object_get_control_binding: + * @object: the object + * @property_name: name of the property + * + * Gets the corresponding #GstControlBinding for the property. This should be + * unreferenced again after use. + * + * Returns: (transfer full): the #GstControlBinding for @property_name or %NULL if + * the property is not controlled. + */ +GstControlBinding * +gst_object_get_control_binding (GstObject * object, const gchar * property_name) +{ + GstControlBinding *binding; + + g_return_val_if_fail (GST_IS_OBJECT (object), NULL); + g_return_val_if_fail (property_name, NULL); + + GST_OBJECT_LOCK (object); + if ((binding = gst_object_find_control_binding (object, property_name))) { + g_object_ref (binding); + } + GST_OBJECT_UNLOCK (object); + + return binding; +} + /** * gst_object_set_control_source: * @object: the controller object @@ -1313,6 +1276,8 @@ gst_object_set_controlled_property_disabled (GstObject * object, * Sets the #GstControlSource for @property_name. If there already was a #GstControlSource * for this property it will be unreferenced. * + * This is a convenience function for gst_object_set_control_binding(). + * * Returns: %FALSE if the given property isn't handled by the controller or the new #GstControlSource * couldn't be bound to the property, %TRUE if everything worked as expected. */ @@ -1320,7 +1285,7 @@ gboolean gst_object_set_control_source (GstObject * object, const gchar * property_name, GstControlSource * csource) { - GstControlledProperty *prop; + GstControlBinding *binding; gboolean ret = FALSE; g_return_val_if_fail (GST_IS_OBJECT (object), FALSE); @@ -1328,30 +1293,21 @@ gst_object_set_control_source (GstObject * object, const gchar * property_name, g_return_val_if_fail ((!csource || GST_IS_CONTROL_SOURCE (csource)), FALSE); GST_OBJECT_LOCK (object); - prop = gst_object_find_controlled_property (object, property_name); - if (!prop) { - if ((prop = gst_controlled_property_new (object, property_name))) { - object->properties = g_list_prepend (object->properties, prop); - GST_DEBUG_OBJECT (object, "controlled property %s added", property_name); - } + if ((binding = gst_object_find_control_binding (object, property_name))) { + object->control_bindings = + g_list_remove (object->control_bindings, binding); + g_object_unref (binding); + GST_DEBUG_OBJECT (object, "controlled property %s removed", property_name); + ret = TRUE; } - if (prop) { - GstControlSource *old = prop->csource; - - if (csource != old) { - if (csource && (ret = gst_control_source_bind (csource, prop->pspec))) { - prop->csource = g_object_ref (csource); - } else if (!csource) { - ret = TRUE; - prop->csource = NULL; - object->properties = g_list_remove (object->properties, prop); - //g_signal_handler_disconnect (self->object, prop->notify_handler_id); - gst_controlled_property_free (prop); - GST_DEBUG_OBJECT (object, "controlled property %s removed", - property_name); - } - if (ret && old) - g_object_unref (old); + if (csource) { + if ((binding = gst_control_binding_new (object, property_name, csource))) { + object->control_bindings = + g_list_prepend (object->control_bindings, binding); + GST_DEBUG_OBJECT (object, "controlled property %s added", property_name); + ret = TRUE; + } else { + ret = FALSE; } } GST_OBJECT_UNLOCK (object); @@ -1362,33 +1318,35 @@ gst_object_set_control_source (GstObject * object, const gchar * property_name, /** * gst_object_get_control_source: * @object: the object - * @property_name: name of the property for which the #GstControlSource should be get + * @property_name: name of the property * - * Gets the corresponding #GstControlSource for the property. This should be unreferenced - * again after use. + * Gets the corresponding #GstControlSource for the property. This should be + * unreferenced again after use. * - * Returns: (transfer full): the #GstControlSource for @property_name or NULL if - * the property is not controlled by this controller or no #GstControlSource was - * assigned yet. + * This is a convenience function for gst_object_get_control_binding(). + * + * Returns: (transfer full): the #GstControlSource for @property_name or %NULL if + * the property is not controlled. */ GstControlSource * gst_object_get_control_source (GstObject * object, const gchar * property_name) { - GstControlledProperty *prop; + GstControlBinding *binding; GstControlSource *ret = NULL; g_return_val_if_fail (GST_IS_OBJECT (object), NULL); g_return_val_if_fail (property_name, NULL); GST_OBJECT_LOCK (object); - if ((prop = gst_object_find_controlled_property (object, property_name))) { - ret = g_object_ref (prop->csource); + if ((binding = gst_object_find_control_binding (object, property_name))) { + ret = gst_control_binding_get_control_source (binding); } GST_OBJECT_UNLOCK (object); return ret; } + /** * gst_object_get_value: * @object: the object that has controlled properties @@ -1404,7 +1362,7 @@ GValue * gst_object_get_value (GstObject * object, const gchar * property_name, GstClockTime timestamp) { - GstControlledProperty *prop; + GstControlBinding *binding; GValue *val = NULL; g_return_val_if_fail (GST_IS_OBJECT (object), NULL); @@ -1412,12 +1370,12 @@ gst_object_get_value (GstObject * object, const gchar * property_name, g_return_val_if_fail (GST_CLOCK_TIME_IS_VALID (timestamp), NULL); GST_OBJECT_LOCK (object); - if ((prop = gst_object_find_controlled_property (object, property_name))) { + if ((binding = gst_object_find_control_binding (object, property_name))) { val = g_new0 (GValue, 1); - g_value_init (val, G_PARAM_SPEC_VALUE_TYPE (prop->pspec)); + g_value_init (val, G_PARAM_SPEC_VALUE_TYPE (binding->pspec)); /* get current value via control source */ - if (!gst_control_source_get_value (prop->csource, timestamp, val)) { + if (!gst_control_source_get_value (binding->csource, timestamp, val)) { g_free (val); val = NULL; } @@ -1451,7 +1409,7 @@ gst_object_get_value_array (GstObject * object, const gchar * property_name, gpointer values) { gboolean res = FALSE; - GstControlledProperty *prop; + GstControlBinding *binding; g_return_val_if_fail (GST_IS_OBJECT (object), FALSE); g_return_val_if_fail (property_name, FALSE); @@ -1460,14 +1418,15 @@ gst_object_get_value_array (GstObject * object, const gchar * property_name, g_return_val_if_fail (values, FALSE); GST_OBJECT_LOCK (object); - if ((prop = gst_object_find_controlled_property (object, property_name))) { - res = gst_control_source_get_value_array (prop->csource, timestamp, + if ((binding = gst_object_find_control_binding (object, property_name))) { + res = gst_control_source_get_value_array (binding->csource, timestamp, interval, n_values, values); } GST_OBJECT_UNLOCK (object); return res; } + /** * gst_object_get_control_rate: * @object: the object that has controlled properties diff --git a/gst/gstobject.h b/gst/gstobject.h index 08eafb6ab9..ec7925e7ef 100644 --- a/gst/gstobject.h +++ b/gst/gstobject.h @@ -171,7 +171,7 @@ struct _GstObject { guint32 flags; /*< private >*/ - GList *properties; /* List of GstControlledProperty */ + GList *control_bindings; /* List of GstControlBinding */ guint64 control_rate; guint64 last_sync; @@ -232,30 +232,35 @@ gchar * gst_object_get_path_string (GstObject *object); gboolean gst_object_check_uniqueness (GList *list, const gchar *name); /* controller functions */ +#include #include GstClockTime gst_object_suggest_next_sync (GstObject * object); gboolean gst_object_sync_values (GstObject * object, GstClockTime timestamp); -gboolean gst_object_has_active_controlled_properties (GstObject *object); -void gst_object_set_controlled_properties_disabled (GstObject *object, gboolean disabled); -void gst_object_set_controlled_property_disabled (GstObject *object, +gboolean gst_object_has_active_control_bindings (GstObject *object); +void gst_object_set_control_bindings_disabled (GstObject *object, gboolean disabled); +void gst_object_set_control_binding_disabled (GstObject *object, const gchar * property_name, gboolean disabled); +gboolean gst_object_set_control_binding (GstObject * object, GstControlBinding * binding); +GstControlBinding * + gst_object_get_control_binding (GstObject *object, const gchar * property_name); + gboolean gst_object_set_control_source (GstObject *object, const gchar * property_name, GstControlSource *csource); GstControlSource * gst_object_get_control_source (GstObject *object, const gchar * property_name); -GValue * gst_object_get_value (GstObject * object, const gchar * property_name, +GValue * gst_object_get_value (GstObject * object, const gchar * property_name, GstClockTime timestamp); -gboolean gst_object_get_value_array (GstObject * object, const gchar * property_name, +gboolean gst_object_get_value_array (GstObject * object, const gchar * property_name, GstClockTime timestamp, GstClockTime interval, guint n_values, gpointer values); -GstClockTime gst_object_get_control_rate (GstObject * object); -void gst_object_set_control_rate (GstObject * object, GstClockTime control_rate); +GstClockTime gst_object_get_control_rate (GstObject * object); +void gst_object_set_control_rate (GstObject * object, GstClockTime control_rate); G_END_DECLS diff --git a/libs/gst/controller/gsttimedvaluecontrolsource.c b/libs/gst/controller/gsttimedvaluecontrolsource.c index 46fc61a526..2c248d503e 100644 --- a/libs/gst/controller/gsttimedvaluecontrolsource.c +++ b/libs/gst/controller/gsttimedvaluecontrolsource.c @@ -102,6 +102,10 @@ gst_timed_value_control_source_bind (GstControlSource * source, GstTimedValueControlSource *self = (GstTimedValueControlSource *) source; gboolean ret = TRUE; + if (self->type != G_TYPE_INVALID) { + gst_timed_value_control_source_reset (self); + } + /* get the fundamental base type */ self->type = base = type = G_PARAM_SPEC_VALUE_TYPE (pspec); while ((type = g_type_parent (type))) diff --git a/tests/check/libs/controller.c b/tests/check/libs/controller.c index 73d8ac3b1f..26a3c9dd38 100644 --- a/tests/check/libs/controller.c +++ b/tests/check/libs/controller.c @@ -378,10 +378,12 @@ GST_START_TEST (controller_param_twice) GST_CONTROL_SOURCE (cs)); fail_unless (res, NULL); - /* setting it again should not work */ + /* setting it again will just unset the old and set it again + * this might cause some trouble with binding the control source again + */ res = gst_object_set_control_source (GST_OBJECT (elem), "ulong", GST_CONTROL_SOURCE (cs)); - fail_unless (!res, NULL); + fail_unless (res, NULL); /* it should have been added at least once, let remove it */ res = gst_object_set_control_source (GST_OBJECT (elem), "ulong", NULL); @@ -1091,8 +1093,7 @@ GST_START_TEST (controller_interpolate_linear_disabled) /* now pull in values for some timestamps, prop double disabled */ GST_TEST_MONO_SOURCE (elem)->val_ulong = 0; GST_TEST_MONO_SOURCE (elem)->val_double = 0.0; - gst_object_set_controlled_property_disabled (GST_OBJECT (elem), "double", - TRUE); + gst_object_set_control_binding_disabled (GST_OBJECT (elem), "double", TRUE); gst_object_sync_values (GST_OBJECT (elem), 0 * GST_SECOND); fail_unless_equals_int (GST_TEST_MONO_SOURCE (elem)->val_ulong, 0); fail_unless (GST_TEST_MONO_SOURCE (elem)->val_double == 0.0, NULL); @@ -1106,8 +1107,7 @@ GST_START_TEST (controller_interpolate_linear_disabled) /* now pull in values for some timestamps, after enabling double again */ GST_TEST_MONO_SOURCE (elem)->val_ulong = 0; GST_TEST_MONO_SOURCE (elem)->val_double = 0.0; - gst_object_set_controlled_property_disabled (GST_OBJECT (elem), "double", - FALSE); + gst_object_set_control_binding_disabled (GST_OBJECT (elem), "double", FALSE); gst_object_sync_values (GST_OBJECT (elem), 0 * GST_SECOND); fail_unless_equals_int (GST_TEST_MONO_SOURCE (elem)->val_ulong, 0); fail_unless (GST_TEST_MONO_SOURCE (elem)->val_double == 2.0, NULL); @@ -1121,7 +1121,7 @@ GST_START_TEST (controller_interpolate_linear_disabled) /* now pull in values for some timestamps, after disabling all props */ GST_TEST_MONO_SOURCE (elem)->val_ulong = 0; GST_TEST_MONO_SOURCE (elem)->val_double = 0.0; - gst_object_set_controlled_properties_disabled (GST_OBJECT (elem), TRUE); + gst_object_set_control_bindings_disabled (GST_OBJECT (elem), TRUE); gst_object_sync_values (GST_OBJECT (elem), 0 * GST_SECOND); fail_unless_equals_int (GST_TEST_MONO_SOURCE (elem)->val_ulong, 0); fail_unless (GST_TEST_MONO_SOURCE (elem)->val_double == 0.0, NULL); @@ -1135,8 +1135,7 @@ GST_START_TEST (controller_interpolate_linear_disabled) /* now pull in values for some timestamps, enabling double again */ GST_TEST_MONO_SOURCE (elem)->val_ulong = 0; GST_TEST_MONO_SOURCE (elem)->val_double = 0.0; - gst_object_set_controlled_property_disabled (GST_OBJECT (elem), "double", - FALSE); + gst_object_set_control_binding_disabled (GST_OBJECT (elem), "double", FALSE); gst_object_sync_values (GST_OBJECT (elem), 0 * GST_SECOND); fail_unless_equals_int (GST_TEST_MONO_SOURCE (elem)->val_ulong, 0); fail_unless (GST_TEST_MONO_SOURCE (elem)->val_double == 2.0, NULL); @@ -1150,7 +1149,7 @@ GST_START_TEST (controller_interpolate_linear_disabled) /* now pull in values for some timestamps, enabling all */ GST_TEST_MONO_SOURCE (elem)->val_ulong = 0; GST_TEST_MONO_SOURCE (elem)->val_double = 0.0; - gst_object_set_controlled_properties_disabled (GST_OBJECT (elem), FALSE); + gst_object_set_control_bindings_disabled (GST_OBJECT (elem), FALSE); gst_object_sync_values (GST_OBJECT (elem), 0 * GST_SECOND); fail_unless_equals_int (GST_TEST_MONO_SOURCE (elem)->val_ulong, 0); fail_unless (GST_TEST_MONO_SOURCE (elem)->val_double == 2.0, NULL);