qtdemux: some stream synchronization to aid seeking in unbalanced clips

Some clips (trailers) may have (length-wise) unbalanced streams,
which stalls the pipeline if seeking into that region.
Additional stream synchronization can handle this, as well as
sparse (subtitle) streams (at some later time ?)
This commit is contained in:
Mark Nauwelaerts 2009-03-26 14:39:06 +01:00
parent a5502c9b37
commit 3360f449c0

View file

@ -231,6 +231,8 @@ struct _QtDemuxStream
QtDemuxSegment *segments; QtDemuxSegment *segments;
guint32 from_sample; guint32 from_sample;
guint32 to_sample; guint32 to_sample;
gboolean sent_eos;
}; };
enum QtDemuxState enum QtDemuxState
@ -922,6 +924,7 @@ gst_qtdemux_perform_seek (GstQTDemux * qtdemux, GstSegment * segment)
stream->sample_index = -1; stream->sample_index = -1;
stream->segment_index = -1; stream->segment_index = -1;
stream->last_ret = GST_FLOW_OK; stream->last_ret = GST_FLOW_OK;
stream->sent_eos = FALSE;
} }
segment->last_stop = desired_offset; segment->last_stop = desired_offset;
segment->time = desired_offset; segment->time = desired_offset;
@ -1253,8 +1256,10 @@ gst_qtdemux_handle_sink_event (GstPad * sinkpad, GstEvent * event)
demux->offset = 0; demux->offset = 0;
demux->neededbytes = -1; demux->neededbytes = -1;
/* reset flow return, e.g. following seek */ /* reset flow return, e.g. following seek */
for (i = 0; i < demux->n_streams; i++) for (i = 0; i < demux->n_streams; i++) {
demux->streams[i]->last_ret = GST_FLOW_OK; demux->streams[i]->last_ret = GST_FLOW_OK;
demux->streams[i]->sent_eos = FALSE;
}
break; break;
} }
case GST_EVENT_EOS: case GST_EVENT_EOS:
@ -1919,6 +1924,59 @@ next_segment:
} }
} }
static void
gst_qtdemux_sync_streams (GstQTDemux * demux)
{
gint i;
if (demux->n_streams <= 1)
return;
for (i = 0; i < demux->n_streams; i++) {
QtDemuxStream *stream;
GstClockTime end_time;
stream = demux->streams[i];
if (!stream->pad)
continue;
/* TODO advance time on subtitle streams here, if any some day */
/* some clips/trailers may have unbalanced streams at the end,
* so send EOS on shorter stream to prevent stalling others */
/* do not mess with EOS if SEGMENT seeking */
if (demux->segment.flags & GST_SEEK_FLAG_SEGMENT)
continue;
if (demux->pullbased) {
/* loop mode is sample time based */
if (stream->time_position != -1)
continue;
} else {
/* push mode is byte position based */
if (stream->samples[stream->n_samples - 1].offset >= demux->offset)
continue;
}
if (stream->sent_eos)
continue;
/* only act if some gap */
end_time = stream->segments[stream->n_segments - 1].stop_time;
GST_LOG_OBJECT (demux, "current position: %" GST_TIME_FORMAT
", stream end: %" GST_TIME_FORMAT, GST_TIME_ARGS (end_time),
GST_TIME_ARGS (demux->segment.last_stop));
if (end_time + 2 * GST_SECOND < demux->segment.last_stop) {
GST_DEBUG_OBJECT (demux, "sending EOS for stream %s",
GST_PAD_NAME (stream->pad));
stream->sent_eos = TRUE;
gst_pad_push_event (stream->pad, gst_event_new_eos ());
}
}
}
/* UNEXPECTED and NOT_LINKED need to be combined. This means that we return: /* UNEXPECTED and NOT_LINKED need to be combined. This means that we return:
* *
* GST_FLOW_NOT_LINKED: when all pads NOT_LINKED. * GST_FLOW_NOT_LINKED: when all pads NOT_LINKED.
@ -2119,7 +2177,7 @@ gst_qtdemux_loop_state_movie (GstQTDemux * qtdemux)
/* fetch info for the current sample of this stream */ /* fetch info for the current sample of this stream */
if (!gst_qtdemux_prepare_current_sample (qtdemux, stream, &offset, &size, if (!gst_qtdemux_prepare_current_sample (qtdemux, stream, &offset, &size,
&timestamp, &duration, &keyframe)) &timestamp, &duration, &keyframe))
goto eos; goto eos_stream;
GST_LOG_OBJECT (qtdemux, GST_LOG_OBJECT (qtdemux,
"pushing from stream %d, offset %" G_GUINT64_FORMAT "pushing from stream %d, offset %" G_GUINT64_FORMAT
@ -2159,6 +2217,7 @@ gst_qtdemux_loop_state_movie (GstQTDemux * qtdemux)
qtdemux->last_ts = min_time; qtdemux->last_ts = min_time;
if (qtdemux->segment.rate >= 0) { if (qtdemux->segment.rate >= 0) {
gst_segment_set_last_stop (&qtdemux->segment, GST_FORMAT_TIME, min_time); gst_segment_set_last_stop (&qtdemux->segment, GST_FORMAT_TIME, min_time);
gst_qtdemux_sync_streams (qtdemux);
} }
if (stream->pad) { if (stream->pad) {
/* we're going to modify the metadata */ /* we're going to modify the metadata */
@ -2219,6 +2278,13 @@ eos:
ret = GST_FLOW_UNEXPECTED; ret = GST_FLOW_UNEXPECTED;
goto beach; goto beach;
} }
eos_stream:
{
GST_DEBUG_OBJECT (qtdemux, "No samples left for stream");
/* EOS will be raised if all are EOS */
ret = GST_FLOW_OK;
goto beach;
}
} }
static void static void
@ -2591,6 +2657,7 @@ gst_qtdemux_chain (GstPad * sinkpad, GstBuffer * inbuf)
/* position reporting */ /* position reporting */
gst_segment_set_last_stop (&demux->segment, GST_FORMAT_TIME, gst_segment_set_last_stop (&demux->segment, GST_FORMAT_TIME,
demux->last_ts); demux->last_ts);
gst_qtdemux_sync_streams (demux);
/* send buffer */ /* send buffer */
if (stream->pad) { if (stream->pad) {
@ -3625,14 +3692,22 @@ done:
/* no segments, create one to play the complete trak */ /* no segments, create one to play the complete trak */
if (stream->n_segments == 0) { if (stream->n_segments == 0) {
GstClockTime stream_duration = 0;
if (stream->segments == NULL) if (stream->segments == NULL)
stream->segments = g_new (QtDemuxSegment, 1); stream->segments = g_new (QtDemuxSegment, 1);
/* samples know best */
if (stream->n_samples > 0)
stream_duration =
stream->samples[stream->n_samples - 1].timestamp +
stream->samples[stream->n_samples - 1].pts_offset;
stream->segments[0].time = 0; stream->segments[0].time = 0;
stream->segments[0].stop_time = qtdemux->segment.duration; stream->segments[0].stop_time = stream_duration;
stream->segments[0].duration = qtdemux->segment.duration; stream->segments[0].duration = stream_duration;
stream->segments[0].media_start = 0; stream->segments[0].media_start = 0;
stream->segments[0].media_stop = qtdemux->segment.duration; stream->segments[0].media_stop = stream_duration;
stream->segments[0].rate = 1.0; stream->segments[0].rate = 1.0;
GST_DEBUG_OBJECT (qtdemux, "created dummy segment"); GST_DEBUG_OBJECT (qtdemux, "created dummy segment");