+2005-08-18 Andy Wingo <wingo@pobox.com>
+
+ Make sure that when a pipeline goes to PLAYING, that data has
+ actually hit the sink.
+
+ * check/states/sinks.c (test_sink): A sink that doesn't get any
+ data shouldn't return SUCCESS for going to either PLAYING or
+ PAUSED. Test also the return values on the way back down.
+
+ * gst/gstelement.c (gst_element_set_state): When changing the
+ state of an element currently changing state asynchronously, go to
+ lost-state after commiting the pending state. Makes future calls
+ to get_state continue to return ASYNC.
+
+ * gst/base/gstbasesink.c (gst_base_sink_change_state): Return
+ ASYNC when going to PLAYING if we still don't have preroll, as can
+ happen with live sources.
+
2005-08-18 Jan Schmidt <thaytan@mad.scientist.com>
* docs/pwg/advanced-types.xml:
GstElement *sink;
GstElementStateReturn ret;
GstElementState current, pending;
+ GTimeVal tv;
sink = gst_element_factory_make ("fakesink", "sink");
fail_unless (ret == GST_STATE_ASYNC, "no async state return");
ret = gst_element_set_state (sink, GST_STATE_PLAYING);
- fail_unless (ret == GST_STATE_SUCCESS, "cannot force play");
+ fail_unless (ret == GST_STATE_ASYNC, "no forced async state change");
- ret = gst_element_get_state (sink, ¤t, &pending, NULL);
- fail_unless (ret == GST_STATE_SUCCESS, "not playing");
- fail_unless (current == GST_STATE_PLAYING, "not playing");
- fail_unless (pending == GST_STATE_VOID_PENDING, "not playing");
+ GST_TIME_TO_TIMEVAL ((GstClockTime) 0, tv);
+
+ ret = gst_element_get_state (sink, ¤t, &pending, &tv);
+ fail_unless (ret == GST_STATE_ASYNC, "not changing state async");
+ fail_unless (current == GST_STATE_PAUSED, "bad current state");
+ fail_unless (pending == GST_STATE_PLAYING, "bad pending state");
+
+ ret = gst_element_set_state (sink, GST_STATE_PAUSED);
+ fail_unless (ret == GST_STATE_ASYNC, "no async going back to paused");
+
+ ret = gst_element_set_state (sink, GST_STATE_READY);
+ fail_unless (ret == GST_STATE_SUCCESS, "failed to go to ready");
+
+ gst_object_unref (sink);
}
GST_END_TEST
basesink->preroll_queued,
basesink->buffers_queued, basesink->events_queued);
+ if (basesink->playing_async)
+ goto playing_async;
+
/* check if we are prerolling */
if (!basesink->need_preroll)
goto no_preroll;
ret = gst_base_sink_preroll_queue_empty (basesink, pad);
GST_PREROLL_UNLOCK (pad);
+ return ret;
+ }
+playing_async:
+ {
+ GstFlowReturn ret;
+ gint t;
+
+ basesink->have_preroll = FALSE;
+ basesink->playing_async = FALSE;
+
+ /* handle buffer first */
+ ret = gst_base_sink_preroll_queue_empty (basesink, pad);
+
+ /* unroll locks, commit state, reacquire stream lock */
+ GST_PREROLL_UNLOCK (pad);
+ t = GST_STREAM_UNLOCK_FULL (pad);
+ GST_DEBUG ("released stream lock %d times", t);
+ if (t <= 0) {
+ GST_WARNING ("STREAM_LOCK should have been locked !!");
+ g_warning ("STREAM_LOCK should have been locked !!");
+ }
+ GST_STATE_LOCK (basesink);
+ GST_DEBUG ("commit state %p >", basesink);
+ gst_element_commit_state (GST_ELEMENT (basesink));
+ GST_STATE_UNLOCK (basesink);
+ if (t > 0)
+ GST_STREAM_LOCK_FULL (pad, t);
+
return ret;
}
flushing:
* thread to do this. */
if (basesink->eos) {
gst_base_sink_preroll_queue_empty (basesink, basesink->sinkpad);
+ } else if (!basesink->have_preroll) {
+ /* don't need preroll, but do queue a commit_state */
+ basesink->need_preroll = FALSE;
+ basesink->playing_async = TRUE;
+ ret = GST_STATE_ASYNC;
+ /* we know it's not waiting, no need to signal */
+ } else {
+ /* don't need the preroll anymore */
+ basesink->need_preroll = FALSE;
+ /* now let it play */
+ GST_PREROLL_SIGNAL (basesink->sinkpad);
}
- /* don't need the preroll anymore */
- basesink->need_preroll = FALSE;
- /* now let it play */
- GST_PREROLL_SIGNAL (basesink->sinkpad);
GST_PREROLL_UNLOCK (basesink->sinkpad);
break;
}
}
GST_UNLOCK (basesink);
+ basesink->playing_async = FALSE;
+
/* unlock any subclasses */
if (bclass->unlock)
bclass->unlock (basesink);
gboolean eos;
gboolean need_preroll;
gboolean have_preroll;
+ gboolean playing_async;
/*< private >*/
gpointer _gst_reserved[GST_PADDING];
GST_STATE_FINAL (element) = state;
if (ret == GST_STATE_ASYNC) {
gst_element_commit_state (element);
+ gst_element_lost_state (element);
}
/* start with the current state */
basesink->preroll_queued,
basesink->buffers_queued, basesink->events_queued);
+ if (basesink->playing_async)
+ goto playing_async;
+
/* check if we are prerolling */
if (!basesink->need_preroll)
goto no_preroll;
ret = gst_base_sink_preroll_queue_empty (basesink, pad);
GST_PREROLL_UNLOCK (pad);
+ return ret;
+ }
+playing_async:
+ {
+ GstFlowReturn ret;
+ gint t;
+
+ basesink->have_preroll = FALSE;
+ basesink->playing_async = FALSE;
+
+ /* handle buffer first */
+ ret = gst_base_sink_preroll_queue_empty (basesink, pad);
+
+ /* unroll locks, commit state, reacquire stream lock */
+ GST_PREROLL_UNLOCK (pad);
+ t = GST_STREAM_UNLOCK_FULL (pad);
+ GST_DEBUG ("released stream lock %d times", t);
+ if (t <= 0) {
+ GST_WARNING ("STREAM_LOCK should have been locked !!");
+ g_warning ("STREAM_LOCK should have been locked !!");
+ }
+ GST_STATE_LOCK (basesink);
+ GST_DEBUG ("commit state %p >", basesink);
+ gst_element_commit_state (GST_ELEMENT (basesink));
+ GST_STATE_UNLOCK (basesink);
+ if (t > 0)
+ GST_STREAM_LOCK_FULL (pad, t);
+
return ret;
}
flushing:
* thread to do this. */
if (basesink->eos) {
gst_base_sink_preroll_queue_empty (basesink, basesink->sinkpad);
+ } else if (!basesink->have_preroll) {
+ /* don't need preroll, but do queue a commit_state */
+ basesink->need_preroll = FALSE;
+ basesink->playing_async = TRUE;
+ ret = GST_STATE_ASYNC;
+ /* we know it's not waiting, no need to signal */
+ } else {
+ /* don't need the preroll anymore */
+ basesink->need_preroll = FALSE;
+ /* now let it play */
+ GST_PREROLL_SIGNAL (basesink->sinkpad);
}
- /* don't need the preroll anymore */
- basesink->need_preroll = FALSE;
- /* now let it play */
- GST_PREROLL_SIGNAL (basesink->sinkpad);
GST_PREROLL_UNLOCK (basesink->sinkpad);
break;
}
}
GST_UNLOCK (basesink);
+ basesink->playing_async = FALSE;
+
/* unlock any subclasses */
if (bclass->unlock)
bclass->unlock (basesink);
gboolean eos;
gboolean need_preroll;
gboolean have_preroll;
+ gboolean playing_async;
/*< private >*/
gpointer _gst_reserved[GST_PADDING];
GstElement *sink;
GstElementStateReturn ret;
GstElementState current, pending;
+ GTimeVal tv;
sink = gst_element_factory_make ("fakesink", "sink");
fail_unless (ret == GST_STATE_ASYNC, "no async state return");
ret = gst_element_set_state (sink, GST_STATE_PLAYING);
- fail_unless (ret == GST_STATE_SUCCESS, "cannot force play");
+ fail_unless (ret == GST_STATE_ASYNC, "no forced async state change");
- ret = gst_element_get_state (sink, ¤t, &pending, NULL);
- fail_unless (ret == GST_STATE_SUCCESS, "not playing");
- fail_unless (current == GST_STATE_PLAYING, "not playing");
- fail_unless (pending == GST_STATE_VOID_PENDING, "not playing");
+ GST_TIME_TO_TIMEVAL ((GstClockTime) 0, tv);
+
+ ret = gst_element_get_state (sink, ¤t, &pending, &tv);
+ fail_unless (ret == GST_STATE_ASYNC, "not changing state async");
+ fail_unless (current == GST_STATE_PAUSED, "bad current state");
+ fail_unless (pending == GST_STATE_PLAYING, "bad pending state");
+
+ ret = gst_element_set_state (sink, GST_STATE_PAUSED);
+ fail_unless (ret == GST_STATE_ASYNC, "no async going back to paused");
+
+ ret = gst_element_set_state (sink, GST_STATE_READY);
+ fail_unless (ret == GST_STATE_SUCCESS, "failed to go to ready");
+
+ gst_object_unref (sink);
}
GST_END_TEST