ducatividdec: move frame reordering to the base class
[glsdk/gst-plugin-ducati.git] / src / gstducatividdec.c
index 4bdd40b852a533caecee86b7dc1fd51e0ebe546f..ab3147dd4fb54cea58833c8dfefad6c4adf38278 100644 (file)
@@ -294,9 +294,41 @@ codec_unlock_outbuf (GstDucatiVidDec * self, XDAS_Int32 id)
   }
 }
 
+static GstFlowReturn
+gst_ducati_viddec_push_earliest (GstDucatiVidDec * self)
+{
+  guint64 earliest_order = 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 poc */
+  for (i = 0; i < self->backlog_nframes; i++) {
+    guint64 order = GST_BUFFER_OFFSET_END (self->backlog_frames[i]);
+    if (earliest_order == G_MAXUINT64 || order < earliest_order) {
+      earliest_order = order;
+      earliest_index = i;
+    }
+  }
+
+  /* send it, giving away the ref */
+  buf = self->backlog_frames[earliest_index];
+  self->backlog_frames[earliest_index] =
+      self->backlog_frames[--self->backlog_nframes];
+  GST_DEBUG_OBJECT (self, "Actually pushing backlog buffer %" GST_PTR_FORMAT,
+      buf);
+  return gst_pad_push (self->srcpad, buf);
+}
+
 static void
 gst_ducati_viddec_on_flush (GstDucatiVidDec * self, gboolean eos)
 {
+  /* push everything on the backlog, ignoring errors */
+  while (self->backlog_nframes > 0) {
+    gst_ducati_viddec_push_earliest (self);
+  }
 }
 
 static gint
@@ -686,6 +718,20 @@ gst_ducati_viddec_allocate_params (GstDucatiVidDec * self, gint params_sz,
 static GstBuffer *
 gst_ducati_viddec_push_input (GstDucatiVidDec * self, GstBuffer * buf)
 {
+  /* If we're about to push a keyframe, then we can flush all the frames
+     we currently have queued. For formats such as H264, this is actually
+     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. For
+     other formats, it is not necessary, but avoids buffering frames that
+     do not need to be. */
+  if (!GST_BUFFER_FLAG_IS_SET (buf, GST_BUFFER_FLAG_DELTA_UNIT)) {
+    GST_DEBUG_OBJECT (self, "Got keyframe, pushing %u frames",
+        self->backlog_nframes);
+    while (self->backlog_nframes > 0) {
+      gst_ducati_viddec_push_earliest (self);
+    }
+  }
+
   if (G_UNLIKELY (self->first_in_buffer) && self->codec_data) {
     push_input (self, GST_BUFFER_DATA (self->codec_data),
         GST_BUFFER_SIZE (self->codec_data));
@@ -701,7 +747,20 @@ gst_ducati_viddec_push_input (GstDucatiVidDec * self, GstBuffer * buf)
 static GstFlowReturn
 gst_ducati_viddec_push_output (GstDucatiVidDec * self, GstBuffer * buf)
 {
-  return gst_pad_push (self->srcpad, buf);
+  GstFlowReturn ret = GST_FLOW_OK;
+
+  /* add the frame to the list, the array will own the ref */
+  GST_DEBUG_OBJECT (self, "Adding buffer %" GST_PTR_FORMAT " to backlog", buf);
+  self->backlog_frames[self->backlog_nframes++] = buf;
+
+  /* push till we have no more than the max needed, or error */
+  while (self->backlog_nframes > self->backlog_maxframes) {
+    ret = gst_ducati_viddec_push_earliest (self);
+    if (ret != GST_FLOW_OK)
+      break;
+  }
+
+  return ret;
 }
 
 static gint
@@ -809,6 +868,9 @@ gst_ducati_viddec_set_sink_caps (GstDucatiVidDec * self, GstCaps * caps)
 
   GST_INFO_OBJECT (self, "set caps done %d, %" GST_PTR_FORMAT, ret, outcaps);
 
+  /* default to no reordering */
+  self->backlog_maxframes = 0;
+
 out:
   if (outcaps)
     gst_caps_unref (outcaps);
@@ -1466,6 +1528,9 @@ gst_ducati_viddec_init (GstDucatiVidDec * self, GstDucatiVidDecClass * klass)
   self->device = NULL;
   self->input_bo = NULL;
 
+  self->backlog_maxframes = 0;
+  self->backlog_nframes = 0;
+
   self->passed_in_bufs = g_hash_table_new_full (g_direct_hash, g_direct_equal,
       NULL, (GDestroyNotify) gst_buffer_unref);
 }