qtdemux: respect qt segments in push-mode for empty starts
authorThiago Santos <thiagoss@osg.samsung.com>
Mon, 10 Aug 2015 14:23:45 +0000 (11:23 -0300)
committerThiago Santos <thiagoss@osg.samsung.com>
Mon, 9 Nov 2015 14:49:27 +0000 (11:49 -0300)
In push-mode it is hard to support qt segments overall but it is
possible to support when the file isn't heavily edited but just contain
a segment to indicate a gap at the beginning. This also allows properly
timestamping data that has negative DTS in push-mode.

It is relevant to support those for 2 scenarios:

1) fragmented streaming
2) HTTP playback of 'regular' mp4

https://bugzilla.gnome.org/show_bug.cgi?id=753484

gst/isomp4/qtdemux.c

index 7945495..19ecaac 100644 (file)
@@ -4131,8 +4131,6 @@ eos:
  * This will push out a NEWSEGMENT event with the right values and
  * position the stream index to the first decodable sample before
  * @offset.
- *
- * PULL-BASED
  */
 static gboolean
 gst_qtdemux_activate_segment (GstQTDemux * qtdemux, QtDemuxStream * stream,
@@ -4264,6 +4262,10 @@ gst_qtdemux_activate_segment (GstQTDemux * qtdemux, QtDemuxStream * stream,
     return TRUE;
   }
 
+  /* We don't need to look for a sample in push-based */
+  if (!qtdemux->pullbased)
+    return TRUE;
+
   /* and move to the keyframe before the indicated media time of the
    * segment */
   if (G_LIKELY (!QTSEGMENT_IS_EMPTY (segment))) {
@@ -5529,6 +5531,62 @@ gst_qtdemux_drop_data (GstQTDemux * demux, gint bytes)
   demux->todrop -= bytes;
 }
 
+static void
+gst_qtdemux_check_send_pending_segment (GstQTDemux * demux)
+{
+  if (G_UNLIKELY (demux->pending_newsegment)) {
+    gint i;
+
+    gst_qtdemux_push_pending_newsegment (demux);
+    /* clear to send tags on all streams */
+    for (i = 0; i < demux->n_streams; i++) {
+      QtDemuxStream *stream;
+      stream = demux->streams[i];
+      gst_qtdemux_push_tags (demux, stream);
+      if (stream->sparse) {
+        GST_INFO_OBJECT (demux, "Sending gap event on stream %d", i);
+        gst_pad_push_event (stream->pad,
+            gst_event_new_gap (stream->segment.position, GST_CLOCK_TIME_NONE));
+      }
+    }
+  }
+}
+
+static void
+gst_qtdemux_stream_send_initial_gap_segments (GstQTDemux * demux,
+    QtDemuxStream * stream)
+{
+  gint i;
+
+  /* Push any initial gap segments before proceeding to the
+   * 'real' data */
+  for (i = 0; i < stream->n_segments; i++) {
+    gst_qtdemux_activate_segment (demux, stream, i, stream->time_position);
+
+    if (QTSEGMENT_IS_EMPTY (&stream->segments[i])) {
+      GstClockTime ts, dur;
+      GstEvent *gap;
+
+      ts = stream->time_position;
+      dur =
+          stream->segments[i].duration - (stream->time_position -
+          stream->segments[i].time);
+      gap = gst_event_new_gap (ts, dur);
+      stream->time_position += dur;
+
+      GST_DEBUG_OBJECT (stream->pad, "Pushing gap for empty "
+          "segment: %" GST_PTR_FORMAT, gap);
+      gst_pad_push_event (stream->pad, gap);
+    } else {
+      /* Only support empty segment at the beginning followed by
+       * one non-empty segment, this was checked when parsing the
+       * edts atom, arriving here is unexpected */
+      g_assert (i + 1 == stream->n_segments);
+      break;
+    }
+  }
+}
+
 static GstFlowReturn
 gst_qtdemux_chain (GstPad * sinkpad, GstObject * parent, GstBuffer * inbuf)
 {
@@ -5698,6 +5756,8 @@ gst_qtdemux_process_adapter (GstQTDemux * demux, gboolean force)
         extract_initial_length_and_fourcc (data, demux->neededbytes, NULL,
             &fourcc);
         if (fourcc == FOURCC_moov) {
+          gint n;
+
           /* in usual fragmented setup we could try to scan for more
            * and end up at the the moov (after mdat) again */
           if (demux->got_moov && demux->n_streams > 0 &&
@@ -5731,7 +5791,6 @@ gst_qtdemux_process_adapter (GstQTDemux * demux, gboolean force)
             if (!demux->got_moov)
               qtdemux_expose_streams (demux);
             else {
-              gint n;
 
               for (n = 0; n < demux->n_streams; n++) {
                 QtDemuxStream *stream = demux->streams[n];
@@ -5741,6 +5800,11 @@ gst_qtdemux_process_adapter (GstQTDemux * demux, gboolean force)
             }
 
             demux->got_moov = TRUE;
+            gst_qtdemux_check_send_pending_segment (demux);
+            for (n = 0; n < demux->n_streams; n++) {
+              gst_qtdemux_stream_send_initial_gap_segments (demux,
+                  demux->streams[n]);
+            }
 
             g_node_destroy (demux->moov_node);
             demux->moov_node = NULL;
@@ -5966,20 +6030,7 @@ gst_qtdemux_process_adapter (GstQTDemux * demux, gboolean force)
         /* first buffer? */
         /* initial newsegment sent here after having added pads,
          * possible others in sink_event */
-        if (G_UNLIKELY (demux->pending_newsegment)) {
-          gst_qtdemux_push_pending_newsegment (demux);
-          /* clear to send tags on all streams */
-          for (i = 0; i < demux->n_streams; i++) {
-            stream = demux->streams[i];
-            gst_qtdemux_push_tags (demux, stream);
-            if (stream->sparse) {
-              GST_INFO_OBJECT (demux, "Sending gap event on stream %d", i);
-              gst_pad_push_event (stream->pad,
-                  gst_event_new_gap (stream->segment.position,
-                      GST_CLOCK_TIME_NONE));
-            }
-          }
-        }
+        gst_qtdemux_check_send_pending_segment (demux);
 
         /* Figure out which stream this packet belongs to */
         for (i = 0; i < demux->n_streams; i++) {
@@ -7876,6 +7927,10 @@ qtdemux_parse_segments (GstQTDemux * qtdemux, QtDemuxStream * stream,
     GNode * trak)
 {
   GNode *edts;
+  /* accept edts if they contain gaps at start and there is only
+   * one media segment */
+  gboolean allow_pushbased_edts = TRUE;
+  gint media_segments_count = 0;
 
   /* parse and prepare segment info from the edit list */
   GST_DEBUG_OBJECT (qtdemux, "looking for edit list container");
@@ -7928,6 +7983,7 @@ qtdemux_parse_segments (GstQTDemux * qtdemux, QtDemuxStream * stream,
       if (media_time != G_MAXUINT32) {
         segment->media_start = QTSTREAMTIME_TO_GSTTIME (stream, media_time);
         segment->media_stop = segment->media_start + segment->duration;
+        media_segments_count++;
       } else {
         segment->media_start = GST_CLOCK_TIME_NONE;
         segment->media_stop = GST_CLOCK_TIME_NONE;
@@ -7965,12 +8021,14 @@ qtdemux_parse_segments (GstQTDemux * qtdemux, QtDemuxStream * stream,
     }
     GST_DEBUG_OBJECT (qtdemux, "found %d segments", count);
     stream->n_segments = count;
+    if (media_segments_count != 1)
+      allow_pushbased_edts = FALSE;
   }
 done:
 
   /* push based does not handle segments, so act accordingly here,
    * and warn if applicable */
-  if (!qtdemux->pullbased) {
+  if (!qtdemux->pullbased && !allow_pushbased_edts) {
     GST_WARNING_OBJECT (qtdemux, "streaming; discarding edit list segments");
     /* remove and use default one below, we stream like it anyway */
     g_free (stream->segments);