+2008-08-04 Wim Taymans <wim.taymans@collabora.co.uk>
+
+ * gst/gstbin.c: (gst_bin_add_func), (gst_bin_remove_func),
+ (gst_bin_change_state_func), (bin_handle_async_done),
+ (gst_bin_handle_message_func):
+ Fix race for bins that simulate ASYNC state changes by inserting
+ ASYNC_START and ASYNC_DONE messages in their bus. We need to check for
+ pending ASYNC messages even when the bin does not have ASYNC children.
+ We note detect this behaviour because we will receive an ASYNC message
+ that is originating from the bin itself.
+ Fixes races with decodebin2 state changes.
+
+ * tests/check/gst/gstbin.c: (GST_START_TEST):
+ Add some more debug.
+
2008-08-04 Tim-Philipp Müller <tim.muller at collabora co uk>
* gst/gsttaglist.c: (_gst_tag_initialize):
struct _GstBinPrivate
{
gboolean asynchandling;
+ /* if we get an ASYNC_DONE message from ourselves, this means that the
+ * subclass will simulate ASYNC behaviour without having ASYNC children. When
+ * such an ASYNC_DONE message is posted while we are doing a state change, we
+ * have to process the message after finishing the state change even when no
+ * child returned GST_STATE_CHANGE_ASYNC. */
+ gboolean pending_async_done;
};
typedef struct
GstStateChange transition);
static GstStateChangeReturn gst_bin_get_state_func (GstElement * element,
GstState * state, GstState * pending, GstClockTime timeout);
-static void bin_handle_async_done (GstBin * bin, GstStateChangeReturn ret);
+static void bin_handle_async_done (GstBin * bin, GstStateChangeReturn ret,
+ gboolean is_bin);
static void bin_handle_async_start (GstBin * bin, gboolean new_base_time);
static void bin_push_state_continue (BinContinueData * data);
}
case GST_STATE_CHANGE_NO_PREROLL:
/* ignore all async elements we might have and commit our state */
- bin_handle_async_done (bin, ret);
+ bin_handle_async_done (bin, ret, FALSE);
break;
case GST_STATE_CHANGE_FAILURE:
break;
else
ret = GST_STATE_CHANGE_SUCCESS;
- bin_handle_async_done (bin, ret);
+ bin_handle_async_done (bin, ret, FALSE);
} else {
GST_DEBUG_OBJECT (bin,
"recalc state preroll: %d, other async: %d, this async %d",
GST_OBJECT_LOCK (bin);
bin->polling = FALSE;
- if (ret != GST_STATE_CHANGE_ASYNC) {
+ /* it's possible that we did not get ASYNC form the children while the bin is
+ * simulating ASYNC behaviour by posting an ASYNC_DONE message on the bus with
+ * itself as the source. In that case we still want to check if the state
+ * change completed. */
+ if (ret != GST_STATE_CHANGE_ASYNC && !bin->priv->pending_async_done) {
/* no element returned ASYNC, we can just complete. */
GST_DEBUG_OBJECT (bin, "no async elements");
goto state_end;
bin_remove_messages (bin, NULL, GST_MESSAGE_ASYNC_DONE);
GST_DEBUG_OBJECT (bin, "async elements commited");
- bin_handle_async_done (bin, GST_STATE_CHANGE_SUCCESS);
+ bin_handle_async_done (bin, GST_STATE_CHANGE_SUCCESS, FALSE);
}
state_end:
+ bin->priv->pending_async_done = FALSE;
GST_OBJECT_UNLOCK (bin);
GST_CAT_DEBUG_OBJECT (GST_CAT_STATES, element,
* This function is called with the OBJECT lock.
*/
static void
-bin_handle_async_done (GstBin * bin, GstStateChangeReturn ret)
+bin_handle_async_done (GstBin * bin, GstStateChangeReturn ret, gboolean is_bin)
{
GstState current, pending, target;
GstStateChangeReturn old_ret;
was_busy:
{
GST_CAT_DEBUG_OBJECT (GST_CAT_STATES, bin, "state change busy");
+ if (is_bin)
+ bin->priv->pending_async_done = TRUE;
return;
}
nothing_pending:
case GST_MESSAGE_ASYNC_DONE:
{
GstState target;
+ gboolean is_bin;
GST_DEBUG_OBJECT (bin, "ASYNC_DONE message %p, %s", message,
GST_OBJECT_NAME (src));
if (target <= GST_STATE_READY)
goto ignore_done_message;
+ /* check if the message came from the bin itself in which case the bin
+ * will simulate ASYNC behaviour without having ASYNC children (such as
+ * decodebin2) */
+ is_bin = (GST_MESSAGE_SRC (message) == GST_OBJECT_CAST (bin));
+
bin_replace_message (bin, message, GST_MESSAGE_ASYNC_START);
/* if there are no more ASYNC_START messages, everybody posted
* a ASYNC_DONE and we can post one on the bus. When checking, we
bin_remove_messages (bin, NULL, GST_MESSAGE_ASYNC_DONE);
GST_DEBUG_OBJECT (bin, "async elements commited");
- bin_handle_async_done (bin, GST_STATE_CHANGE_SUCCESS);
+ bin_handle_async_done (bin, GST_STATE_CHANGE_SUCCESS, is_bin);
}
GST_OBJECT_UNLOCK (bin);
break;