check/: Check fixes, use API as stated in design docs, remove hacks.
authorWim Taymans <wim.taymans@gmail.com>
Mon, 10 Oct 2005 16:38:26 +0000 (16:38 +0000)
committerWim Taymans <wim.taymans@gmail.com>
Mon, 10 Oct 2005 16:38:26 +0000 (16:38 +0000)
Original commit message from CVS:
* check/Makefile.am:
* check/generic/states.c: (GST_START_TEST):
* check/gst/gstbin.c: (GST_START_TEST):
* check/gst/gstpipeline.c: (GST_START_TEST), (gst_pipeline_suite):
* check/states/sinks.c: (GST_START_TEST):
* check/states/sinks2.c: (GST_START_TEST), (gst_object_suite),
(main):
Check fixes, use API as stated in design docs, remove hacks.

* gst/base/gstbasesink.c: (gst_base_sink_handle_object),
(gst_base_sink_change_state):
Catch stopping our task while we're shutting down.

* gst/gstbin.c: (gst_bin_init), (gst_bin_add_func),
(gst_bin_remove_func), (gst_bin_get_state_func),
(gst_bin_recalc_state), (gst_bin_change_state_func),
(bin_bus_handler):
* gst/gstbin.h:
* gst/gstelement.c: (gst_element_init),
(gst_element_get_state_func), (gst_element_abort_state),
(gst_element_commit_state), (gst_element_lost_state),
(gst_element_set_state), (gst_element_change_state),
(gst_element_change_state_func):
* gst/gstelement.h:
New state change algorithm (see #318116)

* gst/gstpipeline.c: (gst_pipeline_class_init),
(gst_pipeline_init), (gst_pipeline_set_property),
(gst_pipeline_get_property), (do_pipeline_seek),
(gst_pipeline_change_state), (gst_pipeline_provide_clock_func):
* gst/gstpipeline.h:
Remove crude state change hacks.

* gst/gstutils.h:
Remove crude hacks.

* tools/gst-launch.c: (main):
Fixes for state change. Needs some more work to fully use the
new stuff.

22 files changed:
ChangeLog
check/Makefile.am
check/generic/states.c
check/gst/gstbin.c
check/gst/gstpipeline.c
check/states/sinks.c
check/states/sinks2.c [new file with mode: 0644]
gst/base/gstbasesink.c
gst/gstbin.c
gst/gstbin.h
gst/gstelement.c
gst/gstelement.h
gst/gstpipeline.c
gst/gstpipeline.h
gst/gstutils.h
libs/gst/base/gstbasesink.c
tests/check/Makefile.am
tests/check/generic/sinks.c
tests/check/generic/states.c
tests/check/gst/gstbin.c
tests/check/gst/gstpipeline.c
tools/gst-launch.c

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