| /* GStreamer |
| * |
| * Copyright (C) 1999,2000 Erik Walthinsen <omega@cse.ogi.edu> |
| * 2004 Wim Taymans <wim@fluendo.com> |
| * |
| * gstbin.c: GstBin container object and support code |
| * |
| * This library is free software; you can redistribute it and/or |
| * modify it under the terms of the GNU Library General Public |
| * License as published by the Free Software Foundation; either |
| * version 2 of the License, or (at your option) any later version. |
| * |
| * This library is distributed in the hope that it will be useful, |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| * Library General Public License for more details. |
| * |
| * You should have received a copy of the GNU Library General Public |
| * License along with this library; if not, write to the |
| * Free Software Foundation, Inc., 59 Temple Place - Suite 330, |
| * Boston, MA 02111-1307, USA. |
| * |
| * MT safe. |
| */ |
| |
| #include "gst_private.h" |
| |
| #include "gstevent.h" |
| #include "gstbin.h" |
| #include "gstmarshal.h" |
| #include "gstxml.h" |
| #include "gstinfo.h" |
| #include "gsterror.h" |
| |
| #include "gstscheduler.h" |
| #include "gstindex.h" |
| #include "gstutils.h" |
| |
| GST_DEBUG_CATEGORY_STATIC (bin_debug); |
| #define GST_CAT_DEFAULT bin_debug |
| #define GST_LOG_BIN_CONTENTS(bin, text) GST_LOG_OBJECT ((bin), \ |
| text ": %d elements: %u PLAYING, %u PAUSED, %u READY, %u NULL, own state: %s", \ |
| (bin)->numchildren, (guint) (bin)->child_states[3], \ |
| (guint) (bin)->child_states[2], (bin)->child_states[1], \ |
| (bin)->child_states[0], gst_element_state_get_name (GST_STATE (bin))) |
| |
| |
| static GstElementDetails gst_bin_details = GST_ELEMENT_DETAILS ("Generic bin", |
| "Generic/Bin", |
| "Simple container object", |
| "Erik Walthinsen <omega@cse.ogi.edu>," "Wim Taymans <wim@fluendo.com>"); |
| |
| GType _gst_bin_type = 0; |
| |
| static void gst_bin_dispose (GObject * object); |
| |
| static GstElementStateReturn gst_bin_change_state (GstElement * element); |
| static GstElementStateReturn gst_bin_get_state (GstElement * element, |
| GstElementState * state, GstElementState * pending, GTimeVal * timeout); |
| |
| static gboolean gst_bin_add_func (GstBin * bin, GstElement * element); |
| static gboolean gst_bin_remove_func (GstBin * bin, GstElement * element); |
| |
| #ifndef GST_DISABLE_INDEX |
| static void gst_bin_set_index_func (GstElement * element, GstIndex * index); |
| #endif |
| static GstClock *gst_bin_get_clock_func (GstElement * element); |
| static void gst_bin_set_clock_func (GstElement * element, GstClock * clock); |
| |
| static void gst_bin_set_manager (GstElement * element, GstPipeline * manager); |
| static void gst_bin_set_bus (GstElement * element, GstBus * bus); |
| static void gst_bin_set_scheduler (GstElement * element, GstScheduler * sched); |
| |
| static gboolean gst_bin_send_event (GstElement * element, GstEvent * event); |
| static gboolean gst_bin_query (GstElement * element, GstQuery * query); |
| |
| #ifndef GST_DISABLE_LOADSAVE |
| static xmlNodePtr gst_bin_save_thyself (GstObject * object, xmlNodePtr parent); |
| static void gst_bin_restore_thyself (GstObject * object, xmlNodePtr self); |
| #endif |
| |
| static gint bin_element_is_sink (GstElement * child, GstBin * bin); |
| |
| /* Bin signals and args */ |
| enum |
| { |
| ELEMENT_ADDED, |
| ELEMENT_REMOVED, |
| LAST_SIGNAL |
| }; |
| |
| enum |
| { |
| ARG_0 |
| /* FILL ME */ |
| }; |
| |
| static void gst_bin_base_init (gpointer g_class); |
| static void gst_bin_class_init (GstBinClass * klass); |
| static void gst_bin_init (GstBin * bin); |
| |
| static GstElementClass *parent_class = NULL; |
| static guint gst_bin_signals[LAST_SIGNAL] = { 0 }; |
| |
| /** |
| * gst_bin_get_type: |
| * |
| * Returns: the type of #GstBin |
| */ |
| GType |
| gst_bin_get_type (void) |
| { |
| if (!_gst_bin_type) { |
| static const GTypeInfo bin_info = { |
| sizeof (GstBinClass), |
| gst_bin_base_init, |
| NULL, |
| (GClassInitFunc) gst_bin_class_init, |
| NULL, |
| NULL, |
| sizeof (GstBin), |
| 0, |
| (GInstanceInitFunc) gst_bin_init, |
| NULL |
| }; |
| |
| _gst_bin_type = |
| g_type_register_static (GST_TYPE_ELEMENT, "GstBin", &bin_info, 0); |
| |
| GST_DEBUG_CATEGORY_INIT (bin_debug, "bin", GST_DEBUG_BOLD, |
| "debugging info for the 'bin' container element"); |
| } |
| return _gst_bin_type; |
| } |
| |
| static void |
| gst_bin_base_init (gpointer g_class) |
| { |
| GstElementClass *gstelement_class = GST_ELEMENT_CLASS (g_class); |
| |
| gst_element_class_set_details (gstelement_class, &gst_bin_details); |
| } |
| |
| static void |
| gst_bin_class_init (GstBinClass * klass) |
| { |
| GObjectClass *gobject_class; |
| GstObjectClass *gstobject_class; |
| GstElementClass *gstelement_class; |
| |
| gobject_class = (GObjectClass *) klass; |
| gstobject_class = (GstObjectClass *) klass; |
| gstelement_class = (GstElementClass *) klass; |
| |
| parent_class = g_type_class_ref (GST_TYPE_ELEMENT); |
| |
| gst_bin_signals[ELEMENT_ADDED] = |
| g_signal_new ("element-added", G_TYPE_FROM_CLASS (klass), |
| G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET (GstBinClass, element_added), NULL, |
| NULL, gst_marshal_VOID__OBJECT, G_TYPE_NONE, 1, GST_TYPE_ELEMENT); |
| gst_bin_signals[ELEMENT_REMOVED] = |
| g_signal_new ("element-removed", G_TYPE_FROM_CLASS (klass), |
| G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET (GstBinClass, element_removed), NULL, |
| NULL, gst_marshal_VOID__OBJECT, G_TYPE_NONE, 1, GST_TYPE_ELEMENT); |
| |
| gobject_class->dispose = GST_DEBUG_FUNCPTR (gst_bin_dispose); |
| |
| #ifndef GST_DISABLE_LOADSAVE |
| gstobject_class->save_thyself = GST_DEBUG_FUNCPTR (gst_bin_save_thyself); |
| gstobject_class->restore_thyself = |
| GST_DEBUG_FUNCPTR (gst_bin_restore_thyself); |
| #endif |
| |
| gstelement_class->change_state = GST_DEBUG_FUNCPTR (gst_bin_change_state); |
| gstelement_class->get_state = GST_DEBUG_FUNCPTR (gst_bin_get_state); |
| #ifndef GST_DISABLE_INDEX |
| gstelement_class->set_index = GST_DEBUG_FUNCPTR (gst_bin_set_index_func); |
| #endif |
| gstelement_class->get_clock = GST_DEBUG_FUNCPTR (gst_bin_get_clock_func); |
| gstelement_class->set_clock = GST_DEBUG_FUNCPTR (gst_bin_set_clock_func); |
| gstelement_class->set_manager = GST_DEBUG_FUNCPTR (gst_bin_set_manager); |
| gstelement_class->set_bus = GST_DEBUG_FUNCPTR (gst_bin_set_bus); |
| gstelement_class->set_scheduler = GST_DEBUG_FUNCPTR (gst_bin_set_scheduler); |
| |
| gstelement_class->send_event = GST_DEBUG_FUNCPTR (gst_bin_send_event); |
| gstelement_class->query = GST_DEBUG_FUNCPTR (gst_bin_query); |
| |
| klass->add_element = GST_DEBUG_FUNCPTR (gst_bin_add_func); |
| klass->remove_element = GST_DEBUG_FUNCPTR (gst_bin_remove_func); |
| } |
| |
| static void |
| gst_bin_init (GstBin * bin) |
| { |
| bin->numchildren = 0; |
| bin->children = NULL; |
| bin->children_cookie = 0; |
| } |
| |
| /** |
| * gst_bin_new: |
| * @name: name of new bin |
| * |
| * Create a new bin with given name. |
| * |
| * Returns: new bin |
| */ |
| GstElement * |
| gst_bin_new (const gchar * name) |
| { |
| return gst_element_factory_make ("bin", name); |
| } |
| |
| /* set the index on all elements in this bin |
| * |
| * MT safe |
| */ |
| #ifndef GST_DISABLE_INDEX |
| static void |
| gst_bin_set_index_func (GstElement * element, GstIndex * index) |
| { |
| GstBin *bin; |
| GList *children; |
| |
| bin = GST_BIN (element); |
| |
| GST_LOCK (bin); |
| for (children = bin->children; children; children = g_list_next (children)) { |
| GstElement *child = GST_ELEMENT (children->data); |
| |
| gst_element_set_index (child, index); |
| } |
| GST_UNLOCK (bin); |
| } |
| #endif |
| |
| /* set the clock on all elements in this bin |
| * |
| * MT safe |
| */ |
| static void |
| gst_bin_set_clock_func (GstElement * element, GstClock * clock) |
| { |
| GList *children; |
| GstBin *bin; |
| |
| bin = GST_BIN (element); |
| |
| GST_LOCK (bin); |
| for (children = bin->children; children; children = g_list_next (children)) { |
| GstElement *child = GST_ELEMENT (children->data); |
| |
| gst_element_set_clock (child, clock); |
| } |
| GST_UNLOCK (bin); |
| } |
| |
| /* get the clock for this bin by asking all of the children in this bin |
| * |
| * MT safe |
| */ |
| static GstClock * |
| gst_bin_get_clock_func (GstElement * element) |
| { |
| GstClock *result = NULL; |
| GstBin *bin; |
| GList *children; |
| |
| bin = GST_BIN (element); |
| |
| GST_LOCK (bin); |
| for (children = bin->children; children; children = g_list_next (children)) { |
| GstElement *child = GST_ELEMENT (children->data); |
| |
| result = gst_element_get_clock (child); |
| if (result) |
| break; |
| } |
| GST_UNLOCK (bin); |
| |
| return result; |
| } |
| |
| /* set the bus on all of the children in this bin |
| * |
| * MT safe |
| */ |
| static void |
| gst_bin_set_bus (GstElement * element, GstBus * bus) |
| { |
| GList *children; |
| GstBin *bin; |
| |
| bin = GST_BIN (element); |
| |
| parent_class->set_bus (element, bus); |
| |
| GST_LOCK (bin); |
| for (children = bin->children; children; children = g_list_next (children)) { |
| GstElement *child = GST_ELEMENT (children->data); |
| |
| gst_element_set_bus (child, bus); |
| } |
| GST_UNLOCK (bin); |
| } |
| |
| /* set the scheduler on all of the children in this bin |
| * |
| * MT safe |
| */ |
| static void |
| gst_bin_set_scheduler (GstElement * element, GstScheduler * sched) |
| { |
| GList *children; |
| GstBin *bin; |
| |
| bin = GST_BIN (element); |
| |
| parent_class->set_scheduler (element, sched); |
| |
| GST_LOCK (bin); |
| for (children = bin->children; children; children = g_list_next (children)) { |
| GstElement *child = GST_ELEMENT (children->data); |
| |
| gst_element_set_scheduler (child, sched); |
| } |
| GST_UNLOCK (bin); |
| } |
| |
| /* set the manager on all of the children in this bin |
| * |
| * MT safe |
| */ |
| static void |
| gst_bin_set_manager (GstElement * element, GstPipeline * manager) |
| { |
| GstBin *bin = GST_BIN (element); |
| GList *kids; |
| GstElement *kid; |
| |
| GST_ELEMENT_CLASS (parent_class)->set_manager (element, manager); |
| |
| GST_LOCK (element); |
| for (kids = bin->children; kids != NULL; kids = kids->next) { |
| kid = GST_ELEMENT (kids->data); |
| gst_element_set_manager (kid, manager); |
| } |
| GST_UNLOCK (element); |
| } |
| |
| /* add an element to this bin |
| * |
| * MT safe |
| */ |
| static gboolean |
| gst_bin_add_func (GstBin * bin, GstElement * element) |
| { |
| gchar *elem_name; |
| |
| /* we obviously can't add ourself to ourself */ |
| if (G_UNLIKELY (GST_ELEMENT_CAST (element) == GST_ELEMENT_CAST (bin))) |
| goto adding_itself; |
| |
| /* get the element name to make sure it is unique in this bin, FIXME, another |
| * thread can change the name after the unlock. */ |
| GST_LOCK (element); |
| elem_name = g_strdup (GST_ELEMENT_NAME (element)); |
| GST_UNLOCK (element); |
| |
| GST_LOCK (bin); |
| |
| /* then check to see if the element's name is already taken in the bin, |
| * we can safely take the lock here. This check is probably bogus because |
| * you can safely change the element name after adding it to the bin. */ |
| if (G_UNLIKELY (gst_object_check_uniqueness (bin->children, |
| elem_name) == FALSE)) |
| goto duplicate_name; |
| |
| /* set the element's parent and add the element to the bin's list of children */ |
| if (G_UNLIKELY (!gst_object_set_parent (GST_OBJECT_CAST (element), |
| GST_OBJECT_CAST (bin)))) |
| goto had_parent; |
| |
| if (GST_FLAG_IS_SET (element, GST_ELEMENT_IS_SINK)) |
| GST_FLAG_SET (bin, GST_ELEMENT_IS_SINK); |
| |
| bin->children = g_list_prepend (bin->children, element); |
| bin->numchildren++; |
| bin->children_cookie++; |
| |
| gst_element_set_manager (element, GST_ELEMENT (bin)->manager); |
| gst_element_set_bus (element, GST_ELEMENT (bin)->bus); |
| gst_element_set_scheduler (element, GST_ELEMENT_SCHEDULER (bin)); |
| gst_element_set_clock (element, GST_ELEMENT_CLOCK (bin)); |
| |
| GST_UNLOCK (bin); |
| |
| GST_CAT_DEBUG_OBJECT (GST_CAT_PARENTAGE, bin, "added element \"%s\"", |
| elem_name); |
| g_free (elem_name); |
| |
| g_signal_emit (G_OBJECT (bin), gst_bin_signals[ELEMENT_ADDED], 0, element); |
| |
| return TRUE; |
| |
| /* ERROR handling here */ |
| adding_itself: |
| { |
| GST_LOCK (bin); |
| g_warning ("Cannot add bin %s to itself", GST_ELEMENT_NAME (bin)); |
| GST_UNLOCK (bin); |
| return FALSE; |
| } |
| duplicate_name: |
| { |
| g_warning ("Name %s is not unique in bin %s, not adding", |
| elem_name, GST_ELEMENT_NAME (bin)); |
| GST_UNLOCK (bin); |
| g_free (elem_name); |
| return FALSE; |
| } |
| had_parent: |
| { |
| g_warning ("Element %s already has parent", elem_name); |
| GST_UNLOCK (bin); |
| g_free (elem_name); |
| return FALSE; |
| } |
| } |
| |
| /** |
| * gst_bin_add: |
| * @bin: #GstBin to add element to |
| * @element: #GstElement to add to bin |
| * |
| * Adds the given element to the bin. Sets the element's parent, and thus |
| * takes ownership of the element. An element can only be added to one bin. |
| * |
| * MT safe. |
| * |
| * Returns: TRUE if the element could be added, FALSE on wrong parameters or |
| * the bin does not want to accept the element. |
| */ |
| gboolean |
| gst_bin_add (GstBin * bin, GstElement * element) |
| { |
| GstBinClass *bclass; |
| gboolean result; |
| |
| g_return_val_if_fail (GST_IS_BIN (bin), FALSE); |
| g_return_val_if_fail (GST_IS_ELEMENT (element), FALSE); |
| |
| bclass = GST_BIN_GET_CLASS (bin); |
| |
| if (G_UNLIKELY (bclass->add_element == NULL)) |
| goto no_function; |
| |
| GST_CAT_DEBUG (GST_CAT_PARENTAGE, "adding element %s to bin %s", |
| GST_ELEMENT_NAME (element), GST_ELEMENT_NAME (bin)); |
| |
| result = bclass->add_element (bin, element); |
| |
| return result; |
| |
| /* ERROR handling */ |
| no_function: |
| { |
| g_warning ("adding elements to bin %s is not supported", |
| GST_ELEMENT_NAME (bin)); |
| return FALSE; |
| } |
| } |
| |
| /* remove an element from the bin |
| * |
| * MT safe |
| */ |
| static gboolean |
| gst_bin_remove_func (GstBin * bin, GstElement * element) |
| { |
| gchar *elem_name; |
| |
| /* grab element name so we can print it */ |
| GST_LOCK (element); |
| elem_name = g_strdup (GST_ELEMENT_NAME (element)); |
| GST_UNLOCK (element); |
| |
| GST_LOCK (bin); |
| /* the element must be in the bin's list of children */ |
| if (G_UNLIKELY (g_list_find (bin->children, element) == NULL)) |
| goto not_in_bin; |
| |
| /* now remove the element from the list of elements */ |
| bin->children = g_list_remove (bin->children, element); |
| bin->numchildren--; |
| bin->children_cookie++; |
| |
| /* check if we a sink */ |
| if (GST_FLAG_IS_SET (element, GST_ELEMENT_IS_SINK)) { |
| GList *other_sink; |
| |
| /* check if we removed the last sink */ |
| other_sink = g_list_find_custom (bin->children, |
| bin, (GCompareFunc) bin_element_is_sink); |
| if (!other_sink) { |
| /* yups, we're not a sink anymore */ |
| GST_FLAG_UNSET (bin, GST_ELEMENT_IS_SINK); |
| } |
| } |
| |
| GST_UNLOCK (bin); |
| |
| GST_CAT_INFO_OBJECT (GST_CAT_PARENTAGE, bin, "removed child \"%s\"", |
| elem_name); |
| g_free (elem_name); |
| |
| gst_element_set_manager (element, NULL); |
| gst_element_set_bus (element, NULL); |
| gst_element_set_scheduler (element, NULL); |
| |
| /* unlock any waiters for the state change. It is possible that |
| * we are waiting for an ASYNC state change on this element. The |
| * element cannot be added to another bin yet as it is not yet |
| * unparented. */ |
| GST_STATE_LOCK (element); |
| GST_STATE_BROADCAST (element); |
| GST_STATE_UNLOCK (element); |
| |
| /* we ref here because after the _unparent() the element can be disposed |
| * and we still need it to fire a signal. */ |
| gst_object_ref (element); |
| gst_object_unparent (GST_OBJECT_CAST (element)); |
| |
| g_signal_emit (G_OBJECT (bin), gst_bin_signals[ELEMENT_REMOVED], 0, element); |
| /* element is really out of our control now */ |
| gst_object_unref (element); |
| |
| return TRUE; |
| |
| /* ERROR handling */ |
| not_in_bin: |
| { |
| g_warning ("Element %s is not in bin %s", elem_name, |
| GST_ELEMENT_NAME (bin)); |
| GST_UNLOCK (bin); |
| g_free (elem_name); |
| return FALSE; |
| } |
| } |
| |
| /** |
| * gst_bin_remove: |
| * @bin: #GstBin to remove element from |
| * @element: #GstElement to remove |
| * |
| * Remove the element from its associated bin, unparenting it as well. |
| * Unparenting the element means that the element will be dereferenced, |
| * so if the bin holds the only reference to the element, the element |
| * will be freed in the process of removing it from the bin. If you |
| * want the element to still exist after removing, you need to call |
| * #gst_object_ref before removing it from the bin. |
| * |
| * MT safe. |
| * |
| * Returns: TRUE if the element could be removed, FALSE on wrong parameters or |
| * the bin does not want to remove the element. |
| */ |
| gboolean |
| gst_bin_remove (GstBin * bin, GstElement * element) |
| { |
| GstBinClass *bclass; |
| gboolean result; |
| |
| g_return_val_if_fail (GST_IS_BIN (bin), FALSE); |
| g_return_val_if_fail (GST_IS_ELEMENT (element), FALSE); |
| |
| bclass = GST_BIN_GET_CLASS (bin); |
| |
| if (G_UNLIKELY (bclass->remove_element == NULL)) |
| goto no_function; |
| |
| GST_CAT_DEBUG (GST_CAT_PARENTAGE, "removing element %s from bin %s", |
| GST_ELEMENT_NAME (element), GST_ELEMENT_NAME (bin)); |
| |
| result = bclass->remove_element (bin, element); |
| |
| return result; |
| |
| /* ERROR handling */ |
| no_function: |
| { |
| g_warning ("removing elements from bin %s is not supported", |
| GST_ELEMENT_NAME (bin)); |
| return FALSE; |
| } |
| } |
| |
| static GstIteratorItem |
| iterate_child (GstIterator * it, GstElement * child) |
| { |
| gst_object_ref (child); |
| return GST_ITERATOR_ITEM_PASS; |
| } |
| |
| /** |
| * gst_bin_iterate_elements: |
| * @bin: #Gstbin to iterate the elements of |
| * |
| * Get an iterator for the elements in this bin. |
| * Each element will have its refcount increased, so unref |
| * after use. |
| * |
| * MT safe. |
| * |
| * Returns: a #GstIterator of #GstElements. gst_iterator_free after |
| * use. returns NULL when passing bad parameters. |
| */ |
| GstIterator * |
| gst_bin_iterate_elements (GstBin * bin) |
| { |
| GstIterator *result; |
| |
| g_return_val_if_fail (GST_IS_BIN (bin), NULL); |
| |
| GST_LOCK (bin); |
| /* add ref because the iterator refs the bin. When the iterator |
| * is freed it will unref the bin again using the provided dispose |
| * function. */ |
| gst_object_ref (bin); |
| result = gst_iterator_new_list (GST_GET_LOCK (bin), |
| &bin->children_cookie, |
| &bin->children, |
| bin, |
| (GstIteratorItemFunction) iterate_child, |
| (GstIteratorDisposeFunction) gst_object_unref); |
| GST_UNLOCK (bin); |
| |
| return result; |
| } |
| |
| static GstIteratorItem |
| iterate_child_recurse (GstIterator * it, GstElement * child) |
| { |
| gst_object_ref (child); |
| if (GST_IS_BIN (child)) { |
| GstIterator *other = gst_bin_iterate_recurse (GST_BIN (child)); |
| |
| gst_iterator_push (it, other); |
| } |
| return GST_ITERATOR_ITEM_PASS; |
| } |
| |
| /** |
| * gst_bin_iterate_recurse: |
| * @bin: #Gstbin to iterate the elements of |
| * |
| * Get an iterator for the elements in this bin. |
| * Each element will have its refcount increased, so unref |
| * after use. This iterator recurses into GstBin children. |
| * |
| * MT safe. |
| * |
| * Returns: a #GstIterator of #GstElements. gst_iterator_free after |
| * use. returns NULL when passing bad parameters. |
| */ |
| GstIterator * |
| gst_bin_iterate_recurse (GstBin * bin) |
| { |
| GstIterator *result; |
| |
| g_return_val_if_fail (GST_IS_BIN (bin), NULL); |
| |
| GST_LOCK (bin); |
| /* add ref because the iterator refs the bin. When the iterator |
| * is freed it will unref the bin again using the provided dispose |
| * function. */ |
| gst_object_ref (bin); |
| result = gst_iterator_new_list (GST_GET_LOCK (bin), |
| &bin->children_cookie, |
| &bin->children, |
| bin, |
| (GstIteratorItemFunction) iterate_child_recurse, |
| (GstIteratorDisposeFunction) gst_object_unref); |
| GST_UNLOCK (bin); |
| |
| return result; |
| } |
| |
| /* returns 0 when TRUE because this is a GCompareFunc */ |
| /* MT safe */ |
| static gint |
| bin_element_is_sink (GstElement * child, GstBin * bin) |
| { |
| gboolean is_sink; |
| |
| /* we lock the child here for the remainder of the function to |
| * get its name safely. */ |
| GST_LOCK (child); |
| is_sink = GST_FLAG_IS_SET (child, GST_ELEMENT_IS_SINK); |
| GST_UNLOCK (child); |
| |
| GST_CAT_DEBUG_OBJECT (GST_CAT_STATES, bin, |
| "child %s %s sink", GST_OBJECT_NAME (child), is_sink ? "is" : "is not"); |
| |
| return is_sink ? 0 : 1; |
| } |
| |
| static gboolean |
| has_ancestor (GstObject * object, GstObject * ancestor) |
| { |
| GstObject *parent; |
| gboolean result = FALSE; |
| |
| if (object == NULL) |
| return FALSE; |
| |
| if (object == ancestor) |
| return TRUE; |
| |
| parent = gst_object_get_parent (object); |
| result = has_ancestor (parent, ancestor); |
| if (parent) |
| gst_object_unref (parent); |
| |
| return result; |
| } |
| |
| /* returns 0 when TRUE because this is a GCompareFunc. |
| * This function returns elements that have no connected srcpads and |
| * are therefore not reachable from a real sink. */ |
| /* MT safe */ |
| static gint |
| bin_element_is_semi_sink (GstElement * child, GstBin * bin) |
| { |
| int ret = 1; |
| |
| /* we lock the child here for the remainder of the function to |
| * get its pads and name safely. */ |
| GST_LOCK (child); |
| |
| /* check if this is a sink element, these are the elements |
| * without (linked) source pads. */ |
| if (child->numsrcpads == 0) { |
| /* shortcut */ |
| GST_CAT_DEBUG_OBJECT (GST_CAT_STATES, bin, |
| "adding child %s as sink", GST_OBJECT_NAME (child)); |
| ret = 0; |
| } else { |
| /* loop over all pads, try to figure out if this element |
| * is a semi sink because it has no linked source pads */ |
| GList *pads; |
| gboolean connected_src = FALSE; |
| |
| for (pads = child->srcpads; pads; pads = g_list_next (pads)) { |
| GstPad *peer; |
| |
| if ((peer = gst_pad_get_peer (GST_PAD_CAST (pads->data)))) { |
| connected_src = |
| has_ancestor (GST_OBJECT_CAST (peer), GST_OBJECT_CAST (bin)); |
| gst_object_unref (peer); |
| if (connected_src) { |
| break; |
| } |
| } |
| } |
| if (connected_src) { |
| GST_CAT_DEBUG_OBJECT (GST_CAT_STATES, bin, |
| "not adding child %s as sink: linked source pads", |
| GST_OBJECT_NAME (child)); |
| } else { |
| GST_CAT_DEBUG_OBJECT (GST_CAT_STATES, bin, |
| "adding child %s as sink since it has unlinked source pads in this bin", |
| GST_OBJECT_NAME (child)); |
| ret = 0; |
| } |
| } |
| GST_UNLOCK (child); |
| |
| return ret; |
| } |
| |
| static gint |
| sink_iterator_filter (GstElement * child, GstBin * bin) |
| { |
| if (bin_element_is_sink (child, bin) == 0) { |
| /* returns 0 because this is a GCompareFunc */ |
| return 0; |
| } else { |
| /* child carries a ref from gst_bin_iterate_elements -- drop if not passing |
| through */ |
| gst_object_unref ((GstObject *) child); |
| return 1; |
| } |
| } |
| |
| /** |
| * gst_bin_iterate_sinks: |
| * @bin: #Gstbin to iterate on |
| * |
| * Get an iterator for the sink elements in this bin. |
| * Each element will have its refcount increased, so unref |
| * after use. |
| * |
| * The sink elements are those without any linked srcpads. |
| * |
| * MT safe. |
| * |
| * Returns: a #GstIterator of #GstElements. gst_iterator_free after use. |
| */ |
| GstIterator * |
| gst_bin_iterate_sinks (GstBin * bin) |
| { |
| GstIterator *children; |
| GstIterator *result; |
| |
| g_return_val_if_fail (GST_IS_BIN (bin), NULL); |
| |
| children = gst_bin_iterate_elements (bin); |
| result = gst_iterator_filter (children, |
| (GCompareFunc) sink_iterator_filter, bin); |
| |
| return result; |
| } |
| |
| /* 2 phases: |
| * 1) check state of all children with 0 timeout to find ERROR and |
| * NO_PREROLL elements. return if found. |
| * 2) perform full blocking wait with requested timeout. |
| * |
| * 2) cannot be performed when 1) returns results as the sinks might |
| * not be able to complete the state change making 2) block forever. |
| * |
| * MT safe |
| */ |
| static GstElementStateReturn |
| gst_bin_get_state (GstElement * element, GstElementState * state, |
| GstElementState * pending, GTimeVal * timeout) |
| { |
| GstBin *bin = GST_BIN (element); |
| GstElementStateReturn ret = GST_STATE_SUCCESS; |
| GList *children; |
| guint32 children_cookie; |
| gboolean zero_timeout; |
| gboolean have_no_preroll; |
| |
| GST_CAT_INFO_OBJECT (GST_CAT_STATES, element, "getting state"); |
| |
| zero_timeout = timeout != NULL && timeout->tv_sec == 0 |
| && timeout->tv_usec == 0; |
| |
| /* lock bin, no element can be added or removed between going into |
| * the quick scan and the blocking wait. */ |
| GST_LOCK (bin); |
| |
| restart: |
| have_no_preroll = FALSE; |
| |
| /* if we have a non zero timeout we must make sure not to block |
| * on the sinks when we have NO_PREROLL elements. This is why we do |
| * a quick check if there are still NO_PREROLL elements. We also |
| * catch the error elements this way. */ |
| if (!zero_timeout) { |
| GTimeVal tv; |
| gboolean have_async = FALSE; |
| |
| GST_CAT_INFO_OBJECT (GST_CAT_STATES, element, "checking for NO_PREROLL"); |
| /* use 0 timeout so we don't block on the sinks */ |
| GST_TIME_TO_TIMEVAL (0, tv); |
| children = bin->children; |
| children_cookie = bin->children_cookie; |
| while (children) { |
| GstElement *child = GST_ELEMENT_CAST (children->data); |
| |
| gst_object_ref (child); |
| /* now we release the lock to enter a non blocking wait. We |
| * release the lock anyway since we can. */ |
| GST_UNLOCK (bin); |
| |
| ret = gst_element_get_state (child, NULL, NULL, &tv); |
| |
| gst_object_unref (child); |
| |
| /* now grab the lock to iterate to the next child */ |
| GST_LOCK (bin); |
| if (G_UNLIKELY (children_cookie != bin->children_cookie)) { |
| /* child added/removed during state change, restart. We need |
| * to restart with the quick check as a no-preroll element could |
| * have been added here and we don't want to block on sinks then.*/ |
| goto restart; |
| } |
| |
| switch (ret) { |
| /* report FAILURE or NO_PREROLL immediatly */ |
| case GST_STATE_FAILURE: |
| goto done; |
| case GST_STATE_NO_PREROLL: |
| /* we have to continue scanning as there might be |
| * ERRORS too */ |
| have_no_preroll = TRUE; |
| break; |
| case GST_STATE_ASYNC: |
| have_async = TRUE; |
| break; |
| default: |
| break; |
| } |
| children = g_list_next (children); |
| } |
| /* if we get here, we have no FAILURES, check for any NO_PREROLL |
| * elements then. */ |
| if (have_no_preroll) { |
| ret = GST_STATE_NO_PREROLL; |
| goto done; |
| } |
| |
| /* if we get here, no NO_PREROLL elements are in the pipeline */ |
| GST_CAT_INFO_OBJECT (GST_CAT_STATES, element, "no NO_PREROLL elements"); |
| |
| /* if no ASYNC elements exist we don't even have to poll with a |
| * timeout again */ |
| if (!have_async) { |
| ret = GST_STATE_SUCCESS; |
| goto done; |
| } |
| } |
| |
| /* next we poll all children for their state to see if one of them |
| * is still busy with its state change. We did not release the bin lock |
| * yet so the elements are the same as the ones from the quick scan. */ |
| children = bin->children; |
| children_cookie = bin->children_cookie; |
| while (children) { |
| GstElement *child = GST_ELEMENT_CAST (children->data); |
| |
| gst_object_ref (child); |
| /* now we release the lock to enter the potentialy blocking wait */ |
| GST_UNLOCK (bin); |
| |
| /* ret is ASYNC if some child is still performing the state change |
| * ater the timeout. */ |
| ret = gst_element_get_state (child, NULL, NULL, timeout); |
| |
| gst_object_unref (child); |
| |
| /* now grab the lock to iterate to the next child */ |
| GST_LOCK (bin); |
| if (G_UNLIKELY (children_cookie != bin->children_cookie)) { |
| /* child added/removed during state change, restart. We need |
| * to restart with the quick check as a no-preroll element could |
| * have been added here and we don't want to block on sinks then.*/ |
| goto restart; |
| } |
| |
| switch (ret) { |
| case GST_STATE_SUCCESS: |
| break; |
| case GST_STATE_FAILURE: |
| case GST_STATE_NO_PREROLL: |
| /* report FAILURE and NO_PREROLL immediatly */ |
| goto done; |
| break; |
| case GST_STATE_ASYNC: |
| /* since we checked for non prerollable elements before, |
| * the first ASYNC return is the real return value */ |
| if (!zero_timeout) |
| goto done; |
| break; |
| default: |
| g_assert_not_reached (); |
| } |
| children = g_list_next (children); |
| } |
| /* if we got here, all elements can do preroll */ |
| have_no_preroll = FALSE; |
| |
| done: |
| GST_UNLOCK (bin); |
| |
| /* now we can take the state lock, it is possible that new elements |
| * are added now and we still report the old state. No problem though as |
| * the return is still consistent, the effect is as if the element was |
| * added after this function completed. */ |
| GST_STATE_LOCK (bin); |
| switch (ret) { |
| case GST_STATE_SUCCESS: |
| /* we can commit the state */ |
| gst_element_commit_state (element); |
| break; |
| case GST_STATE_FAILURE: |
| /* some element failed, abort the state change */ |
| gst_element_abort_state (element); |
| break; |
| default: |
| /* other cases are just passed along */ |
| break; |
| } |
| |
| /* and report the state if needed */ |
| if (state) |
| *state = GST_STATE (element); |
| if (pending) |
| *pending = GST_STATE_PENDING (element); |
| |
| GST_STATE_NO_PREROLL (element) = have_no_preroll; |
| |
| GST_CAT_INFO_OBJECT (GST_CAT_STATES, element, |
| "state current: %s, pending: %s, error: %d, no_preroll: %d, result: %d", |
| gst_element_state_get_name (GST_STATE (element)), |
| gst_element_state_get_name (GST_STATE_PENDING (element)), |
| GST_STATE_ERROR (element), GST_STATE_NO_PREROLL (element), ret); |
| |
| GST_STATE_UNLOCK (bin); |
| |
| return ret; |
| } |
| |
| static void |
| append_child (gpointer child, GQueue * queue) |
| { |
| g_queue_push_tail (queue, child); |
| } |
| |
| /** |
| * gst_bin_iterate_state_order: |
| * @bin: #Gstbin to iterate on |
| * |
| * Get an iterator for the elements in this bin in the order |
| * in which a state change should be performed on them. This |
| * means that first the sinks and then the other elements will |
| * be returned. |
| * Each element will have its refcount increased, so unref |
| * after use. |
| * |
| * MT safe. |
| * |
| * Returns: a #GstIterator of #GstElements. gst_iterator_free after use. |
| */ |
| GstIterator * |
| gst_bin_iterate_state_order (GstBin * bin) |
| { |
| GstIterator *result; |
| |
| g_return_val_if_fail (GST_IS_BIN (bin), NULL); |
| |
| result = NULL; |
| |
| return result; |
| } |
| |
| /* this function is called with the STATE_LOCK held. It works |
| * as follows: |
| * |
| * 1) put all sink elements on the queue. |
| * 2) put all semisink elements on the queue. |
| * 3) change state of elements in queue, put linked elements to queue. |
| * 4) while queue not empty goto 3) |
| * |
| * This will effectively change the state of all elements in the bin |
| * from the sinks to the sources. We have to change the states this |
| * way so that when a source element pushes data, the downstream element |
| * is in the right state to receive the data. |
| * |
| * MT safe. |
| */ |
| static GstElementStateReturn |
| gst_bin_change_state (GstElement * element) |
| { |
| GstBin *bin; |
| GstElementStateReturn ret; |
| GstElementState old_state, pending; |
| gboolean have_async = FALSE; |
| gboolean have_no_preroll = FALSE; |
| GList *children; |
| guint32 children_cookie; |
| GQueue *elem_queue; /* list of elements waiting for a state change */ |
| GQueue *semi_queue; /* list of elements with no connected srcpads */ |
| GQueue *temp; /* queue of leftovers */ |
| |
| bin = GST_BIN (element); |
| |
| /* we don't need to take the STATE_LOCK, it is already taken */ |
| old_state = GST_STATE (element); |
| pending = GST_STATE_PENDING (element); |
| |
| GST_CAT_DEBUG_OBJECT (GST_CAT_STATES, element, |
| "changing state of children from %s to %s", |
| gst_element_state_get_name (old_state), |
| gst_element_state_get_name (pending)); |
| |
| if (pending == GST_STATE_VOID_PENDING) |
| return GST_STATE_SUCCESS; |
| |
| /* all elements added to these queues should have their refcount |
| * incremented */ |
| elem_queue = g_queue_new (); |
| semi_queue = g_queue_new (); |
| temp = g_queue_new (); |
| |
| /* first step, find all sink elements, these are the elements |
| * without (linked) source pads. */ |
| GST_LOCK (bin); |
| restart: |
| children = bin->children; |
| children_cookie = bin->children_cookie; |
| while (children) { |
| GstElement *child = GST_ELEMENT_CAST (children->data); |
| |
| gst_object_ref (child); |
| GST_UNLOCK (bin); |
| |
| if (bin_element_is_sink (child, bin) == 0) { |
| g_queue_push_tail (elem_queue, child); |
| } else if (bin_element_is_semi_sink (child, bin) == 0) { |
| g_queue_push_tail (semi_queue, child); |
| } else { |
| g_queue_push_tail (temp, child); |
| } |
| |
| GST_LOCK (bin); |
| if (G_UNLIKELY (children_cookie != bin->children_cookie)) { |
| /* undo what we had */ |
| g_queue_foreach (elem_queue, (GFunc) gst_object_unref, NULL); |
| g_queue_foreach (semi_queue, (GFunc) gst_object_unref, NULL); |
| g_queue_foreach (temp, (GFunc) gst_object_unref, NULL); |
| while (g_queue_pop_head (elem_queue)); |
| while (g_queue_pop_head (semi_queue)); |
| while (g_queue_pop_head (temp)); |
| goto restart; |
| } |
| |
| children = g_list_next (children); |
| } |
| GST_UNLOCK (bin); |
| |
| /* now change state for semi sink elements first so add them in |
| * front of the other elements */ |
| g_queue_foreach (temp, (GFunc) append_child, semi_queue); |
| g_queue_free (temp); |
| |
| /* can be the case for a bin like ( identity ) */ |
| if (g_queue_is_empty (elem_queue) && !g_queue_is_empty (semi_queue)) { |
| GQueue *q = elem_queue; |
| |
| elem_queue = semi_queue; |
| semi_queue = q; |
| } |
| |
| /* second step, change state of elements in the queue */ |
| while (!g_queue_is_empty (elem_queue)) { |
| GstElement *qelement; |
| GList *pads; |
| gboolean locked; |
| |
| /* take element */ |
| qelement = g_queue_pop_head (elem_queue); |
| /* we don't need it in the semi_queue anymore */ |
| g_queue_remove_all (semi_queue, qelement); |
| |
| /* if queue is empty now, continue with a non-sink */ |
| if (g_queue_is_empty (elem_queue)) { |
| GstElement *non_sink; |
| |
| GST_DEBUG ("sinks and upstream elements exhausted"); |
| non_sink = g_queue_pop_head (semi_queue); |
| if (non_sink) { |
| GST_DEBUG ("found lefover non-sink %s", GST_OBJECT_NAME (non_sink)); |
| g_queue_push_tail (elem_queue, non_sink); |
| } |
| } |
| |
| /* queue all elements connected to the sinkpads of this element */ |
| GST_LOCK (qelement); |
| pads = qelement->sinkpads; |
| while (pads) { |
| GstPad *pad = GST_PAD_CAST (pads->data); |
| GstPad *peer; |
| |
| GST_CAT_DEBUG_OBJECT (GST_CAT_STATES, element, |
| "found sinkpad %s:%s", GST_DEBUG_PAD_NAME (pad)); |
| |
| peer = gst_pad_get_peer (pad); |
| if (peer) { |
| GstElement *peer_elem; |
| |
| peer_elem = gst_pad_get_parent (peer); |
| |
| if (peer_elem) { |
| GstObject *parent; |
| |
| /* see if this element is in the bin we are currently handling */ |
| parent = gst_object_get_parent (GST_OBJECT_CAST (peer_elem)); |
| if (parent && parent == GST_OBJECT_CAST (bin)) { |
| GList *oldelem; |
| |
| GST_CAT_DEBUG_OBJECT (GST_CAT_STATES, element, |
| "adding element %s to queue", GST_ELEMENT_NAME (peer_elem)); |
| |
| /* make sure we don't have duplicates */ |
| while ((oldelem = g_queue_find (semi_queue, peer_elem))) { |
| gst_object_unref (peer_elem); |
| g_queue_delete_link (semi_queue, oldelem); |
| } |
| while ((oldelem = g_queue_find (elem_queue, peer_elem))) { |
| gst_object_unref (peer_elem); |
| g_queue_delete_link (elem_queue, oldelem); |
| } |
| /* was reffed before pushing on the queue by the |
| * gst_object_get_parent() call we used to get the element. */ |
| g_queue_push_tail (elem_queue, peer_elem); |
| } else { |
| GST_CAT_DEBUG_OBJECT (GST_CAT_STATES, element, |
| "not adding element %s to queue, it is in another bin", |
| GST_ELEMENT_NAME (peer_elem)); |
| gst_object_unref (peer_elem); |
| } |
| if (parent) { |
| gst_object_unref (parent); |
| } |
| } |
| gst_object_unref (peer); |
| } else { |
| GST_CAT_DEBUG_OBJECT (GST_CAT_STATES, element, |
| "pad %s:%s does not have a peer", GST_DEBUG_PAD_NAME (pad)); |
| } |
| pads = g_list_next (pads); |
| } |
| /* peel off the locked flag and release the element lock */ |
| locked = GST_FLAG_IS_SET (qelement, GST_ELEMENT_LOCKED_STATE); |
| GST_UNLOCK (qelement); |
| |
| if (G_UNLIKELY (locked)) |
| goto next_element; |
| |
| qelement->base_time = element->base_time; |
| ret = gst_element_set_state (qelement, pending); |
| switch (ret) { |
| case GST_STATE_SUCCESS: |
| GST_CAT_DEBUG (GST_CAT_STATES, |
| "child '%s' changed state to %d(%s) successfully", |
| GST_ELEMENT_NAME (qelement), pending, |
| gst_element_state_get_name (pending)); |
| break; |
| case GST_STATE_ASYNC: |
| GST_CAT_INFO_OBJECT (GST_CAT_STATES, element, |
| "child '%s' is changing state asynchronously", |
| GST_ELEMENT_NAME (qelement)); |
| have_async = TRUE; |
| break; |
| case GST_STATE_FAILURE: |
| GST_CAT_INFO_OBJECT (GST_CAT_STATES, element, |
| "child '%s' failed to go to state %d(%s)", |
| GST_ELEMENT_NAME (qelement), |
| pending, gst_element_state_get_name (pending)); |
| ret = GST_STATE_FAILURE; |
| /* release refcount of element we popped off the queue */ |
| gst_object_unref (qelement); |
| goto exit; |
| case GST_STATE_NO_PREROLL: |
| GST_CAT_DEBUG (GST_CAT_STATES, |
| "child '%s' changed state to %d(%s) successfully without preroll", |
| GST_ELEMENT_NAME (qelement), pending, |
| gst_element_state_get_name (pending)); |
| have_no_preroll = TRUE; |
| break; |
| default: |
| g_assert_not_reached (); |
| break; |
| } |
| next_element: |
| gst_object_unref (qelement); |
| } |
| |
| if (have_no_preroll) { |
| ret = GST_STATE_NO_PREROLL; |
| } else if (have_async) { |
| ret = GST_STATE_ASYNC; |
| } else { |
| if ((ret = parent_class->change_state (element)) == GST_STATE_SUCCESS) { |
| /* we can commit the state change now */ |
| gst_element_commit_state (element); |
| } |
| } |
| |
| GST_CAT_DEBUG_OBJECT (GST_CAT_STATES, element, |
| "done changing bin's state from %s to %s, now in %s", |
| gst_element_state_get_name (old_state), |
| gst_element_state_get_name (pending), |
| gst_element_state_get_name (GST_STATE (element))); |
| |
| exit: |
| /* release refcounts in queue, should normally be empty unless we |
| * had an error. */ |
| g_queue_foreach (elem_queue, (GFunc) gst_object_unref, NULL); |
| g_queue_free (elem_queue); |
| g_queue_foreach (semi_queue, (GFunc) gst_object_unref, NULL); |
| g_queue_free (semi_queue); |
| |
| return ret; |
| } |
| |
| static void |
| gst_bin_dispose (GObject * object) |
| { |
| GstBin *bin = GST_BIN (object); |
| |
| GST_CAT_DEBUG_OBJECT (GST_CAT_REFCOUNTING, object, "dispose"); |
| |
| /* ref to not hit 0 again */ |
| gst_object_ref (object); |
| |
| while (bin->children) { |
| gst_bin_remove (bin, GST_ELEMENT (bin->children->data)); |
| } |
| GST_CAT_DEBUG_OBJECT (GST_CAT_REFCOUNTING, object, "dispose no children"); |
| g_assert (bin->children == NULL); |
| g_assert (bin->numchildren == 0); |
| |
| G_OBJECT_CLASS (parent_class)->dispose (object); |
| } |
| |
| /* |
| * This function is a utility event handler for seek events. |
| * It will send the event to all sinks. |
| * Applications are free to override this behaviour and |
| * implement their own seek handler, but this will work for |
| * pretty much all cases in practice. |
| */ |
| static gboolean |
| gst_bin_send_event (GstElement * element, GstEvent * event) |
| { |
| GstBin *bin = GST_BIN (element); |
| GstIterator *iter; |
| gboolean res = TRUE; |
| gboolean done = FALSE; |
| |
| iter = gst_bin_iterate_sinks (bin); |
| GST_DEBUG_OBJECT (bin, "Sending event to sink children"); |
| |
| while (!done) { |
| gpointer data; |
| |
| switch (gst_iterator_next (iter, &data)) { |
| case GST_ITERATOR_OK: |
| { |
| GstElement *sink; |
| |
| gst_event_ref (event); |
| sink = GST_ELEMENT_CAST (data); |
| res &= gst_element_send_event (sink, event); |
| gst_object_unref (sink); |
| break; |
| } |
| case GST_ITERATOR_RESYNC: |
| gst_iterator_resync (iter); |
| res = TRUE; |
| break; |
| default: |
| case GST_ITERATOR_DONE: |
| done = TRUE; |
| break; |
| } |
| } |
| gst_iterator_free (iter); |
| gst_event_unref (event); |
| |
| return res; |
| } |
| |
| static gboolean |
| gst_bin_query (GstElement * element, GstQuery * query) |
| { |
| GstBin *bin = GST_BIN (element); |
| GstIterator *iter; |
| gboolean res = FALSE, done = FALSE; |
| |
| iter = gst_bin_iterate_sinks (bin); |
| GST_DEBUG_OBJECT (bin, "Sending event to sink children"); |
| |
| while (!(res || done)) { |
| gpointer data; |
| |
| switch (gst_iterator_next (iter, &data)) { |
| case GST_ITERATOR_OK: |
| { |
| GstElement *sink; |
| |
| sink = GST_ELEMENT_CAST (data); |
| res = gst_element_query (sink, query); |
| gst_object_unref (sink); |
| break; |
| } |
| case GST_ITERATOR_RESYNC: |
| gst_iterator_resync (iter); |
| break; |
| default: |
| case GST_ITERATOR_DONE: |
| done = TRUE; |
| break; |
| } |
| } |
| gst_iterator_free (iter); |
| |
| return res; |
| } |
| |
| static gint |
| compare_name (GstElement * element, const gchar * name) |
| { |
| gint eq; |
| |
| GST_LOCK (element); |
| eq = strcmp (GST_ELEMENT_NAME (element), name); |
| GST_UNLOCK (element); |
| |
| if (eq != 0) { |
| gst_object_unref (element); |
| } |
| return eq; |
| } |
| |
| /** |
| * gst_bin_get_by_name: |
| * @bin: #Gstbin to search |
| * @name: the element name to search for |
| * |
| * Get the element with the given name from this bin. This |
| * function recurses into subbins. |
| * |
| * MT safe. |
| * |
| * Returns: the element with the given name. Returns NULL if the |
| * element is not found or when bad parameters were given. Unref after |
| * use. |
| */ |
| GstElement * |
| gst_bin_get_by_name (GstBin * bin, const gchar * name) |
| { |
| GstIterator *children; |
| GstIterator *result; |
| |
| g_return_val_if_fail (GST_IS_BIN (bin), NULL); |
| |
| GST_CAT_INFO (GST_CAT_PARENTAGE, "[%s]: looking up child element %s", |
| GST_ELEMENT_NAME (bin), name); |
| |
| children = gst_bin_iterate_recurse (bin); |
| result = gst_iterator_find_custom (children, |
| (GCompareFunc) compare_name, (gpointer) name); |
| gst_iterator_free (children); |
| |
| return GST_ELEMENT_CAST (result); |
| } |
| |
| /** |
| * gst_bin_get_by_name_recurse_up: |
| * @bin: #Gstbin to search |
| * @name: the element name to search for |
| * |
| * MT safe. |
| * |
| * Get the element with the given name from this bin. If the |
| * element is not found, a recursion is performed on the parent bin. |
| * |
| * Returns: the element with the given name or NULL when the element |
| * was not found or bad parameters were given. Unref after use. |
| */ |
| GstElement * |
| gst_bin_get_by_name_recurse_up (GstBin * bin, const gchar * name) |
| { |
| GstElement *result; |
| |
| g_return_val_if_fail (GST_IS_BIN (bin), NULL); |
| g_return_val_if_fail (name != NULL, NULL); |
| |
| result = gst_bin_get_by_name (bin, name); |
| |
| if (!result) { |
| GstObject *parent; |
| |
| parent = gst_object_get_parent (GST_OBJECT_CAST (bin)); |
| if (parent) { |
| if (GST_IS_BIN (parent)) { |
| result = gst_bin_get_by_name_recurse_up (GST_BIN_CAST (parent), name); |
| } |
| gst_object_unref (parent); |
| } |
| } |
| |
| return result; |
| } |
| |
| static gint |
| compare_interface (GstElement * element, gpointer interface) |
| { |
| gint ret; |
| |
| if (G_TYPE_CHECK_INSTANCE_TYPE (element, GPOINTER_TO_INT (interface))) { |
| ret = 0; |
| } else { |
| /* we did not find the element, need to release the ref |
| * added by the iterator */ |
| gst_object_unref (element); |
| ret = 1; |
| } |
| return ret; |
| } |
| |
| /** |
| * gst_bin_get_by_interface: |
| * @bin: bin to find element in |
| * @interface: interface to be implemented by interface |
| * |
| * Looks for the first element inside the bin that implements the given |
| * interface. If such an element is found, it returns the element. You can |
| * cast this element to the given interface afterwards. |
| * If you want all elements that implement the interface, use |
| * gst_bin_iterate_all_by_interface(). The function recurses inside bins. |
| * |
| * MT safe. |
| * |
| * Returns: An #GstElement inside the bin implementing the interface. |
| * Unref after use. |
| */ |
| GstElement * |
| gst_bin_get_by_interface (GstBin * bin, GType interface) |
| { |
| GstIterator *children; |
| GstIterator *result; |
| |
| g_return_val_if_fail (GST_IS_BIN (bin), NULL); |
| |
| children = gst_bin_iterate_recurse (bin); |
| result = gst_iterator_find_custom (children, (GCompareFunc) compare_interface, |
| GINT_TO_POINTER (interface)); |
| gst_iterator_free (children); |
| |
| return GST_ELEMENT_CAST (result); |
| } |
| |
| /** |
| * gst_bin_get_all_by_interface: |
| * @bin: bin to find elements in |
| * @interface: interface to be implemented by interface |
| * |
| * Looks for all elements inside the bin that implements the given |
| * interface. You can safely cast all returned elements to the given interface. |
| * The function recurses bins inside bins. The iterator will return a series |
| * of #GstElement that should be unreffed after use. |
| * |
| * MT safe. |
| * |
| * Returns: A #GstIterator for the elements inside the bin implementing the |
| * given interface. |
| */ |
| GstIterator * |
| gst_bin_iterate_all_by_interface (GstBin * bin, GType interface) |
| { |
| GstIterator *children; |
| GstIterator *result; |
| |
| g_return_val_if_fail (GST_IS_BIN (bin), NULL); |
| |
| children = gst_bin_iterate_recurse (bin); |
| result = gst_iterator_filter (children, (GCompareFunc) compare_interface, |
| GINT_TO_POINTER (interface)); |
| |
| return result; |
| } |
| |
| #ifndef GST_DISABLE_LOADSAVE |
| static xmlNodePtr |
| gst_bin_save_thyself (GstObject * object, xmlNodePtr parent) |
| { |
| GstBin *bin = GST_BIN (object); |
| xmlNodePtr childlist, elementnode; |
| GList *children; |
| GstElement *child; |
| |
| if (GST_OBJECT_CLASS (parent_class)->save_thyself) |
| GST_OBJECT_CLASS (parent_class)->save_thyself (GST_OBJECT (bin), parent); |
| |
| childlist = xmlNewChild (parent, NULL, (xmlChar *) "children", NULL); |
| |
| GST_CAT_INFO (GST_CAT_XML, "[%s]: saving %d children", |
| GST_ELEMENT_NAME (bin), bin->numchildren); |
| |
| children = bin->children; |
| while (children) { |
| child = GST_ELEMENT (children->data); |
| elementnode = xmlNewChild (childlist, NULL, (xmlChar *) "element", NULL); |
| gst_object_save_thyself (GST_OBJECT (child), elementnode); |
| children = g_list_next (children); |
| } |
| return childlist; |
| } |
| |
| static void |
| gst_bin_restore_thyself (GstObject * object, xmlNodePtr self) |
| { |
| GstBin *bin = GST_BIN (object); |
| xmlNodePtr field = self->xmlChildrenNode; |
| xmlNodePtr childlist; |
| |
| while (field) { |
| if (!strcmp ((char *) field->name, "children")) { |
| GST_CAT_INFO (GST_CAT_XML, "[%s]: loading children", |
| GST_ELEMENT_NAME (object)); |
| childlist = field->xmlChildrenNode; |
| while (childlist) { |
| if (!strcmp ((char *) childlist->name, "element")) { |
| GstElement *element = |
| gst_xml_make_element (childlist, GST_OBJECT (bin)); |
| |
| /* it had to be parented to find the pads, now we ref and unparent so |
| * we can add it to the bin */ |
| gst_object_ref (element); |
| gst_object_unparent (GST_OBJECT (element)); |
| |
| gst_bin_add (bin, element); |
| } |
| childlist = childlist->next; |
| } |
| } |
| |
| field = field->next; |
| } |
| if (GST_OBJECT_CLASS (parent_class)->restore_thyself) |
| (GST_OBJECT_CLASS (parent_class)->restore_thyself) (object, self); |
| } |
| #endif /* GST_DISABLE_LOADSAVE */ |