From 2153c45964935922acbefbe152b159e3f0716b5e Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Mon, 10 Oct 2005 16:38:26 +0000 Subject: [PATCH] check/: Check fixes, use API as stated in design docs, remove hacks. Original commit message from CVS: * check/Makefile.am: * check/generic/states.c: (GST_START_TEST): * check/gst/gstbin.c: (GST_START_TEST): * check/gst/gstpipeline.c: (GST_START_TEST), (gst_pipeline_suite): * check/states/sinks.c: (GST_START_TEST): * check/states/sinks2.c: (GST_START_TEST), (gst_object_suite), (main): Check fixes, use API as stated in design docs, remove hacks. * gst/base/gstbasesink.c: (gst_base_sink_handle_object), (gst_base_sink_change_state): Catch stopping our task while we're shutting down. * gst/gstbin.c: (gst_bin_init), (gst_bin_add_func), (gst_bin_remove_func), (gst_bin_get_state_func), (gst_bin_recalc_state), (gst_bin_change_state_func), (bin_bus_handler): * gst/gstbin.h: * gst/gstelement.c: (gst_element_init), (gst_element_get_state_func), (gst_element_abort_state), (gst_element_commit_state), (gst_element_lost_state), (gst_element_set_state), (gst_element_change_state), (gst_element_change_state_func): * gst/gstelement.h: New state change algorithm (see #318116) * gst/gstpipeline.c: (gst_pipeline_class_init), (gst_pipeline_init), (gst_pipeline_set_property), (gst_pipeline_get_property), (do_pipeline_seek), (gst_pipeline_change_state), (gst_pipeline_provide_clock_func): * gst/gstpipeline.h: Remove crude state change hacks. * gst/gstutils.h: Remove crude hacks. * tools/gst-launch.c: (main): Fixes for state change. Needs some more work to fully use the new stuff. --- ChangeLog | 42 ++++++ check/Makefile.am | 1 + check/generic/states.c | 4 +- check/gst/gstbin.c | 27 ++-- check/gst/gstpipeline.c | 14 +- check/states/sinks.c | 6 +- check/states/sinks2.c | 95 ++++++++++++ gst/base/gstbasesink.c | 27 +++- gst/gstbin.c | 216 ++++++++++++++++----------- gst/gstbin.h | 2 + gst/gstelement.c | 334 ++++++++++++++++++++++++++---------------- gst/gstelement.h | 74 +++++----- gst/gstpipeline.c | 55 +------ gst/gstpipeline.h | 1 - gst/gstutils.h | 3 - libs/gst/base/gstbasesink.c | 27 +++- tests/check/Makefile.am | 1 + tests/check/generic/sinks.c | 6 +- tests/check/generic/states.c | 4 +- tests/check/gst/gstbin.c | 27 ++-- tests/check/gst/gstpipeline.c | 14 +- tools/gst-launch.c | 5 +- 22 files changed, 613 insertions(+), 372 deletions(-) create mode 100644 check/states/sinks2.c diff --git a/ChangeLog b/ChangeLog index 1fafb62..e3d96e7 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,45 @@ +2005-10-10 Wim Taymans + + * check/Makefile.am: + * check/generic/states.c: (GST_START_TEST): + * check/gst/gstbin.c: (GST_START_TEST): + * check/gst/gstpipeline.c: (GST_START_TEST), (gst_pipeline_suite): + * check/states/sinks.c: (GST_START_TEST): + * check/states/sinks2.c: (GST_START_TEST), (gst_object_suite), + (main): + Check fixes, use API as stated in design docs, remove hacks. + + * gst/base/gstbasesink.c: (gst_base_sink_handle_object), + (gst_base_sink_change_state): + Catch stopping our task while we're shutting down. + + * gst/gstbin.c: (gst_bin_init), (gst_bin_add_func), + (gst_bin_remove_func), (gst_bin_get_state_func), + (gst_bin_recalc_state), (gst_bin_change_state_func), + (bin_bus_handler): + * gst/gstbin.h: + * gst/gstelement.c: (gst_element_init), + (gst_element_get_state_func), (gst_element_abort_state), + (gst_element_commit_state), (gst_element_lost_state), + (gst_element_set_state), (gst_element_change_state), + (gst_element_change_state_func): + * gst/gstelement.h: + New state change algorithm (see #318116) + + * gst/gstpipeline.c: (gst_pipeline_class_init), + (gst_pipeline_init), (gst_pipeline_set_property), + (gst_pipeline_get_property), (do_pipeline_seek), + (gst_pipeline_change_state), (gst_pipeline_provide_clock_func): + * gst/gstpipeline.h: + Remove crude state change hacks. + + * gst/gstutils.h: + Remove crude hacks. + + * tools/gst-launch.c: (main): + Fixes for state change. Needs some more work to fully use the + new stuff. + 2005-10-10 Andy Wingo * tests/Makefile.am (noinst_PROGRAMS): No more init.c. diff --git a/check/Makefile.am b/check/Makefile.am index 53cedc7..242bb27 100644 --- a/check/Makefile.am +++ b/check/Makefile.am @@ -54,6 +54,7 @@ check_PROGRAMS = \ pipelines/simple_launch_lines \ pipelines/cleanup \ states/sinks \ + states/sinks2 \ gst-libs/controller \ gst-libs/gdp diff --git a/check/generic/states.c b/check/generic/states.c index 002d2fb..ac1faea 100644 --- a/check/generic/states.c +++ b/check/generic/states.c @@ -42,8 +42,7 @@ GST_START_TEST (test_state_changes) gst_element_set_state (element, GST_STATE_READY); gst_element_set_state (element, GST_STATE_PAUSED); gst_element_set_state (element, GST_STATE_PLAYING); - /* Sleep to give any pad tasks time to start */ - g_usleep (0.2 * G_USEC_PER_SEC); + gst_element_set_state (element, GST_STATE_PAUSED); gst_element_set_state (element, GST_STATE_READY); gst_element_set_state (element, GST_STATE_NULL); @@ -52,6 +51,7 @@ GST_START_TEST (test_state_changes) gst_element_set_state (element, GST_STATE_PLAYING); gst_element_set_state (element, GST_STATE_PAUSED); gst_element_set_state (element, GST_STATE_NULL); + gst_object_unref (GST_OBJECT (element)); } gst_task_cleanup_all (); diff --git a/check/gst/gstbin.c b/check/gst/gstbin.c index 713d61f..e84608e 100644 --- a/check/gst/gstbin.c +++ b/check/gst/gstbin.c @@ -262,7 +262,7 @@ GST_START_TEST (test_message_state_changed_children) /* change state to PAUSED, spawning three messages */ GST_DEBUG ("setting pipeline to PAUSED"); ret = gst_element_set_state (GST_ELEMENT (pipeline), GST_STATE_PAUSED); - fail_unless (ret == GST_STATE_CHANGE_SUCCESS); + fail_unless (ret == GST_STATE_CHANGE_ASYNC); ret = gst_element_get_state (GST_ELEMENT (pipeline), ¤t, &pending, NULL); fail_unless (ret == GST_STATE_CHANGE_SUCCESS); @@ -364,19 +364,13 @@ GST_START_TEST (test_watch_for_state_change) fail_unless (gst_element_link (src, sink), "could not link src and sink"); - /* change state, spawning two times three messages, minus one async */ + /* change state, spawning two times three messages */ ret = gst_element_set_state (GST_ELEMENT (bin), GST_STATE_PAUSED); fail_unless (ret == GST_STATE_CHANGE_ASYNC); + ret = gst_element_get_state (GST_ELEMENT (bin), NULL, NULL, NULL); + fail_unless (ret == GST_STATE_CHANGE_SUCCESS); - pop_messages (bus, 5); - - fail_unless (gst_bus_have_pending (bus) == FALSE, - "Unexpected messages on bus"); - - gst_bin_watch_for_state_change (GST_BIN (bin)); - - /* should get the bin's state change message now */ - pop_messages (bus, 1); + pop_messages (bus, 6); fail_unless (gst_bus_have_pending (bus) == FALSE, "Unexpected messages on bus"); @@ -388,8 +382,7 @@ GST_START_TEST (test_watch_for_state_change) /* this one might return either SUCCESS or ASYNC, likely SUCCESS */ gst_element_set_state (GST_ELEMENT (bin), GST_STATE_PAUSED); - - gst_bin_watch_for_state_change (GST_BIN (bin)); + gst_element_get_state (GST_ELEMENT (bin), NULL, NULL, NULL); pop_messages (bus, 3); @@ -482,6 +475,7 @@ GST_START_TEST (test_children_state_change_order_flagged_sink) { GstElement *src, *identity, *sink, *pipeline; GstStateChangeReturn ret; + GstState current, pending; GstBus *bus; pipeline = gst_pipeline_new (NULL); @@ -506,7 +500,12 @@ GST_START_TEST (test_children_state_change_order_flagged_sink) /* (1) Test state change with fakesink being a regular sink */ ret = gst_element_set_state (pipeline, GST_STATE_PLAYING); + fail_if (ret != GST_STATE_CHANGE_ASYNC, + "State change to PLAYING did not return ASYNC"); + ret = gst_element_get_state (pipeline, ¤t, &pending, NULL); fail_if (ret != GST_STATE_CHANGE_SUCCESS, "State change to PLAYING failed"); + fail_if (current != GST_STATE_PLAYING, "State change to PLAYING failed"); + fail_if (pending != GST_STATE_VOID_PENDING, "State change to PLAYING failed"); /* NULL => READY */ ASSERT_STATE_CHANGE_MSG (bus, sink, GST_STATE_NULL, GST_STATE_READY, 101); @@ -602,7 +601,7 @@ GST_START_TEST (test_children_state_change_order_semi_sink) GST_FLAG_UNSET (sink, GST_ELEMENT_IS_SINK); /* <======== */ ret = gst_element_set_state (pipeline, GST_STATE_PLAYING); - fail_if (ret != GST_STATE_CHANGE_SUCCESS, "State change to PLAYING failed"); + fail_if (ret != GST_STATE_CHANGE_ASYNC, "State change to PLAYING not ASYNC"); ret = gst_element_get_state (pipeline, ¤t, &pending, NULL); fail_if (ret != GST_STATE_CHANGE_SUCCESS, "State change to PLAYING failed"); fail_if (current != GST_STATE_PLAYING, "State change to PLAYING failed"); diff --git a/check/gst/gstpipeline.c b/check/gst/gstpipeline.c index e516789..127a940 100644 --- a/check/gst/gstpipeline.c +++ b/check/gst/gstpipeline.c @@ -28,7 +28,6 @@ GST_START_TEST (test_async_state_change_empty) pipeline = GST_PIPELINE (gst_pipeline_new (NULL)); fail_unless (pipeline != NULL, "Could not create pipeline"); - g_object_set (G_OBJECT (pipeline), "play-timeout", 0LL, NULL); fail_unless_equals_int (gst_element_set_state (GST_ELEMENT (pipeline), GST_STATE_PLAYING), GST_STATE_CHANGE_SUCCESS); @@ -45,7 +44,6 @@ GST_START_TEST (test_async_state_change_fake_ready) pipeline = GST_PIPELINE (gst_pipeline_new (NULL)); fail_unless (pipeline != NULL, "Could not create pipeline"); - g_object_set (G_OBJECT (pipeline), "play-timeout", 0LL, NULL); src = gst_element_factory_make ("fakesrc", NULL); sink = gst_element_factory_make ("fakesink", NULL); @@ -71,7 +69,6 @@ GST_START_TEST (test_async_state_change_fake) pipeline = GST_PIPELINE (gst_pipeline_new (NULL)); fail_unless (pipeline != NULL, "Could not create pipeline"); - g_object_set (G_OBJECT (pipeline), "play-timeout", 0LL, NULL); src = gst_element_factory_make ("fakesrc", NULL); sink = gst_element_factory_make ("fakesink", NULL); @@ -81,7 +78,7 @@ GST_START_TEST (test_async_state_change_fake) bus = gst_pipeline_get_bus (pipeline); - fail_unless_equals_int (gst_element_set_state_async (GST_ELEMENT (pipeline), + fail_unless_equals_int (gst_element_set_state (GST_ELEMENT (pipeline), GST_STATE_PLAYING), GST_STATE_CHANGE_ASYNC); while (!done) { @@ -98,7 +95,6 @@ GST_START_TEST (test_async_state_change_fake) } } - g_object_set (G_OBJECT (pipeline), "play-timeout", 3 * GST_SECOND, NULL); fail_unless_equals_int (gst_element_set_state (GST_ELEMENT (pipeline), GST_STATE_NULL), GST_STATE_CHANGE_SUCCESS); @@ -175,7 +171,6 @@ GST_START_TEST (test_bus) pipeline = gst_pipeline_new (NULL); fail_unless (pipeline != NULL, "Could not create pipeline"); ASSERT_OBJECT_REFCOUNT (pipeline, "pipeline", 1); - g_object_set (pipeline, "play-timeout", 0LL, NULL); src = gst_element_factory_make ("fakesrc", NULL); fail_unless (src != NULL); @@ -193,7 +188,7 @@ GST_START_TEST (test_bus) ASSERT_OBJECT_REFCOUNT (pipeline, "pipeline after add_watch", 1); ASSERT_OBJECT_REFCOUNT (bus, "bus after add_watch", 3); - gst_element_set_state_async (pipeline, GST_STATE_PLAYING); + gst_element_set_state (pipeline, GST_STATE_PLAYING); loop = g_main_loop_new (NULL, FALSE); GST_DEBUG ("going into main loop"); g_main_loop_run (loop); @@ -207,9 +202,6 @@ GST_START_TEST (test_bus) /* cleanup */ GST_DEBUG ("cleanup"); - /* current semantics require us to go step by step; this will change */ - gst_element_set_state (pipeline, GST_STATE_PAUSED); - gst_element_set_state (pipeline, GST_STATE_READY); gst_element_set_state (pipeline, GST_STATE_NULL); fail_unless (gst_element_get_state (pipeline, ¤t, NULL, NULL) == GST_STATE_CHANGE_SUCCESS); @@ -238,6 +230,8 @@ gst_pipeline_suite (void) Suite *s = suite_create ("GstPipeline"); TCase *tc_chain = tcase_create ("pipeline tests"); + tcase_set_timeout (tc_chain, 0); + suite_add_tcase (s, tc_chain); tcase_add_test (tc_chain, test_async_state_change_empty); tcase_add_test (tc_chain, test_async_state_change_fake_ready); diff --git a/check/states/sinks.c b/check/states/sinks.c index af4dffe..e63abfc 100644 --- a/check/states/sinks.c +++ b/check/states/sinks.c @@ -42,7 +42,7 @@ GST_START_TEST (test_sink) ret = gst_element_get_state (sink, ¤t, &pending, &tv); fail_unless (ret == GST_STATE_CHANGE_ASYNC, "not changing state async"); - fail_unless (current == GST_STATE_PAUSED, "bad current state"); + fail_unless (current == GST_STATE_READY, "bad current state"); fail_unless (pending == GST_STATE_PLAYING, "bad pending state"); ret = gst_element_set_state (sink, GST_STATE_PAUSED); @@ -81,6 +81,7 @@ GST_START_TEST (test_src_sink) gst_object_unref (sinkpad); ret = gst_element_set_state (pipeline, GST_STATE_PAUSED); + fail_unless (ret == GST_STATE_CHANGE_ASYNC, "no async state return"); ret = gst_element_get_state (pipeline, NULL, NULL, NULL); fail_unless (ret == GST_STATE_CHANGE_SUCCESS, "no success state return"); @@ -139,8 +140,7 @@ GST_START_TEST (test_livesrc_remove) ret = gst_element_get_state (pipeline, ¤t, &pending, &tv); fail_unless (ret == GST_STATE_CHANGE_ASYNC, "not async"); fail_unless (current == GST_STATE_PAUSED, "not paused"); - fail_unless (pending == GST_STATE_VOID_PENDING, "not playing"); - + fail_unless (pending == GST_STATE_PAUSED, "not paused"); } GST_END_TEST diff --git a/check/states/sinks2.c b/check/states/sinks2.c new file mode 100644 index 0000000..433a222 --- /dev/null +++ b/check/states/sinks2.c @@ -0,0 +1,95 @@ +/* GStreamer + * + * unit test for sinks + * + * Copyright (C) <2005> Wim Taymans + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include + +/* a sink should go ASYNC to PAUSE and PLAYING. */ +GST_START_TEST (test_sink) +{ + GstElement *sink, *src; + GstStateChangeReturn ret; + GstState current, pending; + GTimeVal tv; + + sink = gst_element_factory_make ("fakesink", "sink"); + + ret = gst_element_set_state (sink, GST_STATE_PLAYING); + fail_unless (ret == GST_STATE_CHANGE_ASYNC, "no async state return"); + + GST_TIME_TO_TIMEVAL ((GstClockTime) 0, tv); + + ret = gst_element_get_state (sink, ¤t, &pending, &tv); + fail_unless (ret == GST_STATE_CHANGE_ASYNC, "not changing state async"); + fail_unless (current == GST_STATE_READY, "bad current state"); + fail_unless (pending == GST_STATE_PLAYING, "bad pending state"); + + src = gst_element_factory_make ("fakesrc", "src"); + gst_element_link (src, sink); + + ret = gst_element_set_state (src, GST_STATE_PLAYING); + fail_unless (ret == GST_STATE_CHANGE_SUCCESS, "no success state return"); + + /* now wait for final state */ + ret = gst_element_get_state (sink, ¤t, &pending, NULL); + fail_unless (ret == GST_STATE_CHANGE_SUCCESS, "failed to change state"); + fail_unless (current == GST_STATE_PLAYING, "bad current state"); + fail_unless (pending == GST_STATE_VOID_PENDING, "bad pending state"); + + ret = gst_element_set_state (sink, GST_STATE_NULL); + fail_unless (ret == GST_STATE_CHANGE_SUCCESS, "failed to go to null"); + + ret = gst_element_set_state (src, GST_STATE_NULL); + fail_unless (ret == GST_STATE_CHANGE_SUCCESS, "failed to go to null"); + + gst_object_unref (sink); + gst_object_unref (src); +} + +GST_END_TEST +/* test: try changing state of sinks */ + Suite * gst_object_suite (void) +{ + Suite *s = suite_create ("Sinks"); + TCase *tc_chain = tcase_create ("general"); + + suite_add_tcase (s, tc_chain); + tcase_add_test (tc_chain, test_sink); + + return s; +} + +int +main (int argc, char **argv) +{ + int nf; + + Suite *s = gst_object_suite (); + SRunner *sr = srunner_create (s); + + gst_check_init (&argc, &argv); + + srunner_run_all (sr, CK_NORMAL); + nf = srunner_ntests_failed (sr); + srunner_free (sr); + + return nf; +} diff --git a/gst/base/gstbasesink.c b/gst/base/gstbasesink.c index 4d57ec2..f66b83e 100644 --- a/gst/base/gstbasesink.c +++ b/gst/base/gstbasesink.c @@ -622,6 +622,7 @@ gst_base_sink_handle_object (GstBaseSink * basesink, GstPad * pad, if (length == 1) { gint t; + GstTask *task; basesink->have_preroll = TRUE; /* we are prerolling */ @@ -636,8 +637,21 @@ gst_base_sink_handle_object (GstBaseSink * basesink, GstPad * pad, g_warning ("STREAM_LOCK should have been locked !!"); } - /* now we commit our state */ - GST_STATE_LOCK (basesink); + /* now we commit our state, this will also automatically proceed to + * the next pending state. */ + /* FIXME */ + if ((task = GST_PAD_TASK (pad))) { + while (!GST_STATE_TRYLOCK (basesink)) { + GST_DEBUG_OBJECT (basesink, + "state change happening, checking shutdown"); + GST_LOCK (pad); + if (G_UNLIKELY (GST_PAD_IS_FLUSHING (pad))) + goto task_stopped; + GST_UNLOCK (pad); + } + } else { + GST_STATE_LOCK (basesink); + } GST_DEBUG_OBJECT (basesink, "commit state"); gst_element_commit_state (GST_ELEMENT (basesink)); GST_STATE_UNLOCK (basesink); @@ -742,6 +756,12 @@ playing_async: return ret; } +task_stopped: + { + GST_UNLOCK (pad); + GST_DEBUG_OBJECT (basesink, "task is stopped"); + return GST_FLOW_WRONG_STATE; + } flushing: { GST_UNLOCK (pad); @@ -1514,7 +1534,8 @@ gst_base_sink_change_state (GstElement * element, GstStateChange transition) * we need to wait for a preroll */ GST_DEBUG_OBJECT (basesink, "have_preroll: %d, EOS: %d", basesink->have_preroll, basesink->eos); - if (!basesink->have_preroll && !basesink->eos) { + if (!basesink->have_preroll && !basesink->eos + && GST_STATE_PENDING (basesink) == GST_STATE_PAUSED) { GST_DEBUG_OBJECT (basesink, "PLAYING to PAUSED, need preroll to TRUE"); basesink->need_preroll = TRUE; ret = GST_STATE_CHANGE_ASYNC; diff --git a/gst/gstbin.c b/gst/gstbin.c index 96331e4..aa10368 100644 --- a/gst/gstbin.c +++ b/gst/gstbin.c @@ -76,7 +76,6 @@ GST_DEBUG_CATEGORY_STATIC (bin_debug); (guint) (bin)->child_states[2], (bin)->child_states[1], \ (bin)->child_states[0], gst_element_state_get_name (GST_STATE (bin))) - static GstElementDetails gst_bin_details = GST_ELEMENT_DETAILS ("Generic bin", "Generic/Bin", "Simple container object", @@ -86,6 +85,7 @@ GType _gst_bin_type = 0; static void gst_bin_dispose (GObject * object); +static void gst_bin_recalc_state (GstBin * bin, gboolean force); static GstStateChangeReturn gst_bin_change_state_func (GstElement * element, GstStateChange transition); static GstStateChangeReturn gst_bin_get_state_func (GstElement * element, @@ -292,6 +292,8 @@ gst_bin_init (GstBin * bin) bin->children = NULL; bin->children_cookie = 0; bin->eosed = NULL; + bin->polling = FALSE; + bin->state_dirty = FALSE; /* Set up a bus for listening to child elements */ bus = g_object_new (gst_bus_get_type (), NULL); @@ -483,7 +485,7 @@ gst_bin_add_func (GstBin * bin, GstElement * element) gst_element_set_base_time (element, GST_ELEMENT (bin)->base_time); GST_DEBUG_OBJECT (element, "setting clock %p", GST_ELEMENT_CLOCK (bin)); gst_element_set_clock (element, GST_ELEMENT_CLOCK (bin)); - + bin->state_dirty = TRUE; GST_UNLOCK (bin); /* unlink all linked pads */ @@ -619,6 +621,7 @@ gst_bin_remove_func (GstBin * bin, GstElement * element) GST_FLAG_UNSET (bin, GST_ELEMENT_IS_SINK); } } + bin->state_dirty = TRUE; GST_UNLOCK (bin); GST_CAT_INFO_OBJECT (GST_CAT_PARENTAGE, bin, "removed child \"%s\"", @@ -872,14 +875,7 @@ gst_bin_iterate_sinks (GstBin * bin) return result; } -/* 2 phases: - * 1) check state of all children with 0 timeout to find ERROR and - * NO_PREROLL elements. return if found. - * 2) perform full blocking wait with requested timeout. - * - * 2) cannot be performed when 1) returns results as the sinks might - * not be able to complete the state change making 2) block forever. - * +/* * MT safe */ static GstStateChangeReturn @@ -887,6 +883,18 @@ gst_bin_get_state_func (GstElement * element, GstState * state, GstState * pending, GTimeVal * timeout) { GstBin *bin = GST_BIN (element); + + GST_CAT_INFO_OBJECT (GST_CAT_STATES, element, "getting state"); + + /* do a non forced recalculation of the state */ + gst_bin_recalc_state (bin, FALSE); + + return parent_class->get_state (element, state, pending, timeout); +} + +static void +gst_bin_recalc_state (GstBin * bin, gboolean force) +{ GstStateChangeReturn ret; GList *children; guint32 children_cookie; @@ -894,12 +902,21 @@ gst_bin_get_state_func (GstElement * element, GstState * state, gboolean have_async; GTimeVal tv; - GST_CAT_INFO_OBJECT (GST_CAT_STATES, element, "getting state"); - ret = GST_STATE_CHANGE_SUCCESS; /* lock bin, no element can be added or removed while we have this lock */ GST_LOCK (bin); + /* no point in scanning if nothing changed and it's no forced recalc */ + if (!force && !bin->state_dirty) + goto not_dirty; + + /* no point in having two scans run concurrently */ + if (bin->polling) + goto was_polling; + + bin->polling = TRUE; + + GST_CAT_INFO_OBJECT (GST_CAT_STATES, bin, "recalc state"); restart: have_no_preroll = FALSE; @@ -935,7 +952,7 @@ restart: switch (ret) { case GST_STATE_CHANGE_FAILURE: - /* report FAILURE immediately */ + /* report FAILURE immediatly */ goto done; case GST_STATE_CHANGE_NO_PREROLL: /* we have to continue scanning as there might be @@ -957,59 +974,15 @@ restart: /* if we have NO_PREROLL, return that */ if (have_no_preroll) { ret = GST_STATE_CHANGE_NO_PREROLL; - goto done; } - /* else return SUCCESS if no async elements were found */ - else if (!have_async) { - ret = GST_STATE_CHANGE_SUCCESS; - goto done; - } - - /* next we poll all children for their state to see if one of them - * is still busy with its state change. We did not release the bin lock - * yet so the elements are the same as the ones from the quick scan. */ - children = bin->children; - children_cookie = bin->children_cookie; - while (children) { - GstElement *child = GST_ELEMENT_CAST (children->data); - - gst_object_ref (child); - /* now we release the lock to enter the potentialy blocking wait */ - GST_UNLOCK (bin); - - /* ret is ASYNC if some child is still performing the state change - * ater the timeout. */ - ret = gst_element_get_state (child, NULL, NULL, timeout); - - gst_object_unref (child); - - /* now grab the lock to iterate to the next child */ - GST_LOCK (bin); - if (G_UNLIKELY (children_cookie != bin->children_cookie)) { - /* child added/removed during state change, restart. We need - * to restart with the quick check as a no-preroll element could - * have been added here and we don't want to block on sinks then.*/ - goto restart; - } - - switch (ret) { - case GST_STATE_CHANGE_SUCCESS: - break; - case GST_STATE_CHANGE_FAILURE: - case GST_STATE_CHANGE_NO_PREROLL: - /* report FAILURE and NO_PREROLL immediatly */ - goto done; - case GST_STATE_CHANGE_ASYNC: - goto done; - default: - g_assert_not_reached (); - } - children = g_list_next (children); + /* else return ASYNC if async elements where found. */ + else if (have_async) { + ret = GST_STATE_CHANGE_ASYNC; } - /* if we got here, all elements can do preroll */ - have_no_preroll = FALSE; done: + bin->state_dirty = FALSE; + bin->polling = FALSE; GST_UNLOCK (bin); /* now we can take the state lock, it is possible that new elements @@ -1019,33 +992,45 @@ done: GST_STATE_LOCK (bin); switch (ret) { case GST_STATE_CHANGE_SUCCESS: - gst_element_commit_state (element); + case GST_STATE_CHANGE_NO_PREROLL: + gst_element_commit_state (GST_ELEMENT_CAST (bin)); + break; + case GST_STATE_CHANGE_ASYNC: + gst_element_lost_state (GST_ELEMENT_CAST (bin)); break; case GST_STATE_CHANGE_FAILURE: - gst_element_abort_state (element); + gst_element_abort_state (GST_ELEMENT_CAST (bin)); break; default: - /* other cases are just passed along */ - break; + goto unknown_state; } - - /* and report the state if needed */ - if (state) - *state = GST_STATE (element); - if (pending) - *pending = GST_STATE_PENDING (element); - - GST_STATE_NO_PREROLL (element) = have_no_preroll; - - GST_CAT_INFO_OBJECT (GST_CAT_STATES, element, - "state current: %s, pending: %s, error: %d, no_preroll: %d, result: %d", - gst_element_state_get_name (GST_STATE (element)), - gst_element_state_get_name (GST_STATE_PENDING (element)), - GST_STATE_ERROR (element), GST_STATE_NO_PREROLL (element), ret); - + GST_CAT_INFO_OBJECT (GST_CAT_STATES, bin, "return now %d", + GST_STATE_RETURN (bin)); GST_STATE_UNLOCK (bin); + return; - return ret; +not_dirty: + { + GST_CAT_INFO_OBJECT (GST_CAT_STATES, bin, "not dirty"); + GST_UNLOCK (bin); + return; + } +was_polling: + { + GST_CAT_INFO_OBJECT (GST_CAT_STATES, bin, "was polling"); + GST_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; + } } /*********************************************** @@ -1340,16 +1325,13 @@ gst_bin_change_state_func (GstElement * element, GstStateChange transition) gboolean done; /* we don't need to take the STATE_LOCK, it is already taken */ - current = GST_STATE (element); - next = GST_STATE_PENDING (element); + current = GST_STATE_TRANSITION_CURRENT (transition); + next = GST_STATE_TRANSITION_NEXT (transition); GST_CAT_DEBUG_OBJECT (GST_CAT_STATES, element, "changing state of children from %s to %s", gst_element_state_get_name (current), gst_element_state_get_name (next)); - if (next == GST_STATE_VOID_PENDING) - return GST_STATE_CHANGE_SUCCESS; - bin = GST_BIN_CAST (element); /* Clear eosed element list on next PAUSED */ @@ -1435,12 +1417,14 @@ restart: } } + ret = parent_class->change_state (element, transition); + if (ret == GST_STATE_CHANGE_FAILURE) + goto done; + if (have_no_preroll) { ret = GST_STATE_CHANGE_NO_PREROLL; } else if (have_async) { ret = GST_STATE_CHANGE_ASYNC; - } else { - ret = parent_class->change_state (element, transition); } done: @@ -1563,6 +1547,60 @@ bin_bus_handler (GstBus * bus, GstMessage * message, GstBin * bin) gst_message_unref (message); break; } + case GST_MESSAGE_STATE_CHANGED: + { + GstState old, new, pending; + GstObject *src; + + gst_message_parse_state_changed (message, &old, &new, &pending); + src = GST_MESSAGE_SRC (message); + /* ref src, as we need it after we post the message up */ + gst_object_ref (src); + + GST_DEBUG_OBJECT (bin, "%s gave state change, %s -> %s, pending %s", + GST_ELEMENT_NAME (src), + gst_element_state_get_name (old), + gst_element_state_get_name (new), + gst_element_state_get_name (pending)); + + /* post message up */ + gst_element_post_message (GST_ELEMENT_CAST (bin), message); + + /* we only act on our own children */ + GST_LOCK (bin); + if (!g_list_find (bin->children, src)) + goto not_our_child; + GST_UNLOCK (bin); + + gst_object_unref (src); + + /* we can lock, either the state change is sync and we can + * recursively lock or the state change is async and we + * lock when the bin has done it state change. We can check which + * case it is by looking at the CHANGING_STATE flag. */ + GST_STATE_LOCK (bin); + GST_DEBUG_OBJECT (bin, "locked"); + + if (!GST_FLAG_IS_SET (bin, GST_ELEMENT_CHANGING_STATE)) { + GST_DEBUG_OBJECT (bin, "got ASYNC message, forcing recalc state"); + GST_STATE_UNLOCK (bin); + + /* force bin state recalculation on async messages. */ + gst_bin_recalc_state (bin, TRUE); + } else { + GST_STATE_UNLOCK (bin); + GST_DEBUG_OBJECT (bin, "got SYNC message"); + } + break; + + not_our_child: + { + GST_UNLOCK (bin); + GST_DEBUG_OBJECT (bin, "not our child"); + gst_object_unref (src); + break; + } + } default: /* Send all other messages upward */ GST_DEBUG_OBJECT (bin, "posting message upward"); diff --git a/gst/gstbin.h b/gst/gstbin.h index eaae8c2..bc95399 100644 --- a/gst/gstbin.h +++ b/gst/gstbin.h @@ -92,6 +92,8 @@ struct _GstBin { GstBus *child_bus; /* Bus we set on our children */ GList *eosed; /* list of elements that posted EOS */ + gboolean polling; + gboolean state_dirty; /*< private >*/ gpointer _gst_reserved[GST_PADDING]; }; diff --git a/gst/gstelement.c b/gst/gstelement.c index 02ee455..6962513 100644 --- a/gst/gstelement.c +++ b/gst/gstelement.c @@ -55,7 +55,6 @@ * * Each element has a state (see #GstState). You can get and set the state * of an element with gst_element_get_state() and gst_element_set_state(). - * You can wait for an element to change it's state with gst_element_wait_state_change(). * To get a string representation of a #GstState, use * gst_element_state_get_name(). * @@ -71,6 +70,7 @@ * Note that clock slection and distribution is normally handled by the toplevel * #GstPipeline so the clock functions are only to be used in very specific situations. */ + #include "gst_private.h" #include #include @@ -88,7 +88,6 @@ /* Element signals and args */ enum { - STATE_CHANGE, NEW_PAD, PAD_REMOVED, NO_MORE_PADS, @@ -118,6 +117,8 @@ static GstStateChangeReturn gst_element_change_state (GstElement * element, GstStateChange transition); static GstStateChangeReturn gst_element_change_state_func (GstElement * element, GstStateChange transition); +static GstStateChangeReturn gst_element_change_state_func (GstElement * element, + GstStateChange transition); static GstStateChangeReturn gst_element_get_state_func (GstElement * element, GstState * state, GstState * pending, GTimeVal * timeout); static void gst_element_set_bus_func (GstElement * element, GstBus * bus); @@ -239,8 +240,11 @@ gst_element_base_class_finalize (gpointer g_class) static void gst_element_init (GstElement * element) { - element->current_state = GST_STATE_NULL; - element->pending_state = GST_STATE_VOID_PENDING; + GST_STATE (element) = GST_STATE_NULL; + GST_STATE_NEXT (element) = GST_STATE_VOID_PENDING; + GST_STATE_PENDING (element) = GST_STATE_VOID_PENDING; + GST_STATE_RETURN (element) = GST_STATE_CHANGE_SUCCESS; + element->state_lock = g_new0 (GStaticRecMutex, 1); g_static_rec_mutex_init (element->state_lock); element->state_cond = g_cond_new (); @@ -1608,17 +1612,19 @@ gst_element_get_state_func (GstElement * element, GST_CAT_INFO_OBJECT (GST_CAT_STATES, element, "getting state"); GST_STATE_LOCK (element); + ret = GST_STATE_RETURN (element); + /* we got an error, report immediatly */ - if (GST_STATE_NO_PREROLL (element)) { - ret = GST_STATE_CHANGE_NO_PREROLL; + if (ret == GST_STATE_CHANGE_FAILURE) goto done; - } - /* we got an error, report immediatly */ - if (GST_STATE_ERROR (element)) { - ret = GST_STATE_CHANGE_FAILURE; + /* we got no_preroll, report immediatly */ + if (ret == GST_STATE_CHANGE_NO_PREROLL) + goto done; + + /* no need to wait async if we are not async */ + if (ret != GST_STATE_CHANGE_ASYNC) goto done; - } old_pending = GST_STATE_PENDING (element); if (old_pending != GST_STATE_VOID_PENDING) { @@ -1627,6 +1633,9 @@ gst_element_get_state_func (GstElement * element, if (timeout) { glong add = timeout->tv_sec * G_USEC_PER_SEC + timeout->tv_usec; + if (add == 0) + goto done; + /* make timeout absolute */ g_get_current_time (&abstimeout); g_time_val_add (&abstimeout, add); @@ -1664,10 +1673,9 @@ done: *pending = GST_STATE_PENDING (element); GST_CAT_INFO_OBJECT (GST_CAT_STATES, element, - "state current: %s, pending: %s, error: %d, no_preroll: %d, result: %d", + "state current: %s, pending: %s, result: %d", gst_element_state_get_name (GST_STATE (element)), - gst_element_state_get_name (GST_STATE_PENDING (element)), - GST_STATE_ERROR (element), GST_STATE_NO_PREROLL (element), ret); + gst_element_state_get_name (GST_STATE_PENDING (element)), ret); GST_STATE_UNLOCK (element); @@ -1692,6 +1700,9 @@ done: * an error, this function returns immediatly with a return value of * GST_STATE_CHANGE_SUCCESS or GST_STATE_CHANGE_FAILURE respectively. * + * For elements that did not return ASYNC, this function returns the + * current and pending state immediatly. + * * Returns: GST_STATE_CHANGE_SUCCESS if the element has no more pending state and * the last state change succeeded, GST_STATE_CHANGE_ASYNC * if the element is still performing a state change or @@ -1737,17 +1748,17 @@ gst_element_abort_state (GstElement * element) pending = GST_STATE_PENDING (element); - if (pending != GST_STATE_VOID_PENDING && !GST_STATE_ERROR (element)) { + if (pending != GST_STATE_VOID_PENDING && + GST_STATE_RETURN (element) != GST_STATE_CHANGE_FAILURE) { #ifndef GST_DISABLE_GST_DEBUG GstState old_state = GST_STATE (element); #endif - GST_CAT_INFO_OBJECT (GST_CAT_STATES, element, "aborting state from %s to %s", gst_element_state_get_name (old_state), gst_element_state_get_name (pending)); /* flag error */ - GST_STATE_ERROR (element) = TRUE; + GST_STATE_RETURN (element) = GST_STATE_CHANGE_FAILURE; GST_STATE_BROADCAST (element); } @@ -1759,38 +1770,93 @@ gst_element_abort_state (GstElement * element) * * 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. */ -void +GstStateChangeReturn gst_element_commit_state (GstElement * element) { GstState pending; + GstStateChangeReturn ret; - g_return_if_fail (GST_IS_ELEMENT (element)); + g_return_val_if_fail (GST_IS_ELEMENT (element), GST_STATE_CHANGE_FAILURE); + + ret = GST_STATE_CHANGE_SUCCESS; pending = GST_STATE_PENDING (element); + /* check if there is something to commit */ if (pending != GST_STATE_VOID_PENDING) { - GstState old_state = GST_STATE (element); + GstState old_state; + GstState current, next; GstMessage *message; + GST_FLAG_SET (element, GST_ELEMENT_CHANGING_STATE); + + old_state = GST_STATE (element); + /* this is the state we should go to next */ + next = GST_STATE_NEXT (element); + /* update current state */ + current = GST_STATE (element) = next; + + /* see if we reached the final state */ + if (pending == next) { + pending = GST_STATE_VOID_PENDING; + GST_STATE_PENDING (element) = pending; + GST_STATE_NEXT (element) = pending; + ret = GST_STATE_CHANGE_SUCCESS; + } else { + /* not there yet, will get there ASYNC */ + ret = GST_STATE_CHANGE_ASYNC; + } + + GST_STATE_RETURN (element) = ret; + GST_CAT_INFO_OBJECT (GST_CAT_STATES, element, - "committing state from %s to %s", + "committing state from %s to %s, pending %s", gst_element_state_get_name (old_state), + gst_element_state_get_name (next), gst_element_state_get_name (pending)); - GST_STATE (element) = pending; - GST_STATE_PENDING (element) = GST_STATE_VOID_PENDING; - GST_STATE_ERROR (element) = FALSE; - message = gst_message_new_state_changed (GST_OBJECT (element), - old_state, pending, pending); + old_state, next, pending); gst_element_post_message (element, message); - GST_STATE_BROADCAST (element); + + if (pending != GST_STATE_VOID_PENDING) { + GstStateChange transition; + + /* calc new next state */ + next = GST_STATE_GET_NEXT (current, pending); + + GST_CAT_INFO_OBJECT (GST_CAT_STATES, element, + "continue state change %s to %s, final %s", + gst_element_state_get_name (current), + gst_element_state_get_name (next), + gst_element_state_get_name (pending)); + + /* create transition */ + transition = GST_STATE_TRANSITION (current, next); + + /* perform next transition */ + ret = gst_element_change_state (element, transition); + } else { + GST_STATE_BROADCAST (element); + } + GST_FLAG_UNSET (element, GST_ELEMENT_CHANGING_STATE); } + return ret; } /** @@ -1817,7 +1883,7 @@ gst_element_lost_state (GstElement * element) g_return_if_fail (GST_IS_ELEMENT (element)); if (GST_STATE_PENDING (element) == GST_STATE_VOID_PENDING && - !GST_STATE_ERROR (element)) { + GST_STATE_RETURN (element) != GST_STATE_CHANGE_FAILURE) { GstState current_state; GstMessage *message; @@ -1826,8 +1892,9 @@ gst_element_lost_state (GstElement * element) GST_CAT_INFO_OBJECT (GST_CAT_STATES, element, "lost state of %s", gst_element_state_get_name (current_state)); + GST_STATE_NEXT (element) = current_state; GST_STATE_PENDING (element) = current_state; - GST_STATE_ERROR (element) = FALSE; + GST_STATE_RETURN (element) = GST_STATE_CHANGE_ASYNC; message = gst_message_new_state_changed (GST_OBJECT (element), current_state, current_state, current_state); @@ -1844,6 +1911,11 @@ gst_element_lost_state (GstElement * element) * requested state by going through all the intermediary states and calling * the class's state change function for each. * + * This function can return GST_STATE_CHANGE_ASYNC, in which case the + * element will perform the remainder of the state change asynchronously. + * An application can use gst_element_get_state() to wait for the completion + * of the state change or it can wait for a state change message on the bus. + * * Returns: Result of the state change using #GstStateChangeReturn. * * MT safe. @@ -1851,40 +1923,70 @@ gst_element_lost_state (GstElement * element) GstStateChangeReturn gst_element_set_state (GstElement * element, GstState state) { - GstState current, old_pending; + GstState current, next, old_pending; GstStateChangeReturn ret; GstStateChange transition; - GTimeVal tv; + GstStateChangeReturn old_ret; g_return_val_if_fail (GST_IS_ELEMENT (element), GST_STATE_CHANGE_FAILURE); GST_CAT_DEBUG_OBJECT (GST_CAT_STATES, element, "set_state to %s", gst_element_state_get_name (state)); - /* get current element state, need to call the method so that - * we call the virtual method and subclasses can implement their - * own algorithms */ - GST_TIME_TO_TIMEVAL (0, tv); - ret = gst_element_get_state (element, ¤t, &old_pending, &tv); - GST_STATE_LOCK (element); + GST_FLAG_SET (element, GST_ELEMENT_CHANGING_STATE); + + old_ret = GST_STATE_RETURN (element); + /* previous state change returned an error, remove all pending + * and next states */ + if (old_ret == GST_STATE_CHANGE_FAILURE) { + GST_STATE_NEXT (element) = GST_STATE_VOID_PENDING; + GST_STATE_PENDING (element) = GST_STATE_VOID_PENDING; + GST_STATE_RETURN (element) = GST_STATE_CHANGE_SUCCESS; + } + + current = GST_STATE (element); + next = GST_STATE_NEXT (element); + old_pending = GST_STATE_PENDING (element); /* this is the (new) state we should go to */ - GST_STATE_FINAL (element) = state; - if (ret == GST_STATE_CHANGE_ASYNC) { - /* force next state keeping ASYNC, this is atomic as we hold - * the STATE_LOCK */ - gst_element_commit_state (element); - gst_element_lost_state (element); - if (state == GST_STATE_PENDING (element)) + GST_STATE_PENDING (element) = state; + + GST_CAT_DEBUG_OBJECT (GST_CAT_STATES, element, + "current %s, old_pending %s, next %s, old return %d", + gst_element_state_get_name (current), + gst_element_state_get_name (old_pending), + gst_element_state_get_name (next), old_ret); + + /* if the element was busy doing a state change, we just update the + * target state, it'll get to it async then. */ + if (old_pending != GST_STATE_VOID_PENDING) { + /* upwards state change will happen ASYNC */ + if (old_pending <= state) + goto was_busy; + /* element is going to this state already */ + else if (next == state) goto was_busy; + /* element was performing an ASYNC upward state change and + * we request to go downward again. Start from the next pending + * state then. */ + else if (next > state + && GST_STATE_RETURN (element) == GST_STATE_CHANGE_ASYNC) { + current = next; + } } + next = GST_STATE_GET_NEXT (current, state); - /* fixme, not right */ - transition = GST_STATE_CHANGE (element); + GST_CAT_DEBUG_OBJECT (GST_CAT_STATES, element, + "%s: setting state from %s to %s", + (next != state ? "intermediate" : "final"), + gst_element_state_get_name (current), gst_element_state_get_name (next)); + + transition = GST_STATE_TRANSITION (current, next); ret = gst_element_change_state (element, transition); + GST_FLAG_UNSET (element, GST_ELEMENT_CHANGING_STATE); GST_STATE_UNLOCK (element); GST_DEBUG_OBJECT (element, "returned %d", ret); @@ -1893,12 +1995,15 @@ gst_element_set_state (GstElement * element, GstState state) was_busy: { + GST_STATE_RETURN (element) = GST_STATE_CHANGE_ASYNC; + GST_FLAG_UNSET (element, GST_ELEMENT_CHANGING_STATE); GST_STATE_UNLOCK (element); GST_DEBUG_OBJECT (element, "element was busy with async state change"); return GST_STATE_CHANGE_ASYNC; } + } /* with STATE_LOCK */ @@ -1907,90 +2012,72 @@ gst_element_change_state (GstElement * element, GstStateChange transition) { GstElementClass *oclass; GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS; - GstState current, next, final; + GstState current; + GstState next; oclass = GST_ELEMENT_GET_CLASS (element); /* start with the current state. */ - current = GST_STATE (element); - next = GST_STATE_PENDING (element); - final = GST_STATE_FINAL (element); - - /* We always perform at least one state change, even if the - * current state is equal to the required state. This is needed - * for bins that sync their children. */ - do { - GstState pending; - - /* calculate the pending state */ - if (current < final) - pending = current + 1; - else if (current > final) - pending = current - 1; - else - pending = current; + current = GST_STATE_TRANSITION_CURRENT (transition); + next = GST_STATE_TRANSITION_NEXT (transition); - /* set the pending state variable */ - GST_STATE_PENDING (element) = pending; + /* now we store the next state */ + GST_STATE_NEXT (element) = next; - GST_CAT_DEBUG_OBJECT (GST_CAT_STATES, element, - "%s: setting state from %s to %s", - (pending != final ? "intermediate" : "final"), - gst_element_state_get_name (current), - gst_element_state_get_name (pending)); + /* call the state change function so it can set the state */ + if (oclass->change_state) + ret = (oclass->change_state) (element, transition); + else + ret = GST_STATE_CHANGE_FAILURE; - /* call the state change function so it can set the state */ - if (oclass->change_state) - ret = (oclass->change_state) (element, GST_STATE_CHANGE (element)); - else - ret = GST_STATE_CHANGE_FAILURE; + switch (ret) { + case GST_STATE_CHANGE_FAILURE: + GST_CAT_INFO_OBJECT (GST_CAT_STATES, element, + "have FAILURE change_state return"); + /* state change failure */ + gst_element_abort_state (element); + break; + case GST_STATE_CHANGE_ASYNC: + GST_CAT_INFO_OBJECT (GST_CAT_STATES, element, + "element will change state ASYNC"); - /* clear the error and preroll flag, we need to do that after - * calling the virtual change_state function so that it can use the - * old previous value. */ - GST_STATE_ERROR (element) = FALSE; - GST_STATE_NO_PREROLL (element) = FALSE; - - switch (ret) { - case GST_STATE_CHANGE_FAILURE: - GST_CAT_INFO_OBJECT (GST_CAT_STATES, element, - "have failed change_state return"); - /* state change failure exits the loop */ - gst_element_abort_state (element); - goto exit; - case GST_STATE_CHANGE_ASYNC: - GST_CAT_INFO_OBJECT (GST_CAT_STATES, element, - "element will change state async"); - /* an async state change exits the loop, we can only - * go to the next state change when this one completes. */ + /* if we go upwards, we give the app a change to wait for + * completion */ + if (current < next) goto exit; - case GST_STATE_CHANGE_SUCCESS: - GST_CAT_INFO_OBJECT (GST_CAT_STATES, element, - "element changed state successfully"); - /* we can commit the state now and proceed to the next state */ - gst_element_commit_state (element); - GST_CAT_INFO_OBJECT (GST_CAT_STATES, element, "committed state"); - break; - case GST_STATE_CHANGE_NO_PREROLL: - GST_CAT_INFO_OBJECT (GST_CAT_STATES, element, - "element changed state successfully and can't preroll"); - /* we can commit the state now and proceed to the next state */ - gst_element_commit_state (element); - GST_STATE_NO_PREROLL (element) = TRUE; - GST_CAT_INFO_OBJECT (GST_CAT_STATES, element, "committed state"); - break; - default: - ret = GST_STATE_CHANGE_FAILURE; - goto invalid_return; - } - /* get the current state of the element and see if we need to do more - * state changes */ - current = GST_STATE (element); + + /* else we just continue the state change downwards */ + GST_CAT_INFO_OBJECT (GST_CAT_STATES, element, + "forcing commit state %s < %s", + gst_element_state_get_name (current), + gst_element_state_get_name (next)); + + GST_STATE_RETURN (element) = GST_STATE_CHANGE_SUCCESS; + + ret = gst_element_commit_state (element); + break; + case GST_STATE_CHANGE_SUCCESS: + GST_CAT_INFO_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); + break; + case GST_STATE_CHANGE_NO_PREROLL: + GST_CAT_INFO_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; + break; + default: + ret = GST_STATE_CHANGE_FAILURE; + goto invalid_return; } - while (current != final); exit: - GST_STATE_FINAL (element) = GST_STATE_VOID_PENDING; + GST_STATE_RETURN (element) = ret; GST_CAT_INFO_OBJECT (GST_CAT_STATES, element, "exit state change %d", ret); @@ -1999,7 +2086,7 @@ exit: /* ERROR */ invalid_return: { - GST_STATE_FINAL (element) = GST_STATE_VOID_PENDING; + GST_STATE_RETURN (element) = ret; /* 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; @@ -2087,8 +2174,8 @@ gst_element_change_state_func (GstElement * element, GstStateChange transition) g_return_val_if_fail (GST_IS_ELEMENT (element), GST_STATE_CHANGE_FAILURE); - state = GST_STATE (element); - next = GST_STATE_PENDING (element); + state = GST_STATE_TRANSITION_CURRENT (transition); + next = GST_STATE_TRANSITION_NEXT (transition); /* if the element already is in the given state, we just return success */ if (next == GST_STATE_VOID_PENDING || state == next) @@ -2139,10 +2226,7 @@ was_ok: GST_CAT_DEBUG_OBJECT (GST_CAT_STATES, element, "element is already in the %s state", gst_element_state_get_name (state)); - if (GST_STATE_NO_PREROLL (element)) - return GST_STATE_CHANGE_NO_PREROLL; - else - return GST_STATE_CHANGE_SUCCESS; + return GST_STATE_RETURN (element); } } diff --git a/gst/gstelement.h b/gst/gstelement.h index d834d39..4e6f470 100644 --- a/gst/gstelement.h +++ b/gst/gstelement.h @@ -94,34 +94,35 @@ typedef enum { #define GST_STATE(obj) (GST_ELEMENT(obj)->current_state) /** + * GST_STATE_NEXT: + * @obj: Element to return the next state for. + * + * This macro returns the next state of the element. + */ +#define GST_STATE_NEXT(obj) (GST_ELEMENT(obj)->next_state) + +/** * GST_STATE_PENDING: * @obj: Element to return the pending state for. * * This macro returns the currently pending state of the element. */ #define GST_STATE_PENDING(obj) (GST_ELEMENT(obj)->pending_state) -#define GST_STATE_FINAL(obj) (GST_ELEMENT(obj)->final_state) -#define GST_STATE_ERROR(obj) (GST_ELEMENT(obj)->state_error) -#define GST_STATE_NO_PREROLL(obj) (GST_ELEMENT(obj)->no_preroll) - -#ifndef GST_DEBUG_STATE_CHANGE -#define GST_STATE_CHANGE(obj) ((1<<(GST_STATE(obj)+8)) | 1<state < GST_STATE_NULL || e->state > GST_STATE_PLAYING) - g_assert_not_reached (); - if (e->pending_state < GST_STATE_NULL || e->pending_state > GST_STATE_PLAYING) - g_assert_not_reached (); - if (e->state - e->pending_state != 1 && e->pending_state - e->state != 1) - g_assert_not_reached (); - return (1<<(GST_STATE(obj)+8)) | 1<last_return) + +#define GST_SIGN(val) ((val) < 0 ? -1 : ((val) > 0 ? 1 : 0)) +#define GST_STATE_GET_NEXT(cur,pending) ((cur)+GST_SIGN ((gint)(pending)-(gint)(cur))) +#define GST_STATE_TRANSITION(cur,next) (((cur)<<3)|(next)) +#define GST_STATE_TRANSITION_CURRENT(trans) ((trans)>>3) +#define GST_STATE_TRANSITION_NEXT(trans) ((trans)&0x7) + /** * GstStateChange: * @GST_STATE_CHANGE_NULL_TO_READY : state change from NULL to READY @@ -133,12 +134,12 @@ _gst_element_get_state_change (GstElement *e) */ typedef enum /*< flags=0 >*/ { - GST_STATE_CHANGE_NULL_TO_READY = 1<<(GST_STATE_NULL+8) | 1<*/ /* with LOCK */ GstBus *bus; @@ -491,7 +489,7 @@ GstStateChangeReturn gst_element_get_state (GstElement * element, GstStateChangeReturn gst_element_set_state (GstElement *element, GstState state); void gst_element_abort_state (GstElement * element); -void gst_element_commit_state (GstElement * element); +GstStateChangeReturn gst_element_commit_state (GstElement * element); void gst_element_lost_state (GstElement * element); /* factory management */ diff --git a/gst/gstpipeline.c b/gst/gstpipeline.c index a328bb4..2e8fc90 100644 --- a/gst/gstpipeline.c +++ b/gst/gstpipeline.c @@ -60,12 +60,11 @@ enum }; #define DEFAULT_DELAY 0 -#define DEFAULT_PLAY_TIMEOUT (2*GST_SECOND) + enum { PROP_0, PROP_DELAY, - PROP_PLAY_TIMEOUT, /* FILL ME */ }; @@ -141,10 +140,6 @@ gst_pipeline_class_init (gpointer g_class, gpointer class_data) "Expected delay needed for elements " "to spin up to PLAYING in nanoseconds", 0, G_MAXUINT64, DEFAULT_DELAY, G_PARAM_READWRITE)); - g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_PLAY_TIMEOUT, - g_param_spec_uint64 ("play-timeout", "Play Timeout", - "Max timeout for going to PLAYING in nanoseconds", 0, G_MAXUINT64, - DEFAULT_PLAY_TIMEOUT, G_PARAM_READWRITE)); gobject_class->dispose = GST_DEBUG_FUNCPTR (gst_pipeline_dispose); @@ -162,7 +157,6 @@ gst_pipeline_init (GTypeInstance * instance, gpointer g_class) GstBus *bus; pipeline->delay = DEFAULT_DELAY; - pipeline->play_timeout = DEFAULT_PLAY_TIMEOUT; bus = g_object_new (gst_bus_get_type (), NULL); gst_element_set_bus (GST_ELEMENT_CAST (pipeline), bus); @@ -192,9 +186,6 @@ gst_pipeline_set_property (GObject * object, guint prop_id, case PROP_DELAY: pipeline->delay = g_value_get_uint64 (value); break; - case PROP_PLAY_TIMEOUT: - pipeline->play_timeout = g_value_get_uint64 (value); - break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -213,9 +204,6 @@ gst_pipeline_get_property (GObject * object, guint prop_id, case PROP_DELAY: g_value_set_uint64 (value, pipeline->delay); break; - case PROP_PLAY_TIMEOUT: - g_value_set_uint64 (value, pipeline->play_timeout); - break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -246,8 +234,9 @@ do_pipeline_seek (GstElement * element, GstEvent * event) gst_element_get_state (element, &state, NULL, &timeout); was_playing = state == GST_STATE_PLAYING; - if (was_playing) + if (was_playing) { gst_element_set_state (element, GST_STATE_PAUSED); + } } res = GST_ELEMENT_CLASS (parent_class)->send_event (element, event); @@ -255,10 +244,9 @@ do_pipeline_seek (GstElement * element, GstEvent * event) if (flush && res) { /* need to reset the stream time to 0 after a flushing seek */ gst_pipeline_set_new_stream_time (GST_PIPELINE (element), 0); - if (was_playing) { + if (was_playing) /* and continue playing */ gst_element_set_state (element, GST_STATE_PLAYING); - } } return res; } @@ -306,7 +294,6 @@ gst_pipeline_change_state (GstElement * element, GstStateChange transition) { GstStateChangeReturn result = GST_STATE_CHANGE_SUCCESS; GstPipeline *pipeline = GST_PIPELINE (element); - GstClockTime play_timeout; GstClock *clock; switch (transition) { @@ -400,40 +387,6 @@ gst_pipeline_change_state (GstElement * element, GstStateChange transition) GST_UNLOCK (element); break; } - - if (result == GST_STATE_CHANGE_ASYNC) { - GST_LOCK (pipeline); - play_timeout = pipeline->play_timeout; - GST_UNLOCK (pipeline); - } else { - play_timeout = 0; - } - - /* we wait for async state changes ourselves when we are in an - * intermediate state. */ - if (play_timeout > 0) { - GTimeVal *timeval, timeout; - - GST_STATE_UNLOCK (pipeline); - - if (play_timeout == G_MAXUINT64) { - timeval = NULL; - } else { - GST_TIME_TO_TIMEVAL (play_timeout, timeout); - timeval = &timeout; - } - - result = gst_element_get_state (element, NULL, NULL, timeval); - if (result == GST_STATE_CHANGE_ASYNC) { - GST_WARNING_OBJECT (pipeline, - "timeout in PREROLL, forcing next state change"); - g_warning ("timeout in PREROLL, forcing next state change"); - result = GST_STATE_CHANGE_SUCCESS; - } - - GST_STATE_LOCK (pipeline); - } - return result; } diff --git a/gst/gstpipeline.h b/gst/gstpipeline.h index da62369..564de35 100644 --- a/gst/gstpipeline.h +++ b/gst/gstpipeline.h @@ -59,7 +59,6 @@ struct _GstPipeline { GstClock *fixed_clock; /* fixed clock if any */ GstClockTime stream_time; GstClockTime delay; - GstClockTime play_timeout; /*< private >*/ gpointer _gst_reserved[GST_PADDING]; diff --git a/gst/gstutils.h b/gst/gstutils.h index 3bd1acb..eb0b724 100644 --- a/gst/gstutils.h +++ b/gst/gstutils.h @@ -497,8 +497,6 @@ void gst_element_unlink_pads (GstElement *src, const gboolean gst_element_link_pads_filtered (GstElement * src, const gchar * srcpadname, GstElement * dest, const gchar * destpadname, GstCaps *filter); -GstStateChangeReturn gst_element_set_state_async (GstElement * element, GstState state); - /* util elementfactory functions */ gboolean gst_element_factory_can_src_caps(GstElementFactory *factory, const GstCaps *caps); gboolean gst_element_factory_can_sink_caps(GstElementFactory *factory, const GstCaps *caps); @@ -536,7 +534,6 @@ gboolean gst_pad_query_convert (GstPad *pad, GstFormat /* bin functions */ void gst_bin_add_many (GstBin *bin, GstElement *element_1, ...); void gst_bin_remove_many (GstBin *bin, GstElement *element_1, ...); -void gst_bin_watch_for_state_change (GstBin *bin); /* buffer functions */ GstBuffer * gst_buffer_merge (GstBuffer * buf1, GstBuffer * buf2); diff --git a/libs/gst/base/gstbasesink.c b/libs/gst/base/gstbasesink.c index 4d57ec2..f66b83e 100644 --- a/libs/gst/base/gstbasesink.c +++ b/libs/gst/base/gstbasesink.c @@ -622,6 +622,7 @@ gst_base_sink_handle_object (GstBaseSink * basesink, GstPad * pad, if (length == 1) { gint t; + GstTask *task; basesink->have_preroll = TRUE; /* we are prerolling */ @@ -636,8 +637,21 @@ gst_base_sink_handle_object (GstBaseSink * basesink, GstPad * pad, g_warning ("STREAM_LOCK should have been locked !!"); } - /* now we commit our state */ - GST_STATE_LOCK (basesink); + /* now we commit our state, this will also automatically proceed to + * the next pending state. */ + /* FIXME */ + if ((task = GST_PAD_TASK (pad))) { + while (!GST_STATE_TRYLOCK (basesink)) { + GST_DEBUG_OBJECT (basesink, + "state change happening, checking shutdown"); + GST_LOCK (pad); + if (G_UNLIKELY (GST_PAD_IS_FLUSHING (pad))) + goto task_stopped; + GST_UNLOCK (pad); + } + } else { + GST_STATE_LOCK (basesink); + } GST_DEBUG_OBJECT (basesink, "commit state"); gst_element_commit_state (GST_ELEMENT (basesink)); GST_STATE_UNLOCK (basesink); @@ -742,6 +756,12 @@ playing_async: return ret; } +task_stopped: + { + GST_UNLOCK (pad); + GST_DEBUG_OBJECT (basesink, "task is stopped"); + return GST_FLOW_WRONG_STATE; + } flushing: { GST_UNLOCK (pad); @@ -1514,7 +1534,8 @@ gst_base_sink_change_state (GstElement * element, GstStateChange transition) * we need to wait for a preroll */ GST_DEBUG_OBJECT (basesink, "have_preroll: %d, EOS: %d", basesink->have_preroll, basesink->eos); - if (!basesink->have_preroll && !basesink->eos) { + if (!basesink->have_preroll && !basesink->eos + && GST_STATE_PENDING (basesink) == GST_STATE_PAUSED) { GST_DEBUG_OBJECT (basesink, "PLAYING to PAUSED, need preroll to TRUE"); basesink->need_preroll = TRUE; ret = GST_STATE_CHANGE_ASYNC; diff --git a/tests/check/Makefile.am b/tests/check/Makefile.am index 53cedc7..242bb27 100644 --- a/tests/check/Makefile.am +++ b/tests/check/Makefile.am @@ -54,6 +54,7 @@ check_PROGRAMS = \ pipelines/simple_launch_lines \ pipelines/cleanup \ states/sinks \ + states/sinks2 \ gst-libs/controller \ gst-libs/gdp diff --git a/tests/check/generic/sinks.c b/tests/check/generic/sinks.c index af4dffe..e63abfc 100644 --- a/tests/check/generic/sinks.c +++ b/tests/check/generic/sinks.c @@ -42,7 +42,7 @@ GST_START_TEST (test_sink) ret = gst_element_get_state (sink, ¤t, &pending, &tv); fail_unless (ret == GST_STATE_CHANGE_ASYNC, "not changing state async"); - fail_unless (current == GST_STATE_PAUSED, "bad current state"); + fail_unless (current == GST_STATE_READY, "bad current state"); fail_unless (pending == GST_STATE_PLAYING, "bad pending state"); ret = gst_element_set_state (sink, GST_STATE_PAUSED); @@ -81,6 +81,7 @@ GST_START_TEST (test_src_sink) gst_object_unref (sinkpad); ret = gst_element_set_state (pipeline, GST_STATE_PAUSED); + fail_unless (ret == GST_STATE_CHANGE_ASYNC, "no async state return"); ret = gst_element_get_state (pipeline, NULL, NULL, NULL); fail_unless (ret == GST_STATE_CHANGE_SUCCESS, "no success state return"); @@ -139,8 +140,7 @@ GST_START_TEST (test_livesrc_remove) ret = gst_element_get_state (pipeline, ¤t, &pending, &tv); fail_unless (ret == GST_STATE_CHANGE_ASYNC, "not async"); fail_unless (current == GST_STATE_PAUSED, "not paused"); - fail_unless (pending == GST_STATE_VOID_PENDING, "not playing"); - + fail_unless (pending == GST_STATE_PAUSED, "not paused"); } GST_END_TEST diff --git a/tests/check/generic/states.c b/tests/check/generic/states.c index 002d2fb..ac1faea 100644 --- a/tests/check/generic/states.c +++ b/tests/check/generic/states.c @@ -42,8 +42,7 @@ GST_START_TEST (test_state_changes) gst_element_set_state (element, GST_STATE_READY); gst_element_set_state (element, GST_STATE_PAUSED); gst_element_set_state (element, GST_STATE_PLAYING); - /* Sleep to give any pad tasks time to start */ - g_usleep (0.2 * G_USEC_PER_SEC); + gst_element_set_state (element, GST_STATE_PAUSED); gst_element_set_state (element, GST_STATE_READY); gst_element_set_state (element, GST_STATE_NULL); @@ -52,6 +51,7 @@ GST_START_TEST (test_state_changes) gst_element_set_state (element, GST_STATE_PLAYING); gst_element_set_state (element, GST_STATE_PAUSED); gst_element_set_state (element, GST_STATE_NULL); + gst_object_unref (GST_OBJECT (element)); } gst_task_cleanup_all (); diff --git a/tests/check/gst/gstbin.c b/tests/check/gst/gstbin.c index 713d61f..e84608e 100644 --- a/tests/check/gst/gstbin.c +++ b/tests/check/gst/gstbin.c @@ -262,7 +262,7 @@ GST_START_TEST (test_message_state_changed_children) /* change state to PAUSED, spawning three messages */ GST_DEBUG ("setting pipeline to PAUSED"); ret = gst_element_set_state (GST_ELEMENT (pipeline), GST_STATE_PAUSED); - fail_unless (ret == GST_STATE_CHANGE_SUCCESS); + fail_unless (ret == GST_STATE_CHANGE_ASYNC); ret = gst_element_get_state (GST_ELEMENT (pipeline), ¤t, &pending, NULL); fail_unless (ret == GST_STATE_CHANGE_SUCCESS); @@ -364,19 +364,13 @@ GST_START_TEST (test_watch_for_state_change) fail_unless (gst_element_link (src, sink), "could not link src and sink"); - /* change state, spawning two times three messages, minus one async */ + /* change state, spawning two times three messages */ ret = gst_element_set_state (GST_ELEMENT (bin), GST_STATE_PAUSED); fail_unless (ret == GST_STATE_CHANGE_ASYNC); + ret = gst_element_get_state (GST_ELEMENT (bin), NULL, NULL, NULL); + fail_unless (ret == GST_STATE_CHANGE_SUCCESS); - pop_messages (bus, 5); - - fail_unless (gst_bus_have_pending (bus) == FALSE, - "Unexpected messages on bus"); - - gst_bin_watch_for_state_change (GST_BIN (bin)); - - /* should get the bin's state change message now */ - pop_messages (bus, 1); + pop_messages (bus, 6); fail_unless (gst_bus_have_pending (bus) == FALSE, "Unexpected messages on bus"); @@ -388,8 +382,7 @@ GST_START_TEST (test_watch_for_state_change) /* this one might return either SUCCESS or ASYNC, likely SUCCESS */ gst_element_set_state (GST_ELEMENT (bin), GST_STATE_PAUSED); - - gst_bin_watch_for_state_change (GST_BIN (bin)); + gst_element_get_state (GST_ELEMENT (bin), NULL, NULL, NULL); pop_messages (bus, 3); @@ -482,6 +475,7 @@ GST_START_TEST (test_children_state_change_order_flagged_sink) { GstElement *src, *identity, *sink, *pipeline; GstStateChangeReturn ret; + GstState current, pending; GstBus *bus; pipeline = gst_pipeline_new (NULL); @@ -506,7 +500,12 @@ GST_START_TEST (test_children_state_change_order_flagged_sink) /* (1) Test state change with fakesink being a regular sink */ ret = gst_element_set_state (pipeline, GST_STATE_PLAYING); + fail_if (ret != GST_STATE_CHANGE_ASYNC, + "State change to PLAYING did not return ASYNC"); + ret = gst_element_get_state (pipeline, ¤t, &pending, NULL); fail_if (ret != GST_STATE_CHANGE_SUCCESS, "State change to PLAYING failed"); + fail_if (current != GST_STATE_PLAYING, "State change to PLAYING failed"); + fail_if (pending != GST_STATE_VOID_PENDING, "State change to PLAYING failed"); /* NULL => READY */ ASSERT_STATE_CHANGE_MSG (bus, sink, GST_STATE_NULL, GST_STATE_READY, 101); @@ -602,7 +601,7 @@ GST_START_TEST (test_children_state_change_order_semi_sink) GST_FLAG_UNSET (sink, GST_ELEMENT_IS_SINK); /* <======== */ ret = gst_element_set_state (pipeline, GST_STATE_PLAYING); - fail_if (ret != GST_STATE_CHANGE_SUCCESS, "State change to PLAYING failed"); + fail_if (ret != GST_STATE_CHANGE_ASYNC, "State change to PLAYING not ASYNC"); ret = gst_element_get_state (pipeline, ¤t, &pending, NULL); fail_if (ret != GST_STATE_CHANGE_SUCCESS, "State change to PLAYING failed"); fail_if (current != GST_STATE_PLAYING, "State change to PLAYING failed"); diff --git a/tests/check/gst/gstpipeline.c b/tests/check/gst/gstpipeline.c index e516789..127a940 100644 --- a/tests/check/gst/gstpipeline.c +++ b/tests/check/gst/gstpipeline.c @@ -28,7 +28,6 @@ GST_START_TEST (test_async_state_change_empty) pipeline = GST_PIPELINE (gst_pipeline_new (NULL)); fail_unless (pipeline != NULL, "Could not create pipeline"); - g_object_set (G_OBJECT (pipeline), "play-timeout", 0LL, NULL); fail_unless_equals_int (gst_element_set_state (GST_ELEMENT (pipeline), GST_STATE_PLAYING), GST_STATE_CHANGE_SUCCESS); @@ -45,7 +44,6 @@ GST_START_TEST (test_async_state_change_fake_ready) pipeline = GST_PIPELINE (gst_pipeline_new (NULL)); fail_unless (pipeline != NULL, "Could not create pipeline"); - g_object_set (G_OBJECT (pipeline), "play-timeout", 0LL, NULL); src = gst_element_factory_make ("fakesrc", NULL); sink = gst_element_factory_make ("fakesink", NULL); @@ -71,7 +69,6 @@ GST_START_TEST (test_async_state_change_fake) pipeline = GST_PIPELINE (gst_pipeline_new (NULL)); fail_unless (pipeline != NULL, "Could not create pipeline"); - g_object_set (G_OBJECT (pipeline), "play-timeout", 0LL, NULL); src = gst_element_factory_make ("fakesrc", NULL); sink = gst_element_factory_make ("fakesink", NULL); @@ -81,7 +78,7 @@ GST_START_TEST (test_async_state_change_fake) bus = gst_pipeline_get_bus (pipeline); - fail_unless_equals_int (gst_element_set_state_async (GST_ELEMENT (pipeline), + fail_unless_equals_int (gst_element_set_state (GST_ELEMENT (pipeline), GST_STATE_PLAYING), GST_STATE_CHANGE_ASYNC); while (!done) { @@ -98,7 +95,6 @@ GST_START_TEST (test_async_state_change_fake) } } - g_object_set (G_OBJECT (pipeline), "play-timeout", 3 * GST_SECOND, NULL); fail_unless_equals_int (gst_element_set_state (GST_ELEMENT (pipeline), GST_STATE_NULL), GST_STATE_CHANGE_SUCCESS); @@ -175,7 +171,6 @@ GST_START_TEST (test_bus) pipeline = gst_pipeline_new (NULL); fail_unless (pipeline != NULL, "Could not create pipeline"); ASSERT_OBJECT_REFCOUNT (pipeline, "pipeline", 1); - g_object_set (pipeline, "play-timeout", 0LL, NULL); src = gst_element_factory_make ("fakesrc", NULL); fail_unless (src != NULL); @@ -193,7 +188,7 @@ GST_START_TEST (test_bus) ASSERT_OBJECT_REFCOUNT (pipeline, "pipeline after add_watch", 1); ASSERT_OBJECT_REFCOUNT (bus, "bus after add_watch", 3); - gst_element_set_state_async (pipeline, GST_STATE_PLAYING); + gst_element_set_state (pipeline, GST_STATE_PLAYING); loop = g_main_loop_new (NULL, FALSE); GST_DEBUG ("going into main loop"); g_main_loop_run (loop); @@ -207,9 +202,6 @@ GST_START_TEST (test_bus) /* cleanup */ GST_DEBUG ("cleanup"); - /* current semantics require us to go step by step; this will change */ - gst_element_set_state (pipeline, GST_STATE_PAUSED); - gst_element_set_state (pipeline, GST_STATE_READY); gst_element_set_state (pipeline, GST_STATE_NULL); fail_unless (gst_element_get_state (pipeline, ¤t, NULL, NULL) == GST_STATE_CHANGE_SUCCESS); @@ -238,6 +230,8 @@ gst_pipeline_suite (void) Suite *s = suite_create ("GstPipeline"); TCase *tc_chain = tcase_create ("pipeline tests"); + tcase_set_timeout (tc_chain, 0); + suite_add_tcase (s, tc_chain); tcase_add_test (tc_chain, test_async_state_change_empty); tcase_add_test (tc_chain, test_async_state_change_fake_ready); diff --git a/tools/gst-launch.c b/tools/gst-launch.c index 75e3013..a06ba07 100644 --- a/tools/gst-launch.c +++ b/tools/gst-launch.c @@ -623,9 +623,12 @@ main (int argc, char *argv[]) gst_bin_add (GST_BIN (real_pipeline), pipeline); pipeline = real_pipeline; } - +#if 0 fprintf (stderr, _("PAUSE pipeline ...\n")); ret = gst_element_set_state (pipeline, GST_STATE_PAUSED); +#endif + fprintf (stderr, _("PLAYING pipeline ...\n")); + ret = gst_element_set_state (pipeline, GST_STATE_PLAYING); switch (ret) { case GST_STATE_CHANGE_FAILURE: -- 2.7.4