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