diff -r 000000000000 -r 0e761a78d257 gstreamer_core/gst/gstchildproxy.c --- /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 + * + * 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 + +/* 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; +}