diff --git a/src/gstducatividdec.c b/src/gstducatividdec.c
index 94b60431526f15006de1ca0ebb4e546bd0ac35d3..7fe084797b726cfbd96d078a412fa1fcbc611bde 100644 (file)
--- a/src/gstducatividdec.c
+++ b/src/gstducatividdec.c
+#define USE_DTS_PTS_CODE
/*
* GStreamer
* Copyright (c) 2010, Texas Instruments Incorporated
# include <config.h>
#endif
-#include <gst/gst.h>
-#include <gst/video/video.h>
-
#include "gstducatividdec.h"
+#include "gstducatibufferpriv.h"
GST_BOILERPLATE (GstDucatiVidDec, gst_ducati_viddec, GstElement,
GST_TYPE_ELEMENT);
static GstStaticPadTemplate src_factory = GST_STATIC_PAD_TEMPLATE ("src",
GST_PAD_SRC,
GST_PAD_ALWAYS,
- GST_STATIC_CAPS (GST_VIDEO_CAPS_YUV_STRIDED ("NV12", "[ 0, max ]"))
+ GST_STATIC_CAPS (GST_VIDEO_CAPS_YUV ("NV12"))
);
+enum
+{
+ PROP_0,
+ PROP_VERSION,
+ PROP_MAX_REORDER_FRAMES,
+};
+
/* helper functions */
static void
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 ("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;
static void
codec_delete (GstDucatiVidDec * self)
{
+ if (self->pool) {
+ gst_drm_buffer_pool_destroy (self->pool);
+ self->pool = NULL;
+ }
+
if (self->codec) {
- VIDDEC3_delete(self->codec);
+ VIDDEC3_delete (self->codec);
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;
}
}
}
/* these need to be set before VIDDEC3_create */
- self->params->maxWidth = (self->width + 15) & ~0xf; /* round up to MB */
- self->params->maxHeight = (self->height + 15) & ~0xf; /* round up to MB */
+ self->params->maxWidth = self->width;
+ self->params->maxHeight = self->height;
codec_name = GST_DUCATIVIDDEC_GET_CLASS (self)->codec_name;
/* create codec: */
GST_DEBUG_OBJECT (self, "creating codec: %s", codec_name);
- self->codec = VIDDEC3_create (self->engine, (char *)codec_name, self->params);
+ self->codec =
+ VIDDEC3_create (self->engine, (String) codec_name, self->params);
if (!self->codec) {
return FALSE;
}
- err = VIDDEC3_control (self->codec, XDM_SETPARAMS, self->dynParams, self->status);
+ err =
+ VIDDEC3_control (self->codec, XDM_SETPARAMS, self->dynParams,
+ self->status);
if (err) {
GST_ERROR_OBJECT (self, "failed XDM_SETPARAMS");
return FALSE;
}
-#if 0
- /* not entirely sure why we need to call this here.. just copying omx.. */
- err = VIDDEC3_control(self->codec, XDM_GETBUFINFO, self->dynParams, self->status);
- if (err) {
- GST_ERROR_OBJECT (self, "failed XDM_GETBUFINFO");
- return FALSE;
- }
-#endif
-
self->first_in_buffer = TRUE;
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 XDAS_Int32
-codec_prepare_outbuf (GstDucatiVidDec * self, GstBuffer * buf)
+static inline GstBuffer *
+codec_buffer_pool_get (GstDucatiVidDec * self, GstBuffer * buf)
+{
+ if (G_UNLIKELY (!self->pool)) {
+ 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_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)
{
- 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);
- y_vaddr = GST_BUFFER_DATA (buf);
- uv_vaddr = y_vaddr + self->stride * self->padded_height;
+ /* 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 ();
- 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);
+ 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)) {
- return 0;
+ 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[0].bufSize.tileMem.width = self->padded_width;
- self->outBufs->descs[0].bufSize.tileMem.height = self->padded_height;
- self->outBufs->descs[1].memType = uv_type;
- /* 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
- */
- // TODO
+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;
}
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;
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_buffer_unref (buf);
+ GST_DEBUG_OBJECT (self, "free buffer: %d %p", id, 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);
}
}
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));
+
+ GST_DEBUG ("Calling VIDDEC3_process");
t = gst_util_get_timestamp ();
err = VIDDEC3_process (self->codec,
self->inBufs, self->outBufs, self->inArgs, self->outArgs);
- GST_INFO_OBJECT (self, "%10dns", (gint) (gst_util_get_timestamp () - t));
+ t = gst_util_get_timestamp () - t;
+ GST_DEBUG_OBJECT (self, "VIDDEC3_process took %10dns (%d ms)", (gint) t,
+ (gint) (t / 1000000));
+
if (err) {
- if (XDM_ISFATALERROR (self->outArgs->extendedError) || flush) {
- return 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) {
+ GST_WARNING_OBJECT (self, "XDM_GETSTATUS: err=%d, extendedError=%08x",
+ err, self->status->extendedError);
+ gst_ducati_log_extended_error_info (self->status->extendedError);
}
- /* we are processing for display and it is a non-fatal error, so lets
- * try to recover..
- */
- err = XDM_EOK;
+
+ if (flush)
+ err = XDM_EFAIL;
+ 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) {
+ /* 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;
+
+ /* 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_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");
+ 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;
+
/* 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;
+
+ 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));
+
+ 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) {
+ GstDRMBufferPool *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_drm_buffer_pool_destroy (pool);
}
- outbuf = codec_get_outbuf (self, self->outArgs->outputID[i]);
if (send) {
+ GstClockTime ts;
+
+ 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)));
- gst_pad_push (self->srcpad, outbuf);
+ 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, "
+ "enabling ts_is_pts");
+ self->ts_is_pts = TRUE;
+ }
+ }
+#endif
+
+ 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));
+ }
+
+ 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) {
+ 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, "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]);
}
}
/** 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);
+ 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);
+ err = VIDDEC3_control (self->codec, XDM_FLUSH, self->dynParams, self->status);
if (err) {
GST_ERROR_OBJECT (self, "failed XDM_FLUSH");
- return FALSE;
+ goto out;
}
+ 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);
+ err = codec_process (self, eos, TRUE, NULL);
} while (err != XDM_EFAIL);
- self->first_in_buffer = TRUE;
+ /* 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..
+ */
+ err = XDM_EOK;
+
+out:
+ GST_PAD_STREAM_UNLOCK (self->sinkpad);
GST_DEBUG_OBJECT (self, "done");
- return TRUE;
+ return !err;
}
/* GstDucatiVidDec vmethod default implementations */
gst_ducati_viddec_parse_caps (GstDucatiVidDec * self, GstStructure * s)
{
const GValue *codec_data;
+ gint w, h;
+
+ if (gst_structure_get_int (s, "width", &self->input_width) &&
+ gst_structure_get_int (s, "height", &self->input_height)) {
+
+ 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:
+ */
+ if (G_UNLIKELY (self->codec)) {
+ if ((h != self->height) || (w != self->width)) {
+ codec_delete (self);
+ }
+ }
- if (gst_structure_get_int (s, "width", &self->width) &&
- gst_structure_get_int (s, "height", &self->height)) {
+ self->width = w;
+ self->height = h;
- const GValue *codec_data = gst_structure_get_value (s, "codec_data");
+ codec_data = gst_structure_get_value (s, "codec_data");
if (codec_data) {
GstBuffer *buffer = gst_value_get_buffer (codec_data);
self->params->maxFrameRate = 30000;
self->params->maxBitRate = 10000000;
- //rv??
-
self->params->dataEndianness = XDM_BYTE;
self->params->forceChromaFormat = XDM_YUV_420SP;
self->params->operatingMode = IVIDEO_DECODE_ONLY;
if (G_UNLIKELY (!self->status)) {
return FALSE;
}
+ memset (self->status, 0, status_sz);
self->status->size = status_sz;
/* allocate inBufs/outBufs: */
}
self->inArgs->size = inargs_sz;
self->outArgs->size = outargs_sz;
+
+ return TRUE;
}
static GstBuffer *
return NULL;
}
+static GstFlowReturn
+gst_ducati_viddec_push_output (GstDucatiVidDec * self, GstBuffer * buf)
+{
+ GstFlowReturn ret = GST_FLOW_OK;
+
+ /* if no reordering info was set, just send the buffer */
+ if (GST_BUFFER_OFFSET_END (buf) == GST_BUFFER_OFFSET_NONE) {
+ GST_DEBUG_OBJECT (self, "No reordering info on that buffer, sending now");
+ return gst_pad_push (self->srcpad, buf);
+ }
+
+ /* 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)
+{
+ if (XDM_ISFATALERROR (extended_error))
+ ret = XDM_EFAIL;
+ else
+ ret = XDM_EOK;
+
+ return ret;
+}
+
/* GstElement vmethod implementations */
static gboolean
-gst_ducati_viddec_set_caps (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;
-
- g_return_val_if_fail (caps, FALSE);
- g_return_val_if_fail (gst_caps_is_fixed (caps), FALSE);
+ GstCaps *outcaps = NULL;
+ GstStructure *out_s;
+ gint par_width, par_height;
+ gboolean par_present;
s = gst_caps_get_structure (caps, 0);
+ if (!klass->parse_caps (self, s)) {
+ GST_WARNING_OBJECT (self, "missing required fields");
+ ret = FALSE;
+ goto out;
+ }
- if (pad == self->sinkpad) {
- gint frn = 0, frd = 1;
- GST_INFO_OBJECT (self, "setcaps (sink): %" GST_PTR_FORMAT, caps);
-
- if (klass->parse_caps (self, s)) {
- GstCaps *outcaps;
+ /* 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);
+
+ outcaps = gst_pad_get_allowed_caps (self->srcpad);
+ if (outcaps) {
+ outcaps = gst_caps_make_writable (outcaps);
+ gst_caps_truncate (outcaps);
+ if (gst_caps_is_empty (outcaps)) {
+ gst_caps_unref (outcaps);
+ outcaps = NULL;
+ }
+ }
- self->stride = 4096; /* TODO: don't hardcode */
+ if (!outcaps) {
+ outcaps = gst_caps_new_simple ("video/x-raw-yuv",
+ "format", GST_TYPE_FOURCC, GST_MAKE_FOURCC ('N', 'V', '1', '2'), NULL);
+ }
- /* update output/padded sizes:
- */
- klass->update_buffer_size (self);
+ out_s = gst_caps_get_structure (outcaps, 0);
+ gst_structure_set (out_s,
+ "width", G_TYPE_INT, self->padded_width,
+ "height", G_TYPE_INT, self->padded_height,
+ "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);
- self->outsize =
- GST_ROUND_UP_2 (self->stride * self->padded_height * 3) / 2;
+ if (self->interlaced)
+ gst_structure_set (out_s, "interlaced", G_TYPE_BOOLEAN, TRUE, NULL);
- outcaps = gst_caps_new_simple ("video/x-raw-yuv-strided",
- "rowstride", G_TYPE_INT, self->stride,
- "format", GST_TYPE_FOURCC, GST_MAKE_FOURCC ('N','V','1','2'),
- "width", G_TYPE_INT, self->padded_width,
- "height", G_TYPE_INT, self->padded_height,
- "framerate", GST_TYPE_FRACTION, frn, frd,
- NULL);
+ self->stride = gst_video_format_get_row_stride (GST_VIDEO_FORMAT_NV12,
+ 0, self->padded_width);
- GST_DEBUG_OBJECT (self, "outcaps: %" GST_PTR_FORMAT, outcaps);
+ self->outsize = gst_video_format_get_size (GST_VIDEO_FORMAT_NV12,
+ self->stride, self->padded_height);
- ret = gst_pad_set_caps (self->srcpad, outcaps);
- gst_caps_unref (outcaps);
+ GST_INFO_OBJECT (self, "outsize %d stride %d outcaps: %" GST_PTR_FORMAT,
+ self->outsize, self->stride, outcaps);
- if (!ret) {
- GST_WARNING_OBJECT (self, "failed to set caps");
- return FALSE;
- }
- } else {
- GST_WARNING_OBJECT (self, "missing required fields");
- return FALSE;
- }
- } else {
- GST_INFO_OBJECT (self, "setcaps (src): %" GST_PTR_FORMAT, caps);
- // XXX check to make sure caps are ok.. keep track if we
- // XXX need to handle unstrided buffers..
- GST_WARNING_OBJECT (self, "TODO");
+ 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);
+
+ /* 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 gst_pad_set_caps (pad, caps);
+ return ret;
+}
+
+static GstCaps *
+gst_ducati_viddec_src_getcaps (GstPad * pad)
+{
+ GstCaps *caps = NULL;
+
+ caps = GST_PAD_CAPS (pad);
+ if (caps == NULL) {
+ return gst_caps_copy (gst_pad_get_pad_template_caps (pad));
+ } else {
+ return gst_caps_copy (caps);
+ }
}
static gboolean
-gst_ducati_viddec_query (GstPad * pad, GstQuery * query)
+gst_ducati_viddec_query (GstDucatiVidDec * self, GstPad * pad,
+ GstQuery * query, gboolean * forward)
{
- GstDucatiVidDec *self = GST_DUCATIVIDDEC (GST_OBJECT_PARENT (pad));
+ gboolean res = TRUE;
switch (GST_QUERY_TYPE (query)) {
case GST_QUERY_BUFFERS:
self->padded_width, self->padded_height);
gst_query_set_buffers_dimensions (query,
self->padded_width, self->padded_height);
- return TRUE;
+ *forward = FALSE;
+ break;
default:
+ break;
+ }
+
+
+ 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;
+}
+
+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->can_drop_frame (self, buf, diff)) {
+ GST_INFO_OBJECT (self, "dropping frame");
+ return FALSE;
}
+
+no_qos:
+ return TRUE;
+}
+
+static gboolean
+gst_ducati_viddec_can_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));
- GstFlowReturn ret;
+ GstClockTime ts = GST_BUFFER_TIMESTAMP (buf);
+ GstFlowReturn ret = GST_FLOW_OK;
Int32 err;
GstBuffer *outbuf = NULL;
+ GstCaps *outcaps = NULL;
+ gboolean decode;
if (G_UNLIKELY (!self->engine)) {
GST_ERROR_OBJECT (self, "no engine");
+ gst_buffer_unref (buf);
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:
*/
- ret = gst_pad_alloc_buffer_and_set_caps (self->srcpad, 0, self->outsize,
+allocate_buffer:
+ ret = gst_pad_alloc_buffer (self->srcpad, 0, self->outsize,
GST_PAD_CAPS (self->srcpad), &outbuf);
-
if (ret != GST_FLOW_OK) {
- /* TODO: if we had our own buffer class, we could allocate our own
- * output buffer from TILER...
- */
- GST_WARNING_OBJECT (self, "TODO: allocate output TILER buffer");
+ GST_WARNING_OBJECT (self, "alloc_buffer failed %s",
+ gst_flow_get_name (ret));
+ gst_buffer_unref (buf);
return ret;
}
+ outcaps = GST_BUFFER_CAPS (outbuf);
+ if (outcaps && !gst_caps_is_equal (outcaps, GST_PAD_CAPS (self->srcpad))) {
+ GstStructure *s;
+
+ GST_INFO_OBJECT (self, "doing upstream negotiation bufsize %d",
+ GST_BUFFER_SIZE (outbuf));
+
+ s = gst_caps_get_structure (outcaps, 0);
+ gst_structure_get_int (s, "rowstride", &self->stride);
+ self->outsize = gst_video_format_get_size (GST_VIDEO_FORMAT_NV12,
+ self->stride, self->padded_height);
+
+ GST_INFO_OBJECT (self, "outsize %d stride %d outcaps: %" GST_PTR_FORMAT,
+ self->outsize, self->stride, outcaps);
+
+ gst_pad_set_caps (self->srcpad, outcaps);
+
+ if (GST_BUFFER_SIZE (outbuf) != self->outsize) {
+ GST_INFO_OBJECT (self, "dropping buffer (bufsize %d != outsize %d)",
+ GST_BUFFER_SIZE (outbuf), self->outsize);
+ gst_buffer_unref (outbuf);
+ goto 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;
}
}
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");
+ gst_buffer_unref (buf);
return GST_FLOW_ERROR;
}
+ GST_BUFFER_OFFSET_END (outbuf) = GST_BUFFER_OFFSET_END (buf);
- self->in_size = 0;
+have_out_buf:
buf = GST_DUCATIVIDDEC_GET_CLASS (self)->push_input (self, buf);
- 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;
+#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
+ * 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;
+ }
+#endif
+
+ if (self->in_size == 0 && outbuf) {
+ GST_DEBUG_OBJECT (self, "no input, skipping process");
+
+ gst_buffer_unref (outbuf);
+ return GST_FLOW_OK;
}
- err = codec_process (self, TRUE, FALSE);
+ 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, &ret);
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));
+ 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_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;
}
+ if (self->needs_flushing)
+ gst_ducati_viddec_codec_flush (self, FALSE);
+
return GST_FLOW_OK;
}
{
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;
+ gst_event_unref (event);
+ 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:
+ 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, ×tamp);
+
+ 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->srcpad, event);
+ ret = gst_pad_push_event (self->sinkpad, event);
break;
}
}
static GstStateChangeReturn
-gst_ducati_viddec_change_state (GstElement * element,
- GstStateChange transition)
+gst_ducati_viddec_change_state (GstElement * element, GstStateChange transition)
{
GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
GstDucatiVidDec *self = GST_DUCATIVIDDEC (element);
+ gboolean supported;
- 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)));
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;
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);
/* GObject vmethod implementations */
+#define VERSION_LENGTH 256
+
+static void
+gst_ducati_viddec_get_property (GObject * obj,
+ guint prop_id, GValue * value, GParamSpec * pspec)
+{
+ GstDucatiVidDec *self = GST_DUCATIVIDDEC (obj);
+
+
+ switch (prop_id) {
+ case PROP_VERSION:{
+ int err;
+ char *version = NULL;
+
+ if (!self->engine)
+ engine_open (self);
+
+ if (!self->codec)
+ codec_create (self);
+
+ if (self->codec) {
+ 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,
+ self->dynParams, self->status);
+ if (err) {
+ GST_ERROR_OBJECT (self, "failed XDM_GETVERSION");
+ }
+
+ self->status->data.buf = NULL;
+ self->status->data.bufSize = 0;
+ }
+
+ g_value_set_string (value, version);
+ if (version)
+ dce_free (version);
+
+ break;
+ }
+ case PROP_MAX_REORDER_FRAMES:
+ g_value_set_int (value, self->backlog_max_maxframes);
+ break;
+ default:{
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, prop_id, pspec);
+ break;
+ }
+ }
+}
+
+static void
+gst_ducati_viddec_set_property (GObject * obj,
+ guint prop_id, const GValue * value, GParamSpec * pspec)
+{
+ GstDucatiVidDec *self = GST_DUCATIVIDDEC (obj);
+
+ switch (prop_id) {
+ case PROP_MAX_REORDER_FRAMES:
+ self->backlog_max_maxframes = g_value_get_int (value);
+ break;
+ default:{
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, prop_id, pspec);
+ break;
+ }
+ }
+}
+
static void
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;
+ gst_buffer_unref (self->codec_data);
+ self->codec_data = NULL;
}
G_OBJECT_CLASS (parent_class)->finalize (obj);
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
GstElementClass *gstelement_class = GST_ELEMENT_CLASS (klass);
- gobject_class->finalize = gst_ducati_viddec_finalize;
- gstelement_class->change_state = gst_ducati_viddec_change_state;
+ gobject_class->get_property =
+ GST_DEBUG_FUNCPTR (gst_ducati_viddec_get_property);
+ gobject_class->set_property =
+ GST_DEBUG_FUNCPTR (gst_ducati_viddec_set_property);
+ gobject_class->finalize = GST_DEBUG_FUNCPTR (gst_ducati_viddec_finalize);
+ gstelement_class->change_state =
+ GST_DEBUG_FUNCPTR (gst_ducati_viddec_change_state);
- klass->parse_caps =
- GST_DEBUG_FUNCPTR (gst_ducati_viddec_parse_caps);
+ klass->parse_caps = GST_DEBUG_FUNCPTR (gst_ducati_viddec_parse_caps);
klass->allocate_params =
GST_DEBUG_FUNCPTR (gst_ducati_viddec_allocate_params);
- klass->push_input =
- GST_DEBUG_FUNCPTR (gst_ducati_viddec_push_input);
+ klass->push_input = GST_DEBUG_FUNCPTR (gst_ducati_viddec_push_input);
+ klass->handle_error = GST_DEBUG_FUNCPTR (gst_ducati_viddec_handle_error);
+ 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",
+ "The codec version string", "",
+ G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
+
+ g_object_class_install_property (gobject_class, PROP_MAX_REORDER_FRAMES,
+ g_param_spec_int ("max-reorder-frames",
+ "Maximum number of frames needed for reordering",
+ "The maximum number of frames needed for reordering output frames. "
+ "Only meaningful for codecs with B frames. 0 means no reordering. "
+ "This value will be used if the correct value cannot be inferred "
+ "from the stream. Too low a value may cause misordering, too high "
+ "will cause extra latency.",
+ 0, MAX_BACKLOG_FRAMES, MAX_BACKLOG_FRAMES,
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
}
static void
@@ -709,21 +1464,65 @@ gst_ducati_viddec_init (GstDucatiVidDec * self, GstDucatiVidDecClass * klass)
{
GstElementClass *gstelement_class = GST_ELEMENT_CLASS (klass);
- self->sinkpad = gst_pad_new_from_template (
- gst_element_class_get_pad_template (gstelement_class, "sink"), "sink");
+ self->sinkpad =
+ gst_pad_new_from_template (gst_element_class_get_pad_template
+ (gstelement_class, "sink"), "sink");
gst_pad_set_setcaps_function (self->sinkpad,
- GST_DEBUG_FUNCPTR (gst_ducati_viddec_set_caps));
+ GST_DEBUG_FUNCPTR (gst_ducati_viddec_sink_setcaps));
gst_pad_set_chain_function (self->sinkpad,
GST_DEBUG_FUNCPTR (gst_ducati_viddec_chain));
gst_pad_set_event_function (self->sinkpad,
GST_DEBUG_FUNCPTR (gst_ducati_viddec_event));
self->srcpad = gst_pad_new_from_static_template (&src_factory, "src");
- gst_pad_set_setcaps_function (self->srcpad,
- GST_DEBUG_FUNCPTR (gst_ducati_viddec_set_caps));
+ 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);
+
+ 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)
+ */
+ 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;
+
+#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;
+
+ 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;
+ self->device = NULL;
+ self->input_bo = NULL;
+
+ self->backlog_maxframes = 0;
+ self->backlog_nframes = 0;
+ self->backlog_max_maxframes = MAX_BACKLOG_FRAMES;
+
+ self->passed_in_bufs = g_hash_table_new_full (g_direct_hash, g_direct_equal,
+ NULL, (GDestroyNotify) gst_buffer_unref);
}