/* * 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};") ); 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 *= (vdec->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 -1; level = gst_structure_get_string (structure, "level"); if (!level) return -1; max_dpb = gst_ducati_h264dec_find_max_dpb_from_level (self, level); if (max_dpb < 0) return -1; max_dpb_size = lrint (ceil (1024 * max_dpb / (wmb * hmb * 256 * chroma_factor))); if (max_dpb_size > MAX_BACKLOG_FRAMES) max_dpb_size = MAX_BACKLOG_FRAMES; return max_dpb_size; } static gboolean gst_ducati_h264dec_set_sink_caps (GstDucatiVidDec * self, GstCaps * caps) { 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 self->backlog_maxframes = -1; structure = gst_caps_get_structure (caps, 0); if (structure) { gint num_ref_frames = -1, num_reorder_frames = -1; const char *profile; /* baseline profile does not use B frames (and I'll say constrained-baseline is unlikely either from the name, it's not present in my H264 spec... */ profile = gst_structure_get_string (structure, "profile"); if (profile && (!strcmp (profile, "baseline") || !strcmp (profile, "constrained-baseline"))) { GST_DEBUG_OBJECT (self, "No need for reordering for %s profile", profile); self->backlog_maxframes = 0; goto no_b_frames; } 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 (gst_structure_get_int (structure, "num-reorder-frames", &num_reorder_frames) && num_reorder_frames >= 0) { if (num_reorder_frames > MAX_BACKLOG_FRAMES) { self->backlog_maxframes = MAX_BACKLOG_FRAMES; GST_WARNING_OBJECT (self, "Stream needs %d frames for reordering, we can only accomodate %d", num_reorder_frames, MAX_BACKLOG_FRAMES); } else { self->backlog_maxframes = num_reorder_frames; GST_INFO_OBJECT (self, "Num frames for reordering: %d", self->backlog_maxframes); } } } /* If not present, use the spec forumula for a bound */ if (self->backlog_maxframes < 0) { self->backlog_maxframes = gst_ducati_h264dec_get_max_dpb_size (self, caps); if (self->backlog_maxframes < 0) self->backlog_maxframes = self->backlog_default_maxframes; GST_WARNING_OBJECT (self, "num-reorder-frames not found on caps, defaulting to %d", self->backlog_maxframes); } no_b_frames: return TRUE; } /* 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->set_sink_caps = GST_DEBUG_FUNCPTR (gst_ducati_h264dec_set_sink_caps); } static void gst_ducati_h264dec_init (GstDucatiH264Dec * self, GstDucatiH264DecClass * gclass) { }