adaptivedemux: Handle prepared streams on seeks
authorEdward Hervey <edward@centricular.com>
Tue, 9 May 2017 09:41:49 +0000 (11:41 +0200)
committerEdward Hervey <bilboed@bilboed.com>
Thu, 13 Jul 2017 09:15:38 +0000 (11:15 +0200)
This is a race that was exposed by the {hls|dash}.scrub_forward_seeking
validate test.

The "race" is that a subclass might want to change format, causing
a new stream to be created (but not exposed/switched yet) and put on the
prepared_streams list. That stream will have values (including pending
segment) from the pre-seek state.

Before the stream is exposed/switched, a new seek comes in and the stream
values get updated ... but the ones that will be changed don't get updated
causing them to push out wrong segments once they are exposed.

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

gst-libs/gst/adaptivedemux/gstadaptivedemux.c

index 3c98eac126eedef334452930c999435bb86e973c..ddcc8126fe432ebc70fd9c7fabea48bf15fc2eb2 100644 (file)
@@ -1702,6 +1702,35 @@ gst_adaptive_demux_handle_seek_event (GstAdaptiveDemux * demux, GstPad * pad,
       seg_evt = gst_event_new_segment (&stream->segment);
       gst_event_set_seqnum (seg_evt, demux->priv->segment_seqnum);
       gst_event_replace (&stream->pending_segment, seg_evt);
+      GST_DEBUG_OBJECT (stream->pad, "Pending segment now %" GST_SEGMENT_FORMAT,
+          &stream->pending_segment);
+      gst_event_unref (seg_evt);
+      /* Make sure the first buffer after a seek has the discont flag */
+      stream->discont = TRUE;
+    }
+    /* Same thing but for prepared streams that haven't been exposed yet */
+    for (iter = demux->prepared_streams; iter; iter = g_list_next (iter)) {
+      GstAdaptiveDemuxStream *stream = iter->data;
+      GstEvent *seg_evt;
+      GstClockTime offset;
+
+      /* See comments in gst_adaptive_demux_get_period_start_time() for
+       * an explanation of the segment modifications */
+      stream->segment = demux->segment;
+      offset =
+          gst_adaptive_demux_stream_get_presentation_offset (demux, stream);
+      stream->segment.start += offset - period_start;
+      if (GST_CLOCK_TIME_IS_VALID (demux->segment.stop))
+        stream->segment.stop += offset - period_start;
+      if (demux->segment.rate > 0 && start_type != GST_SEEK_TYPE_NONE)
+        stream->segment.position = stream->segment.start;
+      else if (demux->segment.rate < 0 && stop_type != GST_SEEK_TYPE_NONE)
+        stream->segment.position = stream->segment.stop;
+      seg_evt = gst_event_new_segment (&stream->segment);
+      gst_event_set_seqnum (seg_evt, demux->priv->segment_seqnum);
+      gst_event_replace (&stream->pending_segment, seg_evt);
+      GST_DEBUG_OBJECT (stream->pad, "Pending segment now %" GST_SEGMENT_FORMAT,
+          &stream->pending_segment);
       gst_event_unref (seg_evt);
       /* Make sure the first buffer after a seek has the discont flag */
       stream->discont = TRUE;