From: Hyunseok Lee Date: Tue, 21 Aug 2012 08:58:13 +0000 (+0900) Subject: Update tizen 2.0 beta source X-Git-Tag: 2.0_alpha^0 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=refs%2Fheads%2Fmaster;p=framework%2Fmultimedia%2Fgst-openmax.git Update tizen 2.0 beta source --- diff --git a/debian/control b/debian/control index cf010c6..006aad7 100755 --- a/debian/control +++ b/debian/control @@ -1,6 +1,6 @@ Source: gst-openmax Maintainer: Hyunseok Lee , JongHyuk Choi -Uploaders: Hyunseok Lee , Dowan Kim , Sunghyun Eum , Seongho Jeong(sh33.jeong@samsung.com) +Uploaders: Hyunseok Lee , Dowan Kim , Sunghyun Eum Section: libs Priority: optional Standards-Version: 3.7.3 diff --git a/omx/Makefile.am b/omx/Makefile.am index 915b562..423d8d6 100644 --- a/omx/Makefile.am +++ b/omx/Makefile.am @@ -14,7 +14,7 @@ libgstomx_la_SOURCES = gstomx.c gstomx.h \ gstomx_h264dec.c gstomx_h264dec.h \ gstomx_wmvdec.c gstomx_wmvdec.h \ gstomx_mpeg4enc.c gstomx_mpeg4enc.h \ - gstomx_h264enc.c gstomx_h264enc.h \ + gstomx_h264enc.c gstomx_h264enc.h gstomx_h264.h \ gstomx_h263enc.c gstomx_h263enc.h \ gstomx_vorbisdec.c gstomx_vorbisdec.h \ gstomx_mp3dec.c gstomx_mp3dec.h \ diff --git a/omx/gstomx.c b/omx/gstomx.c index 7172077..e04d09b 100644 --- a/omx/gstomx.c +++ b/omx/gstomx.c @@ -104,6 +104,7 @@ gst_omx_volume_get_type,}; static gchar * get_config_path (void) { +/* MODIFICATION */ #if 1 /* Fix_config_path */ return g_build_filename (OMX_CONFIG_DIRPATH, OMX_CONFIG_FILENAME, NULL); #else diff --git a/omx/gstomx_base_audiodec.c b/omx/gstomx_base_audiodec.c index 6f94b33..74f7dc1 100644 --- a/omx/gstomx_base_audiodec.c +++ b/omx/gstomx_base_audiodec.c @@ -22,6 +22,12 @@ #include "gstomx_base_audiodec.h" #include "gstomx.h" +enum +{ + ARG_0, + ARG_USE_STATETUNING, /* STATE_TUNING */ +}; + GSTOMX_BOILERPLATE (GstOmxBaseAudioDec, gst_omx_base_audiodec, GstOmxBaseFilter, GST_OMX_BASE_FILTER_TYPE); @@ -30,9 +36,64 @@ type_base_init (gpointer g_class) { } +/* MODIFICATION: add state tuning property */ +static void +set_property (GObject * obj, + guint prop_id, const GValue * value, GParamSpec * pspec) +{ + GstOmxBaseAudioDec *self; + + self = GST_OMX_BASE_AUDIODEC (obj); + + switch (prop_id) { + /* STATE_TUNING */ + case ARG_USE_STATETUNING: + self->omx_base.use_state_tuning = g_value_get_boolean(value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, prop_id, pspec); + break; + } +} + +static void +get_property (GObject * obj, guint prop_id, GValue * value, GParamSpec * pspec) +{ + GstOmxBaseAudioDec *self; + + self = GST_OMX_BASE_AUDIODEC (obj); + + switch (prop_id) { + /* STATE_TUNING */ + case ARG_USE_STATETUNING: + g_value_set_boolean(value, self->omx_base.use_state_tuning); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, prop_id, pspec); + break; + } +} + static void type_class_init (gpointer g_class, gpointer class_data) { + GObjectClass *gobject_class; + GstOmxBaseFilterClass *basefilter_class; + + gobject_class = G_OBJECT_CLASS (g_class); + basefilter_class = GST_OMX_BASE_FILTER_CLASS (g_class); + + /* Properties stuff */ + { + gobject_class->set_property = set_property; + gobject_class->get_property = get_property; + + /* STATE_TUNING */ + g_object_class_install_property (gobject_class, ARG_USE_STATETUNING, + g_param_spec_boolean ("state-tuning", "start omx component in gst paused state", + "Whether or not to use state-tuning feature", + FALSE, G_PARAM_READWRITE)); + } } static void diff --git a/omx/gstomx_base_filter.c b/omx/gstomx_base_filter.c index 8ae95ee..e737613 100644 --- a/omx/gstomx_base_filter.c +++ b/omx/gstomx_base_filter.c @@ -25,7 +25,7 @@ #include /* for memcpy */ -/* STATE_TUNING */ +/* MODIFICATION: for state-tuning */ static void output_loop (gpointer data); enum @@ -40,15 +40,15 @@ GSTOMX_BOILERPLATE_FULL (GstOmxBaseFilter, gst_omx_base_filter, GstElement, GST_TYPE_ELEMENT, init_interfaces); static inline void -log_buffer (GstOmxBaseFilter * self, OMX_BUFFERHEADERTYPE * omx_buffer) +log_buffer (GstOmxBaseFilter * self, OMX_BUFFERHEADERTYPE * omx_buffer, const gchar *name) { - GST_DEBUG_OBJECT (self, "omx_buffer: " + GST_DEBUG_OBJECT (self, "%s: omx_buffer: " "size=%lu, " "len=%lu, " "flags=%lu, " "offset=%lu, " "timestamp=%lld", - omx_buffer->nAllocLen, omx_buffer->nFilledLen, omx_buffer->nFlags, + name, omx_buffer->nAllocLen, omx_buffer->nFilledLen, omx_buffer->nFlags, omx_buffer->nOffset, omx_buffer->nTimeStamp); } @@ -95,22 +95,28 @@ setup_ports (GstOmxBaseFilter * self) GST_DEBUG_OBJECT (self, "OMX_ALLOCATE_ON"); self->in_port->omx_allocate = TRUE; self->out_port->omx_allocate = TRUE; - self->share_input_buffer = FALSE; - self->share_output_buffer = FALSE; + self->in_port->shared_buffer = FALSE; + self->out_port->shared_buffer = FALSE; } else if (g_getenv ("OMX_SHARE_HACK_ON")) { GST_DEBUG_OBJECT (self, "OMX_SHARE_HACK_ON"); - self->share_input_buffer = TRUE; - self->share_output_buffer = TRUE; + self->in_port->shared_buffer = TRUE; + self->out_port->shared_buffer = TRUE; } else if (g_getenv ("OMX_SHARE_HACK_OFF")) { GST_DEBUG_OBJECT (self, "OMX_SHARE_HACK_OFF"); - self->share_input_buffer = FALSE; - self->share_output_buffer = FALSE; - /* Add extended_color_format */ + self->in_port->shared_buffer = FALSE; + self->out_port->shared_buffer = FALSE; + /* MODIFICATION: Add extended_color_format */ } else if (self->gomx->component_vendor == GOMX_VENDOR_SLSI) { - self->share_input_buffer = (is_extended_color_format(self, self->in_port)) + self->in_port->shared_buffer = (is_extended_color_format(self, self->in_port)) ? FALSE : TRUE; - self->share_output_buffer = (is_extended_color_format(self, self->out_port)) + self->out_port->shared_buffer = (is_extended_color_format(self, self->out_port)) ? FALSE : TRUE; + } else if (self->gomx->component_vendor == GOMX_VENDOR_QCT) { + GST_DEBUG_OBJECT (self, "GOMX_VENDOR_QCT"); + self->in_port->omx_allocate = TRUE; + self->out_port->omx_allocate = TRUE; + self->in_port->shared_buffer = FALSE; + self->out_port->shared_buffer = FALSE; } else { GST_DEBUG_OBJECT (self, "default sharing and allocation"); } @@ -118,7 +124,7 @@ setup_ports (GstOmxBaseFilter * self) GST_DEBUG_OBJECT (self, "omx_allocate: in: %d, out: %d", self->in_port->omx_allocate, self->out_port->omx_allocate); GST_DEBUG_OBJECT (self, "share_buffer: in: %d, out: %d", - self->share_input_buffer, self->share_output_buffer); + self->in_port->shared_buffer, self->out_port->shared_buffer); } static GstFlowReturn @@ -213,7 +219,8 @@ out_flushing: ret = GST_FLOW_ERROR; } - gst_buffer_unref (buf); + if (buf) + gst_buffer_unref (buf); goto leave; } } @@ -236,6 +243,7 @@ change_state (GstElement * element, GstStateChange transition) switch (transition) { case GST_STATE_CHANGE_NULL_TO_READY: + GST_INFO_OBJECT (self, "GST_STATE_CHANGE_NULL_TO_READY"); if (core->omx_state != OMX_StateLoaded) { ret = GST_STATE_CHANGE_FAILURE; goto leave; @@ -243,23 +251,31 @@ change_state (GstElement * element, GstStateChange transition) break; case GST_STATE_CHANGE_READY_TO_PAUSED: - /* STATE_TUNING */ - if (self->use_state_tuning) { - GST_INFO_OBJECT (self, "use state-tuning feature"); - omx_change_state(self, GstOmx_LodedToIdle, NULL, NULL); - - if (core->omx_state == OMX_StateIdle) { - self->ready = TRUE; - gst_pad_start_task (self->srcpad, output_loop, self->srcpad); - } else { - GST_ERROR_OBJECT(self, "fail to move from OMX state Loaded to Idle"); - g_omx_port_finish(self->in_port); - g_omx_port_finish(self->out_port); - g_omx_core_stop(core); - g_omx_core_unload(core); - ret = GST_STATE_CHANGE_FAILURE; - goto leave; - } + GST_INFO_OBJECT (self, "GST_STATE_CHANGE_READY_TO_PAUSED"); + /* MODIFICATION: state tuning */ + if (self->use_state_tuning) { + GST_INFO_OBJECT (self, "use state-tuning feature"); + /* to handle abnormal state change. */ + if (self->gomx != self->in_port->core) { + GST_ERROR_OBJECT(self, "self->gomx != self->in_port->core. start new in_port"); + self->in_port = g_omx_core_new_port (self->gomx, 0); + } + if (self->gomx != self->out_port->core) { + GST_ERROR_OBJECT(self, "self->gomx != self->out_port->core. start new out_port"); + self->out_port = g_omx_core_new_port (self->gomx, 1); + } + + omx_change_state(self, GstOmx_LodedToIdle, NULL, NULL); + + if (core->omx_state != OMX_StateIdle) { + GST_ERROR_OBJECT(self, "fail to move from OMX state Loaded to Idle"); + g_omx_port_finish(self->in_port); + g_omx_port_finish(self->out_port); + g_omx_core_stop(core); + g_omx_core_unload(core); + ret = GST_STATE_CHANGE_FAILURE; + goto leave; + } } break; @@ -274,6 +290,7 @@ change_state (GstElement * element, GstStateChange transition) switch (transition) { case GST_STATE_CHANGE_PAUSED_TO_READY: + GST_INFO_OBJECT (self, "GST_STATE_CHANGE_PAUSED_TO_READY"); g_mutex_lock (self->ready_lock); if (self->ready) { /* unlock */ @@ -292,6 +309,10 @@ change_state (GstElement * element, GstStateChange transition) } break; + case GST_STATE_CHANGE_READY_TO_NULL: + GST_INFO_OBJECT (self, "GST_STATE_CHANGE_READY_TO_NULL"); + break; + default: break; } @@ -309,6 +330,12 @@ finalize (GObject * obj) self = GST_OMX_BASE_FILTER (obj); + if (self->adapter) { + gst_adapter_clear(self->adapter); + g_object_unref(self->adapter); + self->adapter = NULL; + } + if (self->codec_data) { gst_buffer_unref (self->codec_data); self->codec_data = NULL; @@ -462,15 +489,14 @@ push_buffer (GstOmxBaseFilter * self, GstBuffer * buf, OMX_BUFFERHEADERTYPE * om basefilter_class = GST_OMX_BASE_FILTER_GET_CLASS (self); /* process output gst buffer before gst_pad_push */ - if (basefilter_class->process_output_buf) - { - basefilter_class->process_output_buf(self, buf, omx_buffer); + if (basefilter_class->process_output_buf) { + basefilter_class->process_output_buf(self, &buf, omx_buffer); } - /** @todo check if tainted */ - GST_LOG_OBJECT (self, "begin"); + GST_LOG_OBJECT (self, "OUT_BUFFER: timestamp = %" GST_TIME_FORMAT " size = %lu", + GST_TIME_ARGS(GST_BUFFER_TIMESTAMP (buf)), GST_BUFFER_SIZE (buf)); ret = gst_pad_push (self->srcpad, buf); - GST_LOG_OBJECT (self, "end"); + GST_LOG_OBJECT (self, "gst_pad_push end. ret = %d", ret); return ret; } @@ -515,7 +541,7 @@ output_loop (gpointer data) goto leave; } - log_buffer (self, omx_buffer); + log_buffer (self, omx_buffer, "output_loop"); if (G_LIKELY (omx_buffer->nFilledLen > 0)) { GstBuffer *buf; @@ -545,32 +571,22 @@ output_loop (gpointer data) /** @todo we need to move all the caps handling to one single * place, in the output loop probably. */ if (G_UNLIKELY (omx_buffer->nFlags & OMX_BUFFERFLAG_CODECCONFIG)) { - GstCaps *caps = NULL; - GstStructure *structure; - GValue value = { 0, {{0} - } - }; - - caps = gst_pad_get_negotiated_caps (self->srcpad); - caps = gst_caps_make_writable (caps); - structure = gst_caps_get_structure (caps, 0); - - g_value_init (&value, GST_TYPE_BUFFER); - buf = gst_buffer_new_and_alloc (omx_buffer->nFilledLen); - memcpy (GST_BUFFER_DATA (buf), - omx_buffer->pBuffer + omx_buffer->nOffset, omx_buffer->nFilledLen); - gst_value_set_buffer (&value, buf); - gst_buffer_unref (buf); - gst_structure_set_value (structure, "codec_data", &value); - g_value_unset (&value); - - gst_pad_set_caps (self->srcpad, caps); + /* modification: to handle both byte-stream and packetized codec_data */ + GstOmxBaseFilterClass *basefilter_class; + basefilter_class = GST_OMX_BASE_FILTER_GET_CLASS (self); + if (basefilter_class->process_output_caps) { + basefilter_class->process_output_caps(self, omx_buffer); + } + /* MODIFICATION: to handle output ST12 HW addr (dec) */ } else if (is_extended_color_format(self, self->out_port)) { GstCaps *caps = NULL; GstStructure *structure; gint width = 0, height = 0; + if (G_UNLIKELY (omx_buffer->nFlags & OMX_BUFFERFLAG_DECODEONLY)) + goto leave; + caps = gst_pad_get_negotiated_caps(self->srcpad); structure = gst_caps_get_structure(caps, 0); @@ -578,7 +594,7 @@ output_loop (gpointer data) gst_structure_get_int(structure, "height", &height); if (G_LIKELY((width > 0) && (height > 0))) { - buf = gst_buffer_new_and_alloc((width * height * 3) / 2); + buf = gst_buffer_new_and_alloc(width * height * 3 / 2); } else { GST_ERROR_OBJECT (self, "invalid buffer size"); ret = GST_FLOW_UNEXPECTED; @@ -593,6 +609,7 @@ output_loop (gpointer data) OMX_TICKS_PER_SECOND); } gst_buffer_set_caps(buf, GST_PAD_CAPS(self->srcpad)); + gst_caps_unref (caps); ret = push_buffer (self, buf, omx_buffer); } else if (buf && !(omx_buffer->nFlags & OMX_BUFFERFLAG_EOS)) { @@ -627,7 +644,7 @@ output_loop (gpointer data) OMX_TICKS_PER_SECOND); } - if (self->share_output_buffer) { + if (self->out_port->shared_buffer) { GST_WARNING_OBJECT (self, "couldn't zero-copy"); /* If pAppPrivate is NULL, it means it was a dummy * allocation, free it. */ @@ -647,7 +664,7 @@ output_loop (gpointer data) GST_WARNING_OBJECT (self, "empty buffer"); } - if (self->share_output_buffer && + if (self->out_port->shared_buffer && !omx_buffer->pBuffer && omx_buffer->nOffset == 0) { GstBuffer *buf; GstFlowReturn result; @@ -670,7 +687,7 @@ output_loop (gpointer data) } } - if (self->share_output_buffer && !omx_buffer->pBuffer) { + if (self->out_port->shared_buffer && !omx_buffer->pBuffer) { GST_ERROR_OBJECT (self, "no input buffer to share"); } @@ -716,10 +733,8 @@ pad_chain (GstPad * pad, GstBuffer * buf) gomx = self->gomx; - GST_LOG_OBJECT (self, "begin"); - GST_LOG_OBJECT (self, "gst_buffer: size=%u", GST_BUFFER_SIZE (buf)); - - GST_LOG_OBJECT (self, "state: %d", gomx->omx_state); + GST_LOG_OBJECT (self, "IN_BUFFER: timestamp = %" GST_TIME_FORMAT " size = %lu, state:%d", + GST_TIME_ARGS(GST_BUFFER_TIMESTAMP (buf)), GST_BUFFER_SIZE (buf), gomx->omx_state); /* STATE_TUNING */ if (!self->use_state_tuning) { @@ -747,7 +762,7 @@ pad_chain (GstPad * pad, GstBuffer * buf) /* process input gst buffer before OMX_EmptyThisBuffer */ if (basefilter_class->process_input_buf) { - basefilter_class->process_input_buf(self,buf); + basefilter_class->process_input_buf(self,&buf); } if (self->adapter_size > 0) { @@ -763,7 +778,6 @@ pad_chain (GstPad * pad, GstBuffer * buf) src_size = gst_adapter_available(self->adapter); if (src_size + GST_BUFFER_SIZE(buf) <= self->adapter_size) { gst_adapter_push(self->adapter, buf); - gst_buffer_ref(buf); goto leave; } src_data = (guint8*) gst_adapter_peek(self->adapter, src_size); @@ -791,8 +805,9 @@ pad_chain (GstPad * pad, GstBuffer * buf) GST_LOG_OBJECT (self, "omx_buffer: %p", omx_buffer); if (G_LIKELY (omx_buffer)) { - log_buffer (self, omx_buffer); + log_buffer (self, omx_buffer, "pad_chain"); + /* MODIFICATION: to handle input SN12 HW addr. (enc) */ if (is_extended_color_format(self, self->in_port)) { if (!GST_BUFFER_MALLOCDATA(buf)) { GST_WARNING_OBJECT (self, "null MALLOCDATA in hw color format. skip this."); @@ -808,7 +823,7 @@ pad_chain (GstPad * pad, GstBuffer * buf) sizeof (void*)); omx_buffer->nAllocLen = sizeof (void*) * 2; omx_buffer->nFilledLen = sizeof (void*) * 2; - } else if (omx_buffer->nOffset == 0 && self->share_input_buffer) { + } else if (omx_buffer->nOffset == 0 && self->in_port->shared_buffer) { { GstBuffer *old_buf; old_buf = omx_buffer->pAppPrivate; @@ -821,6 +836,7 @@ pad_chain (GstPad * pad, GstBuffer * buf) } } else if (omx_buffer->pBuffer) { g_free (omx_buffer->pBuffer); + omx_buffer->pBuffer = NULL; } } @@ -848,6 +864,7 @@ pad_chain (GstPad * pad, GstBuffer * buf) timestamp_offset, OMX_TICKS_PER_SECOND, GST_SECOND); } + /* MODIFICATION: hw addr */ if (is_extended_color_format(self, self->in_port)) { buffer_offset = GST_BUFFER_SIZE (buf); } else { @@ -869,15 +886,14 @@ pad_chain (GstPad * pad, GstBuffer * buf) } if (self->adapter_size > 0) { - if (!self->share_input_buffer) { + if (!self->in_port->shared_buffer) { gst_adapter_clear(self->adapter); } else { self->adapter = gst_adapter_new(); } gst_adapter_push(self->adapter, buf); - gst_buffer_ref(buf); } else { - if (!self->share_input_buffer) { + if (!self->in_port->shared_buffer) { gst_buffer_unref (buf); } } @@ -927,8 +943,8 @@ pad_event (GstPad * pad, GstEvent * event) GST_INFO_OBJECT (self, "event: %s", GST_EVENT_TYPE_NAME (event)); - if (self->omx_event) { - if (!self->omx_event(pad, event)) + if (self->pad_event) { + if (!self->pad_event(pad, event)) return TRUE; } diff --git a/omx/gstomx_base_filter.h b/omx/gstomx_base_filter.h index 47b348c..1368217 100644 --- a/omx/gstomx_base_filter.h +++ b/omx/gstomx_base_filter.h @@ -38,7 +38,7 @@ typedef void (*GstOmxBaseFilterCb) (GstOmxBaseFilter * self); typedef gboolean (*GstOmxBaseFilterEventCb) (GstPad * pad, GstEvent * event); -/* Add extended_color_format */ +/* MODIFICATION: Add extended_color_format */ typedef enum _EXT_OMX_COLOR_FORMATTYPE { OMX_EXT_COLOR_FormatNV12TPhysicalAddress = 0x7F000001, /**< Reserved region for introducing Vendor Extensions */ OMX_EXT_COLOR_FormatNV12LPhysicalAddress = 0x7F000002, @@ -68,15 +68,11 @@ struct GstOmxBaseFilter GMutex *ready_lock; GstOmxBaseFilterCb omx_setup; - GstOmxBaseFilterEventCb omx_event; + GstOmxBaseFilterEventCb pad_event; GstFlowReturn last_pad_push_return; GstBuffer *codec_data; - /** @todo these are hacks, OpenMAX IL spec should be revised. */ - gboolean share_input_buffer; - gboolean share_output_buffer; - - + /* MODIFICATION: state-tuning */ gboolean use_state_tuning; GstAdapter *adapter; /* adapter */ @@ -87,8 +83,10 @@ struct GstOmxBaseFilterClass { GstElementClass parent_class; - void (*process_input_buf)(GstOmxBaseFilter * omx_base_filter, GstBuffer * buf); - void (*process_output_buf)(GstOmxBaseFilter * omx_base_filter, GstBuffer * buf, OMX_BUFFERHEADERTYPE *omx_buffer); + void (*process_input_buf)(GstOmxBaseFilter *omx_base_filter, GstBuffer **buf); + void (*process_output_buf)(GstOmxBaseFilter *omx_base_filter, GstBuffer **buf, OMX_BUFFERHEADERTYPE *omx_buffer); + void (*process_output_caps)(GstOmxBaseFilter *omx_base_filter, OMX_BUFFERHEADERTYPE *omx_buffer); + }; GType gst_omx_base_filter_get_type (void); diff --git a/omx/gstomx_base_sink.c b/omx/gstomx_base_sink.c index 9c2d40b..9939c8d 100644 --- a/omx/gstomx_base_sink.c +++ b/omx/gstomx_base_sink.c @@ -33,8 +33,6 @@ enum ARG_NUM_INPUT_BUFFERS = GSTOMX_NUM_COMMON_PROP, }; -static gboolean share_input_buffer; - static inline gboolean omx_init (GstOmxBaseSink * self); static void init_interfaces (GType type); @@ -161,7 +159,7 @@ render (GstBaseSink * gst_base, GstBuffer * buf) omx_buffer->nAllocLen, omx_buffer->nFilledLen, omx_buffer->nFlags, omx_buffer->nOffset, omx_buffer->nTimeStamp); - if (omx_buffer->nOffset == 0 && share_input_buffer) { + if (omx_buffer->nOffset == 0 && self->in_port->shared_buffer) { { GstBuffer *old_buf; old_buf = omx_buffer->pAppPrivate; @@ -170,6 +168,7 @@ render (GstBaseSink * gst_base, GstBuffer * buf) gst_buffer_unref (old_buf); } else if (omx_buffer->pBuffer) { g_free (omx_buffer->pBuffer); + omx_buffer->pBuffer = NULL; } } diff --git a/omx/gstomx_base_videodec.c b/omx/gstomx_base_videodec.c index 6cd8236..e27fa14 100644 --- a/omx/gstomx_base_videodec.c +++ b/omx/gstomx_base_videodec.c @@ -32,7 +32,7 @@ GSTOMX_BOILERPLATE (GstOmxBaseVideoDec, gst_omx_base_videodec, GstOmxBaseFilter, GST_OMX_BASE_FILTER_TYPE); static void -process_input_buf (GstOmxBaseFilter * omx_base_filter, GstBuffer * buf) +process_input_buf (GstOmxBaseFilter * omx_base_filter, GstBuffer **buf) { } @@ -41,8 +41,7 @@ type_base_init (gpointer g_class) { } - -/* add state tuning property */ +/* MODIFICATION: add state tuning property */ static void set_property (GObject * obj, guint prop_id, const GValue * value, GParamSpec * pspec) @@ -139,7 +138,7 @@ settings_changed_cb (GOmxCore * core) case OMX_COLOR_FormatCbYCrY: format = GST_MAKE_FOURCC ('U', 'Y', 'V', 'Y'); break; - /* Add extended_color_format */ + /* MODIFICATION: Add extended_color_format */ case OMX_EXT_COLOR_FormatNV12TPhysicalAddress: format = GST_MAKE_FOURCC ('S', 'T', '1', '2'); break; @@ -174,6 +173,7 @@ settings_changed_cb (GOmxCore * core) GST_INFO_OBJECT (omx_base, "caps are: %" GST_PTR_FORMAT, new_caps); gst_pad_set_caps (omx_base->srcpad, new_caps); + gst_caps_unref (new_caps); /* Modification: unref caps */ } } diff --git a/omx/gstomx_base_videoenc.c b/omx/gstomx_base_videoenc.c index 25f8a51..7bb77bf 100644 --- a/omx/gstomx_base_videoenc.c +++ b/omx/gstomx_base_videoenc.c @@ -28,6 +28,7 @@ enum { ARG_0, ARG_BITRATE, + ARG_FORCE_KEY_FRAME, }; #define DEFAULT_BITRATE 0 @@ -35,8 +36,9 @@ enum GSTOMX_BOILERPLATE (GstOmxBaseVideoEnc, gst_omx_base_videoenc, GstOmxBaseFilter, GST_OMX_BASE_FILTER_TYPE); +/* modification: postprocess for outputbuf. in this videoenc case, set sync frame */ static void -process_output_buf(GstOmxBaseFilter * omx_base, GstBuffer * buf, OMX_BUFFERHEADERTYPE *omx_buffer) +process_output_buf(GstOmxBaseFilter * omx_base, GstBuffer **buf, OMX_BUFFERHEADERTYPE *omx_buffer) { GstOmxBaseVideoEnc *self; @@ -44,14 +46,68 @@ process_output_buf(GstOmxBaseFilter * omx_base, GstBuffer * buf, OMX_BUFFERHEADE GST_LOG_OBJECT (self, "base videoenc process_output_buf enter"); - /* MODIFICATION: Set sync frame info while encoding */ + /* modification: set sync frame info while encoding */ if (omx_buffer->nFlags & OMX_BUFFERFLAG_SYNCFRAME) { - GST_BUFFER_FLAG_UNSET(buf, GST_BUFFER_FLAG_DELTA_UNIT); + GST_BUFFER_FLAG_UNSET(*buf, GST_BUFFER_FLAG_DELTA_UNIT); } else { - GST_BUFFER_FLAG_SET(buf, GST_BUFFER_FLAG_DELTA_UNIT); + GST_BUFFER_FLAG_SET(*buf, GST_BUFFER_FLAG_DELTA_UNIT); } } +/* modification: user force I frame */ +static void +add_force_key_frame(GstOmxBaseVideoEnc *enc) +{ + GstOmxBaseFilter *omx_base; + GOmxCore *gomx; + OMX_CONFIG_INTRAREFRESHVOPTYPE config; + omx_base = GST_OMX_BASE_FILTER (enc); + gomx = (GOmxCore *) omx_base->gomx; + + GST_INFO_OBJECT (enc, "set forcing I frame."); + + if (!omx_base->out_port || !gomx->omx_handle) { + GST_WARNING_OBJECT (enc, "failed to set force-i-frame..."); + return; + } + + G_OMX_INIT_PARAM (config); + config.nPortIndex = omx_base->out_port->port_index; + + OMX_GetConfig (gomx->omx_handle, OMX_IndexConfigVideoIntraVOPRefresh, &config); + config.IntraRefreshVOP = OMX_TRUE; + + OMX_SetConfig (gomx->omx_handle, OMX_IndexConfigVideoIntraVOPRefresh, &config); +} + +/* modification: get codec_data from omx component and set it caps */ +static void +process_output_caps(GstOmxBaseFilter * self, OMX_BUFFERHEADERTYPE *omx_buffer) +{ + GstBuffer *buf; + GstCaps *caps = NULL; + GstStructure *structure; + GValue value = { 0, {{0} + } + }; + + caps = gst_pad_get_negotiated_caps (self->srcpad); + caps = gst_caps_make_writable (caps); + structure = gst_caps_get_structure (caps, 0); + + g_value_init (&value, GST_TYPE_BUFFER); + buf = gst_buffer_new_and_alloc (omx_buffer->nFilledLen); + memcpy (GST_BUFFER_DATA (buf), + omx_buffer->pBuffer + omx_buffer->nOffset, omx_buffer->nFilledLen); + gst_value_set_buffer (&value, buf); + gst_buffer_unref (buf); + gst_structure_set_value (structure, "codec_data", &value); + g_value_unset (&value); + + gst_pad_set_caps (self->srcpad, caps); + gst_caps_unref (caps); +} + static void type_base_init (gpointer g_class) { @@ -69,6 +125,14 @@ set_property (GObject * obj, case ARG_BITRATE: self->bitrate = g_value_get_uint (value); break; + /* modification: request to component to make key frame */ + case ARG_FORCE_KEY_FRAME: + self->use_force_key_frame = g_value_get_boolean (value); + if (self->use_force_key_frame) { + add_force_key_frame (self); + self->use_force_key_frame = FALSE; + } + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, prop_id, pspec); break; @@ -98,9 +162,11 @@ type_class_init (gpointer g_class, gpointer class_data) { GObjectClass *gobject_class; GstOmxBaseFilterClass *basefilter_class; + GstOmxBaseVideoEncClass *videoenc_class; gobject_class = G_OBJECT_CLASS (g_class); basefilter_class = GST_OMX_BASE_FILTER_CLASS (g_class); + videoenc_class = GST_OMX_BASE_VIDEOENC_CLASS (g_class); /* Properties stuff */ { @@ -112,8 +178,15 @@ type_class_init (gpointer g_class, gpointer class_data) "Encoding bit-rate", 0, G_MAXUINT, DEFAULT_BITRATE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + + g_object_class_install_property (gobject_class, ARG_FORCE_KEY_FRAME, + g_param_spec_boolean ("force-i-frame", "force the encoder to produce I frame", + "force the encoder to produce I frame", + FALSE, + G_PARAM_WRITABLE | G_PARAM_STATIC_STRINGS)); } basefilter_class->process_output_buf = process_output_buf; + basefilter_class->process_output_caps = process_output_caps; } static gboolean @@ -162,7 +235,7 @@ sink_setcaps (GstPad * pad, GstCaps * caps) case GST_MAKE_FOURCC ('U', 'Y', 'V', 'Y'): color_format = OMX_COLOR_FormatCbYCrY; break; - /* Add extended_color_format */ + /* MODIFICATION: Add extended_color_format */ case GST_MAKE_FOURCC ('S', 'T', '1', '2'): color_format = OMX_EXT_COLOR_FormatNV12TPhysicalAddress; break; @@ -196,12 +269,13 @@ sink_setcaps (GstPad * pad, GstCaps * caps) OMX_SetParameter (gomx->omx_handle, OMX_IndexParamPortDefinition, ¶m); } + /* modification: set nBufferSize */ G_OMX_INIT_PARAM (param); param.nPortIndex = omx_base->out_port->port_index; OMX_GetParameter (gomx->omx_handle, OMX_IndexParamPortDefinition, ¶m); - param.nBufferSize = MAX(param.nBufferSize, width * height * 3 / 2); + param.nBufferSize = width * height * 3 / 2; OMX_SetParameter (gomx->omx_handle, OMX_IndexParamPortDefinition, ¶m); } @@ -231,13 +305,28 @@ omx_setup (GstOmxBaseFilter * omx_base) param.format.video.eCompressionFormat = self->compression_format; - if (self->bitrate != 0) + if (self->bitrate > 0) param.format.video.nBitrate = self->bitrate; OMX_SetParameter (gomx->omx_handle, OMX_IndexParamPortDefinition, ¶m); } } + /* modification: set bitrate by using OMX_IndexParamVideoBitrate macro*/ + if (self->bitrate > 0) { + OMX_VIDEO_PARAM_BITRATETYPE param; + G_OMX_INIT_PARAM (param); + + param.nPortIndex = omx_base->out_port->port_index; + OMX_GetParameter (gomx->omx_handle, OMX_IndexParamVideoBitrate, ¶m); + + param.nTargetBitrate = self->bitrate; + param.eControlRate = OMX_Video_ControlRateConstant; + GST_INFO_OBJECT (self, "set bitrate (OMX_Video_ControlRateConstant): %d", param.nTargetBitrate); + + OMX_SetParameter (gomx->omx_handle, OMX_IndexParamVideoBitrate, ¶m); + } + GST_INFO_OBJECT (omx_base, "end"); } @@ -255,4 +344,5 @@ type_instance_init (GTypeInstance * instance, gpointer g_class) gst_pad_set_setcaps_function (omx_base->sinkpad, sink_setcaps); self->bitrate = DEFAULT_BITRATE; + self->use_force_key_frame = FALSE; } diff --git a/omx/gstomx_base_videoenc.h b/omx/gstomx_base_videoenc.h index 96a1b07..24e903e 100644 --- a/omx/gstomx_base_videoenc.h +++ b/omx/gstomx_base_videoenc.h @@ -27,6 +27,7 @@ G_BEGIN_DECLS #define GST_OMX_BASE_VIDEOENC(obj) (GstOmxBaseVideoEnc *) (obj) #define GST_OMX_BASE_VIDEOENC_TYPE (gst_omx_base_videoenc_get_type ()) +#define GST_OMX_BASE_VIDEOENC_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), GST_OMX_BASE_VIDEOENC_TYPE, GstOmxBaseVideoEncClass)) typedef struct GstOmxBaseVideoEnc GstOmxBaseVideoEnc; typedef struct GstOmxBaseVideoEncClass GstOmxBaseVideoEncClass; @@ -40,6 +41,7 @@ struct GstOmxBaseVideoEnc guint bitrate; gint framerate_num; gint framerate_denom; + gboolean use_force_key_frame; }; struct GstOmxBaseVideoEncClass diff --git a/omx/gstomx_h264.h b/omx/gstomx_h264.h new file mode 100644 index 0000000..a9e37d9 --- /dev/null +++ b/omx/gstomx_h264.h @@ -0,0 +1,83 @@ +/* + * Copyright (C) 2012 Samsung Electronics Co., Ltd. All rights reserved. + * + * Author: Sunghyun Eum + * + * 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; either version 2.1 of the License, or (at your option) + * any later version. + * + * 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 + * + */ + +#ifndef GSTOMX_H264_H +#define GSTOMX_H264_H + +G_BEGIN_DECLS + +/* modification: byte-stream(nal) - packetized (3gpp) converting (h264dec, h264enc) */ +#define GSTOMX_H264_MDATA 6 /* packtized meta data size. (ver, profile...) */ +#define GSTOMX_H264_A_DCI_LEN 4 /* 4-byte sps, pps size field in append dci*/ +#define GSTOMX_H264_C_DCI_LEN 2 /* 2-byte sps, pps size field in codec data*/ +#define GSTOMX_H264_CNT_LEN 1 /* 1-byte sps, pps count field */ + +#define GSTOMX_H264_NAL_START_LEN 4 +#define GSTOMX_H264_SPSPPS_LEN 2 + +typedef enum +{ + GSTOMX_H264_NUT_UNKNOWN = 0, + GSTOMX_H264_NUT_SLICE = 1, + GSTOMX_H264_NUT_DPA = 2, + GSTOMX_H264_NUT_DPB = 3, + GSTOMX_H264_NUT_DPC = 4, + GSTOMX_H264_NUT_IDR = 5, + GSTOMX_H264_NUT_SEI = 6, + GSTOMX_H264_NUT_SPS = 7, + GSTOMX_H264_NUT_PPS = 8, + GSTOMX_H264_NUT_AUD = 9, + GSTOMX_H264_NUT_EOSEQ = 10, + GSTOMX_H264_NUT_EOSTREAM = 11, + GSTOMX_H264_NUT_FILL = 12, + GSTOMX_H264_NUT_MIXED = 24, +} GSTOMX_H264_NAL_UNIT_TYPE; + +typedef enum +{ + GSTOMX_H264_FORMAT_UNKNOWN, + GSTOMX_H264_FORMAT_3GPP, + GSTOMX_H264_FORMAT_NALU +} GSTOMX_H264_STREAM_FORMAT; + +#define GSTOMX_H264_WB32(p, d) \ + do { \ + ((unsigned char*)(p))[3] = (d); \ + ((unsigned char*)(p))[2] = (d)>>8; \ + ((unsigned char*)(p))[1] = (d)>>16; \ + ((unsigned char*)(p))[0] = (d)>>24; \ + } while(0); + +#define GSTOMX_H264_WB16(p, d) \ + do { \ + ((unsigned char*)(p))[1] = (d); \ + ((unsigned char*)(p))[0] = (d)>>8; \ + } while(0); + +#define GSTOMX_H264_RB16(x) ((((const unsigned char*)(x))[0] << 8) | ((const unsigned char*)(x))[1]) + +#define GSTOMX_H264_RB32(x) ((((const unsigned char*)(x))[0] << 24) | \ + (((const unsigned char*)(x))[1] << 16) | \ + (((const unsigned char*)(x))[2] << 8) | \ + ((const unsigned char*)(x))[3]) + +G_END_DECLS +#endif /* GSTOMX_H264_H */ diff --git a/omx/gstomx_h264dec.c b/omx/gstomx_h264dec.c index 0507952..6e19592 100644 --- a/omx/gstomx_h264dec.c +++ b/omx/gstomx_h264dec.c @@ -25,6 +25,309 @@ GSTOMX_BOILERPLATE (GstOmxH264Dec, gst_omx_h264dec, GstOmxBaseVideoDec, GST_OMX_BASE_VIDEODEC_TYPE); +/* + * description : find stream format(3gpp or nalu) + * params : @self : GstOmxH264Dec, @buf: input gstbuffer in pad_chain + * return : none + * comments : finding whether the stream format of input buf is 3GPP or Elementary Stream(nalu) + */ +static void +check_frame (GstOmxH264Dec *self, GstBuffer * buf) +{ + guint buf_size = GST_BUFFER_SIZE (buf); + guint8 *buf_data = GST_BUFFER_DATA (buf); + unsigned int checklen = 0; + + if (buf_data == NULL || buf_size < GSTOMX_H264_NAL_START_LEN) { + self->h264Format = GSTOMX_H264_FORMAT_UNKNOWN; + GST_INFO_OBJECT(self, "H264 format is unknown"); + return; + } + + self->h264Format = GSTOMX_H264_FORMAT_3GPP; + + do + { + if (buf_data[checklen] == 0x00 && + buf_data[checklen+1] == 0x00 && + buf_data[checklen+2] == 0x00 && + buf_data[checklen+3] == 0x01) { + self->h264Format = GSTOMX_H264_FORMAT_NALU; + GST_INFO_OBJECT(self, "H264 format is NALU"); + break; + } else { + checklen++; + } + } while(checklen < (buf_size - GSTOMX_H264_NAL_START_LEN)); + + if (GSTOMX_H264_FORMAT_3GPP == self->h264Format) + GST_INFO_OBJECT(self, "H264 format is 3GPP"); +} + +/* + * description : convert input 3gpp buffer to nalu based buffer + * params : @self : GstOmxH264Dec, @buf: buffer to be converted + * return : none + * comments : none + */ +static void +convert_frame (GstOmxH264Dec *self, GstBuffer **buf) +{ + OMX_U8 frameType; + OMX_U32 nalSize = 0; + OMX_U32 cumulSize = 0; + OMX_U32 offset = 0; + OMX_U32 nalHeaderSize = 0; + OMX_U32 outSize = 0; + OMX_U8 *frame_3gpp = GST_BUFFER_DATA(*buf); + OMX_U32 frame_3gpp_size = GST_BUFFER_SIZE(*buf); + GstBuffer *nalu_next_buf = NULL; + GstBuffer *nalu_buf = NULL; + + do { + /* get NAL Length based on length of length*/ + if (self->h264NalLengthSize == 1) { + nalSize = frame_3gpp[0]; + } else if (self->h264NalLengthSize == 2) { + nalSize = GSTOMX_H264_RB16(frame_3gpp); + } else { + nalSize = GSTOMX_H264_RB32(frame_3gpp); + } + + GST_LOG_OBJECT(self, "packetized frame size = %d", nalSize); + + frame_3gpp += self->h264NalLengthSize; + + /* Checking frame type */ + frameType = *frame_3gpp & 0x1f; + + switch (frameType) + { + case GSTOMX_H264_NUT_SLICE: + GST_LOG_OBJECT(self, "Frame is non-IDR frame..."); + break; + case GSTOMX_H264_NUT_IDR: + GST_LOG_OBJECT(self, "Frame is an IDR frame..."); + break; + case GSTOMX_H264_NUT_SEI: + GST_LOG_OBJECT(self, "Found SEI Data..."); + break; + case GSTOMX_H264_NUT_SPS: + GST_LOG_OBJECT(self, "Found SPS data..."); + break; + case GSTOMX_H264_NUT_PPS: + GST_LOG_OBJECT(self, "Found PPS data..."); + break; + case GSTOMX_H264_NUT_EOSEQ: + GST_LOG_OBJECT(self, "End of sequence..."); + break; + case GSTOMX_H264_NUT_EOSTREAM: + GST_LOG_OBJECT(self, "End of stream..."); + break; + case GSTOMX_H264_NUT_DPA: + case GSTOMX_H264_NUT_DPB: + case GSTOMX_H264_NUT_DPC: + case GSTOMX_H264_NUT_AUD: + case GSTOMX_H264_NUT_FILL: + case GSTOMX_H264_NUT_MIXED: + break; + default: + GST_INFO_OBJECT(self, "Unknown Frame type: %d\n", frameType); + goto EXIT; + } + + /* if nal size is same, we can change only start code */ + if((nalSize + GSTOMX_H264_NAL_START_LEN) == frame_3gpp_size) { + GST_LOG_OBJECT(self, "only change start code"); + GSTOMX_H264_WB32(GST_BUFFER_DATA(*buf), 1); + return; + } + + /* Convert 3GPP Frame to NALU Frame */ + offset = outSize; + nalHeaderSize = offset ? 3 : 4; + + outSize += nalSize + nalHeaderSize; + + if ((nalSize > frame_3gpp_size)||(outSize < 0)) { + GST_ERROR_OBJECT(self, "out of bounds Error. frame_nalu_size=%d", outSize); + goto EXIT; + } + + if (nalu_buf) { + nalu_next_buf= gst_buffer_new_and_alloc(nalSize + nalHeaderSize); + if (nalu_next_buf == NULL) { + GST_ERROR_OBJECT(self, "gst_buffer_new_and_alloc failed.(nalu_next_buf)"); + goto EXIT; + } + } else { + nalu_buf = gst_buffer_new_and_alloc(outSize); + } + + if (nalu_buf == NULL) { + GST_ERROR_OBJECT(self, "gst_buffer_new_and_alloc failed.(nalu_buf)"); + goto EXIT; + } + + if (!offset) { + memcpy(GST_BUFFER_DATA(nalu_buf)+nalHeaderSize, frame_3gpp, nalSize); + GSTOMX_H264_WB32(GST_BUFFER_DATA(nalu_buf), 1); + } else { + if (nalu_next_buf) { + GstBuffer *nalu_joined_buf = gst_buffer_join(nalu_buf,nalu_next_buf); + nalu_buf = nalu_joined_buf; + nalu_next_buf = NULL; + } + memcpy(GST_BUFFER_DATA(nalu_buf)+nalHeaderSize+offset, frame_3gpp, nalSize); + (GST_BUFFER_DATA(nalu_buf)+offset)[0] = (GST_BUFFER_DATA(nalu_buf)+offset)[1] = 0; + (GST_BUFFER_DATA(nalu_buf)+offset)[2] = 1; + } + + frame_3gpp += nalSize; + cumulSize += nalSize + self->h264NalLengthSize; + GST_LOG_OBJECT(self, "frame_3gpp_size = %d => frame_nalu_size=%d", frame_3gpp_size, outSize); + } while (cumulSize < frame_3gpp_size); + + gst_buffer_copy_metadata(nalu_buf, *buf, GST_BUFFER_COPY_ALL); + + if (*buf) { gst_buffer_unref (*buf); } + *buf = nalu_buf; + + return; + +EXIT: + if (nalu_buf) { gst_buffer_unref (nalu_buf); } + GST_ERROR_OBJECT(self, "converting frame error."); + + return; +} + +/* + * description : convert input 3gpp buffer(codec data) to nalu based buffer + * params : @self : GstOmxH264Dec, @buf: buffer to be converted, @dci_nalu: converted buffer + * return : true on successes / false on failure + * comments : none + */ +static gboolean +convert_dci (GstOmxH264Dec *self, GstBuffer *buf, GstBuffer **dci_nalu) +{ + gboolean ret = FALSE; + OMX_U8 *out = NULL; + OMX_U16 unitSize = 0; + OMX_U32 totalSize = 0; + OMX_U8 unitNb = 0; + OMX_U8 spsDone = 0; + + OMX_U8 *pInputStream = GST_BUFFER_DATA(buf); + OMX_U32 pBuffSize = GST_BUFFER_SIZE(buf); + + const OMX_U8 *extraData = (guchar*)pInputStream + 4; + static const OMX_U8 naluHeader[GSTOMX_H264_NAL_START_LEN] = {0, 0, 0, 1}; + + if (pInputStream != NULL) { + /* retrieve Length of Length*/ + self->h264NalLengthSize = (*extraData++ & 0x03) + 1; + GST_INFO("Length Of Length is %d", self->h264NalLengthSize); + if (self->h264NalLengthSize == 3) + { + GST_INFO("LengthOfLength is WRONG..."); + goto EXIT; + } + /* retrieve sps and pps unit(s) */ + unitNb = *extraData++ & 0x1f; + GST_INFO("No. of SPS units = %u", unitNb); + if (!unitNb) { + GST_INFO("SPS is not present..."); + goto EXIT; + } + + while (unitNb--) { + /* get SPS/PPS data Length*/ + unitSize = GSTOMX_H264_RB16(extraData); + + /* Extra 4 bytes for adding delimiter */ + totalSize += unitSize + GSTOMX_H264_NAL_START_LEN; + + /* Check if SPS/PPS Data Length crossed buffer Length */ + if ((extraData + 2 + unitSize) > (pInputStream + pBuffSize)) { + GST_INFO("SPS length is wrong in DCI..."); + goto EXIT; + } + + if (out) + out = g_realloc(out, totalSize); + else + out = g_malloc(totalSize); + + if (!out) { + GST_INFO("realloc failed..."); + goto EXIT; + } + + /* Copy NALU header */ + memcpy(out + totalSize - unitSize - GSTOMX_H264_NAL_START_LEN, + naluHeader, GSTOMX_H264_NAL_START_LEN); + + /* Copy SPS/PPS Length and data */ + memcpy(out + totalSize - unitSize, extraData + GSTOMX_H264_SPSPPS_LEN, unitSize); + + extraData += (GSTOMX_H264_SPSPPS_LEN + unitSize); + + if (!unitNb && !spsDone++) + { + /* Completed reading SPS data, now read PPS data */ + unitNb = *extraData++; /* number of pps unit(s) */ + GST_INFO( "No. of PPS units = %d", unitNb); + } + } + + *dci_nalu = gst_buffer_new_and_alloc(totalSize); + if (*dci_nalu == NULL) { + GST_ERROR_OBJECT(self, " gst_buffer_new_and_alloc failed...\n"); + goto EXIT; + } + + memcpy(GST_BUFFER_DATA(*dci_nalu), out, totalSize); + GST_INFO( "Total SPS+PPS size = %d", totalSize); + } + + ret = TRUE; + +EXIT: + if (out) { + g_free(out); + } + return ret; +} + + +static void +process_input_buf (GstOmxBaseFilter * omx_base_filter, GstBuffer **buf) +{ + GstOmxH264Dec *h264_self; + + h264_self = GST_OMX_H264DEC (omx_base_filter); + + if (h264_self->h264Format == GSTOMX_H264_FORMAT_UNKNOWN) { + check_frame(h264_self, *buf); + } + + if (h264_self->h264Format == GSTOMX_H264_FORMAT_3GPP) { + + if (omx_base_filter->last_pad_push_return != GST_FLOW_OK || + !(omx_base_filter->gomx->omx_state == OMX_StateExecuting || + omx_base_filter->gomx->omx_state == OMX_StatePause)) { + GST_LOG_OBJECT(h264_self, "this frame will not be converted and go to out_flushing"); + return; + } + + GST_LOG_OBJECT(h264_self, "H264 format is 3GPP. convert to NALU"); + convert_frame(h264_self, buf); + } + + GST_OMX_BASE_FILTER_CLASS (parent_class)->process_input_buf (omx_base_filter, buf); +} + static void type_base_init (gpointer g_class) { @@ -49,14 +352,121 @@ type_base_init (gpointer g_class) static void type_class_init (gpointer g_class, gpointer class_data) { + GstOmxBaseFilterClass *basefilter_class; + + basefilter_class = GST_OMX_BASE_FILTER_CLASS (g_class); + + basefilter_class->process_input_buf = process_input_buf; +} + +/* h264 dec has its own sink_setcaps for supporting nalu convert codec data */ +static gboolean +sink_setcaps (GstPad * pad, GstCaps * caps) +{ + GstStructure *structure; + GstOmxBaseVideoDec *self; + GstOmxH264Dec *h264_self; + GstOmxBaseFilter *omx_base; + GOmxCore *gomx; + OMX_PARAM_PORTDEFINITIONTYPE param; + gint width = 0; + gint height = 0; + + self = GST_OMX_BASE_VIDEODEC (GST_PAD_PARENT (pad)); + h264_self = GST_OMX_H264DEC (GST_PAD_PARENT (pad)); + omx_base = GST_OMX_BASE_FILTER (self); + + gomx = (GOmxCore *) omx_base->gomx; + + GST_INFO_OBJECT (self, "setcaps (sink)(h264): %" GST_PTR_FORMAT, caps); + + g_return_val_if_fail (gst_caps_get_size (caps) == 1, FALSE); + + structure = gst_caps_get_structure (caps, 0); + + gst_structure_get_int (structure, "width", &width); + gst_structure_get_int (structure, "height", &height); + + { + const GValue *framerate = NULL; + framerate = gst_structure_get_value (structure, "framerate"); + if (framerate) { + self->framerate_num = gst_value_get_fraction_numerator (framerate); + self->framerate_denom = gst_value_get_fraction_denominator (framerate); + } + } + + G_OMX_INIT_PARAM (param); + + { + const GValue *codec_data; + GstBuffer *buffer; + gboolean ret = FALSE; + guint8 *buf_data = NULL; + + codec_data = gst_structure_get_value (structure, "codec_data"); + if (codec_data) { + buffer = gst_value_get_buffer (codec_data); + + buf_data = GST_BUFFER_DATA(buffer); + + if (GST_BUFFER_SIZE(buffer) < 4) GST_ERROR("codec data size is less than 4!!"); //check this. + + if ((buf_data[0] == 0x00)&&(buf_data[1] == 0x00)&& + ((buf_data[2] == 0x01)||((buf_data[2] == 0x00)&&(buf_data[3] == 0x01)))) { + h264_self->h264Format = GSTOMX_H264_FORMAT_NALU; + GST_INFO_OBJECT(self, "H264 format is NALU"); + } else { + h264_self->h264Format = GSTOMX_H264_FORMAT_3GPP; + GST_INFO_OBJECT(self, "H264 format is 3GPP"); + } + + + /* if codec data is 3gpp format, convert nalu format */ + if(h264_self->h264Format == GSTOMX_H264_FORMAT_3GPP) { + GstBuffer *nalu_dci = NULL; + + ret = convert_dci(h264_self, buffer, &nalu_dci); + + if (ret) { + omx_base->codec_data = nalu_dci; + } else { + if (nalu_dci) { gst_buffer_unref (nalu_dci); } + GST_ERROR_OBJECT(h264_self, "converting dci error."); + omx_base->codec_data = buffer; + gst_buffer_ref (buffer); + } + + } else { /* not 3GPP format */ + omx_base->codec_data = buffer; + gst_buffer_ref (buffer); + } + h264_self->h264Format = GSTOMX_H264_FORMAT_UNKNOWN; + } + } + /* Input port configuration. */ + { + param.nPortIndex = omx_base->in_port->port_index; + OMX_GetParameter (gomx->omx_handle, OMX_IndexParamPortDefinition, ¶m); + + param.format.video.nFrameWidth = width; + param.format.video.nFrameHeight = height; + + OMX_SetParameter (gomx->omx_handle, OMX_IndexParamPortDefinition, ¶m); + } + return gst_pad_set_caps (pad, caps); } static void type_instance_init (GTypeInstance * instance, gpointer g_class) { GstOmxBaseVideoDec *omx_base; + GstOmxBaseFilter *omx_base_filter; omx_base = GST_OMX_BASE_VIDEODEC (instance); + omx_base_filter = GST_OMX_BASE_FILTER (instance); omx_base->compression_format = OMX_VIDEO_CodingAVC; + + gst_pad_set_setcaps_function (omx_base_filter->sinkpad, sink_setcaps); } diff --git a/omx/gstomx_h264dec.h b/omx/gstomx_h264dec.h index 8a771ac..d736fbd 100644 --- a/omx/gstomx_h264dec.h +++ b/omx/gstomx_h264dec.h @@ -27,14 +27,18 @@ G_BEGIN_DECLS #define GST_OMX_H264DEC(obj) (GstOmxH264Dec *) (obj) #define GST_OMX_H264DEC_TYPE (gst_omx_h264dec_get_type ()) + typedef struct GstOmxH264Dec GstOmxH264Dec; typedef struct GstOmxH264DecClass GstOmxH264DecClass; #include "gstomx_base_videodec.h" +#include "gstomx_h264.h" struct GstOmxH264Dec { GstOmxBaseVideoDec omx_base; + GSTOMX_H264_STREAM_FORMAT h264Format; + OMX_U32 h264NalLengthSize; }; struct GstOmxH264DecClass diff --git a/omx/gstomx_h264enc.c b/omx/gstomx_h264enc.c index 4b2d59c..f8910b1 100644 --- a/omx/gstomx_h264enc.c +++ b/omx/gstomx_h264enc.c @@ -22,9 +22,517 @@ #include "gstomx_h264enc.h" #include "gstomx.h" +enum +{ + ARG_0, + ARG_APPEND_DCI, + ARG_BYTE_STREAM, + ARG_SLICE_MODE, + ARG_SLICE_SIZE, +}; + GSTOMX_BOILERPLATE (GstOmxH264Enc, gst_omx_h264enc, GstOmxBaseVideoEnc, GST_OMX_BASE_VIDEOENC_TYPE); +/* + * description : convert byte-stream format to packetized frame + * params : @self : GstOmxH264Enc, @buf: byte-stream buf, @sync: notify this buf is sync frame + * return : none + * comments : + */ +static void +convert_to_packetized_frame (GstOmxH264Enc *self, GstBuffer **buf) +{ + unsigned char *data = GST_BUFFER_DATA (*buf); + unsigned int size = GST_BUFFER_SIZE(*buf); + int idx = 0; + gint start_idx = -1; + unsigned char *nalu_start = NULL; + GstOmxBaseFilter *omx_base = GST_OMX_BASE_FILTER(self); + + GST_LOG_OBJECT (self, "convert_to_packtized format. size=%d sliceMode=%d", + GST_BUFFER_SIZE(*buf), self->slice_fmo.eSliceMode); + + if (omx_base->gomx->component_vendor == GOMX_VENDOR_SLSI && + self->slice_fmo.eSliceMode == OMX_VIDEO_SLICEMODE_AVCDefault) { /* 1 slice per frame */ + GST_LOG_OBJECT (self, " handle single NALU per buffer"); + while (idx < size - GSTOMX_H264_NAL_START_LEN) { + if (((data[idx] == 0x00) && (data[idx+1] == 0x00) && (data[idx+2] == 0x00)&& (data[idx+3] == 0x01)) || + ((data[idx] == 0x00) && (data[idx+1] == 0x00) && (data[idx+2] == 0x01))) { + if (data[idx+2] == 0x01) { + GST_ERROR_OBJECT (self, "ERROR : NALU header is 3-bytes, yet to support !!"); + } else { + GSTOMX_H264_WB32(data + idx, size - idx - GSTOMX_H264_NAL_START_LEN); + return; + } + } + idx++; + } /* while */ + } else { /* handle multiple NALUs in one buffer */ + GST_LOG_OBJECT (self, " handle multiple NALUs per buffer"); + while (idx < size - GSTOMX_H264_NAL_START_LEN) { + if (((data[idx] == 0x00) && (data[idx+1] == 0x00) && (data[idx+2] == 0x00)&& (data[idx+3] == 0x01)) || + ((data[idx] == 0x00) && (data[idx+1] == 0x00) && (data[idx+2] == 0x01))) { + if (data[idx+2] == 0x01) { + GST_ERROR_OBJECT (self, "ERROR : NALU header is 3-bytes, yet to support !!"); + } else { + if (start_idx >= 0) { + if (idx <= start_idx) { + GST_ERROR_OBJECT (self, "ERROR : idx <= start_idx !!"); + return; + } + GST_LOG_OBJECT (self, "size of current nal unit = %d", idx-start_idx); + GSTOMX_H264_WB32(nalu_start, idx - start_idx - GSTOMX_H264_NAL_START_LEN); + } + start_idx = idx; + nalu_start = data + idx; + idx++; + } + } + idx++; + } /* while */ + idx += GSTOMX_H264_NAL_START_LEN; + + /* converting last nal unit */ + if (start_idx >= 0) { + GST_LOG_OBJECT (self, "size of current nal unit = %d", idx-start_idx); + GSTOMX_H264_WB32(nalu_start, idx - start_idx - GSTOMX_H264_NAL_START_LEN); + } + } +} + +/* + * description : convert byte-stream codec data to packtized codec_data + * params : @self : GstOmxH264Enc, @inbuf: byte-stream codec data (omx buf), @outbuf: packetized codec_data + * return : true on successes / false on failure + * comments : + */ +static gboolean +convert_to_packetized_dci (GstOmxH264Enc *self, unsigned char *nalu_dci, unsigned nalu_dci_len, + GstBuffer **packetized_dci, gint *out_sps_cnt, gint *out_pps_cnt) +{ + gint idx = 0; + gint sps_cnt = 0; + gint pps_cnt = 0; + GQueue *sps_queue = 0; + GQueue *pps_queue = 0; + unsigned char *packet_dci = NULL; + gint prev_nalu_start = 0; + gint prev_nalu_type = GSTOMX_H264_NUT_UNKNOWN; + gint sps_size = 0; + gint pps_size = 0; + GstBuffer *sps_data = NULL; + GstBuffer *pps_data = NULL; + GstBuffer *queue_data = NULL; + gint nal_type = GSTOMX_H264_NUT_UNKNOWN; + unsigned char profile = 0; + unsigned char level = 0; + unsigned char profile_comp = 0; + gboolean bret = TRUE; + gboolean single_sps_pps = FALSE; /* if there is only 1 sps, pps set, */ + + sps_queue = g_queue_new (); + pps_queue = g_queue_new (); + + /* find no.of SPS & PPS units */ + while (idx < nalu_dci_len) { + if ((nalu_dci[idx] == 0x00) && (nalu_dci[idx+1] == 0x00) && (nalu_dci[idx+2] == 0x01)) { + /* copy previous nal unit */ + if (prev_nalu_start && prev_nalu_type == GSTOMX_H264_NUT_SPS) { + if (nalu_dci[idx -1] == 0x00) { + sps_size = idx -1 - prev_nalu_start; + } else { + sps_size = idx - prev_nalu_start; + } + sps_data = gst_buffer_new_and_alloc (sps_size + GSTOMX_H264_C_DCI_LEN); + if (!sps_data) { + GST_ERROR_OBJECT (self, "failed to allocate memory.."); + bret = FALSE; + goto exit; + } + GSTOMX_H264_WB16(GST_BUFFER_DATA(sps_data), sps_size); + memcpy (GST_BUFFER_DATA(sps_data) + GSTOMX_H264_C_DCI_LEN, nalu_dci + prev_nalu_start, sps_size); + g_queue_push_tail (sps_queue, sps_data); + } else if (prev_nalu_start && prev_nalu_type == GSTOMX_H264_NUT_PPS) { + if (nalu_dci[idx -1] == 0x00) { + pps_size = idx -1 - prev_nalu_start; + } else { + pps_size = idx - prev_nalu_start; + } + pps_data = gst_buffer_new_and_alloc (pps_size + GSTOMX_H264_C_DCI_LEN); + if (!pps_data) { + GST_ERROR_OBJECT (self, "failed to allocate memory.."); + bret = FALSE; + goto exit; + } + GSTOMX_H264_WB16(GST_BUFFER_DATA(pps_data), pps_size); + memcpy (GST_BUFFER_DATA(pps_data) + GSTOMX_H264_C_DCI_LEN, nalu_dci + prev_nalu_start, pps_size); + g_queue_push_tail (pps_queue, pps_data); + } + /* present nalu type */ + nal_type = nalu_dci[idx+3] & 0x1f; + + if (nal_type == GSTOMX_H264_NUT_SPS) { + sps_cnt++; + prev_nalu_start = idx + 3; + prev_nalu_type =GSTOMX_H264_NUT_SPS; + profile = nalu_dci[idx+4]; + level = nalu_dci[idx+6]; + GST_INFO_OBJECT (self, "Profile Number = %d and Level = %d...", nalu_dci[idx+4], level); + GST_INFO_OBJECT (self, "Profile is %s", (profile == 66) ? "BaseLine Profile": (profile == 77)? "Main Profile": profile==88 ? + "Extended Profile": profile==100 ? "High Profile": "Not Supported codec"); + } else if ((nalu_dci[idx+3] & 0x1f) == GSTOMX_H264_NUT_PPS) { + pps_cnt++; + prev_nalu_start = idx + 3; + prev_nalu_type = GSTOMX_H264_NUT_PPS; + } + } + idx++; + } + + /* copy previous nal unit */ + if (prev_nalu_start && prev_nalu_type == GSTOMX_H264_NUT_SPS) { + sps_size = idx - prev_nalu_start; + + sps_data = gst_buffer_new_and_alloc (sps_size + GSTOMX_H264_C_DCI_LEN); + if (!sps_data) { + GST_ERROR_OBJECT (self, "failed to allocate memory.."); + bret = FALSE; + goto exit; + } + + GSTOMX_H264_WB16(GST_BUFFER_DATA(sps_data), sps_size); + memcpy (GST_BUFFER_DATA(sps_data) + GSTOMX_H264_C_DCI_LEN, nalu_dci + prev_nalu_start, sps_size); + g_queue_push_tail (sps_queue, sps_data); + + } else if (prev_nalu_start && prev_nalu_type == GSTOMX_H264_NUT_PPS) { + pps_size = idx - prev_nalu_start; + + pps_data = gst_buffer_new_and_alloc (pps_size + GSTOMX_H264_C_DCI_LEN); + if (!pps_data) { + GST_ERROR_OBJECT (self, "failed to allocate memory.."); + bret = FALSE; + goto exit; + } + + GSTOMX_H264_WB16(GST_BUFFER_DATA(pps_data), pps_size); + memcpy (GST_BUFFER_DATA(pps_data) + GSTOMX_H264_C_DCI_LEN, nalu_dci + prev_nalu_start, pps_size); + g_queue_push_tail (pps_queue, pps_data); + } + + /* make packetized codec data */ + if (sps_cnt == 1 && pps_cnt == 1) { + single_sps_pps = TRUE; + *packetized_dci = gst_buffer_new_and_alloc(GSTOMX_H264_MDATA + GSTOMX_H264_C_DCI_LEN + sps_size + GSTOMX_H264_CNT_LEN + GSTOMX_H264_C_DCI_LEN + pps_size); + GST_LOG_OBJECT(self, "allocate packetized_dci in case of single sps, pps. (size=%d)", GST_BUFFER_SIZE(*packetized_dci)); + } else { + *packetized_dci = gst_buffer_new_and_alloc(GSTOMX_H264_MDATA); + GST_LOG_OBJECT(self, "allocate packetized_dci in case of multi sps, pps"); + } + + if (*packetized_dci == NULL) { + GST_ERROR_OBJECT (self, "Failed to allocate memory.."); + bret = FALSE; + goto exit; + } + + packet_dci = GST_BUFFER_DATA(*packetized_dci); + packet_dci[0] = 0x01; /* configurationVersion */ + packet_dci[1] = profile; /* AVCProfileIndication */ + packet_dci[2] = profile_comp; /* profile_compatibility*/ + packet_dci[3] = level; /* AVCLevelIndication */ + packet_dci[4] = 0xff; /* Reserver (6bits.111111) + LengthSizeMinusOne (2bits). lengthoflength = 4 for present */ + packet_dci[5] = 0xe0 | sps_cnt; /* Reserver (3bits. 111) + numOfSequenceParameterSets (5bits) */ + + /* copy SPS sets */ + while (!g_queue_is_empty (sps_queue)) { + sps_data = g_queue_pop_head (sps_queue); + + if (TRUE == single_sps_pps) { + memcpy(packet_dci + GSTOMX_H264_MDATA, GST_BUFFER_DATA(sps_data), GST_BUFFER_SIZE(sps_data)); + gst_buffer_unref(sps_data); + sps_data = NULL; + } else { + *packetized_dci = gst_buffer_join(*packetized_dci, sps_data); + } + } + + /* add number of PPS sets (1byte)*/ + if (TRUE == single_sps_pps) { + packet_dci[GSTOMX_H264_MDATA + GSTOMX_H264_C_DCI_LEN + sps_size] = pps_cnt; + } else { + GstBuffer *next_data = gst_buffer_new_and_alloc(GSTOMX_H264_CNT_LEN); + if (!next_data) { + GST_ERROR_OBJECT (self, "Failed to allocate memory.."); + bret = FALSE; + goto exit; + } + GST_BUFFER_DATA(next_data)[0] = pps_cnt; + *packetized_dci = gst_buffer_join(*packetized_dci, next_data); + } + + /* copy PPS sets */ + while (!g_queue_is_empty (pps_queue)) { + pps_data = g_queue_pop_head (pps_queue); + + if (TRUE == single_sps_pps) { + memcpy(packet_dci + GSTOMX_H264_MDATA + GSTOMX_H264_C_DCI_LEN + sps_size + GSTOMX_H264_CNT_LEN, + GST_BUFFER_DATA(pps_data), GST_BUFFER_SIZE(pps_data)); + gst_buffer_unref(pps_data); + pps_data = NULL; + } else { + *packetized_dci = gst_buffer_join(*packetized_dci, pps_data); + } + } + +exit: + while (!g_queue_is_empty (sps_queue)) { + queue_data = g_queue_pop_head (sps_queue); + gst_buffer_unref (queue_data); + queue_data = NULL; + } + g_queue_free (sps_queue); + + while (!g_queue_is_empty (pps_queue)) { + queue_data = g_queue_pop_head (pps_queue); + gst_buffer_unref (queue_data); + queue_data = NULL; + } + g_queue_free (pps_queue); + + *out_sps_cnt = sps_cnt; + *out_pps_cnt = sps_cnt; + + return bret; +} + +/* + * description : resotre packtized dci (codec_data) + * params : @self : GstOmxH264Enc, @inbuf: codec data, @outbuf: h264enc->dci + * return : none + * comments : + * from original packetized codec_data: METADATA(6) + SPS_CNT(0) + {SPS_SIZE(2)+SPS_DATA(sps_size)}*n + + PPS_CNT(1) + {PPS_SIZE(2)+PPS_DATA(pps_size)}*n + * to estore packetized dci: {SPS_SIZE(4)+SPS_DATA(sps_size)}*n + {PPS_SIZE(4)+PPS_DATA(pps_size)}*n + */ +static gboolean +restore_packetized_dci (GstOmxH264Enc *self, GstBuffer *inbuf, GstBuffer **outbuf, gint sps_cnt, gint pps_cnt) +{ + unsigned char *indata = GST_BUFFER_DATA(inbuf); + guint sps_size =0; + guint pps_size =0; + gint i = 0; + GstBuffer *next_data = NULL; + + GST_INFO_OBJECT (self, "restore_packetized_dci. sps_cnt=%d, pps_cnt=%d", sps_cnt, pps_cnt); + + if (sps_cnt == 1 && pps_cnt == 1) { + sps_size = GSTOMX_H264_RB16(indata + GSTOMX_H264_MDATA); + pps_size = GSTOMX_H264_RB16(indata + GSTOMX_H264_MDATA + GSTOMX_H264_C_DCI_LEN + sps_size + GSTOMX_H264_CNT_LEN); + + *outbuf = gst_buffer_new_and_alloc (GSTOMX_H264_A_DCI_LEN + sps_size + GSTOMX_H264_A_DCI_LEN + pps_size); + if (!*outbuf) { + GST_ERROR_OBJECT (self, "Failed to allocate memory in gst_buffer_new_and_alloc."); + goto error_exit; + } + + GSTOMX_H264_WB32(GST_BUFFER_DATA(*outbuf), sps_size); + indata += GSTOMX_H264_MDATA + GSTOMX_H264_C_DCI_LEN; + memcpy (GST_BUFFER_DATA(*outbuf) + GSTOMX_H264_A_DCI_LEN, indata, sps_size); + indata += sps_size; + + GSTOMX_H264_WB32(GST_BUFFER_DATA(*outbuf) + GSTOMX_H264_A_DCI_LEN + sps_size, pps_size); + indata += GSTOMX_H264_CNT_LEN + GSTOMX_H264_C_DCI_LEN; + memcpy (GST_BUFFER_DATA(*outbuf) + GSTOMX_H264_A_DCI_LEN + sps_size + GSTOMX_H264_A_DCI_LEN, indata, pps_size); + indata += pps_size; + } else { + /* in this case, dci has multi nalu. ex) sps + sps + sps + pps + pps */ + indata += GSTOMX_H264_MDATA; + *outbuf = gst_buffer_new (); + + for (i = 0; i < sps_cnt; i++) { + sps_size = GSTOMX_H264_RB16(indata); /* metadata(6) */ + + next_data = gst_buffer_new_and_alloc(GSTOMX_H264_A_DCI_LEN + sps_size); + if (!next_data) { + GST_ERROR_OBJECT (self, "Failed to allocate memory in gst_buffer_new_and_alloc."); + goto error_exit; + } + GSTOMX_H264_WB32(GST_BUFFER_DATA(next_data), sps_size); + indata += GSTOMX_H264_C_DCI_LEN; /* sps size field (2 byte) */ + memcpy (GST_BUFFER_DATA(next_data) + GSTOMX_H264_A_DCI_LEN, indata, sps_size); + + *outbuf = gst_buffer_join(*outbuf, next_data); + indata += sps_size; + } + indata += GSTOMX_H264_CNT_LEN; /* pps cnt field (1 byte) */ + + for (i = 0; i < pps_cnt; i++) { + pps_size = GSTOMX_H264_RB16(indata); + + next_data = gst_buffer_new_and_alloc(GSTOMX_H264_A_DCI_LEN + pps_size); + if (!next_data) { + GST_ERROR_OBJECT (self, "Failed to allocate memory in gst_buffer_new_and_alloc."); + goto error_exit; + } + GSTOMX_H264_WB32(GST_BUFFER_DATA(next_data), pps_size); + indata += GSTOMX_H264_C_DCI_LEN; + memcpy (GST_BUFFER_DATA(next_data) + GSTOMX_H264_A_DCI_LEN, indata, pps_size); + + *outbuf = gst_buffer_join(*outbuf, next_data); + indata += pps_size; + } + } + + return TRUE; + +error_exit: + if (*outbuf) { + gst_buffer_unref(*outbuf); + *outbuf = NULL; + } + return FALSE; +} + +/* + * description : set sync frame. if needed, convert output to packetized format, append dci every I frame. + * params : @self : GstOmxBaseFilter, @buf: encoder output frame, @omx_buffer: omx_buffer + * return : none + * comments : + */ +static void +process_output_buf(GstOmxBaseFilter * omx_base, GstBuffer **buf, OMX_BUFFERHEADERTYPE *omx_buffer) +{ + GstOmxH264Enc *self; + self = GST_OMX_H264ENC (omx_base); + + if (!self->byte_stream) { /* Packtized Format */ + convert_to_packetized_frame (self, buf); /* convert byte stream to packetized stream */ + GST_LOG_OBJECT (self, "output buffer is converted to Packtized format."); + } else { + GST_LOG_OBJECT (self, "output buffer is Byte-stream format."); + } + + if ((self->first_frame) ||(self->append_dci && omx_buffer->nFlags & OMX_BUFFERFLAG_SYNCFRAME)) { + GstBuffer *newbuf = NULL; + GST_LOG_OBJECT (self, "append dci at %s by gst-openmax.", (self->first_frame == TRUE) ? "first frame": "every I frame"); + + if (self->dci == NULL) { + GST_ERROR_OBJECT (self, "dci is null. can not append dci."); + self->append_dci = FALSE; + } else { + newbuf = gst_buffer_merge (self->dci, *buf); + if (newbuf == NULL) { + GST_ERROR_OBJECT (self, "Failed to gst_buffer_merge."); + return; + } + gst_buffer_copy_metadata(newbuf, *buf, GST_BUFFER_COPY_ALL); + if (*buf) + gst_buffer_unref(*buf); + *buf = newbuf; + } + } + + if (self->first_frame == TRUE) + self->first_frame = FALSE; + + /* Set sync frame info while encoding */ + if (omx_buffer->nFlags & OMX_BUFFERFLAG_SYNCFRAME) { + GST_BUFFER_FLAG_UNSET(*buf, GST_BUFFER_FLAG_DELTA_UNIT); + } else { + GST_BUFFER_FLAG_SET(*buf, GST_BUFFER_FLAG_DELTA_UNIT); + } +} + +/* + * description : if needed, convert byte-stream codec_data to packetized format, save dci. + * params : @self : GstOmxBaseFilter, @omx_buffer: omx_buffer + * return : none + * comments : + */ +static void +process_output_caps(GstOmxBaseFilter * base, OMX_BUFFERHEADERTYPE *omx_buffer) +{ + GstOmxH264Enc *h264enc; + gboolean ret = FALSE; + + h264enc = GST_OMX_H264ENC (base); + + if (!h264enc->byte_stream) { /* Packtized Format */ + GstCaps *caps = NULL; + GstStructure *structure; + GstBuffer *codec_data = NULL; + gint sps_cnt = 0; + gint pps_cnt = 0; + GValue value = { 0, {{0} + } + }; + g_value_init (&value, GST_TYPE_BUFFER); + + GST_INFO_OBJECT (h264enc, "Packtized Format: set src caps with codec-data"); + + /* convert codec_data to packtized format.. (metadata(cnt) + sps_size + sps + cnt + pps_size + pps) */ + ret = convert_to_packetized_dci (h264enc, omx_buffer->pBuffer + omx_buffer->nOffset, omx_buffer->nFilledLen, + &codec_data, &sps_cnt, &pps_cnt); + + if (FALSE == ret) { + GST_ERROR_OBJECT (h264enc, "ERROR: convert to packetized dci"); + if (codec_data) { + gst_buffer_unref(codec_data); + codec_data = NULL; + } + return; + } + + /* restore packtized format sps, pps */ + ret = restore_packetized_dci (h264enc, codec_data, &h264enc->dci, sps_cnt, pps_cnt); + if (ret == FALSE) { + GST_ERROR_OBJECT (h264enc, "ERROR: restore packetized dci"); + return; + } + + GST_DEBUG_OBJECT (h264enc, "adding codec_data in caps"); + caps = gst_pad_get_negotiated_caps (base->srcpad); + caps = gst_caps_make_writable (caps); + structure = gst_caps_get_structure (caps, 0); + + /* packetized format, set codec_data field in caps */ + gst_value_set_buffer (&value,codec_data); + gst_buffer_unref (codec_data); + codec_data = NULL; + gst_structure_set_value (structure, "codec_data", &value); + g_value_unset (&value); + + gst_pad_set_caps (base->srcpad, caps); + gst_caps_unref (caps); + } else { + /* byte-stream format */ + GST_INFO_OBJECT (h264enc, "Byte-stream Format"); + h264enc->dci = gst_buffer_new_and_alloc (omx_buffer->nFilledLen); + if (!h264enc->dci) { + GST_ERROR_OBJECT (h264enc, "failed to allocate memory..."); + return; + } + memcpy (GST_BUFFER_DATA (h264enc->dci), + omx_buffer->pBuffer + omx_buffer->nOffset, omx_buffer->nFilledLen); + } +} + +static void +finalize (GObject * obj) +{ + GstOmxH264Enc *h264enc; + h264enc = GST_OMX_H264ENC (obj); + + GST_LOG_OBJECT (h264enc, "h264enc finalize enter"); + + if (h264enc->dci) { + gst_buffer_unref(h264enc->dci); + h264enc->dci = NULL; + } + G_OBJECT_CLASS (parent_class)->finalize (obj); +} + static void type_base_init (gpointer g_class) { @@ -47,8 +555,144 @@ type_base_init (gpointer g_class) } static void +set_property (GObject * obj, + guint prop_id, const GValue * value, GParamSpec * pspec) +{ + GstOmxH264Enc *self; + GstOmxBaseFilter *omx_base; + + self = GST_OMX_H264ENC (obj); + omx_base = GST_OMX_BASE_FILTER (obj); + + switch (prop_id) { + case ARG_APPEND_DCI: + self->append_dci = g_value_get_boolean (value); + GST_INFO_OBJECT (self, "Appending dci = %d", self->append_dci); + break; + + case ARG_BYTE_STREAM: + self->byte_stream = g_value_get_boolean (value); + GST_INFO_OBJECT (self, "Byte stream = %d", self->byte_stream); + break; + + case ARG_SLICE_MODE: + { + OMX_VIDEO_PARAM_AVCSLICEFMO param; + OMX_ERRORTYPE ret; + + self->slice_fmo.eSliceMode = g_value_get_uint (value); + + G_OMX_INIT_PARAM (param); + param.nPortIndex = omx_base->out_port->port_index; + OMX_GetParameter (omx_base->gomx->omx_handle, OMX_IndexParamVideoSliceFMO, ¶m); + + param.eSliceMode = self->slice_fmo.eSliceMode; + param.nNumSliceGroups = 1; + param.nSliceGroupMapType = 1; + ret = OMX_SetParameter (omx_base->gomx->omx_handle, OMX_IndexParamVideoSliceFMO, ¶m); + if (ret != OMX_ErrorNone) + GST_ERROR_OBJECT (self, "failed to set eSliceMode = %d", self->slice_fmo.eSliceMode); + else + GST_INFO_OBJECT (self, "eSliceMode = %d", self->slice_fmo.eSliceMode); + } + break; + + case ARG_SLICE_SIZE: + { + OMX_VIDEO_PARAM_AVCTYPE param; + OMX_ERRORTYPE ret; + + self->h264type.nSliceHeaderSpacing = g_value_get_uint (value); + + G_OMX_INIT_PARAM (param); + param.nPortIndex = omx_base->out_port->port_index; + OMX_GetParameter (omx_base->gomx->omx_handle, OMX_IndexParamVideoAvc, ¶m); + + param.nSliceHeaderSpacing = self->h264type.nSliceHeaderSpacing; + ret= OMX_SetParameter (omx_base->gomx->omx_handle, OMX_IndexParamVideoAvc, ¶m); + if (ret != OMX_ErrorNone) + GST_ERROR_OBJECT (self, "failed to set nSliceHeaderSpacing = %d", self->h264type.nSliceHeaderSpacing); + else + GST_INFO_OBJECT (self, "nSliceHeaderSpacing = %d", self->h264type.nSliceHeaderSpacing); + } + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, prop_id, pspec); + break; + } +} + +static void +get_property (GObject * obj, guint prop_id, GValue * value, GParamSpec * pspec) +{ + GstOmxH264Enc *self; + + self = GST_OMX_H264ENC (obj); + + switch (prop_id) { + case ARG_APPEND_DCI: + g_value_set_boolean (value, self->append_dci); + break; + case ARG_BYTE_STREAM: + g_value_set_boolean (value, self->byte_stream); + break; + case ARG_SLICE_MODE: + g_value_set_uint (value, self->slice_fmo.eSliceMode); + break; + case ARG_SLICE_SIZE: + g_value_set_uint (value, self->h264type.nSliceHeaderSpacing); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, prop_id, pspec); + break; + } +} + +static void type_class_init (gpointer g_class, gpointer class_data) { + GObjectClass *gobject_class; + GstOmxBaseFilterClass *basefilter_class; + + gobject_class = G_OBJECT_CLASS (g_class); + basefilter_class = GST_OMX_BASE_FILTER_CLASS (g_class); + + { + gobject_class->set_property = set_property; + gobject_class->get_property = get_property; + + g_object_class_install_property (gobject_class, ARG_APPEND_DCI, + g_param_spec_boolean ("append-dci", "append codec_data with every key frame for byte-stream format", + "append codec_data with every key frame for byte-stream format", + FALSE, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + + g_object_class_install_property (gobject_class, ARG_BYTE_STREAM, + g_param_spec_boolean ("byte-stream", "Output stream format", + "output stream format whether byte-stream format (TRUE) or packetized (FALSE)", + FALSE, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + + g_object_class_install_property (gobject_class, ARG_SLICE_MODE, + g_param_spec_uint ("slice-mode", "H264 encoder slice mode", + "slice mode: 0-Disable, 1-Fixed MB num slice, 2-Fixed bit num slice", + 0, G_MAXUINT, 0, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + + g_object_class_install_property (gobject_class, ARG_SLICE_SIZE, + g_param_spec_uint ("slice-size", "H264 encoder slice size", + "MB or bit num: MB number:1 ~ (MBCnt-1), Bit number: 1900 (bit) ~", + 0, G_MAXUINT, 0, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + } + + basefilter_class->process_output_buf = process_output_buf; + basefilter_class->process_output_caps = process_output_caps; + + gobject_class->finalize = finalize; + } static void @@ -56,11 +700,14 @@ settings_changed_cb (GOmxCore * core) { GstOmxBaseVideoEnc *omx_base; GstOmxBaseFilter *omx_base_filter; + GstOmxH264Enc *self; + guint width; guint height; omx_base_filter = core->object; omx_base = GST_OMX_BASE_VIDEOENC (omx_base_filter); + self = GST_OMX_H264ENC (omx_base_filter); GST_DEBUG_OBJECT (omx_base, "settings changed"); @@ -77,7 +724,9 @@ settings_changed_cb (GOmxCore * core) } { - GstCaps *new_caps; + GstCaps *new_caps, *peer_caps; + gchar *format ; + int i =0; new_caps = gst_caps_new_simple ("video/x-h264", "width", G_TYPE_INT, width, @@ -85,6 +734,40 @@ settings_changed_cb (GOmxCore * core) "framerate", GST_TYPE_FRACTION, omx_base->framerate_num, omx_base->framerate_denom, NULL); + /* get peer pad caps */ + peer_caps = gst_pad_peer_get_caps(omx_base_filter->srcpad); + if (peer_caps) { + GST_LOG_OBJECT (omx_base, "peer caps : %s\n", gst_caps_to_string(peer_caps)); + + for (i = 0; i < peer_caps->structs->len; i++) { + GstStructure *s; + s = gst_caps_get_structure (peer_caps, i); + + if (g_strrstr (gst_structure_get_name (s), "video/x-h264")) { + if (!gst_structure_get (s, "stream-format", G_TYPE_STRING, &format, NULL)) { + GST_WARNING_OBJECT (self, "Failed to get stream-format from peer caps..."); + } else { + GST_INFO_OBJECT (self, "format : %s", format); + gst_caps_set_simple (new_caps, "stream-format", G_TYPE_STRING, format, NULL); + if (g_strrstr(format, "byte-stream")) { + GST_INFO_OBJECT (self, "Mandatory BYTE_STREAM"); + self->byte_stream = TRUE; + } else if (g_strrstr(format, "avc")){ + GST_INFO_OBJECT (self, "Mandatory PACKETIZED"); + self->byte_stream = FALSE; + } else { + GST_INFO_OBJECT (self, "Nothing mentioned about stream-format... use byte_stream property to decide"); + if (self->byte_stream) { + GST_INFO_OBJECT (self, "Is in BYTE_STREAM"); + } else { + GST_INFO_OBJECT (self, "Is in PACKETIZED"); + } + } + } + } + } + } + GST_INFO_OBJECT (omx_base, "caps are: %" GST_PTR_FORMAT, new_caps); gst_pad_set_caps (omx_base_filter->srcpad, new_caps); } @@ -95,9 +778,17 @@ type_instance_init (GTypeInstance * instance, gpointer g_class) { GstOmxBaseFilter *omx_base_filter; GstOmxBaseVideoEnc *omx_base; + GstOmxH264Enc *self; omx_base_filter = GST_OMX_BASE_FILTER (instance); omx_base = GST_OMX_BASE_VIDEOENC (instance); + self = GST_OMX_H264ENC (instance); + + self->byte_stream = FALSE; + self->append_dci = FALSE; + self->first_frame = TRUE; + self->dci = NULL; + self->slice_fmo.eSliceMode = OMX_VIDEO_SLICEMODE_AVCLevelMax; omx_base->compression_format = OMX_VIDEO_CodingAVC; diff --git a/omx/gstomx_h264enc.h b/omx/gstomx_h264enc.h index 05258a5..561faec 100644 --- a/omx/gstomx_h264enc.h +++ b/omx/gstomx_h264enc.h @@ -31,10 +31,19 @@ typedef struct GstOmxH264Enc GstOmxH264Enc; typedef struct GstOmxH264EncClass GstOmxH264EncClass; #include "gstomx_base_videoenc.h" +#include "gstomx_h264.h" struct GstOmxH264Enc { GstOmxBaseVideoEnc omx_base; + gboolean byte_stream; + + GstBuffer *dci; + gboolean append_dci; + gboolean first_frame; + + OMX_VIDEO_PARAM_AVCTYPE h264type; + OMX_VIDEO_PARAM_AVCSLICEFMO slice_fmo; }; struct GstOmxH264EncClass diff --git a/omx/gstomx_mpeg4dec.c b/omx/gstomx_mpeg4dec.c index 0a11bb7..d99b9c2 100644 --- a/omx/gstomx_mpeg4dec.c +++ b/omx/gstomx_mpeg4dec.c @@ -118,7 +118,7 @@ error_exit: } static void -process_input_buf (GstOmxBaseFilter * omx_base_filter, GstBuffer * buf) +process_input_buf (GstOmxBaseFilter * omx_base_filter, GstBuffer **buf) { GstOmxMpeg4Dec *self; @@ -128,10 +128,10 @@ process_input_buf (GstOmxBaseFilter * omx_base_filter, GstBuffer * buf) /* decrypt DivX DRM buffer if this is DRM */ if (self->drmContext) { - if (DRM_SUCCESS == self->divx_sym_table.decrypt_video (self->drmContext, GST_BUFFER_DATA(buf), GST_BUFFER_SIZE(buf))) { - GST_DEBUG_OBJECT (self, "##### DivX DRM Mode ##### decrypt video success : buffer = %d", GST_BUFFER_SIZE(buf)); + if (DRM_SUCCESS == self->divx_sym_table.decrypt_video (self->drmContext, GST_BUFFER_DATA(*buf), GST_BUFFER_SIZE(*buf))) { + GST_DEBUG_OBJECT (self, "##### DivX DRM Mode ##### decrypt video success : buffer = %d", GST_BUFFER_SIZE(*buf)); } else { - GST_ERROR_OBJECT (self, "##### DivX DRM Mode ##### decrypt video failed : buffer = %d", GST_BUFFER_SIZE(buf)); + GST_ERROR_OBJECT (self, "##### DivX DRM Mode ##### decrypt video failed : buffer = %d", GST_BUFFER_SIZE(*buf)); } } @@ -174,7 +174,7 @@ print_tag (const GstTagList * list, const gchar * tag, gpointer data) } if (i == 0) { - g_print ("%16s: %s\n", gst_tag_get_nick (tag), str); + GST_LOG_OBJECT(self, "%16s: %s", gst_tag_get_nick (tag), str); if (strcmp (gst_tag_get_nick(tag), "DRM DivX") == 0) { if (self->drmContext == NULL) { @@ -190,7 +190,6 @@ print_tag (const GstTagList * list, const gchar * tag, gpointer data) } } else { GST_LOG_OBJECT(self, "tag is not DRM Divx"); - g_print ("%16s: %s\n", "", str); } g_free (str); @@ -299,6 +298,6 @@ type_instance_init (GTypeInstance * instance, gpointer g_class) omx_base = GST_OMX_BASE_VIDEODEC (instance); omx_base_filter = GST_OMX_BASE_FILTER (instance); - omx_base_filter->omx_event = mpeg4_pad_event; + omx_base_filter->pad_event = mpeg4_pad_event; omx_base->compression_format = OMX_VIDEO_CodingMPEG4; } diff --git a/omx/gstomx_util.c b/omx/gstomx_util.c index 438c41b..e6cf228 100644 --- a/omx/gstomx_util.c +++ b/omx/gstomx_util.c @@ -275,6 +275,7 @@ g_omx_core_free (GOmxCore * core) g_ptr_array_free (core->ports, TRUE); g_free (core); + core = NULL; } void @@ -313,11 +314,15 @@ g_omx_core_init (GOmxCore * core) ¶m); } - /* Add_component_vendor */ + /* MODIFICATION: Add_component_vendor */ if (strncmp(core->component_name+4, "SEC", 3) == 0) { core->component_vendor = GOMX_VENDOR_SLSI; } + else if (strncmp(core->component_name+4, "qcom", 4) == 0) + { + core->component_vendor = GOMX_VENDOR_QCT; + } else { core->component_vendor = GOMX_VENDOR_DEFAULT; @@ -345,6 +350,9 @@ core_deinit (GOmxCore * core) g_free (core->library_name); g_free (core->component_name); g_free (core->component_role); + core->library_name = NULL; + core->component_name = NULL; + core->component_role = NULL; release_imp (core->imp); core->imp = NULL; @@ -477,6 +485,7 @@ g_omx_port_new (GOmxCore * core, guint index) port->num_buffers = 0; port->buffer_size = 0; port->buffers = NULL; + port->shared_buffer = FALSE; port->enabled = TRUE; port->queue = async_queue_new (); @@ -492,7 +501,9 @@ g_omx_port_free (GOmxPort * port) async_queue_free (port->queue); g_free (port->buffers); + port->buffers = NULL; g_free (port); + port = NULL; } void @@ -561,19 +572,40 @@ port_free_buffers (GOmxPort * port) { guint i; + if (port->type == GOMX_PORT_INPUT) { + GST_INFO_OBJECT(port->core->object, "Input port free buffers."); + } else { + GST_INFO_OBJECT(port->core->object, "Output port free buffers."); + } + for (i = 0; i < port->num_buffers; i++) { OMX_BUFFERHEADERTYPE *omx_buffer; omx_buffer = port->buffers[i]; if (omx_buffer) { -#if 0 - /** @todo how shall we free that buffer? */ - if (!port->omx_allocate) { - g_free (omx_buffer->pBuffer); - omx_buffer->pBuffer = NULL; + + if (port->shared_buffer) { + /* Modification: free pAppPrivate when input/output buffer is shared */ + GST_INFO_OBJECT(port->core->object, + " %d: unref shared buffer (pAppPrivate) %p", i, omx_buffer->pAppPrivate); + if(omx_buffer->pAppPrivate) { + gst_buffer_unref(omx_buffer->pAppPrivate); + omx_buffer->pAppPrivate = NULL; + } + + } else { /* this is not shared buffer */ + if (!port->omx_allocate) { + /* Modification: free pBuffer allocated in plugin when OMX_UseBuffer. + * the component shall free only buffer header if it allocated only buffer header.*/ + GST_INFO_OBJECT(port->core->object, + " %d: free buffer (pBuffer) %p", i, omx_buffer->pBuffer); + if (omx_buffer->pBuffer) { + g_free (omx_buffer->pBuffer); + omx_buffer->pBuffer = NULL; + } + } } -#endif OMX_FreeBuffer (port->core->omx_handle, port->port_index, omx_buffer); port->buffers[i] = NULL; diff --git a/omx/gstomx_util.h b/omx/gstomx_util.h index 9f1ac51..e547f63 100644 --- a/omx/gstomx_util.h +++ b/omx/gstomx_util.h @@ -37,6 +37,7 @@ typedef struct GOmxPort GOmxPort; typedef struct GOmxImp GOmxImp; typedef struct GOmxSymbolTable GOmxSymbolTable; typedef enum GOmxPortType GOmxPortType; +/* MODIFICATION: omx vender */ typedef enum GOmxVendor GOmxVendor; typedef void (*GOmxCb) (GOmxCore * core); @@ -54,7 +55,8 @@ enum GOmxPortType enum GOmxVendor { GOMX_VENDOR_DEFAULT, - GOMX_VENDOR_SLSI + GOMX_VENDOR_SLSI, + GOMX_VENDOR_QCT }; /* Structures. */ @@ -101,7 +103,8 @@ struct GOmxCore gchar *library_name; gchar *component_name; gchar *component_role; - GOmxVendor component_vendor; /* Add_component_vendor */ + /* MODIFICATION: omx vender */ + GOmxVendor component_vendor; }; struct GOmxPort @@ -118,6 +121,8 @@ struct GOmxPort gboolean enabled; gboolean omx_allocate; /**< Setup with OMX_AllocateBuffer rather than OMX_UseBuffer */ AsyncQueue *queue; + + gboolean shared_buffer; /* Modification */ }; /* Functions. */ diff --git a/packaging/gst-openmax.spec b/packaging/gst-openmax.spec index d3af874..874db25 100644 --- a/packaging/gst-openmax.spec +++ b/packaging/gst-openmax.spec @@ -1,7 +1,7 @@ Name: gst-openmax Summary: GStreamer plug-in that allows communication with OpenMAX IL components Version: 0.10.1 -Release: 0 +Release: 1 Group: TO_BE/FILLED_IN License: LGPLv2.1 Source0: %{name}-%{version}.tar.gz diff --git a/util/sem.c b/util/sem.c index 3336024..e8ded7f 100644 --- a/util/sem.c +++ b/util/sem.c @@ -42,6 +42,7 @@ g_sem_free (GSem * sem) g_cond_free (sem->condition); g_mutex_free (sem->mutex); g_free (sem); + sem = NULL; } void