check/gst/gstbin.c: Enable check that works now.

Original commit message from CVS:
* check/gst/gstbin.c: (GST_START_TEST):
Enable check that works now.

* gst/gstbin.c: (add_to_queue), (clear_queue), (reset_outdegree),
(update_outdegree), (find_element), (gst_bin_sort_iterator_next),
(gst_bin_sort_iterator_resync), (gst_bin_sort_iterator_free),
(gst_bin_iterate_sorted), (gst_bin_element_set_state),
(gst_bin_change_state):
* gst/gstbin.h:
Redid the state change algorithm using a topological sort algo.
Handles all cases correctly.
Exposed iterator for state change order.

* gst/gstelement.h:
Temp storage for state changes. Need to get rid of this soon.
This commit is contained in:
Wim Taymans 2005-09-27 16:16:39 +00:00
parent 590a0cfb57
commit 4d4a60f6c9
6 changed files with 320 additions and 320 deletions

View file

@ -1,3 +1,21 @@
2005-09-27 Wim Taymans <wim@fluendo.com>
* check/gst/gstbin.c: (GST_START_TEST):
Enable check that works now.
* gst/gstbin.c: (add_to_queue), (clear_queue), (reset_outdegree),
(update_outdegree), (find_element), (gst_bin_sort_iterator_next),
(gst_bin_sort_iterator_resync), (gst_bin_sort_iterator_free),
(gst_bin_iterate_sorted), (gst_bin_element_set_state),
(gst_bin_change_state):
* gst/gstbin.h:
Redid the state change algorithm using a topological sort algo.
Handles all cases correctly.
Exposed iterator for state change order.
* gst/gstelement.h:
Temp storage for state changes. Need to get rid of this soon.
2005-09-27 Wim Taymans <wim@fluendo.com>
* gst/elements/gsttee.c: (gst_tee_init), (gst_tee_do_push):

View file

@ -630,8 +630,6 @@ GST_END_TEST;
GST_START_TEST (test_children_state_change_order_two_sink)
{
/* current algorithm does not handle these state changes correct */
#if 0
GstElement *src, *tee, *identity, *sink1, *sink2, *pipeline;
GstStateChangeReturn ret;
GstBus *bus;
@ -721,7 +719,6 @@ GST_START_TEST (test_children_state_change_order_two_sink)
ASSERT_OBJECT_REFCOUNT (pipeline, "pipeline", 1);
gst_object_unref (pipeline);
#endif
}
GST_END_TEST;

View file

@ -824,62 +824,6 @@ bin_element_is_sink (GstElement * child, GstBin * bin)
return is_sink ? 0 : 1;
}
/* 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;
GST_DEBUG ("looking at pad %p", pads->data);
if ((peer = gst_pad_get_peer (GST_PAD_CAST (pads->data)))) {
connected_src =
gst_object_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)
{
@ -1106,90 +1050,272 @@ done:
return ret;
}
/**
* 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.
*
* Not implemented yet.
*
* MT safe.
*
* Returns: a #GstIterator of #GstElements. gst_iterator_free after use.
/***********************************************
* Topologically sorted iterator
* see http://en.wikipedia.org/wiki/Topological_sorting
*/
GstIterator *
gst_bin_iterate_state_order (GstBin * bin)
typedef struct _GstBinSortIterator
{
GstIterator *result;
GstIterator it;
GQueue *queue;
GstBin *bin;
gint mode;
GstElement *best;
} GstBinSortIterator;
g_return_val_if_fail (GST_IS_BIN (bin), NULL);
result = NULL;
return result;
/* add element to queue of next elements in the iterator.
* We push at the tail to give higher priority elements a
* chance first */
static void
add_to_queue (GQueue * queue, GstElement * element)
{
GST_DEBUG ("%s add to queue", GST_ELEMENT_NAME (element));
gst_object_ref (element);
g_queue_push_tail (queue, element);
element->outdegree = -1;
}
/* clear the queue, unref all objects as we took a ref when
* we added them to the queue */
static void
clear_queue (GQueue * queue, gboolean unref)
clear_queue (GQueue * queue)
{
gpointer p;
while ((p = g_queue_pop_head (queue)))
if (unref)
gst_object_unref (p);
gst_object_unref (p);
}
/* set all outdegrees to 0. Elements marked as a sink are
* added to the queue immediatly. */
static void
remove_all_from_queue (GQueue * queue, gpointer elem, gboolean unref)
reset_outdegree (GstElement * element, GstBinSortIterator * bit)
{
gpointer p;
while ((p = g_queue_find (queue, elem))) {
if (unref)
gst_object_unref (elem);
g_queue_delete_link (queue, p);
/* sinks are added right away */
if (GST_FLAG_IS_SET (element, GST_ELEMENT_IS_SINK)) {
add_to_queue (bit->queue, element);
} else {
/* others are marked with 0 and handled when sinks are done */
element->outdegree = 0;
}
}
/* this function is called with the STATE_LOCK held. It works
* as follows:
/* adjust the outdegree of all elements connected to the given
* element. If an outdegree of an element drops to 0, it is
* added to the queue of elements to schedule next.
*
* 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)
* We have to make sure not to cross the bin boundary this element
* belongs to.
*/
static void
update_outdegree (GstElement * element, GstBinSortIterator * bit)
{
gboolean linked = FALSE;
GST_LOCK (element);
/* don't touch outdegree is element has no sourcepads */
if (element->numsinkpads != 0) {
/* loop over all sinkpads, decrement outdegree for all connected
* elements in this bin */
GList *pads;
for (pads = element->sinkpads; pads; pads = g_list_next (pads)) {
GstPad *peer;
if ((peer = gst_pad_get_peer (GST_PAD_CAST (pads->data)))) {
GstElement *peer_element;
if ((peer_element = gst_pad_get_parent_element (peer))) {
GST_LOCK (peer_element);
if (GST_OBJECT_CAST (peer_element)->parent ==
GST_OBJECT_CAST (bit->bin)) {
GST_DEBUG ("change element %s, degree %d->%d, linked to %s",
GST_ELEMENT_NAME (peer_element),
peer_element->outdegree, peer_element->outdegree + bit->mode,
GST_ELEMENT_NAME (element));
/* update outdegree */
peer_element->outdegree += bit->mode;
if (peer_element->outdegree == 0) {
/* outdegree hit 0, add to queue */
add_to_queue (bit->queue, peer_element);
}
linked = TRUE;
}
GST_UNLOCK (peer_element);
gst_object_unref (peer_element);
}
gst_object_unref (peer);
}
}
}
if (!linked) {
GST_DEBUG ("element %s not linked to anything", GST_ELEMENT_NAME (element));
}
GST_UNLOCK (element);
}
/* find the next best element not handled yet. This is the one
* with the lowest non-negative outdegree */
static void
find_element (GstElement * element, GstBinSortIterator * bit)
{
gint outdegree;
/* element is already handled */
if ((outdegree = element->outdegree) < 0)
return;
/* first element or element with smaller outdegree */
if (bit->best == NULL || bit->best->outdegree > outdegree) {
bit->best = element;
}
}
/* get next element in iterator. the returned element has the
* refcount increased */
static GstIteratorResult
gst_bin_sort_iterator_next (GstBinSortIterator * bit, gpointer * result)
{
/* empty queue, we have to find a next best element */
if (g_queue_is_empty (bit->queue)) {
bit->best = NULL;
g_list_foreach (bit->bin->children, (GFunc) find_element, bit);
if (bit->best) {
if (bit->best->outdegree != 0) {
/* we don't fail on this one yet */
g_warning ("loop detected in the graph !!");
}
/* best unhandled elements, add to queue */
GST_DEBUG ("queue empty, next best: %s", GST_ELEMENT_NAME (bit->best));
gst_object_ref (bit->best);
bit->best->outdegree = -1;
*result = bit->best;
} else {
GST_DEBUG ("queue empty, elements exhausted");
/* no more unhandled elements, we are done */
return GST_ITERATOR_DONE;
}
} else {
/* everything added to the queue got reffed */
*result = g_queue_pop_head (bit->queue);
}
GST_DEBUG ("queue head gives %s", GST_ELEMENT_NAME (*result));
/* update outdegrees of linked elements */
update_outdegree (GST_ELEMENT_CAST (*result), bit);
return GST_ITERATOR_OK;
}
/* clear queues, recalculate the outdegrees and restart. */
static void
gst_bin_sort_iterator_resync (GstBinSortIterator * bit)
{
clear_queue (bit->queue);
/* reset outdegrees */
g_list_foreach (bit->bin->children, (GFunc) reset_outdegree, bit);
/* calc outdegrees, incrementing */
bit->mode = 1;
g_list_foreach (bit->bin->children, (GFunc) update_outdegree, bit);
/* for the rest of the function we decrement the outdegrees */
bit->mode = -1;
}
/* clear queues, unref bin and free iterator. */
static void
gst_bin_sort_iterator_free (GstBinSortIterator * bit)
{
clear_queue (bit->queue);
g_queue_free (bit->queue);
gst_object_unref (bit->bin);
g_free (bit);
}
/**
* gst_bin_iterate_sorted:
* @bin: #Gstbin to iterate on
*
* 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.
* Get an iterator for the elements in this bin in topologically
* sorted order. This means that the elements are returned from
* the most downstream elements (sinks) to the sources.
*
* This function is used internally to perform the state changes
* of the bin elements.
*
* Each element will have its refcount increased, so unref
* after use.
*
* MT safe.
*
* FIXME: No two iterators can run at the same time since the iterators
* use a shared element field.
*
* Returns: a #GstIterator of #GstElements. gst_iterator_free after use.
*/
/* FIXME, make me more elegant, want to use a topological sort algorithm
* based on indegrees (or outdegrees in our case) */
GstIterator *
gst_bin_iterate_sorted (GstBin * bin)
{
GstBinSortIterator *result;
g_return_val_if_fail (GST_IS_BIN (bin), NULL);
GST_LOCK (bin);
gst_object_ref (bin);
/* we don't need a NextFunction because we ref the items in the _next
* method already */
result = (GstBinSortIterator *)
gst_iterator_new (sizeof (GstBinSortIterator),
GST_GET_LOCK (bin),
&bin->children_cookie,
(GstIteratorNextFunction) gst_bin_sort_iterator_next,
(GstIteratorItemFunction) NULL,
(GstIteratorResyncFunction) gst_bin_sort_iterator_resync,
(GstIteratorFreeFunction) gst_bin_sort_iterator_free);
result->queue = g_queue_new ();
result->bin = bin;
gst_bin_sort_iterator_resync (result);
GST_UNLOCK (bin);
return (GstIterator *) result;
}
static GstStateChangeReturn
gst_bin_element_set_state (GstBin * bin, GstElement * element, GstState pending)
{
GstStateChangeReturn ret;
gboolean locked;
/* peel off the locked flag */
GST_LOCK (element);
locked = GST_FLAG_IS_SET (element, GST_ELEMENT_LOCKED_STATE);
GST_UNLOCK (element);
/* skip locked elements */
if (G_UNLIKELY (locked)) {
ret = GST_STATE_CHANGE_SUCCESS;
goto done;
}
/* change state */
ret = gst_element_set_state (element, pending);
done:
return ret;
}
static GstStateChangeReturn
gst_bin_change_state (GstElement * element, GstStateChange transition)
{
GstBin *bin;
GstStateChangeReturn ret;
GstState 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 */
gboolean have_async;
gboolean have_no_preroll;
GstClockTime base_time;
bin = GST_BIN (element);
GstIterator *it;
gboolean done;
/* we don't need to take the STATE_LOCK, it is already taken */
old_state = GST_STATE (element);
@ -1203,220 +1329,84 @@ gst_bin_change_state (GstElement * element, GstStateChange transition)
if (pending == GST_STATE_VOID_PENDING)
return GST_STATE_CHANGE_SUCCESS;
bin = GST_BIN_CAST (element);
/* Clear eosed element list on READY-> PAUSED */
if (transition == GST_STATE_CHANGE_READY_TO_PAUSED) {
g_list_free (bin->eosed);
bin->eosed = NULL;
}
/* 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);
/* iterate in state change order */
it = gst_bin_iterate_sorted (bin);
restart:
/* take base time */
base_time = element->base_time;
/* make sure queues are empty, they could be filled when
* restarting. */
clear_queue (elem_queue, TRUE);
clear_queue (semi_queue, TRUE);
clear_queue (temp, TRUE);
have_async = FALSE;
have_no_preroll = FALSE;
children = bin->children;
children_cookie = bin->children_cookie;
GST_DEBUG_OBJECT (bin, "reffing and examining children");
while (children) {
GstElement *child = GST_ELEMENT_CAST (children->data);
done = FALSE;
while (!done) {
gpointer data;
gst_object_ref (child);
GST_UNLOCK (bin);
switch (gst_iterator_next (it, &data)) {
case GST_ITERATOR_OK:
{
GstElement *element;
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);
}
element = GST_ELEMENT_CAST (data);
GST_LOCK (bin);
if (G_UNLIKELY (children_cookie != bin->children_cookie)) {
GST_INFO_OBJECT (bin, "bin->children_cookie changed, restarting");
/* restart will unref the children in the queues so that we don't
* leak refcounts. */
goto restart;
}
children = g_list_next (children);
}
GST_DEBUG_OBJECT (bin, "reffed and examined children");
GST_UNLOCK (bin);
/* set base time on element */
gst_element_set_base_time (element, base_time);
/* after this point new elements can be added/removed from the
* bin. We operate on the snapshot taken above. Applications
* should serialize their add/remove and set_state. */
/* set state now */
ret = gst_bin_element_set_state (bin, element, pending);
/* if we don't have real sinks, we continue with the other elements, there
* has to be at least one element in the semi queue. */
if (g_queue_is_empty (elem_queue) && !g_queue_is_empty (semi_queue)) {
GQueue *q = elem_queue;
/* we swap the queues as oposed to copy them over */
elem_queue = semi_queue;
semi_queue = q;
}
/* second step, change state of elements in the queue */
GST_DEBUG_OBJECT (bin, "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 any duplicates in the other queue anymore */
remove_all_from_queue (semi_queue, qelement, TRUE);
remove_all_from_queue (temp, qelement, TRUE);
/* 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) {
GstObject *peer_parent;
/* get parent */
peer_parent = gst_object_get_parent (GST_OBJECT (peer));
/* if we have an element parent, follow it */
if (peer_parent && GST_IS_ELEMENT (peer_parent)) {
GstObject *parent;
/* see if this element is in the bin we are currently handling */
parent = gst_object_get_parent (peer_parent);
if (parent) {
if (parent == GST_OBJECT_CAST (bin)) {
GST_CAT_DEBUG_OBJECT (GST_CAT_STATES, element,
"adding element %s to queue", GST_ELEMENT_NAME (peer_parent));
/* make sure we don't have duplicates */
remove_all_from_queue (semi_queue, peer_parent, TRUE);
remove_all_from_queue (elem_queue, peer_parent, TRUE);
remove_all_from_queue (temp, peer_parent, TRUE);
/* 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_parent);
/* so that we don't unref it */
peer_parent = NULL;
} else {
GST_CAT_DEBUG_OBJECT (GST_CAT_STATES, element,
"not adding element %s to queue, it is in another bin",
GST_ELEMENT_NAME (peer_parent));
}
gst_object_unref (parent);
}
switch (ret) {
case GST_STATE_CHANGE_SUCCESS:
GST_CAT_DEBUG (GST_CAT_STATES,
"child '%s' changed state to %d(%s) successfully",
GST_ELEMENT_NAME (element), pending,
gst_element_state_get_name (pending));
break;
case GST_STATE_CHANGE_ASYNC:
GST_CAT_INFO_OBJECT (GST_CAT_STATES, element,
"child '%s' is changing state asynchronously",
GST_ELEMENT_NAME (element));
have_async = TRUE;
break;
case GST_STATE_CHANGE_FAILURE:
GST_CAT_INFO_OBJECT (GST_CAT_STATES, element,
"child '%s' failed to go to state %d(%s)",
GST_ELEMENT_NAME (element),
pending, gst_element_state_get_name (pending));
gst_object_unref (element);
goto done;
case GST_STATE_CHANGE_NO_PREROLL:
GST_CAT_DEBUG (GST_CAT_STATES,
"child '%s' changed state to %d(%s) successfully without preroll",
GST_ELEMENT_NAME (element), pending,
gst_element_state_get_name (pending));
have_no_preroll = TRUE;
break;
default:
g_assert_not_reached ();
break;
}
if (peer_parent)
gst_object_unref (peer_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));
gst_object_unref (element);
break;
}
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);
/* skip locked elements */
if (G_UNLIKELY (locked))
goto next_element;
/* set base time on element */
gst_element_set_base_time (qelement, base_time);
/* then change state */
ret = gst_element_set_state (qelement, pending);
/* the set state could have cause elements to be added/removed,
* we support that. */
GST_LOCK (bin);
if (G_UNLIKELY (children_cookie != bin->children_cookie)) {
gst_object_unref (qelement);
goto restart;
}
GST_UNLOCK (bin);
switch (ret) {
case GST_STATE_CHANGE_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_CHANGE_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_CHANGE_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_CHANGE_FAILURE;
/* release refcount of element we popped off the queue */
gst_object_unref (qelement);
goto exit;
case GST_STATE_CHANGE_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;
case GST_ITERATOR_RESYNC:
gst_iterator_resync (it);
goto restart;
break;
default:
g_assert_not_reached ();
case GST_ITERATOR_DONE:
done = TRUE;
break;
}
next_element:
gst_object_unref (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 semi-sink %s", GST_OBJECT_NAME (non_sink));
g_queue_push_tail (elem_queue, non_sink);
} else {
non_sink = g_queue_pop_head (temp);
if (non_sink) {
GST_DEBUG ("found lefover non-sink %s", GST_OBJECT_NAME (non_sink));
g_queue_push_tail (elem_queue, non_sink);
}
}
}
}
if (have_no_preroll) {
@ -1427,21 +1417,14 @@ restart:
ret = parent_class->change_state (element, transition);
}
done:
GST_CAT_DEBUG_OBJECT (GST_CAT_STATES, element,
"done changing bin's state from %s to %s, now in %s, ret %d",
gst_element_state_get_name (old_state),
gst_element_state_get_name (pending),
gst_element_state_get_name (GST_STATE (element)), ret);
exit:
/* release refcounts in queue, should normally be empty unless we
* had an error. */
clear_queue (elem_queue, TRUE);
clear_queue (semi_queue, TRUE);
clear_queue (temp, TRUE);
g_queue_free (elem_queue);
g_queue_free (semi_queue);
g_queue_free (temp);
gst_iterator_free (it);
return ret;
}

View file

@ -127,6 +127,7 @@ GstElement* gst_bin_get_by_interface (GstBin *bin, GType interface);
/* retrieve multiple children */
GstIterator* gst_bin_iterate_elements (GstBin *bin);
GstIterator* gst_bin_iterate_sorted (GstBin *bin);
GstIterator* gst_bin_iterate_recurse (GstBin *bin);
GstIterator* gst_bin_iterate_sinks (GstBin *bin);

View file

@ -304,6 +304,10 @@ struct _GstElement
GList *sinkpads;
guint32 pads_cookie;
/* used in bin state change to calculate number of connections
* on the srcpad */
gint outdegree;
/*< private >*/
gpointer _gst_reserved[GST_PADDING];
};

View file

@ -630,8 +630,6 @@ GST_END_TEST;
GST_START_TEST (test_children_state_change_order_two_sink)
{
/* current algorithm does not handle these state changes correct */
#if 0
GstElement *src, *tee, *identity, *sink1, *sink2, *pipeline;
GstStateChangeReturn ret;
GstBus *bus;
@ -721,7 +719,6 @@ GST_START_TEST (test_children_state_change_order_two_sink)
ASSERT_OBJECT_REFCOUNT (pipeline, "pipeline", 1);
gst_object_unref (pipeline);
#endif
}
GST_END_TEST;