diff --git a/src/gstducatividdec.c b/src/gstducatividdec.c
index 205dca17fa5db24ae89bdd507e2e81c76a2ba770..5203b9a00b08d14ea60ead101343efdb532af184 100644 (file)
--- a/src/gstducatividdec.c
+++ b/src/gstducatividdec.c
+#define USE_DTS_PTS_CODE
/*
* GStreamer
* Copyright (c) 2010, Texas Instruments Incorporated
{
PROP_0,
PROP_VERSION,
+ PROP_MAX_REORDER_FRAMES,
};
/* helper functions */
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->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_DEBUG_OBJECT (self, "free buffer: %d %p", id, buf);
- gst_buffer_unref (buf);
+ g_hash_table_remove (self->passed_in_bufs, buf);
+ }
+}
+
+static GstFlowReturn
+gst_ducati_viddec_push_earliest (GstDucatiVidDec * self)
+{
+ guint64 earliest_order = G_MAXUINT64;
+ guint earliest_index = 0, i;
+ GstBuffer *buf;
+
+ if (self->backlog_nframes == 0)
+ return GST_FLOW_OK;
+
+ /* work out which frame has the earliest poc */
+ for (i = 0; i < self->backlog_nframes; i++) {
+ guint64 order = GST_BUFFER_OFFSET_END (self->backlog_frames[i]);
+ if (earliest_order == G_MAXUINT64 || order < earliest_order) {
+ earliest_order = order;
+ earliest_index = i;
+ }
+ }
+
+ /* send it, giving away the ref */
+ buf = self->backlog_frames[earliest_index];
+ self->backlog_frames[earliest_index] =
+ self->backlog_frames[--self->backlog_nframes];
+ GST_DEBUG_OBJECT (self, "Actually pushing backlog buffer %" GST_PTR_FORMAT,
+ buf);
+ return gst_pad_push (self->srcpad, buf);
+}
+
+static void
+gst_ducati_viddec_on_flush (GstDucatiVidDec * self, gboolean eos)
+{
+ /* push everything on the backlog, ignoring errors */
+ while (self->backlog_nframes > 0) {
+ gst_ducati_viddec_push_earliest (self);
}
}
/* we now let the codec decide */
self->dynParams->newFrameFlag = XDAS_FALSE;
- if (err == XDM_EFAIL || self->outArgs->outBufsInUseFlag)
+ 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 (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);
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);
self->send_crop_event = FALSE;
}
- if (G_UNLIKELY (self->first_out_buffer) && send && !self->outArgs->outBufsInUseFlag) {
+ if (G_UNLIKELY (self->first_out_buffer) && send) {
GstDRMBufferPool *pool;
self->first_out_buffer = FALSE;
gst_drm_buffer_pool_destroy (pool);
}
- if (send && !self->outArgs->outBufsInUseFlag) {
+ 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 (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, "
self->ts_is_pts = TRUE;
}
}
+#endif
self->last_pts = ts;
if (self->crop)
gst_buffer_set_video_crop (outbuf, self->crop);
- ret = gst_pad_push (self->srcpad, outbuf);
+ ret = klass->push_output (self, outbuf);
if (flow_ret)
*flow_ret = ret;
if (ret != GST_FLOW_OK) {
send = FALSE;
}
} else {
- GST_DEBUG_OBJECT (self, "free buffer: %d %p", i, outbuf);
+ GST_DEBUG_OBJECT (self, "Buffer not pushed, dropping 'chain' ref: %d %p",
+ i, outbuf);
+
gst_buffer_unref (outbuf);
}
}
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->inBufs->descs[0].bufSize.bytes = 0;
+ self->inBufs->numBufs = 0;
self->inArgs->numBytes = 0;
self->inArgs->inputID = 0;
+ self->outBufs->numBufs = 0;
do {
err = codec_process (self, eos, TRUE, NULL);
} while (err != XDM_EFAIL);
+ /* We flushed the decoder, we can now remove the buffer that have never been
+ * unrefed in it */
+ g_hash_table_remove_all (self->passed_in_bufs);
+
/* reset outArgs in case we're flushing in codec_process trying to do error
* recovery */
memset (&self->outArgs->outputID, 0, sizeof (self->outArgs->outputID));
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..
*/
if (G_UNLIKELY (!self->status)) {
return FALSE;
}
+ memset (self->status, 0, status_sz);
self->status->size = status_sz;
/* allocate inBufs/outBufs: */
static GstBuffer *
gst_ducati_viddec_push_input (GstDucatiVidDec * self, GstBuffer * buf)
{
+ /* If we're about to push a keyframe, then we can flush all the frames
+ we currently have queued. For formats such as H264, this is actually
+ necessary as picture order count is local to each IDR + set of non
+ IDR frames, so will restart at 0 when next IDR frames comes in. For
+ other formats, it is not necessary, but avoids buffering frames that
+ do not need to be. */
+ if (!GST_BUFFER_FLAG_IS_SET (buf, GST_BUFFER_FLAG_DELTA_UNIT)) {
+ GST_DEBUG_OBJECT (self, "Got keyframe, pushing %u frames",
+ self->backlog_nframes);
+ while (self->backlog_nframes > 0) {
+ gst_ducati_viddec_push_earliest (self);
+ }
+ }
+
if (G_UNLIKELY (self->first_in_buffer) && self->codec_data) {
push_input (self, GST_BUFFER_DATA (self->codec_data),
GST_BUFFER_SIZE (self->codec_data));
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)
/* GstElement vmethod implementations */
static gboolean
-gst_ducati_viddec_sink_setcaps (GstPad * pad, GstCaps * caps)
+gst_ducati_viddec_set_sink_caps (GstDucatiVidDec * self, GstCaps * caps)
{
gboolean ret = TRUE;
- GstDucatiVidDec *self = GST_DUCATIVIDDEC (gst_pad_get_parent (pad));
GstDucatiVidDecClass *klass = GST_DUCATIVIDDEC_GET_CLASS (self);
GstStructure *s;
GstCaps *outcaps = NULL;
gint par_width, par_height;
gboolean par_present;
- GST_INFO_OBJECT (self, "setcaps (sink): %" GST_PTR_FORMAT, caps);
-
s = gst_caps_get_structure (caps, 0);
if (!klass->parse_caps (self, s)) {
GST_WARNING_OBJECT (self, "missing required fields");
GST_INFO_OBJECT (self, "set caps done %d, %" GST_PTR_FORMAT, ret, outcaps);
+ /* default to no reordering */
+ self->backlog_maxframes = 0;
+
out:
if (outcaps)
gst_caps_unref (outcaps);
+
+ return ret;
+}
+
+static gboolean
+gst_ducati_viddec_sink_setcaps (GstPad * pad, GstCaps * caps)
+{
+ gboolean ret = TRUE;
+ GstDucatiVidDec *self = GST_DUCATIVIDDEC (gst_pad_get_parent (pad));
+ GstDucatiVidDecClass *klass = GST_DUCATIVIDDEC_GET_CLASS (self);
+
+ GST_INFO_OBJECT (self, "setcaps (sink): %" GST_PTR_FORMAT, caps);
+
+ ret = klass->set_sink_caps (self, caps);
+
gst_object_unref (self);
return ret;
GST_TIME_ARGS (qostime), GST_TIME_ARGS (self->qos_earliest_time), diff,
self->qos_proportion);
- if (klass->drop_frame (self, buf, diff)) {
+ if (klass->can_drop_frame (self, buf, diff)) {
GST_INFO_OBJECT (self, "dropping frame");
return FALSE;
}
}
static gboolean
-gst_ducati_viddec_drop_frame (GstDucatiVidDec * self, GstBuffer * buf,
+gst_ducati_viddec_can_drop_frame (GstDucatiVidDec * self, GstBuffer * buf,
gint64 diff)
{
gboolean is_keyframe = !GST_BUFFER_FLAG_IS_SET (buf,
gst_buffer_unref (buf);
return GST_FLOW_ERROR;
}
+ GST_BUFFER_OFFSET_END (outbuf) = GST_BUFFER_OFFSET_END (buf);
have_out_buf:
buf = GST_DUCATIVIDDEC_GET_CLASS (self)->push_input (self, buf);
+#ifdef USE_DTS_PTS_CODE
if (ts != GST_CLOCK_TIME_NONE) {
self->dts_queue[self->dts_widx++ % NDTS] = ts;
/* if next buffer has earlier ts than previous, then the ts
}
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;
}
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);
+ 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;
}
- self->in_size -= self->outArgs->bytesConsumed;
}
+ } else {
+ self->in_size = 0;
}
if (self->outArgs->outBufsInUseFlag) {
break;
}
+ case PROP_MAX_REORDER_FRAMES:
+ g_value_set_int (value, self->backlog_default_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_default_maxframes = g_value_get_int (value);
+ break;
default:{
G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, prop_id, pspec);
break;
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;
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);
GST_DEBUG_FUNCPTR (gst_ducati_viddec_allocate_params);
klass->push_input = GST_DEBUG_FUNCPTR (gst_ducati_viddec_push_input);
klass->handle_error = GST_DEBUG_FUNCPTR (gst_ducati_viddec_handle_error);
- klass->drop_frame = GST_DEBUG_FUNCPTR (gst_ducati_viddec_drop_frame);
+ klass->can_drop_frame = GST_DEBUG_FUNCPTR (gst_ducati_viddec_can_drop_frame);
klass->query = GST_DEBUG_FUNCPTR (gst_ducati_viddec_query);
+ klass->push_output = GST_DEBUG_FUNCPTR (gst_ducati_viddec_push_output);
+ klass->on_flush = GST_DEBUG_FUNCPTR (gst_ducati_viddec_on_flush);
+ klass->set_sink_caps = GST_DEBUG_FUNCPTR (gst_ducati_viddec_set_sink_caps);
g_object_class_install_property (gobject_class, PROP_VERSION,
g_param_spec_string ("version", "Version",
"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
@@ -1389,10 +1549,12 @@ gst_ducati_viddec_init (GstDucatiVidDec * self, GstDucatiVidDecClass * klass)
self->interlaced = FALSE;
self->send_crop_event = TRUE;
+#ifdef USE_DTS_PTS_CODE
self->dts_ridx = self->dts_widx = 0;
self->last_dts = self->last_pts = GST_CLOCK_TIME_NONE;
self->ts_may_be_pts = TRUE;
self->ts_is_pts = FALSE;
+#endif
self->pageMemType = XDM_MEMTYPE_TILEDPAGE;
@@ -1405,4 +1567,11 @@ gst_ducati_viddec_init (GstDucatiVidDec * self, GstDucatiVidDecClass * klass)
self->need_out_buf = TRUE;
self->device = NULL;
self->input_bo = NULL;
+
+ self->backlog_maxframes = 0;
+ self->backlog_nframes = 0;
+ self->backlog_default_maxframes = MAX_BACKLOG_FRAMES;
+
+ self->passed_in_bufs = g_hash_table_new_full (g_direct_hash, g_direct_equal,
+ NULL, (GDestroyNotify) gst_buffer_unref);
}