qtdemux: some stream synchronization to aid seeking in unbalanced clips
authorMark Nauwelaerts <mark.nauwelaerts@collabora.co.uk>
Thu, 26 Mar 2009 13:39:06 +0000 (14:39 +0100)
committerMark Nauwelaerts <mark.nauwelaerts@collabora.co.uk>
Thu, 26 Mar 2009 13:39:06 +0000 (14:39 +0100)
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 ?)

gst/qtdemux/qtdemux.c

index 6f5b1c0..87d458a 100644 (file)
@@ -231,6 +231,8 @@ struct _QtDemuxStream
   QtDemuxSegment *segments;
   guint32 from_sample;
   guint32 to_sample;
+
+  gboolean sent_eos;
 };
 
 enum QtDemuxState
@@ -922,6 +924,7 @@ gst_qtdemux_perform_seek (GstQTDemux * qtdemux, GstSegment * segment)
     stream->sample_index = -1;
     stream->segment_index = -1;
     stream->last_ret = GST_FLOW_OK;
+    stream->sent_eos = FALSE;
   }
   segment->last_stop = desired_offset;
   segment->time = desired_offset;
@@ -1253,8 +1256,10 @@ gst_qtdemux_handle_sink_event (GstPad * sinkpad, GstEvent * event)
       demux->offset = 0;
       demux->neededbytes = -1;
       /* 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]->sent_eos = FALSE;
+      }
       break;
     }
     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:
  *  
  *  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 */
   if (!gst_qtdemux_prepare_current_sample (qtdemux, stream, &offset, &size,
           &timestamp, &duration, &keyframe))
-    goto eos;
+    goto eos_stream;
 
   GST_LOG_OBJECT (qtdemux,
       "pushing from stream %d, offset %" G_GUINT64_FORMAT
@@ -2159,6 +2217,7 @@ gst_qtdemux_loop_state_movie (GstQTDemux * qtdemux)
   qtdemux->last_ts = min_time;
   if (qtdemux->segment.rate >= 0) {
     gst_segment_set_last_stop (&qtdemux->segment, GST_FORMAT_TIME, min_time);
+    gst_qtdemux_sync_streams (qtdemux);
   }
   if (stream->pad) {
     /* we're going to modify the metadata */
@@ -2219,6 +2278,13 @@ eos:
     ret = GST_FLOW_UNEXPECTED;
     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
@@ -2591,6 +2657,7 @@ gst_qtdemux_chain (GstPad * sinkpad, GstBuffer * inbuf)
         /* position reporting */
         gst_segment_set_last_stop (&demux->segment, GST_FORMAT_TIME,
             demux->last_ts);
+        gst_qtdemux_sync_streams (demux);
 
         /* send buffer */
         if (stream->pad) {
@@ -3625,14 +3692,22 @@ done:
 
   /* no segments, create one to play the complete trak */
   if (stream->n_segments == 0) {
+    GstClockTime stream_duration = 0;
+
     if (stream->segments == NULL)
       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].stop_time = qtdemux->segment.duration;
-    stream->segments[0].duration = qtdemux->segment.duration;
+    stream->segments[0].stop_time = stream_duration;
+    stream->segments[0].duration = stream_duration;
     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;
 
     GST_DEBUG_OBJECT (qtdemux, "created dummy segment");