oggdemux: Fix reverse playback
authorJan Schmidt <jan@centricular.com>
Fri, 3 Mar 2017 13:27:30 +0000 (00:27 +1100)
committerJan Schmidt <jan@centricular.com>
Fri, 3 Mar 2017 13:30:37 +0000 (00:30 +1100)
Fix various issues with reverse playback by clearing tracking
vars when working in reverse, and where possible using the
timestamp interpolation code to generate timestamps for
outgoing buffers. Make sure to mark things as discontinuous
only when looping backward to a new position and fix seeking
to the next page when starting.

ext/ogg/gstoggdemux.c

index ff2cc61..51c5509 100644 (file)
@@ -676,7 +676,7 @@ gst_ogg_demux_chain_peer (GstOggPad * pad, ogg_packet * packet,
           packet->granulepos);
       GST_DEBUG_OBJECT (ogg, "new granule %" G_GUINT64_FORMAT,
           pad->current_granule);
-    } else if (ogg->segment.rate > 0.0 && pad->current_granule != -1) {
+    } else if (pad->current_granule != -1) {
       pad->current_granule += duration;
       if (!delta_unit) {
         pad->keyframe_granule = pad->current_granule;
@@ -684,19 +684,23 @@ gst_ogg_demux_chain_peer (GstOggPad * pad, ogg_packet * packet,
       GST_DEBUG_OBJECT (ogg, "interpolating granule %" G_GUINT64_FORMAT,
           pad->current_granule);
     }
-    if (ogg->segment.rate < 0.0 && packet->granulepos == -1) {
-      /* negative rates, only set timestamp on the packets with a granulepos */
+
+    if (ogg->segment.rate < 0.0 && pad->current_granule == -1) {
+      /* negative rates, allow output of packets with no timestamp, let downstream reconstruct */
       out_timestamp = -1;
       out_duration = -1;
       out_offset = -1;
       out_offset_end = -1;
+      pad->prev_granule = -1;
     } else {
       /* we only push buffers after we have a valid granule. This is done so that
        * we nicely skip packets without a timestamp after a seek. This is ok
-       * because we base or seek on the packet after the page with the smaller
+       * because we base our seek on the packet after the page with the smaller
        * timestamp. */
-      if (pad->current_granule == -1)
+      if (pad->current_granule == -1) {
+        pad->prev_granule = -1;
         goto no_timestamp;
+      }
 
       if (pad->map.is_ogm) {
         out_timestamp = gst_ogg_stream_granule_to_time (&pad->map,
@@ -802,9 +806,8 @@ gst_ogg_demux_chain_peer (GstOggPad * pad, ogg_packet * packet,
   /* Mark discont on the buffer */
   if (pad->discont) {
     GST_BUFFER_FLAG_SET (buf, GST_BUFFER_FLAG_DISCONT);
-    if GST_BUFFER_TIMESTAMP_IS_VALID
-      (buf)
-          pad->discont = FALSE;
+    if (ogg->segment.rate < 0.0 || GST_BUFFER_TIMESTAMP_IS_VALID (buf))
+      pad->discont = FALSE;
   }
 
   /* don't push the header packets when we are asked to skip them */
@@ -2100,6 +2103,7 @@ gst_ogg_chain_free (GstOggChain * chain)
 static void
 gst_ogg_pad_mark_discont (GstOggPad * pad)
 {
+  GST_LOG_OBJECT (pad, "Marking discont on pad");
   pad->discont = TRUE;
   pad->map.last_size = 0;
 }
@@ -3341,8 +3345,12 @@ gst_ogg_demux_do_seek (GstOggDemux * ogg, GstSegment * segment,
       granule_time = gst_ogg_stream_get_end_time_for_granulepos (&pad->map,
           granulepos);
 
+      /* Convert to stream time */
+      granule_time -= pad->start_time;
+      granule_time += chain->begin_time;
+
       GST_LOG_OBJECT (ogg,
-          "looking at page with ts %" GST_TIME_FORMAT ", target %"
+          "looking at page with time %" GST_TIME_FORMAT ", target %"
           GST_TIME_FORMAT, GST_TIME_ARGS (granule_time),
           GST_TIME_ARGS (target));
       if (granule_time < target)
@@ -4435,7 +4443,7 @@ gst_ogg_demux_update_chunk_size (GstOggDemux * ogg, ogg_page * page)
 }
 
 static GstFlowReturn
-gst_ogg_demux_handle_page (GstOggDemux * ogg, ogg_page * page)
+gst_ogg_demux_handle_page (GstOggDemux * ogg, ogg_page * page, gboolean discont)
 {
   GstOggPad *pad;
   gint64 granule;
@@ -4517,6 +4525,10 @@ gst_ogg_demux_handle_page (GstOggDemux * ogg, ogg_page * page)
     pad = gst_ogg_demux_find_pad (ogg, serialno);
   }
   if (pad) {
+    /* Reset granule interpolation if chaining in reverse (discont = TRUE) */
+    if (discont)
+      pad->current_granule = -1;
+
     result = gst_ogg_pad_submit_page (pad, page);
   } else {
     GST_PUSH_LOCK (ogg);
@@ -4595,7 +4607,7 @@ gst_ogg_demux_chain (GstPad * pad, GstObject * parent, GstBuffer * buffer)
       /* discontinuity in the pages */
       GST_DEBUG_OBJECT (ogg, "discont in page found, continuing");
     } else {
-      result = gst_ogg_demux_handle_page (ogg, &page);
+      result = gst_ogg_demux_handle_page (ogg, &page, FALSE);
       if (result < 0) {
         GST_DEBUG_OBJECT (ogg, "gst_ogg_demux_handle_page returned %d", result);
       }
@@ -4718,7 +4730,9 @@ gst_ogg_demux_loop_reverse (GstOggDemux * ogg)
     ogg->newsegment = NULL;
   }
 
-  ret = gst_ogg_demux_handle_page (ogg, &page);
+  GST_LOG_OBJECT (ogg, "Handling page at offset %" G_GINT64_FORMAT,
+      ogg->offset);
+  ret = gst_ogg_demux_handle_page (ogg, &page, TRUE);
 
 done:
   return ret;