ducatih264dec: reorder buffers by picture order count
authorVincent Penquerc'h <vincent.penquerch@collabora.co.uk>
Thu, 4 Oct 2012 14:47:49 +0000 (15:47 +0100)
committerVincent Penquerc'h <vincent.penquerch@collabora.co.uk>
Tue, 9 Oct 2012 13:06:53 +0000 (14:06 +0100)
This fixes out of order frames when H264 video comes from
demuxers which timestamp by decoding order instead of
presentation order (such as MPEG/TS and AVI).

src/gstducatih264dec.c
src/gstducatividdec.c

index 2ee244c36a8820141c8adf0ca41342d832c3f21d..d7149fbef30582d80b1285fe0cc1dce81a3d975a 100644 (file)
@@ -336,27 +336,25 @@ gst_ducati_h264dec_set_sink_caps (GstDucatiVidDec * self, GstCaps * caps)
   return TRUE;
 }
 
-/* The following few functions reorder buffers by timestamp, on the
-   assumption that buffers are properly timestamped by presentation
-   time, but pushed in any (but presumably decode) order.
-   They also assume all incoming buffers have a valid timestamp.
+/* The following few functions reorder buffers by "picture order
+   count", which is passed through OFFSET_END by h264parse.
  */
 
 static GstFlowReturn
 gst_ducati_h264dec_push_earliest (GstDucatiH264Dec * self)
 {
-  GstClockTime earliest_ts = GST_CLOCK_TIME_NONE;
+  guint64 earliest_poc = G_MAXUINT64;
   guint earliest_index = 0, i;
   GstBuffer *buf;
 
   if (self->backlog_nframes == 0)
     return GST_FLOW_OK;
 
-  /* work out which frame has the earliest ts */
+  /* work out which frame has the earliest poc */
   for (i = 0; i < self->backlog_nframes; i++) {
-    GstClockTime ts = GST_BUFFER_TIMESTAMP (self->backlog_frames[i]);
-    if (earliest_ts == GST_CLOCK_TIME_NONE || ts < earliest_ts) {
-      earliest_ts = ts;
+    guint64 poc = GST_BUFFER_OFFSET_END (self->backlog_frames[i]);
+    if (earliest_poc == G_MAXUINT64 || poc < earliest_poc) {
+      earliest_poc = poc;
       earliest_index = i;
     }
   }
@@ -370,6 +368,26 @@ gst_ducati_h264dec_push_earliest (GstDucatiH264Dec * self)
   return gst_pad_push (GST_DUCATIVIDDEC (self)->srcpad, buf);
 }
 
+static GstBuffer *
+gst_ducati_h264dec_push_input (GstDucatiVidDec * self, GstBuffer * buf)
+{
+  GstDucatiH264Dec *h264dec = GST_DUCATIH264DEC (self);
+
+  /* If we're about to push an IDR frame, then we can flush all the frames
+     we currently have queued. This is necessary as picture order count
+     is local to each IDR + set of non IDR frames, so will restart at 0
+     when next IDR frames comes in. */
+  if (!GST_BUFFER_FLAG_IS_SET (buf, GST_BUFFER_FLAG_DELTA_UNIT)) {
+    GST_DEBUG_OBJECT (self, "Got keyframe, pushing %u frames",
+        h264dec->backlog_nframes);
+    while (h264dec->backlog_nframes > 0) {
+      gst_ducati_h264dec_push_earliest (h264dec);
+    }
+  }
+
+  return parent_class->push_input (self, buf);
+}
+
 static GstFlowReturn
 gst_ducati_h264dec_push_output (GstDucatiVidDec * self, GstBuffer * buf)
 {
@@ -431,6 +449,7 @@ gst_ducati_h264dec_class_init (GstDucatiH264DecClass * klass)
   bclass->can_drop_frame =
       GST_DEBUG_FUNCPTR (gst_ducati_h264dec_can_drop_frame);
   bclass->query = GST_DEBUG_FUNCPTR (gst_ducati_h264dec_query);
+  bclass->push_input = GST_DEBUG_FUNCPTR (gst_ducati_h264dec_push_input);
   bclass->push_output = GST_DEBUG_FUNCPTR (gst_ducati_h264dec_push_output);
   bclass->on_flush = GST_DEBUG_FUNCPTR (gst_ducati_h264dec_on_flush);
   bclass->set_sink_caps = GST_DEBUG_FUNCPTR (gst_ducati_h264dec_set_sink_caps);
index 6639e2c355935ab08c5859de546d8e422552fb13..4bdd40b852a533caecee86b7dc1fd51e0ebe546f 100644 (file)
@@ -1063,6 +1063,7 @@ allocate_buffer:
     gst_buffer_unref (buf);
     return GST_FLOW_ERROR;
   }
+  GST_BUFFER_OFFSET_END (outbuf) = GST_BUFFER_OFFSET_END (buf);
 
 have_out_buf:
   buf = GST_DUCATIVIDDEC_GET_CLASS (self)->push_input (self, buf);