]> Gitweb @ Texas Instruments - Open Source Git Repositories - git.TI.com/gitweb - glsdk/gst-plugin-ducati.git/blobdiff - src/gstducatividdec.c
ducatividdec: flush the codec on caps changes
[glsdk/gst-plugin-ducati.git] / src / gstducatividdec.c
index d5c1c154448fb9657de981af50417ecd2098621f..0ba4af36d61ee32bd34355932f719c576b32dc8e 100644 (file)
@@ -192,13 +192,25 @@ codec_bufferpool_get (GstDucatiVidDec * self, GstBuffer * buf)
 }
 
 static XDAS_Int32
-codec_prepare_outbuf (GstDucatiVidDec * self, GstBuffer * buf)
+codec_prepare_outbuf (GstDucatiVidDec * self, GstBuffer ** buf,
+    gboolean force_internal)
 {
   XDAS_Int16 y_type, uv_type;
   guint8 *y_vaddr, *uv_vaddr;
   SSPtr y_paddr, uv_paddr;
 
-  y_vaddr = GST_BUFFER_DATA (buf);
+  if (force_internal) {
+    GstBuffer *orig = *buf;
+
+    GST_DEBUG_OBJECT (self, "internal bufferpool forced");
+    *buf = codec_bufferpool_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);
+  }
+
+  y_vaddr = GST_BUFFER_DATA (*buf);
   uv_vaddr = y_vaddr + self->stride * self->padded_height;
 
   y_paddr = TilerMem_VirtToPhys (y_vaddr);
@@ -215,7 +227,8 @@ codec_prepare_outbuf (GstDucatiVidDec * self, GstBuffer * buf)
 
   if (y_type < 0 || uv_type < 0) {
     GST_DEBUG_OBJECT (self, "non TILER buffer, fallback to bufferpool");
-    return codec_prepare_outbuf (self, codec_bufferpool_get (self, buf));
+    *buf = codec_bufferpool_get (self, *buf);
+    return codec_prepare_outbuf (self, buf, FALSE);
   }
 
   if (!self->outBufs->numBufs) {
@@ -242,14 +255,15 @@ codec_prepare_outbuf (GstDucatiVidDec * self, GstBuffer * buf)
     if ((self->outBufs->descs[0].memType != y_type) ||
         (self->outBufs->descs[1].memType != uv_type)) {
       GST_DEBUG_OBJECT (self, "buffer mismatch, fallback to bufferpool");
-      return codec_prepare_outbuf (self, codec_bufferpool_get (self, buf));
+      *buf = codec_bufferpool_get (self, *buf);
+      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;
 
-  return (XDAS_Int32) buf;      // XXX use lookup table
+  return (XDAS_Int32) *buf;      // XXX use lookup table
 }
 
 static GstBuffer *
@@ -279,9 +293,10 @@ codec_process (GstDucatiVidDec * self, gboolean send, gboolean flush)
   GstClockTime t;
   GstBuffer *outbuf = NULL;
   gint i;
+  GstDucatiVidDecClass *klass = GST_DUCATIVIDDEC_GET_CLASS (self);
 
-  self->outArgs->outputID[0] = 0;
-  self->outArgs->freeBufID[0] = 0;
+  memset (&self->outArgs->outputID, 0, sizeof (self->outArgs->outputID));
+  memset (&self->outArgs->freeBufID, 0, sizeof (self->outArgs->freeBufID));
 
   t = gst_util_get_timestamp ();
   err = VIDDEC3_process (self->codec,
@@ -294,43 +309,138 @@ codec_process (GstDucatiVidDec * self, gboolean send, gboolean flush)
 
     err = VIDDEC3_control (self->codec, XDM_GETSTATUS,
         self->dynParams, self->status);
+    if (!err) {
+      GST_WARNING_OBJECT (self, "XDM_GETSTATUS: err=%d, extendedError=%08x",
+          err, self->status->extendedError);
+    }
 
-    GST_WARNING_OBJECT (self, "XDM_GETSTATUS: err=%d, extendedError=%08x",
-        err, self->status->extendedError);
-
-    if (XDM_ISFATALERROR (self->outArgs->extendedError) || flush) {
-      /* we are processing for display and it is a non-fatal error, so lets
-       * try to recover.. otherwise return the error
-       */
+    if (flush)
       err = XDM_EFAIL;
-    } else {
-      err = XDM_EOK;
-    }
+    else
+      err = klass->handle_error (self, err,
+          self->outArgs->extendedError, self->status->extendedError);
   }
 
   for (i = 0; self->outArgs->outputID[i]; i++) {
-    if (G_UNLIKELY (self->first_out_buffer) && send) {
+    gboolean interlaced;
+
+    outbuf = codec_get_outbuf (self, self->outArgs->outputID[i]);
+
+    interlaced = self->outArgs->decodedBufs.contentType == IVIDEO_PROGRESSIVE ? FALSE : TRUE;
+
+    /* 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 "
+          "thinks interlaced=%d... trusting codec", self->interlaced,
+          interlaced);
+
+      self->interlaced = interlaced;
+
+      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);
+      if (!gst_pad_set_caps (self->srcpad, caps)) {
+        GST_ERROR_OBJECT (self, "downstream didn't want to change interlace mode");
+        err = XDM_EFAIL;
+      }
+      gst_caps_unref (caps);
+
+      /* this buffer still has the old caps so we skip it */
+      send = FALSE;
+    }
+
+    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);
 
-      GST_DEBUG_OBJECT (self, "setting crop to %d, %d, %d, %d",
-          r->topLeft.x, r->topLeft.y, r->bottomRight.x, r->bottomRight.y);
+      crop_width = r->bottomRight.x - r->topLeft.x;
+      crop_height = r->bottomRight.y - r->topLeft.y;
+
+      if (crop_width > self->input_width)
+        crop_width = self->input_width;
+      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);
 
       gst_pad_push_event (self->srcpad,
           gst_event_new_crop (r->topLeft.y, r->topLeft.x,
-              r->bottomRight.x - r->topLeft.x,
-              r->bottomRight.y - r->topLeft.y));
+              crop_width, crop_height));
 
+      self->send_crop_event = FALSE;
+    }
+
+    if (G_UNLIKELY (self->first_out_buffer) && send) {
+      GstDucatiBufferPool *pool;
       self->first_out_buffer = FALSE;
+
+      /* Destroy the pool so the buffers we used so far are eventually released.
+       * The pool will be recreated if needed.
+       */
+      pool = self->pool;
+      self->pool = NULL;
+      gst_ducati_bufferpool_destroy (pool);
     }
 
-    outbuf = codec_get_outbuf (self, self->outArgs->outputID[i]);
     if (send) {
+      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 (GST_BUFFER_TIMESTAMP (outbuf)));
+          i, outbuf, GST_TIME_ARGS (ts));
+
+      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, "
+              "enabling ts_is_pts");
+          self->ts_is_pts = TRUE;
+        }
+      }
+
+      self->last_pts = ts;
+
+      if (self->dts_ridx != self->dts_widx) {
+        ts = self->dts_queue[self->dts_ridx++ % NDTS];
+      }
+
+      if (self->ts_is_pts) {
+        /* if we have a queued DTS from demuxer, use that instead: */
+        GST_BUFFER_TIMESTAMP (outbuf) = ts;
+        GST_DEBUG_OBJECT (self, "fixed ts: %d %p (%" GST_TIME_FORMAT ")",
+            i, outbuf, GST_TIME_ARGS (ts));
+      }
+
+      if (GST_BUFFER_CAPS (outbuf) &&
+          !gst_caps_is_equal (GST_BUFFER_CAPS (outbuf),
+              GST_PAD_CAPS (self->srcpad))) {
+        /* this looks a bit scary but it's really just to change the interlace=
+         * field in caps when we start as !interlaced and the codec detects
+         * otherwise */
+        GST_WARNING_OBJECT (self, "overriding buffer caps to fix "
+            "interlace mismatch");
+        outbuf = gst_buffer_make_metadata_writable (outbuf);
+        gst_buffer_set_caps (outbuf, GST_PAD_CAPS (self->srcpad));
+      }
+
       gst_pad_push (self->srcpad, outbuf);
     } else {
       GST_DEBUG_OBJECT (self, "free buffer: %d %p", i, outbuf);
@@ -346,8 +456,8 @@ 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;
 
@@ -358,6 +468,12 @@ codec_flush (GstDucatiVidDec * self, gboolean eos)
    */
   GST_PAD_STREAM_LOCK (self->sinkpad);
 
+  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;
+
   if (G_UNLIKELY (self->first_in_buffer)) {
     return TRUE;
   }
@@ -401,11 +517,11 @@ gst_ducati_viddec_parse_caps (GstDucatiVidDec * self, GstStructure * s)
   const GValue *codec_data;
   gint w, h;
 
-  if (gst_structure_get_int (s, "width", &w) &&
-      gst_structure_get_int (s, "height", &h)) {
+  if (gst_structure_get_int (s, "width", &self->input_width) &&
+      gst_structure_get_int (s, "height", &self->input_height)) {
 
-    h = ALIGN2 (h, 4);          /* round up to MB */
-    w = ALIGN2 (w, 4);          /* round up to MB */
+    h = ALIGN2 (self->input_height, 4); /* round up to MB */
+    w = ALIGN2 (self->input_width, 4);  /* round up to MB */
 
     /* if we've already created codec, but the resolution has changed, we
      * need to re-create the codec:
@@ -514,6 +630,18 @@ gst_ducati_viddec_push_input (GstDucatiVidDec * self, GstBuffer * buf)
   return NULL;
 }
 
+static gint
+gst_ducati_viddec_handle_error (GstDucatiVidDec * self, gint ret,
+    gint extended_error, gint status_extended_error)
+{
+  if (XDM_ISFATALERROR (extended_error))
+    ret = XDM_EFAIL;
+  else
+    ret = XDM_EOK;
+
+  return ret;
+}
+
 /* GstElement vmethod implementations */
 
 static gboolean
@@ -525,8 +653,11 @@ gst_ducati_viddec_sink_setcaps (GstPad * pad, GstCaps * caps)
   GstStructure *s;
   GstCaps *outcaps = NULL;
   GstStructure *out_s;
-  gboolean interlaced = FALSE;
   gint frn = 0, frd = 1;
+  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)) {
@@ -538,17 +669,26 @@ gst_ducati_viddec_sink_setcaps (GstPad * pad, GstCaps * caps)
   /* update output/padded sizes */
   klass->update_buffer_size (self);
 
-  GST_INFO_OBJECT (self, "setcaps (sink): %" GST_PTR_FORMAT, caps);
-
   gst_structure_get_fraction (s, "framerate", &frn, &frd);
-  gst_structure_get_boolean (s, "interlaced", &interlaced);
+  gst_structure_get_boolean (s, "interlaced", &self->interlaced);
+  par_present = gst_structure_get_fraction (s, "pixel-aspect-ratio",
+      &par_width, &par_height);
 
   outcaps = gst_pad_get_allowed_caps (self->srcpad);
   if (outcaps) {
     outcaps = gst_caps_make_writable (outcaps);
     gst_caps_truncate (outcaps);
-  } else {
-    outcaps = gst_caps_new_simple ("video/x-raw-yuv-strided",
+    if (gst_caps_is_empty (outcaps)) {
+      gst_caps_unref (outcaps);
+      outcaps = NULL;
+    }
+  }
+
+  if (!outcaps) {
+    /* note: default to non-strided for better compatibility with
+     * other gst elements that don't understand stride:
+     */
+    outcaps = gst_caps_new_simple ("video/x-raw-yuv",
         "format", GST_TYPE_FOURCC, GST_MAKE_FOURCC ('N', 'V', '1', '2'), NULL);
   }
 
@@ -557,8 +697,11 @@ gst_ducati_viddec_sink_setcaps (GstPad * pad, GstCaps * caps)
       "width", G_TYPE_INT, self->padded_width,
       "height", G_TYPE_INT, self->padded_height,
       "framerate", GST_TYPE_FRACTION, frn, frd, NULL);
+  if (par_present)
+    gst_structure_set (out_s, "pixel-aspect-ratio", GST_TYPE_FRACTION,
+        par_width, par_height, NULL);
 
-  if (interlaced)
+  if (self->interlaced)
     gst_structure_set (out_s, "interlaced", G_TYPE_BOOLEAN, TRUE, NULL);
 
   if (!strcmp (gst_structure_get_name (out_s), "video/x-raw-yuv-strided")) {
@@ -577,6 +720,18 @@ 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);
 
 out:
@@ -616,20 +771,95 @@ gst_ducati_viddec_query (GstPad * pad, GstQuery * query)
   return res;
 }
 
+static gboolean
+gst_ducati_viddec_do_qos (GstDucatiVidDec * self, GstBuffer * buf)
+{
+  GstClockTime timestamp, qostime;
+  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)
+    goto no_qos;
+
+  if (G_UNLIKELY (!GST_CLOCK_TIME_IS_VALID (timestamp)))
+    goto no_qos;
+
+  qostime = gst_segment_to_running_time (&self->segment,
+      GST_FORMAT_TIME, timestamp);
+  if (G_UNLIKELY (!GST_CLOCK_TIME_IS_VALID (qostime)))
+    /* out of segment */
+    goto no_qos;
+
+  /* see how our next timestamp relates to the latest qos timestamp. negative
+   * values mean we are early, positive values mean we are too late. */
+  diff = GST_CLOCK_DIFF (qostime, self->qos_earliest_time);
+
+  GST_DEBUG_OBJECT (self, "QOS: qostime %" GST_TIME_FORMAT
+      ", earliest %" GST_TIME_FORMAT " diff %" G_GINT64_FORMAT " proportion %f",
+      GST_TIME_ARGS (qostime), GST_TIME_ARGS (self->qos_earliest_time), diff,
+      self->qos_proportion);
+
+  if (klass->drop_frame (self, buf, diff)) {
+    GST_INFO_OBJECT (self, "dropping frame");
+    return FALSE;
+  }
+
+no_qos:
+  return TRUE;
+}
+
+static gboolean
+gst_ducati_viddec_drop_frame (GstDucatiVidDec * self, GstBuffer * buf,
+    gint64 diff)
+{
+  gboolean is_keyframe = !GST_BUFFER_FLAG_IS_SET (buf,
+      GST_BUFFER_FLAG_DELTA_UNIT);
+
+  if (diff >= 0 && !is_keyframe)
+    return TRUE;
+
+  return FALSE;
+}
+
 static GstFlowReturn
 gst_ducati_viddec_chain (GstPad * pad, GstBuffer * buf)
 {
   GstDucatiVidDec *self = GST_DUCATIVIDDEC (GST_OBJECT_PARENT (pad));
+  GstClockTime ts = GST_BUFFER_TIMESTAMP (buf);
   GstFlowReturn ret;
   Int32 err;
   GstBuffer *outbuf = NULL;
   GstCaps *outcaps = NULL;
+  gboolean decode;
 
   if (G_UNLIKELY (!self->engine)) {
     GST_ERROR_OBJECT (self, "no engine");
     return GST_FLOW_ERROR;
   }
 
+  GST_DEBUG_OBJECT (self, "chain: %" GST_TIME_FORMAT " (%d bytes, flags %d)",
+      GST_TIME_ARGS (ts), GST_BUFFER_SIZE (buf), GST_BUFFER_FLAGS (buf));
+
+  decode = gst_ducati_viddec_do_qos (self, buf);
+  if (!decode) {
+    gst_buffer_unref (buf);
+    return GST_FLOW_OK;
+  }
+
+  if (!self->need_out_buf)
+    goto have_out_buf;
+
   /* do this before creating codec to ensure reverse caps negotiation
    * happens first:
    */
@@ -637,7 +867,7 @@ allocate_buffer:
   ret = gst_pad_alloc_buffer (self->srcpad, 0, self->outsize,
       GST_PAD_CAPS (self->srcpad), &outbuf);
   if (ret != GST_FLOW_OK) {
-    GST_DEBUG_OBJECT (self, "alloc_buffer failed %s", gst_flow_get_name (ret));
+    GST_ERROR_OBJECT (self, "alloc_buffer failed %s", gst_flow_get_name (ret));
     return ret;
   }
 
@@ -676,17 +906,33 @@ allocate_buffer:
   GST_BUFFER_TIMESTAMP (outbuf) = GST_BUFFER_TIMESTAMP (buf);
   GST_BUFFER_DURATION (outbuf) = GST_BUFFER_DURATION (buf);
 
-  /* pass new output buffer as to the decoder to decode into: */
-  self->inArgs->inputID = codec_prepare_outbuf (self, outbuf);
+  /* 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);
   if (!self->inArgs->inputID) {
     GST_ERROR_OBJECT (self, "could not prepare output buffer");
     return GST_FLOW_ERROR;
   }
 
+have_out_buf:
   self->in_size = 0;
   buf = GST_DUCATIVIDDEC_GET_CLASS (self)->push_input (self, buf);
 
-  if (self->in_size == 0) {
+  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
+     * we are getting are definitely decode order (DTS):
+     */
+    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;
+    }
+    self->last_dts = ts;
+  }
+
+  if (self->in_size == 0 && outbuf) {
     GST_DEBUG_OBJECT (self, "no input, skipping process");
     gst_buffer_unref (outbuf);
     return GST_FLOW_OK;
@@ -695,24 +941,27 @@ allocate_buffer:
   self->inArgs->numBytes = self->in_size;
   self->inBufs->descs[0].bufSize.bytes = self->in_size;
 
-  if (buf) {
-    // XXX
-    GST_WARNING_OBJECT (self, "TODO.. can't push more than one.. need loop");
-    gst_buffer_unref (buf);
-    buf = NULL;
-  }
-
   err = codec_process (self, TRUE, FALSE);
   if (err) {
-    GST_ERROR_OBJECT (self, "process returned error: %d %08x",
-        err, self->outArgs->extendedError);
+    GST_ELEMENT_ERROR (self, STREAM, DECODE, (NULL),
+        ("process returned error: %d %08x", err, self->outArgs->extendedError));
     return GST_FLOW_ERROR;
   }
 
   self->first_in_buffer = FALSE;
 
   if (self->outArgs->outBufsInUseFlag) {
-    GST_WARNING_OBJECT (self, "TODO... outBufsInUseFlag");      // XXX
+    GST_DEBUG_OBJECT (self, "outBufsInUseFlag set");
+    self->need_out_buf = FALSE;
+  } else {
+    self->need_out_buf = TRUE;
+  }
+
+  if (buf) {
+    GST_DEBUG_OBJECT (self, "found remaining data: %d bytes",
+        GST_BUFFER_SIZE (buf));
+    ts = GST_BUFFER_TIMESTAMP (buf);
+    goto allocate_buffer;
   }
 
   return GST_FLOW_OK;
@@ -723,22 +972,81 @@ gst_ducati_viddec_event (GstPad * pad, GstEvent * event)
 {
   GstDucatiVidDec *self = GST_DUCATIVIDDEC (GST_OBJECT_PARENT (pad));
   gboolean ret = TRUE;
-  gboolean eos = FALSE;
 
-  GST_INFO_OBJECT (self, "begin: event=%s", GST_EVENT_TYPE_NAME (event));
+  GST_DEBUG_OBJECT (self, "begin: event=%s", GST_EVENT_TYPE_NAME (event));
 
   switch (GST_EVENT_TYPE (event)) {
+    case GST_EVENT_NEWSEGMENT:
+    {
+      gboolean update;
+      GstFormat fmt;
+      gint64 start, stop, time;
+      gdouble rate, arate;
+
+      gst_event_parse_new_segment_full (event, &update, &rate, &arate, &fmt,
+          &start, &stop, &time);
+      gst_segment_set_newsegment_full (&self->segment, update,
+          rate, arate, fmt, start, stop, time);
+      break;
+    }
     case GST_EVENT_EOS:
-      eos = TRUE;
-      /* fall-through */
+      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, eos)) {
+      if (!gst_ducati_viddec_codec_flush (self, FALSE)) {
         GST_ERROR_OBJECT (self, "could not flush");
-        return FALSE;
+        ret = FALSE;
       }
-      /* fall-through */
+      gst_segment_init (&self->segment, GST_FORMAT_UNDEFINED);
+      self->qos_earliest_time = GST_CLOCK_TIME_NONE;
+      self->qos_proportion = 1;
+      self->need_out_buf = TRUE;
+      break;
     default:
-      ret = gst_pad_push_event (self->srcpad, event);
+      break;
+  }
+
+  if (ret)
+    ret = gst_pad_push_event (self->srcpad, event);
+  GST_LOG_OBJECT (self, "end");
+
+  return ret;
+}
+
+static gboolean
+gst_ducati_viddec_src_event (GstPad * pad, GstEvent * event)
+{
+  GstDucatiVidDec *self = GST_DUCATIVIDDEC (GST_OBJECT_PARENT (pad));
+  gboolean ret = TRUE;
+
+  GST_LOG_OBJECT (self, "begin: event=%s", GST_EVENT_TYPE_NAME (event));
+
+  switch (GST_EVENT_TYPE (event)) {
+    case GST_EVENT_QOS:
+    {
+      gdouble proportion;
+      GstClockTimeDiff diff;
+      GstClockTime timestamp;
+
+      gst_event_parse_qos (event, &proportion, &diff, &timestamp);
+
+      GST_OBJECT_LOCK (self);
+      self->qos_proportion = proportion;
+      self->qos_earliest_time = timestamp + 2 * diff;
+      GST_OBJECT_UNLOCK (self);
+
+      GST_DEBUG_OBJECT (self,
+          "got QoS proportion %f %" GST_TIME_FORMAT ", %" G_GINT64_FORMAT,
+          proportion, GST_TIME_ARGS (timestamp), diff);
+
+      ret = gst_pad_push_event (self->sinkpad, event);
+      break;
+    }
+    default:
+      ret = gst_pad_push_event (self->sinkpad, event);
       break;
   }
 
@@ -753,7 +1061,7 @@ gst_ducati_viddec_change_state (GstElement * element, GstStateChange transition)
   GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
   GstDucatiVidDec *self = GST_DUCATIVIDDEC (element);
 
-  GST_INFO_OBJECT (self, "begin: changing state %s -> %s",
+  GST_DEBUG_OBJECT (self, "begin: changing state %s -> %s",
       gst_element_state_get_name (GST_STATE_TRANSITION_CURRENT (transition)),
       gst_element_state_get_name (GST_STATE_TRANSITION_NEXT (transition)));
 
@@ -774,6 +1082,11 @@ gst_ducati_viddec_change_state (GstElement * element, GstStateChange transition)
     goto leave;
 
   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);
       engine_close (self);
@@ -880,6 +1193,8 @@ gst_ducati_viddec_class_init (GstDucatiVidDecClass * klass)
   klass->allocate_params =
       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);
 
   g_object_class_install_property (gobject_class, PROP_VERSION,
       g_param_spec_string ("version", "Version",
@@ -903,12 +1218,16 @@ gst_ducati_viddec_init (GstDucatiVidDec * self, GstDucatiVidDecClass * klass)
       GST_DEBUG_FUNCPTR (gst_ducati_viddec_event));
 
   self->srcpad = gst_pad_new_from_static_template (&src_factory, "src");
+  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_element_add_pad (GST_ELEMENT (self), self->sinkpad);
   gst_element_add_pad (GST_ELEMENT (self), self->srcpad);
 
+  self->input_width = 0;
+  self->input_height = 0;
   /* sane defaults in case we need to create codec without caps negotiation
    * (for example, to get 'version' property)
    */
@@ -917,6 +1236,21 @@ gst_ducati_viddec_init (GstDucatiVidDec * self, GstDucatiVidDecClass * klass)
 
   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;
+  self->ts_may_be_pts = TRUE;
+  self->ts_is_pts = FALSE;
 
   self->pageMemType = XDM_MEMTYPE_TILEDPAGE;
+
+  gst_segment_init (&self->segment, GST_FORMAT_UNDEFINED);
+
+  self->qos_proportion = 1;
+  self->qos_earliest_time = GST_CLOCK_TIME_NONE;
+  self->wait_keyframe = TRUE;
+
+  self->need_out_buf = TRUE;
 }