gstreamer_core/libs/gst/controller/gstcontroller.c
changeset 8 4a7fac7dd34a
parent 0 0e761a78d257
child 30 7e817e7e631c
--- a/gstreamer_core/libs/gst/controller/gstcontroller.c	Fri Mar 19 09:35:09 2010 +0200
+++ b/gstreamer_core/libs/gst/controller/gstcontroller.c	Fri Apr 16 15:15:52 2010 +0300
@@ -81,7 +81,6 @@
 #ifdef __SYMBIAN32__
 #include <glib_global.h>
 #include <gobject_global.h>
-
 #endif
 #define GST_CAT_DEFAULT controller_debug
 GST_DEBUG_CATEGORY_EXTERN (GST_CAT_DEFAULT);
@@ -151,6 +150,8 @@
       prop->pspec = pspec;
       prop->name = pspec->name;
       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),
@@ -171,6 +172,7 @@
 {
   if (prop->csource)
     g_object_unref (prop->csource);
+  g_value_unset (&prop->last_value);
   g_free (prop);
 }
 
@@ -203,6 +205,58 @@
   return NULL;
 }
 
+/*
+ * gst_controller_add_property:
+ * @self: the controller object or %NULL if none yet exists
+ * @object: object to bind the property
+ * @name: name of projecty in @object
+ * @ref_existing: pointer to flag that tracks if we need to ref an existng
+ *   controller still
+ *
+ * Creates a new #GstControlledProperty if there is none for property @name yet.
+ * In case this is the first controlled propery, it creates the controller as
+ * well.
+ *
+ * Returns: a newly created controller object or reffed existing one with the
+ * given property bound.
+ */
+static GstController *
+gst_controller_add_property (GstController * self, GObject * object,
+    gchar * name, gboolean * ref_existing)
+{
+  /* test if this property isn't yet controlled */
+  if (!self || !gst_controller_find_controlled_property (self, name)) {
+    GstControlledProperty *prop;
+
+    /* create GstControlledProperty and add to self->propeties List */
+    if ((prop = gst_controlled_property_new (object, name))) {
+      /* if we don't have a controller object yet, now is the time to create one */
+      if (!self) {
+        self = g_object_new (GST_TYPE_CONTROLLER, NULL);
+        self->object = g_object_ref (object);
+        /* store the controller */
+        g_object_set_qdata (object, priv_gst_controller_key, self);
+        *ref_existing = FALSE;
+      } else {
+        /* only want one single _ref(), even for multiple properties */
+        if (*ref_existing) {
+          g_object_ref (self);
+          *ref_existing = FALSE;
+          GST_INFO ("returning existing controller");
+        }
+      }
+      self->properties = g_list_prepend (self->properties, prop);
+    }
+  } else {
+    GST_WARNING ("trying to control property again");
+    if (*ref_existing) {
+      g_object_ref (self);
+      *ref_existing = FALSE;
+    }
+  }
+  return self;
+}
+
 /* methods */
 
 /**
@@ -222,7 +276,6 @@
 gst_controller_new_valist (GObject * object, va_list var_args)
 {
   GstController *self;
-  GstControlledProperty *prop;
   gboolean ref_existing = TRUE;
   gchar *name;
 
@@ -233,34 +286,7 @@
   self = g_object_get_qdata (object, priv_gst_controller_key);
   /* create GstControlledProperty for each property */
   while ((name = va_arg (var_args, gchar *))) {
-    /* test if this property isn't yet controlled */
-    if (!self || !(prop = gst_controller_find_controlled_property (self, name))) {
-      /* create GstControlledProperty and add to self->propeties List */
-      if ((prop = gst_controlled_property_new (object, name))) {
-        /* if we don't have a controller object yet, now is the time to create one */
-        if (!self) {
-          self = g_object_new (GST_TYPE_CONTROLLER, NULL);
-          self->object = g_object_ref (object);
-          /* store the controller */
-          g_object_set_qdata (object, priv_gst_controller_key, self);
-          ref_existing = FALSE;
-        } else {
-          /* only want one single _ref(), even for multiple properties */
-          if (ref_existing) {
-            g_object_ref (self);
-            ref_existing = FALSE;
-            GST_INFO ("returning existing controller");
-          }
-        }
-        self->properties = g_list_prepend (self->properties, prop);
-      }
-    } else {
-      GST_WARNING ("trying to control property again");
-      if (ref_existing) {
-        g_object_ref (self);
-        ref_existing = FALSE;
-      }
-    }
+    self = gst_controller_add_property (self, object, name, &ref_existing);
   }
   va_end (var_args);
 
@@ -286,7 +312,6 @@
 gst_controller_new_list (GObject * object, GList * list)
 {
   GstController *self;
-  GstControlledProperty *prop;
   gboolean ref_existing = TRUE;
   gchar *name;
   GList *node;
@@ -299,34 +324,7 @@
   /* create GstControlledProperty for each property */
   for (node = list; node; node = g_list_next (node)) {
     name = (gchar *) node->data;
-    /* test if this property isn't yet controlled */
-    if (!self || !(prop = gst_controller_find_controlled_property (self, name))) {
-      /* create GstControlledProperty and add to self->propeties List */
-      if ((prop = gst_controlled_property_new (object, name))) {
-        /* if we don't have a controller object yet, now is the time to create one */
-        if (!self) {
-          self = g_object_new (GST_TYPE_CONTROLLER, NULL);
-          self->object = g_object_ref (object);
-          /* store the controller */
-          g_object_set_qdata (object, priv_gst_controller_key, self);
-          ref_existing = FALSE;
-        } else {
-          /* only want one single _ref(), even for multiple properties */
-          if (ref_existing) {
-            g_object_ref (self);
-            ref_existing = FALSE;
-            GST_INFO ("returning existing controller");
-          }
-        }
-        self->properties = g_list_prepend (self->properties, prop);
-      }
-    } else {
-      GST_WARNING ("trying to control property again");
-      if (ref_existing) {
-        g_object_ref (self);
-        ref_existing = FALSE;
-      }
-    }
+    self = gst_controller_add_property (self, object, name, &ref_existing);
   }
 
   if (self)
@@ -604,6 +602,9 @@
   GstControlledProperty *prop;
   GstControlSource *ret = NULL;
 
+  g_return_val_if_fail (GST_IS_CONTROLLER (self), NULL);
+  g_return_val_if_fail (property_name, NULL);
+
   g_mutex_lock (self->lock);
   if ((prop = gst_controller_find_controlled_property (self, property_name))) {
     ret = prop->csource;
@@ -625,7 +626,8 @@
  * Gets the value for the given controller-handled property at the requested
  * time.
  *
- * Returns: the GValue of the property at the given time, or %NULL if the property isn't handled by the controller
+ * Returns: the GValue of the property at the given time, or %NULL if the
+ * property isn't handled by the controller
  */
 #ifdef __SYMBIAN32__
 EXPORT_C
@@ -692,7 +694,9 @@
   g_mutex_lock (self->lock);
 
   /* TODO: Implement more logic, depending on interpolation mode
-   * and control points */
+   * and control points
+   * FIXME: we need playback direction
+   */
   ret = self->priv->last_sync + self->priv->control_rate;
 
   g_mutex_unlock (self->lock);
@@ -708,6 +712,9 @@
  * Sets the properties of the element, according to the controller that (maybe)
  * handles 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 values could be applied to the object
  * properties, %FALSE otherwise
  */
@@ -720,7 +727,8 @@
 {
   GstControlledProperty *prop;
   GList *node;
-  gboolean ret = FALSE;
+  gboolean ret = TRUE, val_ret;
+  GValue value = { 0, };
 
   g_return_val_if_fail (GST_IS_CONTROLLER (self), FALSE);
   g_return_val_if_fail (GST_CLOCK_TIME_IS_VALID (timestamp), FALSE);
@@ -728,25 +736,39 @@
   GST_LOG ("sync_values");
 
   g_mutex_lock (self->lock);
+  g_object_freeze_notify (self->object);
   /* go over the controlled properties of the controller */
   for (node = self->properties; node; node = g_list_next (node)) {
-    GValue value = { 0, };
     prop = node->data;
 
-    GST_DEBUG ("  property '%s' at ts=%" G_GUINT64_FORMAT, prop->name,
-        timestamp);
-
     if (!prop->csource || prop->disabled)
       continue;
 
+    GST_LOG ("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));
-    ret = gst_control_source_get_value (prop->csource, timestamp, &value);
-    if (ret) {
-      g_object_set_property (self->object, prop->name, &value);
-      g_value_unset (&value);
+    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 < self->priv->last_sync) ||
+          gst_value_compare (&value, &prop->last_value) != GST_VALUE_EQUAL) {
+        g_object_set_property (self->object, prop->name, &value);
+        g_value_copy (&value, &prop->last_value);
+      }
+    } else {
+      GST_DEBUG ("no control value for param %s", prop->name);
     }
+    g_value_unset (&value);
+    ret &= val_ret;
   }
   self->priv->last_sync = timestamp;
+  g_object_thaw_notify (self->object);
 
   g_mutex_unlock (self->lock);
 
@@ -982,7 +1004,8 @@
       g_param_spec_uint64 ("control-rate",
           "control rate",
           "Controlled properties will be updated at least every control-rate nanoseconds",
-          1, G_MAXUINT, 100 * GST_MSECOND, G_PARAM_READWRITE));
+          1, G_MAXUINT, 100 * GST_MSECOND,
+          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
 
   /* register signals */
   /* set defaults for overridable methods */
@@ -995,9 +1018,10 @@
 GType
 gst_controller_get_type (void)
 {
-  static GType type = 0;
+  static volatile gsize type = 0;
 
-  if (type == 0) {
+  if (g_once_init_enter (&type)) {
+    GType _type;
     static const GTypeInfo info = {
       sizeof (GstControllerClass),
       NULL,                     /* base_init */
@@ -1010,7 +1034,8 @@
       (GInstanceInitFunc) _gst_controller_init, /* instance_init */
       NULL                      /* value_table */
     };
-    type = g_type_register_static (G_TYPE_OBJECT, "GstController", &info, 0);
+    _type = g_type_register_static (G_TYPE_OBJECT, "GstController", &info, 0);
+    g_once_init_leave (&type, _type);
   }
   return type;
 }