diff --git a/src/gstducatividdec.c b/src/gstducatividdec.c
index 7c718626d6a789dc6f73a239827c0caa17ae8f49..5203b9a00b08d14ea60ead101343efdb532af184 100644 (file)
--- a/src/gstducatividdec.c
+++ b/src/gstducatividdec.c
{
PROP_0,
PROP_VERSION,
+ PROP_MAX_REORDER_FRAMES,
};
/* helper functions */
}
}
+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
self->inBufs->numBufs = 0;
self->inArgs->numBytes = 0;
self->inArgs->inputID = 0;
+ self->outBufs->numBufs = 0;
do {
err = codec_process (self, eos, TRUE, NULL);
self->dynParams->newFrameFlag = XDAS_TRUE;
- /* Reset the push buffer */
+ /* 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..
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));
static GstFlowReturn
gst_ducati_viddec_push_output (GstDucatiVidDec * self, GstBuffer * buf)
{
- return gst_pad_push (self->srcpad, 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_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);
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);
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;
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);
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
@@ -1463,6 +1568,10 @@ gst_ducati_viddec_init (GstDucatiVidDec * self, GstDucatiVidDecClass * klass)
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);
}