gstdevicemonitor: added cleanup of signal handlers and hidden providers list
[platform/upstream/gstreamer.git] / subprojects / gstreamer / 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     gst_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.
114  */
115 GObject *
116 gst_child_proxy_get_child_by_name (GstChildProxy * parent, const gchar * name)
117 {
118   GstChildProxyInterface *iface;
119
120   g_return_val_if_fail (GST_IS_CHILD_PROXY (parent), 0);
121
122   iface = GST_CHILD_PROXY_GET_INTERFACE (parent);
123
124   if (iface->get_child_by_name != NULL)
125     return iface->get_child_by_name (parent, name);
126
127   return NULL;
128 }
129
130 /**
131  * gst_child_proxy_get_child_by_index:
132  * @parent: the parent object to get the child from
133  * @index: the child's position in the child list
134  *
135  * Fetches a child by its number.
136  *
137  * Returns: (transfer full) (nullable): the child object or %NULL if
138  *     not found (index too high).
139  */
140 GObject *
141 gst_child_proxy_get_child_by_index (GstChildProxy * parent, guint index)
142 {
143   GstChildProxyInterface *iface;
144
145   g_return_val_if_fail (GST_IS_CHILD_PROXY (parent), NULL);
146
147   iface = GST_CHILD_PROXY_GET_INTERFACE (parent);
148
149   if (iface->get_child_by_index != NULL)
150     return iface->get_child_by_index (parent, index);
151
152   return NULL;
153 }
154
155 /**
156  * gst_child_proxy_get_children_count:
157  * @parent: the parent object
158  *
159  * Gets the number of child objects this parent contains.
160  *
161  * Returns: the number of child objects
162  */
163 guint
164 gst_child_proxy_get_children_count (GstChildProxy * parent)
165 {
166   GstChildProxyInterface *iface;
167
168   g_return_val_if_fail (GST_IS_CHILD_PROXY (parent), 0);
169
170   iface = GST_CHILD_PROXY_GET_INTERFACE (parent);
171
172   if (iface->get_children_count != NULL)
173     return iface->get_children_count (parent);
174
175   return 0;
176 }
177
178 /**
179  * gst_child_proxy_lookup:
180  * @object: child proxy object to lookup the property in
181  * @name: name of the property to look up
182  * @target: (out) (allow-none) (transfer full): pointer to a #GObject that
183  *     takes the real object to set property on
184  * @pspec: (out) (allow-none) (transfer none): pointer to take the #GParamSpec
185  *     describing the property
186  *
187  * Looks up which object and #GParamSpec would be effected by the given @name.
188  *
189  * Returns: %TRUE if @target and @pspec could be found. %FALSE otherwise. In that
190  * case the values for @pspec and @target are not modified. Unref @target after
191  * usage. For plain #GObject @target is the same as @object.
192  */
193 gboolean
194 gst_child_proxy_lookup (GstChildProxy * object, const gchar * name,
195     GObject ** target, GParamSpec ** pspec)
196 {
197   GObject *obj;
198   gboolean res = FALSE;
199   gchar **names, **current;
200
201   g_return_val_if_fail (GST_IS_CHILD_PROXY (object), FALSE);
202   g_return_val_if_fail (name != NULL, FALSE);
203
204   obj = G_OBJECT (g_object_ref (object));
205
206   current = names = g_strsplit (name, "::", -1);
207   /* find the owner of the property */
208   while (current[1]) {
209     GObject *next;
210
211     if (!GST_IS_CHILD_PROXY (obj)) {
212       GST_INFO
213           ("object %s is not a parent, so you cannot request a child by name %s",
214           (GST_IS_OBJECT (obj) ? GST_OBJECT_NAME (obj) : ""), current[0]);
215       break;
216     }
217     next = gst_child_proxy_get_child_by_name (GST_CHILD_PROXY (obj),
218         current[0]);
219     if (!next) {
220       GST_INFO ("no such object %s", current[0]);
221       break;
222     }
223     gst_object_unref (obj);
224     obj = next;
225     current++;
226   }
227
228   /* look for psec */
229   if (current[1] == NULL) {
230     GParamSpec *spec =
231         g_object_class_find_property (G_OBJECT_GET_CLASS (obj), current[0]);
232     if (spec == NULL) {
233       GST_INFO ("no param spec named %s", current[0]);
234     } else {
235       if (pspec)
236         *pspec = spec;
237       if (target) {
238         g_object_ref (obj);
239         *target = obj;
240       }
241       res = TRUE;
242     }
243   }
244   gst_object_unref (obj);
245   g_strfreev (names);
246   return res;
247 }
248
249 /**
250  * gst_child_proxy_get_property:
251  * @object: object to query
252  * @name: name of the property
253  * @value: (out caller-allocates): a #GValue that should take the result.
254  *
255  * Gets a single property using the GstChildProxy mechanism.
256  * You are responsible for freeing it by calling g_value_unset()
257  */
258 void
259 gst_child_proxy_get_property (GstChildProxy * object, const gchar * name,
260     GValue * value)
261 {
262   GParamSpec *pspec;
263   GObject *target;
264
265   g_return_if_fail (GST_IS_CHILD_PROXY (object));
266   g_return_if_fail (name != NULL);
267   g_return_if_fail (value != NULL);
268
269   if (!gst_child_proxy_lookup (object, name, &target, &pspec))
270     goto not_found;
271
272   if (!G_IS_VALUE (value)) {
273     g_value_init (value, pspec->value_type);
274   }
275
276   g_object_get_property (target, pspec->name, value);
277   gst_object_unref (target);
278
279   return;
280
281 not_found:
282   {
283     g_warning ("no property %s in object %s", name,
284         (GST_IS_OBJECT (object) ? GST_OBJECT_NAME (object) : ""));
285     return;
286   }
287 }
288
289 /**
290  * gst_child_proxy_get_valist:
291  * @object: the object to query
292  * @first_property_name: name of the first property to get
293  * @var_args: return location for the first property, followed optionally by more name/return location pairs, followed by %NULL
294  *
295  * Gets properties of the parent object and its children.
296  */
297 void
298 gst_child_proxy_get_valist (GstChildProxy * object,
299     const gchar * first_property_name, va_list var_args)
300 {
301   const gchar *name;
302   gchar *error = NULL;
303   GValue value = { 0, };
304   GParamSpec *pspec;
305   GObject *target;
306
307   g_return_if_fail (GST_IS_CHILD_PROXY (object));
308
309   name = first_property_name;
310
311   /* iterate over pairs */
312   while (name) {
313     if (!gst_child_proxy_lookup (object, name, &target, &pspec))
314       goto not_found;
315
316     g_value_init (&value, pspec->value_type);
317     g_object_get_property (target, pspec->name, &value);
318     gst_object_unref (target);
319
320     G_VALUE_LCOPY (&value, var_args, 0, &error);
321     if (error)
322       goto cant_copy;
323     g_value_unset (&value);
324     name = va_arg (var_args, gchar *);
325   }
326   return;
327
328 not_found:
329   {
330     g_warning ("no property %s in object %s", name,
331         (GST_IS_OBJECT (object) ? GST_OBJECT_NAME (object) : ""));
332     return;
333   }
334 cant_copy:
335   {
336     g_warning ("error copying value %s in object %s: %s", pspec->name,
337         (GST_IS_OBJECT (object) ? GST_OBJECT_NAME (object) : ""), error);
338     g_value_unset (&value);
339     return;
340   }
341 }
342
343 /**
344  * gst_child_proxy_get:
345  * @object: the parent object
346  * @first_property_name: name of the first property to get
347  * @...: return location for the first property, followed optionally by more name/return location pairs, followed by %NULL
348  *
349  * Gets properties of the parent object and its children.
350  */
351 void
352 gst_child_proxy_get (GstChildProxy * object, const gchar * first_property_name,
353     ...)
354 {
355   va_list var_args;
356
357   g_return_if_fail (GST_IS_CHILD_PROXY (object));
358
359   va_start (var_args, first_property_name);
360   gst_child_proxy_get_valist (object, first_property_name, var_args);
361   va_end (var_args);
362 }
363
364 /**
365  * gst_child_proxy_set_property:
366  * @object: the parent object
367  * @name: name of the property to set
368  * @value: new #GValue for the property
369  *
370  * Sets a single property using the GstChildProxy mechanism.
371  */
372 void
373 gst_child_proxy_set_property (GstChildProxy * object, const gchar * name,
374     const GValue * value)
375 {
376   GParamSpec *pspec;
377   GObject *target;
378
379   g_return_if_fail (GST_IS_CHILD_PROXY (object));
380   g_return_if_fail (name != NULL);
381   g_return_if_fail (G_IS_VALUE (value));
382
383   if (!gst_child_proxy_lookup (object, name, &target, &pspec))
384     goto not_found;
385
386   g_object_set_property (target, pspec->name, value);
387   gst_object_unref (target);
388   return;
389
390 not_found:
391   {
392     g_warning ("cannot set property %s on object %s", name,
393         (GST_IS_OBJECT (object) ? GST_OBJECT_NAME (object) : ""));
394     return;
395   }
396 }
397
398 /**
399  * gst_child_proxy_set_valist:
400  * @object: the parent object
401  * @first_property_name: name of the first property to set
402  * @var_args: value for the first property, followed optionally by more name/value pairs, followed by %NULL
403  *
404  * Sets properties of the parent object and its children.
405  */
406 void
407 gst_child_proxy_set_valist (GstChildProxy * object,
408     const gchar * first_property_name, va_list var_args)
409 {
410   const gchar *name;
411   gchar *error = NULL;
412   GValue value = { 0, };
413   GParamSpec *pspec;
414   GObject *target;
415
416   g_return_if_fail (GST_IS_CHILD_PROXY (object));
417
418   name = first_property_name;
419
420   /* iterate over pairs */
421   while (name) {
422     if (!gst_child_proxy_lookup (object, name, &target, &pspec))
423       goto not_found;
424
425     G_VALUE_COLLECT_INIT (&value, pspec->value_type, var_args,
426         G_VALUE_NOCOPY_CONTENTS, &error);
427
428     if (error)
429       goto cant_copy;
430
431     g_object_set_property (target, pspec->name, &value);
432     gst_object_unref (target);
433
434     g_value_unset (&value);
435     name = va_arg (var_args, gchar *);
436   }
437   return;
438
439 not_found:
440   {
441     g_warning ("no property %s in object %s", name,
442         (GST_IS_OBJECT (object) ? GST_OBJECT_NAME (object) : ""));
443     return;
444   }
445 cant_copy:
446   {
447     g_warning ("error copying value %s in object %s: %s", pspec->name,
448         (GST_IS_OBJECT (object) ? GST_OBJECT_NAME (object) : ""), error);
449     g_value_unset (&value);
450     gst_object_unref (target);
451     g_free (error);
452     return;
453   }
454 }
455
456 /**
457  * gst_child_proxy_set:
458  * @object: the parent object
459  * @first_property_name: name of the first property to set
460  * @...: value for the first property, followed optionally by more name/value pairs, followed by %NULL
461  *
462  * Sets properties of the parent object and its children.
463  */
464 void
465 gst_child_proxy_set (GstChildProxy * object, const gchar * first_property_name,
466     ...)
467 {
468   va_list var_args;
469
470   g_return_if_fail (GST_IS_CHILD_PROXY (object));
471
472   va_start (var_args, first_property_name);
473   gst_child_proxy_set_valist (object, first_property_name, var_args);
474   va_end (var_args);
475 }
476
477 /**
478  * gst_child_proxy_child_added:
479  * @parent: the parent object
480  * @child: the newly added child
481  * @name: the name of the new child
482  *
483  * Emits the #GstChildProxy::child-added signal.
484  */
485 void
486 gst_child_proxy_child_added (GstChildProxy * parent, GObject * child,
487     const gchar * name)
488 {
489   g_signal_emit (parent, signals[CHILD_ADDED], 0, child, name);
490 }
491
492 /**
493  * gst_child_proxy_child_removed:
494  * @parent: the parent object
495  * @child: the removed child
496  * @name: the name of the old child
497  *
498  * Emits the #GstChildProxy::child-removed signal.
499  */
500 void
501 gst_child_proxy_child_removed (GstChildProxy * parent, GObject * child,
502     const gchar * name)
503 {
504   g_signal_emit (parent, signals[CHILD_REMOVED], 0, child, name);
505 }
506
507 /* gobject methods */
508
509 static void
510 gst_child_proxy_class_init (gpointer g_class, gpointer class_data)
511 {
512   GstChildProxyInterface *iface = (GstChildProxyInterface *) g_class;
513
514   iface->get_child_by_name = gst_child_proxy_default_get_child_by_name;
515 }
516
517 static void
518 gst_child_proxy_base_init (gpointer g_class)
519 {
520   static gboolean initialized = FALSE;
521
522   if (!initialized) {
523     /* create interface signals and properties here. */
524     /**
525      * GstChildProxy::child-added:
526      * @self: the #GstChildProxy
527      * @object: the #GObject that was added
528      * @name: the name of the new child
529      *
530      * Will be emitted after the @object was added to the @child_proxy.
531      */
532     signals[CHILD_ADDED] =
533         g_signal_new ("child-added", G_TYPE_FROM_CLASS (g_class),
534         G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET (GstChildProxyInterface,
535             child_added), NULL, NULL, NULL, G_TYPE_NONE,
536         2, G_TYPE_OBJECT, G_TYPE_STRING);
537
538     /**
539      * GstChildProxy::child-removed:
540      * @self: the #GstChildProxy
541      * @object: the #GObject that was removed
542      * @name: the name of the old child
543      *
544      * Will be emitted after the @object was removed from the @child_proxy.
545      */
546     signals[CHILD_REMOVED] =
547         g_signal_new ("child-removed", G_TYPE_FROM_CLASS (g_class),
548         G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET (GstChildProxyInterface,
549             child_removed), NULL, NULL, NULL, G_TYPE_NONE,
550         2, G_TYPE_OBJECT, G_TYPE_STRING);
551
552     initialized = TRUE;
553   }
554 }
555
556 GType
557 gst_child_proxy_get_type (void)
558 {
559   static gsize type = 0;
560
561   if (g_once_init_enter (&type)) {
562     GType _type;
563     static const GTypeInfo info = {
564       sizeof (GstChildProxyInterface),
565       gst_child_proxy_base_init,        /* base_init */
566       NULL,                     /* base_finalize */
567       gst_child_proxy_class_init,       /* class_init */
568       NULL,                     /* class_finalize */
569       NULL,                     /* class_data */
570       0,
571       0,                        /* n_preallocs */
572       NULL                      /* instance_init */
573     };
574
575     _type =
576         g_type_register_static (G_TYPE_INTERFACE, "GstChildProxy", &info, 0);
577
578     g_type_interface_add_prerequisite (_type, G_TYPE_OBJECT);
579     g_once_init_leave (&type, (gsize) _type);
580   }
581   return type;
582 }