controller: move GstControlledProperty into a separate class
authorStefan Sauer <ensonic@users.sf.net>
Tue, 20 Dec 2011 21:36:18 +0000 (22:36 +0100)
committerStefan Sauer <ensonic@users.sf.net>
Sun, 25 Dec 2011 17:50:45 +0000 (18:50 +0100)
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.

12 files changed:
docs/gst/gstreamer-docs.sgml
docs/gst/gstreamer-sections.txt
docs/gst/gstreamer.types.in
gst/Makefile.am
gst/gst.c
gst/gstcontrolbinding.c [new file with mode: 0644]
gst/gstcontrolbinding.h [new file with mode: 0644]
gst/gstcontrolsource.c
gst/gstobject.c
gst/gstobject.h
libs/gst/controller/gsttimedvaluecontrolsource.c
tests/check/libs/controller.c

index e03fbc3..95faaef 100644 (file)
@@ -67,6 +67,7 @@ Windows.  It is released under the GNU Library General Public License
     <xi:include href="xml/gstchildproxy.xml" />
     <xi:include href="xml/gstclock.xml" />
     <xi:include href="xml/gstconfig.xml" />
+    <xi:include href="xml/gstcontrolbinding.xml" />
     <xi:include href="xml/gstcontrolsource.xml" />
     <xi:include href="xml/gstdatetime.xml" />
     <xi:include href="xml/gstelement.xml" />
index f51b1b3..164e98e 100644 (file)
@@ -558,6 +558,27 @@ GST_USING_PRINTF_EXTENSION
 
 
 <SECTION>
+<FILE>gstcontrolbinding</FILE>
+<TITLE>GstControlBinding</TITLE>
+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
+<SUBSECTION Standard>
+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
+<SUBSECTION Private>
+gst_control_binding_get_type
+</SECTION>
+
+<SECTION>
 <FILE>gstcontrolsource</FILE>
 <TITLE>GstControlSource</TITLE>
 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
index 4bf9129..1dd8679 100644 (file)
@@ -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
index e797c63..4a8df1d 100644 (file)
@@ -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         \
index bcaae43..4aeb656 100644 (file)
--- 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 (file)
index 0000000..4bba851
--- /dev/null
@@ -0,0 +1,233 @@
+/* GStreamer
+ *
+ * Copyright (C) 2011 Stefan Sauer <ensonic@users.sf.net>
+ *
+ * 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 <glib-object.h>
+#include <gst/gst.h>
+
+#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 (file)
index 0000000..e9ee23a
--- /dev/null
@@ -0,0 +1,98 @@
+/* GStreamer
+ *
+ * Copyright (C) 2011 Stefan Sauer <ensonic@users.sf.net>
+ *
+ * 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 <gst/gstconfig.h>
+
+#include <glib-object.h>
+
+#include <gst/gstcontrolsource.h>
+
+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__ */
index faffc47..85f242d 100644 (file)
 #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;
 }
index 4ed8d0f..5b098fe 100644 (file)
 #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.
+ * 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 by this controller or no #GstControlSource was
- * assigned yet.
+ * 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
index 08eafb6..ec7925e 100644 (file)
@@ -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 <gst/gstcontrolbinding.h>
 #include <gst/gstcontrolsource.h>
 
 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
 
index 46fc61a..2c248d5 100644 (file)
@@ -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)))
index 73d8ac3..26a3c9d 100644 (file)
@@ -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);