matroskademux: skip buffers before a late keyframe (QoS)
authorPhilip Jägenstedt <philipj@opera.com>
Sun, 23 May 2010 07:32:08 +0000 (09:32 +0200)
committerSebastian Dröge <sebastian.droege@collabora.co.uk>
Tue, 1 Jun 2010 09:21:29 +0000 (11:21 +0200)
Before, vp8dec had no option but to decode all frames even if some/all
of them would be late. With this change, performance when keyframes are
frequent is helped a great deal. On my Thinkpad X60s, decoding a 20 s
1080p sunflower encode with keyframes every 10 frames went from taking
42 s with 5 frames shown to 21 s with 15 frames shown (still slow
enough to count by hand). When keyframes are more sparse, you will
still be able to catch up eventually, but the results won't be as
noticable.

gst/matroska/matroska-demux.c
gst/matroska/matroska-ids.c
gst/matroska/matroska-ids.h

index a5f551d1da15d744504b55f51186ad3129195322..06a5a69bf9afb52e0b558fb304de36f619bc9415 100644 (file)
@@ -2197,6 +2197,13 @@ gst_matroska_demux_reset_streams (GstMatroskaDemux * demux, GstClockTime time,
     context->from_time = GST_CLOCK_TIME_NONE;
     if (full)
       context->last_flow = GST_FLOW_OK;
+    if (context->type == GST_MATROSKA_TRACK_TYPE_VIDEO) {
+      GstMatroskaTrackVideoContext *videocontext =
+          (GstMatroskaTrackVideoContext *) context;
+      GST_OBJECT_LOCK (demux);
+      videocontext->earliest_time = GST_CLOCK_TIME_NONE;
+      GST_OBJECT_UNLOCK (demux);
+    }
   }
 }
 
@@ -2496,9 +2503,29 @@ gst_matroska_demux_handle_src_event (GstPad * pad, GstEvent * event)
       gst_event_unref (event);
       break;
 
+    case GST_EVENT_QOS:
+    {
+      GstMatroskaTrackContext *context = gst_pad_get_element_private (pad);
+      if (context->type == GST_MATROSKA_TRACK_TYPE_VIDEO) {
+        GstMatroskaTrackVideoContext *videocontext =
+            (GstMatroskaTrackVideoContext *) context;
+        gdouble proportion;
+        GstClockTimeDiff diff;
+        GstClockTime timestamp;
+
+        gst_event_parse_qos (event, &proportion, &diff, &timestamp);
+
+        GST_OBJECT_LOCK (demux);
+        videocontext->earliest_time = timestamp + diff;
+        GST_OBJECT_UNLOCK (demux);
+      }
+      res = TRUE;
+      gst_event_unref (event);
+      break;
+    }
+
       /* events we don't need to handle */
     case GST_EVENT_NAVIGATION:
-    case GST_EVENT_QOS:
       gst_event_unref (event);
       res = FALSE;
       break;
@@ -4635,6 +4662,42 @@ gst_matroska_demux_parse_blockgroup_or_simpleblock (GstMatroskaDemux * demux,
         break;
       }
 
+      /* QoS for video track with an index. the assumption is that
+         index entries point to keyframes, but if that is not true we
+         will instad skip until the next keyframe. */
+      if (GST_CLOCK_TIME_IS_VALID (lace_time) &&
+          stream->type == GST_MATROSKA_TRACK_TYPE_VIDEO &&
+          stream->index_table) {
+        GstMatroskaTrackVideoContext *videocontext =
+            (GstMatroskaTrackVideoContext *) stream;
+        GstClockTime running_time;
+        GstClockTime earliest_time;
+        running_time = gst_segment_to_running_time (&demux->segment,
+            GST_FORMAT_TIME, lace_time);
+        GST_OBJECT_LOCK (demux);
+        earliest_time = videocontext->earliest_time;
+        GST_OBJECT_UNLOCK (demux);
+        if (GST_CLOCK_TIME_IS_VALID (running_time) &&
+            GST_CLOCK_TIME_IS_VALID (earliest_time) &&
+            running_time <= earliest_time) {
+          /* find index entry (keyframe) <= earliest_time */
+          GstMatroskaIndex *entry =
+              gst_util_array_binary_search (stream->index_table->data,
+              stream->index_table->len, sizeof (GstMatroskaIndex),
+              (GCompareDataFunc) gst_matroska_index_seek_find,
+              GST_SEARCH_MODE_BEFORE, &earliest_time, NULL);
+          /* if that entry (keyframe) is after the current the current
+             buffer, we can skip pushing (and thus decoding) all
+             buffers until that keyframe. */
+          if (entry && GST_CLOCK_TIME_IS_VALID (entry->time) &&
+              entry->time > lace_time) {
+            GST_LOG_OBJECT (demux, "Skipping lace before late keyframe");
+            stream->set_discont = TRUE;
+            goto next_lace;
+          }
+        }
+      }
+
       sub = gst_buffer_create_sub (buf,
           GST_BUFFER_SIZE (buf) - size, lace_size[n]);
       GST_DEBUG_OBJECT (demux, "created subbuffer %p", sub);
index 0ee39d31e96c02e93df44a5f9b7ea76314913463..5bed5e6dd36e86f46b52e9d3561b78979756fbb1 100644 (file)
@@ -56,6 +56,7 @@ gst_matroska_track_init_video_context (GstMatroskaTrackContext ** p_context)
   video_context->asr_mode = 0;
   video_context->fourcc = 0;
   video_context->default_fps = 0.0;
+  video_context->earliest_time = GST_CLOCK_TIME_NONE;
   return TRUE;
 }
 
index 085bf8409c4b7a1e2148f27f5c895084ba4fe6b6..16b56010bc4b407bb00cb81e17f9c4da49fcdea9 100644 (file)
@@ -545,6 +545,9 @@ typedef struct _GstMatroskaTrackVideoContext {
   GstMatroskaAspectRatioMode asr_mode;
   guint32       fourcc;
 
+  /* QoS */
+  GstClockTime  earliest_time;
+
   GstBuffer     *dirac_unit;
 } GstMatroskaTrackVideoContext;