diff options
author | Sebastian Dröge <sebastian.droege@collabora.co.uk> | 2010-07-24 18:17:43 +0200 |
---|---|---|
committer | Sebastian Dröge <sebastian.droege@collabora.co.uk> | 2010-07-24 18:17:43 +0200 |
commit | 175aae9e85365f681e73a7bf427a6685e6f4faa1 (patch) | |
tree | 1db8f313eabd8e85b365bfb58f8f385413e1f9fa | |
parent | ae03de8ff811090d20e92073aa3611e2f47cb561 (diff) |
streamsynchronizer: Delay EOS events until all streams are EOS
This fixes a race condition in playbin2's gapless mode, where the
EOS of other streams might arrive in the sinks before the last stream
ends and the switch to the new track happens. The EOS sinks won't
accept any new data then and playback stops.
To prevent this, delay all EOS events until all streams are EOS
and advance the sinks of the EOS streams by filler newsegment
events if necessary.
Fixes bug #625118.
-rw-r--r-- | gst/playback/gststreamsynchronizer.c | 84 |
1 files changed, 84 insertions, 0 deletions
diff --git a/gst/playback/gststreamsynchronizer.c b/gst/playback/gststreamsynchronizer.c index d4b8abe75..78bf00d3e 100644 --- a/gst/playback/gststreamsynchronizer.c +++ b/gst/playback/gststreamsynchronizer.c @@ -66,6 +66,7 @@ typedef struct gboolean wait; gboolean new_stream; gboolean drop_discont; + gboolean is_eos; gint64 running_time_diff; } GstStream; @@ -447,6 +448,40 @@ gst_stream_synchronizer_sink_event (GstPad * pad, GstEvent * event) GST_STREAM_SYNCHRONIZER_UNLOCK (self); break; } + case GST_EVENT_EOS:{ + GstStream *stream; + GList *l; + gboolean all_eos = TRUE; + + GST_STREAM_SYNCHRONIZER_LOCK (self); + stream = gst_pad_get_element_private (pad); + if (stream) { + GST_DEBUG_OBJECT (pad, "Have EOS for stream %d", stream->stream_number); + stream->is_eos = TRUE; + } + + for (l = self->streams; l; l = l->next) { + GstStream *ostream = l->data; + + all_eos = all_eos && ostream->is_eos; + if (!all_eos) + break; + } + + if (all_eos) { + ret = TRUE; + GST_DEBUG_OBJECT (self, "All streams are EOS -- forwarding"); + for (l = self->streams; l; l = l->next) { + GstStream *ostream = l->data; + ret = ret + && gst_pad_push_event (ostream->srcpad, gst_event_new_eos ()); + } + } + GST_STREAM_SYNCHRONIZER_UNLOCK (self); + + goto done; + break; + } default: break; } @@ -533,6 +568,8 @@ gst_stream_synchronizer_sink_chain (GstPad * pad, GstBuffer * buffer) GST_LOG_OBJECT (pad, "Push returned: %s", gst_flow_get_name (ret)); if (ret == GST_FLOW_OK) { + GList *l; + GST_STREAM_SYNCHRONIZER_LOCK (self); stream = gst_pad_get_element_private (pad); if (stream && stream->segment.format == GST_FORMAT_TIME @@ -544,6 +581,43 @@ gst_stream_synchronizer_sink_chain (GstPad * pad, GstBuffer * buffer) gst_segment_set_last_stop (&stream->segment, GST_FORMAT_TIME, timestamp_end); } + + /* Advance EOS streams if necessary. For non-EOS + * streams the demuxers should already do this! */ + for (l = self->streams; l; l = l->next) { + GstStream *ostream = l->data; + gint64 last_stop; + + if (!ostream->is_eos || ostream->segment.format != GST_FORMAT_TIME) + continue; + + if (ostream->segment.last_stop != -1) + last_stop = ostream->segment.last_stop; + else + last_stop = ostream->segment.start; + + /* Is there a 1 second lag? */ + if (last_stop != -1 && last_stop + GST_SECOND < timestamp_end) { + gint64 new_start; + + new_start = timestamp_end - GST_SECOND; + + GST_DEBUG_OBJECT (ostream->sinkpad, + "Advancing stream %d from %" GST_TIME_FORMAT " to %" + GST_TIME_FORMAT, ostream->stream_number, last_stop, new_start); + + gst_pad_push_event (ostream->srcpad, + gst_event_new_new_segment_full (TRUE, ostream->segment.rate, + ostream->segment.applied_rate, ostream->segment.format, + new_start, ostream->segment.stop, new_start)); + gst_segment_set_newsegment_full (&ostream->segment, TRUE, + ostream->segment.rate, ostream->segment.applied_rate, + ostream->segment.format, new_start, ostream->segment.stop, + new_start); + gst_segment_set_last_stop (&ostream->segment, GST_FORMAT_TIME, + new_start); + } + } GST_STREAM_SYNCHRONIZER_UNLOCK (self); } @@ -666,6 +740,15 @@ gst_stream_synchronizer_release_stream (GstStreamSynchronizer * self, } g_slice_free (GstStream, stream); + + /* NOTE: In theory we have to check here if all streams + * are EOS but the one that was removed wasn't and then + * send EOS downstream. But due to the way how playsink + * works this is not necessary and will only cause problems + * for gapless playback. playsink will only add/remove pads + * when it's reconfigured, which happens when the streams + * change + */ } static void @@ -742,6 +825,7 @@ gst_stream_synchronizer_change_state (GstElement * element, stream->wait = FALSE; stream->new_stream = FALSE; stream->drop_discont = FALSE; + stream->is_eos = FALSE; } GST_STREAM_SYNCHRONIZER_UNLOCK (self); break; |