gstpad: Avoid race in (un)setting EOS flag on sinkpads
[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 /**
59  * gst_child_proxy_get_child_by_name_recurse:
60  * @parent: the parent object to get the child from
61  * @name: the full-path child's name
62  *
63  * Looks up a child element by the given full-path name.
64  *
65  * Similar to gst_child_proxy_get_child_by_name(), this method
66  * searches and returns a child given a name. The difference is that
67  * this method allows a hierarchical path in the form of
68  * child1::child2::child3. In the later example this method would
69  * return a reference to child3, if found. The name should be made of
70  * element names only and should not contain any property names.
71  *
72  * Returns: (transfer full) (nullable): the child object or %NULL if
73  *     not found.
74  *
75  * Since: 1.22
76  */
77 GObject *
78 gst_child_proxy_get_child_by_name_recurse (GstChildProxy * child_proxy,
79     const gchar * name)
80 {
81   gchar **names = NULL, **current = NULL;
82   GObject *obj = NULL, *next = NULL;
83
84   g_return_val_if_fail (child_proxy != NULL, NULL);
85   g_return_val_if_fail (name != NULL, NULL);
86
87   current = names = g_strsplit (name, "::", -1);
88   if (current[0]) {
89     obj = G_OBJECT (g_object_ref (child_proxy));
90   }
91
92   /* walk through the children hierarchy until the requested one is found */
93   while (current[0]) {
94
95     /* Cannot ask for the child of a non-childproxy */
96     if (!GST_IS_CHILD_PROXY (obj)) {
97       gst_object_unref (obj);
98       next = NULL;
99       break;
100     }
101
102     next = gst_child_proxy_get_child_by_name (GST_CHILD_PROXY (obj),
103         current[0]);
104     gst_object_unref (obj);
105
106     /* The child does not exist */
107     if (!next) {
108       GST_INFO ("Unable to find child %s", current[0]);
109       break;
110     }
111
112     obj = next;
113     current++;
114   }
115
116   g_strfreev (names);
117
118   return next;
119 }
120
121 static GObject *
122 gst_child_proxy_default_get_child_by_name (GstChildProxy * parent,
123     const gchar * name)
124 {
125   guint count, i;
126   GObject *object, *result;
127   gchar *object_name;
128
129   g_return_val_if_fail (GST_IS_CHILD_PROXY (parent), NULL);
130   g_return_val_if_fail (name != NULL, NULL);
131
132   result = NULL;
133
134   count = gst_child_proxy_get_children_count (parent);
135   for (i = 0; i < count; i++) {
136     gboolean eq;
137
138     if (!(object = gst_child_proxy_get_child_by_index (parent, i)))
139       continue;
140
141     if (!GST_IS_OBJECT (object)) {
142       goto next;
143     }
144     object_name = gst_object_get_name (GST_OBJECT_CAST (object));
145     if (object_name == NULL) {
146       g_warning ("child %u of parent %s has no name", i,
147           GST_OBJECT_NAME (parent));
148       goto next;
149     }
150     eq = g_str_equal (object_name, name);
151     g_free (object_name);
152
153     if (eq) {
154       result = object;
155       break;
156     }
157   next:
158     gst_object_unref (object);
159   }
160   return result;
161 }
162
163
164 /**
165  * gst_child_proxy_get_child_by_name:
166  * @parent: the parent object to get the child from
167  * @name: the child's name
168  *
169  * Looks up a child element by the given name.
170  *
171  * This virtual method has a default implementation that uses #GstObject
172  * together with gst_object_get_name(). If the interface is to be used with
173  * #GObjects, this methods needs to be overridden.
174  *
175  * Returns: (transfer full) (nullable): the child object or %NULL if
176  *     not found.
177  */
178 GObject *
179 gst_child_proxy_get_child_by_name (GstChildProxy * parent, const gchar * name)
180 {
181   GstChildProxyInterface *iface;
182
183   g_return_val_if_fail (GST_IS_CHILD_PROXY (parent), 0);
184
185   iface = GST_CHILD_PROXY_GET_INTERFACE (parent);
186
187   if (iface->get_child_by_name != NULL)
188     return iface->get_child_by_name (parent, name);
189
190   return NULL;
191 }
192
193 /**
194  * gst_child_proxy_get_child_by_index:
195  * @parent: the parent object to get the child from
196  * @index: the child's position in the child list
197  *
198  * Fetches a child by its number.
199  *
200  * Returns: (transfer full) (nullable): the child object or %NULL if
201  *     not found (index too high).
202  */
203 GObject *
204 gst_child_proxy_get_child_by_index (GstChildProxy * parent, guint index)
205 {
206   GstChildProxyInterface *iface;
207
208   g_return_val_if_fail (GST_IS_CHILD_PROXY (parent), NULL);
209
210   iface = GST_CHILD_PROXY_GET_INTERFACE (parent);
211
212   if (iface->get_child_by_index != NULL)
213     return iface->get_child_by_index (parent, index);
214
215   return NULL;
216 }
217
218 /**
219  * gst_child_proxy_get_children_count:
220  * @parent: the parent object
221  *
222  * Gets the number of child objects this parent contains.
223  *
224  * Returns: the number of child objects
225  */
226 guint
227 gst_child_proxy_get_children_count (GstChildProxy * parent)
228 {
229   GstChildProxyInterface *iface;
230
231   g_return_val_if_fail (GST_IS_CHILD_PROXY (parent), 0);
232
233   iface = GST_CHILD_PROXY_GET_INTERFACE (parent);
234
235   if (iface->get_children_count != NULL)
236     return iface->get_children_count (parent);
237
238   return 0;
239 }
240
241 /**
242  * gst_child_proxy_lookup:
243  * @object: child proxy object to lookup the property in
244  * @name: name of the property to look up
245  * @target: (out) (allow-none) (transfer full): pointer to a #GObject that
246  *     takes the real object to set property on
247  * @pspec: (out) (allow-none) (transfer none): pointer to take the #GParamSpec
248  *     describing the property
249  *
250  * Looks up which object and #GParamSpec would be effected by the given @name.
251  *
252  * Returns: %TRUE if @target and @pspec could be found. %FALSE otherwise. In that
253  * case the values for @pspec and @target are not modified. Unref @target after
254  * usage. For plain #GObject @target is the same as @object.
255  */
256 gboolean
257 gst_child_proxy_lookup (GstChildProxy * object, const gchar * name,
258     GObject ** target, GParamSpec ** pspec)
259 {
260   gboolean res = FALSE;
261   gchar *children = NULL;
262   const gchar *prop = NULL;
263   GObject *child = NULL;
264   static const gchar *separator = "::";
265
266   g_return_val_if_fail (GST_IS_CHILD_PROXY (object), FALSE);
267   g_return_val_if_fail (name != NULL, FALSE);
268
269   /* If the requested name does not include a "::" then it is not a
270      child proxy path, but the property name directly */
271   prop = g_strrstr (name, separator);
272   if (!prop) {
273     child = gst_object_ref (G_OBJECT (object));
274     prop = name;
275   } else {
276     /* Skip "::" */
277     prop += 2;
278
279     /* Extract "child1::child2" from "child1::child2::prop" */
280     children = g_strndup (name, prop - name - 2);
281     GST_INFO ("Looking for property %s in %s", prop, children);
282
283     child = gst_child_proxy_get_child_by_name_recurse (object, children);
284     g_free (children);
285
286     if (!child) {
287       GST_INFO ("Child not found");
288       goto out;
289     }
290   }
291
292   GParamSpec *spec =
293       g_object_class_find_property (G_OBJECT_GET_CLASS (child), prop);
294   if (spec == NULL) {
295     GST_INFO ("no param spec named %s", prop);
296   } else {
297     if (pspec)
298       *pspec = spec;
299     if (target) {
300       g_object_ref (child);
301       *target = child;
302     }
303     res = TRUE;
304   }
305   gst_object_unref (child);
306
307 out:
308   return res;
309 }
310
311 /**
312  * gst_child_proxy_get_property:
313  * @object: object to query
314  * @name: name of the property
315  * @value: (out caller-allocates): a #GValue that should take the result.
316  *
317  * Gets a single property using the GstChildProxy mechanism.
318  * You are responsible for freeing it by calling g_value_unset()
319  */
320 void
321 gst_child_proxy_get_property (GstChildProxy * object, const gchar * name,
322     GValue * value)
323 {
324   GParamSpec *pspec;
325   GObject *target;
326
327   g_return_if_fail (GST_IS_CHILD_PROXY (object));
328   g_return_if_fail (name != NULL);
329   g_return_if_fail (value != NULL);
330
331   if (!gst_child_proxy_lookup (object, name, &target, &pspec))
332     goto not_found;
333
334   if (!G_IS_VALUE (value)) {
335     g_value_init (value, pspec->value_type);
336   }
337
338   g_object_get_property (target, pspec->name, value);
339   gst_object_unref (target);
340
341   return;
342
343 not_found:
344   {
345     g_warning ("no property %s in object %s", name,
346         (GST_IS_OBJECT (object) ? GST_OBJECT_NAME (object) : ""));
347     return;
348   }
349 }
350
351 /**
352  * gst_child_proxy_get_valist:
353  * @object: the object to query
354  * @first_property_name: name of the first property to get
355  * @var_args: return location for the first property, followed optionally by more name/return location pairs, followed by %NULL
356  *
357  * Gets properties of the parent object and its children.
358  */
359 void
360 gst_child_proxy_get_valist (GstChildProxy * object,
361     const gchar * first_property_name, va_list var_args)
362 {
363   const gchar *name;
364   gchar *error = NULL;
365   GValue value = { 0, };
366   GParamSpec *pspec;
367   GObject *target;
368
369   g_return_if_fail (GST_IS_CHILD_PROXY (object));
370
371   name = first_property_name;
372
373   /* iterate over pairs */
374   while (name) {
375     if (!gst_child_proxy_lookup (object, name, &target, &pspec))
376       goto not_found;
377
378     g_value_init (&value, pspec->value_type);
379     g_object_get_property (target, pspec->name, &value);
380     gst_object_unref (target);
381
382     G_VALUE_LCOPY (&value, var_args, 0, &error);
383     if (error)
384       goto cant_copy;
385     g_value_unset (&value);
386     name = va_arg (var_args, gchar *);
387   }
388   return;
389
390 not_found:
391   {
392     g_warning ("no property %s in object %s", name,
393         (GST_IS_OBJECT (object) ? GST_OBJECT_NAME (object) : ""));
394     return;
395   }
396 cant_copy:
397   {
398     g_warning ("error copying value %s in object %s: %s", pspec->name,
399         (GST_IS_OBJECT (object) ? GST_OBJECT_NAME (object) : ""), error);
400     g_value_unset (&value);
401     return;
402   }
403 }
404
405 /**
406  * gst_child_proxy_get:
407  * @object: the parent object
408  * @first_property_name: name of the first property to get
409  * @...: return location for the first property, followed optionally by more name/return location pairs, followed by %NULL
410  *
411  * Gets properties of the parent object and its children.
412  */
413 void
414 gst_child_proxy_get (GstChildProxy * object, const gchar * first_property_name,
415     ...)
416 {
417   va_list var_args;
418
419   g_return_if_fail (GST_IS_CHILD_PROXY (object));
420
421   va_start (var_args, first_property_name);
422   gst_child_proxy_get_valist (object, first_property_name, var_args);
423   va_end (var_args);
424 }
425
426 /**
427  * gst_child_proxy_set_property:
428  * @object: the parent object
429  * @name: name of the property to set
430  * @value: new #GValue for the property
431  *
432  * Sets a single property using the GstChildProxy mechanism.
433  */
434 void
435 gst_child_proxy_set_property (GstChildProxy * object, const gchar * name,
436     const GValue * value)
437 {
438   GParamSpec *pspec;
439   GObject *target;
440
441   g_return_if_fail (GST_IS_CHILD_PROXY (object));
442   g_return_if_fail (name != NULL);
443   g_return_if_fail (G_IS_VALUE (value));
444
445   if (!gst_child_proxy_lookup (object, name, &target, &pspec))
446     goto not_found;
447
448   g_object_set_property (target, pspec->name, value);
449   gst_object_unref (target);
450   return;
451
452 not_found:
453   {
454     g_warning ("cannot set property %s on object %s", name,
455         (GST_IS_OBJECT (object) ? GST_OBJECT_NAME (object) : ""));
456     return;
457   }
458 }
459
460 /**
461  * gst_child_proxy_set_valist:
462  * @object: the parent object
463  * @first_property_name: name of the first property to set
464  * @var_args: value for the first property, followed optionally by more name/value pairs, followed by %NULL
465  *
466  * Sets properties of the parent object and its children.
467  */
468 void
469 gst_child_proxy_set_valist (GstChildProxy * object,
470     const gchar * first_property_name, va_list var_args)
471 {
472   const gchar *name;
473   gchar *error = NULL;
474   GValue value = { 0, };
475   GParamSpec *pspec;
476   GObject *target;
477
478   g_return_if_fail (GST_IS_CHILD_PROXY (object));
479
480   name = first_property_name;
481
482   /* iterate over pairs */
483   while (name) {
484     if (!gst_child_proxy_lookup (object, name, &target, &pspec))
485       goto not_found;
486
487     G_VALUE_COLLECT_INIT (&value, pspec->value_type, var_args,
488         G_VALUE_NOCOPY_CONTENTS, &error);
489
490     if (error)
491       goto cant_copy;
492
493     g_object_set_property (target, pspec->name, &value);
494     gst_object_unref (target);
495
496     g_value_unset (&value);
497     name = va_arg (var_args, gchar *);
498   }
499   return;
500
501 not_found:
502   {
503     g_warning ("no property %s in object %s", name,
504         (GST_IS_OBJECT (object) ? GST_OBJECT_NAME (object) : ""));
505     return;
506   }
507 cant_copy:
508   {
509     g_warning ("error copying value %s in object %s: %s", pspec->name,
510         (GST_IS_OBJECT (object) ? GST_OBJECT_NAME (object) : ""), error);
511     g_value_unset (&value);
512     gst_object_unref (target);
513     g_free (error);
514     return;
515   }
516 }
517
518 /**
519  * gst_child_proxy_set:
520  * @object: the parent object
521  * @first_property_name: name of the first property to set
522  * @...: value for the first property, followed optionally by more name/value pairs, followed by %NULL
523  *
524  * Sets properties of the parent object and its children.
525  */
526 void
527 gst_child_proxy_set (GstChildProxy * object, const gchar * first_property_name,
528     ...)
529 {
530   va_list var_args;
531
532   g_return_if_fail (GST_IS_CHILD_PROXY (object));
533
534   va_start (var_args, first_property_name);
535   gst_child_proxy_set_valist (object, first_property_name, var_args);
536   va_end (var_args);
537 }
538
539 /**
540  * gst_child_proxy_child_added:
541  * @parent: the parent object
542  * @child: the newly added child
543  * @name: the name of the new child
544  *
545  * Emits the #GstChildProxy::child-added signal.
546  */
547 void
548 gst_child_proxy_child_added (GstChildProxy * parent, GObject * child,
549     const gchar * name)
550 {
551   g_signal_emit (parent, signals[CHILD_ADDED], 0, child, name);
552 }
553
554 /**
555  * gst_child_proxy_child_removed:
556  * @parent: the parent object
557  * @child: the removed child
558  * @name: the name of the old child
559  *
560  * Emits the #GstChildProxy::child-removed signal.
561  */
562 void
563 gst_child_proxy_child_removed (GstChildProxy * parent, GObject * child,
564     const gchar * name)
565 {
566   g_signal_emit (parent, signals[CHILD_REMOVED], 0, child, name);
567 }
568
569 /* gobject methods */
570
571 static void
572 gst_child_proxy_class_init (gpointer g_class, gpointer class_data)
573 {
574   GstChildProxyInterface *iface = (GstChildProxyInterface *) g_class;
575
576   iface->get_child_by_name = gst_child_proxy_default_get_child_by_name;
577 }
578
579 static void
580 gst_child_proxy_base_init (gpointer g_class)
581 {
582   static gboolean initialized = FALSE;
583
584   if (!initialized) {
585     /* create interface signals and properties here. */
586     /**
587      * GstChildProxy::child-added:
588      * @self: the #GstChildProxy
589      * @object: the #GObject that was added
590      * @name: the name of the new child
591      *
592      * Will be emitted after the @object was added to the @child_proxy.
593      */
594     signals[CHILD_ADDED] =
595         g_signal_new ("child-added", G_TYPE_FROM_CLASS (g_class),
596         G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET (GstChildProxyInterface,
597             child_added), NULL, NULL, NULL, G_TYPE_NONE,
598         2, G_TYPE_OBJECT, G_TYPE_STRING);
599
600     /**
601      * GstChildProxy::child-removed:
602      * @self: the #GstChildProxy
603      * @object: the #GObject that was removed
604      * @name: the name of the old child
605      *
606      * Will be emitted after the @object was removed from the @child_proxy.
607      */
608     signals[CHILD_REMOVED] =
609         g_signal_new ("child-removed", G_TYPE_FROM_CLASS (g_class),
610         G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET (GstChildProxyInterface,
611             child_removed), NULL, NULL, NULL, G_TYPE_NONE,
612         2, G_TYPE_OBJECT, G_TYPE_STRING);
613
614     initialized = TRUE;
615   }
616 }
617
618 GType
619 gst_child_proxy_get_type (void)
620 {
621   static gsize type = 0;
622
623   if (g_once_init_enter (&type)) {
624     GType _type;
625     static const GTypeInfo info = {
626       sizeof (GstChildProxyInterface),
627       gst_child_proxy_base_init,        /* base_init */
628       NULL,                     /* base_finalize */
629       gst_child_proxy_class_init,       /* class_init */
630       NULL,                     /* class_finalize */
631       NULL,                     /* class_data */
632       0,
633       0,                        /* n_preallocs */
634       NULL                      /* instance_init */
635     };
636
637     _type =
638         g_type_register_static (G_TYPE_INTERFACE, "GstChildProxy", &info, 0);
639
640     g_type_interface_add_prerequisite (_type, G_TYPE_OBJECT);
641     g_once_init_leave (&type, (gsize) _type);
642   }
643   return type;
644 }