mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2025-01-11 01:45:33 +00:00
gst/gstbin.*: Immediatly commit the toplevel bin state when receiving an async-done message. This enables us to avoid...
Original commit message from CVS: * gst/gstbin.c: (gst_bin_init), (gst_bin_add_func), (gst_bin_remove_func), (gst_bin_get_state_func), (gst_bin_element_set_state), (gst_bin_continue_func), (bin_push_state_continue), (bin_handle_async_start), (bin_handle_async_done), (gst_bin_handle_message_func): * gst/gstbin.h: Immediatly commit the toplevel bin state when receiving an async-done message. This enables us to avoid spawning a thread to commit the state in some common cases and it also avoids some races. Avoid spawning a state thread when adding/removing async elements to a toplevel bin. Instead we immediatly update the bin state. Get rid of iterating all the children when getting the state in the bin because it is now always up-to-date. Fix bug where locked elements would always return _SUCCESS even it they returned NO_PREROLL before being locked. Fix the order of the state_change, async-start/done messages that was sometimes incorrect. Mark the state_dirty field as deprecated, we don't need it anymore as we are always up-to-date. * gst/gstelement.c: (gst_element_get_state_func), (gst_element_continue_state): Small debug inprovements. Return the previous element state return when nothing is pending instead of blindly returning SUCCESS. * tests/check/generic/sinks.c: (GST_START_TEST), (pad_blocked_cb), (gst_sinks_suite): Add a whole bunch of new testcases.
This commit is contained in:
parent
43efcf4222
commit
0b3153873b
5 changed files with 692 additions and 385 deletions
32
ChangeLog
32
ChangeLog
|
@ -1,3 +1,35 @@
|
|||
2007-06-18 Wim Taymans <wim@fluendo.com>
|
||||
|
||||
* gst/gstbin.c: (gst_bin_init), (gst_bin_add_func),
|
||||
(gst_bin_remove_func), (gst_bin_get_state_func),
|
||||
(gst_bin_element_set_state), (gst_bin_continue_func),
|
||||
(bin_push_state_continue), (bin_handle_async_start),
|
||||
(bin_handle_async_done), (gst_bin_handle_message_func):
|
||||
* gst/gstbin.h:
|
||||
Immediatly commit the toplevel bin state when receiving an async-done
|
||||
message. This enables us to avoid spawning a thread to commit the state
|
||||
in some common cases and it also avoids some races.
|
||||
Avoid spawning a state thread when adding/removing async elements to a
|
||||
toplevel bin. Instead we immediatly update the bin state.
|
||||
Get rid of iterating all the children when getting the state in the bin
|
||||
because it is now always up-to-date.
|
||||
Fix bug where locked elements would always return _SUCCESS even it they
|
||||
returned NO_PREROLL before being locked.
|
||||
Fix the order of the state_change, async-start/done messages that was
|
||||
sometimes incorrect.
|
||||
Mark the state_dirty field as deprecated, we don't need it anymore as we
|
||||
are always up-to-date.
|
||||
|
||||
* gst/gstelement.c: (gst_element_get_state_func),
|
||||
(gst_element_continue_state):
|
||||
Small debug inprovements.
|
||||
Return the previous element state return when nothing is pending instead
|
||||
of blindly returning SUCCESS.
|
||||
|
||||
* tests/check/generic/sinks.c: (GST_START_TEST), (pad_blocked_cb),
|
||||
(gst_sinks_suite):
|
||||
Add a whole bunch of new testcases.
|
||||
|
||||
2007-06-17 Thomas Vander Stichele <thomas at apestaart dot org>
|
||||
|
||||
* po/uk.po:
|
||||
|
|
616
gst/gstbin.c
616
gst/gstbin.c
|
@ -196,6 +196,13 @@ struct _GstBinPrivate
|
|||
gboolean asynchandling;
|
||||
};
|
||||
|
||||
typedef struct
|
||||
{
|
||||
GstBin *bin;
|
||||
guint32 cookie;
|
||||
GstState pending;
|
||||
} BinContinueData;
|
||||
|
||||
static void gst_bin_dispose (GObject * object);
|
||||
|
||||
static void gst_bin_set_property (GObject * object, guint prop_id,
|
||||
|
@ -208,8 +215,9 @@ static GstStateChangeReturn gst_bin_change_state_func (GstElement * element,
|
|||
static GstStateChangeReturn gst_bin_get_state_func (GstElement * element,
|
||||
GstState * state, GstState * pending, GstClockTime timeout);
|
||||
static void bin_handle_async_done (GstBin * bin, GstMessage ** smessage,
|
||||
GstMessage ** amessage);
|
||||
static void bin_push_state_continue (GstBin * bin);
|
||||
GstMessage ** amessage, BinContinueData ** cont, GstStateChangeReturn ret);
|
||||
static void bin_handle_async_start (GstBin * bin, GstMessage ** message);
|
||||
static void bin_push_state_continue (BinContinueData * data);
|
||||
|
||||
static gboolean gst_bin_add_func (GstBin * bin, GstElement * element);
|
||||
static gboolean gst_bin_remove_func (GstBin * bin, GstElement * element);
|
||||
|
@ -233,7 +241,7 @@ static void gst_bin_restore_thyself (GstObject * object, xmlNodePtr self);
|
|||
|
||||
static void bin_remove_messages (GstBin * bin, GstObject * src,
|
||||
GstMessageType types);
|
||||
static void gst_bin_continue_func (GstBin * child, gpointer data);
|
||||
static void gst_bin_continue_func (BinContinueData * data);
|
||||
static gint bin_element_is_sink (GstElement * child, GstBin * bin);
|
||||
static gint bin_element_is_src (GstElement * child, GstBin * bin);
|
||||
|
||||
|
@ -460,7 +468,6 @@ gst_bin_init (GstBin * bin)
|
|||
bin->children_cookie = 0;
|
||||
bin->messages = NULL;
|
||||
bin->provided_clock = NULL;
|
||||
bin->state_dirty = FALSE;
|
||||
bin->clock_dirty = FALSE;
|
||||
|
||||
/* Set up a bus for listening to child elements */
|
||||
|
@ -847,7 +854,12 @@ gst_bin_add_func (GstBin * bin, GstElement * element)
|
|||
gchar *elem_name;
|
||||
GstIterator *it;
|
||||
gboolean is_sink;
|
||||
GstMessage *clock_message = NULL;
|
||||
GstMessage *clock_message = NULL, *async_message = NULL, *state_message =
|
||||
NULL;
|
||||
BinContinueData *cont = NULL;
|
||||
GstStateChangeReturn ret;
|
||||
|
||||
GST_DEBUG_OBJECT (bin, "element :%s", GST_ELEMENT_NAME (element));
|
||||
|
||||
/* we obviously can't add ourself to ourself */
|
||||
if (G_UNLIKELY (GST_ELEMENT_CAST (element) == GST_ELEMENT_CAST (bin)))
|
||||
|
@ -890,6 +902,32 @@ gst_bin_add_func (GstBin * bin, GstElement * element)
|
|||
bin->numchildren++;
|
||||
bin->children_cookie++;
|
||||
|
||||
ret = GST_STATE_RETURN (bin);
|
||||
|
||||
/* no need to update the state if we are in error */
|
||||
if (ret == GST_STATE_CHANGE_FAILURE)
|
||||
goto no_state_recalc;
|
||||
|
||||
/* update the bin state, the new element could have been an ASYNC or
|
||||
* NO_PREROLL element */
|
||||
ret = GST_STATE_RETURN (element);
|
||||
GST_DEBUG_OBJECT (bin, "added %s element",
|
||||
gst_element_state_change_return_get_name (ret));
|
||||
|
||||
switch (ret) {
|
||||
case GST_STATE_CHANGE_ASYNC:
|
||||
bin_handle_async_start (bin, &state_message);
|
||||
break;
|
||||
case GST_STATE_CHANGE_NO_PREROLL:
|
||||
bin_handle_async_done (bin, &state_message, &async_message, &cont, ret);
|
||||
break;
|
||||
case GST_STATE_CHANGE_FAILURE:
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
no_state_recalc:
|
||||
/* distribute the bus */
|
||||
gst_element_set_bus (element, bin->child_bus);
|
||||
|
||||
|
@ -899,13 +937,19 @@ gst_bin_add_func (GstBin * bin, GstElement * element)
|
|||
* that is not important right now. When the pipeline goes to PLAYING,
|
||||
* a new clock will be selected */
|
||||
gst_element_set_clock (element, GST_ELEMENT_CLOCK (bin));
|
||||
GST_DEBUG_OBJECT (bin, "marking state dirty");
|
||||
bin->state_dirty = TRUE;
|
||||
GST_OBJECT_UNLOCK (bin);
|
||||
|
||||
if (clock_message) {
|
||||
if (state_message)
|
||||
gst_element_post_message (GST_ELEMENT_CAST (bin), state_message);
|
||||
|
||||
if (async_message)
|
||||
gst_element_post_message (GST_ELEMENT_CAST (bin), async_message);
|
||||
|
||||
if (clock_message)
|
||||
gst_element_post_message (GST_ELEMENT_CAST (bin), clock_message);
|
||||
}
|
||||
|
||||
if (cont)
|
||||
bin_push_state_continue (cont);
|
||||
|
||||
/* unlink all linked pads */
|
||||
it = gst_element_iterate_pads (element);
|
||||
|
@ -1001,11 +1045,13 @@ gst_bin_remove_func (GstBin * bin, GstElement * element)
|
|||
{
|
||||
gchar *elem_name;
|
||||
GstIterator *it;
|
||||
gboolean is_sink;
|
||||
gboolean is_sink, othersink, found;
|
||||
GstMessage *clock_message = NULL, *async_message = NULL, *state_message =
|
||||
NULL;
|
||||
BinContinueData *cont = NULL;
|
||||
GList *walk, *next;
|
||||
gboolean other_async = FALSE, this_async = FALSE, toplevel = FALSE;
|
||||
gboolean other_async, this_async, have_no_preroll;
|
||||
GstStateChangeReturn ret;
|
||||
|
||||
GST_DEBUG_OBJECT (bin, "element :%s", GST_ELEMENT_NAME (element));
|
||||
|
||||
|
@ -1027,26 +1073,48 @@ gst_bin_remove_func (GstBin * bin, GstElement * element)
|
|||
gst_iterator_free (it);
|
||||
|
||||
GST_OBJECT_LOCK (bin);
|
||||
/* the element must be in the bin's list of children */
|
||||
if (G_UNLIKELY (g_list_find (bin->children, element) == NULL))
|
||||
found = FALSE;
|
||||
othersink = FALSE;
|
||||
have_no_preroll = FALSE;
|
||||
/* iterate the elements, we collect which ones are async and no_preroll. We
|
||||
* also remove the element when we find it. */
|
||||
for (walk = bin->children; walk; walk = next) {
|
||||
GstElement *child = GST_ELEMENT_CAST (walk->data);
|
||||
|
||||
next = g_list_next (walk);
|
||||
|
||||
if (child == element) {
|
||||
found = TRUE;
|
||||
/* remove the element */
|
||||
bin->children = g_list_delete_link (bin->children, walk);
|
||||
} else {
|
||||
gboolean child_sink;
|
||||
|
||||
GST_OBJECT_LOCK (child);
|
||||
child_sink = GST_OBJECT_FLAG_IS_SET (child, GST_ELEMENT_IS_SINK);
|
||||
/* when we remove a sink, check if there are other sinks. */
|
||||
if (is_sink && !othersink && child_sink)
|
||||
othersink = TRUE;
|
||||
/* check if we have NO_PREROLL children */
|
||||
if (GST_STATE_RETURN (child) == GST_STATE_CHANGE_NO_PREROLL)
|
||||
have_no_preroll = TRUE;
|
||||
GST_OBJECT_UNLOCK (child);
|
||||
}
|
||||
}
|
||||
|
||||
/* the element must have been in the bin's list of children */
|
||||
if (G_UNLIKELY (!found))
|
||||
goto not_in_bin;
|
||||
|
||||
/* now remove the element from the list of elements */
|
||||
bin->children = g_list_remove (bin->children, element);
|
||||
/* we now removed the element from the list of elements, increment the cookie
|
||||
* so that others can detect a change in the children list. */
|
||||
bin->numchildren--;
|
||||
bin->children_cookie++;
|
||||
|
||||
/* check if we removed a sink */
|
||||
if (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_OBJECT_FLAG_UNSET (bin, GST_ELEMENT_IS_SINK);
|
||||
}
|
||||
if (is_sink && !othersink) {
|
||||
/* we're not a sink anymore */
|
||||
GST_DEBUG_OBJECT (bin, "we removed the last sink");
|
||||
GST_OBJECT_FLAG_UNSET (bin, GST_ELEMENT_IS_SINK);
|
||||
}
|
||||
|
||||
/* if the clock provider for this element is removed, we lost
|
||||
|
@ -1062,6 +1130,8 @@ gst_bin_remove_func (GstBin * bin, GstElement * element)
|
|||
/* remove messages for the element, if there was a pending ASYNC_START
|
||||
* message we must see if removing the element caused the bin to lose its
|
||||
* async state. */
|
||||
this_async = FALSE;
|
||||
other_async = FALSE;
|
||||
for (walk = bin->messages; walk; walk = next) {
|
||||
GstMessage *message = (GstMessage *) walk->data;
|
||||
GstElement *src = GST_ELEMENT_CAST (GST_MESSAGE_SRC (message));
|
||||
|
@ -1085,18 +1155,50 @@ gst_bin_remove_func (GstBin * bin, GstElement * element)
|
|||
gst_message_unref (message);
|
||||
}
|
||||
}
|
||||
/* all other elements were not async and we removed the async one,
|
||||
* post a ASYNC_DONE message because we are not async anymore now. */
|
||||
if (!other_async && this_async) {
|
||||
GST_DEBUG_OBJECT (bin, "we removed the last async element");
|
||||
|
||||
toplevel = ((GST_OBJECT_PARENT (bin) == NULL) || bin->priv->asynchandling);
|
||||
if (!toplevel) {
|
||||
bin_handle_async_done (bin, &state_message, &async_message);
|
||||
/* get last return */
|
||||
ret = GST_STATE_RETURN (bin);
|
||||
|
||||
/* no need to update the state if we are in error */
|
||||
if (ret == GST_STATE_CHANGE_FAILURE)
|
||||
goto no_state_recalc;
|
||||
|
||||
if (!other_async && this_async) {
|
||||
GstStateChangeReturn ret;
|
||||
|
||||
/* all other elements were not async and we removed the async one,
|
||||
* handle the async-done case because we are not async anymore now. */
|
||||
GST_DEBUG_OBJECT (bin,
|
||||
"we removed the last async element, have no_preroll %d",
|
||||
have_no_preroll);
|
||||
|
||||
/* the current state return of the bin depends on if there are no_preroll
|
||||
* elements in the pipeline or not */
|
||||
if (have_no_preroll)
|
||||
ret = GST_STATE_CHANGE_NO_PREROLL;
|
||||
else
|
||||
ret = GST_STATE_CHANGE_SUCCESS;
|
||||
|
||||
bin_handle_async_done (bin, &state_message, &async_message, &cont, ret);
|
||||
} else {
|
||||
GST_DEBUG_OBJECT (bin,
|
||||
"recalc state preroll: %d, other async: %d, this async %d",
|
||||
have_no_preroll, other_async, this_async);
|
||||
|
||||
if (have_no_preroll) {
|
||||
ret = GST_STATE_CHANGE_NO_PREROLL;
|
||||
} else if (other_async) {
|
||||
/* there are other async elements and we were not doing an async state
|
||||
* change, change our pending state and go async */
|
||||
if (GST_STATE_PENDING (bin) == GST_STATE_VOID_PENDING) {
|
||||
GST_STATE_NEXT (bin) = GST_STATE (bin);
|
||||
GST_STATE_PENDING (bin) = GST_STATE (bin);
|
||||
}
|
||||
ret = GST_STATE_CHANGE_ASYNC;
|
||||
}
|
||||
GST_STATE_RETURN (bin) = ret;
|
||||
}
|
||||
GST_DEBUG_OBJECT (bin, "marking state dirty");
|
||||
bin->state_dirty = TRUE;
|
||||
no_state_recalc:
|
||||
GST_OBJECT_UNLOCK (bin);
|
||||
|
||||
if (clock_message)
|
||||
|
@ -1108,8 +1210,8 @@ gst_bin_remove_func (GstBin * bin, GstElement * element)
|
|||
if (async_message)
|
||||
gst_element_post_message (GST_ELEMENT_CAST (bin), async_message);
|
||||
|
||||
if (toplevel)
|
||||
bin_push_state_continue (bin);
|
||||
if (cont)
|
||||
bin_push_state_continue (cont);
|
||||
|
||||
GST_CAT_INFO_OBJECT (GST_CAT_PARENTAGE, bin, "removed child \"%s\"",
|
||||
elem_name);
|
||||
|
@ -1423,171 +1525,6 @@ gst_bin_iterate_sources (GstBin * bin)
|
|||
return result;
|
||||
}
|
||||
|
||||
static void
|
||||
gst_bin_recalc_state (GstBin * bin, gboolean force)
|
||||
{
|
||||
GstStateChangeReturn ret;
|
||||
GList *children;
|
||||
guint32 children_cookie;
|
||||
guint32 state_cookie;
|
||||
gboolean have_no_preroll;
|
||||
gboolean have_async;
|
||||
|
||||
ret = GST_STATE_CHANGE_SUCCESS;
|
||||
|
||||
/* lock bin, no element can be added or removed while we have this lock */
|
||||
GST_OBJECT_LOCK (bin);
|
||||
/* forced recalc, make state dirty again */
|
||||
if (force)
|
||||
bin->state_dirty = TRUE;
|
||||
|
||||
/* no point in scanning if nothing changed and it's no forced recalc */
|
||||
if (!bin->state_dirty)
|
||||
goto not_dirty;
|
||||
|
||||
/* no point in having two scans run concurrently */
|
||||
if (bin->polling)
|
||||
goto was_polling;
|
||||
|
||||
bin->polling = TRUE;
|
||||
state_cookie = GST_ELEMENT_CAST (bin)->state_cookie;
|
||||
|
||||
GST_CAT_INFO_OBJECT (GST_CAT_STATES, bin, "recalc state");
|
||||
|
||||
restart:
|
||||
/* when we leave this function, the state must not be dirty, whenever
|
||||
* we are scanning and the state becomes dirty again, we restart. */
|
||||
bin->state_dirty = FALSE;
|
||||
|
||||
have_no_preroll = FALSE;
|
||||
have_async = FALSE;
|
||||
|
||||
GST_CAT_INFO_OBJECT (GST_CAT_STATES, bin, "checking element states");
|
||||
|
||||
/* scan all element states with a zero timeout so we don't block on
|
||||
* anything */
|
||||
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_OBJECT_UNLOCK (bin);
|
||||
|
||||
ret = gst_element_get_state (child, NULL, NULL, 0);
|
||||
|
||||
gst_object_unref (child);
|
||||
|
||||
/* now grab the lock to iterate to the next child */
|
||||
GST_OBJECT_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.*/
|
||||
GST_DEBUG_OBJECT (bin, "children added or removed, restarting recalc");
|
||||
goto restart;
|
||||
}
|
||||
if (state_cookie != GST_ELEMENT_CAST (bin)->state_cookie) {
|
||||
GST_DEBUG_OBJECT (bin, "concurrent state change");
|
||||
goto concurrent_state;
|
||||
}
|
||||
if (bin->state_dirty) {
|
||||
GST_DEBUG_OBJECT (bin, "state dirty again, restarting recalc");
|
||||
goto restart;
|
||||
}
|
||||
|
||||
switch (ret) {
|
||||
case GST_STATE_CHANGE_FAILURE:
|
||||
/* report FAILURE immediatly */
|
||||
goto done;
|
||||
case GST_STATE_CHANGE_NO_PREROLL:
|
||||
/* we have to continue scanning as there might be
|
||||
* ERRORS too */
|
||||
have_no_preroll = TRUE;
|
||||
break;
|
||||
case GST_STATE_CHANGE_ASYNC:
|
||||
/* we have to continue scanning as there might be
|
||||
* ERRORS too */
|
||||
have_async = TRUE;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
children = g_list_next (children);
|
||||
}
|
||||
/* if we get here, we have no FAILURES */
|
||||
|
||||
/* if we have NO_PREROLL, return that */
|
||||
if (have_no_preroll) {
|
||||
ret = GST_STATE_CHANGE_NO_PREROLL;
|
||||
}
|
||||
/* else return ASYNC if async elements where found. */
|
||||
else if (have_async) {
|
||||
ret = GST_STATE_CHANGE_ASYNC;
|
||||
}
|
||||
|
||||
done:
|
||||
bin->polling = FALSE;
|
||||
GST_STATE_RETURN (bin) = ret;
|
||||
GST_OBJECT_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. */
|
||||
switch (ret) {
|
||||
case GST_STATE_CHANGE_SUCCESS:
|
||||
case GST_STATE_CHANGE_NO_PREROLL:
|
||||
ret = gst_element_continue_state (GST_ELEMENT_CAST (bin), ret);
|
||||
break;
|
||||
case GST_STATE_CHANGE_ASYNC:
|
||||
gst_element_lost_state (GST_ELEMENT_CAST (bin));
|
||||
break;
|
||||
case GST_STATE_CHANGE_FAILURE:
|
||||
gst_element_abort_state (GST_ELEMENT_CAST (bin));
|
||||
break;
|
||||
default:
|
||||
goto unknown_state;
|
||||
}
|
||||
|
||||
GST_CAT_INFO_OBJECT (GST_CAT_STATES, bin, "bin RETURN is now %s",
|
||||
gst_element_state_change_return_get_name (ret));
|
||||
|
||||
return;
|
||||
|
||||
not_dirty:
|
||||
{
|
||||
GST_CAT_INFO_OBJECT (GST_CAT_STATES, bin, "not dirty");
|
||||
GST_OBJECT_UNLOCK (bin);
|
||||
return;
|
||||
}
|
||||
was_polling:
|
||||
{
|
||||
GST_CAT_INFO_OBJECT (GST_CAT_STATES, bin, "was polling");
|
||||
GST_OBJECT_UNLOCK (bin);
|
||||
return;
|
||||
}
|
||||
concurrent_state:
|
||||
{
|
||||
GST_CAT_INFO_OBJECT (GST_CAT_STATES, bin, "concurrent_state");
|
||||
bin->polling = FALSE;
|
||||
GST_OBJECT_UNLOCK (bin);
|
||||
return;
|
||||
}
|
||||
unknown_state:
|
||||
{
|
||||
/* somebody added a GST_STATE_ and forgot to do stuff here ! */
|
||||
GST_CAT_INFO_OBJECT (GST_CAT_STATES, bin,
|
||||
"unknown return value %d from a state change function", ret);
|
||||
g_critical ("unknown return value %d from a state change function", ret);
|
||||
GST_STATE_RETURN (bin) = GST_STATE_CHANGE_FAILURE;
|
||||
GST_STATE_UNLOCK (bin);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* MT safe
|
||||
*/
|
||||
|
@ -1600,11 +1537,6 @@ gst_bin_get_state_func (GstElement * element, GstState * state,
|
|||
|
||||
GST_CAT_INFO_OBJECT (GST_CAT_STATES, bin, "getting state");
|
||||
|
||||
/* do a non forced recalculation of the state */
|
||||
GST_STATE_LOCK (bin);
|
||||
gst_bin_recalc_state (bin, FALSE);
|
||||
GST_STATE_UNLOCK (bin);
|
||||
|
||||
ret = parent_class->get_state (element, state, pending, timeout);
|
||||
|
||||
return ret;
|
||||
|
@ -1905,6 +1837,8 @@ gst_bin_element_set_state (GstBin * bin, GstElement * element,
|
|||
/* peel off the locked flag */
|
||||
GST_OBJECT_LOCK (element);
|
||||
locked = GST_OBJECT_FLAG_IS_SET (element, GST_ELEMENT_LOCKED_STATE);
|
||||
/* get previous state return */
|
||||
ret = GST_STATE_RETURN (element);
|
||||
GST_OBJECT_UNLOCK (element);
|
||||
|
||||
/* skip locked elements */
|
||||
|
@ -1957,8 +1891,9 @@ no_latency:
|
|||
locked:
|
||||
{
|
||||
GST_DEBUG_OBJECT (element,
|
||||
"element is locked, pretending state change succeeded");
|
||||
return GST_STATE_CHANGE_SUCCESS;
|
||||
"element is locked, return previous return %s",
|
||||
gst_element_state_change_return_get_name (ret));
|
||||
return ret;
|
||||
}
|
||||
was_busy:
|
||||
{
|
||||
|
@ -2264,12 +2199,14 @@ gst_bin_send_event (GstElement * element, GstEvent * event)
|
|||
}
|
||||
|
||||
static void
|
||||
gst_bin_continue_func (GstBin * bin, gpointer data)
|
||||
gst_bin_continue_func (BinContinueData * data)
|
||||
{
|
||||
GstState current, next, pending, target, old_state, old_next;
|
||||
GstStateChangeReturn old_ret, ret;
|
||||
GstStateChange transition;
|
||||
gboolean post;
|
||||
GstBin *bin;
|
||||
GstState current, next, pending, transition;
|
||||
GstStateChangeReturn ret;
|
||||
|
||||
bin = data->bin;
|
||||
pending = data->pending;
|
||||
|
||||
GST_DEBUG_OBJECT (bin, "waiting for state lock");
|
||||
GST_STATE_LOCK (bin);
|
||||
|
@ -2277,43 +2214,7 @@ gst_bin_continue_func (GstBin * bin, gpointer data)
|
|||
GST_DEBUG_OBJECT (bin, "doing state continue");
|
||||
GST_OBJECT_LOCK (bin);
|
||||
|
||||
old_ret = GST_STATE_RETURN (bin);
|
||||
GST_STATE_RETURN (bin) = GST_STATE_CHANGE_SUCCESS;
|
||||
target = GST_STATE_TARGET (bin);
|
||||
pending = GST_STATE_PENDING (bin);
|
||||
|
||||
/* check if there is something to commit */
|
||||
if (pending == GST_STATE_VOID_PENDING)
|
||||
goto nothing_pending;
|
||||
|
||||
old_state = GST_STATE (bin);
|
||||
/* this is the state we should go to next */
|
||||
old_next = GST_STATE_NEXT (bin);
|
||||
|
||||
GST_CAT_INFO_OBJECT (GST_CAT_STATES, bin, "target %s",
|
||||
gst_element_state_get_name (target));
|
||||
|
||||
if (old_next == GST_STATE_PLAYING) {
|
||||
post = FALSE;
|
||||
} else {
|
||||
post = TRUE;
|
||||
}
|
||||
|
||||
/* we're going to PLAYING, always do the PAUSED->PLAYING state change */
|
||||
if (target == GST_STATE_PLAYING) {
|
||||
next = GST_STATE_PAUSED;
|
||||
GST_STATE_PENDING (bin) = pending = target;
|
||||
} else {
|
||||
next = old_next;
|
||||
}
|
||||
|
||||
/* update current state */
|
||||
current = GST_STATE (bin) = next;
|
||||
|
||||
/* see if we reached the final state */
|
||||
if (pending == current)
|
||||
goto complete;
|
||||
|
||||
current = GST_STATE (bin);
|
||||
next = GST_STATE_GET_NEXT (current, pending);
|
||||
transition = (GstStateChange) GST_STATE_TRANSITION (current, next);
|
||||
|
||||
|
@ -2322,22 +2223,6 @@ gst_bin_continue_func (GstBin * bin, gpointer data)
|
|||
GST_STATE_RETURN (bin) = GST_STATE_CHANGE_ASYNC;
|
||||
GST_OBJECT_UNLOCK (bin);
|
||||
|
||||
if (post) {
|
||||
GST_CAT_INFO_OBJECT (GST_CAT_STATES, bin,
|
||||
"committing state from %s to %s, pending %s",
|
||||
gst_element_state_get_name (old_state),
|
||||
gst_element_state_get_name (old_next),
|
||||
gst_element_state_get_name (pending));
|
||||
|
||||
gst_element_post_message (GST_ELEMENT_CAST (bin),
|
||||
gst_message_new_state_changed (GST_OBJECT_CAST (bin),
|
||||
old_state, old_next, pending));
|
||||
}
|
||||
|
||||
GST_DEBUG_OBJECT (bin, "posting ASYNC_DONE");
|
||||
gst_element_post_message (GST_ELEMENT_CAST (bin),
|
||||
gst_message_new_async_done (GST_OBJECT_CAST (bin)));
|
||||
|
||||
GST_CAT_INFO_OBJECT (GST_CAT_STATES, bin,
|
||||
"continue state change %s to %s, final %s",
|
||||
gst_element_state_get_name (current),
|
||||
|
@ -2345,58 +2230,10 @@ gst_bin_continue_func (GstBin * bin, gpointer data)
|
|||
|
||||
ret = gst_element_change_state (GST_ELEMENT_CAST (bin), transition);
|
||||
|
||||
done:
|
||||
GST_STATE_UNLOCK (bin);
|
||||
GST_DEBUG_OBJECT (bin, "state continue done");
|
||||
gst_object_unref (bin);
|
||||
|
||||
return;
|
||||
|
||||
nothing_pending:
|
||||
{
|
||||
GST_CAT_INFO_OBJECT (GST_CAT_STATES, bin, "nothing pending");
|
||||
GST_OBJECT_UNLOCK (bin);
|
||||
|
||||
GST_DEBUG_OBJECT (bin, "posting ASYNC_DONE");
|
||||
gst_element_post_message (GST_ELEMENT_CAST (bin),
|
||||
gst_message_new_async_done (GST_OBJECT_CAST (bin)));
|
||||
|
||||
goto done;
|
||||
}
|
||||
complete:
|
||||
{
|
||||
GST_STATE_PENDING (bin) = GST_STATE_VOID_PENDING;
|
||||
GST_STATE_NEXT (bin) = GST_STATE_VOID_PENDING;
|
||||
|
||||
GST_CAT_INFO_OBJECT (GST_CAT_STATES, bin, "completed state change to %s",
|
||||
gst_element_state_get_name (pending));
|
||||
GST_OBJECT_UNLOCK (bin);
|
||||
|
||||
/* don't post silly messages with the same state. This can happen
|
||||
* when an element state is changed to what it already was. For bins
|
||||
* this can be the result of a lost state, which we check with the
|
||||
* previous return value.
|
||||
* We do signal the cond though as a _get_state() might be blocking
|
||||
* on it. */
|
||||
if (old_state != old_next || old_ret == GST_STATE_CHANGE_ASYNC) {
|
||||
GST_CAT_INFO_OBJECT (GST_CAT_STATES, bin,
|
||||
"changed %s to %s, VOID pending",
|
||||
gst_element_state_get_name (old_state),
|
||||
gst_element_state_get_name (old_next));
|
||||
|
||||
gst_element_post_message (GST_ELEMENT_CAST (bin),
|
||||
gst_message_new_state_changed (GST_OBJECT_CAST (bin),
|
||||
old_state, old_next, GST_STATE_VOID_PENDING));
|
||||
}
|
||||
|
||||
GST_DEBUG_OBJECT (bin, "posting ASYNC_DONE");
|
||||
gst_element_post_message (GST_ELEMENT_CAST (bin),
|
||||
gst_message_new_async_done (GST_OBJECT_CAST (bin)));
|
||||
|
||||
GST_STATE_BROADCAST (bin);
|
||||
|
||||
goto done;
|
||||
}
|
||||
g_free (data);
|
||||
}
|
||||
|
||||
static GstBusSyncReply
|
||||
|
@ -2414,16 +2251,18 @@ bin_bus_handler (GstBus * bus, GstMessage * message, GstBin * bin)
|
|||
}
|
||||
|
||||
static void
|
||||
bin_push_state_continue (GstBin * bin)
|
||||
bin_push_state_continue (BinContinueData * data)
|
||||
{
|
||||
GstBinClass *klass;
|
||||
GstBin *bin;
|
||||
|
||||
/* mark the bin dirty */
|
||||
GST_OBJECT_LOCK (bin);
|
||||
/* ref was taken */
|
||||
bin = data->bin;
|
||||
klass = GST_BIN_GET_CLASS (bin);
|
||||
|
||||
GST_OBJECT_LOCK (bin);
|
||||
GST_DEBUG_OBJECT (bin, "pushing continue on thread pool");
|
||||
gst_object_ref (bin);
|
||||
g_thread_pool_push (klass->pool, bin, NULL);
|
||||
g_thread_pool_push (klass->pool, data, NULL);
|
||||
GST_OBJECT_UNLOCK (bin);
|
||||
}
|
||||
|
||||
|
@ -2441,6 +2280,10 @@ bin_handle_async_start (GstBin * bin, GstMessage ** message)
|
|||
if (GST_STATE_PENDING (bin) != GST_STATE_VOID_PENDING)
|
||||
goto was_busy;
|
||||
|
||||
/* async starts are ignored when we are NO_PREROLL */
|
||||
if (GST_STATE_RETURN (bin) == GST_STATE_CHANGE_NO_PREROLL)
|
||||
goto was_no_preroll;
|
||||
|
||||
old_state = GST_STATE (bin);
|
||||
|
||||
/* when we PLAYING we go back to PAUSED, when preroll happens, we go back to
|
||||
|
@ -2474,88 +2317,111 @@ was_busy:
|
|||
GST_CAT_DEBUG_OBJECT (GST_CAT_STATES, bin, "state change busy");
|
||||
return;
|
||||
}
|
||||
was_no_preroll:
|
||||
{
|
||||
GST_CAT_DEBUG_OBJECT (GST_CAT_STATES, bin, "ignoring, we are NO_PREROLL");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/* this function is called when there are no more async elements in the bin. We
|
||||
* post a state changed message and an ASYNC_DONE message. */
|
||||
static void
|
||||
bin_handle_async_done (GstBin * bin, GstMessage ** smessage,
|
||||
GstMessage ** amessage)
|
||||
GstMessage ** amessage, BinContinueData ** cont, GstStateChangeReturn ret)
|
||||
{
|
||||
GstState pending;
|
||||
GstState current, pending, target;
|
||||
GstStateChangeReturn old_ret;
|
||||
GstState old_state, old_next;
|
||||
GstState current, next;
|
||||
gboolean toplevel;
|
||||
|
||||
old_ret = GST_STATE_RETURN (bin);
|
||||
GST_STATE_RETURN (bin) = GST_STATE_CHANGE_SUCCESS;
|
||||
if (GST_STATE_RETURN (bin) == GST_STATE_CHANGE_FAILURE)
|
||||
goto had_error;
|
||||
|
||||
pending = GST_STATE_PENDING (bin);
|
||||
|
||||
/* check if there is something to commit */
|
||||
if (pending == GST_STATE_VOID_PENDING)
|
||||
goto nothing_pending;
|
||||
|
||||
old_ret = GST_STATE_RETURN (bin);
|
||||
GST_STATE_RETURN (bin) = ret;
|
||||
|
||||
/* move to the next target state */
|
||||
target = GST_STATE_TARGET (bin);
|
||||
pending = GST_STATE_PENDING (bin) = target;
|
||||
|
||||
*amessage = gst_message_new_async_done (GST_OBJECT_CAST (bin));
|
||||
|
||||
old_state = GST_STATE (bin);
|
||||
/* this is the state we should go to next */
|
||||
old_next = GST_STATE_NEXT (bin);
|
||||
/* update current state */
|
||||
current = GST_STATE (bin) = old_next;
|
||||
|
||||
/* see if we need to continue the state change on our own. This happens when
|
||||
* we were asked to do so or when we are the toplevel bin. */
|
||||
if (old_next != GST_STATE_PLAYING) {
|
||||
GST_CAT_INFO_OBJECT (GST_CAT_STATES, bin,
|
||||
"committing state from %s to %s, old pending %s",
|
||||
gst_element_state_get_name (old_state),
|
||||
gst_element_state_get_name (old_next),
|
||||
gst_element_state_get_name (pending));
|
||||
|
||||
/* update current state */
|
||||
current = GST_STATE (bin) = old_next;
|
||||
} else {
|
||||
current = old_state;
|
||||
}
|
||||
|
||||
/* get our toplevel state */
|
||||
toplevel = ((GST_OBJECT_PARENT (bin) == NULL) || bin->priv->asynchandling);
|
||||
|
||||
/* see if we reached the final state. If we are not a toplevel bin we also
|
||||
* must stop at this state change, the parent will set us to the required
|
||||
* state eventually. */
|
||||
if (pending == current || !toplevel)
|
||||
goto complete;
|
||||
/* see if we reached the final state. If we are not toplevel, we also have to
|
||||
* stop here, the parent will continue our state. */
|
||||
if ((pending == current) || !toplevel) {
|
||||
GST_CAT_INFO_OBJECT (GST_CAT_STATES, bin,
|
||||
"completed state change, pending VOID");
|
||||
|
||||
next = GST_STATE_GET_NEXT (current, pending);
|
||||
/* mark VOID pending */
|
||||
pending = GST_STATE_VOID_PENDING;
|
||||
GST_STATE_PENDING (bin) = pending;
|
||||
GST_STATE_NEXT (bin) = GST_STATE_VOID_PENDING;
|
||||
|
||||
GST_STATE_NEXT (bin) = next;
|
||||
/* mark busy */
|
||||
GST_STATE_RETURN (bin) = GST_STATE_CHANGE_ASYNC;
|
||||
GST_STATE_BROADCAST (bin);
|
||||
} else {
|
||||
BinContinueData *data;
|
||||
|
||||
GST_CAT_INFO_OBJECT (GST_CAT_STATES, bin,
|
||||
"committing state from %s to %s, pending %s",
|
||||
gst_element_state_get_name (old_state),
|
||||
gst_element_state_get_name (old_next),
|
||||
gst_element_state_get_name (pending));
|
||||
GST_CAT_INFO_OBJECT (GST_CAT_STATES, bin,
|
||||
"continue state change, pending %s",
|
||||
gst_element_state_get_name (pending));
|
||||
|
||||
*smessage = gst_message_new_state_changed (GST_OBJECT_CAST (bin),
|
||||
old_state, old_next, pending);
|
||||
data = g_new0 (BinContinueData, 1);
|
||||
|
||||
/* ref to the bin */
|
||||
data->bin = gst_object_ref (bin);
|
||||
/* cookie to detect concurrent state change */
|
||||
data->cookie = GST_ELEMENT_CAST (bin)->state_cookie;
|
||||
/* pending target state */
|
||||
data->pending = pending;
|
||||
/* mark busy */
|
||||
GST_STATE_RETURN (bin) = GST_STATE_CHANGE_ASYNC;
|
||||
|
||||
*cont = data;
|
||||
}
|
||||
|
||||
if (old_next != GST_STATE_PLAYING) {
|
||||
if (old_state != old_next || old_ret == GST_STATE_CHANGE_ASYNC) {
|
||||
*smessage = gst_message_new_state_changed (GST_OBJECT_CAST (bin),
|
||||
old_state, old_next, pending);
|
||||
}
|
||||
}
|
||||
return;
|
||||
|
||||
had_error:
|
||||
{
|
||||
GST_CAT_DEBUG_OBJECT (GST_CAT_STATES, bin, "we had an error");
|
||||
return;
|
||||
}
|
||||
nothing_pending:
|
||||
{
|
||||
GST_CAT_INFO_OBJECT (GST_CAT_STATES, bin, "nothing pending");
|
||||
return;
|
||||
}
|
||||
complete:
|
||||
{
|
||||
GST_STATE_PENDING (bin) = GST_STATE_VOID_PENDING;
|
||||
GST_STATE_NEXT (bin) = GST_STATE_VOID_PENDING;
|
||||
|
||||
GST_CAT_INFO_OBJECT (GST_CAT_STATES, bin, "completed state change");
|
||||
|
||||
/* don't post silly messages with the same state. This can happen
|
||||
* when an element state is changed to what it already was. For bins
|
||||
* this can be the result of a lost state, which we check with the
|
||||
* previous return value.
|
||||
* We do signal the cond though as a _get_state() might be blocking
|
||||
* on it. */
|
||||
if (old_state != old_next || old_ret == GST_STATE_CHANGE_ASYNC) {
|
||||
*smessage = gst_message_new_state_changed (GST_OBJECT_CAST (bin),
|
||||
old_state, old_next, GST_STATE_VOID_PENDING);
|
||||
}
|
||||
|
||||
GST_STATE_BROADCAST (bin);
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
@ -2772,6 +2638,7 @@ gst_bin_handle_message_func (GstBin * bin, GstMessage * message)
|
|||
if (target <= GST_STATE_READY)
|
||||
goto ignore_start_message;
|
||||
|
||||
/* takes ownership of the message */
|
||||
bin_replace_message (bin, message, GST_MESSAGE_ASYNC_START);
|
||||
|
||||
bin_handle_async_start (bin, &smessage);
|
||||
|
@ -2786,6 +2653,7 @@ gst_bin_handle_message_func (GstBin * bin, GstMessage * message)
|
|||
message =
|
||||
gst_message_new_async_start (GST_OBJECT_CAST (bin), new_base_time);
|
||||
} else {
|
||||
/* toplevel bin, we don't post async start */
|
||||
message = NULL;
|
||||
}
|
||||
GST_OBJECT_UNLOCK (bin);
|
||||
|
@ -2810,9 +2678,9 @@ gst_bin_handle_message_func (GstBin * bin, GstMessage * message)
|
|||
}
|
||||
case GST_MESSAGE_ASYNC_DONE:
|
||||
{
|
||||
gboolean toplevel = FALSE;
|
||||
GstState target;
|
||||
GstMessage *smessage = NULL, *amessage = NULL;
|
||||
BinContinueData *cont = NULL;
|
||||
|
||||
GST_DEBUG_OBJECT (bin, "ASYNC_DONE message %p, %s", message,
|
||||
GST_OBJECT_NAME (src));
|
||||
|
@ -2831,10 +2699,8 @@ gst_bin_handle_message_func (GstBin * bin, GstMessage * message)
|
|||
/* nothing found, remove all old ASYNC_DONE messages */
|
||||
bin_remove_messages (bin, NULL, GST_MESSAGE_ASYNC_DONE);
|
||||
|
||||
toplevel = ((GST_OBJECT_PARENT (bin) == NULL)
|
||||
|| bin->priv->asynchandling);
|
||||
if (!toplevel)
|
||||
bin_handle_async_done (bin, &smessage, &amessage);
|
||||
bin_handle_async_done (bin, &smessage, &amessage, &cont,
|
||||
GST_STATE_CHANGE_SUCCESS);
|
||||
}
|
||||
GST_OBJECT_UNLOCK (bin);
|
||||
|
||||
|
@ -2848,10 +2714,10 @@ gst_bin_handle_message_func (GstBin * bin, GstMessage * message)
|
|||
gst_element_post_message (GST_ELEMENT_CAST (bin), amessage);
|
||||
}
|
||||
|
||||
if (toplevel) {
|
||||
if (cont) {
|
||||
/* toplevel, start continue state */
|
||||
GST_DEBUG_OBJECT (bin, "all async-done, starting state continue");
|
||||
bin_push_state_continue (bin);
|
||||
bin_push_state_continue (cont);
|
||||
}
|
||||
break;
|
||||
|
||||
|
|
|
@ -86,7 +86,7 @@ typedef struct _GstBinPrivate GstBinPrivate;
|
|||
* @child_bus: internal bus for handling child messages
|
||||
* @messages: queued and cached messages
|
||||
* @polling: the bin is currently calculating its state
|
||||
* @state_dirty: the bin needs to recalculate its state
|
||||
* @state_dirty: the bin needs to recalculate its state (deprecated)
|
||||
* @clock_dirty: the bin needs to select a new clock
|
||||
* @provided_clock: the last clock selected
|
||||
* @clock_provider: the element that provided @provided_clock
|
||||
|
|
|
@ -1858,11 +1858,11 @@ gst_element_get_state_func (GstElement * element,
|
|||
ret = GST_STATE_CHANGE_FAILURE;
|
||||
}
|
||||
}
|
||||
}
|
||||
/* if nothing is pending anymore we can return SUCCESS */
|
||||
if (GST_STATE_PENDING (element) == GST_STATE_VOID_PENDING) {
|
||||
GST_CAT_LOG_OBJECT (GST_CAT_STATES, element, "nothing pending");
|
||||
ret = GST_STATE_CHANGE_SUCCESS;
|
||||
/* if nothing is pending anymore we can return SUCCESS */
|
||||
if (GST_STATE_PENDING (element) == GST_STATE_VOID_PENDING) {
|
||||
GST_CAT_LOG_OBJECT (GST_CAT_STATES, element, "nothing pending");
|
||||
ret = GST_STATE_CHANGE_SUCCESS;
|
||||
}
|
||||
}
|
||||
|
||||
done:
|
||||
|
@ -1887,7 +1887,7 @@ interrupted:
|
|||
if (pending)
|
||||
*pending = GST_STATE_VOID_PENDING;
|
||||
|
||||
GST_CAT_INFO_OBJECT (GST_CAT_STATES, element, "get_state() interruped");
|
||||
GST_CAT_INFO_OBJECT (GST_CAT_STATES, element, "interruped");
|
||||
|
||||
GST_OBJECT_UNLOCK (element);
|
||||
|
||||
|
@ -2100,8 +2100,13 @@ complete:
|
|||
* We do signal the cond though as a _get_state() might be blocking
|
||||
* on it. */
|
||||
if (old_state != old_next || old_ret == GST_STATE_CHANGE_ASYNC) {
|
||||
message = gst_message_new_state_changed (GST_OBJECT_CAST (element),
|
||||
old_state, old_next, GST_STATE_VOID_PENDING);
|
||||
GST_CAT_INFO_OBJECT (GST_CAT_STATES, element,
|
||||
"posting state-changed %s to %s",
|
||||
gst_element_state_get_name (old_state),
|
||||
gst_element_state_get_name (old_next));
|
||||
message =
|
||||
gst_message_new_state_changed (GST_OBJECT_CAST (element), old_state,
|
||||
old_next, GST_STATE_VOID_PENDING);
|
||||
gst_element_post_message (element, message);
|
||||
}
|
||||
|
||||
|
@ -2891,7 +2896,8 @@ gst_element_set_bus (GstElement * element, GstBus * bus)
|
|||
* gst_element_get_bus:
|
||||
* @element: a #GstElement to get the bus of.
|
||||
*
|
||||
* Returns the bus of the element.
|
||||
* Returns the bus of the element. Note that only a #GstPipeline will provide a
|
||||
* bus for the application.
|
||||
*
|
||||
* Returns: the element's #GstBus. unref after usage.
|
||||
*
|
||||
|
|
|
@ -450,6 +450,402 @@ GST_START_TEST (test_livesrc3_sink)
|
|||
|
||||
GST_END_TEST;
|
||||
|
||||
GST_START_TEST (test_locked_sink)
|
||||
{
|
||||
GstElement *sink, *src, *pipeline;
|
||||
GstStateChangeReturn ret;
|
||||
GstState current, pending;
|
||||
|
||||
pipeline = gst_pipeline_new ("pipeline");
|
||||
src = gst_element_factory_make ("fakesrc", "src");
|
||||
g_object_set (G_OBJECT (src), "is-live", TRUE, NULL);
|
||||
sink = gst_element_factory_make ("fakesink", "sink");
|
||||
|
||||
gst_bin_add (GST_BIN (pipeline), src);
|
||||
gst_bin_add (GST_BIN (pipeline), sink);
|
||||
|
||||
/* we don't link the elements */
|
||||
|
||||
ret = gst_element_set_state (pipeline, GST_STATE_PAUSED);
|
||||
fail_unless (ret == GST_STATE_CHANGE_NO_PREROLL,
|
||||
"no NO_PREROLL state return");
|
||||
|
||||
ret =
|
||||
gst_element_get_state (pipeline, ¤t, &pending, GST_CLOCK_TIME_NONE);
|
||||
fail_unless (ret == GST_STATE_CHANGE_NO_PREROLL, "not no_preroll");
|
||||
fail_unless (current == GST_STATE_PAUSED, "not paused");
|
||||
fail_unless (pending == GST_STATE_VOID_PENDING, "have pending");
|
||||
|
||||
/* the sink is now async going from ready to paused */
|
||||
ret = gst_element_get_state (sink, ¤t, &pending, 0);
|
||||
fail_unless (ret == GST_STATE_CHANGE_ASYNC, "not async");
|
||||
fail_unless (current == GST_STATE_READY, "not ready");
|
||||
fail_unless (pending == GST_STATE_PAUSED, "not paused");
|
||||
|
||||
/* lock the sink */
|
||||
gst_element_set_locked_state (sink, TRUE);
|
||||
|
||||
/* move to PlAYING, the sink should remain ASYNC. The pipeline
|
||||
* returns ASYNC */
|
||||
ret = gst_element_set_state (pipeline, GST_STATE_PLAYING);
|
||||
fail_unless (ret == GST_STATE_CHANGE_ASYNC, "no ASYNC state return");
|
||||
|
||||
/* back to PAUSED, we should get NO_PREROLL again */
|
||||
ret = gst_element_set_state (pipeline, GST_STATE_PAUSED);
|
||||
fail_unless (ret == GST_STATE_CHANGE_NO_PREROLL,
|
||||
"no NO_PREROLL state return");
|
||||
|
||||
/* unlock the sink */
|
||||
gst_element_set_locked_state (sink, FALSE);
|
||||
|
||||
/* and now everything back down */
|
||||
ret = gst_element_set_state (pipeline, GST_STATE_NULL);
|
||||
fail_unless (ret == GST_STATE_CHANGE_SUCCESS, "no success state return");
|
||||
gst_object_unref (pipeline);
|
||||
}
|
||||
|
||||
GST_END_TEST;
|
||||
|
||||
GST_START_TEST (test_unlinked_live)
|
||||
{
|
||||
GstElement *sink, *src, *lsrc, *pipeline;
|
||||
GstStateChangeReturn ret;
|
||||
GstState current, pending;
|
||||
GstPad *srcpad, *sinkpad;
|
||||
|
||||
pipeline = gst_pipeline_new ("pipeline");
|
||||
src = gst_element_factory_make ("fakesrc", "src");
|
||||
lsrc = gst_element_factory_make ("fakesrc", "lsrc");
|
||||
g_object_set (G_OBJECT (lsrc), "is-live", TRUE, NULL);
|
||||
sink = gst_element_factory_make ("fakesink", "sink");
|
||||
|
||||
gst_bin_add (GST_BIN (pipeline), src);
|
||||
gst_bin_add (GST_BIN (pipeline), lsrc);
|
||||
gst_bin_add (GST_BIN (pipeline), sink);
|
||||
|
||||
/* link non live source to sink */
|
||||
srcpad = gst_element_get_pad (src, "src");
|
||||
sinkpad = gst_element_get_pad (sink, "sink");
|
||||
gst_pad_link (srcpad, sinkpad);
|
||||
gst_object_unref (srcpad);
|
||||
gst_object_unref (sinkpad);
|
||||
|
||||
/* we don't link the srcpad of the live source, it will not contribute to the
|
||||
* NO_PREROLL. */
|
||||
|
||||
/* set state to PAUSED, this should return NO_PREROLL because there is a live
|
||||
* source. since the only sink in this pipeline is linked to a non-live
|
||||
* source, it will preroll eventually. */
|
||||
ret = gst_element_set_state (pipeline, GST_STATE_PAUSED);
|
||||
fail_unless (ret == GST_STATE_CHANGE_NO_PREROLL,
|
||||
"no NO_PREROLL state return");
|
||||
|
||||
/* wait till the sink is prerolled */
|
||||
ret = gst_element_get_state (sink, ¤t, &pending, GST_CLOCK_TIME_NONE);
|
||||
fail_unless (ret == GST_STATE_CHANGE_SUCCESS, "not success");
|
||||
fail_unless (current == GST_STATE_PAUSED, "not paused");
|
||||
fail_unless (pending == GST_STATE_VOID_PENDING, "have playing");
|
||||
|
||||
/* the pipeline should still return NO_PREROLL */
|
||||
ret =
|
||||
gst_element_get_state (pipeline, ¤t, &pending, GST_CLOCK_TIME_NONE);
|
||||
fail_unless (ret == GST_STATE_CHANGE_NO_PREROLL, "not no_preroll");
|
||||
fail_unless (current == GST_STATE_PAUSED, "not paused");
|
||||
fail_unless (pending == GST_STATE_VOID_PENDING, "have playing");
|
||||
|
||||
ret = gst_element_set_state (pipeline, GST_STATE_NULL);
|
||||
fail_unless (ret == GST_STATE_CHANGE_SUCCESS, "not SUCCESS");
|
||||
|
||||
gst_object_unref (pipeline);
|
||||
}
|
||||
|
||||
GST_END_TEST;
|
||||
|
||||
GST_START_TEST (test_delayed_async)
|
||||
{
|
||||
GstElement *sink, *src, *pipeline;
|
||||
GstStateChangeReturn ret;
|
||||
GstState current, pending;
|
||||
GstPad *srcpad, *sinkpad;
|
||||
|
||||
pipeline = gst_pipeline_new ("pipeline");
|
||||
src = gst_element_factory_make ("fakesrc", "src");
|
||||
g_object_set (G_OBJECT (src), "is-live", TRUE, NULL);
|
||||
sink = gst_element_factory_make ("fakesink", "sink");
|
||||
|
||||
/* add source, don't add sink yet */
|
||||
gst_bin_add (GST_BIN (pipeline), src);
|
||||
|
||||
ret = gst_element_set_state (pipeline, GST_STATE_PAUSED);
|
||||
fail_unless (ret == GST_STATE_CHANGE_NO_PREROLL,
|
||||
"no NO_PREROLL state return");
|
||||
|
||||
/* add sink now and set to PAUSED */
|
||||
gst_bin_add (GST_BIN (pipeline), sink);
|
||||
|
||||
/* This will make the bin notice an ASYNC element. */
|
||||
ret = gst_element_set_state (sink, GST_STATE_PAUSED);
|
||||
fail_unless (ret == GST_STATE_CHANGE_ASYNC, "no ASYNC state return");
|
||||
|
||||
/* we should still be NO_PREROLL now although there is an async element in the
|
||||
* pipeline. */
|
||||
ret =
|
||||
gst_element_get_state (pipeline, ¤t, &pending, GST_CLOCK_TIME_NONE);
|
||||
fail_unless (ret == GST_STATE_CHANGE_NO_PREROLL, "not NO_PREROLL");
|
||||
fail_unless (current == GST_STATE_PAUSED, "not paused");
|
||||
fail_unless (pending == GST_STATE_VOID_PENDING, "have pending");
|
||||
|
||||
/* link live source to sink */
|
||||
srcpad = gst_element_get_pad (src, "src");
|
||||
sinkpad = gst_element_get_pad (sink, "sink");
|
||||
gst_pad_link (srcpad, sinkpad);
|
||||
gst_object_unref (srcpad);
|
||||
gst_object_unref (sinkpad);
|
||||
|
||||
ret = gst_element_set_state (pipeline, GST_STATE_PLAYING);
|
||||
fail_unless (ret == GST_STATE_CHANGE_ASYNC, "no ASYNC state return");
|
||||
|
||||
/* we should get SUCCESS now */
|
||||
ret =
|
||||
gst_element_get_state (pipeline, ¤t, &pending, GST_CLOCK_TIME_NONE);
|
||||
fail_unless (ret == GST_STATE_CHANGE_SUCCESS, "not NO_PREROLL");
|
||||
fail_unless (current == GST_STATE_PLAYING, "not PLAYING");
|
||||
fail_unless (pending == GST_STATE_VOID_PENDING, "have pending");
|
||||
|
||||
ret = gst_element_set_state (pipeline, GST_STATE_NULL);
|
||||
fail_unless (ret == GST_STATE_CHANGE_SUCCESS, "not SUCCESS");
|
||||
|
||||
gst_object_unref (pipeline);
|
||||
}
|
||||
|
||||
GST_END_TEST;
|
||||
|
||||
GST_START_TEST (test_added_async)
|
||||
{
|
||||
GstElement *sink, *src, *pipeline;
|
||||
GstStateChangeReturn ret;
|
||||
GstState current, pending;
|
||||
GstPad *srcpad, *sinkpad;
|
||||
|
||||
pipeline = gst_pipeline_new ("pipeline");
|
||||
src = gst_element_factory_make ("fakesrc", "src");
|
||||
g_object_set (G_OBJECT (src), "is-live", TRUE, NULL);
|
||||
sink = gst_element_factory_make ("fakesink", "sink");
|
||||
|
||||
/* add source, don't add sink yet */
|
||||
gst_bin_add (GST_BIN (pipeline), src);
|
||||
|
||||
ret = gst_element_set_state (pipeline, GST_STATE_PAUSED);
|
||||
fail_unless (ret == GST_STATE_CHANGE_NO_PREROLL,
|
||||
"no NO_PREROLL state return");
|
||||
|
||||
/* set sink to PAUSED without adding it to the pipeline */
|
||||
ret = gst_element_set_state (sink, GST_STATE_PAUSED);
|
||||
fail_unless (ret == GST_STATE_CHANGE_ASYNC, "no ASYNC state return");
|
||||
|
||||
/* add sink now, pipeline should notice the async element */
|
||||
gst_bin_add (GST_BIN (pipeline), sink);
|
||||
|
||||
/* we should still be NO_PREROLL now although there is an async element in the
|
||||
* pipeline. */
|
||||
ret =
|
||||
gst_element_get_state (pipeline, ¤t, &pending, GST_CLOCK_TIME_NONE);
|
||||
fail_unless (ret == GST_STATE_CHANGE_NO_PREROLL, "not NO_PREROLL");
|
||||
fail_unless (current == GST_STATE_PAUSED, "not paused");
|
||||
fail_unless (pending == GST_STATE_VOID_PENDING, "have pending");
|
||||
|
||||
/* link live source to sink */
|
||||
srcpad = gst_element_get_pad (src, "src");
|
||||
sinkpad = gst_element_get_pad (sink, "sink");
|
||||
gst_pad_link (srcpad, sinkpad);
|
||||
gst_object_unref (srcpad);
|
||||
gst_object_unref (sinkpad);
|
||||
|
||||
ret = gst_element_set_state (pipeline, GST_STATE_PLAYING);
|
||||
fail_unless (ret == GST_STATE_CHANGE_ASYNC, "no ASYNC state return");
|
||||
|
||||
/* we should get SUCCESS now */
|
||||
ret =
|
||||
gst_element_get_state (pipeline, ¤t, &pending, GST_CLOCK_TIME_NONE);
|
||||
fail_unless (ret == GST_STATE_CHANGE_SUCCESS, "not NO_PREROLL");
|
||||
fail_unless (current == GST_STATE_PLAYING, "not PLAYING");
|
||||
fail_unless (pending == GST_STATE_VOID_PENDING, "have pending");
|
||||
|
||||
ret = gst_element_set_state (pipeline, GST_STATE_NULL);
|
||||
fail_unless (ret == GST_STATE_CHANGE_SUCCESS, "not SUCCESS");
|
||||
|
||||
gst_object_unref (pipeline);
|
||||
}
|
||||
|
||||
GST_END_TEST;
|
||||
|
||||
GST_START_TEST (test_added_async2)
|
||||
{
|
||||
GstElement *sink, *src, *pipeline;
|
||||
GstStateChangeReturn ret;
|
||||
GstState current, pending;
|
||||
|
||||
pipeline = gst_pipeline_new ("pipeline");
|
||||
src = gst_element_factory_make ("fakesrc", "src");
|
||||
sink = gst_element_factory_make ("fakesink", "sink");
|
||||
|
||||
/* add source, don't add sink yet */
|
||||
gst_bin_add (GST_BIN (pipeline), src);
|
||||
|
||||
ret = gst_element_set_state (pipeline, GST_STATE_PAUSED);
|
||||
fail_unless (ret == GST_STATE_CHANGE_SUCCESS, "no SUCCESS state return");
|
||||
|
||||
/* set sink to PAUSED without adding it to the pipeline */
|
||||
ret = gst_element_set_state (sink, GST_STATE_PAUSED);
|
||||
fail_unless (ret == GST_STATE_CHANGE_ASYNC, "no ASYNC state return");
|
||||
|
||||
/* add sink now, pipeline should notice the async element */
|
||||
gst_bin_add (GST_BIN (pipeline), sink);
|
||||
|
||||
/* we should be ASYNC now because there is an async element in the
|
||||
* pipeline. */
|
||||
ret = gst_element_get_state (pipeline, ¤t, &pending, 0);
|
||||
fail_unless (ret == GST_STATE_CHANGE_ASYNC, "not ASYNC");
|
||||
fail_unless (current == GST_STATE_PAUSED, "not paused");
|
||||
fail_unless (pending == GST_STATE_PAUSED, "not paused");
|
||||
|
||||
ret = gst_element_set_state (pipeline, GST_STATE_NULL);
|
||||
fail_unless (ret == GST_STATE_CHANGE_SUCCESS, "not SUCCESS");
|
||||
|
||||
gst_object_unref (pipeline);
|
||||
}
|
||||
|
||||
GST_END_TEST;
|
||||
|
||||
GST_START_TEST (test_add_live)
|
||||
{
|
||||
GstElement *sink, *src, *pipeline;
|
||||
GstStateChangeReturn ret;
|
||||
GstState current, pending;
|
||||
|
||||
pipeline = gst_pipeline_new ("pipeline");
|
||||
src = gst_element_factory_make ("fakesrc", "src");
|
||||
g_object_set (G_OBJECT (src), "is-live", TRUE, NULL);
|
||||
sink = gst_element_factory_make ("fakesink", "sink");
|
||||
|
||||
/* add sink, don't add sourc3 yet */
|
||||
gst_bin_add (GST_BIN (pipeline), sink);
|
||||
|
||||
ret = gst_element_set_state (pipeline, GST_STATE_PAUSED);
|
||||
fail_unless (ret == GST_STATE_CHANGE_ASYNC, "no ASYNC state return");
|
||||
|
||||
/* set source to PAUSED without adding it to the pipeline */
|
||||
ret = gst_element_set_state (src, GST_STATE_PAUSED);
|
||||
fail_unless (ret == GST_STATE_CHANGE_NO_PREROLL,
|
||||
"no NO_PREROLL state return");
|
||||
|
||||
/* add source now, pipeline should notice the NO_PREROLL element */
|
||||
gst_bin_add (GST_BIN (pipeline), src);
|
||||
|
||||
/* we should be NO_PREROLL now because there is a NO_PREROLL element in the
|
||||
* pipeline. */
|
||||
ret =
|
||||
gst_element_get_state (pipeline, ¤t, &pending, GST_CLOCK_TIME_NONE);
|
||||
fail_unless (ret == GST_STATE_CHANGE_NO_PREROLL, "not NO_PREROLL");
|
||||
fail_unless (current == GST_STATE_PAUSED, "not paused");
|
||||
fail_unless (pending == GST_STATE_VOID_PENDING, "have pending");
|
||||
|
||||
ret = gst_element_set_state (pipeline, GST_STATE_NULL);
|
||||
fail_unless (ret == GST_STATE_CHANGE_SUCCESS, "not SUCCESS");
|
||||
|
||||
gst_object_unref (pipeline);
|
||||
}
|
||||
|
||||
GST_END_TEST;
|
||||
|
||||
static GMutex *blocked_lock;
|
||||
static GCond *blocked_cond;
|
||||
|
||||
static void
|
||||
pad_blocked_cb (GstPad * pad, gboolean blocked, gpointer user_data)
|
||||
{
|
||||
g_mutex_lock (blocked_lock);
|
||||
GST_DEBUG ("srcpad blocked: %d, sending signal", blocked);
|
||||
g_cond_signal (blocked_cond);
|
||||
g_mutex_unlock (blocked_lock);
|
||||
}
|
||||
|
||||
GST_START_TEST (test_add_live2)
|
||||
{
|
||||
GstElement *sink, *src, *pipeline;
|
||||
GstStateChangeReturn ret;
|
||||
GstState current, pending;
|
||||
GstPad *srcpad, *sinkpad;
|
||||
|
||||
blocked_lock = g_mutex_new ();
|
||||
blocked_cond = g_cond_new ();
|
||||
|
||||
pipeline = gst_pipeline_new ("pipeline");
|
||||
src = gst_element_factory_make ("fakesrc", "src");
|
||||
g_object_set (G_OBJECT (src), "is-live", TRUE, NULL);
|
||||
sink = gst_element_factory_make ("fakesink", "sink");
|
||||
|
||||
/* add sink, don't add source yet */
|
||||
gst_bin_add (GST_BIN (pipeline), sink);
|
||||
|
||||
/* set the pipeline to PLAYING. This will return ASYNC on READY->PAUSED */
|
||||
ret = gst_element_set_state (pipeline, GST_STATE_PLAYING);
|
||||
fail_unless (ret == GST_STATE_CHANGE_ASYNC, "no ASYNC state return");
|
||||
|
||||
g_mutex_lock (blocked_lock);
|
||||
|
||||
GST_DEBUG ("blocking srcpad");
|
||||
/* block source pad */
|
||||
srcpad = gst_element_get_pad (src, "src");
|
||||
gst_pad_set_blocked_async (srcpad, TRUE, pad_blocked_cb, NULL);
|
||||
|
||||
/* set source to PAUSED without adding it to the pipeline */
|
||||
ret = gst_element_set_state (src, GST_STATE_PAUSED);
|
||||
fail_unless (ret == GST_STATE_CHANGE_NO_PREROLL,
|
||||
"no NO_PREROLL state return");
|
||||
|
||||
/* add source now, pipeline should notice the NO_PREROLL element. This
|
||||
* should trigger as commit of the ASYNC pipeline and make it continue
|
||||
* to PLAYING. We blocked the source pad so that we don't get an unlinked
|
||||
* error. */
|
||||
gst_bin_add (GST_BIN (pipeline), src);
|
||||
|
||||
/* wait for pad blocked, this means the source is now PLAYING. */
|
||||
g_cond_wait (blocked_cond, blocked_lock);
|
||||
g_mutex_unlock (blocked_lock);
|
||||
|
||||
GST_DEBUG ("linking pads");
|
||||
|
||||
/* link to sink */
|
||||
sinkpad = gst_element_get_pad (sink, "sink");
|
||||
gst_pad_link (srcpad, sinkpad);
|
||||
gst_object_unref (srcpad);
|
||||
gst_object_unref (sinkpad);
|
||||
|
||||
GST_DEBUG ("unblocking srcpad");
|
||||
|
||||
/* and unblock */
|
||||
gst_pad_set_blocked_async (srcpad, FALSE, pad_blocked_cb, NULL);
|
||||
|
||||
GST_DEBUG ("getting state");
|
||||
|
||||
/* we should be SUCCESS now and PLAYING */
|
||||
ret =
|
||||
gst_element_get_state (pipeline, ¤t, &pending, GST_CLOCK_TIME_NONE);
|
||||
fail_unless (ret == GST_STATE_CHANGE_SUCCESS, "not SUCCESS");
|
||||
fail_unless (current == GST_STATE_PLAYING, "not PLAYING");
|
||||
fail_unless (pending == GST_STATE_VOID_PENDING, "have pending");
|
||||
|
||||
ret = gst_element_set_state (pipeline, GST_STATE_NULL);
|
||||
fail_unless (ret == GST_STATE_CHANGE_SUCCESS, "not SUCCESS");
|
||||
|
||||
g_cond_free (blocked_cond);
|
||||
g_mutex_free (blocked_lock);
|
||||
gst_object_unref (pipeline);
|
||||
}
|
||||
|
||||
GST_END_TEST;
|
||||
|
||||
/* test: try changing state of sinks */
|
||||
Suite *
|
||||
gst_sinks_suite (void)
|
||||
|
@ -465,6 +861,13 @@ gst_sinks_suite (void)
|
|||
tcase_add_test (tc_chain, test_livesrc_sink);
|
||||
tcase_add_test (tc_chain, test_livesrc2_sink);
|
||||
tcase_add_test (tc_chain, test_livesrc3_sink);
|
||||
tcase_add_test (tc_chain, test_locked_sink);
|
||||
tcase_add_test (tc_chain, test_unlinked_live);
|
||||
tcase_add_test (tc_chain, test_delayed_async);
|
||||
tcase_add_test (tc_chain, test_added_async);
|
||||
tcase_add_test (tc_chain, test_added_async2);
|
||||
tcase_add_test (tc_chain, test_add_live);
|
||||
tcase_add_test (tc_chain, test_add_live2);
|
||||
|
||||
return s;
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue