gst/gstchildproxy.c: Add FIXMEs for 0.11 to use GST_TYPE_OBJECT in the signature...
[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: the child object or %NULL if not found. Unref 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: the child object or %NULL if not found (index too high). Unref
124  * 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: pointer to a #GstObject that takes the real object to set property on
160  * @pspec: pointer to take the #GParamSpec describing the property
161  *
162  * Looks up which object and #GParamSpec would be effected by the given @name.
163  *
164  * Returns: TRUE if @target and @pspec could be found. FALSE otherwise. In that
165  * case the values for @pspec and @target are not modified. Unref @target after
166  * usage.
167  *
168  * MT safe.
169  */
170 gboolean
171 gst_child_proxy_lookup (GstObject * object, const gchar * name,
172     GstObject ** target, GParamSpec ** pspec)
173 {
174   gboolean res = FALSE;
175   gchar **names, **current;
176
177   g_return_val_if_fail (GST_IS_OBJECT (object), FALSE);
178   g_return_val_if_fail (name != NULL, FALSE);
179
180   gst_object_ref (object);
181
182   current = names = g_strsplit (name, "::", -1);
183   while (current[1]) {
184     GstObject *next;
185
186     if (!GST_IS_CHILD_PROXY (object)) {
187       GST_INFO
188           ("object %s is not a parent, so you cannot request a child by name %s",
189           GST_OBJECT_NAME (object), current[0]);
190       break;
191     }
192     next = gst_child_proxy_get_child_by_name (GST_CHILD_PROXY (object),
193         current[0]);
194     if (!next) {
195       GST_INFO ("no such object %s", current[0]);
196       break;
197     }
198     gst_object_unref (object);
199     object = next;
200     current++;
201   };
202   if (current[1] == NULL) {
203     GParamSpec *spec =
204         g_object_class_find_property (G_OBJECT_GET_CLASS (object), current[0]);
205     if (spec == NULL) {
206       GST_INFO ("no param spec named %s", current[0]);
207     } else {
208       if (pspec)
209         *pspec = spec;
210       if (target) {
211         gst_object_ref (object);
212         *target = object;
213       }
214       res = TRUE;
215     }
216   }
217   gst_object_unref (object);
218   g_strfreev (names);
219   return res;
220 }
221
222 /**
223  * gst_child_proxy_get_property:
224  * @object: object to query
225  * @name: name of the property
226  * @value: a #GValue that should take the result.
227  *
228  * Gets a single property using the GstChildProxy mechanism.
229  * You are responsible for for freeing it by calling g_value_unset()
230  */
231 void
232 gst_child_proxy_get_property (GstObject * object, const gchar * name,
233     GValue * value)
234 {
235   GParamSpec *pspec;
236   GstObject *target;
237
238   g_return_if_fail (GST_IS_OBJECT (object));
239   g_return_if_fail (name != NULL);
240   g_return_if_fail (G_IS_VALUE (value));
241
242   if (!gst_child_proxy_lookup (object, name, &target, &pspec))
243     goto not_found;
244
245   g_object_get_property (G_OBJECT (target), pspec->name, value);
246   gst_object_unref (target);
247
248   return;
249
250 not_found:
251   {
252     g_warning ("cannot get property %s from object %s", name,
253         GST_OBJECT_NAME (object));
254     return;
255   }
256 }
257
258 /**
259  * gst_child_proxy_get_valist:
260  * @object: the object to query
261  * @first_property_name: name of the first property to get
262  * @var_args: return location for the first property, followed optionally by more name/return location pairs, followed by NULL
263  *
264  * Gets properties of the parent object and its children.
265  */
266 void
267 gst_child_proxy_get_valist (GstObject * object,
268     const gchar * first_property_name, va_list var_args)
269 {
270   const gchar *name;
271   gchar *error = NULL;
272   GValue value = { 0, };
273
274   g_return_if_fail (G_IS_OBJECT (object));
275
276   name = first_property_name;
277
278   /* iterate over pairs */
279   while (name) {
280     gst_child_proxy_get_property (object, name, &value);
281     G_VALUE_LCOPY (&value, var_args, 0, &error);
282     if (error) {
283       g_warning ("error copying value: %s", error);
284       return;
285     }
286     g_value_unset (&value);
287     name = va_arg (var_args, gchar *);
288   }
289 }
290
291 /**
292  * gst_child_proxy_get:
293  * @object: the parent object
294  * @first_property_name: name of the first property to get
295  * @...: return location for the first property, followed optionally by more name/return location pairs, followed by NULL
296  *
297  * Gets properties of the parent object and its children.
298  */
299 void
300 gst_child_proxy_get (GstObject * object, const gchar * first_property_name, ...)
301 {
302   va_list var_args;
303
304   g_return_if_fail (GST_IS_OBJECT (object));
305
306   va_start (var_args, first_property_name);
307   gst_child_proxy_get_valist (object, first_property_name, var_args);
308   va_end (var_args);
309 }
310
311 /**
312  * gst_child_proxy_set_property:
313  * @object: the parent object
314  * @name: name of the property to set
315  * @value: new #GValue for the property
316  *
317  * Sets a single property using the GstChildProxy mechanism.
318  */
319 void
320 gst_child_proxy_set_property (GstObject * object, const gchar * name,
321     const GValue * value)
322 {
323   GParamSpec *pspec;
324   GstObject *target;
325
326   g_return_if_fail (GST_IS_OBJECT (object));
327   g_return_if_fail (name != NULL);
328   g_return_if_fail (G_IS_VALUE (value));
329
330   if (!gst_child_proxy_lookup (object, name, &target, &pspec))
331     goto not_found;
332
333   g_object_set_property (G_OBJECT (target), pspec->name, value);
334   gst_object_unref (target);
335   return;
336
337 not_found:
338   {
339     g_warning ("cannot set property %s on object %s", name,
340         GST_OBJECT_NAME (object));
341     return;
342   }
343 }
344
345 /**
346  * gst_child_proxy_set_valist:
347  * @object: the parent object
348  * @first_property_name: name of the first property to set
349  * @var_args: value for the first property, followed optionally by more name/value pairs, followed by NULL
350  *
351  * Sets properties of the parent object and its children.
352  */
353 void
354 gst_child_proxy_set_valist (GstObject * object,
355     const gchar * first_property_name, va_list var_args)
356 {
357   const gchar *name;
358   gchar *error = NULL;
359   GValue value = { 0, };
360
361   g_return_if_fail (G_IS_OBJECT (object));
362
363   name = first_property_name;
364
365   /* iterate over pairs */
366   while (name) {
367     GParamSpec *pspec;
368     GstObject *target;
369
370     if (!gst_child_proxy_lookup (object, name, &target, &pspec)) {
371       g_warning ("no such property %s in object %s", name,
372           GST_OBJECT_NAME (object));
373       continue;
374     }
375     g_value_init (&value, G_PARAM_SPEC_VALUE_TYPE (pspec));
376     G_VALUE_COLLECT (&value, var_args, G_VALUE_NOCOPY_CONTENTS, &error);
377     if (error) {
378       g_warning ("error copying value: %s", error);
379       gst_object_unref (target);
380       return;
381     }
382     g_object_set_property (G_OBJECT (target), pspec->name, &value);
383     gst_object_unref (target);
384
385     g_value_unset (&value);
386     name = va_arg (var_args, gchar *);
387   }
388 }
389
390 /**
391  * gst_child_proxy_set:
392  * @object: the parent object
393  * @first_property_name: name of the first property to set
394  * @...: value for the first property, followed optionally by more name/value pairs, followed by NULL
395  *
396  * Sets properties of the parent object and its children.
397  */
398 void
399 gst_child_proxy_set (GstObject * object, const gchar * first_property_name, ...)
400 {
401   va_list var_args;
402
403   g_return_if_fail (GST_IS_OBJECT (object));
404
405   va_start (var_args, first_property_name);
406   gst_child_proxy_set_valist (object, first_property_name, var_args);
407   va_end (var_args);
408 }
409
410 /**
411  * gst_child_proxy_child_added:
412  * @object: the parent object
413  * @child: the newly added child
414  *
415  * Emits the "child-added" signal.
416  */
417 void
418 gst_child_proxy_child_added (GstObject * object, GstObject * child)
419 {
420   g_signal_emit (G_OBJECT (object), signals[CHILD_ADDED], 0, child);
421 }
422
423 /**
424  * gst_child_proxy_child_removed:
425  * @object: the parent object
426  * @child: the removed child
427  *
428  * Emits the "child-removed" signal.
429  */
430 void
431 gst_child_proxy_child_removed (GstObject * object, GstObject * child)
432 {
433   g_signal_emit (G_OBJECT (object), signals[CHILD_REMOVED], 0, child);
434 }
435
436 /* gobject methods */
437
438 static void
439 gst_child_proxy_base_init (gpointer g_class)
440 {
441   static gboolean initialized = FALSE;
442
443   if (!initialized) {
444     /* create interface signals and properties here. */
445         /**
446          * GstChildProxy::child-added:
447          * @child_proxy: the #GstChildProxy
448          * @object: the #GObject that was added
449          *
450          * Will be emitted after the @object was added to the @child_proxy.
451          */
452     /* FIXME 0.11: use GST_TYPE_OBJECT as GstChildProxy only
453      * supports GstObjects */
454     signals[CHILD_ADDED] =
455         g_signal_new ("child-added", G_TYPE_FROM_CLASS (g_class),
456         G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET (GstChildProxyInterface,
457             child_added), NULL, NULL, gst_marshal_VOID__OBJECT, G_TYPE_NONE, 1,
458         G_TYPE_OBJECT);
459
460         /**
461          * GstChildProxy::child-removed:
462          * @child_proxy: the #GstChildProxy
463          * @object: the #GObject that was removed
464          *
465          * Will be emitted after the @object was removed from the @child_proxy.
466          */
467     /* FIXME 0.11: use GST_TYPE_OBJECT as GstChildProxy only
468      * supports GstObjects */
469     signals[CHILD_REMOVED] =
470         g_signal_new ("child-removed", G_TYPE_FROM_CLASS (g_class),
471         G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET (GstChildProxyInterface,
472             child_removed), NULL, NULL, gst_marshal_VOID__OBJECT, G_TYPE_NONE,
473         1, G_TYPE_OBJECT);
474
475     initialized = TRUE;
476   }
477 }
478
479 GType
480 gst_child_proxy_get_type (void)
481 {
482   static GType type = 0;
483
484   if (G_UNLIKELY (type == 0)) {
485     static const GTypeInfo info = {
486       sizeof (GstChildProxyInterface),
487       gst_child_proxy_base_init,        /* base_init */
488       NULL,                     /* base_finalize */
489       NULL,                     /* class_init */
490       NULL,                     /* class_finalize */
491       NULL,                     /* class_data */
492       0,
493       0,                        /* n_preallocs */
494       NULL                      /* instance_init */
495     };
496     type = g_type_register_static (G_TYPE_INTERFACE, "GstChildProxy", &info, 0);
497
498     g_type_interface_add_prerequisite (type, GST_TYPE_OBJECT);
499   }
500   return type;
501 }