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