From: Vincent Penquerc'h Date: Wed, 18 Jul 2012 12:19:01 +0000 (+0000) Subject: gst-ducati: reorder H.264 frames ourselves X-Git-Tag: glsdk-6_00_00_07~90 X-Git-Url: https://git.ti.com/gitweb?p=glsdk%2Fgst-plugin-ducati.git;a=commitdiff_plain;h=4842a422894b85e1c1ed356a499291278c82cded gst-ducati: reorder H.264 frames ourselves --- diff --git a/src/gstducatih264dec.c b/src/gstducatih264dec.c index 912a9d8..d1abc98 100644 --- a/src/gstducatih264dec.c +++ b/src/gstducatih264dec.c @@ -34,6 +34,7 @@ # include #endif +#include #include "gstducatih264dec.h" @@ -67,6 +68,31 @@ static GstStaticPadTemplate sink_factory = GST_STATIC_PAD_TEMPLATE ("sink", ) ); +/* *INDENT-OFF* */ +static const struct +{ + const char *level; + gint kb; +} max_dpb_by_level[] = { + { "1", 149 }, + { "1b", 149 }, /* That one's not in the spec ?? */ + { "1.1", 338 }, + { "1.2", 891 }, + { "1.3", 891 }, + { "2", 891 }, + { "2.1", 1782 }, + { "2.2", 3038 }, + { "3", 3038 }, + { "3.1", 6750 }, + { "3.2", 7680 }, + { "4", 12288 }, + { "4.1", 12288 }, + { "4.2", 12288 }, + { "5", 41400 }, + { "5.1", 69120 }, +}; +/* *INDENT-ON* */ + /* GstDucatiVidDec vmethod implementations */ static void @@ -92,7 +118,7 @@ gst_ducati_h264dec_allocate_params (GstDucatiVidDec * self, gint params_sz, if (ret) { IH264VDEC_Params *params = (IH264VDEC_Params *) self->params; - self->params->displayDelay = IVIDDEC3_DISPLAY_DELAY_AUTO; + self->params->displayDelay = IVIDDEC3_DECODE_ORDER; params->maxNumRefFrames = IH264VDEC_NUM_REFFRAMES_AUTO; params->pConstantMemory = 0; params->presetLevelIdc = IH264VDEC_LEVEL41; @@ -153,6 +179,9 @@ gst_ducati_h264dec_query (GstDucatiVidDec * vdec, GstPad * pad, else latency = 0; + /* Take into account the backlog frames for reordering */ + latency *= (self->backlog_maxframes + 1); + if (min == GST_CLOCK_TIME_NONE) min = latency; else @@ -189,6 +218,187 @@ gst_ducati_h264dec_can_drop_frame (GstDucatiVidDec * self, GstBuffer * buf, return FALSE; } +static gint +gst_ducati_h264dec_find_max_dpb_from_level (GstDucatiVidDec * self, + const char *level) +{ + guint n; + + for (n = 0; n < G_N_ELEMENTS (max_dpb_by_level); ++n) + if (!strcmp (level, max_dpb_by_level[n].level)) + return max_dpb_by_level[n].kb; + + GST_WARNING_OBJECT (self, "Max DBP not found for level %s", level); + return -1; +} + +static gint +gst_ducati_h264dec_get_max_dpb_size (GstDucatiVidDec * self, GstCaps * caps) +{ + gint wmb = (self->width + 15) / 16; + gint hmb = (self->height + 15) / 16; + gint max_dpb, max_dpb_size; + GstStructure *structure; + const char *level; + float chroma_factor = 1.5; /* We only support NV12, which is 4:2:0 */ + + /* Min( 1024 * MaxDPB / ( PicWidthInMbs * FrameHeightInMbs * 256 * ChromaFormatFactor ), 16 ) */ + + structure = gst_caps_get_structure (caps, 0); + if (!structure) + return 16; + + level = gst_structure_get_string (structure, "level"); + if (!level) + return 16; + + max_dpb = gst_ducati_h264dec_find_max_dpb_from_level (self, level); + if (max_dpb < 0) + return 16; + + max_dpb_size = + lrint (ceil (1024 * max_dpb / (wmb * hmb * 256 * chroma_factor))); + if (max_dpb_size > 16) + max_dpb_size = 16; + + return max_dpb_size; +} + +static gboolean +gst_ducati_h264dec_set_sink_caps (GstDucatiVidDec * self, GstCaps * caps) +{ + GstDucatiH264Dec *h264dec = GST_DUCATIH264DEC (self); + GstStructure *structure; + + GST_DEBUG_OBJECT (self, "set_sink_caps: %" GST_PTR_FORMAT, caps); + + if (!GST_CALL_PARENT_WITH_DEFAULT (GST_DUCATIVIDDEC_CLASS, set_sink_caps, + (self, caps), TRUE)) + return FALSE; + + /* HW decoder fails in GETSTATUS */ +#if 0 + /* When we have the first decoded buffer, we ask the decoder for the + max number of frames needed to reorder */ + int err = VIDDEC3_control (self->codec, XDM_GETSTATUS, + self->dynParams, self->status); + if (!err) { + IH264VDEC_Status *s = (IH264VDEC_Status *) self->status; + if (s->spsMaxRefFrames > MAX_BACKLOG_FRAMES) { + h264dec->backlog_maxframes = MAX_BACKLOG_FRAMES; + GST_WARNING_OBJECT (self, + "Stream needs %d frames for reordering, we can only accomodate %d", + s->spsMaxRefFrames, MAX_BACKLOG_FRAMES); + } else { + h264dec->backlog_maxframes = s->spsMaxRefFrames; + GST_INFO_OBJECT (self, "Num frames for reordering: %d", + h264dec->backlog_maxframes); + } + } else { + h264dec->backlog_maxframes = MAX_BACKLOG_FRAMES; + GST_WARNING_OBJECT (self, + "Failed to request num frames for reordering, defaulting to %d", + h264dec->backlog_maxframes); + } +#endif + + h264dec->backlog_maxframes = -1; + + structure = gst_caps_get_structure (caps, 0); + if (structure) { + gint num_ref_frames = -1; + if (gst_structure_get_int (structure, "num-ref-frames", &num_ref_frames) + && num_ref_frames >= 0) { + if (num_ref_frames > MAX_BACKLOG_FRAMES) { + h264dec->backlog_maxframes = MAX_BACKLOG_FRAMES; + GST_WARNING_OBJECT (self, + "Stream needs %d frames for reordering, we can only accomodate %d", + num_ref_frames, MAX_BACKLOG_FRAMES); + } else { + h264dec->backlog_maxframes = num_ref_frames; + GST_INFO_OBJECT (self, "Num frames for reordering: %d", + h264dec->backlog_maxframes); + } + } + } + + /* If not present, use the spec forumula for a bound */ + if (h264dec->backlog_maxframes < 0) { + h264dec->backlog_maxframes = + gst_ducati_h264dec_get_max_dpb_size (self, caps); + GST_WARNING_OBJECT (self, + "num-ref-frames not found on caps, defaulting to %d", + h264dec->backlog_maxframes); + } + + return TRUE; +} + +/* The following few functions reorder buffers by timestamp, on the + assumption that buffers are properly timestamped by presentation + time, but pushed in any (but presumably decode) order. + They also assume all incoming buffers have a valid timestamp. + */ + +static GstFlowReturn +gst_ducati_h264dec_push_earliest (GstDucatiH264Dec * self) +{ + GstClockTime earliest_ts = GST_CLOCK_TIME_NONE; + guint earliest_index = 0, i; + GstBuffer *buf; + + if (self->backlog_nframes == 0) + return GST_FLOW_OK; + + /* work out which frame has the earliest ts */ + for (i = 0; i < self->backlog_nframes; i++) { + GstClockTime ts = GST_BUFFER_TIMESTAMP (self->backlog_frames[i]); + if (earliest_ts == GST_CLOCK_TIME_NONE || ts < earliest_ts) { + earliest_ts = ts; + 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 (GST_DUCATIVIDDEC (self)->srcpad, buf); +} + +static GstFlowReturn +gst_ducati_h264dec_push_output (GstDucatiVidDec * self, GstBuffer * buf) +{ + GstDucatiH264Dec *h264dec = GST_DUCATIH264DEC (self); + GstFlowReturn ret = GST_FLOW_OK; + + /* add the frame to the list, the array will own the ref */ + GST_DEBUG_OBJECT (self, "Adding buffer %" GST_PTR_FORMAT " to backlog", buf); + h264dec->backlog_frames[h264dec->backlog_nframes++] = buf; + + /* push till we have no more than the max needed, or error */ + while (h264dec->backlog_nframes > h264dec->backlog_maxframes) { + ret = gst_ducati_h264dec_push_earliest (h264dec); + if (ret != GST_FLOW_OK) + break; + } + + return ret; +} + +static void +gst_ducati_h264dec_on_flush (GstDucatiVidDec * self, gboolean eos) +{ + GstDucatiH264Dec *h264dec = GST_DUCATIH264DEC (self); + + /* push everything on the backlog, ignoring errors */ + while (h264dec->backlog_nframes > 0) { + gst_ducati_h264dec_push_earliest (h264dec); + } +} + /* GObject vmethod implementations */ static void @@ -216,12 +426,18 @@ gst_ducati_h264dec_class_init (GstDucatiH264DecClass * klass) bclass->allocate_params = GST_DEBUG_FUNCPTR (gst_ducati_h264dec_allocate_params); bclass->handle_error = GST_DEBUG_FUNCPTR (gst_ducati_h264dec_handle_error); - bclass->can_drop_frame = GST_DEBUG_FUNCPTR (gst_ducati_h264dec_can_drop_frame); + bclass->can_drop_frame = + GST_DEBUG_FUNCPTR (gst_ducati_h264dec_can_drop_frame); bclass->query = GST_DEBUG_FUNCPTR (gst_ducati_h264dec_query); + bclass->push_output = GST_DEBUG_FUNCPTR (gst_ducati_h264dec_push_output); + bclass->on_flush = GST_DEBUG_FUNCPTR (gst_ducati_h264dec_on_flush); + bclass->set_sink_caps = GST_DEBUG_FUNCPTR (gst_ducati_h264dec_set_sink_caps); } static void gst_ducati_h264dec_init (GstDucatiH264Dec * self, GstDucatiH264DecClass * gclass) { + self->backlog_maxframes = 0; + self->backlog_nframes = 0; } diff --git a/src/gstducatih264dec.h b/src/gstducatih264dec.h index a32c65d..42cc12b 100644 --- a/src/gstducatih264dec.h +++ b/src/gstducatih264dec.h @@ -36,9 +36,17 @@ G_BEGIN_DECLS typedef struct _GstDucatiH264Dec GstDucatiH264Dec; typedef struct _GstDucatiH264DecClass GstDucatiH264DecClass; +/* The H.264 spec has a hard limit of 16 */ +#define MAX_BACKLOG_FRAMES 16 + struct _GstDucatiH264Dec { GstDucatiVidDec parent; + + /* Frames waiting to be reordered */ + GstBuffer *backlog_frames[MAX_BACKLOG_FRAMES + 1]; + guint backlog_maxframes; + guint backlog_nframes; }; struct _GstDucatiH264DecClass diff --git a/src/gstducatividdec.c b/src/gstducatividdec.c index 46b28bf..5603da6 100644 --- a/src/gstducatividdec.c +++ b/src/gstducatividdec.c @@ -1,3 +1,4 @@ +#define USE_DTS_PTS_CODE /* * GStreamer * Copyright (c) 2010, Texas Instruments Incorporated @@ -289,6 +290,11 @@ codec_unlock_outbuf (GstDucatiVidDec * self, XDAS_Int32 id) } } +static void +gst_ducati_viddec_on_flush (GstDucatiVidDec * self, gboolean eos) +{ +} + static gint codec_process (GstDucatiVidDec * self, gboolean send, gboolean flush, GstFlowReturn * flow_ret) @@ -430,6 +436,7 @@ codec_process (GstDucatiVidDec * self, gboolean send, gboolean flush, 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, " @@ -437,6 +444,7 @@ codec_process (GstDucatiVidDec * self, gboolean send, gboolean flush, self->ts_is_pts = TRUE; } } +#endif self->last_pts = ts; @@ -466,7 +474,7 @@ codec_process (GstDucatiVidDec * self, gboolean send, gboolean flush, 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) { @@ -496,15 +504,19 @@ gst_ducati_viddec_codec_flush (GstDucatiVidDec * self, gboolean eos) 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; @@ -673,6 +685,12 @@ gst_ducati_viddec_push_input (GstDucatiVidDec * self, GstBuffer * buf) return NULL; } +static GstFlowReturn +gst_ducati_viddec_push_output (GstDucatiVidDec * self, GstBuffer * buf) +{ + return gst_pad_push (self->srcpad, buf); +} + static gint gst_ducati_viddec_handle_error (GstDucatiVidDec * self, gint ret, gint extended_error, gint status_extended_error) @@ -688,10 +706,9 @@ gst_ducati_viddec_handle_error (GstDucatiVidDec * self, gint ret, /* 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; @@ -699,8 +716,6 @@ gst_ducati_viddec_sink_setcaps (GstPad * pad, GstCaps * caps) 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"); @@ -784,6 +799,21 @@ gst_ducati_viddec_sink_setcaps (GstPad * pad, GstCaps * caps) 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; @@ -1024,6 +1054,7 @@ allocate_buffer: 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 @@ -1035,6 +1066,7 @@ have_out_buf: } self->last_dts = ts; } +#endif if (self->in_size == 0 && outbuf) { GST_DEBUG_OBJECT (self, "no input, skipping process"); @@ -1342,6 +1374,9 @@ gst_ducati_viddec_class_init (GstDucatiVidDecClass * klass) 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", @@ -1390,10 +1425,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; diff --git a/src/gstducatividdec.h b/src/gstducatividdec.h index 1aac4aa..ea69e59 100644 --- a/src/gstducatividdec.h +++ b/src/gstducatividdec.h @@ -179,6 +179,21 @@ struct _GstDucatiVidDecClass gboolean (*query) (GstDucatiVidDec * self, GstPad * pad, GstQuery * query, gboolean * forward); + + /** + * Called to push a decoder buffer. Consumes reference to 'buf'. + */ + GstFlowReturn (*push_output) (GstDucatiVidDec * self, GstBuffer * buf); + + /** + * Called before a flush happens. + */ + void (*on_flush) (GstDucatiVidDec * self, gboolean eos); + + /** + * Called to set new caps on the sink pad. + */ + gboolean (*set_sink_caps) (GstDucatiVidDec * self, GstCaps *caps); }; GType gst_ducati_viddec_get_type (void);