CVS Root: /cvs/gstreamer Module: gstreamer Changes by: wtay Date: Mon Oct 10 2005 09:38:38 PDT Log message: * 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. Modified files: . : ChangeLog check : Makefile.am check/generic : states.c check/gst : gstbin.c gstpipeline.c check/states : sinks.c gst : gstbin.c gstbin.h gstelement.c gstelement.h gstpipeline.c gstpipeline.h gstutils.h gst/base : gstbasesink.c tools : gst-launch.c Added files: check/states : sinks2.c Links: http://freedesktop.org/cgi-bin/viewcvs.cgi/gstreamer/gstreamer/ChangeLog.diff?r1=1.1684&r2=1.1685 http://freedesktop.org/cgi-bin/viewcvs.cgi/gstreamer/gstreamer/check/Makefile.am.diff?r1=1.63&r2=1.64 http://freedesktop.org/cgi-bin/viewcvs.cgi/gstreamer/gstreamer/check/generic/states.c.diff?r1=1.5&r2=1.6 http://freedesktop.org/cgi-bin/viewcvs.cgi/gstreamer/gstreamer/check/gst/gstbin.c.diff?r1=1.36&r2=1.37 http://freedesktop.org/cgi-bin/viewcvs.cgi/gstreamer/gstreamer/check/gst/gstpipeline.c.diff?r1=1.8&r2=1.9 http://freedesktop.org/cgi-bin/viewcvs.cgi/gstreamer/gstreamer/check/states/sinks.c.diff?r1=1.10&r2=1.11 http://freedesktop.org/cgi-bin/viewcvs.cgi/gstreamer/gstreamer/check/states/sinks2.c?rev=1.1&content-type=text/vnd.viewcvs-markup http://freedesktop.org/cgi-bin/viewcvs.cgi/gstreamer/gstreamer/gst/gstbin.c.diff?r1=1.281&r2=1.282 http://freedesktop.org/cgi-bin/viewcvs.cgi/gstreamer/gstreamer/gst/gstbin.h.diff?r1=1.79&r2=1.80 http://freedesktop.org/cgi-bin/viewcvs.cgi/gstreamer/gstreamer/gst/gstelement.c.diff?r1=1.382&r2=1.383 http://freedesktop.org/cgi-bin/viewcvs.cgi/gstreamer/gstreamer/gst/gstelement.h.diff?r1=1.215&r2=1.216 http://freedesktop.org/cgi-bin/viewcvs.cgi/gstreamer/gstreamer/gst/gstpipeline.c.diff?r1=1.115&r2=1.116 http://freedesktop.org/cgi-bin/viewcvs.cgi/gstreamer/gstreamer/gst/gstpipeline.h.diff?r1=1.30&r2=1.31 http://freedesktop.org/cgi-bin/viewcvs.cgi/gstreamer/gstreamer/gst/gstutils.h.diff?r1=1.54&r2=1.55 http://freedesktop.org/cgi-bin/viewcvs.cgi/gstreamer/gstreamer/gst/base/gstbasesink.c.diff?r1=1.63&r2=1.64 http://freedesktop.org/cgi-bin/viewcvs.cgi/gstreamer/gstreamer/tools/gst-launch.c.diff?r1=1.91&r2=1.92 ====Begin Diffs==== Index: states.c =================================================================== RCS file: /cvs/gstreamer/gstreamer/check/generic/states.c,v retrieving revision 1.5 retrieving revision 1.6 diff -u -d -r1.5 -r1.6 --- states.c 20 Sep 2005 00:27:37 -0000 1.5 +++ states.c 10 Oct 2005 16:38:23 -0000 1.6 @@ -42,8 +42,7 @@ 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_NULL); @@ -52,6 +51,7 @@ gst_object_unref (GST_OBJECT (element)); } gst_task_cleanup_all (); Index: Makefile.am RCS file: /cvs/gstreamer/gstreamer/check/Makefile.am,v retrieving revision 1.63 retrieving revision 1.64 diff -u -d -r1.63 -r1.64 --- Makefile.am 22 Sep 2005 12:05:04 -0000 1.63 +++ Makefile.am 10 Oct 2005 16:38:23 -0000 1.64 @@ -54,6 +54,7 @@ pipelines/simple_launch_lines \ pipelines/cleanup \ states/sinks \ + states/sinks2 \ gst-libs/controller \ gst-libs/gdp Index: gstbin.c RCS file: /cvs/gstreamer/gstreamer/check/gst/gstbin.c,v retrieving revision 1.36 retrieving revision 1.37 diff -u -d -r1.36 -r1.37 --- gstbin.c 10 Oct 2005 10:50:12 -0000 1.36 +++ gstbin.c 10 Oct 2005 16:38:23 -0000 1.37 @@ -262,7 +262,7 @@ /* 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 @@ 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 @@ /* this one might return either SUCCESS or ASYNC, likely SUCCESS */ gst_element_set_state (GST_ELEMENT (bin), GST_STATE_PAUSED); + gst_element_get_state (GST_ELEMENT (bin), NULL, NULL, NULL); pop_messages (bus, 3); @@ -482,6 +475,7 @@ { GstElement *src, *identity, *sink, *pipeline; GstStateChangeReturn ret; + GstState current, pending; GstBus *bus; pipeline = gst_pipeline_new (NULL); @@ -506,7 +500,12 @@ /* (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_FLAG_UNSET (sink, GST_ELEMENT_IS_SINK); /* <======== */ - 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 (current != GST_STATE_PLAYING, "State change to PLAYING failed"); Index: gstpipeline.c RCS file: /cvs/gstreamer/gstreamer/check/gst/gstpipeline.c,v retrieving revision 1.8 retrieving revision 1.9 diff -u -d -r1.8 -r1.9 --- gstpipeline.c 8 Oct 2005 08:58:45 -0000 1.8 +++ gstpipeline.c 10 Oct 2005 16:38:24 -0000 1.9 @@ -28,7 +28,6 @@ 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 @@ src = gst_element_factory_make ("fakesrc", NULL); sink = gst_element_factory_make ("fakesink", NULL); @@ -71,7 +69,6 @@ @@ -81,7 +78,7 @@ 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 @@ } - g_object_set (G_OBJECT (pipeline), "play-timeout", 3 * GST_SECOND, NULL); GST_STATE_NULL), GST_STATE_CHANGE_SUCCESS); @@ -175,7 +171,6 @@ ASSERT_OBJECT_REFCOUNT (pipeline, "pipeline", 1); - g_object_set (pipeline, "play-timeout", 0LL, NULL); fail_unless (src != NULL); @@ -193,7 +188,7 @@ 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 @@ /* 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 @@ 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); Index: sinks.c RCS file: /cvs/gstreamer/gstreamer/check/states/sinks.c,v retrieving revision 1.10 retrieving revision 1.11 diff -u -d -r1.10 -r1.11 --- sinks.c 10 Oct 2005 11:04:55 -0000 1.10 +++ sinks.c 10 Oct 2005 16:38:24 -0000 1.11 @@ -42,7 +42,7 @@ 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_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 @@ 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 --- NEW FILE: sinks2.c --- /* GStreamer * * unit test for sinks * Copyright (C) <2005> Wim Taymans <wim at fluendo dot com> * 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 <gst/check/gstcheck.h> /* 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); 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; Index: ChangeLog RCS file: /cvs/gstreamer/gstreamer/ChangeLog,v retrieving revision 1.1684 retrieving revision 1.1685 diff -u -d -r1.1684 -r1.1685 --- ChangeLog 10 Oct 2005 16:20:37 -0000 1.1684 +++ ChangeLog 10 Oct 2005 16:38:23 -0000 1.1685 @@ -1,3 +1,45 @@ +2005-10-10 Wim Taymans <wi...@fl...> + * 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 <wi...@po...> * tests/Makefile.am (noinst_PROGRAMS): No more init.c. RCS file: /cvs/gstreamer/gstreamer/gst/gstbin.c,v retrieving revision 1.281 retrieving revision 1.282 diff -u -d -r1.281 -r1.282 --- gstbin.c 10 Oct 2005 15:27:12 -0000 1.281 +++ gstbin.c 10 Oct 2005 16:38:24 -0000 1.282 @@ -76,7 +76,6 @@ (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 @@ 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 @@ 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_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_FLAG_UNSET (bin, GST_ELEMENT_IS_SINK); GST_CAT_INFO_OBJECT (GST_CAT_PARENTAGE, bin, "removed child \"%s\"", @@ -872,14 +875,7 @@ 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 @@ 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) +{ GList *children; guint32 children_cookie; @@ -894,12 +902,21 @@ 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 @@ 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 @@ /* 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; - } - /* 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: - 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: /* now we can take the state lock, it is possible that new elements @@ -1019,33 +992,45 @@ 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)); 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"); +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); /*********************************************** @@ -1340,16 +1325,13 @@ 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 @@ + ret = parent_class->change_state (element, transition); + if (ret == GST_STATE_CHANGE_FAILURE) + goto done; } else if (have_async) { ret = GST_STATE_CHANGE_ASYNC; - } else { - ret = parent_class->change_state (element, transition); @@ -1563,6 +1547,60 @@ gst_message_unref (message); + 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_DEBUG_OBJECT (bin, "got SYNC message"); + } + not_our_child: + { + GST_UNLOCK (bin); + GST_DEBUG_OBJECT (bin, "not our child"); + gst_object_unref (src); + break; + } /* Send all other messages upward */ GST_DEBUG_OBJECT (bin, "posting message upward"); Index: gstbin.h RCS file: /cvs/gstreamer/gstreamer/gst/gstbin.h,v retrieving revision 1.79 retrieving revision 1.80 diff -u -d -r1.79 -r1.80 --- gstbin.h 27 Sep 2005 16:16:39 -0000 1.79 +++ gstbin.h 10 Oct 2005 16:38:24 -0000 1.80 @@ -92,6 +92,8 @@ 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]; }; Index: gstelement.c RCS file: /cvs/gstreamer/gstreamer/gst/gstelement.c,v retrieving revision 1.382 retrieving revision 1.383 diff -u -d -r1.382 -r1.383 --- gstelement.c 10 Oct 2005 15:55:37 -0000 1.382 +++ gstelement.c 10 Oct 2005 16:38:24 -0000 1.383 @@ -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 <glib.h> #include <stdarg.h> @@ -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_func (GstElement * element, +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 @@ 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_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; 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) + /* no need to wait async if we are not async */ + if (ret != GST_STATE_CHANGE_ASYNC) old_pending = GST_STATE_PENDING (element); if (old_pending != GST_STATE_VOID_PENDING) { @@ -1627,6 +1633,9 @@ 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 @@ *pending = GST_STATE_PENDING (element); GST_CAT_INFO_OBJECT (GST_CAT_STATES, element, + "state current: %s, pending: %s, result: %d", gst_element_state_get_name (GST_STATE (element)), + gst_element_state_get_name (GST_STATE_PENDING (element)), ret); GST_STATE_UNLOCK (element); @@ -1692,6 +1700,9 @@ * 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 @@ 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 @@ * 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; + /* 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; - "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_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), + /* create transition */ + transition = GST_STATE_TRANSITION (current, next); + /* perform next transition */ + ret = gst_element_change_state (element, transition); + GST_STATE_BROADCAST (element); + GST_FLAG_UNSET (element, GST_ELEMENT_CHANGING_STATE); + return ret; /** @@ -1817,7 +1883,7 @@ g_return_if_fail (GST_IS_ELEMENT (element)); if (GST_STATE_PENDING (element) == GST_STATE_VOID_PENDING && - !GST_STATE_ERROR (element)) { GstState current_state; @@ -1826,8 +1892,9 @@ "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_RETURN (element) = GST_STATE_CHANGE_ASYNC; current_state, current_state, current_state); @@ -1844,6 +1911,11 @@ * 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. @@ -1851,40 +1923,70 @@ GstStateChangeReturn gst_element_set_state (GstElement * element, GstState state) - GstState current, old_pending; + GstState current, next, old_pending; 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_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); + "%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_DEBUG_OBJECT (element, "returned %d", ret); @@ -1893,12 +1995,15 @@ was_busy: { 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 @@ 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. */ - 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; + /* now we store the next state */ + GST_STATE_NEXT (element) = next; - /* calculate the pending state */ - if (current < final) - pending = current + 1; - else if (current > final) - pending = current - 1; - else - pending = current; + /* 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; - /* set the pending state variable */ - GST_STATE_PENDING (element) = pending; + switch (ret) { + case GST_STATE_CHANGE_FAILURE: + "have FAILURE change_state return"); + /* state change failure */ + gst_element_abort_state (element); + "element will change state ASYNC"); - 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)); + /* if we go upwards, we give the app a change to wait for + * completion */ + if (current < next) + goto exit; - /* call the state change function so it can set the state */ - if (oclass->change_state) - ret = (oclass->change_state) (element, GST_STATE_CHANGE (element)); - ret = GST_STATE_CHANGE_FAILURE; + /* else we just continue the state change downwards */ + "forcing commit state %s < %s", + gst_element_state_get_name (next)); - /* 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_NO_PREROLL (element) = FALSE; + GST_STATE_RETURN (element) = GST_STATE_CHANGE_SUCCESS; - 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; - "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. */ - "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"); - "element changed state successfully and can't preroll"); - GST_STATE_NO_PREROLL (element) = TRUE; - 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); + ret = gst_element_commit_state (element); + case GST_STATE_CHANGE_SUCCESS: + "element changed state SUCCESS"); + /* we can commit the state now which will proceeed to + * the next state */ + "element changed state NO_PREROLL"); + gst_element_commit_state (element); + ret = GST_STATE_CHANGE_NO_PREROLL; + 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 @@ /* ERROR */ invalid_return: - GST_STATE_FINAL (element) = GST_STATE_VOID_PENDING; /* 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 @@ - state = GST_STATE (element); + state = GST_STATE_TRANSITION_CURRENT (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 @@ 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; - return GST_STATE_CHANGE_SUCCESS; + return GST_STATE_RETURN (element); Index: gstelement.h RCS file: /cvs/gstreamer/gstreamer/gst/gstelement.h,v retrieving revision 1.215 retrieving revision 1.216 diff -u -d -r1.215 -r1.216 --- gstelement.h 10 Oct 2005 14:33:13 -0000 1.215 +++ gstelement.h 10 Oct 2005 16:38:24 -0000 1.216 @@ -94,34 +94,35 @@ #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<<GST_STATE_PENDING(obj)) -#else -inline GstStateChange -_gst_element_get_state_change (GstElement *e) -{ - if (e->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) - if (e->state - e->pending_state != 1 && e->pending_state - e->state != 1) - return (1<<(GST_STATE(obj)+8)) | 1<<GST_STATE_PENDING(obj); -} -#define GST_STATE_CHANGE(obj) _gst_element_get_state_change(obj) -#endif + * GST_STATE_RETURN: + * @obj: Element to return the last state result for. + * This macro returns the last state change return value. +#define GST_STATE_RETURN(obj) (GST_ELEMENT(obj)->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) -/* FIXME: How to deal with lost_state ? */ * GstStateChange: * @GST_STATE_CHANGE_NULL_TO_READY : state change from NULL to READY @@ -133,12 +134,12 @@ typedef enum /*< flags=0 >*/ - GST_STATE_CHANGE_NULL_TO_READY = 1<<(GST_STATE_NULL+8) | 1<<GST_STATE_READY, - GST_STATE_CHANGE_READY_TO_PAUSED = 1<<(GST_STATE_READY+8) | 1<<GST_STATE_PAUSED, - GST_STATE_CHANGE_PAUSED_TO_PLAYING = 1<<(GST_STATE_PAUSED+8) | 1<<GST_STATE_PLAYING, - GST_STATE_CHANGE_PLAYING_TO_PAUSED = 1<<(GST_STATE_PLAYING+8) | 1<<GST_STATE_PAUSED, - GST_STATE_CHANGE_PAUSED_TO_READY = 1<<(GST_STATE_PAUSED+8) | 1<<GST_STATE_READY, - GST_STATE_CHANGE_READY_TO_NULL = 1<<(GST_STATE_READY+8) | 1<<GST_STATE_NULL + GST_STATE_CHANGE_NULL_TO_READY = (GST_STATE_NULL<<3) | GST_STATE_READY, + GST_STATE_CHANGE_READY_TO_PAUSED = (GST_STATE_READY<<3) | GST_STATE_PAUSED, + GST_STATE_CHANGE_PAUSED_TO_PLAYING = (GST_STATE_PAUSED<<3) | GST_STATE_PLAYING, + GST_STATE_CHANGE_PLAYING_TO_PAUSED = (GST_STATE_PLAYING<<3) | GST_STATE_PAUSED, + GST_STATE_CHANGE_PAUSED_TO_READY = (GST_STATE_PAUSED<<3) | GST_STATE_READY, + GST_STATE_CHANGE_READY_TO_NULL = (GST_STATE_READY<<3) | GST_STATE_NULL } GstStateChange; @@ -156,6 +157,7 @@ GST_ELEMENT_LOCKED_STATE = GST_OBJECT_FLAG_LAST, GST_ELEMENT_IS_SINK, GST_ELEMENT_UNPARENTING, + GST_ELEMENT_CHANGING_STATE, GST_ELEMENT_FLAG_LAST = GST_OBJECT_FLAG_LAST + 16 } GstElementFlags; @@ -295,15 +297,11 @@ /* element state */ GStaticRecMutex *state_lock; GCond *state_cond; - guint8 current_state; - guint8 pending_state; - guint8 final_state; - gboolean state_error; /* Flag is set when the element has an - * error in the last state change. - * It is cleared when doing another - * state change. */ - gboolean no_preroll; /* Flag is set when the element cannot - * preroll */ + GstState current_state; + GstState next_state; + GstState pending_state; + GstStateChangeReturn last_return; /*< public >*/ /* with LOCK */ GstBus *bus; @@ -491,7 +489,7 @@ 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 */ RCS file: /cvs/gstreamer/gstreamer/gst/gstpipeline.c,v retrieving revision 1.115 retrieving revision 1.116 diff -u -d -r1.115 -r1.116 --- gstpipeline.c 8 Oct 2005 18:21:20 -0000 1.115 +++ gstpipeline.c 10 Oct 2005 16:38:24 -0000 1.116 @@ -60,12 +60,11 @@ #define DEFAULT_DELAY 0 -#define DEFAULT_PLAY_TIMEOUT (2*GST_SECOND) PROP_0, PROP_DELAY, - PROP_PLAY_TIMEOUT, /* FILL ME */ @@ -141,10 +140,6 @@ "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 @@ pipeline->delay = DEFAULT_DELAY; - pipeline->play_timeout = DEFAULT_PLAY_TIMEOUT; gst_element_set_bus (GST_ELEMENT_CAST (pipeline), bus); @@ -192,9 +186,6 @@ case PROP_DELAY: pipeline->delay = g_value_get_uint64 (value); - case PROP_PLAY_TIMEOUT: - pipeline->play_timeout = g_value_get_uint64 (value); G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); @@ -213,9 +204,6 @@ g_value_set_uint64 (value, pipeline->delay); - g_value_set_uint64 (value, pipeline->play_timeout); @@ -246,8 +234,9 @@ 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 @@ 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 @@ GstStateChangeReturn result = GST_STATE_CHANGE_SUCCESS; GstPipeline *pipeline = GST_PIPELINE (element); - GstClockTime play_timeout; GstClock *clock; switch (transition) { @@ -400,40 +387,6 @@ GST_UNLOCK (element); - if (result == GST_STATE_CHANGE_ASYNC) { - GST_LOCK (pipeline); - play_timeout = pipeline->play_timeout; - GST_UNLOCK (pipeline); - 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); Index: gstpipeline.h RCS file: /cvs/gstreamer/gstreamer/gst/gstpipeline.h,v retrieving revision 1.30 retrieving revision 1.31 diff -u -d -r1.30 -r1.31 --- gstpipeline.h 27 Sep 2005 20:40:35 -0000 1.30 +++ gstpipeline.h 10 Oct 2005 16:38:24 -0000 1.31 @@ -59,7 +59,6 @@ GstClock *fixed_clock; /* fixed clock if any */ GstClockTime stream_time; GstClockTime delay; - GstClockTime play_timeout; Index: gstutils.h RCS file: /cvs/gstreamer/gstreamer/gst/gstutils.h,v retrieving revision 1.54 retrieving revision 1.55 diff -u -d -r1.54 -r1.55 --- gstutils.h 10 Oct 2005 14:23:26 -0000 1.54 +++ gstutils.h 10 Oct 2005 16:38:24 -0000 1.55 @@ -497,8 +497,6 @@ 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 @@ /* 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); Index: gstbasesink.c RCS file: /cvs/gstreamer/gstreamer/gst/base/gstbasesink.c,v --- gstbasesink.c 8 Oct 2005 14:57:09 -0000 1.63 +++ gstbasesink.c 10 Oct 2005 16:38:26 -0000 1.64 @@ -622,6 +622,7 @@ if (length == 1) { gint t; + GstTask *task; basesink->have_preroll = TRUE; /* we are prerolling */ @@ -636,8 +637,21 @@ 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); + 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 @@ +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 @@ * 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; Index: gst-launch.c RCS file: /cvs/gstreamer/gstreamer/tools/gst-launch.c,v retrieving revision 1.91 retrieving revision 1.92 diff -u -d -r1.91 -r1.92 --- gst-launch.c 10 Oct 2005 15:53:59 -0000 1.91 +++ gst-launch.c 10 Oct 2005 16:38:26 -0000 1.92 @@ -623,9 +623,12 @@ 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); |