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