]> Gitweb @ Texas Instruments - Open Source Git Repositories - git.TI.com/gitweb - glsdk/gst-plugin-ducati.git/blobdiff - src/gstducatividdec.c
ducatividdec: move frame reordering to the base class
[glsdk/gst-plugin-ducati.git] / src / gstducatividdec.c
index 4c961b06049a70d7e88d923995c17b89b1aa379b..ab3147dd4fb54cea58833c8dfefad6c4adf38278 100644 (file)
@@ -1,3 +1,4 @@
+#define USE_DTS_PTS_CODE
 /*
  * GStreamer
  * Copyright (c) 2010, Texas Instruments Incorporated
@@ -22,7 +23,7 @@
 #endif
 
 #include "gstducatividdec.h"
-#include "gstducatidrmbuffer.h"
+#include "gstducatibufferpriv.h"
 
 GST_BOILERPLATE (GstDucatiVidDec, gst_ducati_viddec, GstElement,
     GST_TYPE_ELEMENT);
@@ -84,11 +85,6 @@ engine_close (GstDucatiVidDec * self)
     self->outArgs = NULL;
   }
 
-  if (self->allocator) {
-    gst_mini_object_unref (GST_MINI_OBJECT (self->allocator));
-    self->allocator = NULL;
-  }
-
   if (self->device) {
     dce_deinit (self->device);
     self->device = NULL;
@@ -111,10 +107,6 @@ engine_open (GstDucatiVidDec * self)
       GST_ERROR_OBJECT (self, "dce_init() failed");
       return FALSE;
     }
-
-    self->allocator =
-        GST_DUCATI_BUFFER_ALLOCATOR (gst_ducati_drm_buffer_allocator_new
-        (self->device));
   }
 
   GST_DEBUG_OBJECT (self, "opening engine");
@@ -137,7 +129,7 @@ static void
 codec_delete (GstDucatiVidDec * self)
 {
   if (self->pool) {
-    gst_ducati_buffer_pool_destroy (self->pool);
+    gst_drm_buffer_pool_destroy (self->pool);
     self->pool = NULL;
   }
 
@@ -192,6 +184,7 @@ codec_create (GstDucatiVidDec * self)
   self->first_out_buffer = TRUE;
 
   /* allocate input buffer and initialize inBufs: */
+  /* FIXME:  needed size here has nothing to do with width * height */
   self->input_bo = omap_bo_new (self->device,
       self->width * self->height, OMAP_BO_WC);
   self->input = omap_bo_map (self->input_bo);
@@ -209,19 +202,53 @@ codec_buffer_pool_get (GstDucatiVidDec * self, GstBuffer * buf)
         self->padded_width, self->padded_height);
 
     GST_DEBUG_OBJECT (self, "creating bufferpool");
-    self->pool = gst_ducati_buffer_pool_new (GST_ELEMENT (self),
-        self->allocator, GST_PAD_CAPS (self->srcpad), size);
+    self->pool = gst_drm_buffer_pool_new (GST_ELEMENT (self),
+        dce_get_fd (), GST_PAD_CAPS (self->srcpad), size);
+  }
+  return GST_BUFFER (gst_drm_buffer_pool_get (self->pool, FALSE));
+}
+
+static GstDucatiBufferPriv *
+get_buffer_priv (GstDucatiVidDec * self, GstBuffer * buf)
+{
+  GstDucatiBufferPriv *priv = gst_ducati_buffer_priv_get (buf);
+  if (!priv) {
+    GstVideoFormat format = GST_VIDEO_FORMAT_NV12;
+    GstDmaBuf *dmabuf = gst_buffer_get_dma_buf (buf);
+
+    /* if it isn't a dmabuf buffer that we can import, then there
+     * is nothing we can do with it:
+     */
+    if (!dmabuf) {
+      GST_DEBUG_OBJECT (self, "not importing non dmabuf buffer");
+      return NULL;
+    }
+
+    priv = gst_ducati_buffer_priv_new ();
+
+    priv->bo = omap_bo_from_dmabuf (self->device, gst_dma_buf_get_fd (dmabuf));
+
+    priv->uv_offset = gst_video_format_get_component_offset (format,
+        1, self->stride, self->padded_height);
+    priv->size = gst_video_format_get_size (format,
+        self->stride, self->padded_height);
+
+    gst_ducati_buffer_priv_set (buf, priv);
+    gst_mini_object_unref (GST_MINI_OBJECT (priv));
   }
-  return GST_BUFFER (gst_ducati_buffer_pool_get (self->pool, buf, FALSE));
+  return priv;
 }
 
 static XDAS_Int32
 codec_prepare_outbuf (GstDucatiVidDec * self, GstBuffer ** buf,
     gboolean force_internal)
 {
-  GstDucatiBufferAllocatorClass *alloc_class;
+  GstDucatiBufferPriv *priv = NULL;
+
+  if (!force_internal)
+    priv = get_buffer_priv (self, *buf);
 
-  if (force_internal) {
+  if (!priv) {
     GstBuffer *orig = *buf;
 
     GST_DEBUG_OBJECT (self, "internal bufferpool forced");
@@ -232,25 +259,25 @@ codec_prepare_outbuf (GstDucatiVidDec * self, GstBuffer ** buf,
     return codec_prepare_outbuf (self, buf, FALSE);
   }
 
-  alloc_class = GST_DUCATI_BUFFER_ALLOCATOR_GET_CLASS (self->allocator);
-  if (!alloc_class->check_compatible (self->allocator, *buf)) {
-    GST_DEBUG_OBJECT (self, "non-ducati buffer, fallback to bufferpool");
-    *buf = codec_buffer_pool_get (self, *buf);
-    return codec_prepare_outbuf (self, buf, FALSE);
-  }
+  self->outBufs->numBufs = 2;
+  self->outBufs->descs[0].memType = XDM_MEMTYPE_BO;
+  self->outBufs->descs[0].buf = (XDAS_Int8 *) omap_bo_handle (priv->bo);
+  self->outBufs->descs[0].bufSize.bytes = priv->uv_offset;
+  self->outBufs->descs[1].memType = XDM_MEMTYPE_BO_OFFSET;
+  self->outBufs->descs[1].buf = (XDAS_Int8 *) priv->uv_offset;
+  self->outBufs->descs[1].bufSize.bytes = priv->size - priv->uv_offset;
 
-  alloc_class->setup_codec_output_buffers (self->allocator,
-      GST_VIDEO_FORMAT_NV12, self->padded_width, self->padded_height,
-      self->stride, *buf, self->outBufs);
-
-  return (XDAS_Int32) * buf;    // XXX use lookup table
+  return (XDAS_Int32) * buf;
 }
 
 static GstBuffer *
 codec_get_outbuf (GstDucatiVidDec * self, XDAS_Int32 id)
 {
-  GstBuffer *buf = (GstBuffer *) id;    // XXX use lookup table
+  GstBuffer *buf = (GstBuffer *) id;
+
   if (buf) {
+    g_hash_table_insert (self->passed_in_bufs, buf, buf);
+
     gst_buffer_ref (buf);
   }
   return buf;
@@ -259,10 +286,48 @@ codec_get_outbuf (GstDucatiVidDec * self, XDAS_Int32 id)
 static void
 codec_unlock_outbuf (GstDucatiVidDec * self, XDAS_Int32 id)
 {
-  GstBuffer *buf = (GstBuffer *) id;    // XXX use lookup table
+  GstBuffer *buf = (GstBuffer *) id;
+
   if (buf) {
     GST_DEBUG_OBJECT (self, "free buffer: %d %p", id, buf);
-    gst_buffer_unref (buf);
+    g_hash_table_remove (self->passed_in_bufs, buf);
+  }
+}
+
+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);
   }
 }
 
@@ -291,12 +356,14 @@ codec_process (GstDucatiVidDec * self, gboolean send, gboolean flush,
   if (err) {
     GST_WARNING_OBJECT (self, "err=%d, extendedError=%08x",
         err, self->outArgs->extendedError);
+    gst_ducati_log_extended_error_info (self->outArgs->extendedError);
 
     err = VIDDEC3_control (self->codec, XDM_GETSTATUS,
         self->dynParams, self->status);
-    if (!err) {
+    if (err) {
       GST_WARNING_OBJECT (self, "XDM_GETSTATUS: err=%d, extendedError=%08x",
           err, self->status->extendedError);
+      gst_ducati_log_extended_error_info (self->status->extendedError);
     }
 
     if (flush)
@@ -306,11 +373,17 @@ codec_process (GstDucatiVidDec * self, gboolean send, gboolean flush,
           self->outArgs->extendedError, self->status->extendedError);
   }
 
-  for (i = 0; self->outArgs->outputID[i]; i++) {
+  /* we now let the codec decide */
+  self->dynParams->newFrameFlag = XDAS_FALSE;
+
+  if (err == XDM_EFAIL)
+    goto skip_outbuf_processing;
+
+  for (i = 0; i < IVIDEO2_MAX_IO_BUFFERS && self->outArgs->outputID[i]; i++) {
     gboolean interlaced;
 
+    /* Getting an extra reference for the decoder */
     outbuf = codec_get_outbuf (self, self->outArgs->outputID[i]);
-
     interlaced =
         self->outArgs->decodedBufs.contentType ==
         IVIDEO_PROGRESSIVE ? FALSE : TRUE;
@@ -332,7 +405,7 @@ codec_process (GstDucatiVidDec * self, gboolean send, gboolean flush,
       GST_INFO_OBJECT (self, "changing interlace field in caps");
       gst_caps_set_simple (caps, "interlaced", G_TYPE_BOOLEAN, interlaced,
           NULL);
-      gst_ducati_buffer_pool_set_caps (self->pool, caps);
+      gst_drm_buffer_pool_set_caps (self->pool, caps);
       if (!gst_pad_set_caps (self->srcpad, caps)) {
         GST_ERROR_OBJECT (self,
             "downstream didn't want to change interlace mode");
@@ -346,7 +419,6 @@ codec_process (GstDucatiVidDec * self, gboolean send, gboolean flush,
 
     if (G_UNLIKELY (self->send_crop_event) && send) {
       gint crop_width, crop_height;
-      GstDucatiVidDecClass *klass = GST_DUCATIVIDDEC_GET_CLASS (self);
 
       /* send region of interest to sink on first buffer: */
       XDM_Rect *r = &(self->outArgs->displayBufs.bufDesc[0].activeFrameRegion);
@@ -359,9 +431,6 @@ codec_process (GstDucatiVidDec * self, gboolean send, gboolean flush,
       if (crop_height > self->input_height)
         crop_height = self->input_height;
 
-      if (self->interlaced && !strcmp (klass->codec_name, "ivahd_mpeg2vdec"))
-        crop_height = crop_height / 2;
-
       GST_INFO_OBJECT (self, "active frame region %d, %d, %d, %d, crop %dx%d",
           r->topLeft.x, r->topLeft.y, r->bottomRight.x, r->bottomRight.y,
           crop_width, crop_height);
@@ -370,11 +439,17 @@ codec_process (GstDucatiVidDec * self, gboolean send, gboolean flush,
           gst_event_new_crop (r->topLeft.y, r->topLeft.x,
               crop_width, crop_height));
 
+      if (self->crop)
+        gst_video_crop_unref (self->crop);
+
+      self->crop = gst_video_crop_new (r->topLeft.y, r->topLeft.x,
+          crop_width, crop_height);
+
       self->send_crop_event = FALSE;
     }
 
     if (G_UNLIKELY (self->first_out_buffer) && send) {
-      GstDucatiBufferPool *pool;
+      GstDRMBufferPool *pool;
       self->first_out_buffer = FALSE;
 
       /* Destroy the pool so the buffers we used so far are eventually released.
@@ -382,21 +457,18 @@ codec_process (GstDucatiVidDec * self, gboolean send, gboolean flush,
        */
       pool = self->pool;
       self->pool = NULL;
-      gst_ducati_buffer_pool_destroy (pool);
+      gst_drm_buffer_pool_destroy (pool);
     }
 
     if (send) {
       GstClockTime ts;
 
-      if (GST_IS_DUCATI_BUFFER (outbuf)) {
-        outbuf = gst_ducati_buffer_get (GST_DUCATI_BUFFER (outbuf));
-      }
-
       ts = GST_BUFFER_TIMESTAMP (outbuf);
 
       GST_DEBUG_OBJECT (self, "got buffer: %d %p (%" GST_TIME_FORMAT ")",
           i, outbuf, GST_TIME_ARGS (ts));
 
+#ifdef USE_DTS_PTS_CODE
       if (self->ts_may_be_pts) {
         if ((self->last_pts != GST_CLOCK_TIME_NONE) && (self->last_pts > ts)) {
           GST_DEBUG_OBJECT (self, "detected PTS going backwards, "
@@ -404,6 +476,7 @@ codec_process (GstDucatiVidDec * self, gboolean send, gboolean flush,
           self->ts_is_pts = TRUE;
         }
       }
+#endif
 
       self->last_pts = ts;
 
@@ -430,7 +503,10 @@ codec_process (GstDucatiVidDec * self, gboolean send, gboolean flush,
         gst_buffer_set_caps (outbuf, GST_PAD_CAPS (self->srcpad));
       }
 
-      ret = gst_pad_push (self->srcpad, outbuf);
+      if (self->crop)
+        gst_buffer_set_video_crop (outbuf, self->crop);
+
+      ret = klass->push_output (self, outbuf);
       if (flow_ret)
         *flow_ret = ret;
       if (ret != GST_FLOW_OK) {
@@ -439,12 +515,15 @@ codec_process (GstDucatiVidDec * self, gboolean send, gboolean flush,
         send = FALSE;
       }
     } else {
-      GST_DEBUG_OBJECT (self, "free buffer: %d %p", i, outbuf);
+      GST_DEBUG_OBJECT (self, "Buffer not pushed, dropping 'chain' ref: %d %p",
+          i, outbuf);
+
       gst_buffer_unref (outbuf);
     }
   }
 
-  for (i = 0; self->outArgs->freeBufID[i]; i++) {
+skip_outbuf_processing:
+  for (i = 0; i < IVIDEO2_MAX_IO_BUFFERS && self->outArgs->freeBufID[i]; i++) {
     codec_unlock_outbuf (self, self->outArgs->freeBufID[i]);
   }
 
@@ -455,28 +534,35 @@ codec_process (GstDucatiVidDec * self, gboolean send, gboolean flush,
 gboolean
 gst_ducati_viddec_codec_flush (GstDucatiVidDec * self, gboolean eos)
 {
-  gint err;
+  gint err = FALSE;
 
   GST_DEBUG_OBJECT (self, "flush: eos=%d", eos);
 
+  GST_DUCATIVIDDEC_GET_CLASS (self)->on_flush (self, eos);
+
   /* note: flush is synchronized against _chain() to avoid calling
    * the codec from multiple threads
    */
   GST_PAD_STREAM_LOCK (self->sinkpad);
 
+#ifdef USE_DTS_PTS_CODE
   self->dts_ridx = self->dts_widx = 0;
   self->last_dts = self->last_pts = GST_CLOCK_TIME_NONE;
   self->ts_may_be_pts = TRUE;
   self->ts_is_pts = FALSE;
+#endif
   self->wait_keyframe = TRUE;
+  self->in_size = 0;
+  self->needs_flushing = FALSE;
+  self->need_out_buf = TRUE;
 
   if (G_UNLIKELY (self->first_in_buffer)) {
-    return TRUE;
+    goto out;
   }
 
   if (G_UNLIKELY (!self->codec)) {
     GST_WARNING_OBJECT (self, "no codec");
-    return TRUE;
+    goto out;
   }
 
   err = VIDDEC3_control (self->codec, XDM_FLUSH, self->dynParams, self->status);
@@ -486,18 +572,30 @@ gst_ducati_viddec_codec_flush (GstDucatiVidDec * self, gboolean eos)
   }
 
   self->inBufs->descs[0].bufSize.bytes = 0;
+  self->inBufs->numBufs = 0;
   self->inArgs->numBytes = 0;
   self->inArgs->inputID = 0;
+  self->outBufs->numBufs = 0;
 
   do {
     err = codec_process (self, eos, TRUE, NULL);
   } while (err != XDM_EFAIL);
 
+  /* We flushed the decoder, we can now remove the buffer that have never been
+   * unrefed in it */
+  g_hash_table_remove_all (self->passed_in_bufs);
+
   /* reset outArgs in case we're flushing in codec_process trying to do error
    * recovery */
   memset (&self->outArgs->outputID, 0, sizeof (self->outArgs->outputID));
   memset (&self->outArgs->freeBufID, 0, sizeof (self->outArgs->freeBufID));
 
+  self->dynParams->newFrameFlag = XDAS_TRUE;
+
+  /* Reset the push buffer and YUV buffers */
+  self->inBufs->numBufs = 1;
+  self->outBufs->numBufs = 2;
+
   /* on a flush, it is normal (and not an error) for the last _process() call
    * to return an error..
    */
@@ -595,6 +693,7 @@ gst_ducati_viddec_allocate_params (GstDucatiVidDec * self, gint params_sz,
   if (G_UNLIKELY (!self->status)) {
     return FALSE;
   }
+  memset (self->status, 0, status_sz);
   self->status->size = status_sz;
 
   /* allocate inBufs/outBufs: */
@@ -619,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));
@@ -631,6 +744,25 @@ gst_ducati_viddec_push_input (GstDucatiVidDec * self, GstBuffer * buf)
   return NULL;
 }
 
+static GstFlowReturn
+gst_ducati_viddec_push_output (GstDucatiVidDec * self, GstBuffer * 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
 gst_ducati_viddec_handle_error (GstDucatiVidDec * self, gint ret,
     gint extended_error, gint status_extended_error)
@@ -646,10 +778,9 @@ gst_ducati_viddec_handle_error (GstDucatiVidDec * self, gint ret,
 /* GstElement vmethod implementations */
 
 static gboolean
-gst_ducati_viddec_sink_setcaps (GstPad * pad, GstCaps * caps)
+gst_ducati_viddec_set_sink_caps (GstDucatiVidDec * self, GstCaps * caps)
 {
   gboolean ret = TRUE;
-  GstDucatiVidDec *self = GST_DUCATIVIDDEC (gst_pad_get_parent (pad));
   GstDucatiVidDecClass *klass = GST_DUCATIVIDDEC_GET_CLASS (self);
   GstStructure *s;
   GstCaps *outcaps = NULL;
@@ -657,8 +788,6 @@ gst_ducati_viddec_sink_setcaps (GstPad * pad, GstCaps * caps)
   gint par_width, par_height;
   gboolean par_present;
 
-  GST_INFO_OBJECT (self, "setcaps (sink): %" GST_PTR_FORMAT, caps);
-
   s = gst_caps_get_structure (caps, 0);
   if (!klass->parse_caps (self, s)) {
     GST_WARNING_OBJECT (self, "missing required fields");
@@ -739,9 +868,27 @@ gst_ducati_viddec_sink_setcaps (GstPad * pad, 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);
+
+  return ret;
+}
+
+static gboolean
+gst_ducati_viddec_sink_setcaps (GstPad * pad, GstCaps * caps)
+{
+  gboolean ret = TRUE;
+  GstDucatiVidDec *self = GST_DUCATIVIDDEC (gst_pad_get_parent (pad));
+  GstDucatiVidDecClass *klass = GST_DUCATIVIDDEC_GET_CLASS (self);
+
+  GST_INFO_OBJECT (self, "setcaps (sink): %" GST_PTR_FORMAT, caps);
+
+  ret = klass->set_sink_caps (self, caps);
+
   gst_object_unref (self);
 
   return ret;
@@ -867,7 +1014,7 @@ gst_ducati_viddec_do_qos (GstDucatiVidDec * self, GstBuffer * buf)
       GST_TIME_ARGS (qostime), GST_TIME_ARGS (self->qos_earliest_time), diff,
       self->qos_proportion);
 
-  if (klass->drop_frame (self, buf, diff)) {
+  if (klass->can_drop_frame (self, buf, diff)) {
     GST_INFO_OBJECT (self, "dropping frame");
     return FALSE;
   }
@@ -877,7 +1024,7 @@ no_qos:
 }
 
 static gboolean
-gst_ducati_viddec_drop_frame (GstDucatiVidDec * self, GstBuffer * buf,
+gst_ducati_viddec_can_drop_frame (GstDucatiVidDec * self, GstBuffer * buf,
     gint64 diff)
 {
   gboolean is_keyframe = !GST_BUFFER_FLAG_IS_SET (buf,
@@ -902,6 +1049,7 @@ gst_ducati_viddec_chain (GstPad * pad, GstBuffer * buf)
 
   if (G_UNLIKELY (!self->engine)) {
     GST_ERROR_OBJECT (self, "no engine");
+    gst_buffer_unref (buf);
     return GST_FLOW_ERROR;
   }
 
@@ -926,6 +1074,7 @@ allocate_buffer:
   if (ret != GST_FLOW_OK) {
     GST_WARNING_OBJECT (self, "alloc_buffer failed %s",
         gst_flow_get_name (ret));
+    gst_buffer_unref (buf);
     return ret;
   }
 
@@ -957,6 +1106,8 @@ allocate_buffer:
   if (G_UNLIKELY (!self->codec)) {
     if (!codec_create (self)) {
       GST_ERROR_OBJECT (self, "could not create codec");
+      gst_buffer_unref (buf);
+      gst_buffer_unref (outbuf);
       return GST_FLOW_ERROR;
     }
   }
@@ -971,13 +1122,15 @@ allocate_buffer:
       codec_prepare_outbuf (self, &outbuf, self->first_out_buffer);
   if (!self->inArgs->inputID) {
     GST_ERROR_OBJECT (self, "could not prepare output buffer");
+    gst_buffer_unref (buf);
     return GST_FLOW_ERROR;
   }
+  GST_BUFFER_OFFSET_END (outbuf) = GST_BUFFER_OFFSET_END (buf);
 
 have_out_buf:
-  self->in_size = 0;
   buf = GST_DUCATIVIDDEC_GET_CLASS (self)->push_input (self, buf);
 
+#ifdef USE_DTS_PTS_CODE
   if (ts != GST_CLOCK_TIME_NONE) {
     self->dts_queue[self->dts_widx++ % NDTS] = ts;
     /* if next buffer has earlier ts than previous, then the ts
@@ -989,9 +1142,11 @@ have_out_buf:
     }
     self->last_dts = ts;
   }
+#endif
 
   if (self->in_size == 0 && outbuf) {
     GST_DEBUG_OBJECT (self, "no input, skipping process");
+
     gst_buffer_unref (outbuf);
     return GST_FLOW_OK;
   }
@@ -1004,16 +1159,43 @@ have_out_buf:
   if (err) {
     GST_ELEMENT_ERROR (self, STREAM, DECODE, (NULL),
         ("process returned error: %d %08x", err, self->outArgs->extendedError));
+    gst_ducati_log_extended_error_info (self->outArgs->extendedError);
+
     return GST_FLOW_ERROR;
   }
+
   if (ret != GST_FLOW_OK) {
     GST_WARNING_OBJECT (self, "push from codec_process failed %s",
         gst_flow_get_name (ret));
+
     return ret;
   }
 
   self->first_in_buffer = FALSE;
 
+  if (self->params->inputDataMode != IVIDEO_ENTIREFRAME) {
+    /* The copy could be avoided by playing with the buffer pointer,
+       but it seems to be rare and for not many bytes */
+    GST_DEBUG_OBJECT (self, "Consumed %d/%d (%d) bytes, %d left",
+        self->outArgs->bytesConsumed, self->in_size,
+        self->inArgs->numBytes, self->in_size - self->outArgs->bytesConsumed);
+    if (self->outArgs->bytesConsumed > 0) {
+      if (self->outArgs->bytesConsumed > self->in_size) {
+        GST_WARNING_OBJECT (self,
+            "Codec claims to have used more bytes than supplied");
+        self->in_size = 0;
+      } else {
+        if (self->outArgs->bytesConsumed < self->in_size) {
+          memmove (self->input, self->input + self->outArgs->bytesConsumed,
+              self->in_size - self->outArgs->bytesConsumed);
+        }
+        self->in_size -= self->outArgs->bytesConsumed;
+      }
+    }
+  } else {
+    self->in_size = 0;
+  }
+
   if (self->outArgs->outBufsInUseFlag) {
     GST_DEBUG_OBJECT (self, "outBufsInUseFlag set");
     self->need_out_buf = FALSE;
@@ -1028,6 +1210,9 @@ have_out_buf:
     goto allocate_buffer;
   }
 
+  if (self->needs_flushing)
+    gst_ducati_viddec_codec_flush (self, FALSE);
+
   return GST_FLOW_OK;
 }
 
@@ -1062,6 +1247,7 @@ gst_ducati_viddec_event (GstPad * pad, GstEvent * event)
     case GST_EVENT_FLUSH_STOP:
       if (!gst_ducati_viddec_codec_flush (self, FALSE)) {
         GST_ERROR_OBJECT (self, "could not flush");
+        gst_event_unref (event);
         ret = FALSE;
       }
       gst_segment_init (&self->segment, GST_FORMAT_UNDEFINED);
@@ -1124,6 +1310,7 @@ gst_ducati_viddec_change_state (GstElement * element, GstStateChange transition)
 {
   GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
   GstDucatiVidDec *self = GST_DUCATIVIDDEC (element);
+  gboolean supported;
 
   GST_DEBUG_OBJECT (self, "begin: changing state %s -> %s",
       gst_element_state_get_name (GST_STATE_TRANSITION_CURRENT (transition)),
@@ -1135,6 +1322,16 @@ gst_ducati_viddec_change_state (GstElement * element, GstStateChange transition)
         GST_ERROR_OBJECT (self, "could not open");
         return GST_STATE_CHANGE_FAILURE;
       }
+      /* try to create/destroy the codec here, it may not be supported */
+      supported = codec_create (self);
+      codec_delete (self);
+      self->codec = NULL;
+      if (!supported) {
+        GST_ERROR_OBJECT (element, "Failed to create codec %s, not supported",
+            GST_DUCATIVIDDEC_GET_CLASS (self)->codec_name);
+        engine_close (self);
+        return GST_STATE_CHANGE_FAILURE;
+      }
       break;
     default:
       break;
@@ -1223,6 +1420,8 @@ gst_ducati_viddec_finalize (GObject * obj)
   codec_delete (self);
   engine_close (self);
 
+  /* Will unref the remaining buffers if needed */
+  g_hash_table_unref (self->passed_in_bufs);
   if (self->codec_data) {
     gst_buffer_unref (self->codec_data);
     self->codec_data = NULL;
@@ -1257,8 +1456,11 @@ gst_ducati_viddec_class_init (GstDucatiVidDecClass * klass)
       GST_DEBUG_FUNCPTR (gst_ducati_viddec_allocate_params);
   klass->push_input = GST_DEBUG_FUNCPTR (gst_ducati_viddec_push_input);
   klass->handle_error = GST_DEBUG_FUNCPTR (gst_ducati_viddec_handle_error);
-  klass->drop_frame = GST_DEBUG_FUNCPTR (gst_ducati_viddec_drop_frame);
+  klass->can_drop_frame = GST_DEBUG_FUNCPTR (gst_ducati_viddec_can_drop_frame);
   klass->query = GST_DEBUG_FUNCPTR (gst_ducati_viddec_query);
+  klass->push_output = GST_DEBUG_FUNCPTR (gst_ducati_viddec_push_output);
+  klass->on_flush = GST_DEBUG_FUNCPTR (gst_ducati_viddec_on_flush);
+  klass->set_sink_caps = GST_DEBUG_FUNCPTR (gst_ducati_viddec_set_sink_caps);
 
   g_object_class_install_property (gobject_class, PROP_VERSION,
       g_param_spec_string ("version", "Version",
@@ -1307,10 +1509,12 @@ gst_ducati_viddec_init (GstDucatiVidDec * self, GstDucatiVidDecClass * klass)
   self->interlaced = FALSE;
   self->send_crop_event = TRUE;
 
+#ifdef USE_DTS_PTS_CODE
   self->dts_ridx = self->dts_widx = 0;
   self->last_dts = self->last_pts = GST_CLOCK_TIME_NONE;
   self->ts_may_be_pts = TRUE;
   self->ts_is_pts = FALSE;
+#endif
 
   self->pageMemType = XDM_MEMTYPE_TILEDPAGE;
 
@@ -1323,4 +1527,10 @@ gst_ducati_viddec_init (GstDucatiVidDec * self, GstDucatiVidDecClass * klass)
   self->need_out_buf = TRUE;
   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);
 }