mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2025-01-11 09:55:36 +00:00
check/: Check fixes, use API as stated in design docs, remove hacks.
Original commit message from CVS: * check/Makefile.am: * check/generic/states.c: (GST_START_TEST): * check/gst/gstbin.c: (GST_START_TEST): * check/gst/gstpipeline.c: (GST_START_TEST), (gst_pipeline_suite): * check/states/sinks.c: (GST_START_TEST): * check/states/sinks2.c: (GST_START_TEST), (gst_object_suite), (main): Check fixes, use API as stated in design docs, remove hacks. * gst/base/gstbasesink.c: (gst_base_sink_handle_object), (gst_base_sink_change_state): Catch stopping our task while we're shutting down. * gst/gstbin.c: (gst_bin_init), (gst_bin_add_func), (gst_bin_remove_func), (gst_bin_get_state_func), (gst_bin_recalc_state), (gst_bin_change_state_func), (bin_bus_handler): * gst/gstbin.h: * gst/gstelement.c: (gst_element_init), (gst_element_get_state_func), (gst_element_abort_state), (gst_element_commit_state), (gst_element_lost_state), (gst_element_set_state), (gst_element_change_state), (gst_element_change_state_func): * gst/gstelement.h: New state change algorithm (see #318116) * gst/gstpipeline.c: (gst_pipeline_class_init), (gst_pipeline_init), (gst_pipeline_set_property), (gst_pipeline_get_property), (do_pipeline_seek), (gst_pipeline_change_state), (gst_pipeline_provide_clock_func): * gst/gstpipeline.h: Remove crude state change hacks. * gst/gstutils.h: Remove crude hacks. * tools/gst-launch.c: (main): Fixes for state change. Needs some more work to fully use the new stuff.
This commit is contained in:
parent
b77f6b0e50
commit
2153c45964
22 changed files with 613 additions and 372 deletions
42
ChangeLog
42
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.
|
||||
|
|
|
@ -54,6 +54,7 @@ check_PROGRAMS = \
|
|||
pipelines/simple_launch_lines \
|
||||
pipelines/cleanup \
|
||||
states/sinks \
|
||||
states/sinks2 \
|
||||
gst-libs/controller \
|
||||
gst-libs/gdp
|
||||
|
||||
|
|
|
@ -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 ();
|
||||
|
|
|
@ -262,7 +262,7 @@ GST_START_TEST (test_message_state_changed_children)
|
|||
/* change state to PAUSED, spawning three messages */
|
||||
GST_DEBUG ("setting pipeline to PAUSED");
|
||||
ret = gst_element_set_state (GST_ELEMENT (pipeline), GST_STATE_PAUSED);
|
||||
fail_unless (ret == GST_STATE_CHANGE_SUCCESS);
|
||||
fail_unless (ret == GST_STATE_CHANGE_ASYNC);
|
||||
ret =
|
||||
gst_element_get_state (GST_ELEMENT (pipeline), ¤t, &pending, NULL);
|
||||
fail_unless (ret == GST_STATE_CHANGE_SUCCESS);
|
||||
|
@ -364,19 +364,13 @@ GST_START_TEST (test_watch_for_state_change)
|
|||
|
||||
fail_unless (gst_element_link (src, sink), "could not link src and sink");
|
||||
|
||||
/* change state, spawning two times three messages, minus one async */
|
||||
/* change state, spawning two times three messages */
|
||||
ret = gst_element_set_state (GST_ELEMENT (bin), GST_STATE_PAUSED);
|
||||
fail_unless (ret == GST_STATE_CHANGE_ASYNC);
|
||||
ret = gst_element_get_state (GST_ELEMENT (bin), NULL, NULL, NULL);
|
||||
fail_unless (ret == GST_STATE_CHANGE_SUCCESS);
|
||||
|
||||
pop_messages (bus, 5);
|
||||
|
||||
fail_unless (gst_bus_have_pending (bus) == FALSE,
|
||||
"Unexpected messages on bus");
|
||||
|
||||
gst_bin_watch_for_state_change (GST_BIN (bin));
|
||||
|
||||
/* should get the bin's state change message now */
|
||||
pop_messages (bus, 1);
|
||||
pop_messages (bus, 6);
|
||||
|
||||
fail_unless (gst_bus_have_pending (bus) == FALSE,
|
||||
"Unexpected messages on bus");
|
||||
|
@ -388,8 +382,7 @@ GST_START_TEST (test_watch_for_state_change)
|
|||
|
||||
/* this one might return either SUCCESS or ASYNC, likely SUCCESS */
|
||||
gst_element_set_state (GST_ELEMENT (bin), GST_STATE_PAUSED);
|
||||
|
||||
gst_bin_watch_for_state_change (GST_BIN (bin));
|
||||
gst_element_get_state (GST_ELEMENT (bin), NULL, NULL, NULL);
|
||||
|
||||
pop_messages (bus, 3);
|
||||
|
||||
|
@ -482,6 +475,7 @@ GST_START_TEST (test_children_state_change_order_flagged_sink)
|
|||
{
|
||||
GstElement *src, *identity, *sink, *pipeline;
|
||||
GstStateChangeReturn ret;
|
||||
GstState current, pending;
|
||||
GstBus *bus;
|
||||
|
||||
pipeline = gst_pipeline_new (NULL);
|
||||
|
@ -506,7 +500,12 @@ GST_START_TEST (test_children_state_change_order_flagged_sink)
|
|||
|
||||
/* (1) Test state change with fakesink being a regular sink */
|
||||
ret = gst_element_set_state (pipeline, GST_STATE_PLAYING);
|
||||
fail_if (ret != GST_STATE_CHANGE_ASYNC,
|
||||
"State change to PLAYING did not return ASYNC");
|
||||
ret = gst_element_get_state (pipeline, ¤t, &pending, NULL);
|
||||
fail_if (ret != GST_STATE_CHANGE_SUCCESS, "State change to PLAYING failed");
|
||||
fail_if (current != GST_STATE_PLAYING, "State change to PLAYING failed");
|
||||
fail_if (pending != GST_STATE_VOID_PENDING, "State change to PLAYING failed");
|
||||
|
||||
/* NULL => READY */
|
||||
ASSERT_STATE_CHANGE_MSG (bus, sink, GST_STATE_NULL, GST_STATE_READY, 101);
|
||||
|
@ -602,7 +601,7 @@ GST_START_TEST (test_children_state_change_order_semi_sink)
|
|||
GST_FLAG_UNSET (sink, GST_ELEMENT_IS_SINK); /* <======== */
|
||||
|
||||
ret = gst_element_set_state (pipeline, GST_STATE_PLAYING);
|
||||
fail_if (ret != GST_STATE_CHANGE_SUCCESS, "State change to PLAYING failed");
|
||||
fail_if (ret != GST_STATE_CHANGE_ASYNC, "State change to PLAYING not ASYNC");
|
||||
ret = gst_element_get_state (pipeline, ¤t, &pending, NULL);
|
||||
fail_if (ret != GST_STATE_CHANGE_SUCCESS, "State change to PLAYING failed");
|
||||
fail_if (current != GST_STATE_PLAYING, "State change to PLAYING failed");
|
||||
|
|
|
@ -28,7 +28,6 @@ GST_START_TEST (test_async_state_change_empty)
|
|||
|
||||
pipeline = GST_PIPELINE (gst_pipeline_new (NULL));
|
||||
fail_unless (pipeline != NULL, "Could not create pipeline");
|
||||
g_object_set (G_OBJECT (pipeline), "play-timeout", 0LL, NULL);
|
||||
|
||||
fail_unless_equals_int (gst_element_set_state (GST_ELEMENT (pipeline),
|
||||
GST_STATE_PLAYING), GST_STATE_CHANGE_SUCCESS);
|
||||
|
@ -45,7 +44,6 @@ GST_START_TEST (test_async_state_change_fake_ready)
|
|||
|
||||
pipeline = GST_PIPELINE (gst_pipeline_new (NULL));
|
||||
fail_unless (pipeline != NULL, "Could not create pipeline");
|
||||
g_object_set (G_OBJECT (pipeline), "play-timeout", 0LL, NULL);
|
||||
|
||||
src = gst_element_factory_make ("fakesrc", NULL);
|
||||
sink = gst_element_factory_make ("fakesink", NULL);
|
||||
|
@ -71,7 +69,6 @@ GST_START_TEST (test_async_state_change_fake)
|
|||
|
||||
pipeline = GST_PIPELINE (gst_pipeline_new (NULL));
|
||||
fail_unless (pipeline != NULL, "Could not create pipeline");
|
||||
g_object_set (G_OBJECT (pipeline), "play-timeout", 0LL, NULL);
|
||||
|
||||
src = gst_element_factory_make ("fakesrc", NULL);
|
||||
sink = gst_element_factory_make ("fakesink", NULL);
|
||||
|
@ -81,7 +78,7 @@ GST_START_TEST (test_async_state_change_fake)
|
|||
|
||||
bus = gst_pipeline_get_bus (pipeline);
|
||||
|
||||
fail_unless_equals_int (gst_element_set_state_async (GST_ELEMENT (pipeline),
|
||||
fail_unless_equals_int (gst_element_set_state (GST_ELEMENT (pipeline),
|
||||
GST_STATE_PLAYING), GST_STATE_CHANGE_ASYNC);
|
||||
|
||||
while (!done) {
|
||||
|
@ -98,7 +95,6 @@ GST_START_TEST (test_async_state_change_fake)
|
|||
}
|
||||
}
|
||||
|
||||
g_object_set (G_OBJECT (pipeline), "play-timeout", 3 * GST_SECOND, NULL);
|
||||
fail_unless_equals_int (gst_element_set_state (GST_ELEMENT (pipeline),
|
||||
GST_STATE_NULL), GST_STATE_CHANGE_SUCCESS);
|
||||
|
||||
|
@ -175,7 +171,6 @@ GST_START_TEST (test_bus)
|
|||
pipeline = gst_pipeline_new (NULL);
|
||||
fail_unless (pipeline != NULL, "Could not create pipeline");
|
||||
ASSERT_OBJECT_REFCOUNT (pipeline, "pipeline", 1);
|
||||
g_object_set (pipeline, "play-timeout", 0LL, NULL);
|
||||
|
||||
src = gst_element_factory_make ("fakesrc", NULL);
|
||||
fail_unless (src != NULL);
|
||||
|
@ -193,7 +188,7 @@ GST_START_TEST (test_bus)
|
|||
ASSERT_OBJECT_REFCOUNT (pipeline, "pipeline after add_watch", 1);
|
||||
ASSERT_OBJECT_REFCOUNT (bus, "bus after add_watch", 3);
|
||||
|
||||
gst_element_set_state_async (pipeline, GST_STATE_PLAYING);
|
||||
gst_element_set_state (pipeline, GST_STATE_PLAYING);
|
||||
loop = g_main_loop_new (NULL, FALSE);
|
||||
GST_DEBUG ("going into main loop");
|
||||
g_main_loop_run (loop);
|
||||
|
@ -207,9 +202,6 @@ GST_START_TEST (test_bus)
|
|||
/* cleanup */
|
||||
GST_DEBUG ("cleanup");
|
||||
|
||||
/* current semantics require us to go step by step; this will change */
|
||||
gst_element_set_state (pipeline, GST_STATE_PAUSED);
|
||||
gst_element_set_state (pipeline, GST_STATE_READY);
|
||||
gst_element_set_state (pipeline, GST_STATE_NULL);
|
||||
fail_unless (gst_element_get_state (pipeline, ¤t, NULL, NULL) ==
|
||||
GST_STATE_CHANGE_SUCCESS);
|
||||
|
@ -238,6 +230,8 @@ gst_pipeline_suite (void)
|
|||
Suite *s = suite_create ("GstPipeline");
|
||||
TCase *tc_chain = tcase_create ("pipeline tests");
|
||||
|
||||
tcase_set_timeout (tc_chain, 0);
|
||||
|
||||
suite_add_tcase (s, tc_chain);
|
||||
tcase_add_test (tc_chain, test_async_state_change_empty);
|
||||
tcase_add_test (tc_chain, test_async_state_change_fake_ready);
|
||||
|
|
|
@ -42,7 +42,7 @@ GST_START_TEST (test_sink)
|
|||
|
||||
ret = gst_element_get_state (sink, ¤t, &pending, &tv);
|
||||
fail_unless (ret == GST_STATE_CHANGE_ASYNC, "not changing state async");
|
||||
fail_unless (current == GST_STATE_PAUSED, "bad current state");
|
||||
fail_unless (current == GST_STATE_READY, "bad current state");
|
||||
fail_unless (pending == GST_STATE_PLAYING, "bad pending state");
|
||||
|
||||
ret = gst_element_set_state (sink, GST_STATE_PAUSED);
|
||||
|
@ -81,6 +81,7 @@ GST_START_TEST (test_src_sink)
|
|||
gst_object_unref (sinkpad);
|
||||
|
||||
ret = gst_element_set_state (pipeline, GST_STATE_PAUSED);
|
||||
fail_unless (ret == GST_STATE_CHANGE_ASYNC, "no async state return");
|
||||
ret = gst_element_get_state (pipeline, NULL, NULL, NULL);
|
||||
fail_unless (ret == GST_STATE_CHANGE_SUCCESS, "no success state return");
|
||||
|
||||
|
@ -139,8 +140,7 @@ GST_START_TEST (test_livesrc_remove)
|
|||
ret = gst_element_get_state (pipeline, ¤t, &pending, &tv);
|
||||
fail_unless (ret == GST_STATE_CHANGE_ASYNC, "not async");
|
||||
fail_unless (current == GST_STATE_PAUSED, "not paused");
|
||||
fail_unless (pending == GST_STATE_VOID_PENDING, "not playing");
|
||||
|
||||
fail_unless (pending == GST_STATE_PAUSED, "not paused");
|
||||
}
|
||||
|
||||
GST_END_TEST
|
||||
|
|
95
check/states/sinks2.c
Normal file
95
check/states/sinks2.c
Normal file
|
@ -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, ¤t, &pending, &tv);
|
||||
fail_unless (ret == GST_STATE_CHANGE_ASYNC, "not changing state async");
|
||||
fail_unless (current == GST_STATE_READY, "bad current state");
|
||||
fail_unless (pending == GST_STATE_PLAYING, "bad pending state");
|
||||
|
||||
src = gst_element_factory_make ("fakesrc", "src");
|
||||
gst_element_link (src, sink);
|
||||
|
||||
ret = gst_element_set_state (src, GST_STATE_PLAYING);
|
||||
fail_unless (ret == GST_STATE_CHANGE_SUCCESS, "no success state return");
|
||||
|
||||
/* now wait for final state */
|
||||
ret = gst_element_get_state (sink, ¤t, &pending, NULL);
|
||||
fail_unless (ret == GST_STATE_CHANGE_SUCCESS, "failed to change state");
|
||||
fail_unless (current == GST_STATE_PLAYING, "bad current state");
|
||||
fail_unless (pending == GST_STATE_VOID_PENDING, "bad pending state");
|
||||
|
||||
ret = gst_element_set_state (sink, GST_STATE_NULL);
|
||||
fail_unless (ret == GST_STATE_CHANGE_SUCCESS, "failed to go to null");
|
||||
|
||||
ret = gst_element_set_state (src, GST_STATE_NULL);
|
||||
fail_unless (ret == GST_STATE_CHANGE_SUCCESS, "failed to go to null");
|
||||
|
||||
gst_object_unref (sink);
|
||||
gst_object_unref (src);
|
||||
}
|
||||
|
||||
GST_END_TEST
|
||||
/* test: try changing state of sinks */
|
||||
Suite * gst_object_suite (void)
|
||||
{
|
||||
Suite *s = suite_create ("Sinks");
|
||||
TCase *tc_chain = tcase_create ("general");
|
||||
|
||||
suite_add_tcase (s, tc_chain);
|
||||
tcase_add_test (tc_chain, test_sink);
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
int
|
||||
main (int argc, char **argv)
|
||||
{
|
||||
int nf;
|
||||
|
||||
Suite *s = gst_object_suite ();
|
||||
SRunner *sr = srunner_create (s);
|
||||
|
||||
gst_check_init (&argc, &argv);
|
||||
|
||||
srunner_run_all (sr, CK_NORMAL);
|
||||
nf = srunner_ntests_failed (sr);
|
||||
srunner_free (sr);
|
||||
|
||||
return nf;
|
||||
}
|
|
@ -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;
|
||||
|
|
216
gst/gstbin.c
216
gst/gstbin.c
|
@ -76,7 +76,6 @@ GST_DEBUG_CATEGORY_STATIC (bin_debug);
|
|||
(guint) (bin)->child_states[2], (bin)->child_states[1], \
|
||||
(bin)->child_states[0], gst_element_state_get_name (GST_STATE (bin)))
|
||||
|
||||
|
||||
static GstElementDetails gst_bin_details = GST_ELEMENT_DETAILS ("Generic bin",
|
||||
"Generic/Bin",
|
||||
"Simple container object",
|
||||
|
@ -86,6 +85,7 @@ GType _gst_bin_type = 0;
|
|||
|
||||
static void gst_bin_dispose (GObject * object);
|
||||
|
||||
static void gst_bin_recalc_state (GstBin * bin, gboolean force);
|
||||
static GstStateChangeReturn gst_bin_change_state_func (GstElement * element,
|
||||
GstStateChange transition);
|
||||
static GstStateChangeReturn gst_bin_get_state_func (GstElement * element,
|
||||
|
@ -292,6 +292,8 @@ gst_bin_init (GstBin * bin)
|
|||
bin->children = NULL;
|
||||
bin->children_cookie = 0;
|
||||
bin->eosed = NULL;
|
||||
bin->polling = FALSE;
|
||||
bin->state_dirty = FALSE;
|
||||
|
||||
/* Set up a bus for listening to child elements */
|
||||
bus = g_object_new (gst_bus_get_type (), NULL);
|
||||
|
@ -483,7 +485,7 @@ gst_bin_add_func (GstBin * bin, GstElement * element)
|
|||
gst_element_set_base_time (element, GST_ELEMENT (bin)->base_time);
|
||||
GST_DEBUG_OBJECT (element, "setting clock %p", GST_ELEMENT_CLOCK (bin));
|
||||
gst_element_set_clock (element, GST_ELEMENT_CLOCK (bin));
|
||||
|
||||
bin->state_dirty = TRUE;
|
||||
GST_UNLOCK (bin);
|
||||
|
||||
/* unlink all linked pads */
|
||||
|
@ -619,6 +621,7 @@ gst_bin_remove_func (GstBin * bin, GstElement * element)
|
|||
GST_FLAG_UNSET (bin, GST_ELEMENT_IS_SINK);
|
||||
}
|
||||
}
|
||||
bin->state_dirty = TRUE;
|
||||
GST_UNLOCK (bin);
|
||||
|
||||
GST_CAT_INFO_OBJECT (GST_CAT_PARENTAGE, bin, "removed child \"%s\"",
|
||||
|
@ -872,14 +875,7 @@ gst_bin_iterate_sinks (GstBin * bin)
|
|||
return result;
|
||||
}
|
||||
|
||||
/* 2 phases:
|
||||
* 1) check state of all children with 0 timeout to find ERROR and
|
||||
* NO_PREROLL elements. return if found.
|
||||
* 2) perform full blocking wait with requested timeout.
|
||||
*
|
||||
* 2) cannot be performed when 1) returns results as the sinks might
|
||||
* not be able to complete the state change making 2) block forever.
|
||||
*
|
||||
/*
|
||||
* MT safe
|
||||
*/
|
||||
static GstStateChangeReturn
|
||||
|
@ -887,6 +883,18 @@ gst_bin_get_state_func (GstElement * element, GstState * state,
|
|||
GstState * pending, GTimeVal * timeout)
|
||||
{
|
||||
GstBin *bin = GST_BIN (element);
|
||||
|
||||
GST_CAT_INFO_OBJECT (GST_CAT_STATES, element, "getting state");
|
||||
|
||||
/* do a non forced recalculation of the state */
|
||||
gst_bin_recalc_state (bin, FALSE);
|
||||
|
||||
return parent_class->get_state (element, state, pending, timeout);
|
||||
}
|
||||
|
||||
static void
|
||||
gst_bin_recalc_state (GstBin * bin, gboolean force)
|
||||
{
|
||||
GstStateChangeReturn ret;
|
||||
GList *children;
|
||||
guint32 children_cookie;
|
||||
|
@ -894,12 +902,21 @@ gst_bin_get_state_func (GstElement * element, GstState * state,
|
|||
gboolean have_async;
|
||||
GTimeVal tv;
|
||||
|
||||
GST_CAT_INFO_OBJECT (GST_CAT_STATES, element, "getting state");
|
||||
|
||||
ret = GST_STATE_CHANGE_SUCCESS;
|
||||
|
||||
/* lock bin, no element can be added or removed while we have this lock */
|
||||
GST_LOCK (bin);
|
||||
/* no point in scanning if nothing changed and it's no forced recalc */
|
||||
if (!force && !bin->state_dirty)
|
||||
goto not_dirty;
|
||||
|
||||
/* no point in having two scans run concurrently */
|
||||
if (bin->polling)
|
||||
goto was_polling;
|
||||
|
||||
bin->polling = TRUE;
|
||||
|
||||
GST_CAT_INFO_OBJECT (GST_CAT_STATES, bin, "recalc state");
|
||||
|
||||
restart:
|
||||
have_no_preroll = FALSE;
|
||||
|
@ -935,7 +952,7 @@ restart:
|
|||
|
||||
switch (ret) {
|
||||
case GST_STATE_CHANGE_FAILURE:
|
||||
/* report FAILURE immediately */
|
||||
/* report FAILURE immediatly */
|
||||
goto done;
|
||||
case GST_STATE_CHANGE_NO_PREROLL:
|
||||
/* we have to continue scanning as there might be
|
||||
|
@ -957,59 +974,15 @@ restart:
|
|||
/* if we have NO_PREROLL, return that */
|
||||
if (have_no_preroll) {
|
||||
ret = GST_STATE_CHANGE_NO_PREROLL;
|
||||
goto done;
|
||||
}
|
||||
/* else return SUCCESS if no async elements were found */
|
||||
else if (!have_async) {
|
||||
ret = GST_STATE_CHANGE_SUCCESS;
|
||||
goto done;
|
||||
/* else return ASYNC if async elements where found. */
|
||||
else if (have_async) {
|
||||
ret = GST_STATE_CHANGE_ASYNC;
|
||||
}
|
||||
|
||||
/* 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);
|
||||
}
|
||||
/* 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");
|
||||
|
|
|
@ -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];
|
||||
};
|
||||
|
|
338
gst/gstelement.c
338
gst/gstelement.c
|
@ -55,7 +55,6 @@
|
|||
*
|
||||
* Each element has a state (see #GstState). You can get and set the state
|
||||
* of an element with gst_element_get_state() and gst_element_set_state().
|
||||
* You can wait for an element to change it's state with gst_element_wait_state_change().
|
||||
* To get a string representation of a #GstState, use
|
||||
* gst_element_state_get_name().
|
||||
*
|
||||
|
@ -71,6 +70,7 @@
|
|||
* Note that clock slection and distribution is normally handled by the toplevel
|
||||
* #GstPipeline so the clock functions are only to be used in very specific situations.
|
||||
*/
|
||||
|
||||
#include "gst_private.h"
|
||||
#include <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);
|
||||
/* we got an error, report immediatly */
|
||||
if (GST_STATE_NO_PREROLL (element)) {
|
||||
ret = GST_STATE_CHANGE_NO_PREROLL;
|
||||
goto done;
|
||||
}
|
||||
ret = GST_STATE_RETURN (element);
|
||||
|
||||
/* we got an error, report immediatly */
|
||||
if (GST_STATE_ERROR (element)) {
|
||||
ret = GST_STATE_CHANGE_FAILURE;
|
||||
if (ret == GST_STATE_CHANGE_FAILURE)
|
||||
goto done;
|
||||
|
||||
/* we got no_preroll, report immediatly */
|
||||
if (ret == GST_STATE_CHANGE_NO_PREROLL)
|
||||
goto done;
|
||||
|
||||
/* no need to wait async if we are not async */
|
||||
if (ret != GST_STATE_CHANGE_ASYNC)
|
||||
goto done;
|
||||
}
|
||||
|
||||
old_pending = GST_STATE_PENDING (element);
|
||||
if (old_pending != GST_STATE_VOID_PENDING) {
|
||||
|
@ -1627,6 +1633,9 @@ gst_element_get_state_func (GstElement * element,
|
|||
if (timeout) {
|
||||
glong add = timeout->tv_sec * G_USEC_PER_SEC + timeout->tv_usec;
|
||||
|
||||
if (add == 0)
|
||||
goto done;
|
||||
|
||||
/* make timeout absolute */
|
||||
g_get_current_time (&abstimeout);
|
||||
g_time_val_add (&abstimeout, add);
|
||||
|
@ -1664,10 +1673,9 @@ done:
|
|||
*pending = GST_STATE_PENDING (element);
|
||||
|
||||
GST_CAT_INFO_OBJECT (GST_CAT_STATES, element,
|
||||
"state current: %s, pending: %s, error: %d, no_preroll: %d, result: %d",
|
||||
"state current: %s, pending: %s, result: %d",
|
||||
gst_element_state_get_name (GST_STATE (element)),
|
||||
gst_element_state_get_name (GST_STATE_PENDING (element)),
|
||||
GST_STATE_ERROR (element), GST_STATE_NO_PREROLL (element), ret);
|
||||
gst_element_state_get_name (GST_STATE_PENDING (element)), ret);
|
||||
|
||||
GST_STATE_UNLOCK (element);
|
||||
|
||||
|
@ -1692,6 +1700,9 @@ done:
|
|||
* an error, this function returns immediatly with a return value of
|
||||
* GST_STATE_CHANGE_SUCCESS or GST_STATE_CHANGE_FAILURE respectively.
|
||||
*
|
||||
* For elements that did not return ASYNC, this function returns the
|
||||
* current and pending state immediatly.
|
||||
*
|
||||
* Returns: GST_STATE_CHANGE_SUCCESS if the element has no more pending state and
|
||||
* the last state change succeeded, GST_STATE_CHANGE_ASYNC
|
||||
* if the element is still performing a state change or
|
||||
|
@ -1737,17 +1748,17 @@ gst_element_abort_state (GstElement * element)
|
|||
|
||||
pending = GST_STATE_PENDING (element);
|
||||
|
||||
if (pending != GST_STATE_VOID_PENDING && !GST_STATE_ERROR (element)) {
|
||||
if (pending != GST_STATE_VOID_PENDING &&
|
||||
GST_STATE_RETURN (element) != GST_STATE_CHANGE_FAILURE) {
|
||||
#ifndef GST_DISABLE_GST_DEBUG
|
||||
GstState old_state = GST_STATE (element);
|
||||
#endif
|
||||
|
||||
GST_CAT_INFO_OBJECT (GST_CAT_STATES, element,
|
||||
"aborting state from %s to %s", gst_element_state_get_name (old_state),
|
||||
gst_element_state_get_name (pending));
|
||||
|
||||
/* flag error */
|
||||
GST_STATE_ERROR (element) = TRUE;
|
||||
GST_STATE_RETURN (element) = GST_STATE_CHANGE_FAILURE;
|
||||
|
||||
GST_STATE_BROADCAST (element);
|
||||
}
|
||||
|
@ -1759,38 +1770,93 @@ gst_element_abort_state (GstElement * element)
|
|||
*
|
||||
* Commit the state change of the element. This function is used
|
||||
* by elements that do asynchronous state changes.
|
||||
* The core will normally call this method automatically when an
|
||||
* element returned SUCCESS from the state change function.
|
||||
* Elements that return ASYNC from the change_state function should
|
||||
* eventually call this method from the streaming thread to signal
|
||||
* successfull state change completion.
|
||||
*
|
||||
* If after calling this method the element still has not reached
|
||||
* the pending state, the next state change is performed.
|
||||
*
|
||||
* This function can only be called with the STATE_LOCK held.
|
||||
*
|
||||
* Returns: The result of the commit state change.
|
||||
*
|
||||
* MT safe.
|
||||
*/
|
||||
void
|
||||
GstStateChangeReturn
|
||||
gst_element_commit_state (GstElement * element)
|
||||
{
|
||||
GstState pending;
|
||||
GstStateChangeReturn ret;
|
||||
|
||||
g_return_if_fail (GST_IS_ELEMENT (element));
|
||||
g_return_val_if_fail (GST_IS_ELEMENT (element), GST_STATE_CHANGE_FAILURE);
|
||||
|
||||
ret = GST_STATE_CHANGE_SUCCESS;
|
||||
|
||||
pending = GST_STATE_PENDING (element);
|
||||
|
||||
/* check if there is something to commit */
|
||||
if (pending != GST_STATE_VOID_PENDING) {
|
||||
GstState old_state = GST_STATE (element);
|
||||
GstState old_state;
|
||||
GstState current, next;
|
||||
GstMessage *message;
|
||||
|
||||
GST_FLAG_SET (element, GST_ELEMENT_CHANGING_STATE);
|
||||
|
||||
old_state = GST_STATE (element);
|
||||
/* this is the state we should go to next */
|
||||
next = GST_STATE_NEXT (element);
|
||||
/* update current state */
|
||||
current = GST_STATE (element) = next;
|
||||
|
||||
/* see if we reached the final state */
|
||||
if (pending == next) {
|
||||
pending = GST_STATE_VOID_PENDING;
|
||||
GST_STATE_PENDING (element) = pending;
|
||||
GST_STATE_NEXT (element) = pending;
|
||||
ret = GST_STATE_CHANGE_SUCCESS;
|
||||
} else {
|
||||
/* not there yet, will get there ASYNC */
|
||||
ret = GST_STATE_CHANGE_ASYNC;
|
||||
}
|
||||
|
||||
GST_STATE_RETURN (element) = ret;
|
||||
|
||||
GST_CAT_INFO_OBJECT (GST_CAT_STATES, element,
|
||||
"committing state from %s to %s",
|
||||
"committing state from %s to %s, pending %s",
|
||||
gst_element_state_get_name (old_state),
|
||||
gst_element_state_get_name (next),
|
||||
gst_element_state_get_name (pending));
|
||||
|
||||
GST_STATE (element) = pending;
|
||||
GST_STATE_PENDING (element) = GST_STATE_VOID_PENDING;
|
||||
GST_STATE_ERROR (element) = FALSE;
|
||||
|
||||
message = gst_message_new_state_changed (GST_OBJECT (element),
|
||||
old_state, pending, pending);
|
||||
old_state, next, pending);
|
||||
gst_element_post_message (element, message);
|
||||
GST_STATE_BROADCAST (element);
|
||||
|
||||
if (pending != GST_STATE_VOID_PENDING) {
|
||||
GstStateChange transition;
|
||||
|
||||
/* calc new next state */
|
||||
next = GST_STATE_GET_NEXT (current, pending);
|
||||
|
||||
GST_CAT_INFO_OBJECT (GST_CAT_STATES, element,
|
||||
"continue state change %s to %s, final %s",
|
||||
gst_element_state_get_name (current),
|
||||
gst_element_state_get_name (next),
|
||||
gst_element_state_get_name (pending));
|
||||
|
||||
/* create transition */
|
||||
transition = GST_STATE_TRANSITION (current, next);
|
||||
|
||||
/* perform next transition */
|
||||
ret = gst_element_change_state (element, transition);
|
||||
} else {
|
||||
GST_STATE_BROADCAST (element);
|
||||
}
|
||||
GST_FLAG_UNSET (element, GST_ELEMENT_CHANGING_STATE);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1817,7 +1883,7 @@ gst_element_lost_state (GstElement * element)
|
|||
g_return_if_fail (GST_IS_ELEMENT (element));
|
||||
|
||||
if (GST_STATE_PENDING (element) == GST_STATE_VOID_PENDING &&
|
||||
!GST_STATE_ERROR (element)) {
|
||||
GST_STATE_RETURN (element) != GST_STATE_CHANGE_FAILURE) {
|
||||
GstState current_state;
|
||||
GstMessage *message;
|
||||
|
||||
|
@ -1826,8 +1892,9 @@ gst_element_lost_state (GstElement * element)
|
|||
GST_CAT_INFO_OBJECT (GST_CAT_STATES, element,
|
||||
"lost state of %s", gst_element_state_get_name (current_state));
|
||||
|
||||
GST_STATE_NEXT (element) = current_state;
|
||||
GST_STATE_PENDING (element) = current_state;
|
||||
GST_STATE_ERROR (element) = FALSE;
|
||||
GST_STATE_RETURN (element) = GST_STATE_CHANGE_ASYNC;
|
||||
|
||||
message = gst_message_new_state_changed (GST_OBJECT (element),
|
||||
current_state, current_state, current_state);
|
||||
|
@ -1844,6 +1911,11 @@ gst_element_lost_state (GstElement * element)
|
|||
* requested state by going through all the intermediary states and calling
|
||||
* the class's state change function for each.
|
||||
*
|
||||
* This function can return GST_STATE_CHANGE_ASYNC, in which case the
|
||||
* element will perform the remainder of the state change asynchronously.
|
||||
* An application can use gst_element_get_state() to wait for the completion
|
||||
* of the state change or it can wait for a state change message on the bus.
|
||||
*
|
||||
* Returns: Result of the state change using #GstStateChangeReturn.
|
||||
*
|
||||
* MT safe.
|
||||
|
@ -1851,40 +1923,70 @@ gst_element_lost_state (GstElement * element)
|
|||
GstStateChangeReturn
|
||||
gst_element_set_state (GstElement * element, GstState state)
|
||||
{
|
||||
GstState current, old_pending;
|
||||
GstState current, next, old_pending;
|
||||
GstStateChangeReturn ret;
|
||||
GstStateChange transition;
|
||||
GTimeVal tv;
|
||||
GstStateChangeReturn old_ret;
|
||||
|
||||
g_return_val_if_fail (GST_IS_ELEMENT (element), GST_STATE_CHANGE_FAILURE);
|
||||
|
||||
GST_CAT_DEBUG_OBJECT (GST_CAT_STATES, element, "set_state to %s",
|
||||
gst_element_state_get_name (state));
|
||||
|
||||
/* get current element state, need to call the method so that
|
||||
* we call the virtual method and subclasses can implement their
|
||||
* own algorithms */
|
||||
GST_TIME_TO_TIMEVAL (0, tv);
|
||||
ret = gst_element_get_state (element, ¤t, &old_pending, &tv);
|
||||
|
||||
GST_STATE_LOCK (element);
|
||||
GST_FLAG_SET (element, GST_ELEMENT_CHANGING_STATE);
|
||||
|
||||
/* 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))
|
||||
goto was_busy;
|
||||
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;
|
||||
}
|
||||
|
||||
/* fixme, not right */
|
||||
transition = GST_STATE_CHANGE (element);
|
||||
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_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);
|
||||
|
||||
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);
|
||||
current = GST_STATE_TRANSITION_CURRENT (transition);
|
||||
next = GST_STATE_TRANSITION_NEXT (transition);
|
||||
|
||||
/* We always perform at least one state change, even if the
|
||||
* current state is equal to the required state. This is needed
|
||||
* for bins that sync their children. */
|
||||
do {
|
||||
GstState pending;
|
||||
/* now we store the next state */
|
||||
GST_STATE_NEXT (element) = next;
|
||||
|
||||
/* calculate the pending state */
|
||||
if (current < final)
|
||||
pending = current + 1;
|
||||
else if (current > final)
|
||||
pending = current - 1;
|
||||
else
|
||||
pending = current;
|
||||
/* call the state change function so it can set the state */
|
||||
if (oclass->change_state)
|
||||
ret = (oclass->change_state) (element, transition);
|
||||
else
|
||||
ret = GST_STATE_CHANGE_FAILURE;
|
||||
|
||||
/* set the pending state variable */
|
||||
GST_STATE_PENDING (element) = pending;
|
||||
switch (ret) {
|
||||
case GST_STATE_CHANGE_FAILURE:
|
||||
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");
|
||||
|
||||
GST_CAT_DEBUG_OBJECT (GST_CAT_STATES, element,
|
||||
"%s: setting state from %s to %s",
|
||||
(pending != final ? "intermediate" : "final"),
|
||||
gst_element_state_get_name (current),
|
||||
gst_element_state_get_name (pending));
|
||||
/* if we go upwards, we give the app a change to wait for
|
||||
* completion */
|
||||
if (current < next)
|
||||
goto exit;
|
||||
|
||||
/* call the state change function so it can set the state */
|
||||
if (oclass->change_state)
|
||||
ret = (oclass->change_state) (element, GST_STATE_CHANGE (element));
|
||||
else
|
||||
/* 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;
|
||||
|
||||
/* 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. */
|
||||
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);
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -93,6 +93,14 @@ 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.
|
||||
|
@ -100,28 +108,21 @@ typedef enum {
|
|||
* 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
|
||||
/**
|
||||
* GST_STATE_RETURN:
|
||||
* @obj: Element to return the last state result for.
|
||||
*
|
||||
* This macro returns the last state change return value.
|
||||
*/
|
||||
#define GST_STATE_RETURN(obj) (GST_ELEMENT(obj)->last_return)
|
||||
|
||||
#define GST_SIGN(val) ((val) < 0 ? -1 : ((val) > 0 ? 1 : 0))
|
||||
#define GST_STATE_GET_NEXT(cur,pending) ((cur)+GST_SIGN ((gint)(pending)-(gint)(cur)))
|
||||
#define GST_STATE_TRANSITION(cur,next) (((cur)<<3)|(next))
|
||||
#define GST_STATE_TRANSITION_CURRENT(trans) ((trans)>>3)
|
||||
#define GST_STATE_TRANSITION_NEXT(trans) ((trans)&0x7)
|
||||
|
||||
/* FIXME: How to deal with lost_state ? */
|
||||
/**
|
||||
* GstStateChange:
|
||||
* @GST_STATE_CHANGE_NULL_TO_READY : state change from NULL to READY
|
||||
|
@ -133,12 +134,12 @@ _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 */
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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];
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -54,6 +54,7 @@ check_PROGRAMS = \
|
|||
pipelines/simple_launch_lines \
|
||||
pipelines/cleanup \
|
||||
states/sinks \
|
||||
states/sinks2 \
|
||||
gst-libs/controller \
|
||||
gst-libs/gdp
|
||||
|
||||
|
|
|
@ -42,7 +42,7 @@ GST_START_TEST (test_sink)
|
|||
|
||||
ret = gst_element_get_state (sink, ¤t, &pending, &tv);
|
||||
fail_unless (ret == GST_STATE_CHANGE_ASYNC, "not changing state async");
|
||||
fail_unless (current == GST_STATE_PAUSED, "bad current state");
|
||||
fail_unless (current == GST_STATE_READY, "bad current state");
|
||||
fail_unless (pending == GST_STATE_PLAYING, "bad pending state");
|
||||
|
||||
ret = gst_element_set_state (sink, GST_STATE_PAUSED);
|
||||
|
@ -81,6 +81,7 @@ GST_START_TEST (test_src_sink)
|
|||
gst_object_unref (sinkpad);
|
||||
|
||||
ret = gst_element_set_state (pipeline, GST_STATE_PAUSED);
|
||||
fail_unless (ret == GST_STATE_CHANGE_ASYNC, "no async state return");
|
||||
ret = gst_element_get_state (pipeline, NULL, NULL, NULL);
|
||||
fail_unless (ret == GST_STATE_CHANGE_SUCCESS, "no success state return");
|
||||
|
||||
|
@ -139,8 +140,7 @@ GST_START_TEST (test_livesrc_remove)
|
|||
ret = gst_element_get_state (pipeline, ¤t, &pending, &tv);
|
||||
fail_unless (ret == GST_STATE_CHANGE_ASYNC, "not async");
|
||||
fail_unless (current == GST_STATE_PAUSED, "not paused");
|
||||
fail_unless (pending == GST_STATE_VOID_PENDING, "not playing");
|
||||
|
||||
fail_unless (pending == GST_STATE_PAUSED, "not paused");
|
||||
}
|
||||
|
||||
GST_END_TEST
|
||||
|
|
|
@ -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 ();
|
||||
|
|
|
@ -262,7 +262,7 @@ GST_START_TEST (test_message_state_changed_children)
|
|||
/* change state to PAUSED, spawning three messages */
|
||||
GST_DEBUG ("setting pipeline to PAUSED");
|
||||
ret = gst_element_set_state (GST_ELEMENT (pipeline), GST_STATE_PAUSED);
|
||||
fail_unless (ret == GST_STATE_CHANGE_SUCCESS);
|
||||
fail_unless (ret == GST_STATE_CHANGE_ASYNC);
|
||||
ret =
|
||||
gst_element_get_state (GST_ELEMENT (pipeline), ¤t, &pending, NULL);
|
||||
fail_unless (ret == GST_STATE_CHANGE_SUCCESS);
|
||||
|
@ -364,19 +364,13 @@ GST_START_TEST (test_watch_for_state_change)
|
|||
|
||||
fail_unless (gst_element_link (src, sink), "could not link src and sink");
|
||||
|
||||
/* change state, spawning two times three messages, minus one async */
|
||||
/* change state, spawning two times three messages */
|
||||
ret = gst_element_set_state (GST_ELEMENT (bin), GST_STATE_PAUSED);
|
||||
fail_unless (ret == GST_STATE_CHANGE_ASYNC);
|
||||
ret = gst_element_get_state (GST_ELEMENT (bin), NULL, NULL, NULL);
|
||||
fail_unless (ret == GST_STATE_CHANGE_SUCCESS);
|
||||
|
||||
pop_messages (bus, 5);
|
||||
|
||||
fail_unless (gst_bus_have_pending (bus) == FALSE,
|
||||
"Unexpected messages on bus");
|
||||
|
||||
gst_bin_watch_for_state_change (GST_BIN (bin));
|
||||
|
||||
/* should get the bin's state change message now */
|
||||
pop_messages (bus, 1);
|
||||
pop_messages (bus, 6);
|
||||
|
||||
fail_unless (gst_bus_have_pending (bus) == FALSE,
|
||||
"Unexpected messages on bus");
|
||||
|
@ -388,8 +382,7 @@ GST_START_TEST (test_watch_for_state_change)
|
|||
|
||||
/* this one might return either SUCCESS or ASYNC, likely SUCCESS */
|
||||
gst_element_set_state (GST_ELEMENT (bin), GST_STATE_PAUSED);
|
||||
|
||||
gst_bin_watch_for_state_change (GST_BIN (bin));
|
||||
gst_element_get_state (GST_ELEMENT (bin), NULL, NULL, NULL);
|
||||
|
||||
pop_messages (bus, 3);
|
||||
|
||||
|
@ -482,6 +475,7 @@ GST_START_TEST (test_children_state_change_order_flagged_sink)
|
|||
{
|
||||
GstElement *src, *identity, *sink, *pipeline;
|
||||
GstStateChangeReturn ret;
|
||||
GstState current, pending;
|
||||
GstBus *bus;
|
||||
|
||||
pipeline = gst_pipeline_new (NULL);
|
||||
|
@ -506,7 +500,12 @@ GST_START_TEST (test_children_state_change_order_flagged_sink)
|
|||
|
||||
/* (1) Test state change with fakesink being a regular sink */
|
||||
ret = gst_element_set_state (pipeline, GST_STATE_PLAYING);
|
||||
fail_if (ret != GST_STATE_CHANGE_ASYNC,
|
||||
"State change to PLAYING did not return ASYNC");
|
||||
ret = gst_element_get_state (pipeline, ¤t, &pending, NULL);
|
||||
fail_if (ret != GST_STATE_CHANGE_SUCCESS, "State change to PLAYING failed");
|
||||
fail_if (current != GST_STATE_PLAYING, "State change to PLAYING failed");
|
||||
fail_if (pending != GST_STATE_VOID_PENDING, "State change to PLAYING failed");
|
||||
|
||||
/* NULL => READY */
|
||||
ASSERT_STATE_CHANGE_MSG (bus, sink, GST_STATE_NULL, GST_STATE_READY, 101);
|
||||
|
@ -602,7 +601,7 @@ GST_START_TEST (test_children_state_change_order_semi_sink)
|
|||
GST_FLAG_UNSET (sink, GST_ELEMENT_IS_SINK); /* <======== */
|
||||
|
||||
ret = gst_element_set_state (pipeline, GST_STATE_PLAYING);
|
||||
fail_if (ret != GST_STATE_CHANGE_SUCCESS, "State change to PLAYING failed");
|
||||
fail_if (ret != GST_STATE_CHANGE_ASYNC, "State change to PLAYING not ASYNC");
|
||||
ret = gst_element_get_state (pipeline, ¤t, &pending, NULL);
|
||||
fail_if (ret != GST_STATE_CHANGE_SUCCESS, "State change to PLAYING failed");
|
||||
fail_if (current != GST_STATE_PLAYING, "State change to PLAYING failed");
|
||||
|
|
|
@ -28,7 +28,6 @@ GST_START_TEST (test_async_state_change_empty)
|
|||
|
||||
pipeline = GST_PIPELINE (gst_pipeline_new (NULL));
|
||||
fail_unless (pipeline != NULL, "Could not create pipeline");
|
||||
g_object_set (G_OBJECT (pipeline), "play-timeout", 0LL, NULL);
|
||||
|
||||
fail_unless_equals_int (gst_element_set_state (GST_ELEMENT (pipeline),
|
||||
GST_STATE_PLAYING), GST_STATE_CHANGE_SUCCESS);
|
||||
|
@ -45,7 +44,6 @@ GST_START_TEST (test_async_state_change_fake_ready)
|
|||
|
||||
pipeline = GST_PIPELINE (gst_pipeline_new (NULL));
|
||||
fail_unless (pipeline != NULL, "Could not create pipeline");
|
||||
g_object_set (G_OBJECT (pipeline), "play-timeout", 0LL, NULL);
|
||||
|
||||
src = gst_element_factory_make ("fakesrc", NULL);
|
||||
sink = gst_element_factory_make ("fakesink", NULL);
|
||||
|
@ -71,7 +69,6 @@ GST_START_TEST (test_async_state_change_fake)
|
|||
|
||||
pipeline = GST_PIPELINE (gst_pipeline_new (NULL));
|
||||
fail_unless (pipeline != NULL, "Could not create pipeline");
|
||||
g_object_set (G_OBJECT (pipeline), "play-timeout", 0LL, NULL);
|
||||
|
||||
src = gst_element_factory_make ("fakesrc", NULL);
|
||||
sink = gst_element_factory_make ("fakesink", NULL);
|
||||
|
@ -81,7 +78,7 @@ GST_START_TEST (test_async_state_change_fake)
|
|||
|
||||
bus = gst_pipeline_get_bus (pipeline);
|
||||
|
||||
fail_unless_equals_int (gst_element_set_state_async (GST_ELEMENT (pipeline),
|
||||
fail_unless_equals_int (gst_element_set_state (GST_ELEMENT (pipeline),
|
||||
GST_STATE_PLAYING), GST_STATE_CHANGE_ASYNC);
|
||||
|
||||
while (!done) {
|
||||
|
@ -98,7 +95,6 @@ GST_START_TEST (test_async_state_change_fake)
|
|||
}
|
||||
}
|
||||
|
||||
g_object_set (G_OBJECT (pipeline), "play-timeout", 3 * GST_SECOND, NULL);
|
||||
fail_unless_equals_int (gst_element_set_state (GST_ELEMENT (pipeline),
|
||||
GST_STATE_NULL), GST_STATE_CHANGE_SUCCESS);
|
||||
|
||||
|
@ -175,7 +171,6 @@ GST_START_TEST (test_bus)
|
|||
pipeline = gst_pipeline_new (NULL);
|
||||
fail_unless (pipeline != NULL, "Could not create pipeline");
|
||||
ASSERT_OBJECT_REFCOUNT (pipeline, "pipeline", 1);
|
||||
g_object_set (pipeline, "play-timeout", 0LL, NULL);
|
||||
|
||||
src = gst_element_factory_make ("fakesrc", NULL);
|
||||
fail_unless (src != NULL);
|
||||
|
@ -193,7 +188,7 @@ GST_START_TEST (test_bus)
|
|||
ASSERT_OBJECT_REFCOUNT (pipeline, "pipeline after add_watch", 1);
|
||||
ASSERT_OBJECT_REFCOUNT (bus, "bus after add_watch", 3);
|
||||
|
||||
gst_element_set_state_async (pipeline, GST_STATE_PLAYING);
|
||||
gst_element_set_state (pipeline, GST_STATE_PLAYING);
|
||||
loop = g_main_loop_new (NULL, FALSE);
|
||||
GST_DEBUG ("going into main loop");
|
||||
g_main_loop_run (loop);
|
||||
|
@ -207,9 +202,6 @@ GST_START_TEST (test_bus)
|
|||
/* cleanup */
|
||||
GST_DEBUG ("cleanup");
|
||||
|
||||
/* current semantics require us to go step by step; this will change */
|
||||
gst_element_set_state (pipeline, GST_STATE_PAUSED);
|
||||
gst_element_set_state (pipeline, GST_STATE_READY);
|
||||
gst_element_set_state (pipeline, GST_STATE_NULL);
|
||||
fail_unless (gst_element_get_state (pipeline, ¤t, NULL, NULL) ==
|
||||
GST_STATE_CHANGE_SUCCESS);
|
||||
|
@ -238,6 +230,8 @@ gst_pipeline_suite (void)
|
|||
Suite *s = suite_create ("GstPipeline");
|
||||
TCase *tc_chain = tcase_create ("pipeline tests");
|
||||
|
||||
tcase_set_timeout (tc_chain, 0);
|
||||
|
||||
suite_add_tcase (s, tc_chain);
|
||||
tcase_add_test (tc_chain, test_async_state_change_empty);
|
||||
tcase_add_test (tc_chain, test_async_state_change_fake_ready);
|
||||
|
|
|
@ -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:
|
||||
|
|
Loading…
Reference in a new issue