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 *
gst_child_proxy_default_get_child_by_name(GstChildProxy * parent,const gchar * name)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 *
gst_child_proxy_get_child_by_name(GstChildProxy * parent,const gchar * name)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 *
gst_child_proxy_get_child_by_index(GstChildProxy * parent,guint index)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
gst_child_proxy_get_children_count(GstChildProxy * parent)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
gst_child_proxy_lookup(GstChildProxy * object,const gchar * name,GObject ** target,GParamSpec ** pspec)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
gst_child_proxy_get_property(GstChildProxy * object,const gchar * name,GValue * value)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
gst_child_proxy_get_valist(GstChildProxy * object,const gchar * first_property_name,va_list var_args)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
gst_child_proxy_get(GstChildProxy * object,const gchar * first_property_name,...)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
gst_child_proxy_set_property(GstChildProxy * object,const gchar * name,const GValue * value)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
gst_child_proxy_set_valist(GstChildProxy * object,const gchar * first_property_name,va_list var_args)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
gst_child_proxy_set(GstChildProxy * object,const gchar * first_property_name,...)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
gst_child_proxy_child_added(GstChildProxy * parent,GObject * child,const gchar * name)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
gst_child_proxy_child_removed(GstChildProxy * parent,GObject * child,const gchar * name)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
gst_child_proxy_class_init(gpointer g_class,gpointer class_data)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
gst_child_proxy_base_init(gpointer g_class)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
gst_child_proxy_get_type(void)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 }
583