gstreamer_core/gst/gstchildproxy.c
changeset 0 0e761a78d257
child 8 4a7fac7dd34a
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/gstreamer_core/gst/gstchildproxy.c	Thu Dec 17 08:53:32 2009 +0200
@@ -0,0 +1,549 @@
+/* GStreamer
+ * Copyright (C) 2005 Stefan Kost <ensonic@users.sf.net>
+ *
+ * gstchildproxy.c: interface for multi child elements
+ *
+ * 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:gstchildproxy
+ * @short_description: Interface for multi child elements.
+ * @see_also: #GstBin
+ *
+ * This interface abstracts handling of property sets for elements with
+ * children. Imagine elements such as mixers or polyphonic generators. They all
+ * have multiple #GstPad or some kind of voice objects. Another use case are
+ * container elements like #GstBin.
+ * The element implementing the interface acts as a parent for those child
+ * objects.
+ *
+ * By implementing this interface the child properties can be accessed from the
+ * parent element by using gst_child_proxy_get() and gst_child_proxy_set().
+ *
+ * Property names are written as "child-name::property-name". The whole naming
+ * scheme is recursive. Thus "child1::child2::property" is valid too, if
+ * "child1" and "child2" implement the #GstChildProxy interface.
+ */
+/* FIXME-0.11:
+ * it would be nice to make gst_child_proxy_get_child_by_name virtual too and
+ * use GObject instead of GstObject. We could eventually provide the current
+ * implementation as a default if children are GstObjects.
+ * This change would allow to propose the interface for inclusion with
+ * glib/gobject. IMHO this is useful for GtkContainer and compound widgets too.
+ */
+
+#include "gst_private.h"
+
+#include "gstchildproxy.h"
+#include "gstmarshal.h"
+#include <gobject/gvaluecollector.h>
+
+/* signals */
+enum
+{
+  CHILD_ADDED,
+  CHILD_REMOVED,
+  LAST_SIGNAL
+};
+
+static guint signals[LAST_SIGNAL] = { 0 };
+
+/**
+ * gst_child_proxy_get_child_by_name:
+ * @parent: the parent object to get the child from
+ * @name: the childs name
+ *
+ * Looks up a child element by the given name.
+ *
+ * Implementors can use #GstObject together with gst_object_get_name()
+ *
+ * Returns: the child object or %NULL if not found. Unref after usage.
+ *
+ * MT safe.
+ */
+#ifdef __SYMBIAN32__
+EXPORT_C
+#endif
+
+GstObject *
+gst_child_proxy_get_child_by_name (GstChildProxy * parent, const gchar * name)
+{
+  guint count, i;
+  GstObject *object, *result;
+  gchar *object_name;
+
+  g_return_val_if_fail (GST_IS_CHILD_PROXY (parent), NULL);
+  g_return_val_if_fail (name != NULL, NULL);
+
+  result = NULL;
+
+  count = gst_child_proxy_get_children_count (parent);
+  for (i = 0; i < count; i++) {
+    gboolean eq;
+
+    if (!(object = gst_child_proxy_get_child_by_index (parent, i)))
+      continue;
+
+    object_name = gst_object_get_name (object);
+    if (object_name == NULL) {
+      g_warning ("child %u of parent %s has no name", i,
+          GST_OBJECT_NAME (parent));
+      goto next;
+    }
+    eq = g_str_equal (object_name, name);
+    g_free (object_name);
+
+    if (eq) {
+      result = object;
+      break;
+    }
+  next:
+    gst_object_unref (object);
+  }
+  return result;
+}
+
+/**
+ * gst_child_proxy_get_child_by_index:
+ * @parent: the parent object to get the child from
+ * @index: the childs position in the child list
+ *
+ * Fetches a child by its number.
+ *
+ * Returns: the child object or %NULL if not found (index too high). Unref
+ * after usage.
+ *
+ * MT safe.
+ */
+#ifdef __SYMBIAN32__
+EXPORT_C
+#endif
+
+GstObject *
+gst_child_proxy_get_child_by_index (GstChildProxy * parent, guint index)
+{
+  g_return_val_if_fail (GST_IS_CHILD_PROXY (parent), NULL);
+
+  return (GST_CHILD_PROXY_GET_INTERFACE (parent)->get_child_by_index (parent,
+          index));
+}
+
+/**
+ * gst_child_proxy_get_children_count:
+ * @parent: the parent object
+ *
+ * Gets the number of child objects this parent contains.
+ *
+ * Returns: the number of child objects
+ *
+ * MT safe.
+ */
+#ifdef __SYMBIAN32__
+EXPORT_C
+#endif
+
+guint
+gst_child_proxy_get_children_count (GstChildProxy * parent)
+{
+  g_return_val_if_fail (GST_IS_CHILD_PROXY (parent), 0);
+
+  return (GST_CHILD_PROXY_GET_INTERFACE (parent)->get_children_count (parent));
+}
+
+/**
+ * gst_child_proxy_lookup:
+ * @object: object to lookup the property in
+ * @name: name of the property to look up
+ * @target: pointer to a #GstObject that takes the real object to set property on
+ * @pspec: pointer to take the #GParamSpec describing the property
+ *
+ * Looks up which object and #GParamSpec would be effected by the given @name.
+ *
+ * Returns: TRUE if @target and @pspec could be found. FALSE otherwise. In that
+ * case the values for @pspec and @target are not modified. Unref @target after
+ * usage.
+ *
+ * MT safe.
+ */
+#ifdef __SYMBIAN32__
+EXPORT_C
+#endif
+
+gboolean
+gst_child_proxy_lookup (GstObject * object, const gchar * name,
+    GstObject ** target, GParamSpec ** pspec)
+{
+  gboolean res = FALSE;
+  gchar **names, **current;
+
+  g_return_val_if_fail (GST_IS_OBJECT (object), FALSE);
+  g_return_val_if_fail (name != NULL, FALSE);
+
+  gst_object_ref (object);
+
+  current = names = g_strsplit (name, "::", -1);
+  while (current[1]) {
+    GstObject *next;
+
+    if (!GST_IS_CHILD_PROXY (object)) {
+      GST_INFO
+          ("object %s is not a parent, so you cannot request a child by name %s",
+          GST_OBJECT_NAME (object), current[0]);
+      break;
+    }
+    next = gst_child_proxy_get_child_by_name (GST_CHILD_PROXY (object),
+        current[0]);
+    if (!next) {
+      GST_INFO ("no such object %s", current[0]);
+      break;
+    }
+    gst_object_unref (object);
+    object = next;
+    current++;
+  };
+  if (current[1] == NULL) {
+    GParamSpec *spec =
+        g_object_class_find_property (G_OBJECT_GET_CLASS (object), current[0]);
+    if (spec == NULL) {
+      GST_INFO ("no param spec named %s", current[0]);
+    } else {
+      if (pspec)
+        *pspec = spec;
+      if (target) {
+        gst_object_ref (object);
+        *target = object;
+      }
+      res = TRUE;
+    }
+  }
+  gst_object_unref (object);
+  g_strfreev (names);
+  return res;
+}
+
+/**
+ * gst_child_proxy_get_property:
+ * @object: object to query
+ * @name: name of the property
+ * @value: a #GValue that should take the result.
+ *
+ * Gets a single property using the GstChildProxy mechanism.
+ * You are responsible for for freeing it by calling g_value_unset()
+ */
+#ifdef __SYMBIAN32__
+EXPORT_C
+#endif
+
+void
+gst_child_proxy_get_property (GstObject * object, const gchar * name,
+    GValue * value)
+{
+  GParamSpec *pspec;
+  GstObject *target;
+
+  g_return_if_fail (GST_IS_OBJECT (object));
+  g_return_if_fail (name != NULL);
+  g_return_if_fail (G_IS_VALUE (value));
+
+  if (!gst_child_proxy_lookup (object, name, &target, &pspec))
+    goto not_found;
+
+  g_object_get_property (G_OBJECT (target), pspec->name, value);
+  gst_object_unref (target);
+
+  return;
+
+not_found:
+  {
+    g_warning ("cannot get property %s from object %s", name,
+        GST_OBJECT_NAME (object));
+    return;
+  }
+}
+
+/**
+ * gst_child_proxy_get_valist:
+ * @object: the object to query
+ * @first_property_name: name of the first property to get
+ * @var_args: return location for the first property, followed optionally by more name/return location pairs, followed by NULL
+ *
+ * Gets properties of the parent object and its children.
+ */
+#ifdef __SYMBIAN32__
+EXPORT_C
+#endif
+
+void
+gst_child_proxy_get_valist (GstObject * object,
+    const gchar * first_property_name, va_list var_args)
+{
+  const gchar *name;
+  gchar *error = NULL;
+  GValue value = { 0, };
+
+  g_return_if_fail (G_IS_OBJECT (object));
+
+  name = first_property_name;
+
+  /* iterate over pairs */
+  while (name) {
+    gst_child_proxy_get_property (object, name, &value);
+    G_VALUE_LCOPY (&value, var_args, 0, &error);
+    if (error) {
+      g_warning ("error copying value: %s", error);
+      return;
+    }
+    g_value_unset (&value);
+    name = va_arg (var_args, gchar *);
+  }
+}
+
+/**
+ * gst_child_proxy_get:
+ * @object: the parent object
+ * @first_property_name: name of the first property to get
+ * @...: return location for the first property, followed optionally by more name/return location pairs, followed by NULL
+ *
+ * Gets properties of the parent object and its children.
+ */
+#ifdef __SYMBIAN32__
+EXPORT_C
+#endif
+
+void
+gst_child_proxy_get (GstObject * object, const gchar * first_property_name, ...)
+{
+  va_list var_args;
+
+  g_return_if_fail (GST_IS_OBJECT (object));
+
+  va_start (var_args, first_property_name);
+  gst_child_proxy_get_valist (object, first_property_name, var_args);
+  va_end (var_args);
+}
+
+/**
+ * gst_child_proxy_set_property:
+ * @object: the parent object
+ * @name: name of the property to set
+ * @value: new #GValue for the property
+ *
+ * Sets a single property using the GstChildProxy mechanism.
+ */
+#ifdef __SYMBIAN32__
+EXPORT_C
+#endif
+
+void
+gst_child_proxy_set_property (GstObject * object, const gchar * name,
+    const GValue * value)
+{
+  GParamSpec *pspec;
+  GstObject *target;
+
+  g_return_if_fail (GST_IS_OBJECT (object));
+  g_return_if_fail (name != NULL);
+  g_return_if_fail (G_IS_VALUE (value));
+
+  if (!gst_child_proxy_lookup (object, name, &target, &pspec))
+    goto not_found;
+
+  g_object_set_property (G_OBJECT (target), pspec->name, value);
+  gst_object_unref (target);
+  return;
+
+not_found:
+  {
+    g_warning ("cannot set property %s on object %s", name,
+        GST_OBJECT_NAME (object));
+    return;
+  }
+}
+
+/**
+ * gst_child_proxy_set_valist:
+ * @object: the parent object
+ * @first_property_name: name of the first property to set
+ * @var_args: value for the first property, followed optionally by more name/value pairs, followed by NULL
+ *
+ * Sets properties of the parent object and its children.
+ */
+#ifdef __SYMBIAN32__
+EXPORT_C
+#endif
+
+void
+gst_child_proxy_set_valist (GstObject * object,
+    const gchar * first_property_name, va_list var_args)
+{
+  const gchar *name;
+  gchar *error = NULL;
+  GValue value = { 0, };
+
+  g_return_if_fail (G_IS_OBJECT (object));
+
+  name = first_property_name;
+
+  /* iterate over pairs */
+  while (name) {
+    GParamSpec *pspec;
+    GstObject *target;
+
+    if (!gst_child_proxy_lookup (object, name, &target, &pspec)) {
+      g_warning ("no such property %s in object %s", name,
+          GST_OBJECT_NAME (object));
+      continue;
+    }
+    g_value_init (&value, G_PARAM_SPEC_VALUE_TYPE (pspec));
+    G_VALUE_COLLECT (&value, var_args, G_VALUE_NOCOPY_CONTENTS, &error);
+    if (error) {
+      g_warning ("error copying value: %s", error);
+      gst_object_unref (target);
+      return;
+    }
+    g_object_set_property (G_OBJECT (target), pspec->name, &value);
+    gst_object_unref (target);
+
+    g_value_unset (&value);
+    name = va_arg (var_args, gchar *);
+  }
+}
+
+/**
+ * gst_child_proxy_set:
+ * @object: the parent object
+ * @first_property_name: name of the first property to set
+ * @...: value for the first property, followed optionally by more name/value pairs, followed by NULL
+ *
+ * Sets properties of the parent object and its children.
+ */
+#ifdef __SYMBIAN32__
+EXPORT_C
+#endif
+
+void
+gst_child_proxy_set (GstObject * object, const gchar * first_property_name, ...)
+{
+  va_list var_args;
+
+  g_return_if_fail (GST_IS_OBJECT (object));
+
+  va_start (var_args, first_property_name);
+  gst_child_proxy_set_valist (object, first_property_name, var_args);
+  va_end (var_args);
+}
+
+/**
+ * gst_child_proxy_child_added:
+ * @object: the parent object
+ * @child: the newly added child
+ *
+ * Emits the "child-added" signal.
+ */
+#ifdef __SYMBIAN32__
+EXPORT_C
+#endif
+
+void
+gst_child_proxy_child_added (GstObject * object, GstObject * child)
+{
+  g_signal_emit (G_OBJECT (object), signals[CHILD_ADDED], 0, child);
+}
+
+/**
+ * gst_child_proxy_child_removed:
+ * @object: the parent object
+ * @child: the newly added child
+ *
+ * Emits the "child-removed" signal.
+ */
+#ifdef __SYMBIAN32__
+EXPORT_C
+#endif
+
+void
+gst_child_proxy_child_removed (GstObject * object, GstObject * child)
+{
+  g_signal_emit (G_OBJECT (object), signals[CHILD_REMOVED], 0, child);
+}
+
+/* gobject methods */
+
+static void
+gst_child_proxy_base_init (gpointer g_class)
+{
+  static gboolean initialized = FALSE;
+
+  if (!initialized) {
+    /* create interface signals and properties here. */
+        /**
+	 * GstChildProxy::child-added:
+	 * @child_proxy: the #GstChildProxy
+	 * @object: the #GObject that was added
+	 *
+	 * Will be emitted after the @object was added to the @child_proxy.
+	 */
+    signals[CHILD_ADDED] =
+        g_signal_new ("child-added", G_TYPE_FROM_CLASS (g_class),
+        G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET (GstChildProxyInterface,
+            child_added), NULL, NULL, gst_marshal_VOID__OBJECT, G_TYPE_NONE, 1,
+        G_TYPE_OBJECT);
+
+        /**
+	 * GstChildProxy::child-removed:
+	 * @child_proxy: the #GstChildProxy
+	 * @object: the #GObject that was removed
+	 *
+	 * Will be emitted after the @object was removed from the @child_proxy.
+	 */
+    signals[CHILD_REMOVED] =
+        g_signal_new ("child-removed", G_TYPE_FROM_CLASS (g_class),
+        G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET (GstChildProxyInterface,
+            child_removed), NULL, NULL, gst_marshal_VOID__OBJECT, G_TYPE_NONE,
+        1, G_TYPE_OBJECT);
+
+    initialized = TRUE;
+  }
+}
+#ifdef __SYMBIAN32__
+EXPORT_C
+#endif
+
+
+GType
+gst_child_proxy_get_type (void)
+{
+  static GType type = 0;
+
+  if (G_UNLIKELY (type == 0)) {
+    static const GTypeInfo info = {
+      sizeof (GstChildProxyInterface),
+      gst_child_proxy_base_init,        /* base_init */
+      NULL,                     /* base_finalize */
+      NULL,                     /* class_init */
+      NULL,                     /* class_finalize */
+      NULL,                     /* class_data */
+      0,
+      0,                        /* n_preallocs */
+      NULL                      /* instance_init */
+    };
+    type = g_type_register_static (G_TYPE_INTERFACE, "GstChildProxy", &info, 0);
+
+    g_type_interface_add_prerequisite (type, GST_TYPE_OBJECT);
+  }
+  return type;
+}