]> Gitweb @ Texas Instruments - Open Source Git Repositories - git.TI.com/gitweb - glsdk/gst-plugin-ducati.git/blobdiff - src/gstducatividdec.c
ducatividdec: add a comment about a fishy, but OK, bit of code
[glsdk/gst-plugin-ducati.git] / src / gstducatividdec.c
index cde050c15b36cd1f93dd3ef41270575d262ca1d7..afb36749a5c47048a6f095e1c685cd4c4beb2f01 100644 (file)
@@ -22,6 +22,7 @@
 #endif
 
 #include "gstducatividdec.h"
+#include "gstducatibufferpriv.h"
 
 GST_BOILERPLATE (GstDucatiVidDec, gst_ducati_viddec, GstElement,
     GST_TYPE_ELEMENT);
@@ -82,20 +83,34 @@ engine_close (GstDucatiVidDec * self)
     dce_free (self->outArgs);
     self->outArgs = NULL;
   }
+
+  if (self->device) {
+    dce_deinit (self->device);
+    self->device = NULL;
+  }
 }
 
 static gboolean
 engine_open (GstDucatiVidDec * self)
 {
   gboolean ret;
+  int ec;
 
   if (G_UNLIKELY (self->engine)) {
     return TRUE;
   }
 
+  if (self->device == NULL) {
+    self->device = dce_init ();
+    if (self->device == NULL) {
+      GST_ERROR_OBJECT (self, "dce_init() failed");
+      return FALSE;
+    }
+  }
+
   GST_DEBUG_OBJECT (self, "opening engine");
 
-  self->engine = Engine_open ((String) "ivahd_vidsvr", NULL, NULL);
+  self->engine = Engine_open ((String) "ivahd_vidsvr", NULL, &ec);
   if (G_UNLIKELY (!self->engine)) {
     GST_ERROR_OBJECT (self, "could not create engine");
     return FALSE;
@@ -113,7 +128,7 @@ static void
 codec_delete (GstDucatiVidDec * self)
 {
   if (self->pool) {
-    gst_ducati_bufferpool_destroy (self->pool);
+    gst_drm_buffer_pool_destroy (self->pool);
     self->pool = NULL;
   }
 
@@ -122,9 +137,9 @@ codec_delete (GstDucatiVidDec * self)
     self->codec = NULL;
   }
 
-  if (self->input) {
-    MemMgr_Free (self->input);
-    self->input = NULL;
+  if (self->input_bo) {
+    omap_bo_del (self->input_bo);
+    self->input_bo = NULL;
   }
 }
 
@@ -168,98 +183,90 @@ 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);
   self->inBufs->numBufs = 1;
-  self->input = gst_ducati_alloc_1d (self->width * self->height);
-  self->inBufs->descs[0].buf = (XDAS_Int8 *) TilerMem_VirtToPhys (self->input);
-  self->inBufs->descs[0].memType = XDM_MEMTYPE_RAW;
+  self->inBufs->descs[0].buf = (XDAS_Int8 *) omap_bo_handle (self->input_bo);
 
   return TRUE;
 }
 
 static inline GstBuffer *
-codec_bufferpool_get (GstDucatiVidDec * self, GstBuffer * buf)
+codec_buffer_pool_get (GstDucatiVidDec * self, GstBuffer * buf)
 {
   if (G_UNLIKELY (!self->pool)) {
-    guint size;
-
-    size = gst_video_format_get_size (GST_VIDEO_FORMAT_NV12,
+    guint size = gst_video_format_get_size (GST_VIDEO_FORMAT_NV12,
         self->padded_width, self->padded_height);
+
     GST_DEBUG_OBJECT (self, "creating bufferpool");
-    self->pool = gst_ducati_bufferpool_new (GST_ELEMENT (self),
-        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_ducati_bufferpool_get (self->pool, buf));
+  return GST_BUFFER (gst_drm_buffer_pool_get (self->pool, FALSE));
 }
 
-static XDAS_Int32
-codec_prepare_outbuf (GstDucatiVidDec * self, GstBuffer ** buf,
-    gboolean force_internal)
+static GstDucatiBufferPriv *
+get_buffer_priv (GstDucatiVidDec * self, GstBuffer * buf)
 {
-  XDAS_Int16 y_type, uv_type;
-  guint8 *y_vaddr, *uv_vaddr;
-  SSPtr y_paddr, uv_paddr;
+  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 (force_internal) {
-    GST_DEBUG_OBJECT (self, "internal bufferpool forced");
-    gst_buffer_unref (*buf);
-    *buf = codec_bufferpool_get (self, NULL);
-    return codec_prepare_outbuf (self, buf, FALSE);
-  }
+    /* 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;
+    }
 
-  y_vaddr = GST_BUFFER_DATA (*buf);
-  uv_vaddr = y_vaddr + self->stride * self->padded_height;
+    priv = gst_ducati_buffer_priv_new ();
 
-  y_paddr = TilerMem_VirtToPhys (y_vaddr);
-  uv_paddr = TilerMem_VirtToPhys (uv_vaddr);
+    priv->bo = omap_bo_from_dmabuf (self->device, gst_dma_buf_get_fd (dmabuf));
 
-  y_type = gst_ducati_get_mem_type (y_paddr);
-  uv_type = gst_ducati_get_mem_type (uv_paddr);
-  /* FIXME: workaround for the vc1 codec expecting _RAW when it's actually
-   * _TILEDPAGE... should be removed once the codec is fixed  */
-  if (y_type == XDM_MEMTYPE_TILEDPAGE && self->pageMemType != y_type)
-    y_type = self->pageMemType;
-  if (uv_type == XDM_MEMTYPE_TILEDPAGE && self->pageMemType != uv_type)
-    uv_type = self->pageMemType;
+    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);
 
-  if (y_type < 0 || uv_type < 0) {
-    GST_DEBUG_OBJECT (self, "non TILER buffer, fallback to bufferpool");
-    *buf = codec_bufferpool_get (self, *buf);
-    return codec_prepare_outbuf (self, buf, FALSE);
+    gst_ducati_buffer_priv_set (buf, priv);
+    gst_mini_object_unref (GST_MINI_OBJECT (priv));
   }
+  return priv;
+}
 
-  if (!self->outBufs->numBufs) {
-    /* initialize output buffer type */
-    self->outBufs->numBufs = 2;
-    self->outBufs->descs[0].memType = y_type;
-    self->outBufs->descs[1].memType = uv_type;
-    if (y_type == XDM_MEMTYPE_RAW || y_type == XDM_MEMTYPE_TILEDPAGE) {
-      self->outBufs->descs[0].bufSize.bytes =
-          self->stride * self->padded_height;
-      self->outBufs->descs[1].bufSize.bytes =
-          self->stride * self->padded_height / 2;
-    } else {
-      self->outBufs->descs[0].bufSize.tileMem.width = self->padded_width;
-      self->outBufs->descs[0].bufSize.tileMem.height = self->padded_height;
-      /* note that UV interleaved width is same a Y: */
-      self->outBufs->descs[1].bufSize.tileMem.width = self->padded_width;
-      self->outBufs->descs[1].bufSize.tileMem.height = self->padded_height / 2;
-    }
-  } else {
-    /* verify output buffer type matches what we've already given
-     * to the codec
-     */
-    if ((self->outBufs->descs[0].memType != y_type) ||
-        (self->outBufs->descs[1].memType != uv_type)) {
-      GST_DEBUG_OBJECT (self, "buffer mismatch, fallback to bufferpool");
-      *buf = codec_bufferpool_get (self, *buf);
-      return codec_prepare_outbuf (self, buf, FALSE);
-    }
+static XDAS_Int32
+codec_prepare_outbuf (GstDucatiVidDec * self, GstBuffer ** buf,
+    gboolean force_internal)
+{
+  GstDucatiBufferPriv *priv = NULL;
+
+  if (!force_internal)
+    priv = get_buffer_priv (self, *buf);
+
+  if (!priv) {
+    GstBuffer *orig = *buf;
+
+    GST_DEBUG_OBJECT (self, "internal bufferpool forced");
+    *buf = codec_buffer_pool_get (self, NULL);
+    GST_BUFFER_TIMESTAMP (*buf) = GST_BUFFER_TIMESTAMP (orig);
+    GST_BUFFER_DURATION (*buf) = GST_BUFFER_DURATION (orig);
+    gst_buffer_unref (orig);
+    return codec_prepare_outbuf (self, buf, FALSE);
   }
 
-  self->outBufs->descs[0].buf = (XDAS_Int8 *) y_paddr;
-  self->outBufs->descs[1].buf = (XDAS_Int8 *) uv_paddr;
+  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;
 
-  return (XDAS_Int32) *buf;      // XXX use lookup table
+  return (XDAS_Int32) * buf;    // XXX use lookup table
 }
 
 static GstBuffer *
@@ -283,13 +290,18 @@ codec_unlock_outbuf (GstDucatiVidDec * self, XDAS_Int32 id)
 }
 
 static gint
-codec_process (GstDucatiVidDec * self, gboolean send, gboolean flush)
+codec_process (GstDucatiVidDec * self, gboolean send, gboolean flush,
+    GstFlowReturn * flow_ret)
 {
   gint err;
   GstClockTime t;
   GstBuffer *outbuf = NULL;
   gint i;
   GstDucatiVidDecClass *klass = GST_DUCATIVIDDEC_GET_CLASS (self);
+  GstFlowReturn ret = GST_FLOW_OK;
+  if (flow_ret)
+    /* never leave flow_ret uninitialized */
+    *flow_ret = GST_FLOW_OK;
 
   memset (&self->outArgs->outputID, 0, sizeof (self->outArgs->outputID));
   memset (&self->outArgs->freeBufID, 0, sizeof (self->outArgs->freeBufID));
@@ -302,12 +314,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)
@@ -317,14 +331,25 @@ 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 || self->outArgs->outBufsInUseFlag)
+    goto skip_outbuf_processing;
+
+  for (i = 0; i < IVIDEO2_MAX_IO_BUFFERS && self->outArgs->outputID[i]; i++) {
     gboolean interlaced;
 
     outbuf = codec_get_outbuf (self, self->outArgs->outputID[i]);
 
-    interlaced = self->outArgs->decodedBufs.contentType == IVIDEO_PROGRESSIVE ? FALSE : TRUE;
+    interlaced =
+        self->outArgs->decodedBufs.contentType ==
+        IVIDEO_PROGRESSIVE ? FALSE : TRUE;
 
-    if (interlaced != self->interlaced) {
+    /* if send is FALSE, don't try to renegotiate as we could be flushing during
+     * a PAUSED->READY state change
+     */
+    if (send && interlaced != self->interlaced) {
       GstCaps *caps;
 
       GST_WARNING_OBJECT (self, "upstream set interlaced=%d but codec "
@@ -333,12 +358,15 @@ codec_process (GstDucatiVidDec * self, gboolean send, gboolean flush)
 
       self->interlaced = interlaced;
 
-      caps = gst_caps_make_writable (gst_pad_get_negotiated_caps (self->srcpad));
+      caps =
+          gst_caps_make_writable (gst_pad_get_negotiated_caps (self->srcpad));
       GST_INFO_OBJECT (self, "changing interlace field in caps");
-      gst_caps_set_simple (caps, "interlaced", G_TYPE_BOOLEAN, interlaced, NULL);
-      gst_ducati_bufferpool_set_caps (self->pool, caps);
+      gst_caps_set_simple (caps, "interlaced", G_TYPE_BOOLEAN, interlaced,
+          NULL);
+      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");
+        GST_ERROR_OBJECT (self,
+            "downstream didn't want to change interlace mode");
         err = XDM_EFAIL;
       }
       gst_caps_unref (caps);
@@ -347,9 +375,9 @@ codec_process (GstDucatiVidDec * self, gboolean send, gboolean flush)
       send = FALSE;
     }
 
-    if (G_UNLIKELY (self->first_out_buffer) && send) {
+    if (G_UNLIKELY (self->send_crop_event) && send) {
       gint crop_width, crop_height;
-      GstDucatiBufferPool *pool;
+      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);
@@ -362,6 +390,9 @@ 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,6 +401,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 && !self->outArgs->outBufsInUseFlag) {
+      GstDRMBufferPool *pool;
       self->first_out_buffer = FALSE;
 
       /* Destroy the pool so the buffers we used so far are eventually released.
@@ -377,24 +419,19 @@ codec_process (GstDucatiVidDec * self, gboolean send, gboolean flush)
        */
       pool = self->pool;
       self->pool = NULL;
-      gst_ducati_bufferpool_destroy (pool);
+      gst_drm_buffer_pool_destroy (pool);
     }
 
-    if (send) {
+    if (send && !self->outArgs->outBufsInUseFlag) {
       GstClockTime ts;
 
-      if (GST_IS_DUCATIBUFFER (outbuf)) {
-        outbuf = gst_ducati_buffer_get (GST_DUCATIBUFFER (outbuf));
-      }
-
       ts = GST_BUFFER_TIMESTAMP (outbuf);
 
       GST_DEBUG_OBJECT (self, "got buffer: %d %p (%" GST_TIME_FORMAT ")",
           i, outbuf, GST_TIME_ARGS (ts));
 
       if (self->ts_may_be_pts) {
-        if ((self->last_pts != GST_CLOCK_TIME_NONE) &&
-            (self->last_pts > ts)) {
+        if ((self->last_pts != GST_CLOCK_TIME_NONE) && (self->last_pts > ts)) {
           GST_DEBUG_OBJECT (self, "detected PTS going backwards, "
               "enabling ts_is_pts");
           self->ts_is_pts = TRUE;
@@ -414,7 +451,6 @@ codec_process (GstDucatiVidDec * self, gboolean send, gboolean flush)
             i, outbuf, GST_TIME_ARGS (ts));
       }
 
-    
       if (GST_BUFFER_CAPS (outbuf) &&
           !gst_caps_is_equal (GST_BUFFER_CAPS (outbuf),
               GST_PAD_CAPS (self->srcpad))) {
@@ -427,14 +463,25 @@ codec_process (GstDucatiVidDec * self, gboolean send, gboolean flush)
         gst_buffer_set_caps (outbuf, GST_PAD_CAPS (self->srcpad));
       }
 
-      gst_pad_push (self->srcpad, outbuf);
+      if (self->crop)
+        gst_buffer_set_video_crop (outbuf, self->crop);
+
+      ret = gst_pad_push (self->srcpad, outbuf);
+      if (flow_ret)
+        *flow_ret = ret;
+      if (ret != GST_FLOW_OK) {
+        GST_WARNING_OBJECT (self, "push failed %s", gst_flow_get_name (ret));
+        /* just unref the remaining buffers (if any) */
+        send = FALSE;
+      }
     } else {
       GST_DEBUG_OBJECT (self, "free buffer: %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]);
   }
 
@@ -442,10 +489,10 @@ codec_process (GstDucatiVidDec * self, gboolean send, gboolean flush)
 }
 
 /** call control(FLUSH), and then process() to pop out all buffers */
-static gboolean
-codec_flush (GstDucatiVidDec * self, gboolean eos)
+gboolean
+gst_ducati_viddec_codec_flush (GstDucatiVidDec * self, gboolean eos)
 {
-  gint err;
+  gint err = FALSE;
 
   GST_DEBUG_OBJECT (self, "flush: eos=%d", eos);
 
@@ -457,14 +504,19 @@ codec_flush (GstDucatiVidDec * self, gboolean eos)
   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;
+  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);
@@ -478,9 +530,16 @@ codec_flush (GstDucatiVidDec * self, gboolean eos)
   self->inArgs->inputID = 0;
 
   do {
-    err = codec_process (self, eos, TRUE);
+    err = codec_process (self, eos, TRUE, NULL);
   } while (err != XDM_EFAIL);
 
+  /* 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;
+
   /* on a flush, it is normal (and not an error) for the last _process() call
    * to return an error..
    */
@@ -637,7 +696,6 @@ gst_ducati_viddec_sink_setcaps (GstPad * pad, GstCaps * caps)
   GstStructure *s;
   GstCaps *outcaps = NULL;
   GstStructure *out_s;
-  gint frn = 0, frd = 1;
   gint par_width, par_height;
   gboolean par_present;
 
@@ -653,7 +711,10 @@ gst_ducati_viddec_sink_setcaps (GstPad * pad, GstCaps * caps)
   /* update output/padded sizes */
   klass->update_buffer_size (self);
 
-  gst_structure_get_fraction (s, "framerate", &frn, &frd);
+  if (!gst_structure_get_fraction (s, "framerate", &self->fps_n, &self->fps_d)) {
+    self->fps_n = 0;
+    self->fps_d = 1;
+  }
   gst_structure_get_boolean (s, "interlaced", &self->interlaced);
   par_present = gst_structure_get_fraction (s, "pixel-aspect-ratio",
       &par_width, &par_height);
@@ -680,7 +741,7 @@ gst_ducati_viddec_sink_setcaps (GstPad * pad, GstCaps * caps)
   gst_structure_set (out_s,
       "width", G_TYPE_INT, self->padded_width,
       "height", G_TYPE_INT, self->padded_height,
-      "framerate", GST_TYPE_FRACTION, frn, frd, NULL);
+      "framerate", GST_TYPE_FRACTION, self->fps_n, self->fps_d, NULL);
   if (par_present)
     gst_structure_set (out_s, "pixel-aspect-ratio", GST_TYPE_FRACTION,
         par_width, par_height, NULL);
@@ -704,8 +765,22 @@ gst_ducati_viddec_sink_setcaps (GstPad * pad, GstCaps * caps)
   GST_INFO_OBJECT (self, "outsize %d stride %d outcaps: %" GST_PTR_FORMAT,
       self->outsize, self->stride, outcaps);
 
+  if (!self->first_in_buffer) {
+    /* Caps changed mid stream. We flush the codec to unlock all the potentially
+     * locked buffers. This is needed for downstream sinks that provide a
+     * buffer pool and need to destroy all the outstanding buffers before they
+     * can negotiate new caps (hello v4l2sink).
+     */
+    gst_ducati_viddec_codec_flush (self, FALSE);
+  }
+
+  /* (re)send a crop event when caps change */
+  self->send_crop_event = TRUE;
+
   ret = gst_pad_set_caps (self->srcpad, outcaps);
 
+  GST_INFO_OBJECT (self, "set caps done %d, %" GST_PTR_FORMAT, ret, outcaps);
+
 out:
   if (outcaps)
     gst_caps_unref (outcaps);
@@ -714,13 +789,52 @@ out:
   return ret;
 }
 
-static gboolean
-gst_ducati_viddec_query (GstPad * pad, GstQuery * query)
+static GstCaps *
+gst_ducati_viddec_src_getcaps (GstPad * pad)
 {
-  gboolean res = TRUE, forward = TRUE;
-  GstDucatiVidDec *self = GST_DUCATIVIDDEC (GST_OBJECT_PARENT (pad));
+  GstCaps *caps = NULL;
+  GstCaps *outcaps = NULL;
+  int i;
 
-  GST_DEBUG_OBJECT (self, "query: %" GST_PTR_FORMAT, query);
+  caps = GST_PAD_CAPS (pad);
+  if (caps == NULL) {
+    outcaps = gst_caps_copy (gst_pad_get_pad_template_caps (pad));
+    return outcaps;
+  }
+
+  outcaps = gst_caps_new_empty ();
+
+  /* allow -rowstrided and regular yuv */
+  for (i = 0; i < gst_caps_get_size (caps); i++) {
+    GstStructure *structure;
+    GstStructure *modified_structure;
+    GValue value = { 0 };
+
+    structure = gst_caps_get_structure (caps, i);
+    gst_caps_append_structure (outcaps, gst_structure_copy (structure));
+    modified_structure = gst_structure_copy (structure);
+
+    if (gst_structure_has_name (structure, "video/x-raw-yuv")) {
+      gst_structure_set_name (modified_structure, "video/x-raw-yuv-strided");
+      g_value_init (&value, GST_TYPE_INT_RANGE);
+      gst_value_set_int_range (&value, 0, G_MAXINT);
+      gst_structure_set_value (modified_structure, "rowstride", &value);
+      gst_caps_append_structure (outcaps, modified_structure);
+      g_value_unset (&value);
+    } else {
+      gst_structure_set_name (modified_structure, "video/x-raw-yuv");
+      gst_structure_remove_field (modified_structure, "rowstride");
+      gst_caps_append_structure (outcaps, modified_structure);
+    }
+  }
+  return outcaps;
+}
+
+static gboolean
+gst_ducati_viddec_query (GstDucatiVidDec * self, GstPad * pad,
+    GstQuery * query, gboolean * forward)
+{
+  gboolean res = TRUE;
 
   switch (GST_QUERY_TYPE (query)) {
     case GST_QUERY_BUFFERS:
@@ -731,13 +845,26 @@ gst_ducati_viddec_query (GstPad * pad, GstQuery * query)
           self->padded_width, self->padded_height);
       gst_query_set_buffers_dimensions (query,
           self->padded_width, self->padded_height);
-      forward = FALSE;
+      *forward = FALSE;
       break;
     default:
       break;
   }
 
-  if (forward)
+
+  return res;
+}
+
+static gboolean
+gst_ducati_viddec_src_query (GstPad * pad, GstQuery * query)
+{
+  gboolean res = TRUE, forward = TRUE;
+  GstDucatiVidDec *self = GST_DUCATIVIDDEC (GST_OBJECT_PARENT (pad));
+  GstDucatiVidDecClass *klass = GST_DUCATIVIDDEC_GET_CLASS (self);
+
+  GST_DEBUG_OBJECT (self, "query: %" GST_PTR_FORMAT, query);
+  res = klass->query (self, pad, query, &forward);
+  if (res && forward)
     res = gst_pad_query_default (pad, query);
 
   return res;
@@ -750,6 +877,15 @@ gst_ducati_viddec_do_qos (GstDucatiVidDec * self, GstBuffer * buf)
   GstDucatiVidDecClass *klass = GST_DUCATIVIDDEC_GET_CLASS (self);
   gint64 diff;
 
+  if (self->wait_keyframe) {
+    if (GST_BUFFER_FLAG_IS_SET (buf, GST_BUFFER_FLAG_DELTA_UNIT)) {
+      GST_INFO_OBJECT (self, "skipping until the next keyframe");
+      return FALSE;
+    }
+
+    self->wait_keyframe = FALSE;
+  }
+
   timestamp = GST_BUFFER_TIMESTAMP (buf);
   if (self->segment.format != GST_FORMAT_TIME ||
       self->qos_earliest_time == GST_CLOCK_TIME_NONE)
@@ -800,7 +936,7 @@ gst_ducati_viddec_chain (GstPad * pad, GstBuffer * buf)
 {
   GstDucatiVidDec *self = GST_DUCATIVIDDEC (GST_OBJECT_PARENT (pad));
   GstClockTime ts = GST_BUFFER_TIMESTAMP (buf);
-  GstFlowReturn ret;
+  GstFlowReturn ret = GST_FLOW_OK;
   Int32 err;
   GstBuffer *outbuf = NULL;
   GstCaps *outcaps = NULL;
@@ -808,6 +944,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;
   }
 
@@ -830,7 +967,9 @@ allocate_buffer:
   ret = gst_pad_alloc_buffer (self->srcpad, 0, self->outsize,
       GST_PAD_CAPS (self->srcpad), &outbuf);
   if (ret != GST_FLOW_OK) {
-    GST_ERROR_OBJECT (self, "alloc_buffer failed %s", gst_flow_get_name (ret));
+    GST_WARNING_OBJECT (self, "alloc_buffer failed %s",
+        gst_flow_get_name (ret));
+    gst_buffer_unref (buf);
     return ret;
   }
 
@@ -862,6 +1001,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;
     }
   }
@@ -872,14 +1013,15 @@ allocate_buffer:
   /* Pass new output buffer to the decoder to decode into. Use buffers from the
    * internal pool while self->first_out_buffer == TRUE in order to simplify
    * things in case we need to renegotiate */
-  self->inArgs->inputID = codec_prepare_outbuf (self, &outbuf, self->first_out_buffer);
+  self->inArgs->inputID =
+      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;
   }
 
 have_out_buf:
-  self->in_size = 0;
   buf = GST_DUCATIVIDDEC_GET_CLASS (self)->push_input (self, buf);
 
   if (ts != GST_CLOCK_TIME_NONE) {
@@ -887,8 +1029,7 @@ have_out_buf:
     /* if next buffer has earlier ts than previous, then the ts
      * we are getting are definitely decode order (DTS):
      */
-    if ((self->last_dts != GST_CLOCK_TIME_NONE) &&
-        (self->last_dts > ts)) {
+    if ((self->last_dts != GST_CLOCK_TIME_NONE) && (self->last_dts > ts)) {
       GST_DEBUG_OBJECT (self, "input timestamp definitely DTS");
       self->ts_may_be_pts = FALSE;
     }
@@ -903,16 +1044,44 @@ have_out_buf:
 
   self->inArgs->numBytes = self->in_size;
   self->inBufs->descs[0].bufSize.bytes = self->in_size;
+  self->inBufs->descs[0].memType = XDM_MEMTYPE_BO;
 
-  err = codec_process (self, TRUE, FALSE);
+  err = codec_process (self, TRUE, FALSE, &ret);
   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;
 
+  /* 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) {
+        GST_DEBUG_OBJECT (self, "First 16 were:");
+        gst_util_dump_mem (self->input, 16);
+        memmove (self->input, self->input + self->outArgs->bytesConsumed,
+            self->in_size - self->outArgs->bytesConsumed);
+      }
+      self->in_size -= self->outArgs->bytesConsumed;
+    }
+  }
+
   if (self->outArgs->outBufsInUseFlag) {
     GST_DEBUG_OBJECT (self, "outBufsInUseFlag set");
     self->need_out_buf = FALSE;
@@ -927,6 +1096,9 @@ have_out_buf:
     goto allocate_buffer;
   }
 
+  if (self->needs_flushing)
+    gst_ducati_viddec_codec_flush (self, FALSE);
+
   return GST_FLOW_OK;
 }
 
@@ -953,14 +1125,15 @@ gst_ducati_viddec_event (GstPad * pad, GstEvent * event)
       break;
     }
     case GST_EVENT_EOS:
-      if (!codec_flush (self, TRUE)) {
+      if (!gst_ducati_viddec_codec_flush (self, TRUE)) {
         GST_ERROR_OBJECT (self, "could not flush on eos");
         ret = FALSE;
       }
       break;
     case GST_EVENT_FLUSH_STOP:
-      if (!codec_flush (self, FALSE)) {
+      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);
@@ -1023,6 +1196,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)),
@@ -1034,6 +1208,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;
@@ -1047,6 +1231,8 @@ gst_ducati_viddec_change_state (GstElement * element, GstStateChange transition)
   switch (transition) {
     case GST_STATE_CHANGE_PAUSED_TO_READY:
       self->interlaced = FALSE;
+      self->send_crop_event = TRUE;
+      gst_ducati_viddec_codec_flush (self, FALSE);
       break;
     case GST_STATE_CHANGE_READY_TO_NULL:
       codec_delete (self);
@@ -1072,13 +1258,11 @@ gst_ducati_viddec_get_property (GObject * obj,
 {
   GstDucatiVidDec *self = GST_DUCATIVIDDEC (obj);
 
+
   switch (prop_id) {
     case PROP_VERSION:{
       int err;
-      char *version = gst_ducati_alloc_1d (VERSION_LENGTH);
-
-      /* in case something fails: */
-      snprintf (version, VERSION_LENGTH, "unsupported");
+      char *version = NULL;
 
       if (!self->engine)
         engine_open (self);
@@ -1087,7 +1271,8 @@ gst_ducati_viddec_get_property (GObject * obj,
         codec_create (self);
 
       if (self->codec) {
-        self->status->data.buf = (XDAS_Int8 *) TilerMem_VirtToPhys (version);
+        version = dce_alloc (VERSION_LENGTH);
+        self->status->data.buf = (XDAS_Int8 *) version;
         self->status->data.bufSize = VERSION_LENGTH;
 
         err = VIDDEC3_control (self->codec, XDM_GETVERSION,
@@ -1101,8 +1286,8 @@ gst_ducati_viddec_get_property (GObject * obj,
       }
 
       g_value_set_string (value, version);
-
-      MemMgr_Free (version);
+      if (version)
+        dce_free (version);
 
       break;
     }
@@ -1156,6 +1341,7 @@ gst_ducati_viddec_class_init (GstDucatiVidDecClass * klass)
   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->query = GST_DEBUG_FUNCPTR (gst_ducati_viddec_query);
 
   g_object_class_install_property (gobject_class, PROP_VERSION,
       g_param_spec_string ("version", "Version",
@@ -1182,7 +1368,9 @@ gst_ducati_viddec_init (GstDucatiVidDec * self, GstDucatiVidDecClass * klass)
   gst_pad_set_event_function (self->srcpad,
       GST_DEBUG_FUNCPTR (gst_ducati_viddec_src_event));
   gst_pad_set_query_function (self->srcpad,
-      GST_DEBUG_FUNCPTR (gst_ducati_viddec_query));
+      GST_DEBUG_FUNCPTR (gst_ducati_viddec_src_query));
+  gst_pad_set_getcaps_function (self->srcpad,
+      GST_DEBUG_FUNCPTR (gst_ducati_viddec_src_getcaps));
 
   gst_element_add_pad (GST_ELEMENT (self), self->sinkpad);
   gst_element_add_pad (GST_ELEMENT (self), self->srcpad);
@@ -1194,10 +1382,13 @@ gst_ducati_viddec_init (GstDucatiVidDec * self, GstDucatiVidDecClass * klass)
    */
   self->width = 128;
   self->height = 128;
+  self->fps_n = -1;
+  self->fps_d = -1;
 
   self->first_in_buffer = TRUE;
   self->first_out_buffer = TRUE;
   self->interlaced = FALSE;
+  self->send_crop_event = TRUE;
 
   self->dts_ridx = self->dts_widx = 0;
   self->last_dts = self->last_pts = GST_CLOCK_TIME_NONE;
@@ -1210,6 +1401,9 @@ gst_ducati_viddec_init (GstDucatiVidDec * self, GstDucatiVidDecClass * klass)
 
   self->qos_proportion = 1;
   self->qos_earliest_time = GST_CLOCK_TIME_NONE;
+  self->wait_keyframe = TRUE;
 
   self->need_out_buf = TRUE;
+  self->device = NULL;
+  self->input_bo = NULL;
 }