childproxy: make get_child_by_name virtual
[platform/upstream/gstreamer.git] / gst / gstchildproxy.c
1 /* GStreamer
2  * Copyright (C) 2005 Stefan Kost <ensonic@users.sf.net>
3  *
4  * gstchildproxy.c: interface for multi child elements
5  *
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.
10  *
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.
15  *
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., 59 Temple Place - Suite 330,
19  * Boston, MA 02111-1307, USA.
20  */
21
22 /**
23  * SECTION:gstchildproxy
24  * @short_description: Interface for multi child elements.
25  * @see_also: #GstBin
26  *
27  * This interface abstracts handling of property sets for elements with
28  * children. Imagine elements such as mixers or polyphonic generators. They all
29  * have multiple #GstPad or some kind of voice objects. Another use case are
30  * container elements like #GstBin.
31  * The element implementing the interface acts as a parent for those child
32  * objects.
33  *
34  * By implementing this interface the child properties can be accessed from the
35  * parent element by using gst_child_proxy_get() and gst_child_proxy_set().
36  *
37  * Property names are written as "child-name::property-name". The whole naming
38  * scheme is recursive. Thus "child1::child2::property" is valid too, if
39  * "child1" and "child2" implement the #GstChildProxy interface.
40  */
41 /* FIXME-0.11:
42  * it would be nice to use GObject instead of GstObject.
43  * This change would allow to propose the interface for inclusion with
44  * glib/gobject. IMHO this is useful for GtkContainer and compound widgets too.
45  */
46
47 #include "gst_private.h"
48
49 #include "gstchildproxy.h"
50 #include <gobject/gvaluecollector.h>
51
52 /* signals */
53 enum
54 {
55   CHILD_ADDED,
56   CHILD_REMOVED,
57   LAST_SIGNAL
58 };
59
60 static guint signals[LAST_SIGNAL] = { 0 };
61
62 static GstObject *
63 gst_child_proxy_default_get_child_by_name (GstChildProxy * parent,
64     const gchar * name)
65 {
66   guint count, i;
67   GstObject *object, *result;
68   gchar *object_name;
69
70   g_return_val_if_fail (GST_IS_CHILD_PROXY (parent), NULL);
71   g_return_val_if_fail (name != NULL, NULL);
72
73   result = NULL;
74
75   count = gst_child_proxy_get_children_count (parent);
76   for (i = 0; i < count; i++) {
77     gboolean eq;
78
79     if (!(object = gst_child_proxy_get_child_by_index (parent, i)))
80       continue;
81
82     object_name = gst_object_get_name (object);
83     if (object_name == NULL) {
84       g_warning ("child %u of parent %s has no name", i,
85           GST_OBJECT_NAME (parent));
86       goto next;
87     }
88     eq = g_str_equal (object_name, name);
89     g_free (object_name);
90
91     if (eq) {
92       result = object;
93       break;
94     }
95   next:
96     gst_object_unref (object);
97   }
98   return result;
99 }
100
101
102 /**
103  * gst_child_proxy_get_child_by_name:
104  * @parent: the parent object to get the child from
105  * @name: the childs name
106  *
107  * Looks up a child element by the given name.
108  *
109  * Implementors can use #GstObject together with gst_object_get_name()
110  *
111  * Returns: (transfer full): the child object or %NULL if not found. Unref
112  *     after usage.
113  *
114  * MT safe.
115  */
116 GstObject *
117 gst_child_proxy_get_child_by_name (GstChildProxy * parent, const gchar * name)
118 {
119   g_return_val_if_fail (GST_IS_CHILD_PROXY (parent), 0);
120
121   return (GST_CHILD_PROXY_GET_INTERFACE (parent)->get_child_by_name (parent,
122           name));
123 }
124
125 /**
126  * gst_child_proxy_get_child_by_index:
127  * @parent: the parent object to get the child from
128  * @index: the childs position in the child list
129  *
130  * Fetches a child by its number.
131  *
132  * Returns: (transfer full): the child object or %NULL if not found (index
133  *     too high). Unref after usage.
134  *
135  * MT safe.
136  */
137 GstObject *
138 gst_child_proxy_get_child_by_index (GstChildProxy * parent, guint index)
139 {
140   g_return_val_if_fail (GST_IS_CHILD_PROXY (parent), NULL);
141
142   return (GST_CHILD_PROXY_GET_INTERFACE (parent)->get_child_by_index (parent,
143           index));
144 }
145
146 /**
147  * gst_child_proxy_get_children_count:
148  * @parent: the parent object
149  *
150  * Gets the number of child objects this parent contains.
151  *
152  * Returns: the number of child objects
153  *
154  * MT safe.
155  */
156 guint
157 gst_child_proxy_get_children_count (GstChildProxy * parent)
158 {
159   g_return_val_if_fail (GST_IS_CHILD_PROXY (parent), 0);
160
161   return (GST_CHILD_PROXY_GET_INTERFACE (parent)->get_children_count (parent));
162 }
163
164 /**
165  * gst_child_proxy_lookup:
166  * @object: object to lookup the property in
167  * @name: name of the property to look up
168  * @target: (out) (allow-none) (transfer full): pointer to a #GstObject that
169  *     takes the real object to set property on
170  * @pspec: (out) (allow-none) (transfer full): pointer to take the #GParamSpec
171  *     describing the property
172  *
173  * Looks up which object and #GParamSpec would be effected by the given @name.
174  *
175  * Returns: TRUE if @target and @pspec could be found. FALSE otherwise. In that
176  * case the values for @pspec and @target are not modified. Unref @target after
177  * usage.
178  *
179  * MT safe.
180  */
181 gboolean
182 gst_child_proxy_lookup (GstObject * object, const gchar * name,
183     GstObject ** target, GParamSpec ** pspec)
184 {
185   gboolean res = FALSE;
186   gchar **names, **current;
187
188   g_return_val_if_fail (GST_IS_OBJECT (object), FALSE);
189   g_return_val_if_fail (name != NULL, FALSE);
190
191   gst_object_ref (object);
192
193   current = names = g_strsplit (name, "::", -1);
194   while (current[1]) {
195     GstObject *next;
196
197     if (!GST_IS_CHILD_PROXY (object)) {
198       GST_INFO
199           ("object %s is not a parent, so you cannot request a child by name %s",
200           GST_OBJECT_NAME (object), current[0]);
201       break;
202     }
203     next = gst_child_proxy_get_child_by_name (GST_CHILD_PROXY (object),
204         current[0]);
205     if (!next) {
206       GST_INFO ("no such object %s", current[0]);
207       break;
208     }
209     gst_object_unref (object);
210     object = next;
211     current++;
212   }
213   if (current[1] == NULL) {
214     GParamSpec *spec =
215         g_object_class_find_property (G_OBJECT_GET_CLASS (object), current[0]);
216     if (spec == NULL) {
217       GST_INFO ("no param spec named %s", current[0]);
218     } else {
219       if (pspec)
220         *pspec = spec;
221       if (target) {
222         gst_object_ref (object);
223         *target = object;
224       }
225       res = TRUE;
226     }
227   }
228   gst_object_unref (object);
229   g_strfreev (names);
230   return res;
231 }
232
233 /**
234  * gst_child_proxy_get_property:
235  * @object: object to query
236  * @name: name of the property
237  * @value: (out caller-allocates): a #GValue that should take the result.
238  *
239  * Gets a single property using the GstChildProxy mechanism.
240  * You are responsible for freeing it by calling g_value_unset()
241  */
242 void
243 gst_child_proxy_get_property (GstObject * object, const gchar * name,
244     GValue * value)
245 {
246   GParamSpec *pspec;
247   GstObject *target;
248
249   g_return_if_fail (GST_IS_OBJECT (object));
250   g_return_if_fail (name != NULL);
251   g_return_if_fail (G_IS_VALUE (value));
252
253   if (!gst_child_proxy_lookup (object, name, &target, &pspec))
254     goto not_found;
255
256   g_object_get_property (G_OBJECT (target), pspec->name, value);
257   gst_object_unref (target);
258
259   return;
260
261 not_found:
262   {
263     g_warning ("no property %s in object %s", name, GST_OBJECT_NAME (object));
264     return;
265   }
266 }
267
268 /**
269  * gst_child_proxy_get_valist:
270  * @object: the object to query
271  * @first_property_name: name of the first property to get
272  * @var_args: return location for the first property, followed optionally by more name/return location pairs, followed by NULL
273  *
274  * Gets properties of the parent object and its children.
275  */
276 void
277 gst_child_proxy_get_valist (GstObject * object,
278     const gchar * first_property_name, va_list var_args)
279 {
280   const gchar *name;
281   gchar *error = NULL;
282   GValue value = { 0, };
283   GParamSpec *pspec;
284   GstObject *target;
285
286   g_return_if_fail (G_IS_OBJECT (object));
287
288   name = first_property_name;
289
290   /* iterate over pairs */
291   while (name) {
292     if (!gst_child_proxy_lookup (object, name, &target, &pspec))
293       goto not_found;
294
295     g_value_init (&value, pspec->value_type);
296     g_object_get_property (G_OBJECT (target), pspec->name, &value);
297     gst_object_unref (target);
298
299     G_VALUE_LCOPY (&value, var_args, 0, &error);
300     if (error)
301       goto cant_copy;
302     g_value_unset (&value);
303     name = va_arg (var_args, gchar *);
304   }
305   return;
306
307 not_found:
308   {
309     g_warning ("no property %s in object %s", name, GST_OBJECT_NAME (object));
310     return;
311   }
312 cant_copy:
313   {
314     g_warning ("error copying value %s in object %s: %s", pspec->name,
315         GST_OBJECT_NAME (object), error);
316     g_value_unset (&value);
317     return;
318   }
319 }
320
321 /**
322  * gst_child_proxy_get:
323  * @object: the parent object
324  * @first_property_name: name of the first property to get
325  * @...: return location for the first property, followed optionally by more name/return location pairs, followed by NULL
326  *
327  * Gets properties of the parent object and its children.
328  */
329 void
330 gst_child_proxy_get (GstObject * object, const gchar * first_property_name, ...)
331 {
332   va_list var_args;
333
334   g_return_if_fail (GST_IS_OBJECT (object));
335
336   va_start (var_args, first_property_name);
337   gst_child_proxy_get_valist (object, first_property_name, var_args);
338   va_end (var_args);
339 }
340
341 /**
342  * gst_child_proxy_set_property:
343  * @object: the parent object
344  * @name: name of the property to set
345  * @value: new #GValue for the property
346  *
347  * Sets a single property using the GstChildProxy mechanism.
348  */
349 void
350 gst_child_proxy_set_property (GstObject * object, const gchar * name,
351     const GValue * value)
352 {
353   GParamSpec *pspec;
354   GstObject *target;
355
356   g_return_if_fail (GST_IS_OBJECT (object));
357   g_return_if_fail (name != NULL);
358   g_return_if_fail (G_IS_VALUE (value));
359
360   if (!gst_child_proxy_lookup (object, name, &target, &pspec))
361     goto not_found;
362
363   g_object_set_property (G_OBJECT (target), pspec->name, value);
364   gst_object_unref (target);
365   return;
366
367 not_found:
368   {
369     g_warning ("cannot set property %s on object %s", name,
370         GST_OBJECT_NAME (object));
371     return;
372   }
373 }
374
375 /**
376  * gst_child_proxy_set_valist:
377  * @object: the parent object
378  * @first_property_name: name of the first property to set
379  * @var_args: value for the first property, followed optionally by more name/value pairs, followed by NULL
380  *
381  * Sets properties of the parent object and its children.
382  */
383 void
384 gst_child_proxy_set_valist (GstObject * object,
385     const gchar * first_property_name, va_list var_args)
386 {
387   const gchar *name;
388   gchar *error = NULL;
389   GValue value = { 0, };
390   GParamSpec *pspec;
391   GstObject *target;
392
393   g_return_if_fail (G_IS_OBJECT (object));
394
395   name = first_property_name;
396
397   /* iterate over pairs */
398   while (name) {
399     if (!gst_child_proxy_lookup (object, name, &target, &pspec))
400       goto not_found;
401
402     G_VALUE_COLLECT_INIT (&value, pspec->value_type, var_args,
403         G_VALUE_NOCOPY_CONTENTS, &error);
404
405     if (error)
406       goto cant_copy;
407
408     g_object_set_property (G_OBJECT (target), pspec->name, &value);
409     gst_object_unref (target);
410
411     g_value_unset (&value);
412     name = va_arg (var_args, gchar *);
413   }
414   return;
415
416 not_found:
417   {
418     g_warning ("no property %s in object %s", name, GST_OBJECT_NAME (object));
419     return;
420   }
421 cant_copy:
422   {
423     g_warning ("error copying value %s in object %s: %s", pspec->name,
424         GST_OBJECT_NAME (object), error);
425     g_value_unset (&value);
426     gst_object_unref (target);
427     return;
428   }
429 }
430
431 /**
432  * gst_child_proxy_set:
433  * @object: the parent object
434  * @first_property_name: name of the first property to set
435  * @...: value for the first property, followed optionally by more name/value pairs, followed by NULL
436  *
437  * Sets properties of the parent object and its children.
438  */
439 void
440 gst_child_proxy_set (GstObject * object, const gchar * first_property_name, ...)
441 {
442   va_list var_args;
443
444   g_return_if_fail (GST_IS_OBJECT (object));
445
446   va_start (var_args, first_property_name);
447   gst_child_proxy_set_valist (object, first_property_name, var_args);
448   va_end (var_args);
449 }
450
451 /**
452  * gst_child_proxy_child_added:
453  * @object: the parent object
454  * @child: the newly added child
455  *
456  * Emits the "child-added" signal.
457  */
458 void
459 gst_child_proxy_child_added (GstObject * object, GstObject * child)
460 {
461   g_signal_emit (G_OBJECT (object), signals[CHILD_ADDED], 0, child);
462 }
463
464 /**
465  * gst_child_proxy_child_removed:
466  * @object: the parent object
467  * @child: the removed child
468  *
469  * Emits the "child-removed" signal.
470  */
471 void
472 gst_child_proxy_child_removed (GstObject * object, GstObject * child)
473 {
474   g_signal_emit (G_OBJECT (object), signals[CHILD_REMOVED], 0, child);
475 }
476
477 /* gobject methods */
478
479 static void
480 gst_child_proxy_class_init (gpointer g_class, gpointer class_data)
481 {
482   GstChildProxyInterface *iface = (GstChildProxyInterface *) g_class;
483
484   iface->get_child_by_name = gst_child_proxy_default_get_child_by_name;
485 }
486
487 static void
488 gst_child_proxy_base_init (gpointer g_class)
489 {
490   static gboolean initialized = FALSE;
491
492   if (!initialized) {
493     /* create interface signals and properties here. */
494     /**
495      * GstChildProxy::child-added:
496      * @child_proxy: the #GstChildProxy
497      * @object: the #GObject that was added
498      *
499      * Will be emitted after the @object was added to the @child_proxy.
500      */
501     signals[CHILD_ADDED] =
502         g_signal_new ("child-added", G_TYPE_FROM_CLASS (g_class),
503         G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET (GstChildProxyInterface,
504             child_added), NULL, NULL, g_cclosure_marshal_generic, G_TYPE_NONE,
505         1, GST_TYPE_OBJECT);
506
507     /**
508      * GstChildProxy::child-removed:
509      * @child_proxy: the #GstChildProxy
510      * @object: the #GObject that was removed
511      *
512      * Will be emitted after the @object was removed from the @child_proxy.
513      */
514     signals[CHILD_REMOVED] =
515         g_signal_new ("child-removed", G_TYPE_FROM_CLASS (g_class),
516         G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET (GstChildProxyInterface,
517             child_removed), NULL, NULL, g_cclosure_marshal_generic, G_TYPE_NONE,
518         1, GST_TYPE_OBJECT);
519
520     initialized = TRUE;
521   }
522 }
523
524 GType
525 gst_child_proxy_get_type (void)
526 {
527   static volatile gsize type = 0;
528
529   if (g_once_init_enter (&type)) {
530     GType _type;
531     static const GTypeInfo info = {
532       sizeof (GstChildProxyInterface),
533       gst_child_proxy_base_init,        /* base_init */
534       NULL,                     /* base_finalize */
535       gst_child_proxy_class_init,       /* class_init */
536       NULL,                     /* class_finalize */
537       NULL,                     /* class_data */
538       0,
539       0,                        /* n_preallocs */
540       NULL                      /* instance_init */
541     };
542
543     _type =
544         g_type_register_static (G_TYPE_INTERFACE, "GstChildProxy", &info, 0);
545
546     g_type_interface_add_prerequisite (_type, GST_TYPE_OBJECT);
547     g_once_init_leave (&type, (gsize) _type);
548   }
549   return type;
550 }