2 * Copyright (C) 2005 Stefan Kost <ensonic@users.sf.net>
4 * gstchildproxy.c: interface for multi child elements
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Library General Public
8 * License as published by the Free Software Foundation; either
9 * version 2 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Library General Public License for more details.
16 * You should have received a copy of the GNU Library General Public
17 * License along with this library; if not, write to the
18 * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
19 * Boston, MA 02110-1301, USA.
23 * SECTION:gstchildproxy
24 * @title: GstChildProxy
25 * @short_description: Interface for multi child elements.
28 * This interface abstracts handling of property sets for elements with
29 * children. Imagine elements such as mixers or polyphonic generators. They all
30 * have multiple #GstPad or some kind of voice objects. Another use case are
31 * container elements like #GstBin.
32 * The element implementing the interface acts as a parent for those child
35 * By implementing this interface the child properties can be accessed from the
36 * parent element by using gst_child_proxy_get() and gst_child_proxy_set().
38 * Property names are written as `child-name::property-name`. The whole naming
39 * scheme is recursive. Thus `child1::child2::property` is valid too, if
40 * `child1` and `child2` implement the #GstChildProxy interface.
43 #include "gst_private.h"
45 #include "gstchildproxy.h"
46 #include <gobject/gvaluecollector.h>
56 static guint signals[LAST_SIGNAL] = { 0 };
59 * gst_child_proxy_get_child_by_name_recurse:
60 * @parent: the parent object to get the child from
61 * @name: the full-path child's name
63 * Looks up a child element by the given full-path name.
65 * Similar to gst_child_proxy_get_child_by_name(), this method
66 * searches and returns a child given a name. The difference is that
67 * this method allows a hierarchical path in the form of
68 * child1::child2::child3. In the later example this method would
69 * return a reference to child3, if found. The name should be made of
70 * element names only and should not contain any property names.
72 * Returns: (transfer full) (nullable): the child object or %NULL if
78 gst_child_proxy_get_child_by_name_recurse (GstChildProxy * child_proxy,
81 gchar **names = NULL, **current = NULL;
82 GObject *obj = NULL, *next = NULL;
84 g_return_val_if_fail (child_proxy != NULL, NULL);
85 g_return_val_if_fail (name != NULL, NULL);
87 current = names = g_strsplit (name, "::", -1);
89 obj = G_OBJECT (g_object_ref (child_proxy));
92 /* walk through the children hierarchy until the requested one is found */
95 /* Cannot ask for the child of a non-childproxy */
96 if (!GST_IS_CHILD_PROXY (obj)) {
97 gst_object_unref (obj);
102 next = gst_child_proxy_get_child_by_name (GST_CHILD_PROXY (obj),
104 gst_object_unref (obj);
106 /* The child does not exist */
108 GST_INFO ("Unable to find child %s", current[0]);
122 gst_child_proxy_default_get_child_by_name (GstChildProxy * parent,
126 GObject *object, *result;
129 g_return_val_if_fail (GST_IS_CHILD_PROXY (parent), NULL);
130 g_return_val_if_fail (name != NULL, NULL);
134 count = gst_child_proxy_get_children_count (parent);
135 for (i = 0; i < count; i++) {
138 if (!(object = gst_child_proxy_get_child_by_index (parent, i)))
141 if (!GST_IS_OBJECT (object)) {
144 object_name = gst_object_get_name (GST_OBJECT_CAST (object));
145 if (object_name == NULL) {
146 g_warning ("child %u of parent %s has no name", i,
147 GST_OBJECT_NAME (parent));
150 eq = g_str_equal (object_name, name);
151 g_free (object_name);
158 gst_object_unref (object);
165 * gst_child_proxy_get_child_by_name:
166 * @parent: the parent object to get the child from
167 * @name: the child's name
169 * Looks up a child element by the given name.
171 * This virtual method has a default implementation that uses #GstObject
172 * together with gst_object_get_name(). If the interface is to be used with
173 * #GObjects, this methods needs to be overridden.
175 * Returns: (transfer full) (nullable): the child object or %NULL if
179 gst_child_proxy_get_child_by_name (GstChildProxy * parent, const gchar * name)
181 GstChildProxyInterface *iface;
183 g_return_val_if_fail (GST_IS_CHILD_PROXY (parent), 0);
185 iface = GST_CHILD_PROXY_GET_INTERFACE (parent);
187 if (iface->get_child_by_name != NULL)
188 return iface->get_child_by_name (parent, name);
194 * gst_child_proxy_get_child_by_index:
195 * @parent: the parent object to get the child from
196 * @index: the child's position in the child list
198 * Fetches a child by its number.
200 * Returns: (transfer full) (nullable): the child object or %NULL if
201 * not found (index too high).
204 gst_child_proxy_get_child_by_index (GstChildProxy * parent, guint index)
206 GstChildProxyInterface *iface;
208 g_return_val_if_fail (GST_IS_CHILD_PROXY (parent), NULL);
210 iface = GST_CHILD_PROXY_GET_INTERFACE (parent);
212 if (iface->get_child_by_index != NULL)
213 return iface->get_child_by_index (parent, index);
219 * gst_child_proxy_get_children_count:
220 * @parent: the parent object
222 * Gets the number of child objects this parent contains.
224 * Returns: the number of child objects
227 gst_child_proxy_get_children_count (GstChildProxy * parent)
229 GstChildProxyInterface *iface;
231 g_return_val_if_fail (GST_IS_CHILD_PROXY (parent), 0);
233 iface = GST_CHILD_PROXY_GET_INTERFACE (parent);
235 if (iface->get_children_count != NULL)
236 return iface->get_children_count (parent);
242 * gst_child_proxy_lookup:
243 * @object: child proxy object to lookup the property in
244 * @name: name of the property to look up
245 * @target: (out) (allow-none) (transfer full): pointer to a #GObject that
246 * takes the real object to set property on
247 * @pspec: (out) (allow-none) (transfer none): pointer to take the #GParamSpec
248 * describing the property
250 * Looks up which object and #GParamSpec would be effected by the given @name.
252 * Returns: %TRUE if @target and @pspec could be found. %FALSE otherwise. In that
253 * case the values for @pspec and @target are not modified. Unref @target after
254 * usage. For plain #GObject @target is the same as @object.
257 gst_child_proxy_lookup (GstChildProxy * object, const gchar * name,
258 GObject ** target, GParamSpec ** pspec)
260 gboolean res = FALSE;
261 gchar *children = NULL;
262 const gchar *prop = NULL;
263 GObject *child = NULL;
264 static const gchar *separator = "::";
266 g_return_val_if_fail (GST_IS_CHILD_PROXY (object), FALSE);
267 g_return_val_if_fail (name != NULL, FALSE);
269 /* If the requested name does not include a "::" then it is not a
270 child proxy path, but the property name directly */
271 prop = g_strrstr (name, separator);
273 child = gst_object_ref (G_OBJECT (object));
279 /* Extract "child1::child2" from "child1::child2::prop" */
280 children = g_strndup (name, prop - name - 2);
281 GST_INFO ("Looking for property %s in %s", prop, children);
283 child = gst_child_proxy_get_child_by_name_recurse (object, children);
287 GST_INFO ("Child not found");
293 g_object_class_find_property (G_OBJECT_GET_CLASS (child), prop);
295 GST_INFO ("no param spec named %s", prop);
300 g_object_ref (child);
305 gst_object_unref (child);
312 * gst_child_proxy_get_property:
313 * @object: object to query
314 * @name: name of the property
315 * @value: (out caller-allocates): a #GValue that should take the result.
317 * Gets a single property using the GstChildProxy mechanism.
318 * You are responsible for freeing it by calling g_value_unset()
321 gst_child_proxy_get_property (GstChildProxy * object, const gchar * name,
327 g_return_if_fail (GST_IS_CHILD_PROXY (object));
328 g_return_if_fail (name != NULL);
329 g_return_if_fail (value != NULL);
331 if (!gst_child_proxy_lookup (object, name, &target, &pspec))
334 if (!G_IS_VALUE (value)) {
335 g_value_init (value, pspec->value_type);
338 g_object_get_property (target, pspec->name, value);
339 gst_object_unref (target);
345 g_warning ("no property %s in object %s", name,
346 (GST_IS_OBJECT (object) ? GST_OBJECT_NAME (object) : ""));
352 * gst_child_proxy_get_valist:
353 * @object: the object to query
354 * @first_property_name: name of the first property to get
355 * @var_args: return location for the first property, followed optionally by more name/return location pairs, followed by %NULL
357 * Gets properties of the parent object and its children.
360 gst_child_proxy_get_valist (GstChildProxy * object,
361 const gchar * first_property_name, va_list var_args)
365 GValue value = { 0, };
369 g_return_if_fail (GST_IS_CHILD_PROXY (object));
371 name = first_property_name;
373 /* iterate over pairs */
375 if (!gst_child_proxy_lookup (object, name, &target, &pspec))
378 g_value_init (&value, pspec->value_type);
379 g_object_get_property (target, pspec->name, &value);
380 gst_object_unref (target);
382 G_VALUE_LCOPY (&value, var_args, 0, &error);
385 g_value_unset (&value);
386 name = va_arg (var_args, gchar *);
392 g_warning ("no property %s in object %s", name,
393 (GST_IS_OBJECT (object) ? GST_OBJECT_NAME (object) : ""));
398 g_warning ("error copying value %s in object %s: %s", pspec->name,
399 (GST_IS_OBJECT (object) ? GST_OBJECT_NAME (object) : ""), error);
400 g_value_unset (&value);
406 * gst_child_proxy_get:
407 * @object: the parent object
408 * @first_property_name: name of the first property to get
409 * @...: return location for the first property, followed optionally by more name/return location pairs, followed by %NULL
411 * Gets properties of the parent object and its children.
414 gst_child_proxy_get (GstChildProxy * object, const gchar * first_property_name,
419 g_return_if_fail (GST_IS_CHILD_PROXY (object));
421 va_start (var_args, first_property_name);
422 gst_child_proxy_get_valist (object, first_property_name, var_args);
427 * gst_child_proxy_set_property:
428 * @object: the parent object
429 * @name: name of the property to set
430 * @value: new #GValue for the property
432 * Sets a single property using the GstChildProxy mechanism.
435 gst_child_proxy_set_property (GstChildProxy * object, const gchar * name,
436 const GValue * value)
441 g_return_if_fail (GST_IS_CHILD_PROXY (object));
442 g_return_if_fail (name != NULL);
443 g_return_if_fail (G_IS_VALUE (value));
445 if (!gst_child_proxy_lookup (object, name, &target, &pspec))
448 g_object_set_property (target, pspec->name, value);
449 gst_object_unref (target);
454 g_warning ("cannot set property %s on object %s", name,
455 (GST_IS_OBJECT (object) ? GST_OBJECT_NAME (object) : ""));
461 * gst_child_proxy_set_valist:
462 * @object: the parent object
463 * @first_property_name: name of the first property to set
464 * @var_args: value for the first property, followed optionally by more name/value pairs, followed by %NULL
466 * Sets properties of the parent object and its children.
469 gst_child_proxy_set_valist (GstChildProxy * object,
470 const gchar * first_property_name, va_list var_args)
474 GValue value = { 0, };
478 g_return_if_fail (GST_IS_CHILD_PROXY (object));
480 name = first_property_name;
482 /* iterate over pairs */
484 if (!gst_child_proxy_lookup (object, name, &target, &pspec))
487 G_VALUE_COLLECT_INIT (&value, pspec->value_type, var_args,
488 G_VALUE_NOCOPY_CONTENTS, &error);
493 g_object_set_property (target, pspec->name, &value);
494 gst_object_unref (target);
496 g_value_unset (&value);
497 name = va_arg (var_args, gchar *);
503 g_warning ("no property %s in object %s", name,
504 (GST_IS_OBJECT (object) ? GST_OBJECT_NAME (object) : ""));
509 g_warning ("error copying value %s in object %s: %s", pspec->name,
510 (GST_IS_OBJECT (object) ? GST_OBJECT_NAME (object) : ""), error);
511 g_value_unset (&value);
512 gst_object_unref (target);
519 * gst_child_proxy_set:
520 * @object: the parent object
521 * @first_property_name: name of the first property to set
522 * @...: value for the first property, followed optionally by more name/value pairs, followed by %NULL
524 * Sets properties of the parent object and its children.
527 gst_child_proxy_set (GstChildProxy * object, const gchar * first_property_name,
532 g_return_if_fail (GST_IS_CHILD_PROXY (object));
534 va_start (var_args, first_property_name);
535 gst_child_proxy_set_valist (object, first_property_name, var_args);
540 * gst_child_proxy_child_added:
541 * @parent: the parent object
542 * @child: the newly added child
543 * @name: the name of the new child
545 * Emits the #GstChildProxy::child-added signal.
548 gst_child_proxy_child_added (GstChildProxy * parent, GObject * child,
551 g_signal_emit (parent, signals[CHILD_ADDED], 0, child, name);
555 * gst_child_proxy_child_removed:
556 * @parent: the parent object
557 * @child: the removed child
558 * @name: the name of the old child
560 * Emits the #GstChildProxy::child-removed signal.
563 gst_child_proxy_child_removed (GstChildProxy * parent, GObject * child,
566 g_signal_emit (parent, signals[CHILD_REMOVED], 0, child, name);
569 /* gobject methods */
572 gst_child_proxy_class_init (gpointer g_class, gpointer class_data)
574 GstChildProxyInterface *iface = (GstChildProxyInterface *) g_class;
576 iface->get_child_by_name = gst_child_proxy_default_get_child_by_name;
580 gst_child_proxy_base_init (gpointer g_class)
582 static gboolean initialized = FALSE;
585 /* create interface signals and properties here. */
587 * GstChildProxy::child-added:
588 * @self: the #GstChildProxy
589 * @object: the #GObject that was added
590 * @name: the name of the new child
592 * Will be emitted after the @object was added to the @child_proxy.
594 signals[CHILD_ADDED] =
595 g_signal_new ("child-added", G_TYPE_FROM_CLASS (g_class),
596 G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET (GstChildProxyInterface,
597 child_added), NULL, NULL, NULL, G_TYPE_NONE,
598 2, G_TYPE_OBJECT, G_TYPE_STRING);
601 * GstChildProxy::child-removed:
602 * @self: the #GstChildProxy
603 * @object: the #GObject that was removed
604 * @name: the name of the old child
606 * Will be emitted after the @object was removed from the @child_proxy.
608 signals[CHILD_REMOVED] =
609 g_signal_new ("child-removed", G_TYPE_FROM_CLASS (g_class),
610 G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET (GstChildProxyInterface,
611 child_removed), NULL, NULL, NULL, G_TYPE_NONE,
612 2, G_TYPE_OBJECT, G_TYPE_STRING);
619 gst_child_proxy_get_type (void)
621 static gsize type = 0;
623 if (g_once_init_enter (&type)) {
625 static const GTypeInfo info = {
626 sizeof (GstChildProxyInterface),
627 gst_child_proxy_base_init, /* base_init */
628 NULL, /* base_finalize */
629 gst_child_proxy_class_init, /* class_init */
630 NULL, /* class_finalize */
631 NULL, /* class_data */
634 NULL /* instance_init */
638 g_type_register_static (G_TYPE_INTERFACE, "GstChildProxy", &info, 0);
640 g_type_interface_add_prerequisite (_type, G_TYPE_OBJECT);
641 g_once_init_leave (&type, (gsize) _type);