? basesrc-segment.patch
? binstate.patch
? bus_event.patch
? cdda2.raw
? newsegment.patch
? prepare-ChangeLog.pl.txt
? refcount.patch
? states.patch
? states2.patch
? tee.patch
? test.c
? videotestsrc-segment.patch
? work.patch
? check/debug.log
? check/elements/gstfakesrc
? check/states/sinks2
? check/states/sinks2.c
? docs/design/.part-states.txt.swp
? docs/plugins/gstreamer-plugins.types
? docs/pwg/pwg.aux
? docs/pwg/pwg.dvi
? docs/pwg/pwg.log
? docs/pwg/pwg.out
? docs/random/patch1-week1.txt
? docs/random/status-0.9.txt
? docs/random/to-0.9
? docs/random/week1-summary.txt
? docs/random/week1.txt
? docs/random/week2-summary.txt
? docs/random/week3-summary.txt
? docs/random/wtay/preroll
? examples/manual/bus
? gst/Makefile.am.new
? gst/gstpad2.h
? gst/gstsegment.c
? gst/gstsegment.h
? gst/elements/libgstelements_la-gstaggregator.loT
? gst/registries/registrytest
? gst/schedulers/libgstbasicomegascheduler_la-gstbasicscheduler.loT
? gst/schedulers/libgstentryomegascheduler_la-entryscheduler.loT
? gst/schedulers/libgstoptomegascheduler_la-gstoptimalscheduler.loT
? libs/gst/bytestream/libgstbytestream_la-bytestream.loT
? tests/complexity
? tests/memchunk/gmemchunktest
? tests/memchunk/gstmemchunktest
? tests/sched/debug.log
? tests/sched/sort
? tests/sched/sort.c
? testsuite/bins/interface
? testsuite/clock/clock1
? testsuite/clock/clock2
? testsuite/clock/signedness
? testsuite/tags/merge
? testsuite/trigger/trigger
? tools/alien-flmp3dec.mp3
? tools/alien-mad.mp3
? tools/alien-mad2.mp3
? tools/alien.mp3
? tools/alien.raw
? tools/audio-AMR-1
? tools/audio-AMR-2
? tools/avi2ogg
? tools/bullet.mp2v
? tools/cdda2.raw
? tools/cooldance.ogg
? tools/debug.log
? tools/dv2ogg
? tools/exports
? tools/index.html
? tools/log
? tools/mpeg2ogg
? tools/mpeg2oggt
? tools/out
? tools/playogg
? tools/record2ogg
? tools/record2ogg2
? tools/rtp1.out
? tools/test.m1v
? tools/test.mp3
? tools/test.ogg
? tools/test.ogg.1
? tools/test.oggt
? tools/test.raw
? tools/test2.mp3
? tools/test2ogg
? tools/video-MP4V-ES-1
? tools/vob2mpeg
? tools/vob2ogg
Index: check/Makefile.am
===================================================================
RCS file: /cvs/gstreamer/gstreamer/check/Makefile.am,v
retrieving revision 1.63
diff -u -B -b -p -r1.63 Makefile.am
--- check/Makefile.am	22 Sep 2005 12:05:04 -0000	1.63
+++ check/Makefile.am	7 Oct 2005 18:05:19 -0000
@@ -54,6 +54,7 @@ check_PROGRAMS =				\
 	pipelines/simple_launch_lines		\
 	pipelines/cleanup			\
 	states/sinks				\
+	states/sinks2				\
 	gst-libs/controller			\
 	gst-libs/gdp
 
Index: check/generic/states.c
===================================================================
RCS file: /cvs/gstreamer/gstreamer/check/generic/states.c,v
retrieving revision 1.5
diff -u -B -b -p -r1.5 states.c
--- check/generic/states.c	20 Sep 2005 00:27:37 -0000	1.5
+++ check/generic/states.c	7 Oct 2005 18:05:19 -0000
@@ -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);
Index: check/gst/gstbin.c
===================================================================
RCS file: /cvs/gstreamer/gstreamer/check/gst/gstbin.c,v
retrieving revision 1.33
diff -u -B -b -p -r1.33 gstbin.c
--- check/gst/gstbin.c	29 Sep 2005 18:37:48 -0000	1.33
+++ check/gst/gstbin.c	7 Oct 2005 18:05:19 -0000
@@ -109,6 +109,7 @@ GST_START_TEST (test_message_state_chang
   GstBin *bin;
   GstBus *bus;
   GstMessage *message;
+  GstStateChangeReturn ret;
 
   bin = GST_BIN (gst_bin_new (NULL));
   fail_unless (bin != NULL, "Could not create bin");
@@ -118,7 +119,8 @@ GST_START_TEST (test_message_state_chang
   gst_element_set_bus (GST_ELEMENT_CAST (bin), bus);
 
   /* change state, spawning a message, causing an incref on the bin */
-  gst_element_set_state (GST_ELEMENT (bin), GST_STATE_READY);
+  ret = gst_element_set_state (GST_ELEMENT (bin), GST_STATE_READY);
+  fail_unless (ret == GST_STATE_CHANGE_SUCCESS);
 
   ASSERT_OBJECT_REFCOUNT (bin, "bin", 2);
 
@@ -133,7 +135,8 @@ GST_START_TEST (test_message_state_chang
   ASSERT_OBJECT_REFCOUNT (bin, "bin", 1);
 
   /* clean up */
-  gst_element_set_state (GST_ELEMENT (bin), GST_STATE_NULL);
+  ret = gst_element_set_state (GST_ELEMENT (bin), GST_STATE_NULL);
+  fail_unless (ret == GST_STATE_CHANGE_SUCCESS);
 
   gst_object_unref (bus);
   gst_object_unref (bin);
@@ -147,6 +150,7 @@ GST_START_TEST (test_message_state_chang
   GstElement *src;
   GstBus *bus;
   GstMessage *message;
+  GstStateChangeReturn ret;
 
   bin = GST_BIN (gst_bin_new (NULL));
   fail_unless (bin != NULL, "Could not create bin");
@@ -165,8 +169,8 @@ GST_START_TEST (test_message_state_chang
    * - first for fakesrc, forwarded to bin's bus, causing incref on fakesrc
    * - second for bin, causing an incref on the bin */
   GST_DEBUG ("setting bin to READY");
-  fail_unless (gst_element_set_state (GST_ELEMENT (bin), GST_STATE_READY)
-      == GST_STATE_CHANGE_SUCCESS);
+  ret = gst_element_set_state (GST_ELEMENT (bin), GST_STATE_READY);
+  fail_unless (ret == GST_STATE_CHANGE_SUCCESS);
 
   ASSERT_OBJECT_REFCOUNT (src, "src", 2);
   ASSERT_OBJECT_REFCOUNT (bin, "bin", 2);
@@ -194,8 +198,8 @@ GST_START_TEST (test_message_state_chang
   ASSERT_OBJECT_REFCOUNT (bin, "bin", 1);
 
   /* clean up */
-  fail_unless (gst_element_set_state (GST_ELEMENT (bin), GST_STATE_NULL)
-      == GST_STATE_CHANGE_SUCCESS);
+  ret = gst_element_set_state (GST_ELEMENT (bin), GST_STATE_NULL);
+  fail_unless (ret == GST_STATE_CHANGE_SUCCESS);
   gst_object_unref (bus);
   gst_object_unref (bin);
 }
@@ -207,6 +211,8 @@ GST_START_TEST (test_message_state_chang
   GstPipeline *pipeline;
   GstElement *src, *sink;
   GstBus *bus;
+  GstStateChangeReturn ret;
+  GstState current, pending;
 
   pipeline = GST_PIPELINE (gst_pipeline_new (NULL));
   fail_unless (pipeline != NULL, "Could not create pipeline");
@@ -236,8 +242,8 @@ GST_START_TEST (test_message_state_chang
 
   /* change state to READY, spawning three messages */
   GST_DEBUG ("setting pipeline to READY");
-  fail_unless (gst_element_set_state (GST_ELEMENT (pipeline), GST_STATE_READY)
-      == GST_STATE_CHANGE_SUCCESS);
+  ret = gst_element_set_state (GST_ELEMENT (pipeline), GST_STATE_READY);
+  fail_unless (ret == GST_STATE_CHANGE_SUCCESS);
 
   /* each object is referenced by a message */
   ASSERT_OBJECT_REFCOUNT (bus, "bus", 2);
@@ -255,8 +261,12 @@ GST_START_TEST (test_message_state_chang
 
   /* change state to PAUSED, spawning three messages */
   GST_DEBUG ("setting pipeline to PAUSED");
-  fail_unless (gst_element_set_state (GST_ELEMENT (pipeline), GST_STATE_PAUSED)
-      == GST_STATE_CHANGE_SUCCESS);
+  ret = gst_element_set_state (GST_ELEMENT (pipeline), GST_STATE_PAUSED);
+  fail_unless (ret == GST_STATE_CHANGE_ASYNC);
+  ret = gst_element_get_state (GST_ELEMENT (pipeline), &current, &pending, NULL);
+  fail_unless (ret == GST_STATE_CHANGE_SUCCESS);
+  fail_unless (current == GST_STATE_PAUSED);
+  fail_unless (pending == GST_STATE_VOID_PENDING);
 
   /* each object is referenced by a message;
    * base_sink_chain has taken a refcount on the sink, and is blocked on
@@ -275,8 +285,12 @@ GST_START_TEST (test_message_state_chang
 
   /* change state to PLAYING, spawning three messages */
   GST_DEBUG ("setting pipeline to PLAYING");
-  fail_unless (gst_element_set_state (GST_ELEMENT (pipeline), GST_STATE_PLAYING)
-      == GST_STATE_CHANGE_SUCCESS);
+  ret = gst_element_set_state (GST_ELEMENT (pipeline), GST_STATE_PLAYING);
+  fail_unless (ret == GST_STATE_CHANGE_SUCCESS);
+  ret = gst_element_get_state (GST_ELEMENT (pipeline), &current, &pending, NULL);
+  fail_unless (ret == GST_STATE_CHANGE_SUCCESS);
+  fail_unless (current == GST_STATE_PLAYING);
+  fail_unless (pending == GST_STATE_VOID_PENDING);
 
   /* each object is referenced by one message
    * sink might have an extra reference if it's still blocked on preroll */
@@ -295,8 +309,8 @@ GST_START_TEST (test_message_state_chang
 
   /* go back to READY, spawning six messages */
   GST_DEBUG ("setting pipeline to READY");
-  fail_unless (gst_element_set_state (GST_ELEMENT (pipeline), GST_STATE_READY)
-      == GST_STATE_CHANGE_SUCCESS);
+  ret = gst_element_set_state (GST_ELEMENT (pipeline), GST_STATE_READY);
+  fail_unless (ret == GST_STATE_CHANGE_SUCCESS);
 
   /* each object is referenced by two messages */
   ASSERT_OBJECT_REFCOUNT (src, "src", 3);
@@ -311,8 +325,8 @@ GST_START_TEST (test_message_state_chang
   ASSERT_OBJECT_REFCOUNT (pipeline, "pipeline", 1);
 
   /* setting pipeline to NULL flushes the bus automatically */
-  fail_unless (gst_element_set_state (GST_ELEMENT (pipeline), GST_STATE_NULL)
-      == GST_STATE_CHANGE_SUCCESS);
+  ret = gst_element_set_state (GST_ELEMENT (pipeline), GST_STATE_NULL);
+  fail_unless (ret == GST_STATE_CHANGE_SUCCESS);
 
   ASSERT_OBJECT_REFCOUNT (src, "src", 1);
   ASSERT_OBJECT_REFCOUNT (sink, "sink", 1);
@@ -329,6 +343,7 @@ GST_START_TEST (test_watch_for_state_cha
 {
   GstElement *src, *sink, *bin;
   GstBus *bus;
+  GstStateChangeReturn ret;
 
   bin = gst_element_factory_make ("bin", NULL);
   fail_unless (bin != NULL, "Could not create bin");
@@ -346,32 +361,25 @@ GST_START_TEST (test_watch_for_state_cha
 
   fail_unless (gst_element_link (src, sink), "could not link src and sink");
 
-  /* change state, spawning two times three messages, minus one async */
-  fail_unless (gst_element_set_state (GST_ELEMENT (bin), GST_STATE_PAUSED)
-      == GST_STATE_CHANGE_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");
 
-  fail_unless (gst_element_set_state (GST_ELEMENT (bin), GST_STATE_PLAYING)
-      == GST_STATE_CHANGE_SUCCESS);
+  ret = gst_element_set_state (GST_ELEMENT (bin), GST_STATE_PLAYING);
+  fail_unless (ret == GST_STATE_CHANGE_SUCCESS);
 
   pop_messages (bus, 3);
 
   /* 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);
 
@@ -379,8 +387,8 @@ GST_START_TEST (test_watch_for_state_cha
       "Unexpected messages on bus");
 
   /* setting bin to NULL flushes the bus automatically */
-  fail_unless (gst_element_set_state (GST_ELEMENT (bin), GST_STATE_NULL)
-      == GST_STATE_CHANGE_SUCCESS);
+  ret = gst_element_set_state (GST_ELEMENT (bin), GST_STATE_NULL);
+  fail_unless (ret == GST_STATE_CHANGE_SUCCESS);
 
   /* clean up */
   gst_object_unref (bus);
@@ -448,11 +456,11 @@ GST_END_TEST;
 #define ASSERT_STATE_CHANGE_MSG(bus,element,old_state,new_state,num)          \
   {                                                                           \
     GstMessage *msg;                                                          \
-    GstState old = 0, new = 0;                                                \
+    GstState old = 0, new = 0, pending = 0;                                   \
     msg = gst_bus_poll (bus, GST_MESSAGE_STATE_CHANGED, GST_SECOND);          \
     fail_if (msg == NULL, "No state change message within 1 second (#"        \
         G_STRINGIFY (num) ")");                                               \
-    gst_message_parse_state_changed (msg, &old, &new);                        \
+    gst_message_parse_state_changed (msg, &old, &new, &pending);              \
     fail_if (msg->src != GST_OBJECT (element), G_STRINGIFY(element)           \
         " should have changed state next (#" G_STRINGIFY (num) ")");          \
     fail_if (old != old_state || new != new_state, "state change is not "     \
@@ -464,6 +472,7 @@ GST_START_TEST (test_children_state_chan
 {
   GstElement *src, *identity, *sink, *pipeline;
   GstStateChangeReturn ret;
+  GstState current, pending;
   GstBus *bus;
 
   pipeline = gst_pipeline_new (NULL);
@@ -488,7 +497,11 @@ GST_START_TEST (test_children_state_chan
 
   /* (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, &current, &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);
@@ -526,6 +539,8 @@ GST_START_TEST (test_children_state_chan
   /* don't set to NULL that will set the bus flushing and kill our messages */
   ret = gst_element_set_state (pipeline, GST_STATE_READY);
   fail_if (ret != GST_STATE_CHANGE_SUCCESS, "State change to READY failed");
+  ret = gst_element_get_state (pipeline, NULL, NULL, NULL);
+  fail_if (ret != GST_STATE_CHANGE_SUCCESS, "State change to READY failed");
 
   /* TODO: do we need to check downwards state change order as well? */
   pop_messages (bus, 4);        /* pop playing => paused messages off the bus */
@@ -553,6 +568,7 @@ GST_START_TEST (test_children_state_chan
 {
   GstElement *src, *identity, *sink, *pipeline;
   GstStateChangeReturn ret;
+  GstState current, pending;
   GstBus *bus;
 
   /* (2) Now again, but check other code path where we don't have
@@ -581,7 +597,11 @@ GST_START_TEST (test_children_state_chan
   GST_FLAG_UNSET (sink, GST_ELEMENT_IS_SINK);   /* <======== */
 
   ret = gst_element_set_state (pipeline, GST_STATE_PLAYING);
+  fail_if (ret != GST_STATE_CHANGE_ASYNC, "State change to PLAYING not ASYNC");
+  ret = gst_element_get_state (pipeline, &current, &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, 201);
@@ -681,13 +701,13 @@ GST_START_TEST (test_children_state_chan
   /* NULL => READY */
   {
     GstMessage *msg;
-    GstState old = 0, new = 0;
+    GstState old = 0, new = 0, pending = 0;
     GstObject *first, *second;
 
     msg = gst_bus_poll (bus, GST_MESSAGE_STATE_CHANGED, GST_SECOND);
     fail_if (msg == NULL, "No state change message within 1 second (#201)");
 
-    gst_message_parse_state_changed (msg, &old, &new);
+    gst_message_parse_state_changed (msg, &old, &new, &pending);
     first = gst_object_ref (msg->src);
 
     fail_if (first != GST_OBJECT (sink1) && first != GST_OBJECT (sink2),
@@ -697,7 +717,7 @@ GST_START_TEST (test_children_state_chan
     msg = gst_bus_poll (bus, GST_MESSAGE_STATE_CHANGED, GST_SECOND);
     fail_if (msg == NULL, "No state change message within 1 second (#201)");
 
-    gst_message_parse_state_changed (msg, &old, &new);
+    gst_message_parse_state_changed (msg, &old, &new, &pending);
     second = gst_object_ref (msg->src);
 
     fail_if (second != GST_OBJECT (sink1) && second != GST_OBJECT (sink2),
@@ -743,6 +763,8 @@ gst_bin_suite (void)
   Suite *s = suite_create ("GstBin");
   TCase *tc_chain = tcase_create ("bin tests");
 
+  tcase_set_timeout (tc_chain, 0);
+
   suite_add_tcase (s, tc_chain);
   tcase_add_test (tc_chain, test_interface);
   tcase_add_test (tc_chain, test_children_state_change_order_flagged_sink);
Index: check/gst/gstghostpad.c
===================================================================
RCS file: /cvs/gstreamer/gstreamer/check/gst/gstghostpad.c,v
retrieving revision 1.10
diff -u -B -b -p -r1.10 gstghostpad.c
--- check/gst/gstghostpad.c	6 Sep 2005 18:18:48 -0000	1.10
+++ check/gst/gstghostpad.c	7 Oct 2005 18:05:19 -0000
@@ -195,6 +195,7 @@ GST_START_TEST (test_ghost_pads)
 {
   GstElement *b1, *b2, *src, *i1, *sink;
   GstPad *gsink, *gsrc, *gisrc, *gisink, *isink, *isrc, *fsrc, *fsink;
+  GstStateChangeReturn ret;
 
   b1 = gst_element_factory_make ("pipeline", NULL);
   b2 = gst_element_factory_make ("bin", NULL);
@@ -246,11 +247,13 @@ GST_START_TEST (test_ghost_pads)
   assert_gstrefcount (gisink, 2);       /* parent */
   assert_gstrefcount (isrc, 3); /* parent and gsrc */
 
-  fail_unless (gst_element_set_state (b1,
-          GST_STATE_PLAYING) == GST_STATE_CHANGE_SUCCESS);
-
-  fail_unless (gst_element_set_state (b1,
-          GST_STATE_NULL) == GST_STATE_CHANGE_SUCCESS);
+  ret = gst_element_set_state (b1, GST_STATE_PLAYING);
+  ret = gst_element_get_state (b1, NULL, NULL, NULL);
+  fail_unless (ret == GST_STATE_CHANGE_SUCCESS);
+
+  ret = gst_element_set_state (b1, GST_STATE_NULL);
+  ret = gst_element_get_state (b1, NULL, NULL, NULL);
+  fail_unless (ret == GST_STATE_CHANGE_SUCCESS);
 
   gst_object_unref (b1);
   /* unreffing the bin will unref all elements, which will unlink and unparent
Index: check/gst/gstmessage.c
===================================================================
RCS file: /cvs/gstreamer/gstreamer/check/gst/gstmessage.c,v
retrieving revision 1.6
diff -u -B -b -p -r1.6 gstmessage.c
--- check/gst/gstmessage.c	2 Sep 2005 15:41:56 -0000	1.6
+++ check/gst/gstmessage.c	7 Oct 2005 18:05:19 -0000
@@ -120,21 +120,24 @@ GST_START_TEST (test_parsing)
   }
   /* GST_MESSAGE_STATE_CHANGED   */
   {
-    GstState oldstate, newstate;
+    GstState oldstate, newstate, pending;
 
-    oldstate = GST_STATE_PAUSED;
-    newstate = GST_STATE_PLAYING;
+    oldstate = GST_STATE_READY;
+    newstate = GST_STATE_PAUSED;
+    pending = GST_STATE_PLAYING;
 
-    message = gst_message_new_state_changed (NULL, oldstate, newstate);
+    message = gst_message_new_state_changed (NULL, oldstate, newstate, pending);
     fail_if (message == NULL);
     fail_unless (GST_MESSAGE_TYPE (message) == GST_MESSAGE_STATE_CHANGED);
     fail_unless (GST_MESSAGE_SRC (message) == NULL);
 
     oldstate = GST_STATE_READY;
     newstate = GST_STATE_READY;
-    gst_message_parse_state_changed (message, &oldstate, &newstate);
-    fail_unless (oldstate == GST_STATE_PAUSED);
-    fail_unless (newstate == GST_STATE_PLAYING);
+    gst_message_parse_state_changed (message, &oldstate, &newstate,
+		    &pending);
+    fail_unless (oldstate == GST_STATE_READY);
+    fail_unless (newstate == GST_STATE_PAUSED);
+    fail_unless (pending == GST_STATE_PLAYING);
 
     gst_message_unref (message);
   }
Index: check/gst/gstpipeline.c
===================================================================
RCS file: /cvs/gstreamer/gstreamer/check/gst/gstpipeline.c,v
retrieving revision 1.7
diff -u -B -b -p -r1.7 gstpipeline.c
--- check/gst/gstpipeline.c	29 Sep 2005 20:16:42 -0000	1.7
+++ check/gst/gstpipeline.c	7 Oct 2005 18:05:19 -0000
@@ -28,7 +28,6 @@ GST_START_TEST (test_async_state_change_
 
   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_
 
   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_
 
   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,16 +78,16 @@ GST_START_TEST (test_async_state_change_
 
   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) {
     GstMessage *message;
-    GstState old, new;
+    GstState old, new, pending;
 
     message = gst_bus_poll (bus, GST_MESSAGE_STATE_CHANGED, -1);
     if (message) {
-      gst_message_parse_state_changed (message, &old, &new);
+      gst_message_parse_state_changed (message, &old, &new, &pending);
       GST_DEBUG_OBJECT (message->src, "state change from %d to %d", old, new);
       if (message->src == GST_OBJECT (pipeline) && new == GST_STATE_PLAYING)
         done = TRUE;
@@ -98,7 +95,6 @@ GST_START_TEST (test_async_state_change_
     }
   }
 
-  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);
 
@@ -141,10 +137,10 @@ message_received (GstBus * bus, GstMessa
   switch (type) {
     case GST_MESSAGE_STATE_CHANGED:
     {
-      GstState old, new;
+      GstState old, new, pending;
 
       GST_DEBUG ("state change message received");
-      gst_message_parse_state_changed (message, &old, &new);
+      gst_message_parse_state_changed (message, &old, &new, &pending);
       GST_DEBUG ("new state %d", new);
       if (message->src == GST_OBJECT (pipeline) && new == GST_STATE_PLAYING) {
         GST_DEBUG ("quitting main loop");
@@ -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, &current, 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);
Index: check/pipelines/simple_launch_lines.c
===================================================================
RCS file: /cvs/gstreamer/gstreamer/check/pipelines/simple_launch_lines.c,v
retrieving revision 1.14
diff -u -B -b -p -r1.14 simple_launch_lines.c
--- check/pipelines/simple_launch_lines.c	19 Sep 2005 14:41:56 -0000	1.14
+++ check/pipelines/simple_launch_lines.c	7 Oct 2005 18:05:20 -0000
@@ -48,12 +48,16 @@ run_pipeline (GstElement * pipeline, gch
 {
   GstBus *bus;
   GstMessageType revent;
+  GstStateChangeReturn ret;
 
   g_assert (pipeline);
   bus = gst_element_get_bus (pipeline);
   g_assert (bus);
-  if (gst_element_set_state (pipeline,
-          GST_STATE_PLAYING) != GST_STATE_CHANGE_SUCCESS) {
+
+  ret = gst_element_set_state (pipeline, GST_STATE_PLAYING);
+  ret = gst_element_get_state (pipeline, NULL, NULL, NULL);
+
+  if (ret != GST_STATE_CHANGE_SUCCESS) {
     g_critical ("Couldn't set pipeline to PLAYING");
     goto done;
   }
@@ -189,7 +193,7 @@ simple_launch_lines_suite (void)
   TCase *tc_chain = tcase_create ("linear");
 
   /* time out after 20s, not the default 3 */
-  tcase_set_timeout (tc_chain, 20);
+  tcase_set_timeout (tc_chain, 0);
 
   suite_add_tcase (s, tc_chain);
   tcase_add_test (tc_chain, test_2_elements);
Index: check/states/sinks.c
===================================================================
RCS file: /cvs/gstreamer/gstreamer/check/states/sinks.c,v
retrieving revision 1.9
diff -u -B -b -p -r1.9 sinks.c
--- check/states/sinks.c	29 Sep 2005 18:37:48 -0000	1.9
+++ check/states/sinks.c	7 Oct 2005 18:05:20 -0000
@@ -42,7 +42,7 @@ GST_START_TEST (test_sink)
 
   ret = gst_element_get_state (sink, &current, &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");
 
@@ -91,9 +92,15 @@ GST_START_TEST (test_src_sink)
   fail_unless (ret == GST_STATE_CHANGE_SUCCESS, "not playing");
   fail_unless (current == GST_STATE_PLAYING, "not playing");
   fail_unless (pending == GST_STATE_VOID_PENDING, "not playing");
-}
 
+  ret = gst_element_set_state (pipeline, GST_STATE_NULL);
+  fail_unless (ret == GST_STATE_CHANGE_SUCCESS, "cannot null pipeline");
+
+  gst_object_unref (pipeline);
+
+}
 GST_END_TEST
+
 /* a pipeline with live source should return NO_PREROLL in
  * PAUSE. When removing the live source it should return ASYNC
  * from the sink */
@@ -134,8 +141,7 @@ GST_START_TEST (test_livesrc_remove)
   ret = gst_element_get_state (pipeline, &current, &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
@@ -177,7 +183,8 @@ GST_START_TEST (test_livesrc_sink)
   fail_unless (pending == GST_STATE_VOID_PENDING, "not playing");
 
   ret = gst_element_set_state (pipeline, GST_STATE_PLAYING);
-  fail_unless (ret == GST_STATE_CHANGE_SUCCESS, "cannot force play");
+  ret = gst_element_get_state (pipeline, NULL, NULL, NULL);
+  fail_unless (ret == GST_STATE_CHANGE_SUCCESS, "cannot force play got %d", ret);
 
   ret = gst_element_get_state (pipeline, &current, &pending, NULL);
   fail_unless (ret == GST_STATE_CHANGE_SUCCESS, "not playing");
Index: docs/design/part-states.txt
===================================================================
RCS file: /cvs/gstreamer/gstreamer/docs/design/part-states.txt,v
retrieving revision 1.4
diff -u -B -b -p -r1.4 part-states.txt
--- docs/design/part-states.txt	30 Jun 2005 09:33:45 -0000	1.4
+++ docs/design/part-states.txt	7 Oct 2005 18:05:20 -0000
@@ -168,5 +168,96 @@ READY
 
 
    
+upward state change
+-------------------
+
+Element:
+
+  A -> B => success 
+    - commit state
+
+  A -> B => async 
+    - no commit state
+    - element commits state async
+
+  A -> B while ASYNC
+    - update target state
+    - no commit state
+    - no change_state called on element
+
+Bin:
+
+  A->B: all elements success
+    - commit state
+
+  A->B: some elements async
+    - no commit state
+    - listen for commit messages on bus
+    - for each commit message, poll pending async elements
+    - if no async elements, commit state, set state to target
+
+downward state change
+----------------------
+
+Element:
+
+  A -> B => success 
+    - commit state
+
+  A -> B => async not final state
+    - commit state
+
+  A -> B => async final state
+    - element will commit async 
+
+Bin:
+  
+  A -> B -> success
+    - commit state
+
+  A -> B -> async not final state
+    - commit state
+
+  A -> B => async final state
+    - no commit state
+    - listen for commit messages on bus
+    - for each commit message, poll pending async elements
+    - if no async elements, commit state
+
+
+Locking overview (element)
+--------------------------
+
+ set_state(element)       change_state (element)     stream_thread      commit_state (element)
+
+      |                         |                          |                  |
+      |                         |                          |                  |
+  STATE_LOCK                    |                          |                  |
+      |                         |                          |                  |
+      |------------------------>|                          |                  |
+      |                         |                          |                  |
+      |                         |                          |                  |
+      |                         | (start_task)             |                  |
+      |                         |                          |                  |
+      |                         |                     STREAM_LOCK             |
+      |                         |                          |                  |
+      |<------------------------|                          |                  |
+      |     ASYNC                                          |                  |
+  STATE_UNLOCK                                             |                  |       
+      |                .....sync........               STATE_LOCK             |               
+    ASYNC                                                  |----------------->|
+                                                           |                  |
+                                                           |                  |---> post_message(ASYNC)
+                                                           |                  |---> change_state (next)
+                                                           |                  |---> SIGNAL
+                                                           |<-----------------|
+                                                       STATE_UNLOCK
+                                                           |
+                                                      STREAM_UNLOCK
+                                     
+       
+       
+       
+               
 
 
Index: gst/gstbin.c
===================================================================
RCS file: /cvs/gstreamer/gstreamer/gst/gstbin.c,v
retrieving revision 1.274
diff -u -B -b -p -r1.274 gstbin.c
--- gst/gstbin.c	7 Oct 2005 00:14:45 -0000	1.274
+++ gst/gstbin.c	7 Oct 2005 18:05:20 -0000
@@ -86,9 +85,10 @@ GType _gst_bin_type = 0;
 
 static void gst_bin_dispose (GObject * object);
 
-static GstStateChangeReturn gst_bin_change_state (GstElement * element,
+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 (GstElement * element,
+static GstStateChangeReturn gst_bin_get_state_func (GstElement * element,
     GstState * state, GstState * pending, GTimeVal * timeout);
 
 static gboolean gst_bin_add_func (GstBin * bin, GstElement * element);
@@ -112,7 +112,7 @@ static void gst_bin_restore_thyself (Gst
 
 static gint bin_element_is_sink (GstElement * child, GstBin * bin);
 
-/* Bin signals and args */
+/* Bin signals and properties */
 enum
 {
   ELEMENT_ADDED,
@@ -122,7 +122,7 @@ enum
 
 enum
 {
-  ARG_0
+  PROP_0
       /* FILL ME */
 };
 
@@ -185,13 +185,32 @@ static GstObject *
 gst_bin_child_proxy_get_child_by_index (GstChildProxy * child_proxy,
     guint index)
 {
-  return g_list_nth_data (GST_BIN (child_proxy)->children, index);
+  GstObject *res;
+  GstBin *bin;
+
+  bin = GST_BIN (child_proxy);
+
+  GST_LOCK (bin);
+  if ((res = g_list_nth_data (bin->children, index))) 
+    gst_object_ref (res);
+  GST_UNLOCK (bin);
+
+  return res;
 }
 
 guint
 gst_bin_child_proxy_get_children_count (GstChildProxy * child_proxy)
 {
-  return GST_BIN (child_proxy)->numchildren;
+  guint num;
+  GstBin *bin;
+
+  bin = GST_BIN (child_proxy);
+
+  GST_LOCK (bin);
+  num = bin->numchildren;
+  GST_UNLOCK (bin);
+
+  return num;
 }
 
 static void
@@ -247,8 +266,8 @@ gst_bin_class_init (GstBinClass * klass)
       GST_DEBUG_FUNCPTR (gst_bin_restore_thyself);
 #endif
 
-  gstelement_class->change_state = GST_DEBUG_FUNCPTR (gst_bin_change_state);
-  gstelement_class->get_state = GST_DEBUG_FUNCPTR (gst_bin_get_state);
+  gstelement_class->change_state = GST_DEBUG_FUNCPTR (gst_bin_change_state_func);
+  gstelement_class->get_state = GST_DEBUG_FUNCPTR (gst_bin_get_state_func);
 #ifndef GST_DISABLE_INDEX
   gstelement_class->set_index = GST_DEBUG_FUNCPTR (gst_bin_set_index_func);
 #endif
@@ -272,6 +291,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);
@@ -367,50 +388,32 @@ gst_bin_provide_clock_func (GstElement *
   return result;
 }
 
+/* Check if the bin is EOS. We do this by scanning all sinks and
+ * checking if they posted EOS.
+ * 
+ * call with bin LOCK */
 static gboolean
 is_eos (GstBin * bin)
 {
-  GstIterator *sinks;
-  gboolean result = TRUE;
-  gboolean done = FALSE;
-
-  sinks = gst_bin_iterate_sinks (bin);
-  while (!done) {
-    gpointer data;
+  gboolean result;
+  GList *walk;
 
-    switch (gst_iterator_next (sinks, &data)) {
-      case GST_ITERATOR_OK:
-      {
-        GstElement *element = GST_ELEMENT (data);
-        GList *eosed;
-        gchar *name;
+  result = TRUE;
+  for (walk = bin->children; walk; walk = g_list_next (walk)) {
+    GstElement *element;
 
-        name = gst_element_get_name (element);
-        eosed = g_list_find (bin->eosed, element);
-        if (!eosed) {
-          GST_DEBUG ("element %s did not post EOS yet", name);
+    element = GST_ELEMENT_CAST (walk->data);
+    if (bin_element_is_sink (element, bin) == 0) {
+      if (!g_list_find (bin->eosed, element)) {
+        GST_DEBUG ("element did not post EOS yet");
           result = FALSE;
-          done = TRUE;
-        } else {
-          GST_DEBUG ("element %s posted EOS", name);
-        }
-        g_free (name);
-        gst_object_unref (element);
         break;
       }
-      case GST_ITERATOR_RESYNC:
-        result = TRUE;
-        gst_iterator_resync (sinks);
-        break;
-      case GST_ITERATOR_DONE:
-        done = TRUE;
-        break;
-      default:
-        g_assert_not_reached ();
-        break;
+      else {
+        GST_DEBUG ("element posted EOS");
+      }
     }
   }
-  gst_iterator_free (sinks);
   return result;
 }
 
@@ -479,7 +482,7 @@ gst_bin_add_func (GstBin * bin, GstEleme
   /* propagate the current base time and clock */
   gst_element_set_base_time (element, GST_ELEMENT (bin)->base_time);
   gst_element_set_clock (element, GST_ELEMENT_CLOCK (bin));
-
+  bin->state_dirty = TRUE;
   GST_UNLOCK (bin);
 
   /* unlink all linked pads */
@@ -615,6 +618,7 @@ gst_bin_remove_func (GstBin * bin, GstEl
       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\"",
@@ -868,45 +872,56 @@ 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
-gst_bin_get_state (GstElement * element, GstState * state,
+gst_bin_get_state_func (GstElement * element, GstState * state,
     GstState * pending, GTimeVal * timeout)
 {
   GstBin *bin = GST_BIN (element);
-  GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
+
+  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;
   gboolean have_no_preroll;
+  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 between going into
-   * the quick scan and the blocking wait. */
   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;
+  have_async = FALSE;
 
-  /* first we need to poll with a non zero timeout to make sure we don't block
-   * on the sinks when we have NO_PREROLL elements. This is why we do
-   * a quick check if there are still NO_PREROLL elements. We also
-   * catch the error elements this way. */
-  {
-    GTimeVal tv;
-    gboolean have_async = FALSE;
+  GST_CAT_INFO_OBJECT (GST_CAT_STATES, bin, "checking element states");
 
-    GST_CAT_INFO_OBJECT (GST_CAT_STATES, element, "checking for NO_PREROLL");
-    /* use 0 timeout so we don't block on the sinks */
+  /* scan all element states with a zero timeout so we don't block on
+   * anything */
     GST_TIME_TO_TIMEVAL (0, tv);
     children = bin->children;
     children_cookie = bin->children_cookie;
@@ -932,8 +947,8 @@ restart:
       }
 
       switch (ret) {
-          /* report FAILURE  immediatly */
         case GST_STATE_CHANGE_FAILURE:
+        /* report FAILURE  immediatly */
           goto done;
         case GST_STATE_CHANGE_NO_PREROLL:
           /* we have to continue scanning as there might be
@@ -941,6 +956,8 @@ restart:
           have_no_preroll = TRUE;
           break;
         case GST_STATE_CHANGE_ASYNC:
+        /* we have to continue scanning as there might be
+         * ERRORS too */
           have_async = TRUE;
           break;
         default:
@@ -948,69 +965,20 @@ restart:
       }
       children = g_list_next (children);
     }
-    /* if we get here, we have no FAILURES, check for any NO_PREROLL
-     * elements then. */
+  /* if we get here, we have no FAILURES */
+
+  /* if we have NO_PREROLL, return that */
     if (have_no_preroll) {
       ret = GST_STATE_CHANGE_NO_PREROLL;
-      goto done;
-    }
-
-    /* if we get here, no NO_PREROLL elements are in the pipeline */
-    GST_CAT_INFO_OBJECT (GST_CAT_STATES, element, "no NO_PREROLL elements");
-
-    /* if no ASYNC elements exist we don't even have to poll with a
-     * timeout again */
-    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
@@ -1020,35 +988,44 @@ done:
   GST_STATE_LOCK (bin);
   switch (ret) {
     case GST_STATE_CHANGE_SUCCESS:
-      /* we can commit the state */
-      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:
-      /* some element failed, abort the state change */
-      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;
+  }
 }
 
 /***********************************************
@@ -1130,7 +1107,7 @@ update_degree (GstElement * element, Gst
   gboolean linked = FALSE;
 
   GST_LOCK (element);
-  /* don't touch degree is element has no sourcepads */
+  /* don't touch degree if element has no sourcepads */
   if (element->numsinkpads != 0) {
     /* loop over all sinkpads, decrement degree for all connected
      * elements in this bin */
@@ -1172,7 +1149,7 @@ update_degree (GstElement * element, Gst
     }
   }
   if (!linked) {
-    GST_DEBUG ("element %s not linked to anything", GST_ELEMENT_NAME (element));
+    GST_DEBUG ("element %s not linked on any sinkpads", GST_ELEMENT_NAME (element));
   }
   GST_UNLOCK (element);
 }
@@ -1329,11 +1306,11 @@ done:
 }
 
 static GstStateChangeReturn
-gst_bin_change_state (GstElement * element, GstStateChange transition)
+gst_bin_change_state_func (GstElement * element, GstStateChange transition)
 {
   GstBin *bin;
   GstStateChangeReturn ret;
-  GstState old_state, pending;
+  GstState current, next;
   gboolean have_async;
   gboolean have_no_preroll;
   GstClockTime base_time;
@@ -1341,23 +1318,23 @@ gst_bin_change_state (GstElement * eleme
   gboolean done;
 
   /* we don't need to take the STATE_LOCK, it is already taken */
-  old_state = GST_STATE (element);
-  pending = 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 (old_state),
-      gst_element_state_get_name (pending));
-
-  if (pending == GST_STATE_VOID_PENDING)
-    return GST_STATE_CHANGE_SUCCESS;
+      gst_element_state_get_name (current),
+      gst_element_state_get_name (next));
 
   bin = GST_BIN_CAST (element);
 
-  /* Clear eosed element list on READY-> PAUSED */
-  if (transition == GST_STATE_CHANGE_READY_TO_PAUSED) {
+  /* Clear eosed element list on next PAUSED */
+  if (next == GST_STATE_PAUSED) {
+    GST_DEBUG_OBJECT (element, "clearing EOS elements");
+    GST_LOCK (bin);
     g_list_free (bin->eosed);
     bin->eosed = NULL;
+    GST_UNLOCK (bin);
   }
 
   /* iterate in state change order */
@@ -1365,7 +1342,7 @@ gst_bin_change_state (GstElement * eleme
 
 restart:
   /* take base time */
-  base_time = element->base_time;
+  base_time = gst_element_get_base_time (element);
 
   have_async = FALSE;
   have_no_preroll = FALSE;
@@ -1377,48 +1354,48 @@ restart:
     switch (gst_iterator_next (it, &data)) {
       case GST_ITERATOR_OK:
       {
-        GstElement *element;
+        GstElement *child;
 
-        element = GST_ELEMENT_CAST (data);
+        child = GST_ELEMENT_CAST (data);
 
-        /* set base time on element */
-        gst_element_set_base_time (element, base_time);
+        /* set base time on child */
+        gst_element_set_base_time (child, base_time);
 
         /* set state now */
-        ret = gst_bin_element_set_state (bin, element, pending);
+        ret = gst_bin_element_set_state (bin, child, next);
 
         switch (ret) {
           case GST_STATE_CHANGE_SUCCESS:
-            GST_CAT_DEBUG (GST_CAT_STATES,
+            GST_CAT_INFO_OBJECT (GST_CAT_STATES, element,
                 "child '%s' changed state to %d(%s) successfully",
-                GST_ELEMENT_NAME (element), pending,
-                gst_element_state_get_name (pending));
+                GST_ELEMENT_NAME (child), next,
+                gst_element_state_get_name (next));
             break;
           case GST_STATE_CHANGE_ASYNC:
             GST_CAT_INFO_OBJECT (GST_CAT_STATES, element,
                 "child '%s' is changing state asynchronously",
-                GST_ELEMENT_NAME (element));
+                GST_ELEMENT_NAME (child));
             have_async = TRUE;
             break;
           case GST_STATE_CHANGE_FAILURE:
             GST_CAT_INFO_OBJECT (GST_CAT_STATES, element,
                 "child '%s' failed to go to state %d(%s)",
-                GST_ELEMENT_NAME (element),
-                pending, gst_element_state_get_name (pending));
-            gst_object_unref (element);
+                GST_ELEMENT_NAME (child),
+                next, gst_element_state_get_name (next));
+            gst_object_unref (child);
             goto done;
           case GST_STATE_CHANGE_NO_PREROLL:
-            GST_CAT_DEBUG (GST_CAT_STATES,
+            GST_CAT_INFO_OBJECT (GST_CAT_STATES, element,
                 "child '%s' changed state to %d(%s) successfully without preroll",
-                GST_ELEMENT_NAME (element), pending,
-                gst_element_state_get_name (pending));
+                GST_ELEMENT_NAME (child), next,
+                gst_element_state_get_name (next));
             have_no_preroll = TRUE;
             break;
           default:
             g_assert_not_reached ();
             break;
         }
-        gst_object_unref (element);
+        gst_object_unref (child);
         break;
       }
       case GST_ITERATOR_RESYNC:
@@ -1434,23 +1411,25 @@ 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:
+  gst_iterator_free (it);
+
   GST_CAT_DEBUG_OBJECT (GST_CAT_STATES, element,
       "done changing bin's state from %s to %s, now in %s, ret %d",
-      gst_element_state_get_name (old_state),
-      gst_element_state_get_name (pending),
+      gst_element_state_get_name (current),
+      gst_element_state_get_name (next),
       gst_element_state_get_name (GST_STATE (element)), ret);
 
-  gst_iterator_free (it);
-
   return ret;
 }
 
@@ -1537,21 +1516,23 @@ bin_bus_handler (GstBus * bus, GstMessag
 
       if (src) {
         gchar *name;
+	gboolean eos;
 
         name = gst_object_get_name (src);
         GST_DEBUG_OBJECT (bin, "got EOS message from %s", name);
         g_free (name);
 
         /* collect all eos messages from the children */
-        GST_LOCK (bin->child_bus);
+        GST_LOCK (bin);
         bin->eosed = g_list_prepend (bin->eosed, src);
-        GST_UNLOCK (bin->child_bus);
+	eos = is_eos (bin);
+        GST_UNLOCK (bin);
 
         /* if we are completely EOS, we forward an EOS message */
-        if (is_eos (bin)) {
+        if (eos) {
           GST_DEBUG_OBJECT (bin, "all sinks posted EOS");
-          gst_element_post_message (GST_ELEMENT (bin),
-              gst_message_new_eos (GST_OBJECT (bin)));
+          gst_element_post_message (GST_ELEMENT_CAST (bin),
+              gst_message_new_eos (GST_OBJECT_CAST (bin)));
         }
       } else {
         GST_DEBUG_OBJECT (bin, "got EOS message from (NULL), not processing");
@@ -1561,10 +1541,65 @@ bin_bus_handler (GstBus * bus, GstMessag
       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");
-      gst_element_post_message (GST_ELEMENT (bin), message);
+      gst_element_post_message (GST_ELEMENT_CAST (bin), message);
       break;
   }
 
Index: gst/gstbin.h
===================================================================
RCS file: /cvs/gstreamer/gstreamer/gst/gstbin.h,v
retrieving revision 1.79
diff -u -B -b -p -r1.79 gstbin.h
--- gst/gstbin.h	27 Sep 2005 16:16:39 -0000	1.79
+++ gst/gstbin.h	7 Oct 2005 18:05:20 -0000
@@ -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];
 };
Index: gst/gstbus.c
===================================================================
RCS file: /cvs/gstreamer/gstreamer/gst/gstbus.c,v
retrieving revision 1.37
diff -u -B -b -p -r1.37 gstbus.c
--- gst/gstbus.c	6 Oct 2005 09:49:42 -0000	1.37
+++ gst/gstbus.c	7 Oct 2005 18:05:20 -0000
@@ -455,18 +455,15 @@ gst_bus_pop (GstBus * bus)
 
   g_return_val_if_fail (GST_IS_BUS (bus), NULL);
 
-  GST_DEBUG_OBJECT (bus, "%d messages on the bus",
-      g_queue_get_length (bus->queue));
-
   g_mutex_lock (bus->queue_lock);
   message = g_queue_pop_head (bus->queue);
-  g_mutex_unlock (bus->queue_lock);
-
   if (message)
-    GST_DEBUG_OBJECT (bus, "pop on bus, got message %p, %s", message,
+    GST_DEBUG_OBJECT (bus, "pop from bus, have %d messages, got message %p, %s", 
+      	g_queue_get_length (bus->queue)+1, message,
         gst_message_type_get_name (GST_MESSAGE_TYPE (message)));
   else
-    GST_DEBUG_OBJECT (bus, "pop on bus, no message");
+    GST_DEBUG_OBJECT (bus, "pop from bus, no messages");
+  g_mutex_unlock (bus->queue_lock);
 
   return message;
 }
Index: gst/gstchildproxy.c
===================================================================
RCS file: /cvs/gstreamer/gstreamer/gst/gstchildproxy.c,v
retrieving revision 1.3
diff -u -B -b -p -r1.3 gstchildproxy.c
--- gst/gstchildproxy.c	20 Sep 2005 06:28:33 -0000	1.3
+++ gst/gstchildproxy.c	7 Oct 2005 18:05:20 -0000
@@ -61,31 +61,42 @@ static guint signals[LAST_SIGNAL] = { 0 
  *
  * Implementors can use #GstObject together with gst_object_get_name() 
  *
- * Returns: the child object or %NULL if not found
+ * Returns: the child object or %NULL if not found. Unref after usage.
+ *
+ * MT safe.
  */
 GstObject *
 gst_child_proxy_get_child_by_name (GstChildProxy * parent, const gchar * name)
 {
   guint count, i;
-  GstObject *object;
-  const gchar *object_name;
+  GstObject *object, *result;
+  gchar *object_name;
 
   g_return_val_if_fail (GST_IS_CHILD_PROXY (parent), NULL);
   g_return_val_if_fail (name != NULL, NULL);
 
+  result = NULL;
+
   count = gst_child_proxy_get_children_count (parent);
   for (i = 0; i < count; i++) {
-    object = gst_child_proxy_get_child_by_index (parent, i);
+    if (!(object = gst_child_proxy_get_child_by_index (parent, i)))
+      continue;
+
     object_name = gst_object_get_name (object);
     if (object_name == NULL) {
       g_warning ("child %u of parent %s has no name", i,
           GST_OBJECT_NAME (parent));
-      continue;
+      goto next;
+    }
+    if (g_str_equal (object_name, name)) {
+      g_free (object_name);
+      result = object;
+      break;
     }
-    if (g_str_equal (object_name, name))
-      return object;
+next:
+    gst_object_unref (object);
   }
-  return NULL;
+  return result;
 }
 
 /**
@@ -95,7 +106,10 @@ gst_child_proxy_get_child_by_name (GstCh
  *
  * Fetches a child by its number.
  *
- * Returns: the child object or %NULL if not found (index too high)
+ * Returns: the child object or %NULL if not found (index too high). Unref
+ * after usage.
+ *
+ * MT safe.
  */
 GstObject *
 gst_child_proxy_get_child_by_index (GstChildProxy * parent, guint index)
@@ -113,6 +127,8 @@ gst_child_proxy_get_child_by_index (GstC
  * Gets the number of child objects this parent contains.
  *
  * Returns: the number of child objects
+ *
+ * MT safe.
  */
 guint
 gst_child_proxy_get_children_count (GstChildProxy * parent)
@@ -132,7 +148,10 @@ gst_child_proxy_get_children_count (GstC
  * Looks up which object and #GParamSpec would be effected by the given @name.
  *
  * Returns: TRUE if @target and @pspec could be found. FALSE otherwise. In that 
- * case the values for @pspec and @target are not modified
+ * case the values for @pspec and @target are not modified. Unref @target after
+ * usage.
+ *
+ * MT safe.
  */
 gboolean
 gst_child_proxy_lookup (GstObject * object, const gchar * name,
@@ -144,21 +163,26 @@ gst_child_proxy_lookup (GstObject * obje
   g_return_val_if_fail (GST_IS_OBJECT (object), FALSE);
   g_return_val_if_fail (name != NULL, FALSE);
 
+  gst_object_ref (object);
+
   current = names = g_strsplit (name, "::", -1);
   while (current[1]) {
+    GstObject *next;
+
     if (!GST_IS_CHILD_PROXY (object)) {
       GST_INFO
           ("object %s is not a parent, so you cannot request a child by name %s",
           GST_OBJECT_NAME (object), current[0]);
       break;
     }
-    object =
-        gst_child_proxy_get_child_by_name (GST_CHILD_PROXY (object),
+    next = gst_child_proxy_get_child_by_name (GST_CHILD_PROXY (object),
         current[0]);
-    if (!object) {
+    if (!next) {
       GST_INFO ("no such object %s", current[0]);
       break;
     }
+    gst_object_unref (object);
+    object = next;
     current++;
   };
   if (current[1] == NULL) {
@@ -169,11 +193,14 @@ gst_child_proxy_lookup (GstObject * obje
     } else {
       if (pspec)
         *pspec = spec;
-      if (target)
+      if (target) {
+	gst_object_ref (object);
         *target = object;
+      }
       res = TRUE;
     }
   }
+  gst_object_unref (object);
   g_strfreev (names);
   return res;
 }
@@ -198,12 +225,20 @@ gst_child_proxy_get_property (GstObject 
   g_return_if_fail (name != NULL);
   g_return_if_fail (!G_IS_VALUE (value));
 
-  if (!gst_child_proxy_lookup (object, name, &target, &pspec)) {
+  if (!gst_child_proxy_lookup (object, name, &target, &pspec))
+    goto not_found;
+
+  g_object_get_property (G_OBJECT (target), pspec->name, value);
+  gst_object_unref (target);
+
+  return;
+
+not_found:
+  {
     g_warning ("cannot get property %s from object %s", name,
         GST_OBJECT_NAME (object));
     return;
   }
-  g_object_get_property (G_OBJECT (target), pspec->name, value);
 }
 
 /**
@@ -224,8 +259,6 @@ gst_child_proxy_get_valist (GstObject * 
 
   g_return_if_fail (G_IS_OBJECT (object));
 
-  g_object_ref (object);
-
   name = first_property_name;
 
   /* iterate over pairs */
@@ -239,8 +272,6 @@ gst_child_proxy_get_valist (GstObject * 
     g_value_unset (&value);
     name = va_arg (var_args, gchar *);
   }
-
-  g_object_unref (object);
 }
 
 /**
@@ -282,12 +313,19 @@ gst_child_proxy_set_property (GstObject 
   g_return_if_fail (name != NULL);
   g_return_if_fail (!G_IS_VALUE (value));
 
-  if (!gst_child_proxy_lookup (object, name, &target, &pspec)) {
+  if (!gst_child_proxy_lookup (object, name, &target, &pspec))
+    goto not_found;
+
+  g_object_set_property (G_OBJECT (target), pspec->name, value);
+  gst_object_unref (target);
+  return;
+
+not_found:
+  {
     g_warning ("cannot set property %s on object %s", name,
         GST_OBJECT_NAME (object));
     return;
   }
-  g_object_set_property (G_OBJECT (target), pspec->name, value);
 }
 
 /**
@@ -308,8 +346,6 @@ gst_child_proxy_set_valist (GstObject * 
 
   g_return_if_fail (G_IS_OBJECT (object));
 
-  g_object_ref (object);
-
   name = first_property_name;
 
   /* iterate over pairs */
@@ -320,21 +356,21 @@ gst_child_proxy_set_valist (GstObject * 
     if (!gst_child_proxy_lookup (object, name, &target, &pspec)) {
       g_warning ("no such property %s in object %s", name,
           GST_OBJECT_NAME (object));
-      g_object_unref (object);
+      continue;
     }
     g_value_init (&value, G_PARAM_SPEC_VALUE_TYPE (pspec));
     G_VALUE_COLLECT (&value, var_args, G_VALUE_NOCOPY_CONTENTS, &error);
     if (error) {
       g_warning ("error copying value: %s", error);
-      g_object_unref (object);
+      gst_object_unref (target);
       return;
     }
     g_object_set_property (G_OBJECT (target), pspec->name, &value);
+    gst_object_unref (target);
+
     g_value_unset (&value);
     name = va_arg (var_args, gchar *);
   }
-
-  g_object_unref (object);
 }
 
 /**
Index: gst/gstelement.c
===================================================================
RCS file: /cvs/gstreamer/gstreamer/gst/gstelement.c,v
retrieving revision 1.375
diff -u -B -b -p -r1.375 gstelement.c
--- gst/gstelement.c	7 Oct 2005 16:28:56 -0000	1.375
+++ gst/gstelement.c	7 Oct 2005 18:05:20 -0000
@@ -87,7 +87,6 @@
 /* Element signals and args */
 enum
 {
-  STATE_CHANGE,
   NEW_PAD,
   PAD_REMOVED,
   NO_MORE_PADS,
@@ -115,6 +114,8 @@ static void gst_element_finalize (GObjec
 
 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_get_state_func (GstElement * element,
     GstState * state, GstState * pending, GTimeVal * timeout);
 static void gst_element_set_bus_func (GstElement * element, GstBus * bus);
@@ -206,7 +207,7 @@ gst_element_class_init (GstElementClass 
       GST_DEBUG_FUNCPTR (gst_element_restore_thyself);
 #endif
 
-  klass->change_state = GST_DEBUG_FUNCPTR (gst_element_change_state);
+  klass->change_state = GST_DEBUG_FUNCPTR (gst_element_change_state_func);
   klass->get_state = GST_DEBUG_FUNCPTR (gst_element_get_state_func);
   klass->set_bus = GST_DEBUG_FUNCPTR (gst_element_set_bus_func);
   klass->numpadtemplates = 0;
@@ -236,9 +237,13 @@ gst_element_base_class_finalize (gpointe
 static void
 gst_element_init (GstElement * element)
 {
-  element->current_state = GST_STATE_NULL;
-  element->pending_state = GST_STATE_VOID_PENDING;
-  element->state_lock = g_mutex_new ();
+  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 ();
 }
 
@@ -350,7 +355,7 @@ gst_element_provides_clock (GstElement *
  *
  * MT safe.
  */
-GstClock *
+GstClock*
 gst_element_provide_clock (GstElement * element)
 {
   GstClock *result = NULL;
@@ -1558,6 +1563,8 @@ was_ok:
  * If this function returns FALSE, the state of element is undefined.
  *
  * Returns: TRUE, if the element's state could be synced to the parent's state.
+ *
+ * MT safe.
  */
 gboolean
 gst_element_sync_state_with_parent (GstElement * element)
@@ -1565,20 +1572,38 @@ gst_element_sync_state_with_parent (GstE
   GstElement *parent;
 
   g_return_val_if_fail (GST_IS_ELEMENT (element), FALSE);
-  parent = GST_ELEMENT (GST_ELEMENT_PARENT (element));
-  g_return_val_if_fail (GST_IS_BIN (parent), FALSE);
 
-  GST_CAT_DEBUG (GST_CAT_STATES, "syncing state of element %s (%s) to %s (%s)",
+  if ((parent = GST_ELEMENT_CAST (gst_element_get_parent (element)))) {
+    GstState parent_current, parent_pending;
+    GstStateChangeReturn ret;
+
+    GST_STATE_LOCK (parent);
+    parent_current = GST_STATE (parent);
+    parent_pending = GST_STATE_PENDING (parent);
+    GST_STATE_UNLOCK (parent);
+
+    GST_CAT_DEBUG (GST_CAT_STATES, "syncing state of element %s (%s) to %s (%s, %s)",
       GST_ELEMENT_NAME (element),
       gst_element_state_get_name (GST_STATE (element)),
       GST_ELEMENT_NAME (parent),
-      gst_element_state_get_name (GST_STATE (parent)));
+        gst_element_state_get_name (parent_current),
+        gst_element_state_get_name (parent_pending));
 
-  if (gst_element_set_state (element,
-          GST_STATE (parent)) == GST_STATE_CHANGE_FAILURE) {
+    ret = gst_element_set_state (element, parent_current);
+    if (ret == GST_STATE_CHANGE_FAILURE)
+      goto failed;
+
+    gst_object_unref (parent);
+
+    return TRUE;
+  }
+  return FALSE;
+
+  /* ERROR */
+failed:
+  {
     return FALSE;
   }
-  return TRUE;
 }
 
 /* MT safe */
@@ -1592,32 +1617,37 @@ gst_element_get_state_func (GstElement *
   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) {
     GTimeVal *timeval, abstimeout;
 
-    GST_CAT_INFO_OBJECT (GST_CAT_STATES, element, "wait for pending");
     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,
-          timeout->tv_sec * G_USEC_PER_SEC + timeout->tv_usec);
+      g_time_val_add (&abstimeout, add);
       timeval = &abstimeout;
     } else {
       timeval = NULL;
     }
+    GST_CAT_INFO_OBJECT (GST_CAT_STATES, element, "wait for pending");
     /* we have a pending state change, wait for it to complete */
     if (!GST_STATE_TIMED_WAIT (element, timeval)) {
       GST_CAT_INFO_OBJECT (GST_CAT_STATES, element, "timeout");
@@ -1647,10 +1677,10 @@ 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);
+      ret);
 
   GST_STATE_UNLOCK (element);
 
@@ -1720,7 +1750,8 @@ gst_element_abort_state (GstElement * el
 
   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
@@ -1730,7 +1760,7 @@ gst_element_abort_state (GstElement * el
         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);
   }
@@ -1741,39 +1771,87 @@ gst_element_abort_state (GstElement * el
  * @element: a #GstElement to commit the state of.
  *
  * Commit the state change of the element. This function is used
- * by elements that do asynchronous state changes.
+ * by elements that do asynchronous state changes, in which case
+ * they set @async to TRUE. The core will normally call this method
+ * automatically when an element returned SUCCESS from the state change
+ * function.
  *
  * This function can only be called with the STATE_LOCK held.
  *
  * 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);
+        old_state, next, pending);
     gst_element_post_message (element, message);
+
+    if (pending != GST_STATE_VOID_PENDING) {
+      GstStateChange transition;
+
+      /* calc new next state */
+      next = GST_STATE_GET_NEXT (current, pending);
+      /* create transition */
+      transition = GST_STATE_TRANSITION (current, next);
+
+      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));
+
+      ret = gst_element_change_state (element, transition);
+    }
+    else {
     GST_STATE_BROADCAST (element);
   }
+    GST_FLAG_UNSET (element, GST_ELEMENT_CHANGING_STATE);
+  }
+  return ret;
 }
 
 /**
@@ -1783,6 +1861,7 @@ gst_element_commit_state (GstElement * e
  * Brings the element to the lost state. The current state of the
  * element is copied to the pending state so that any call to
  * #gst_element_get_state() will return ASYNC.
+ *
  * This is mostly used for elements that lost their preroll buffer
  * in the PAUSED state after a flush, they become PAUSED again
  * if a new preroll buffer is queued.
@@ -1799,18 +1878,21 @@ gst_element_lost_state (GstElement * ele
   g_return_if_fail (GST_IS_ELEMENT (element));
 
   if (GST_STATE_PENDING (element) == GST_STATE_VOID_PENDING &&
-      !GST_STATE_ERROR (element)) {
-    GstState current_state = GST_STATE (element);
+      GST_STATE_RETURN (element) != GST_STATE_CHANGE_FAILURE) {
+    GstState current_state;
     GstMessage *message;
 
+    current_state = GST_STATE (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, current_state, current_state);
     gst_element_post_message (element, message);
   }
 }
@@ -1831,134 +1913,159 @@ gst_element_lost_state (GstElement * ele
 GstStateChangeReturn
 gst_element_set_state (GstElement * element, GstState state)
 {
-  GstElementClass *oclass;
-  GstState current;
-  GstStateChangeReturn return_val = GST_STATE_CHANGE_SUCCESS;
+  GstState current, next, old_pending;
   GstStateChangeReturn ret;
-  GstState pending;
-  GTimeVal tv;
+  GstStateChange transition;
 
   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, &current, &pending, &tv);
 
   GST_STATE_LOCK (element);
-  /* this is the 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_UNLOCK (element);
-      return GST_STATE_CHANGE_ASYNC;
-    }
-  }
+  GST_FLAG_SET (element, GST_ELEMENT_CHANGING_STATE);
 
-  /* start with the current state */
   current = GST_STATE (element);
+  next = GST_STATE_NEXT (element);
+  old_pending = GST_STATE_PENDING (element);
 
-  GST_CAT_INFO_OBJECT (GST_CAT_STATES, element, "setting state from %s to %s",
-      gst_element_state_get_name (current), gst_element_state_get_name (state));
-
-  oclass = GST_ELEMENT_GET_CLASS (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;
+  /* this is the (new) state we should go to */
+  GST_STATE_PENDING (element) = state;
 
-    /* calculate the pending state */
-    if (current < state)
-      pending = current + 1;
-    else if (current > state)
-      pending = current - 1;
-    else
-      pending = current;
+  GST_CAT_DEBUG_OBJECT (GST_CAT_STATES, element, 
+		  "current %s, old_pending %s, next %s",
+      gst_element_state_get_name (current),
+      gst_element_state_get_name (old_pending),
+      gst_element_state_get_name (next));
 
-    /* set the pending state variable */
-    GST_STATE_PENDING (element) = pending;
+  /* 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;
+    else if (next > state) {
+      current = next;
+    }
+  }
+  next = GST_STATE_GET_NEXT (current, state);
 
     GST_CAT_DEBUG_OBJECT (GST_CAT_STATES, element,
         "%s: setting state from %s to %s",
-        (pending != state ? "intermediate" : "final"),
+      (next != state ? "intermediate" : "final"),
         gst_element_state_get_name (current),
-        gst_element_state_get_name (pending));
+      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);
+
+  return ret;
+
+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 */
+static GstStateChangeReturn
+gst_element_change_state (GstElement * element, GstStateChange transition)
+{
+  GstElementClass *oclass;
+  GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
+  GstState current;
+  GstState next;
+
+  oclass = GST_ELEMENT_GET_CLASS (element);
+
+  /* start with the current state. */
+  current = GST_STATE_TRANSITION_CURRENT (transition);
+  next = GST_STATE_TRANSITION_NEXT (transition);
+
+  /* now we store the next state */
+  GST_STATE_NEXT (element) = next;
 
     /* call the state change function so it can set the state */
     if (oclass->change_state)
-      return_val = (oclass->change_state) (element, GST_STATE_CHANGE (element));
+    ret = (oclass->change_state) (element, transition);
     else
-      return_val = GST_STATE_CHANGE_FAILURE;
-
-    /* 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;
+    ret = GST_STATE_CHANGE_FAILURE;
 
-    switch (return_val) {
+  switch (ret) {
       case GST_STATE_CHANGE_FAILURE:
         GST_CAT_INFO_OBJECT (GST_CAT_STATES, element,
-            "have failed change_state return");
+          "have FAILURE change_state return");
         /* state change failure exits the loop */
         gst_element_abort_state (element);
-        goto exit;
+      break;
       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. */
+          "element will change state ASYNC");
+
+      /* if we go upwards, we give the app a change to wait for
+      * the preroll */
+      if (current < next) 
         goto exit;
+
+      /* 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 successfully");
+          "element changed state SUCCESS");
         /* 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");
+      ret = gst_element_commit_state (element);
         break;
       case GST_STATE_CHANGE_NO_PREROLL:
         GST_CAT_INFO_OBJECT (GST_CAT_STATES, element,
-            "element changed state successfully and can't preroll");
+          "element changed state NO_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");
+      ret = GST_STATE_CHANGE_NO_PREROLL;
         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);
-  }
-  while (current != state);
 
 exit:
-  GST_STATE_FINAL (element) = GST_STATE_VOID_PENDING;
-  GST_STATE_UNLOCK (element);
+  GST_STATE_RETURN (element) = ret;
 
-  GST_CAT_INFO_OBJECT (GST_CAT_STATES, element, "exit state change");
+  GST_CAT_INFO_OBJECT (GST_CAT_STATES, element, "exit state change %d", ret);
 
-  return return_val;
+  return ret;
 
   /* ERROR */
 invalid_return:
   {
-    GST_STATE_FINAL (element) = GST_STATE_VOID_PENDING;
-    GST_STATE_UNLOCK (element);
+    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",
-        return_val);
-    return GST_STATE_CHANGE_FAILURE;
+        ret);
+    return ret;
   }
 }
 
@@ -2036,24 +2143,24 @@ gst_element_pads_activate (GstElement * 
 
 /* is called with STATE_LOCK */
 static GstStateChangeReturn
-gst_element_change_state (GstElement * element, GstStateChange transition)
+gst_element_change_state_func (GstElement * element, GstStateChange transition)
 {
-  GstState state, pending;
+  GstState state, next;
   GstStateChangeReturn result = GST_STATE_CHANGE_SUCCESS;
 
   g_return_val_if_fail (GST_IS_ELEMENT (element), GST_STATE_CHANGE_FAILURE);
 
-  state = GST_STATE (element);
-  pending = 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 (pending == GST_STATE_VOID_PENDING || state == GST_STATE_PENDING (element))
+  if (next == GST_STATE_VOID_PENDING || state == next)
     goto was_ok;
 
   GST_CAT_LOG_OBJECT (GST_CAT_STATES, element,
       "default handler tries setting state from %s to %s (%04x)",
       gst_element_state_get_name (state),
-      gst_element_state_get_name (pending), transition);
+      gst_element_state_get_name (next), transition);
 
   switch (transition) {
     case GST_STATE_CHANGE_NULL_TO_READY:
@@ -2085,7 +2192,7 @@ gst_element_change_state (GstElement * e
        */
       g_warning ("Unhandled state change from %s to %s",
           gst_element_state_get_name (state),
-          gst_element_state_get_name (pending));
+          gst_element_state_get_name (next));
       break;
   }
   return result;
@@ -2095,10 +2202,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);
   }
 }
 
@@ -2160,7 +2264,7 @@ gst_element_finalize (GObject * object)
     g_cond_free (element->state_cond);
   element->state_cond = NULL;
   GST_STATE_UNLOCK (element);
-  g_mutex_free (element->state_lock);
+  g_static_rec_mutex_free (element->state_lock);
 
   GST_CAT_INFO_OBJECT (GST_CAT_REFCOUNTING, element, "finalize parent");
 
Index: gst/gstelement.h
===================================================================
RCS file: /cvs/gstreamer/gstreamer/gst/gstelement.h,v
retrieving revision 1.213
diff -u -B -b -p -r1.213 gstelement.h
--- gst/gstelement.h	5 Oct 2005 22:35:14 -0000	1.213
+++ gst/gstelement.h	7 Oct 2005 18:05:20 -0000
@@ -92,34 +92,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<<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)
-    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<<GST_STATE_PENDING(obj);
-}
-#define GST_STATE_CHANGE(obj) _gst_element_get_state_change(obj)
-#endif
 
-/* FIXME: How to deal with lost_state ? */
+/**
+ * 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)
+
 /**
  * GstStateChange:
  * @GST_STATE_CHANGE_NULL_TO_READY    : state change from NULL to READY
@@ -131,12 +132,12 @@ _gst_element_get_state_change (GstElemen
  */
 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;
 
 /**
@@ -154,6 +155,7 @@ typedef enum
   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;
 
@@ -272,13 +274,16 @@ G_STMT_START {								\
  * This function is used by the core.  It is taken while getting or setting
  * the state, during state changes, and while finalizing.
  */
-#define GST_STATE_LOCK(elem)                   g_mutex_lock(GST_STATE_GET_LOCK(elem))
-#define GST_STATE_TRYLOCK(elem)                g_mutex_trylock(GST_STATE_GET_LOCK(elem))
-#define GST_STATE_UNLOCK(elem)                 g_mutex_unlock(GST_STATE_GET_LOCK(elem))
+#define GST_STATE_LOCK(elem)                   g_static_rec_mutex_lock(GST_STATE_GET_LOCK(elem))
+#define GST_STATE_TRYLOCK(elem)                g_static_rec_mutex_trylock(GST_STATE_GET_LOCK(elem))
+#define GST_STATE_UNLOCK(elem)                 g_static_rec_mutex_unlock(GST_STATE_GET_LOCK(elem))
+#define GST_STATE_UNLOCK_FULL(elem)            g_static_rec_mutex_unlock_full(GST_STATE_GET_LOCK(elem))
+#define GST_STATE_LOCK_FULL(elem,t)            g_static_rec_mutex_lock_full(GST_STATE_GET_LOCK(elem), t)
 #define GST_STATE_GET_COND(elem)               (GST_ELEMENT_CAST(elem)->state_cond)
-#define GST_STATE_WAIT(elem)                   g_cond_wait (GST_STATE_GET_COND (elem), GST_STATE_GET_LOCK (elem))
-#define GST_STATE_TIMED_WAIT(elem, timeval)    g_cond_timed_wait (GST_STATE_GET_COND (elem), GST_STATE_GET_LOCK (elem),\
-		                                                timeval)
+#define GST_STATE_WAIT(elem)                   g_static_rec_cond_wait (GST_STATE_GET_COND (elem), \
+							GST_STATE_GET_LOCK (elem))
+#define GST_STATE_TIMED_WAIT(elem, timeval)    g_static_rec_cond_timed_wait (GST_STATE_GET_COND (elem), \
+							GST_STATE_GET_LOCK (elem), timeval)
 #define GST_STATE_SIGNAL(elem)                 g_cond_signal (GST_STATE_GET_COND (elem));
 #define GST_STATE_BROADCAST(elem)              g_cond_broadcast (GST_STATE_GET_COND (elem));
 
@@ -288,17 +293,13 @@ struct _GstElement
 
   /*< public >*/ /* with STATE_LOCK */
   /* element state */
-  GMutex               *state_lock;
+  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;
 
@@ -486,7 +487,7 @@ GstStateChangeReturn	gst_element_get_sta
 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 */
Index: gst/gstmessage.c
===================================================================
RCS file: /cvs/gstreamer/gstreamer/gst/gstmessage.c,v
retrieving revision 1.35
diff -u -B -b -p -r1.35 gstmessage.c
--- gst/gstmessage.c	7 Oct 2005 16:13:51 -0000	1.35
+++ gst/gstmessage.c	7 Oct 2005 18:05:20 -0000
@@ -354,6 +354,7 @@ gst_message_new_tag (GstObject * src, Gs
  * @src: The object originating the message.
  * @old: The previous state.
  * @new: The new (current) state.
+ * @pending: The pending (target) state.
  *
  * Create a state change message. This message is posted whenever an element changed
  * its state.
@@ -363,15 +364,19 @@ gst_message_new_tag (GstObject * src, Gs
  * MT safe.
  */
 GstMessage *
-gst_message_new_state_changed (GstObject * src, GstState old, GstState new)
+gst_message_new_state_changed (GstObject * src,
+		GstState old, GstState new, GstState pending)
 {
   GstMessage *message;
   GstStructure *s;
 
   message = gst_message_new (GST_MESSAGE_STATE_CHANGED, src);
 
-  s = gst_structure_new ("GstMessageState", "old-state", GST_TYPE_STATE,
-      (gint) old, "new-state", GST_TYPE_STATE, (gint) new, NULL);
+  s = gst_structure_new ("GstMessageState", 
+	"old-state", GST_TYPE_STATE, (gint) old, 
+	"new-state", GST_TYPE_STATE, (gint) new, 
+        "pending-state", GST_TYPE_STATE, (gint) pending, 
+        NULL);
   gst_structure_set_parent_refcount (s, &message->mini_object.refcount);
   message->structure = s;
 
@@ -545,6 +550,9 @@ gst_message_parse_tag (GstMessage * mess
 /**
  * gst_message_parse_state_changed:
  * @message: A valid #GstMessage of type GST_MESSAGE_STATE_CHANGED.
+ * @old: The previous state.
+ * @new: The new (current) state.
+ * @pending: The pending (target) state.
  *
  * Extracts the old and new states from the GstMessage.
  *
@@ -552,17 +560,20 @@ gst_message_parse_tag (GstMessage * mess
  */
 void
 gst_message_parse_state_changed (GstMessage * message, GstState * old,
-    GstState * new)
+    GstState * new, GstState *pending)
 {
   g_return_if_fail (GST_IS_MESSAGE (message));
   g_return_if_fail (GST_MESSAGE_TYPE (message) == GST_MESSAGE_STATE_CHANGED);
 
-  if (!gst_structure_get_enum (message->structure, "old-state",
-          GST_TYPE_STATE, (gint *) old))
-    g_assert_not_reached ();
-  if (!gst_structure_get_enum (message->structure, "new-state",
-          GST_TYPE_STATE, (gint *) new))
-    g_assert_not_reached ();
+  if (old)
+    gst_structure_get_enum (message->structure, "old-state",
+          GST_TYPE_STATE, (gint *) old);
+  if (new)
+    gst_structure_get_enum (message->structure, "new-state",
+          GST_TYPE_STATE, (gint *) new);
+  if (pending)
+    gst_structure_get_enum (message->structure, "pending-state",
+          GST_TYPE_STATE, (gint *) pending);
 }
 
 /**
Index: gst/gstmessage.h
===================================================================
RCS file: /cvs/gstreamer/gstreamer/gst/gstmessage.h,v
retrieving revision 1.21
diff -u -B -b -p -r1.21 gstmessage.h
--- gst/gstmessage.h	7 Oct 2005 16:13:51 -0000	1.21
+++ gst/gstmessage.h	7 Oct 2005 18:05:20 -0000
@@ -145,7 +145,7 @@ GstMessage *	gst_message_new_error 		(Gs
 GstMessage *	gst_message_new_warning 	(GstObject * src, GError * error, gchar * debug);
 GstMessage *	gst_message_new_tag 		(GstObject * src, GstTagList * tag_list);
 GstMessage *	gst_message_new_state_changed 	(GstObject * src, GstState old_state,
-                                                 GstState new_state);
+                                                 GstState new_state, GstState pending);
 GstMessage *	gst_message_new_segment_start 	(GstObject * src, GstClockTime timestamp);
 GstMessage *	gst_message_new_segment_done 	(GstObject * src, GstClockTime timestamp);
 GstMessage *	gst_message_new_application	(GstObject * src, GstStructure * structure);
@@ -158,7 +158,7 @@ void		gst_message_parse_error		(GstMessa
 void		gst_message_parse_warning	(GstMessage *message, GError **gerror, gchar **debug);
 void		gst_message_parse_tag		(GstMessage *message, GstTagList **tag_list);
 void		gst_message_parse_state_changed	(GstMessage *message, GstState *old_state,
-                                                 GstState *new_state);
+                                                 GstState *new_state, GstState *pending);
 void 		gst_message_parse_segment_start (GstMessage *message, GstClockTime *timestamp);
 void		gst_message_parse_segment_done 	(GstMessage *message, GstClockTime *timestamp);
 
Index: gst/gstpad.c
===================================================================
RCS file: /cvs/gstreamer/gstreamer/gst/gstpad.c,v
retrieving revision 1.449
diff -u -B -b -p -r1.449 gstpad.c
--- gst/gstpad.c	6 Oct 2005 14:01:44 -0000	1.449
+++ gst/gstpad.c	7 Oct 2005 18:05:20 -0000
@@ -2410,7 +2410,8 @@ gst_pad_alloc_buffer (GstPad * pad, guin
         "calling bufferallocfunc &%s (@%p) of peer pad %s:%s for size %d offset NONE",
         GST_DEBUG_FUNCPTR_NAME (bufferallocfunc),
         &bufferallocfunc, GST_DEBUG_PAD_NAME (peer), size);
-  } else {
+  }
+  else {
     GST_CAT_DEBUG_OBJECT (GST_CAT_PADS, pad,
         "calling bufferallocfunc &%s (@%p) of peer pad %s:%s for size %d offset %"
         G_GUINT64_FORMAT, GST_DEBUG_FUNCPTR_NAME (bufferallocfunc),
Index: gst/gstpipeline.c
===================================================================
RCS file: /cvs/gstreamer/gstreamer/gst/gstpipeline.c,v
retrieving revision 1.112
diff -u -B -b -p -r1.112 gstpipeline.c
--- gst/gstpipeline.c	29 Sep 2005 09:39:36 -0000	1.112
+++ gst/gstpipeline.c	7 Oct 2005 18:05:20 -0000
@@ -54,12 +54,11 @@ enum
 };
 
 #define DEFAULT_DELAY 0
-#define DEFAULT_PLAY_TIMEOUT  (2*GST_SECOND)
+
 enum
 {
-  ARG_0,
-  ARG_DELAY,
-  ARG_PLAY_TIMEOUT,
+  PROP_0,
+  PROP_DELAY,
   /* FILL ME */
 };
 
@@ -130,23 +129,18 @@ gst_pipeline_class_init (gpointer g_clas
   gobject_class->set_property = GST_DEBUG_FUNCPTR (gst_pipeline_set_property);
   gobject_class->get_property = GST_DEBUG_FUNCPTR (gst_pipeline_get_property);
 
-  g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_DELAY,
+  g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_DELAY,
       g_param_spec_uint64 ("delay", "Delay",
           "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), ARG_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);
 
   gstelement_class->send_event = GST_DEBUG_FUNCPTR (gst_pipeline_send_event);
   gstelement_class->change_state =
       GST_DEBUG_FUNCPTR (gst_pipeline_change_state);
-  gstelement_class->provide_clock =
-      GST_DEBUG_FUNCPTR (gst_pipeline_provide_clock_func);
+  gstelement_class->provide_clock = GST_DEBUG_FUNCPTR (gst_pipeline_provide_clock_func);
 }
 
 static void
@@ -156,7 +150,6 @@ gst_pipeline_init (GTypeInstance * insta
   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);
@@ -183,12 +176,9 @@ gst_pipeline_set_property (GObject * obj
 
   GST_LOCK (pipeline);
   switch (prop_id) {
-    case ARG_DELAY:
+    case PROP_DELAY:
       pipeline->delay = g_value_get_uint64 (value);
       break;
-    case ARG_PLAY_TIMEOUT:
-      pipeline->play_timeout = g_value_get_uint64 (value);
-      break;
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
       break;
@@ -204,12 +194,9 @@ gst_pipeline_get_property (GObject * obj
 
   GST_LOCK (pipeline);
   switch (prop_id) {
-    case ARG_DELAY:
+    case PROP_DELAY:
       g_value_set_uint64 (value, pipeline->delay);
       break;
-    case ARG_PLAY_TIMEOUT:
-      g_value_set_uint64 (value, pipeline->play_timeout);
-      break;
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
       break;
@@ -240,9 +227,10 @@ do_pipeline_seek (GstElement * element, 
     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);
 
@@ -300,7 +288,6 @@ gst_pipeline_change_state (GstElement * 
 {
   GstStateChangeReturn result = GST_STATE_CHANGE_SUCCESS;
   GstPipeline *pipeline = GST_PIPELINE (element);
-  GstClockTime play_timeout;
   GstClock *clock;
 
   switch (transition) {
@@ -385,40 +372,6 @@ gst_pipeline_change_state (GstElement * 
       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;
 }
 
@@ -506,8 +459,7 @@ gst_pipeline_provide_clock_func (GstElem
   } else {
     GST_UNLOCK (pipeline);
     clock =
-        GST_ELEMENT_CLASS (parent_class)->
-        provide_clock (GST_ELEMENT (pipeline));
+        GST_ELEMENT_CLASS (parent_class)->provide_clock (GST_ELEMENT (pipeline));
     /* no clock, use a system clock */
     if (!clock) {
       clock = gst_system_clock_obtain ();
Index: gst/gstpipeline.h
===================================================================
RCS file: /cvs/gstreamer/gstreamer/gst/gstpipeline.h,v
retrieving revision 1.30
diff -u -B -b -p -r1.30 gstpipeline.h
--- gst/gstpipeline.h	27 Sep 2005 20:40:35 -0000	1.30
+++ gst/gstpipeline.h	7 Oct 2005 18:05:20 -0000
@@ -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];
Index: gst/gstutils.c
===================================================================
RCS file: /cvs/gstreamer/gstreamer/gst/gstutils.c,v
retrieving revision 1.94
diff -u -B -b -p -r1.94 gstutils.c
--- gst/gstutils.c	5 Oct 2005 21:34:42 -0000	1.94
+++ gst/gstutils.c	7 Oct 2005 18:05:20 -0000
@@ -769,6 +769,7 @@ gst_element_state_get_name (GstState sta
       return "PAUSED";
       break;
     default:
+      /* This is a memory leak */
       return g_strdup_printf ("UNKNOWN!(%d)", state);
 #endif
   }
@@ -1815,6 +1816,61 @@ gst_object_default_error (GstObject * so
   g_free (name);
 }
 
+#if GLIB_SIZEOF_SYSTEM_THREAD == SIZEOF_VOID_P
+# define g_system_thread_equal_simple(thread1, thread2)                 \
+   ((thread1).dummy_pointer == (thread2).dummy_pointer)
+# define g_system_thread_assign(dest, src)                              \
+   ((dest).dummy_pointer = (src).dummy_pointer)
+#else /* GLIB_SIZEOF_SYSTEM_THREAD != SIZEOF_VOID_P */
+# define g_system_thread_equal_simple(thread1, thread2)                 \
+   (memcmp (&(thread1), &(thread2), GLIB_SIZEOF_SYSTEM_THREAD) == 0)
+# define g_system_thread_assign(dest, src)                              \
+   (memcpy (&(dest), &(src), GLIB_SIZEOF_SYSTEM_THREAD))
+#endif /* GLIB_SIZEOF_SYSTEM_THREAD == SIZEOF_VOID_P */
+
+#define g_system_thread_equal(thread1, thread2)                         \
+  (g_thread_functions_for_glib_use.thread_equal ?                       \
+   g_thread_functions_for_glib_use.thread_equal (&(thread1), &(thread2)) :\
+   g_system_thread_equal_simple((thread1), (thread2)))
+
+static GSystemThread zero_thread;
+
+gboolean 
+g_static_rec_cond_timed_wait (GCond *cond, 
+		GStaticRecMutex *mutex, GTimeVal *end_time)
+{
+  GMutex *smutex;
+  guint depth;
+  GSystemThread self;
+  gboolean res;
+
+  if (!g_thread_supported ())
+    return FALSE;
+
+  G_THREAD_UF (thread_self, (&self));
+
+  g_return_val_if_fail (g_system_thread_equal (self, mutex->owner), FALSE);
+
+  depth = mutex->depth;
+
+  g_system_thread_assign (mutex->owner, zero_thread);
+  mutex->depth = 0;
+  smutex = g_static_mutex_get_mutex (&mutex->mutex);
+
+  res = g_cond_timed_wait (cond, smutex, end_time);
+
+  g_system_thread_assign (mutex->owner, self);
+  mutex->depth = depth;
+
+  return res;
+}
+
+void
+g_static_rec_cond_wait (GCond *cond, GStaticRecMutex *mutex)
+{
+  g_static_rec_cond_timed_wait (cond, mutex, NULL);
+}
+
 /**
  * gst_bin_add_many:
  * @bin: the bin to add the elements to
@@ -1872,154 +1928,6 @@ gst_bin_remove_many (GstBin * bin, GstEl
 }
 
 static void
-get_state_func (GstElement * element, gpointer unused)
-{
-  GstStateChangeReturn ret = GST_STATE_CHANGE_ASYNC;
-
-  GST_CAT_INFO_OBJECT (GST_CAT_STATES, element,
-      "new thread waiting on state change");
-
-  /* wait indefinitely */
-  while (ret == GST_STATE_CHANGE_ASYNC)
-    ret = gst_element_get_state (element, NULL, NULL, NULL);
-
-  GST_CAT_INFO_OBJECT (GST_CAT_STATES, element,
-      "thread done waiting on state change");
-
-  gst_object_unref (element);
-}
-
-/**
- * gst_bin_watch_for_state_change:
- * @bin: the bin to watch for state changes
- *
- * Spawns a thread calling gst_element_get_state on @bin with infinite timeout.
- *
- * In practice this is done because if a bin returns %GST_STATE_CHANGE_ASYNC
- * from a state change, it will not commit its state until someone calls
- * gst_element_get_state() on it. Thus having another thread checking the bin's
- * state will ensure that a state-changed message gets posted on the bus
- * eventually.
- *
- * This function is admittedly a bit of a hack. Bins should always post
- * messages. However this behavior was broken out into this function to avoid
- * spawning threads when scrubbing, when the bin's state is changing quickly and
- * asynchronously.
- */
-void
-gst_bin_watch_for_state_change (GstBin * bin)
-{
-  static GThreadPool *pool = NULL;
-  static GStaticMutex mutex = G_STATIC_MUTEX_INIT;
-
-  g_static_mutex_lock (&mutex);
-  if (pool == NULL)
-    pool = g_thread_pool_new ((GFunc) get_state_func, NULL, -1, FALSE, NULL);
-  g_static_mutex_unlock (&mutex);
-
-  g_thread_pool_push (pool, gst_object_ref (bin), NULL);
-}
-
-struct _GstAsyncThreadData
-{
-  GstElement *element;
-  GstState state;
-};
-
-static void
-set_state_async_thread_func (struct _GstAsyncThreadData *data, gpointer unused)
-{
-  GstState current, pending;
-  GstStateChangeReturn ret = GST_STATE_CHANGE_ASYNC;
-
-  GST_CAT_INFO_OBJECT (GST_CAT_STATES, data->element,
-      "new thread ensuring state change to %s",
-      gst_element_state_get_name (data->state));
-
-  while (TRUE) {
-    /* wait indefinitely */
-    ret = gst_element_get_state (data->element, &current, &pending, NULL);
-    GST_CAT_INFO_OBJECT (GST_CAT_STATES, data->element,
-        "get_state returned %d, current %s, pending %s", ret,
-        gst_element_state_get_name (current),
-        gst_element_state_get_name (pending));
-
-    /* can only be SUCCESS or FAILURE */
-    if (ret == GST_STATE_CHANGE_FAILURE) {
-      /* we can only break, hopefully an error message was posted as well */
-      GST_CAT_INFO_OBJECT (GST_CAT_STATES, data->element,
-          "FAILURE during state change");
-      break;
-    } else if (ret == GST_STATE_CHANGE_SUCCESS) {
-      if (current == data->state) {
-        GST_CAT_INFO_OBJECT (GST_CAT_STATES, data->element,
-            "successfully reached final state");
-        break;
-      }
-      GST_CAT_INFO_OBJECT (GST_CAT_STATES, data->element,
-          "setting target state %s again",
-          gst_element_state_get_name (data->state));
-      gst_element_set_state (data->element, data->state);
-    } else {
-      g_assert_not_reached ();
-    }
-  }
-
-  GST_CAT_INFO_OBJECT (GST_CAT_STATES, data->element,
-      "thread done waiting on state change");
-
-  gst_object_unref (data->element);
-  g_free (data);
-}
-
-/**
- * gst_element_set_state_async:
- * @element: a #GstElement to change state of
- * @state: the element's new #GstState
- *
- * Sets the state of the element. This function will try to set the
- * requested state by going through all the intermediary states and calling
- * the class's state change function for each.  If the state change returns
- * #GST_STATE_CHANGE_ASYNC at any time, a thread will be started to
- * monitor the state change and make sure the element is brought to the
- * requested state.
- *
- * Returns: Result of the state change using #GstStateChangeReturn.
- *
- * MT safe.
- */
-GstStateChangeReturn
-gst_element_set_state_async (GstElement * element, GstState state)
-{
-  GstStateChangeReturn ret;
-  struct _GstAsyncThreadData *data;
-
-  ret = gst_element_set_state (element, state);
-  if (ret == GST_STATE_CHANGE_ASYNC) {
-    static GThreadPool *pool = NULL;
-    static GStaticMutex mutex = G_STATIC_MUTEX_INIT;
-
-    GST_CAT_INFO_OBJECT (GST_CAT_STATES, element,
-        "starting new thread to ensure state change to %s",
-        gst_element_state_get_name (state));
-    g_static_mutex_lock (&mutex);
-    if (pool == NULL)
-      pool = g_thread_pool_new ((GFunc) set_state_async_thread_func,
-          NULL, -1, FALSE, NULL);
-    g_static_mutex_unlock (&mutex);
-
-    data = g_new0 (struct _GstAsyncThreadData, 1);
-
-    gst_object_ref (element);
-    data->element = element;
-    data->state = state;
-    g_thread_pool_push (pool, data, NULL);
-  }
-
-  return ret;
-}
-
-static void
 gst_element_populate_std_props (GObjectClass * klass, const gchar * prop_name,
     guint arg_id, GParamFlags flags)
 {
Index: gst/gstutils.h
===================================================================
RCS file: /cvs/gstreamer/gstreamer/gst/gstutils.h,v
retrieving revision 1.52
diff -u -B -b -p -r1.52 gstutils.h
--- gst/gstutils.h	27 Sep 2005 20:40:35 -0000	1.52
+++ gst/gstutils.h	7 Oct 2005 18:05:20 -0000
@@ -329,6 +329,12 @@ GST_BOILERPLATE_FULL (type, type_as_func
 void			gst_object_default_error	(GstObject * source,
 							 GError * error, gchar * debug);
 
+/* rec mutex */
+void     		g_static_rec_cond_wait        	(GCond                *cond,
+		                                         GStaticRecMutex      *mutex);
+gboolean 		g_static_rec_cond_timed_wait  	(GCond                *cond,
+		                                         GStaticRecMutex      *mutex,
+						         GTimeVal             *end_time);
 
 /* element functions */
 GstFlowReturn		gst_element_abort_preroll	(GstElement *element);
@@ -360,8 +366,6 @@ void                    gst_element_unli
 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);
@@ -399,7 +403,6 @@ gboolean                gst_pad_query_co
 /* 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: gst/base/gstbasesink.c
===================================================================
RCS file: /cvs/gstreamer/gstreamer/gst/base/gstbasesink.c,v
retrieving revision 1.60
diff -u -B -b -p -r1.60 gstbasesink.c
--- gst/base/gstbasesink.c	30 Sep 2005 08:00:12 -0000	1.60
+++ gst/base/gstbasesink.c	7 Oct 2005 18:05:20 -0000
@@ -432,6 +432,7 @@ gst_base_sink_preroll_queue_flush (GstBa
   }
   /* we can't have EOS anymore now */
   basesink->eos = FALSE;
+  basesink->eos_queued = FALSE;
   basesink->preroll_queued = 0;
   basesink->buffers_queued = 0;
   basesink->events_queued = 0;
@@ -460,6 +461,7 @@ gst_base_sink_handle_object (GstBaseSink
     switch (GST_EVENT_TYPE (obj)) {
       case GST_EVENT_EOS:
         basesink->preroll_queued++;
+        basesink->eos_queued = TRUE;
         basesink->eos = TRUE;
         break;
       case GST_EVENT_NEWSEGMENT:
@@ -475,6 +477,14 @@ gst_base_sink_handle_object (GstBaseSink
 
         basesink->have_newsegment = TRUE;
 
+	/* any other format with 0 also gives time 0, thhe other values are
+	 * invalid as time though. */
+        if (format != GST_FORMAT_TIME && segment_start == 0) {
+          format = GST_FORMAT_TIME;
+	  segment_stop = -1;
+	  basesink->segment_base = -1;
+	}
+
         if (format != GST_FORMAT_TIME) {
           GST_DEBUG_OBJECT (basesink,
               "received non time %d NEW_SEGMENT %" G_GINT64_FORMAT
@@ -612,6 +622,7 @@ gst_base_sink_handle_object (GstBaseSink
 
   if (length == 1) {
     gint t;
+    GstTask *task;
 
     basesink->have_preroll = TRUE;
     /* we are prerolling */
@@ -626,14 +637,27 @@ gst_base_sink_handle_object (GstBaseSink
       g_warning ("STREAM_LOCK should have been locked !!");
     }
 
-    /* now we commit our state */
+    /* 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);
 
     /* reacquire stream lock, pad could be flushing now */
-    /* FIXME in glib, if t==0, the lock is still taken... hmmm */
+    /* FIXME in glib, if t==0, the lock is still taken... hmmm.. bug #317802 */
     if (t > 0)
       GST_STREAM_LOCK_FULL (pad, t);
 
@@ -732,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);
@@ -812,18 +842,19 @@ gst_base_sink_event (GstPad * pad, GstEv
       if (bclass->event)
         bclass->event (basesink, event);
 
+      GST_LOCK (basesink);
+      basesink->flushing = TRUE;
+      if (basesink->clock_id) {
+        gst_clock_id_unschedule (basesink->clock_id);
+      }
+      GST_UNLOCK (basesink);
+
       GST_PREROLL_LOCK (pad);
       /* we need preroll after the flush */
       GST_DEBUG_OBJECT (basesink, "flushing, need preroll after flush");
       basesink->need_preroll = TRUE;
       /* unlock from a possible state change/preroll */
       gst_base_sink_preroll_queue_flush (basesink, pad);
-
-      GST_LOCK (basesink);
-      if (basesink->clock_id) {
-        gst_clock_id_unschedule (basesink->clock_id);
-      }
-      GST_UNLOCK (basesink);
       GST_PREROLL_UNLOCK (pad);
 
       /* and we need to commit our state again on the next
@@ -843,6 +874,9 @@ gst_base_sink_event (GstPad * pad, GstEv
       /* now we are completely unblocked and the _chain method
        * will return */
       GST_STREAM_LOCK (pad);
+      GST_LOCK (basesink);
+      basesink->flushing = FALSE;
+      GST_UNLOCK (basesink);
       /* we need new segment info after the flush. */
       basesink->segment_start = -1;
       basesink->segment_stop = -1;
@@ -891,19 +925,28 @@ static GstClockReturn
 gst_base_sink_wait (GstBaseSink * basesink, GstClockTime time)
 {
   GstClockReturn ret;
+  GstClockID id;
+
+  if (basesink->flushing) {
+    g_print ("flushing\n");
+    return GST_CLOCK_UNSCHEDULED;
+  }
 
   /* clock_id should be NULL outside of this function */
   g_assert (basesink->clock_id == NULL);
   g_assert (GST_CLOCK_TIME_IS_VALID (time));
 
-  basesink->clock_id = gst_clock_new_single_shot_id (basesink->clock, time);
+  id = gst_clock_new_single_shot_id (basesink->clock, time);
 
+  basesink->clock_id = id;
   /* release the object lock while waiting */
   GST_UNLOCK (basesink);
-  ret = gst_clock_id_wait (basesink->clock_id, NULL);
+
+  ret = gst_clock_id_wait (id, NULL);
+
   GST_LOCK (basesink);
 
-  gst_clock_id_unref (basesink->clock_id);
+  gst_clock_id_unref (id);
   basesink->clock_id = NULL;
 
   return ret;
@@ -1056,6 +1099,7 @@ gst_base_sink_handle_event (GstBaseSink 
         GST_DEBUG_OBJECT (basesink, "Now posting EOS");
         gst_element_post_message (GST_ELEMENT (basesink),
             gst_message_new_eos (GST_OBJECT (basesink)));
+	basesink->eos_queued = FALSE;
       }
       GST_PREROLL_UNLOCK (basesink->sinkpad);
       break;
@@ -1413,7 +1457,6 @@ gst_base_sink_change_state (GstElement *
       ret = GST_STATE_CHANGE_ASYNC;
       break;
     case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
-    {
       GST_PREROLL_LOCK (basesink->sinkpad);
       /* if we have EOS, we should empty the queue now as there will
        * be no more data received in the chain function.
@@ -1421,7 +1464,16 @@ gst_base_sink_change_state (GstElement *
        * we are pushing and syncing the buffers, better start a new
        * thread to do this. */
       if (basesink->eos) {
+	gboolean do_eos = !basesink->eos_queued;
+
         gst_base_sink_preroll_queue_empty (basesink, basesink->sinkpad);
+
+	/* need to post EOS message here if it was not in the preroll queue */
+	if (do_eos) {
+          GST_DEBUG_OBJECT (basesink, "Now posting EOS");
+          gst_element_post_message (GST_ELEMENT (basesink),
+              gst_message_new_eos (GST_OBJECT (basesink)));
+	}
       } else if (!basesink->have_preroll) {
         /* don't need preroll, but do queue a commit_state */
         GST_DEBUG_OBJECT (basesink,
@@ -1440,7 +1492,6 @@ gst_base_sink_change_state (GstElement *
       }
       GST_PREROLL_UNLOCK (basesink->sinkpad);
       break;
-    }
     default:
       break;
   }
@@ -1478,7 +1529,7 @@ gst_base_sink_change_state (GstElement *
        * 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/base/gstbasesink.h
===================================================================
RCS file: /cvs/gstreamer/gstreamer/gst/base/gstbasesink.h,v
retrieving revision 1.21
diff -u -B -b -p -r1.21 gstbasesink.h
--- gst/base/gstbasesink.h	20 Sep 2005 12:05:47 -0000	1.21
+++ gst/base/gstbasesink.h	7 Oct 2005 18:05:20 -0000
@@ -72,6 +72,7 @@ struct _GstBaseSink {
   gint		 buffers_queued;
   gint		 events_queued;
   gboolean       eos;
+  gboolean       eos_queued;
   gboolean       need_preroll;
   gboolean       have_preroll;
   gboolean       playing_async;
@@ -93,6 +94,7 @@ struct _GstBaseSink {
   GstClockID     clock_id;
   GstClockTime   end_time;
   gboolean       sync;
+  gboolean	 flushing;
 
   /*< private >*/
   gpointer       _gst_reserved[GST_PADDING];
Index: gst/parse/grammar.y
===================================================================
RCS file: /cvs/gstreamer/gstreamer/gst/parse/grammar.y,v
retrieving revision 1.61
diff -u -B -b -p -r1.61 grammar.y
--- gst/parse/grammar.y	2 Sep 2005 15:41:58 -0000	1.61
+++ gst/parse/grammar.y	7 Oct 2005 18:05:20 -0000
@@ -267,6 +267,7 @@ gst_parse_element_set (gchar *value, Gst
     if (!gst_value_deserialize (&v, pos))
       goto error;
     g_object_set_property (G_OBJECT (target), pspec->name, &v);
+    gst_object_unref (target);
   } else { 
     SET_ERROR (((graph_t *) graph)->error, GST_PARSE_ERROR_NO_SUCH_PROPERTY, _("no property \"%s\" in element \"%s\""), value, GST_ELEMENT_NAME (element)); 
   }
@@ -280,6 +281,7 @@ out:
   return;
   
 error:
+  gst_object_unref (target);
   SET_ERROR (((graph_t *) graph)->error, GST_PARSE_ERROR_COULD_NOT_SET_PROPERTY,
          _("could not set property \"%s\" in element \"%s\" to \"%s\""), 
 	 value, GST_ELEMENT_NAME (element), pos); 
Index: tests/sched/Makefile.am
===================================================================
RCS file: /cvs/gstreamer/gstreamer/tests/sched/Makefile.am,v
retrieving revision 1.11
diff -u -B -b -p -r1.11 Makefile.am
--- tests/sched/Makefile.am	4 May 2004 12:38:35 -0000	1.11
+++ tests/sched/Makefile.am	7 Oct 2005 18:05:20 -0000
@@ -1,7 +1,7 @@
 if GST_DISABLE_LOADSAVE
 noinst_PROGRAMS =
 else
-noinst_PROGRAMS = runxml dynamic-pipeline sched-stress interrupt1 interrupt2 interrupt3
+noinst_PROGRAMS = runxml dynamic-pipeline sched-stress interrupt1 interrupt2 interrupt3 sort
 endif
 
 dynamic_pipeline_SOURCES = dynamic-pipeline.c 
Index: tools/gst-launch.c
===================================================================
RCS file: /cvs/gstreamer/gstreamer/tools/gst-launch.c,v
retrieving revision 1.87
diff -u -B -b -p -r1.87 gst-launch.c
--- tools/gst-launch.c	7 Oct 2005 15:13:25 -0000	1.87
+++ tools/gst-launch.c	7 Oct 2005 18:05:20 -0000
@@ -377,7 +377,8 @@ event_loop (GstElement * pipeline, gbool
       const GstStructure *s;
 
       s = gst_message_get_structure (message);
-      g_print (_("Got Message from element \"%s\"\n"),
+
+      g_print (_("Got Message from element \"%s\": "),
           GST_STR_NULL (GST_ELEMENT_NAME (GST_MESSAGE_SRC (message))));
       if (s) {
         gchar *sstr;
@@ -386,6 +387,8 @@ event_loop (GstElement * pipeline, gbool
         g_print ("%s\n", sstr);
         g_free (sstr);
       }
+      else
+        g_print ("no details\n");
     }
 
     switch (GST_MESSAGE_TYPE (message)) {
@@ -438,9 +441,9 @@ event_loop (GstElement * pipeline, gbool
         return TRUE;
       }
       case GST_MESSAGE_STATE_CHANGED:{
-        GstState old, new;
+        GstState old, new, pending;
 
-        gst_message_parse_state_changed (message, &old, &new);
+        gst_message_parse_state_changed (message, &old, &new, &pending);
         if (!(old == GST_STATE_PLAYING && new == GST_STATE_PAUSED &&
                 GST_MESSAGE_SRC (message) == GST_OBJECT (pipeline))) {
           gst_message_unref (message);
@@ -604,8 +607,12 @@ main (int argc, char *argv[])
       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:
