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