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., 59 Temple Place - Suite 330,
19 * Boston, MA 02111-1307, USA.
20 */
21 /**
22 * SECTION:gstchildproxy
23 * @short_description: Interface for multi child elements.
24 * @see_also: #GstBin
25 *
26 * This interface abstracts handling of property sets for child elements.
27 * Imagine elements such as mixers or polyphonic generators. They all have
28 * multiple #GstPads or some kind of voice objects. The element acts as a parent
29 * for those child objects. Each child has the same properties.
30 *
31 * By implementing this interface the child properties can be accessed from the
32 * parent element by using gst_child_proxy_get() and gst_child_proxy_set().
33 *
34 * Property names are written as "child-name::property-name". The whole naming
35 * scheme is recursive. Thus "child1::child2::property" is valid too, if
36 * "child1" also implements the #GstChildProxy interface.
37 */
39 #include "gst_private.h"
41 #include "gstchildproxy.h"
42 #include "gstmarshal.h"
43 #include <gobject/gvaluecollector.h>
45 /* signals */
46 enum
47 {
48 CHILD_ADDED,
49 CHILD_REMOVED,
50 LAST_SIGNAL
51 };
53 static guint signals[LAST_SIGNAL] = { 0 };
55 /**
56 * gst_child_proxy_get_child_by_name:
57 * @parent: the parent object to get the child from
58 * @name: the childs name
59 *
60 * Looks up a child element by the given name.
61 *
62 * Implementors can use #GstObject together with gst_object_get_name()
63 *
64 * Returns: the child object or %NULL if not found. Unref after usage.
65 *
66 * MT safe.
67 */
68 GstObject *
69 gst_child_proxy_get_child_by_name (GstChildProxy * parent, const gchar * name)
70 {
71 guint count, i;
72 GstObject *object, *result;
73 gchar *object_name;
75 g_return_val_if_fail (GST_IS_CHILD_PROXY (parent), NULL);
76 g_return_val_if_fail (name != NULL, NULL);
78 result = NULL;
80 count = gst_child_proxy_get_children_count (parent);
81 for (i = 0; i < count; i++) {
82 gboolean eq;
84 if (!(object = gst_child_proxy_get_child_by_index (parent, i)))
85 continue;
87 object_name = gst_object_get_name (object);
88 if (object_name == NULL) {
89 g_warning ("child %u of parent %s has no name", i,
90 GST_OBJECT_NAME (parent));
91 goto next;
92 }
93 eq = g_str_equal (object_name, name);
94 g_free (object_name);
96 if (eq) {
97 result = object;
98 break;
99 }
100 next:
101 gst_object_unref (object);
102 }
103 return result;
104 }
106 /**
107 * gst_child_proxy_get_child_by_index:
108 * @parent: the parent object to get the child from
109 * @index: the childs position in the child list
110 *
111 * Fetches a child by its number.
112 *
113 * Returns: the child object or %NULL if not found (index too high). Unref
114 * after usage.
115 *
116 * MT safe.
117 */
118 GstObject *
119 gst_child_proxy_get_child_by_index (GstChildProxy * parent, guint index)
120 {
121 g_return_val_if_fail (GST_IS_CHILD_PROXY (parent), NULL);
123 return (GST_CHILD_PROXY_GET_INTERFACE (parent)->get_child_by_index (parent,
124 index));
125 }
127 /**
128 * gst_child_proxy_get_children_count:
129 * @parent: the parent object
130 *
131 * Gets the number of child objects this parent contains.
132 *
133 * Returns: the number of child objects
134 *
135 * MT safe.
136 */
137 guint
138 gst_child_proxy_get_children_count (GstChildProxy * parent)
139 {
140 g_return_val_if_fail (GST_IS_CHILD_PROXY (parent), 0);
142 return (GST_CHILD_PROXY_GET_INTERFACE (parent)->get_children_count (parent));
143 }
145 /**
146 * gst_child_proxy_lookup:
147 * @object: object to lookup the property in
148 * @name: name of the property to look up
149 * @target: pointer to a #GstObject that takes the real object to set property on
150 * @pspec: pointer to take the #GParamSpec describing the property
151 *
152 * Looks up which object and #GParamSpec would be effected by the given @name.
153 *
154 * Returns: TRUE if @target and @pspec could be found. FALSE otherwise. In that
155 * case the values for @pspec and @target are not modified. Unref @target after
156 * usage.
157 *
158 * MT safe.
159 */
160 gboolean
161 gst_child_proxy_lookup (GstObject * object, const gchar * name,
162 GstObject ** target, GParamSpec ** pspec)
163 {
164 gboolean res = FALSE;
165 gchar **names, **current;
167 g_return_val_if_fail (GST_IS_OBJECT (object), FALSE);
168 g_return_val_if_fail (name != NULL, FALSE);
170 gst_object_ref (object);
172 current = names = g_strsplit (name, "::", -1);
173 while (current[1]) {
174 GstObject *next;
176 if (!GST_IS_CHILD_PROXY (object)) {
177 GST_INFO
178 ("object %s is not a parent, so you cannot request a child by name %s",
179 GST_OBJECT_NAME (object), current[0]);
180 break;
181 }
182 next = gst_child_proxy_get_child_by_name (GST_CHILD_PROXY (object),
183 current[0]);
184 if (!next) {
185 GST_INFO ("no such object %s", current[0]);
186 break;
187 }
188 gst_object_unref (object);
189 object = next;
190 current++;
191 };
192 if (current[1] == NULL) {
193 GParamSpec *spec =
194 g_object_class_find_property (G_OBJECT_GET_CLASS (object), current[0]);
195 if (spec == NULL) {
196 GST_INFO ("no param spec named %s", current[0]);
197 } else {
198 if (pspec)
199 *pspec = spec;
200 if (target) {
201 gst_object_ref (object);
202 *target = object;
203 }
204 res = TRUE;
205 }
206 }
207 gst_object_unref (object);
208 g_strfreev (names);
209 return res;
210 }
212 /**
213 * gst_child_proxy_get_property:
214 * @object: object to query
215 * @name: name of the property
216 * @value: an uninitialized #GValue that should take the result.
217 *
218 * Gets a single property using the GstChildProxy mechanism.
219 * You are responsible for for freeing it by calling g_value_unset()
220 */
221 void
222 gst_child_proxy_get_property (GstObject * object, const gchar * name,
223 GValue * value)
224 {
225 GParamSpec *pspec;
226 GstObject *target;
228 g_return_if_fail (GST_IS_OBJECT (object));
229 g_return_if_fail (name != NULL);
230 g_return_if_fail (!G_IS_VALUE (value));
232 if (!gst_child_proxy_lookup (object, name, &target, &pspec))
233 goto not_found;
235 g_object_get_property (G_OBJECT (target), pspec->name, value);
236 gst_object_unref (target);
238 return;
240 not_found:
241 {
242 g_warning ("cannot get property %s from object %s", name,
243 GST_OBJECT_NAME (object));
244 return;
245 }
246 }
248 /**
249 * gst_child_proxy_get_valist:
250 * @object: the object to query
251 * @first_property_name: name of the first property to get
252 * @var_args: return location for the first property, followed optionally by more name/return location pairs, followed by NULL
253 *
254 * Gets properties of the parent object and its children.
255 */
256 void
257 gst_child_proxy_get_valist (GstObject * object,
258 const gchar * first_property_name, va_list var_args)
259 {
260 const gchar *name;
261 gchar *error = NULL;
262 GValue value = { 0, };
264 g_return_if_fail (G_IS_OBJECT (object));
266 name = first_property_name;
268 /* iterate over pairs */
269 while (name) {
270 gst_child_proxy_get_property (object, name, &value);
271 G_VALUE_LCOPY (&value, var_args, 0, &error);
272 if (error) {
273 g_warning ("error copying value: %s", error);
274 return;
275 }
276 g_value_unset (&value);
277 name = va_arg (var_args, gchar *);
278 }
279 }
281 /**
282 * gst_child_proxy_get:
283 * @object: the parent object
284 * @first_property_name: name of the first property to get
285 * @...: return location for the first property, followed optionally by more name/return location pairs, followed by NULL
286 *
287 * Gets properties of the parent object and its children.
288 */
289 void
290 gst_child_proxy_get (GstObject * object, const gchar * first_property_name, ...)
291 {
292 va_list var_args;
294 g_return_if_fail (GST_IS_OBJECT (object));
296 va_start (var_args, first_property_name);
297 gst_child_proxy_get_valist (object, first_property_name, var_args);
298 va_end (var_args);
299 }
301 /**
302 * gst_child_proxy_set_property:
303 * @object: the parent object
304 * @name: name of the property to set
305 * @value: new #GValue for the property
306 *
307 * Sets a single property using the GstChildProxy mechanism.
308 */
309 void
310 gst_child_proxy_set_property (GstObject * object, const gchar * name,
311 const GValue * value)
312 {
313 GParamSpec *pspec;
314 GstObject *target;
316 g_return_if_fail (GST_IS_OBJECT (object));
317 g_return_if_fail (name != NULL);
318 g_return_if_fail (!G_IS_VALUE (value));
320 if (!gst_child_proxy_lookup (object, name, &target, &pspec))
321 goto not_found;
323 g_object_set_property (G_OBJECT (target), pspec->name, value);
324 gst_object_unref (target);
325 return;
327 not_found:
328 {
329 g_warning ("cannot set property %s on object %s", name,
330 GST_OBJECT_NAME (object));
331 return;
332 }
333 }
335 /**
336 * gst_child_proxy_set_valist:
337 * @object: the parent object
338 * @first_property_name: name of the first property to set
339 * @var_args: value for the first property, followed optionally by more name/value pairs, followed by NULL
340 *
341 * Sets properties of the parent object and its children.
342 */
343 void
344 gst_child_proxy_set_valist (GstObject * object,
345 const gchar * first_property_name, va_list var_args)
346 {
347 const gchar *name;
348 gchar *error = NULL;
349 GValue value = { 0, };
351 g_return_if_fail (G_IS_OBJECT (object));
353 name = first_property_name;
355 /* iterate over pairs */
356 while (name) {
357 GParamSpec *pspec;
358 GstObject *target;
360 if (!gst_child_proxy_lookup (object, name, &target, &pspec)) {
361 g_warning ("no such property %s in object %s", name,
362 GST_OBJECT_NAME (object));
363 continue;
364 }
365 g_value_init (&value, G_PARAM_SPEC_VALUE_TYPE (pspec));
366 G_VALUE_COLLECT (&value, var_args, G_VALUE_NOCOPY_CONTENTS, &error);
367 if (error) {
368 g_warning ("error copying value: %s", error);
369 gst_object_unref (target);
370 return;
371 }
372 g_object_set_property (G_OBJECT (target), pspec->name, &value);
373 gst_object_unref (target);
375 g_value_unset (&value);
376 name = va_arg (var_args, gchar *);
377 }
378 }
380 /**
381 * gst_child_proxy_set:
382 * @object: the parent object
383 * @first_property_name: name of the first property to set
384 * @...: value for the first property, followed optionally by more name/value pairs, followed by NULL
385 *
386 * Sets properties of the parent object and its children.
387 */
388 void
389 gst_child_proxy_set (GstObject * object, const gchar * first_property_name, ...)
390 {
391 va_list var_args;
393 g_return_if_fail (GST_IS_OBJECT (object));
395 va_start (var_args, first_property_name);
396 gst_child_proxy_set_valist (object, first_property_name, var_args);
397 va_end (var_args);
398 }
400 /**
401 * gst_child_proxy_child_added:
402 * @object: the parent object
403 * @child: the newly added child
404 *
405 * Emits the "child-added" signal.
406 */
407 void
408 gst_child_proxy_child_added (GstObject * object, GstObject * child)
409 {
410 g_signal_emit (G_OBJECT (object), signals[CHILD_ADDED], 0, child);
411 }
413 /**
414 * gst_child_proxy_child_removed:
415 * @object: the parent object
416 * @child: the newly added child
417 *
418 * Emits the "child-removed" signal.
419 */
420 void
421 gst_child_proxy_child_removed (GstObject * object, GstObject * child)
422 {
423 g_signal_emit (G_OBJECT (object), signals[CHILD_REMOVED], 0, child);
424 }
426 /* gobject methods */
428 static void
429 gst_child_proxy_base_init (gpointer g_class)
430 {
431 static gboolean initialized = FALSE;
433 if (!initialized) {
434 /* create interface signals and properties here. */
435 signals[CHILD_ADDED] =
436 g_signal_new ("child-added", G_TYPE_FROM_CLASS (g_class),
437 G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET (GstChildProxyInterface,
438 child_added), NULL, NULL, gst_marshal_VOID__OBJECT, G_TYPE_NONE, 1,
439 G_TYPE_OBJECT);
441 signals[CHILD_REMOVED] =
442 g_signal_new ("child-removed", G_TYPE_FROM_CLASS (g_class),
443 G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET (GstChildProxyInterface,
444 child_removed), NULL, NULL, gst_marshal_VOID__OBJECT, G_TYPE_NONE,
445 1, G_TYPE_OBJECT);
447 initialized = TRUE;
448 }
449 }
451 GType
452 gst_child_proxy_get_type (void)
453 {
454 static GType type = 0;
456 if (type == 0) {
457 static const GTypeInfo info = {
458 sizeof (GstChildProxyInterface),
459 gst_child_proxy_base_init, /* base_init */
460 NULL, /* base_finalize */
461 NULL, /* class_init */
462 NULL, /* class_finalize */
463 NULL, /* class_data */
464 0,
465 0, /* n_preallocs */
466 NULL /* instance_init */
467 };
468 type = g_type_register_static (G_TYPE_INTERFACE, "GstChildProxy", &info, 0);
470 g_type_interface_add_prerequisite (type, GST_TYPE_OBJECT);
471 }
472 return type;
473 }