/* * GStreamer * Copyright (c) 2010, Texas Instruments Incorporated * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation * version 2.1 of the License. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /** * SECTION:element-ducatih264dec * * FIXME:Describe ducatih264dec here. * * * Example launch line * |[ * gst-launch -v -m fakesrc ! ducatih264dec ! fakesink silent=TRUE * ]| * */ #ifdef HAVE_CONFIG_H # include #endif #include #include "gstducatih264dec.h" #define GST_BUFFER_FLAG_B_FRAME (GST_BUFFER_FLAG_LAST << 0) #define PADX 32 #define PADY 24 GST_BOILERPLATE (GstDucatiH264Dec, gst_ducati_h264dec, GstDucatiVidDec, GST_TYPE_DUCATIVIDDEC); /* *INDENT-OFF* */ static GstStaticPadTemplate sink_factory = GST_STATIC_PAD_TEMPLATE ("sink", GST_PAD_SINK, GST_PAD_ALWAYS, GST_STATIC_CAPS ("video/x-h264, " "stream-format = byte-stream, " /* only byte-stream */ "alignment = au, " /* only entire frames */ "width = (int)[ 16, 2048 ], " "height = (int)[ 16, 2048 ], " "framerate = (fraction)[ 0, max ]," "profile = (string){constrained-baseline, baseline, main, extended};" "video/x-h264, " "stream-format = byte-stream, " /* only byte-stream */ "alignment = au, " /* only entire frames */ "width = (int)[ 16, 2048 ], " "height = (int)[ 16, 2048 ], " "framerate = (fraction)[ 0, max ]," "profile = (string) {high, high-10-intra, high-10, high-4:2:2-intra, " "high-4:2:2, high-4:4:4-intra, high-4:4:4, cavlc-4:4:4-intra}, " "level = (string) {1, 1b, 1.1, 1.2, 1.3, 2, 2.1, 2.2, 3, 3.1, 3.2, 4, 4.1, 4.2, 5.1};") ); 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 gst_ducati_h264dec_update_buffer_size (GstDucatiVidDec * self) { gint w = self->width; gint h = self->height; /* calculate output buffer parameters: */ self->padded_width = ALIGN2 (w + (2 * PADX), 7); self->padded_height = h + 4 * PADY; self->min_buffers = MIN (16, 32768 / ((w / 16) * (h / 16))) + 3; } static gboolean gst_ducati_h264dec_allocate_params (GstDucatiVidDec * self, gint params_sz, gint dynparams_sz, gint status_sz, gint inargs_sz, gint outargs_sz) { gboolean ret = parent_class->allocate_params (self, sizeof (IH264VDEC_Params), sizeof (IH264VDEC_DynamicParams), sizeof (IH264VDEC_Status), sizeof (IH264VDEC_InArgs), sizeof (IH264VDEC_OutArgs)); if (ret) { IH264VDEC_Params *params = (IH264VDEC_Params *) self->params; self->params->displayDelay = IVIDDEC3_DECODE_ORDER; params->maxNumRefFrames = IH264VDEC_NUM_REFFRAMES_AUTO; params->pConstantMemory = 0; params->presetLevelIdc = IH264VDEC_LEVEL41; params->errConcealmentMode = IH264VDEC_APPLY_CONCEALMENT; params->temporalDirModePred = TRUE; } return ret; } static gint gst_ducati_h264dec_handle_error (GstDucatiVidDec * self, gint ret, gint extended_error, gint status_extended_error) { if (extended_error & 0x00000001) { /* No valid slice. This seems to be bad enough that it's better to flush and * skip to the next keyframe. */ if (extended_error == 0x00000201) { /* the codec doesn't unlock the input buffer in this case... */ gst_buffer_unref ((GstBuffer *) self->inArgs->inputID); self->inArgs->inputID = 0; } self->needs_flushing = TRUE; } ret = parent_class->handle_error (self, ret, extended_error, status_extended_error); return ret; } static gboolean gst_ducati_h264dec_query (GstDucatiVidDec * vdec, GstPad * pad, GstQuery * query, gboolean * forward) { GstDucatiH264Dec *self = GST_DUCATIH264DEC (vdec); gboolean res = TRUE; switch (GST_QUERY_TYPE (query)) { case GST_QUERY_LATENCY: { gboolean live; GstClockTime min, max, latency; if (vdec->fps_d == 0) { GST_INFO_OBJECT (self, "not ready to report latency"); res = FALSE; break; } gst_query_parse_latency (query, &live, &min, &max); if (vdec->fps_n != 0) latency = gst_util_uint64_scale (GST_SECOND, vdec->fps_d, vdec->fps_n); 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 min += latency; if (max != GST_CLOCK_TIME_NONE) max += latency; GST_INFO_OBJECT (self, "latency %" GST_TIME_FORMAT " ours %" GST_TIME_FORMAT, GST_TIME_ARGS (min), GST_TIME_ARGS (latency)); gst_query_set_latency (query, live, min, max); break; } default: break; } if (res) res = parent_class->query (vdec, pad, query, forward); return res; } static gboolean gst_ducati_h264dec_can_drop_frame (GstDucatiVidDec * self, GstBuffer * buf, gint64 diff) { gboolean is_bframe = GST_BUFFER_FLAG_IS_SET (buf, GST_BUFFER_FLAG_B_FRAME); if (diff >= 0 && is_bframe) return TRUE; 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) { ((IH264VDEC_Params *) self->params)->maxNumRefFrames = num_ref_frames; 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 gst_ducati_h264dec_base_init (gpointer gclass) { GstElementClass *element_class = GST_ELEMENT_CLASS (gclass); gst_element_class_set_details_simple (element_class, "DucatiH264Dec", "Codec/Decoder/Video", "Decodes video in H.264/bytestream format with ducati", "Rob Clark "); gst_element_class_add_pad_template (element_class, gst_static_pad_template_get (&sink_factory)); } static void gst_ducati_h264dec_class_init (GstDucatiH264DecClass * klass) { GstDucatiVidDecClass *bclass = GST_DUCATIVIDDEC_CLASS (klass); bclass->codec_name = "ivahd_h264dec"; bclass->update_buffer_size = GST_DEBUG_FUNCPTR (gst_ducati_h264dec_update_buffer_size); 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->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; }