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