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