2005-11-03 Wim Taymans <wim@fluendo.com>
+ * check/states/sinks.c: (GST_START_TEST), (gst_object_suite):
+ Added some more checks. Specifically the case where NO_PREROLL
+ elements are in the pipeline.
+
+ * gst/base/gstbasesink.c: (gst_base_sink_commit_state),
+ (gst_base_sink_handle_object), (gst_base_sink_do_sync),
+ (gst_base_sink_get_position):
+ Post READY->PAUSED state change messages too.
+ Fix bug where VOID was posted as pending state...
+
+ * gst/gstbin.c: (gst_bin_recalc_state):
+ use _element_continue_state() to continue the state change.
+
+ * gst/gstelement.c: (gst_element_continue_state),
+ (gst_element_commit_state), (gst_element_set_state_func),
+ (gst_element_change_state), (gst_element_change_state_func):
+ Lots of state change cleanups, assign the STATE_RETURN in
+ a new continue_state() function that also propagates the
+ last return value from a state change to the app.
+ Update some debug statements with proper category.
+
+2005-11-03 Wim Taymans <wim@fluendo.com>
+
* docs/design/part-events.txt:
* docs/design/part-gstpipeline.txt:
* docs/design/part-messages.txt:
}
GST_END_TEST
-/* a sink should go ASYNC to PAUSE. PAUSE does not complete
- * since we have a live source. */
+/* the sink should go ASYNC to PAUSE. The live source should go
+ * NO_PREROLL to PAUSE. the pipeline returns NO_PREROLL. An
+ * attempt to go to PLAYING will return ASYNC. polling state
+ * completion should return SUCCESS when the sink is gone to
+ * PLAYING. */
GST_START_TEST (test_livesrc_sink)
{
GstElement *sink, *src, *pipeline;
fail_unless (ret == GST_STATE_CHANGE_NO_PREROLL,
"no no_preroll state return");
+ ret = gst_element_set_state (pipeline, GST_STATE_PAUSED);
+ fail_unless (ret == GST_STATE_CHANGE_NO_PREROLL,
+ "no no_preroll state return the second time");
+
ret = gst_element_get_state (src, ¤t, &pending, GST_CLOCK_TIME_NONE);
fail_unless (ret == GST_STATE_CHANGE_NO_PREROLL, "not paused");
fail_unless (current == GST_STATE_PAUSED, "not paused");
GST_END_TEST;
+/* The sink should go ASYNC to PLAYING. The source should go
+ * to PLAYING with SUCCESS. The pipeline returns ASYNC. */
+GST_START_TEST (test_livesrc2_sink)
+{
+ 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");
+
+ gst_bin_add (GST_BIN (pipeline), src);
+ gst_bin_add (GST_BIN (pipeline), 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");
+
+ ret = gst_element_get_state (src, ¤t, &pending, GST_CLOCK_TIME_NONE);
+ fail_unless (ret == GST_STATE_CHANGE_SUCCESS, "not playing");
+ fail_unless (current == GST_STATE_PLAYING, "not playing");
+ fail_unless (pending == GST_STATE_VOID_PENDING, "not playing");
+
+ ret =
+ gst_element_get_state (pipeline, ¤t, &pending, GST_CLOCK_TIME_NONE);
+ fail_unless (ret == GST_STATE_CHANGE_SUCCESS, "not playing");
+ fail_unless (current == GST_STATE_PLAYING, "not playing");
+ fail_unless (pending == GST_STATE_VOID_PENDING, "not playing");
+
+ /* and back down */
+ 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 (src, ¤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, "not paused");
+
+ /* sink state is not known.. it might be prerolled or not */
+
+ /* and to READY */
+ ret = gst_element_set_state (pipeline, GST_STATE_READY);
+ fail_unless (ret == GST_STATE_CHANGE_SUCCESS, "no success state return");
+
+ ret = gst_element_get_state (src, ¤t, &pending, GST_CLOCK_TIME_NONE);
+ fail_unless (ret == GST_STATE_CHANGE_SUCCESS, "not success");
+ fail_unless (current == GST_STATE_READY, "not ready");
+ fail_unless (pending == GST_STATE_VOID_PENDING, "not ready");
+
+ 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_READY, "not ready");
+ fail_unless (pending == GST_STATE_VOID_PENDING, "not ready");
+}
+
+GST_END_TEST;
+
+GST_START_TEST (test_livesrc3_sink)
+{
+ 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");
+
+ gst_bin_add (GST_BIN (pipeline), src);
+ gst_bin_add (GST_BIN (pipeline), 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");
+
+ ret =
+ gst_element_get_state (pipeline, ¤t, &pending, GST_CLOCK_TIME_NONE);
+ fail_unless (ret == GST_STATE_CHANGE_SUCCESS, "not playing");
+ fail_unless (current == GST_STATE_PLAYING, "not playing");
+ fail_unless (pending == GST_STATE_VOID_PENDING, "not playing");
+
+ /* and back down */
+ ret = gst_element_set_state (pipeline, GST_STATE_NULL);
+ fail_unless (ret == GST_STATE_CHANGE_SUCCESS, "no success state return");
+}
+
+GST_END_TEST;
/* test: try changing state of sinks */
Suite *
tcase_add_test (tc_chain, test_src_sink);
tcase_add_test (tc_chain, test_livesrc_remove);
tcase_add_test (tc_chain, test_livesrc_sink);
+ tcase_add_test (tc_chain, test_livesrc2_sink);
+ tcase_add_test (tc_chain, test_livesrc3_sink);
return s;
}
case GST_STATE_PLAYING:
basesink->need_preroll = FALSE;
post_playing = TRUE;
+ /* post PAUSED too when we were READY */
+ if (current == GST_STATE_READY)
+ post_paused = TRUE;
break;
case GST_STATE_PAUSED:
basesink->need_preroll = TRUE;
GST_STATE_NEXT (basesink) = GST_STATE_VOID_PENDING;
GST_STATE_PENDING (basesink) = GST_STATE_VOID_PENDING;
GST_STATE_RETURN (basesink) = GST_STATE_CHANGE_SUCCESS;
-
- pending = GST_STATE_VOID_PENDING;
}
GST_UNLOCK (basesink);
return ret;
}
+/* FIXME, defined in gstelement.c but not yet in the header */
+GstStateChangeReturn
+gst_element_continue_state (GstElement * element, GstStateChangeReturn ret);
+
static void
gst_bin_recalc_state (GstBin * bin, gboolean force)
{
switch (ret) {
case GST_STATE_CHANGE_SUCCESS:
case GST_STATE_CHANGE_NO_PREROLL:
- gst_element_commit_state (GST_ELEMENT_CAST (bin));
+ ret = gst_element_continue_state (GST_ELEMENT_CAST (bin), ret);
break;
case GST_STATE_CHANGE_ASYNC:
gst_element_lost_state (GST_ELEMENT_CAST (bin));
goto unknown_state;
}
- GST_CAT_INFO_OBJECT (GST_CAT_STATES, bin, "return now %d",
- GST_STATE_RETURN (bin));
+ GST_CAT_INFO_OBJECT (GST_CAT_STATES, bin, "bin RETURN is now %d", ret);
return;
}
}
-/**
- * gst_element_commit_state:
- * @element: a #GstElement to commit the state of.
- *
- * Commit the state change of the element. This function is used
- * by elements that do asynchronous state changes.
- * The core will normally call this method automatically when an
- * element returned SUCCESS from the state change function.
- * Elements that return ASYNC from the change_state function should
- * eventually call this method from the streaming thread to signal
- * successfull state change completion.
- *
- * If after calling this method the element still has not reached
- * the pending state, the next state change is performed.
- *
- * This function can only be called with the STATE_LOCK held.
- *
- * Returns: The result of the commit state change.
- *
- * MT safe.
- */
+/* FIXME, this function is to be put in the .h file,
+ * gst_element_commit_state() should go away. */
GstStateChangeReturn
-gst_element_commit_state (GstElement * element)
+gst_element_continue_state (GstElement * element, GstStateChangeReturn ret)
{
GstState pending;
- GstStateChangeReturn ret;
GstState old_state, old_next;
GstState current, next;
GstMessage *message;
GstStateChange transition;
- g_return_val_if_fail (GST_IS_ELEMENT (element), GST_STATE_CHANGE_FAILURE);
-
GST_LOCK (element);
+ GST_STATE_RETURN (element) = ret;
pending = GST_STATE_PENDING (element);
/* check if there is something to commit */
nothing_pending:
{
- GST_DEBUG_OBJECT (element, "nothing pending");
+ GST_CAT_INFO_OBJECT (GST_CAT_STATES, element, "nothing pending");
GST_UNLOCK (element);
- return GST_STATE_CHANGE_SUCCESS;
+ return ret;
}
complete:
{
GST_STATE_PENDING (element) = GST_STATE_VOID_PENDING;
GST_STATE_NEXT (element) = GST_STATE_VOID_PENDING;
- ret = GST_STATE_RETURN (element) = GST_STATE_CHANGE_SUCCESS;
- GST_DEBUG_OBJECT (element, "completed state change");
+ GST_CAT_INFO_OBJECT (GST_CAT_STATES, element, "completed state change");
GST_UNLOCK (element);
message = gst_message_new_state_changed (GST_OBJECT (element),
}
/**
+ * gst_element_commit_state:
+ * @element: a #GstElement to commit the state of.
+ *
+ * Commit the state change of the element. This function is used
+ * by elements that do asynchronous state changes.
+ * The core will normally call this method automatically when an
+ * element returned SUCCESS from the state change function.
+ * Elements that return ASYNC from the change_state function should
+ * eventually call this method from the streaming thread to signal
+ * successfull state change completion.
+ *
+ * If after calling this method the element still has not reached
+ * the pending state, the next state change is performed.
+ *
+ * This function can only be called with the STATE_LOCK held.
+ *
+ * Returns: The result of the commit state change.
+ *
+ * MT safe.
+ */
+GstStateChangeReturn
+gst_element_commit_state (GstElement * element)
+{
+ g_return_val_if_fail (GST_IS_ELEMENT (element), GST_STATE_CHANGE_FAILURE);
+
+ return gst_element_continue_state (element, GST_STATE_CHANGE_SUCCESS);
+}
+
+/**
* gst_element_lost_state:
* @element: a #GstElement the state is lost of
*
GST_STATE_UNLOCK (element);
- GST_DEBUG_OBJECT (element, "returned %d", ret);
+ GST_CAT_DEBUG_OBJECT (GST_CAT_STATES, element, "returned %d", ret);
return ret;
was_busy:
{
GST_STATE_RETURN (element) = GST_STATE_CHANGE_ASYNC;
- GST_DEBUG_OBJECT (element, "element was busy with async state change");
+ GST_CAT_DEBUG_OBJECT (GST_CAT_STATES, element,
+ "element was busy with async state change");
GST_UNLOCK (element);
GST_STATE_UNLOCK (element);
/* if we go upwards, we give the app a change to wait for
* completion */
if (current < next)
- goto exit;
+ goto async;
/* else we just continue the state change downwards */
GST_CAT_INFO_OBJECT (GST_CAT_STATES, element,
gst_element_state_get_name (current),
gst_element_state_get_name (next));
- GST_LOCK (element);
- GST_STATE_RETURN (element) = GST_STATE_CHANGE_SUCCESS;
- GST_UNLOCK (element);
-
- ret = gst_element_commit_state (element);
+ ret = gst_element_continue_state (element, GST_STATE_CHANGE_SUCCESS);
break;
case GST_STATE_CHANGE_SUCCESS:
GST_CAT_DEBUG_OBJECT (GST_CAT_STATES, element,
"element changed state SUCCESS");
/* we can commit the state now which will proceeed to
* the next state */
- ret = gst_element_commit_state (element);
+ ret = gst_element_continue_state (element, ret);
break;
case GST_STATE_CHANGE_NO_PREROLL:
GST_CAT_DEBUG_OBJECT (GST_CAT_STATES, element,
"element changed state NO_PREROLL");
/* we can commit the state now which will proceeed to
* the next state */
- gst_element_commit_state (element);
- ret = GST_STATE_CHANGE_NO_PREROLL;
+ ret = gst_element_continue_state (element, ret);
break;
default:
- ret = GST_STATE_CHANGE_FAILURE;
goto invalid_return;
}
-exit:
+ GST_CAT_LOG_OBJECT (GST_CAT_STATES, element, "exit state change %d", ret);
+
+ return ret;
+
+async:
GST_LOCK (element);
GST_STATE_RETURN (element) = ret;
+ GST_CAT_LOG_OBJECT (GST_CAT_STATES, element, "exit async state change %d",
+ ret);
GST_UNLOCK (element);
- GST_CAT_LOG_OBJECT (GST_CAT_STATES, element, "exit state change %d", ret);
-
return ret;
/* ERROR */
invalid_return:
{
GST_LOCK (element);
+ /* somebody added a GST_STATE_ and forgot to do stuff here ! */
+ g_critical ("%s: unknown return value %d from a state change function",
+ GST_ELEMENT_NAME (element), ret);
+
+ ret = GST_STATE_CHANGE_FAILURE;
GST_STATE_RETURN (element) = ret;
GST_UNLOCK (element);
- /* somebody added a GST_STATE_ and forgot to do stuff here ! */
- g_critical ("unknown return value %d from a state change function", ret);
return ret;
}
}
was_ok:
{
+ GST_LOCK (element);
+ result = GST_STATE_RETURN (element);
GST_CAT_DEBUG_OBJECT (GST_CAT_STATES, element,
"element is already in the %s state",
gst_element_state_get_name (state));
- return GST_STATE_RETURN (element);
+ GST_UNLOCK (element);
+
+ return result;
}
}
case GST_STATE_PLAYING:
basesink->need_preroll = FALSE;
post_playing = TRUE;
+ /* post PAUSED too when we were READY */
+ if (current == GST_STATE_READY)
+ post_paused = TRUE;
break;
case GST_STATE_PAUSED:
basesink->need_preroll = TRUE;
GST_STATE_NEXT (basesink) = GST_STATE_VOID_PENDING;
GST_STATE_PENDING (basesink) = GST_STATE_VOID_PENDING;
GST_STATE_RETURN (basesink) = GST_STATE_CHANGE_SUCCESS;
-
- pending = GST_STATE_VOID_PENDING;
}
GST_UNLOCK (basesink);
}
GST_END_TEST
-/* a sink should go ASYNC to PAUSE. PAUSE does not complete
- * since we have a live source. */
+/* the sink should go ASYNC to PAUSE. The live source should go
+ * NO_PREROLL to PAUSE. the pipeline returns NO_PREROLL. An
+ * attempt to go to PLAYING will return ASYNC. polling state
+ * completion should return SUCCESS when the sink is gone to
+ * PLAYING. */
GST_START_TEST (test_livesrc_sink)
{
GstElement *sink, *src, *pipeline;
fail_unless (ret == GST_STATE_CHANGE_NO_PREROLL,
"no no_preroll state return");
+ ret = gst_element_set_state (pipeline, GST_STATE_PAUSED);
+ fail_unless (ret == GST_STATE_CHANGE_NO_PREROLL,
+ "no no_preroll state return the second time");
+
ret = gst_element_get_state (src, ¤t, &pending, GST_CLOCK_TIME_NONE);
fail_unless (ret == GST_STATE_CHANGE_NO_PREROLL, "not paused");
fail_unless (current == GST_STATE_PAUSED, "not paused");
GST_END_TEST;
+/* The sink should go ASYNC to PLAYING. The source should go
+ * to PLAYING with SUCCESS. The pipeline returns ASYNC. */
+GST_START_TEST (test_livesrc2_sink)
+{
+ 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");
+
+ gst_bin_add (GST_BIN (pipeline), src);
+ gst_bin_add (GST_BIN (pipeline), 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");
+
+ ret = gst_element_get_state (src, ¤t, &pending, GST_CLOCK_TIME_NONE);
+ fail_unless (ret == GST_STATE_CHANGE_SUCCESS, "not playing");
+ fail_unless (current == GST_STATE_PLAYING, "not playing");
+ fail_unless (pending == GST_STATE_VOID_PENDING, "not playing");
+
+ ret =
+ gst_element_get_state (pipeline, ¤t, &pending, GST_CLOCK_TIME_NONE);
+ fail_unless (ret == GST_STATE_CHANGE_SUCCESS, "not playing");
+ fail_unless (current == GST_STATE_PLAYING, "not playing");
+ fail_unless (pending == GST_STATE_VOID_PENDING, "not playing");
+
+ /* and back down */
+ 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 (src, ¤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, "not paused");
+
+ /* sink state is not known.. it might be prerolled or not */
+
+ /* and to READY */
+ ret = gst_element_set_state (pipeline, GST_STATE_READY);
+ fail_unless (ret == GST_STATE_CHANGE_SUCCESS, "no success state return");
+
+ ret = gst_element_get_state (src, ¤t, &pending, GST_CLOCK_TIME_NONE);
+ fail_unless (ret == GST_STATE_CHANGE_SUCCESS, "not success");
+ fail_unless (current == GST_STATE_READY, "not ready");
+ fail_unless (pending == GST_STATE_VOID_PENDING, "not ready");
+
+ 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_READY, "not ready");
+ fail_unless (pending == GST_STATE_VOID_PENDING, "not ready");
+}
+
+GST_END_TEST;
+
+GST_START_TEST (test_livesrc3_sink)
+{
+ 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");
+
+ gst_bin_add (GST_BIN (pipeline), src);
+ gst_bin_add (GST_BIN (pipeline), 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");
+
+ ret =
+ gst_element_get_state (pipeline, ¤t, &pending, GST_CLOCK_TIME_NONE);
+ fail_unless (ret == GST_STATE_CHANGE_SUCCESS, "not playing");
+ fail_unless (current == GST_STATE_PLAYING, "not playing");
+ fail_unless (pending == GST_STATE_VOID_PENDING, "not playing");
+
+ /* and back down */
+ ret = gst_element_set_state (pipeline, GST_STATE_NULL);
+ fail_unless (ret == GST_STATE_CHANGE_SUCCESS, "no success state return");
+}
+
+GST_END_TEST;
/* test: try changing state of sinks */
Suite *
tcase_add_test (tc_chain, test_src_sink);
tcase_add_test (tc_chain, test_livesrc_remove);
tcase_add_test (tc_chain, test_livesrc_sink);
+ tcase_add_test (tc_chain, test_livesrc2_sink);
+ tcase_add_test (tc_chain, test_livesrc3_sink);
return s;
}