From 7f99a3abbba84aae07e519efb99e668319ba9b77 Mon Sep 17 00:00:00 2001 From: Jinkun Jang Date: Wed, 13 Mar 2013 01:49:08 +0900 Subject: [PATCH] Tizen 2.1 base --- debian/changelog | 8 + debian/control | 2 +- gst-openmax.manifest | 5 + omx/Makefile.am | 2 +- omx/gstomx.c | 6 +- omx/gstomx.h | 2 +- omx/gstomx_base_audiodec.c | 61 ++++ omx/gstomx_base_filter.c | 595 ++++++++++++++++++++++++-------------- omx/gstomx_base_filter.h | 92 +++++- omx/gstomx_base_sink.c | 5 +- omx/gstomx_base_videodec.c | 79 +++++- omx/gstomx_base_videoenc.c | 137 ++++++++- omx/gstomx_base_videoenc.h | 4 +- omx/gstomx_h264.h | 83 ++++++ omx/gstomx_h264dec.c | 423 +++++++++++++++++++++++++++ omx/gstomx_h264dec.h | 4 + omx/gstomx_h264enc.c | 694 ++++++++++++++++++++++++++++++++++++++++++++- omx/gstomx_h264enc.h | 9 + omx/gstomx_mpeg4dec.c | 244 ++++++++++++++++ omx/gstomx_mpeg4dec.h | 28 ++ omx/gstomx_util.c | 56 +++- omx/gstomx_util.h | 9 +- packaging/gst-openmax.spec | 17 +- util/sem.c | 1 + 24 files changed, 2293 insertions(+), 273 deletions(-) create mode 100755 gst-openmax.manifest create mode 100644 omx/gstomx_h264.h diff --git a/debian/changelog b/debian/changelog index 7e1d203..889145c 100755 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,11 @@ +gst-openmax (0.10.1-1slp2+22) unstable; urgency=low + + * modify adapter memory leak and support dmabuf + * Git: pkgs/g/gst-openmax + * Tag: gst-openmax_0.10.1-1slp2+21 + + -- Sunghyun Eum Fri, 19 Oct 2012 18:33:12 +0900 + gst-openmax (0.10.1-1slp2+21) unstable; urgency=low * Initial Release. 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/gst-openmax.manifest b/gst-openmax.manifest new file mode 100755 index 0000000..a76fdba --- /dev/null +++ b/gst-openmax.manifest @@ -0,0 +1,5 @@ + + + + + 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..12019e4 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 @@ -242,7 +243,8 @@ plugin_init (GstPlugin * plugin) const gchar *element_name = gst_structure_nth_field_name (element_table, i); GstStructure *element = get_element_entry (element_name); const gchar *type_name, *parent_type_name; - const gchar *component_name, *component_role, *library_name; + const gchar *component_name, *library_name; +// const gchar *component_role; GType type; gint rank; @@ -252,7 +254,7 @@ plugin_init (GstPlugin * plugin) parent_type_name = gst_structure_get_string (element, "parent-type"); type_name = gst_structure_get_string (element, "type"); component_name = gst_structure_get_string (element, "component-name"); - component_role = gst_structure_get_string (element, "component-role"); +// component_role = gst_structure_get_string (element, "component-role"); library_name = gst_structure_get_string (element, "library-name"); if (!type_name || !component_name || !library_name) { diff --git a/omx/gstomx.h b/omx/gstomx.h index 3babcfa..136616f 100644 --- a/omx/gstomx.h +++ b/omx/gstomx.h @@ -29,7 +29,7 @@ GST_DEBUG_CATEGORY_EXTERN (gstomx_util_debug); #define GST_CAT_DEFAULT gstomx_debug /* Fix_config_path */ -#define OMX_CONFIG_DIRPATH "/opt/etc" +#define OMX_CONFIG_DIRPATH "/usr/etc" #define OMX_CONFIG_FILENAME "gst-openmax.conf" enum diff --git a/omx/gstomx_base_audiodec.c b/omx/gstomx_base_audiodec.c index 6f94b33..5d1cf5b 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 b8a4841..4bf8111 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 @@ -33,7 +33,6 @@ enum ARG_USE_TIMESTAMPS = GSTOMX_NUM_COMMON_PROP, ARG_NUM_INPUT_BUFFERS, ARG_NUM_OUTPUT_BUFFERS, - ARG_USE_STATETUNING, /* STATE_TUNING */ }; static void init_interfaces (GType type); @@ -41,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); } @@ -74,6 +73,8 @@ is_extended_color_format(GstOmxBaseFilter * self, GOmxPort * port) case OMX_EXT_COLOR_FormatNV12TPhysicalAddress: case OMX_EXT_COLOR_FormatNV12LPhysicalAddress: case OMX_EXT_COLOR_FormatNV12Tiled: + case OMX_EXT_COLOR_FormatNV12TFdValue: + case OMX_EXT_COLOR_FormatNV12LFdValue: return TRUE; default: return FALSE; @@ -96,22 +97,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"); } @@ -119,7 +126,105 @@ 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 +omx_change_state(GstOmxBaseFilter * self,GstOmxChangeState transition, GOmxPort *in_port, GstBuffer * buf) +{ + GOmxCore *gomx; + GstFlowReturn ret = GST_FLOW_OK; + + gomx = self->gomx; + + switch (transition) { + case GstOmx_LodedToIdle: + { + g_mutex_lock (self->ready_lock); + + GST_INFO_OBJECT (self, "omx: prepare"); + + /** @todo this should probably go after doing preparations. */ + if (self->omx_setup) { + self->omx_setup (self); + } + + setup_ports (self); + + g_omx_core_prepare (self->gomx); + + if (gomx->omx_state == OMX_StateIdle) { + self->ready = TRUE; + gst_pad_start_task (self->srcpad, output_loop, self->srcpad); + } + + g_mutex_unlock (self->ready_lock); + + if (gomx->omx_state != OMX_StateIdle) + goto out_flushing; + } + break; + + case GstOmx_IdleToExcuting: + { + GST_INFO_OBJECT (self, "omx: play"); + g_omx_core_start (gomx); + + if (gomx->omx_state != OMX_StateExecuting) + goto out_flushing; + + /* send buffer with codec data flag */ + /** @todo move to util */ + if (self->codec_data) { + OMX_BUFFERHEADERTYPE *omx_buffer; + + GST_LOG_OBJECT (self, "request buffer"); + omx_buffer = g_omx_port_request_buffer (in_port); + + if (G_LIKELY (omx_buffer)) { + omx_buffer->nFlags |= OMX_BUFFERFLAG_CODECCONFIG; + + omx_buffer->nFilledLen = GST_BUFFER_SIZE (self->codec_data); + memcpy (omx_buffer->pBuffer + omx_buffer->nOffset, + GST_BUFFER_DATA (self->codec_data), omx_buffer->nFilledLen); + + GST_LOG_OBJECT (self, "release_buffer"); + g_omx_port_release_buffer (in_port, omx_buffer); + } + } + } + break; + + default: + break; + } + +leave: + + GST_LOG_OBJECT (self, "end"); + return ret; + + /* special conditions */ +out_flushing: + { + const gchar *error_msg = NULL; + + if (gomx->omx_error) { + error_msg = "Error from OpenMAX component"; + } else if (gomx->omx_state != OMX_StateExecuting && + gomx->omx_state != OMX_StatePause) { + error_msg = "OpenMAX component in wrong state"; + } + + if (error_msg) { + GST_ELEMENT_ERROR (self, STREAM, FAILED, (NULL), ("%s", error_msg)); + ret = GST_FLOW_ERROR; + } + + if (buf) + gst_buffer_unref (buf); + goto leave; + } } static GstStateChangeReturn @@ -140,49 +245,46 @@ 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; } + + if (self->adapter_size > 0) { + GST_LOG_OBJECT (self, "gst_adapter_new. size: %d", self->adapter_size); + self->adapter = gst_adapter_new(); + if (self->adapter == NULL) + GST_ERROR_OBJECT (self, "Failed to create adapter!!"); + } break; case GST_STATE_CHANGE_READY_TO_PAUSED: - /* STATE_TUNING */ - if (self->use_state_tuning) { - GST_INFO_OBJECT (self, "use state-tuning feature"); - g_mutex_lock (self->ready_lock); - - self->sink_set_caps = (GstCaps *)gst_pad_peer_get_caps (self->sinkpad); - if (self->sink_set_caps == NULL) { - GST_ERROR_OBJECT (self, "Caps is NULL"); - } - - GST_INFO_OBJECT (self, "omx: prepare"); - - /** @todo this should probably go after doing preparations. */ - if (self->omx_setup) { - self->omx_setup (self); - } - - setup_ports (self); - - g_omx_core_prepare (self->gomx); - - 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); - g_mutex_unlock(self->ready_lock); - ret = GST_STATE_CHANGE_FAILURE; - goto leave; - } - - g_mutex_unlock (self->ready_lock); + 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; @@ -197,9 +299,9 @@ 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 */ g_omx_port_finish (self->in_port); g_omx_port_finish (self->out_port); @@ -215,6 +317,15 @@ change_state (GstElement * element, GstStateChange transition) } break; + case GST_STATE_CHANGE_READY_TO_NULL: + GST_INFO_OBJECT (self, "GST_STATE_CHANGE_READY_TO_NULL"); + if (self->adapter) { + gst_adapter_clear(self->adapter); + g_object_unref(self->adapter); + self->adapter = NULL; + } + break; + default: break; } @@ -232,6 +343,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; @@ -288,10 +405,6 @@ set_property (GObject * obj, OMX_SetParameter (omx_handle, OMX_IndexParamPortDefinition, ¶m); } break; - /* STATE_TUNING */ - case ARG_USE_STATETUNING: - self->use_state_tuning = g_value_get_boolean(value); - break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, prop_id, pspec); break; @@ -334,10 +447,6 @@ get_property (GObject * obj, guint prop_id, GValue * value, GParamSpec * pspec) g_value_set_uint (value, param.nBufferCountActual); } break; - /* STATE_TUNING */ - case ARG_USE_STATETUNING: - g_value_set_boolean(value, self->use_state_tuning); - break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, prop_id, pspec); break; @@ -382,24 +491,32 @@ type_class_init (gpointer g_class, gpointer class_data) "The number of OMX output buffers", 1, 10, 4, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); - /* 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 inline GstFlowReturn -push_buffer (GstOmxBaseFilter * self, GstBuffer * buf) +push_buffer (GstOmxBaseFilter * self, GstBuffer * buf, OMX_BUFFERHEADERTYPE * omx_buffer) { - GstFlowReturn ret; + GstFlowReturn ret = GST_FLOW_OK; + GstOmxBaseFilterClass *basefilter_class; + + basefilter_class = GST_OMX_BASE_FILTER_GET_CLASS (self); + /* process output gst buffer before gst_pad_push */ + if (basefilter_class->process_output_buf) { + GstOmxReturn ret = GSTOMX_RETURN_OK; + ret = basefilter_class->process_output_buf(self, &buf, omx_buffer); + if (ret == GSTOMX_RETURN_SKIP) { + gst_buffer_unref (buf); + goto leave; + } + } - /** @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); +leave: return ret; } @@ -443,7 +560,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; @@ -473,31 +590,25 @@ 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; + SCMN_IMGB *outbuf = NULL; + + if (G_UNLIKELY (omx_buffer->nFlags & OMX_BUFFERFLAG_DECODEONLY)) { + GST_INFO_OBJECT (self, "Decodeonly flag was set from component"); + g_omx_port_release_buffer (out_port, omx_buffer); + goto leave; + } caps = gst_pad_get_negotiated_caps(self->srcpad); structure = gst_caps_get_structure(caps, 0); @@ -506,13 +617,22 @@ 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; goto leave; } + outbuf = (SCMN_IMGB*)(omx_buffer->pBuffer); + if (outbuf->buf_share_method == 1) { + GST_LOG_OBJECT (self, "dec output buf: fd[0]:%d fd[1]:%d fd[2]:%d w[0]:%d h[0]:%d buf_share_method:%d", + outbuf->fd[0], outbuf->fd[1], outbuf->fd[2], outbuf->w[0], outbuf->h[0], outbuf->buf_share_method); + } else if (outbuf->buf_share_method == 0) { + GST_LOG_OBJECT (self, "dec output uses hw addr"); + } else { + GST_WARNING_OBJECT (self, "dec output buf has wrong buf_share_method"); + } memcpy (GST_BUFFER_MALLOCDATA(buf), omx_buffer->pBuffer, omx_buffer->nFilledLen); if (self->use_timestamps) { @@ -521,8 +641,9 @@ 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); + ret = push_buffer (self, buf, omx_buffer); } else if (buf && !(omx_buffer->nFlags & OMX_BUFFERFLAG_EOS)) { GST_BUFFER_SIZE (buf) = omx_buffer->nFilledLen; if (self->use_timestamps) { @@ -534,16 +655,8 @@ output_loop (gpointer data) omx_buffer->pAppPrivate = NULL; omx_buffer->pBuffer = NULL; - /* 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); - } + ret = push_buffer (self, buf, omx_buffer); - ret = push_buffer (self, buf); - - gst_buffer_unref (buf); } else { /* This is only meant for the first OpenMAX buffers, * which need to be pre-allocated. */ @@ -562,7 +675,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. */ @@ -572,14 +685,7 @@ output_loop (gpointer data) } } - /* 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); - } - - ret = push_buffer (self, buf); + ret = push_buffer (self, buf, omx_buffer); } else { GST_WARNING_OBJECT (self, "couldn't allocate buffer of size %lu", omx_buffer->nFilledLen); @@ -589,7 +695,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; @@ -600,7 +706,6 @@ output_loop (gpointer data) omx_buffer->nAllocLen, GST_PAD_CAPS (self->srcpad), &buf); if (G_LIKELY (result == GST_FLOW_OK)) { - gst_buffer_ref (buf); omx_buffer->pAppPrivate = buf; omx_buffer->pBuffer = GST_BUFFER_DATA (buf); @@ -612,7 +717,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"); } @@ -651,83 +756,88 @@ pad_chain (GstPad * pad, GstBuffer * buf) GOmxCore *gomx; GOmxPort *in_port; GstOmxBaseFilter *self; + GstOmxBaseFilterClass *basefilter_class; GstFlowReturn ret = GST_FLOW_OK; + GstBuffer *adapter_buf = NULL; self = GST_OMX_BASE_FILTER (GST_OBJECT_PARENT (pad)); 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) { - if (G_UNLIKELY (gomx->omx_state == OMX_StateLoaded)) { - g_mutex_lock (self->ready_lock); - - GST_INFO_OBJECT (self, "omx: prepare"); - - /** @todo this should probably go after doing preparations. */ - if (self->omx_setup) { - self->omx_setup (self); - } - - setup_ports (self); - - g_omx_core_prepare (self->gomx); - - if (gomx->omx_state == OMX_StateIdle) { - self->ready = TRUE; - gst_pad_start_task (self->srcpad, output_loop, self->srcpad); - } - - g_mutex_unlock (self->ready_lock); - - if (gomx->omx_state != OMX_StateIdle) - goto out_flushing; - } + if (G_UNLIKELY (gomx->omx_state == OMX_StateLoaded)) + omx_change_state(self, GstOmx_LodedToIdle, NULL, NULL); } in_port = self->in_port; if (G_LIKELY (in_port->enabled)) { guint buffer_offset = 0; + guint8 *src_data = NULL; + guint src_size = 0; + GstClockTime src_timestamp = 0; + GstClockTime src_duration = 0; - if (G_UNLIKELY (gomx->omx_state == OMX_StateIdle)) { - GST_INFO_OBJECT (self, "omx: play"); - g_omx_core_start (gomx); + if (G_UNLIKELY (gomx->omx_state == OMX_StateIdle)) + omx_change_state(self, GstOmx_IdleToExcuting,in_port, buf); - if (gomx->omx_state != OMX_StateExecuting) - goto out_flushing; + if (G_UNLIKELY (gomx->omx_state != OMX_StateExecuting)) { + GST_ERROR_OBJECT (self, "Whoa! very wrong"); + } - /* send buffer with codec data flag */ - /** @todo move to util */ - if (self->codec_data) { - OMX_BUFFERHEADERTYPE *omx_buffer; + basefilter_class = GST_OMX_BASE_FILTER_GET_CLASS (self); + /* process input gst buffer before OMX_EmptyThisBuffer */ + if (basefilter_class->process_input_buf) + { + GstOmxReturn ret = GSTOMX_RETURN_OK; + ret = basefilter_class->process_input_buf(self,&buf); + if (ret == GSTOMX_RETURN_SKIP) { + gst_buffer_unref(buf); + goto leave; + } + } - GST_LOG_OBJECT (self, "request buffer"); - omx_buffer = g_omx_port_request_buffer (in_port); + if (self->adapter_size > 0) { + if (!self->adapter) { + GST_WARNING_OBJECT (self, "adapter is NULL. retry gst_adapter_new"); + self->adapter = gst_adapter_new(); + } - if (G_LIKELY (omx_buffer)) { - omx_buffer->nFlags |= OMX_BUFFERFLAG_CODECCONFIG; /* codec data flag */ + if (GST_BUFFER_IS_DISCONT(buf)) + { + GST_INFO_OBJECT (self, "got GST_BUFFER_IS_DISCONT."); + gst_adapter_clear(self->adapter); + } - omx_buffer->nFilledLen = GST_BUFFER_SIZE (self->codec_data); - memcpy (omx_buffer->pBuffer + omx_buffer->nOffset, - GST_BUFFER_DATA (self->codec_data), omx_buffer->nFilledLen); + gst_adapter_push(self->adapter, buf); - GST_LOG_OBJECT (self, "release_buffer"); - g_omx_port_release_buffer (in_port, omx_buffer); - } + src_size = gst_adapter_available(self->adapter); + if (src_size < self->adapter_size) { + GST_LOG_OBJECT (self, "Not enough data in adapter to feed to decoder."); + goto leave; } - } - if (G_UNLIKELY (gomx->omx_state != OMX_StateExecuting)) { - GST_ERROR_OBJECT (self, "Whoa! very wrong"); + if (src_size > self->adapter_size) { + src_size = src_size - GST_BUFFER_SIZE(buf); + GST_LOG_OBJECT (self, "take buffer from adapter. size=%d", src_size); + } + + src_timestamp = gst_adapter_prev_timestamp(self->adapter, NULL); + adapter_buf = gst_adapter_take_buffer(self->adapter, src_size); + src_data = GST_BUFFER_DATA(adapter_buf); + src_duration = GST_BUFFER_TIMESTAMP (buf) - src_timestamp; + } else { + src_data = GST_BUFFER_DATA (buf); + src_size = GST_BUFFER_SIZE (buf); + src_timestamp = GST_BUFFER_TIMESTAMP (buf); + src_duration = GST_BUFFER_DURATION (buf); } - while (G_LIKELY (buffer_offset < GST_BUFFER_SIZE (buf))) { + while (G_LIKELY (buffer_offset < src_size)) { OMX_BUFFERHEADERTYPE *omx_buffer; if (self->last_pad_push_return != GST_FLOW_OK || @@ -742,60 +852,68 @@ 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."); - ret = GST_FLOW_OK; - goto out_flushing; - } - /* Copy p[0], p[1] of SCMN_IMGB to pAddrY, pAddrC of MFC_ENC_ADDR_INFO */ - memcpy (omx_buffer->pBuffer, - GST_BUFFER_MALLOCDATA(buf) - + (sizeof (int) * 4 + sizeof (void*)) * 4, sizeof (void*)); - memcpy (omx_buffer->pBuffer + sizeof (void*), - GST_BUFFER_MALLOCDATA(buf) - + (sizeof (int) * 4 + sizeof (void*)) * 4 + sizeof (void*), - sizeof (void*)); - omx_buffer->nAllocLen = sizeof (void*) * 2; - omx_buffer->nFilledLen = sizeof (void*) * 2; - } else if (omx_buffer->nOffset == 0 && self->share_input_buffer) { + SCMN_IMGB *inbuf = NULL; + + if (!GST_BUFFER_MALLOCDATA(buf)) { + GST_WARNING_OBJECT (self, "null MALLOCDATA in hw color format. skip this."); + goto out_flushing; + } + + inbuf = (SCMN_IMGB*)(GST_BUFFER_MALLOCDATA(buf)); + if (inbuf != NULL && inbuf->buf_share_method == 1) { + GST_LOG_OBJECT (self, "enc. fd[0]:%d fd[1]:%d fd[2]:%d w[0]:%d h[0]:%d buf_share_method:%d", + inbuf->fd[0], inbuf->fd[1], inbuf->fd[2], inbuf->w[0], inbuf->h[0], inbuf->buf_share_method); + } else if (inbuf != NULL && inbuf->buf_share_method == 0) { + GST_LOG_OBJECT (self, "enc input buf uses hw addr"); + } else { + GST_WARNING_OBJECT (self, "enc input buf has wrong buf_share_method"); + } + + memcpy (omx_buffer->pBuffer, GST_BUFFER_MALLOCDATA(buf), sizeof(SCMN_IMGB)); + omx_buffer->nAllocLen = sizeof(SCMN_IMGB); + omx_buffer->nFilledLen = sizeof(SCMN_IMGB); + } else if (omx_buffer->nOffset == 0 && self->in_port->shared_buffer) { { GstBuffer *old_buf; old_buf = omx_buffer->pAppPrivate; if (old_buf) { - gst_buffer_unref (old_buf); + gst_buffer_unref ((GstBuffer *)old_buf); } else if (omx_buffer->pBuffer) { g_free (omx_buffer->pBuffer); + omx_buffer->pBuffer = NULL; } } - omx_buffer->pBuffer = GST_BUFFER_DATA (buf); - omx_buffer->nAllocLen = GST_BUFFER_SIZE (buf); - omx_buffer->nFilledLen = GST_BUFFER_SIZE (buf); - omx_buffer->pAppPrivate = buf; + omx_buffer->pBuffer = src_data; + omx_buffer->nAllocLen = src_size; + omx_buffer->nFilledLen = src_size; + omx_buffer->pAppPrivate = (self->adapter_size > 0) ? (OMX_PTR)adapter_buf : (OMX_PTR)buf; } else { - omx_buffer->nFilledLen = MIN (GST_BUFFER_SIZE (buf) - buffer_offset, + omx_buffer->nFilledLen = MIN (src_size - buffer_offset, omx_buffer->nAllocLen - omx_buffer->nOffset); memcpy (omx_buffer->pBuffer + omx_buffer->nOffset, - GST_BUFFER_DATA (buf) + buffer_offset, omx_buffer->nFilledLen); + src_data + buffer_offset, omx_buffer->nFilledLen); } if (self->use_timestamps) { GstClockTime timestamp_offset = 0; - if (buffer_offset && GST_BUFFER_DURATION (buf) != GST_CLOCK_TIME_NONE) { + if (buffer_offset && src_duration != GST_CLOCK_TIME_NONE) { timestamp_offset = gst_util_uint64_scale_int (buffer_offset, - GST_BUFFER_DURATION (buf), GST_BUFFER_SIZE (buf)); + src_duration, src_size); } omx_buffer->nTimeStamp = - gst_util_uint64_scale_int (GST_BUFFER_TIMESTAMP (buf) + + gst_util_uint64_scale_int (src_timestamp + 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 { @@ -816,8 +934,13 @@ pad_chain (GstPad * pad, GstBuffer * buf) ret = GST_FLOW_UNEXPECTED; } - if (!self->share_input_buffer) { - gst_buffer_unref (buf); + if (!self->in_port->shared_buffer) { + if (self->adapter_size > 0 && adapter_buf) { + gst_buffer_unref (adapter_buf); + adapter_buf = NULL; + } else { + gst_buffer_unref (buf); + } } leave: @@ -831,6 +954,8 @@ out_flushing: { const gchar *error_msg = NULL; + GST_LOG_OBJECT(self, "out_flushing"); + if (gomx->omx_error) { error_msg = "Error from OpenMAX component"; } else if (gomx->omx_state != OMX_StateExecuting && @@ -843,7 +968,12 @@ out_flushing: ret = GST_FLOW_ERROR; } - gst_buffer_unref (buf); + if (self->adapter_size > 0 && adapter_buf) { + gst_buffer_unref (adapter_buf); + adapter_buf = NULL; + } else { + gst_buffer_unref (buf); + } goto leave; } @@ -865,6 +995,11 @@ pad_event (GstPad * pad, GstEvent * event) GST_INFO_OBJECT (self, "event: %s", GST_EVENT_TYPE_NAME (event)); + if (self->pad_event) { + if (!self->pad_event(pad, event)) + return TRUE; + } + switch (GST_EVENT_TYPE (event)) { case GST_EVENT_EOS: /* if we are init'ed, and there is a running loop; then @@ -880,9 +1015,27 @@ pad_event (GstPad * pad, GstEvent * event) omx_buffer = g_omx_port_request_buffer (in_port); if (G_LIKELY (omx_buffer)) { + + if (self->adapter_size > 0 && self->adapter) { + guint src_len = 0; + GstBuffer *adapter_buf = NULL; + + src_len = gst_adapter_available(self->adapter); + if (src_len > 0 && src_len < self->adapter_size) { + omx_buffer->nTimeStamp = gst_util_uint64_scale_int( + gst_adapter_prev_timestamp(self->adapter, NULL), + OMX_TICKS_PER_SECOND, GST_SECOND); + adapter_buf = gst_adapter_take_buffer(self->adapter, src_len); + omx_buffer->pBuffer = GST_BUFFER_DATA(adapter_buf); + omx_buffer->nAllocLen = src_len; + omx_buffer->nFilledLen = src_len; + omx_buffer->pAppPrivate = adapter_buf; + } + gst_adapter_clear(self->adapter); + } omx_buffer->nFlags |= OMX_BUFFERFLAG_EOS; - GST_LOG_OBJECT (self, "release_buffer"); + GST_LOG_OBJECT (self, "release_buffer in EOS. size=%d", omx_buffer->nFilledLen); /* foo_buffer_untaint (omx_buffer); */ g_omx_port_release_buffer (in_port, omx_buffer); /* loop handles EOS, eat it here */ @@ -897,36 +1050,40 @@ pad_event (GstPad * pad, GstEvent * event) break; case GST_EVENT_FLUSH_START: - if((gomx->omx_state == OMX_StatePause)||(gomx->omx_state == OMX_StateExecuting)) { - gst_pad_push_event (self->srcpad, event); - self->last_pad_push_return = GST_FLOW_WRONG_STATE; + if (gomx->omx_state == OMX_StatePause || gomx->omx_state == OMX_StateExecuting) { + gst_pad_push_event (self->srcpad, event); + self->last_pad_push_return = GST_FLOW_WRONG_STATE; - g_omx_core_flush_start (gomx); + g_omx_core_flush_start (gomx); - gst_pad_pause_task (self->srcpad); + gst_pad_pause_task (self->srcpad); - ret = TRUE; - } else { - GST_WARNING_OBJECT (self, "flush start in wrong omx state"); - ret = FALSE; - } + ret = TRUE; + } else { + GST_ERROR_OBJECT (self, "flush start in wrong omx state"); + ret = FALSE; + } break; case GST_EVENT_FLUSH_STOP: - if((gomx->omx_state == OMX_StatePause)||(gomx->omx_state == OMX_StateExecuting)) { - gst_pad_push_event (self->srcpad, event); - self->last_pad_push_return = GST_FLOW_OK; + if (gomx->omx_state == OMX_StatePause || gomx->omx_state == OMX_StateExecuting) { + gst_pad_push_event (self->srcpad, event); + self->last_pad_push_return = GST_FLOW_OK; - g_omx_core_flush_stop (gomx); + g_omx_core_flush_stop (gomx); - if (self->ready) - gst_pad_start_task (self->srcpad, output_loop, self->srcpad); + if (self->adapter_size > 0 && self->adapter) { + gst_adapter_clear(self->adapter); + } - ret = TRUE; - } else { - GST_WARNING_OBJECT (self, "flush start in wrong omx state"); - ret = FALSE; - } + if (self->ready) + gst_pad_start_task (self->srcpad, output_loop, self->srcpad); + + ret = TRUE; + } else { + GST_ERROR_OBJECT (self, "flush start in wrong omx state"); + ret = FALSE; + } break; case GST_EVENT_NEWSEGMENT: @@ -1006,7 +1163,9 @@ type_instance_init (GTypeInstance * instance, gpointer g_class) GST_LOG_OBJECT (self, "begin"); self->use_timestamps = TRUE; - self->use_state_tuning = FALSE; /* STATE_TUNING */ + self->use_state_tuning = FALSE; + self->adapter_size = 0; + self->adapter = NULL; self->gomx = gstomx_core_new (self, G_TYPE_FROM_CLASS (g_class)); self->in_port = g_omx_core_new_port (self->gomx, 0); diff --git a/omx/gstomx_base_filter.h b/omx/gstomx_base_filter.h index 5ade8e2..e7cdfb6 100644 --- a/omx/gstomx_base_filter.h +++ b/omx/gstomx_base_filter.h @@ -23,25 +23,89 @@ #define GSTOMX_BASE_FILTER_H #include +#include +#include "gstomx_util.h" +#include G_BEGIN_DECLS -#define GST_OMX_BASE_FILTER(obj) (GstOmxBaseFilter *) (obj) #define GST_OMX_BASE_FILTER_TYPE (gst_omx_base_filter_get_type ()) -#define GST_OMX_BASE_FILTER_CLASS(obj) (GstOmxBaseFilterClass *) (obj) +#define GST_OMX_BASE_FILTER(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), GST_OMX_BASE_FILTER_TYPE, GstOmxBaseFilter)) +#define GST_OMX_BASE_FILTER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), GST_OMX_BASE_FILTER_TYPE, GstOmxBaseFilterClass)) +#define GST_OMX_BASE_FILTER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GST_OMX_BASE_FILTER_TYPE, GstOmxBaseFilterClass)) typedef struct GstOmxBaseFilter GstOmxBaseFilter; typedef struct GstOmxBaseFilterClass GstOmxBaseFilterClass; typedef void (*GstOmxBaseFilterCb) (GstOmxBaseFilter * self); +typedef gboolean (*GstOmxBaseFilterEventCb) (GstPad * pad, GstEvent * event); -#include "gstomx_util.h" -#include -/* Add extended_color_format */ +/* using common scmn_imgb format */ +#define SCMN_IMGB_MAX_PLANE (4) /* max channel count */ + +/* image buffer definition + +------------------------------------------+ --- + | | ^ + | a[], p[] | | + | +---------------------------+ --- | | + | | | ^ | | + | |<---------- w[] ---------->| | | | + | | | | | | + | | | | + | | | h[] | e[] + | | | | + | | | | | | + | | | | | | + | | | v | | + | +---------------------------+ --- | | + | | v + +------------------------------------------+ --- + + |<----------------- s[] ------------------>| +*/ + +typedef struct +{ + int w[SCMN_IMGB_MAX_PLANE]; /* width of each image plane */ + int h[SCMN_IMGB_MAX_PLANE]; /* height of each image plane */ + int s[SCMN_IMGB_MAX_PLANE]; /* stride of each image plane */ + int e[SCMN_IMGB_MAX_PLANE]; /* elevation of each image plane */ + void * a[SCMN_IMGB_MAX_PLANE]; /* user space address of each image plane */ + void * p[SCMN_IMGB_MAX_PLANE]; /* physical address of each image plane, if needs */ + int cs; /* color space type of image */ + int x; /* left postion, if needs */ + int y; /* top position, if needs */ + int __dummy2; /* to align memory */ + int data[16]; /* arbitrary data */ + + /* dmabuf fd */ + int fd[SCMN_IMGB_MAX_PLANE]; + + /* flag for buffer share */ + int buf_share_method; +} SCMN_IMGB; + +/* 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, - OMX_EXT_COLOR_FormatNV12Tiled = 0x7FC00002 /* 0x7FC00002 */ + OMX_EXT_COLOR_FormatNV12Tiled = 0x7FC00002, + OMX_EXT_COLOR_FormatNV12TFdValue = 0x7F000012, + OMX_EXT_COLOR_FormatNV12LFdValue = 0x7F000013 }EXT_OMX_COLOR_FORMATTYPE; +typedef enum GstOmxReturn +{ + GSTOMX_RETURN_OK, + GSTOMX_RETURN_SKIP, + GSTOMX_RETURN_ERROR +}GstOmxReturn; + +typedef enum GstOmxChangeState +{ + GstOmx_ToLoaded, + GstOmx_LodedToIdle, + GstOmx_IdleToExcuting +}GstOmxChangeState; + struct GstOmxBaseFilter { GstElement element; @@ -58,21 +122,25 @@ struct GstOmxBaseFilter GMutex *ready_lock; GstOmxBaseFilterCb omx_setup; + 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; - - /* STATE_TUNING */ - GstCaps *sink_set_caps; + /* MODIFICATION: state-tuning */ gboolean use_state_tuning; + + GstAdapter *adapter; /* adapter */ + guint adapter_size; }; struct GstOmxBaseFilterClass { GstElementClass parent_class; + + GstOmxReturn (*process_input_buf)(GstOmxBaseFilter *omx_base_filter, GstBuffer **buf); + GstOmxReturn (*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 827d51f..a253762 100644 --- a/omx/gstomx_base_videodec.c +++ b/omx/gstomx_base_videodec.c @@ -22,17 +22,85 @@ #include "gstomx_base_videodec.h" #include "gstomx.h" +enum +{ + ARG_0, + ARG_USE_STATETUNING, /* STATE_TUNING */ +}; + GSTOMX_BOILERPLATE (GstOmxBaseVideoDec, gst_omx_base_videodec, GstOmxBaseFilter, GST_OMX_BASE_FILTER_TYPE); +static GstOmxReturn +process_input_buf (GstOmxBaseFilter * omx_base_filter, GstBuffer **buf) +{ + return GSTOMX_RETURN_OK; +} + static void 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) +{ + GstOmxBaseVideoDec *self; + + self = GST_OMX_BASE_VIDEODEC (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) +{ + GstOmxBaseVideoDec *self; + + self = GST_OMX_BASE_VIDEODEC (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)); + } + basefilter_class->process_input_buf = process_input_buf; } static void @@ -60,6 +128,8 @@ settings_changed_cb (GOmxCore * core) width = param.format.video.nFrameWidth; height = param.format.video.nFrameHeight; + + GST_LOG_OBJECT (omx_base, "settings changed: fourcc =0x%x", (guint)param.format.video.eColorFormat); switch ((guint)param.format.video.eColorFormat) { case OMX_COLOR_FormatYUV420Planar: case OMX_COLOR_FormatYUV420PackedPlanar: @@ -71,7 +141,8 @@ 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_FormatNV12TFdValue: case OMX_EXT_COLOR_FormatNV12TPhysicalAddress: format = GST_MAKE_FOURCC ('S', 'T', '1', '2'); break; @@ -106,6 +177,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 */ } } @@ -198,11 +270,6 @@ omx_setup (GstOmxBaseFilter * omx_base) } } - /* STATE_TUNING */ - if (omx_base->use_state_tuning && omx_base->sink_set_caps) { - sink_setcaps(omx_base->sinkpad, omx_base->sink_set_caps); - } - GST_INFO_OBJECT (omx_base, "end"); } diff --git a/omx/gstomx_base_videoenc.c b/omx/gstomx_base_videoenc.c index aca21ec..f65269c 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,6 +36,97 @@ enum GSTOMX_BOILERPLATE (GstOmxBaseVideoEnc, gst_omx_base_videoenc, GstOmxBaseFilter, GST_OMX_BASE_FILTER_TYPE); + +/* 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, "request forced key frame now."); + + 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); +} + + +static GstOmxReturn +process_input_buf (GstOmxBaseFilter * omx_base_filter, GstBuffer **buf) +{ + GstOmxBaseVideoEnc *self; + + self = GST_OMX_BASE_VIDEOENC (omx_base_filter); + + GST_LOG_OBJECT (self, "base videoenc process_input_buf enter"); + + + return GSTOMX_RETURN_OK; +} + +/* modification: postprocess for outputbuf. in this videoenc case, set sync frame */ +static GstOmxReturn +process_output_buf(GstOmxBaseFilter * omx_base, GstBuffer **buf, OMX_BUFFERHEADERTYPE *omx_buffer) +{ + GstOmxBaseVideoEnc *self; + + self = GST_OMX_BASE_VIDEOENC (omx_base); + + GST_LOG_OBJECT (self, "base videoenc process_output_buf enter"); + + /* modification: 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); + } + + return GSTOMX_RETURN_OK; +} + +/* 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) { @@ -52,6 +144,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; @@ -80,8 +180,12 @@ static void 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 */ { @@ -93,7 +197,16 @@ 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_input_buf = process_input_buf; + basefilter_class->process_output_buf = process_output_buf; + basefilter_class->process_output_caps = process_output_caps; } static gboolean @@ -142,7 +255,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; @@ -176,12 +289,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); } @@ -211,16 +325,26 @@ 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); } } - /* STATE_TUNING */ - if (omx_base->use_state_tuning && omx_base->sink_set_caps) { - sink_setcaps(omx_base->sinkpad, omx_base->sink_set_caps); + /* 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"); @@ -240,4 +364,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..4b0ac5b 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; @@ -35,11 +36,12 @@ typedef struct GstOmxBaseVideoEncClass GstOmxBaseVideoEncClass; struct GstOmxBaseVideoEnc { GstOmxBaseFilter omx_base; - OMX_VIDEO_CODINGTYPE compression_format; + 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..4401940 --- /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_PACKETIZED, + GSTOMX_H264_FORMAT_BYTE_STREAM, +} 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..0f2f825 100644 --- a/omx/gstomx_h264dec.c +++ b/omx/gstomx_h264dec.c @@ -25,6 +25,319 @@ 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); + guint nal_type = 0; + guint forbidden_zero_bit = 0; + guint check_byte= 0; + + if (buf_data == NULL || buf_size < GSTOMX_H264_NAL_START_LEN + 1) { + self->h264Format = GSTOMX_H264_FORMAT_UNKNOWN; + GST_WARNING_OBJECT(self, "H264 format is unknown"); + return; + } + + self->h264Format = GSTOMX_H264_FORMAT_PACKETIZED; + + if ((buf_data[0] == 0x00)&&(buf_data[1] == 0x00)&& + ((buf_data[2] == 0x01)||((buf_data[2] == 0x00)&&(buf_data[3] == 0x01)))) { + check_byte = (buf_data[2] == 0x01) ? 3 : 4; + + nal_type = (guint)(buf_data[check_byte] & 0x1f); + forbidden_zero_bit = (guint)(buf_data[check_byte] & 0x80); + GST_LOG_OBJECT(self, "check first frame: nal_type=%d, forbidden_zero_bit=%d", nal_type, forbidden_zero_bit); + + if (forbidden_zero_bit == 0) { + /* check nal_unit_type is invaild value: ex) slice, DPA,DPB... */ + if ((0 < nal_type && nal_type <= 15) || nal_type == 19 || nal_type == 20) { + GST_INFO_OBJECT(self, "H264 format is Byte-stream format"); + self->h264Format = GSTOMX_H264_FORMAT_BYTE_STREAM; + } + } + } + + if (self->h264Format == GSTOMX_H264_FORMAT_PACKETIZED) + GST_INFO_OBJECT(self, "H264 format is Packetized format"); +} + +/* + * 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. do check_frame one more time with next frame.", frameType); + self->h264Format = GSTOMX_H264_FORMAT_UNKNOWN; + 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"); + *buf = gst_buffer_make_writable(*buf); /* make writable to support memsrc */ + 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) { + 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); + + 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 GstOmxReturn +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_PACKETIZED) { + + 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 GSTOMX_RETURN_OK; + } + + GST_LOG_OBJECT(h264_self, "H264 format is Packetized format. convert to Byte-stream format"); + convert_frame(h264_self, buf); + } + +/* if you want to use commonly for videodec input, use this */ +/* GST_OMX_BASE_FILTER_CLASS (parent_class)->process_input_buf (omx_base_filter, buf); */ + + return GSTOMX_RETURN_OK; +} + static void type_base_init (gpointer g_class) { @@ -49,14 +362,124 @@ 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) <= GSTOMX_H264_NAL_START_LEN) { + GST_ERROR("codec data size (%d) is less than start code length.", GST_BUFFER_SIZE(buffer)); + } else { + 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_BYTE_STREAM; + GST_INFO_OBJECT(self, "H264 codec_data format is Byte-stream."); + } else { + h264_self->h264Format = GSTOMX_H264_FORMAT_PACKETIZED; + GST_INFO_OBJECT(self, "H264 codec_data format is Packetized."); + } + + /* if codec data is 3gpp format, convert nalu format */ + if(h264_self->h264Format == GSTOMX_H264_FORMAT_PACKETIZED) { + GstBuffer *nalu_dci = NULL; + + ret = convert_dci(h264_self, buffer, &nalu_dci); + if (ret) { + omx_base->codec_data = nalu_dci; + } else { + GST_ERROR_OBJECT(h264_self, "converting dci error."); + if (nalu_dci) { + gst_buffer_unref (nalu_dci); + nalu_dci = NULL; + } + 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..354336a 100644 --- a/omx/gstomx_h264enc.c +++ b/omx/gstomx_h264enc.c @@ -22,9 +22,518 @@ #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 GstOmxReturn +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 GSTOMX_RETURN_ERROR; + } + gst_buffer_copy_metadata(newbuf, *buf, GST_BUFFER_COPY_ALL); + 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); + } + + return GSTOMX_RETURN_OK; +} + +/* + * 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 +556,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, "set Appending dci = %d", self->append_dci); + break; + + case ARG_BYTE_STREAM: + self->byte_stream = g_value_get_boolean (value); + GST_INFO_OBJECT (self, "set 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, "set 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_int (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_int ("slice-mode", "H264 encoder slice mode", + "slice mode: 0-Disable, 1-Fixed MB num slice, 2-Fixed bit num slice", + 0, 4, 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 +701,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 +725,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 +735,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 +779,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 1286f65..d424333 100644 --- a/omx/gstomx_mpeg4dec.c +++ b/omx/gstomx_mpeg4dec.c @@ -25,6 +25,239 @@ GSTOMX_BOILERPLATE (GstOmxMpeg4Dec, gst_omx_mpeg4dec, GstOmxBaseVideoDec, GST_OMX_BASE_VIDEODEC_TYPE); +static gboolean init_divx_symbol (GstOmxMpeg4Dec * self) +{ + GST_LOG_OBJECT (self, "mpeg4dec load_divx_symbol enter"); + + self->divx_handle = dlopen (DIVX_SDK_PLUGIN_NAME, RTLD_LAZY); + if (!self->divx_handle) { + GST_ERROR_OBJECT (self, "dlopen failed [%s]", dlerror()); + goto error_exit; + } + + self->divx_sym_table.init_decrypt = dlsym (self->divx_handle, "divx_init_decrypt"); + if (!self->divx_sym_table.init_decrypt) { + GST_ERROR_OBJECT (self, "loading divx_init_decrypt failed : %s", dlerror()); + goto error_exit; + } + self->divx_sym_table.commit = dlsym (self->divx_handle, "divx_commit"); + if (!self->divx_sym_table.commit) { + GST_ERROR_OBJECT (self, "loading divx_commit failed : %s", dlerror()); + goto error_exit; + } + self->divx_sym_table.decrypt_video = dlsym (self->divx_handle, "divx_decrypt_video"); + if (!self->divx_sym_table.decrypt_video) { + GST_ERROR_OBJECT (self, "loading divx_decrypt_video failed : %s", dlerror()); + goto error_exit; + } + self->divx_sym_table.prepare_video_bitstream = dlsym (self->divx_handle, "divx_prepare_video_bitstream"); + if (!self->divx_sym_table.prepare_video_bitstream) { + GST_ERROR_OBJECT (self, "loading divx_prepare_video_bitstream failed : %s", dlerror()); + goto error_exit; + } + self->divx_sym_table.finalize = dlsym (self->divx_handle, "divx_finalize"); + if (!self->divx_sym_table.finalize) { + GST_ERROR_OBJECT (self, "loading divx_finalize failed : %s", dlerror()); + goto error_exit; + } + + return TRUE; + +error_exit: + + if (self->divx_handle) { + dlclose(self->divx_handle); + self->divx_handle = NULL; + } + + return FALSE; +} + +static gboolean +init_divx_drm (GstOmxMpeg4Dec * self) +{ + int error = 0; + + GST_LOG_OBJECT (self, "mpeg4dec init_divx_drm enter"); + + if (init_divx_symbol(self) == FALSE) { + GST_ERROR_OBJECT (self, "loading symbol failed...."); + goto error_exit; + } + + self->drmContext = self->divx_sym_table.init_decrypt (&error); + + if (self->drmContext) { + GST_DEBUG_OBJECT (self, "%s init success: drmContext = %p\n", __func__, self->drmContext); + } else { + GST_ERROR_OBJECT (self, "%s failed to init... error code = %d \n", __func__, error); + goto error_exit; + } + + error = self->divx_sym_table.commit (self->drmContext); + + if (error == DRM_SUCCESS) { + GST_DEBUG_OBJECT (self, "%s commit success: drmContext = %p\n", __func__, self->drmContext); + } else { + GST_ERROR_OBJECT (self, "%s failed to commit... error code = %d \n", __func__, error); + goto error_exit; + } + + return TRUE; + +error_exit: + + if (self->drmContext) + { + self->divx_sym_table.finalize (self->drmContext); + free(self->drmContext); + self->drmContext = NULL; + } + + return FALSE; +} + +static GstOmxReturn +process_input_buf (GstOmxBaseFilter * omx_base_filter, GstBuffer **buf) +{ + GstOmxMpeg4Dec *self; + + self = GST_OMX_MPEG4DEC (omx_base_filter); + + GST_LOG_OBJECT (self, "mpeg4dec process_input_buf enter"); + + /* 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)); + } else { + GST_ERROR_OBJECT (self, "##### DivX DRM Mode ##### decrypt video failed : buffer = %d", GST_BUFFER_SIZE(*buf)); + } + } + +/* if you want to use commonly for videodec input, use this */ +/* GST_OMX_BASE_FILTER_CLASS (parent_class)->process_input_buf (omx_base_filter, buf); */ + + return GSTOMX_RETURN_OK; +} + +static void +print_tag (const GstTagList * list, const gchar * tag, gpointer data) +{ + gint i, count; + GstOmxMpeg4Dec *self; + + self = GST_OMX_MPEG4DEC (data); + + count = gst_tag_list_get_tag_size (list, tag); + + for (i = 0; i < count; i++) { + gchar *str; + + if (gst_tag_get_type (tag) == G_TYPE_STRING) { + if (!gst_tag_list_get_string_index (list, tag, i, &str)) + g_assert_not_reached (); + } else if (gst_tag_get_type (tag) == GST_TYPE_BUFFER) { + GstBuffer *img; + + img = gst_value_get_buffer (gst_tag_list_get_value_index (list, tag, i)); + if (img) { + gchar *caps_str; + + caps_str = GST_BUFFER_CAPS (img) ? + gst_caps_to_string (GST_BUFFER_CAPS (img)) : g_strdup ("unknown"); + str = g_strdup_printf ("buffer of %u bytes, type: %s", + GST_BUFFER_SIZE (img), caps_str); + g_free (caps_str); + } else { + str = g_strdup ("NULL buffer"); + } + } else { + str = g_strdup_value_contents (gst_tag_list_get_value_index (list, tag, i)); + } + + if (i == 0) { + 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) { + GST_LOG_OBJECT(self, "Init divx drm !!!!!!!!!!!!!!!!!!!! [%s]", str); + if (init_divx_drm (self)) { + GST_LOG_OBJECT(self, "omx_printtag_init_divx_drm() success"); + } else { + GST_ERROR_OBJECT(self, "omx_printtag_init_divx_drm() failed"); + } + } else { + GST_LOG_OBJECT(self, "Init divx drm is DONE before. so do nothing [%s]", str); + } + } + } else { + GST_LOG_OBJECT(self, "tag is not DRM Divx"); + } + + g_free (str); + } + + GST_LOG_OBJECT(self, "print_tag End"); +} + +static gboolean +mpeg4_pad_event (GstPad * pad, GstEvent * event) +{ + GstOmxMpeg4Dec *self; + gboolean ret = TRUE; + + self = GST_OMX_MPEG4DEC (GST_OBJECT_PARENT (pad)); + + GST_LOG_OBJECT (self, "begin"); + + GST_INFO_OBJECT (self, "event: %s", GST_EVENT_TYPE_NAME (event)); + + switch (GST_EVENT_TYPE (event)) { + case GST_EVENT_TAG: + { + GstTagList *taglist = NULL; + + GST_LOG_OBJECT (self, "GST_EVENT_TAG"); + + gst_event_parse_tag (event, &taglist); + gst_tag_list_foreach (taglist, print_tag, self); + gst_event_unref (event); + ret= FALSE; + break; + } + default: + ret = TRUE; + break; + } + return ret; +} + +static void +finalize (GObject * obj) +{ + GstOmxMpeg4Dec *self; + + self = GST_OMX_MPEG4DEC (obj); + + GST_LOG_OBJECT (self, "mpeg4dec finalize enter"); + + if (self->drmContext) + { + self->divx_sym_table.finalize (self->drmContext); + free(self->drmContext); + self->drmContext = NULL; + } + + if (self->divx_handle) + { + dlclose(self->divx_handle); + self->divx_handle = NULL; + } + + G_OBJECT_CLASS (parent_class)->finalize (obj); +} + static void type_base_init (gpointer g_class) { @@ -49,14 +282,25 @@ type_base_init (gpointer g_class) 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->finalize = finalize; + basefilter_class->process_input_buf = process_input_buf; } 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_filter->pad_event = mpeg4_pad_event; omx_base->compression_format = OMX_VIDEO_CodingMPEG4; } diff --git a/omx/gstomx_mpeg4dec.h b/omx/gstomx_mpeg4dec.h index 1b5cce8..506de05 100644 --- a/omx/gstomx_mpeg4dec.h +++ b/omx/gstomx_mpeg4dec.h @@ -23,18 +23,46 @@ #define GSTOMX_MPEG4DEC_H #include +#include +#include +#include G_BEGIN_DECLS #define GST_OMX_MPEG4DEC(obj) (GstOmxMpeg4Dec *) (obj) #define GST_OMX_MPEG4DEC_TYPE (gst_omx_mpeg4dec_get_type ()) +typedef struct DivXSymbolTable DivXSymbolTable; typedef struct GstOmxMpeg4Dec GstOmxMpeg4Dec; typedef struct GstOmxMpeg4DecClass GstOmxMpeg4DecClass; #include "gstomx_base_videodec.h" +#define DIVX_SDK_PLUGIN_NAME "libmm_divxsdk.so" + +typedef enum drmErrorCodes +{ + DRM_SUCCESS = 0, + DRM_NOT_AUTHORIZED, + DRM_NOT_REGISTERED, + DRM_RENTAL_EXPIRED, + DRM_GENERAL_ERROR, + DRM_NEVER_REGISTERED, +} drmErrorCodes_t; + +struct DivXSymbolTable +{ + uint8_t* (*init_decrypt) (int*); + int (*commit) (uint8_t *); + int (*decrypt_video) (uint8_t *, uint8_t *, uint32_t); + int (*prepare_video_bitstream) (uint8_t *, uint8_t * , uint32_t , uint8_t * , uint32_t * ); + int (*finalize) (uint8_t *); +}; + struct GstOmxMpeg4Dec { GstOmxBaseVideoDec omx_base; + uint8_t* drmContext; + void *divx_handle; + DivXSymbolTable divx_sym_table; }; struct GstOmxMpeg4DecClass diff --git a/omx/gstomx_util.c b/omx/gstomx_util.c index 438c41b..417c19d 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 @@ -307,17 +308,21 @@ g_omx_core_init (GOmxCore * core) G_OMX_INIT_PARAM (param); strncpy ((char *) param.cRole, core->component_role, - OMX_MAX_STRINGNAME_SIZE); + OMX_MAX_STRINGNAME_SIZE - 1); OMX_SetParameter (core->omx_handle, OMX_IndexParamStandardComponentRole, ¶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,48 @@ 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 */ + + if (!omx_buffer->pAppPrivate && port->type == GOMX_PORT_OUTPUT && omx_buffer->pBuffer) { + GST_INFO_OBJECT(port->core->object, + " %d: g_free shared buffer (pBuffer) %p", i, omx_buffer->pBuffer); + g_free (omx_buffer->pBuffer); + omx_buffer->pBuffer = NULL; + } + + if (omx_buffer->pAppPrivate) { + GST_INFO_OBJECT(port->core->object, + " %d: unref shared buffer (pAppPrivate) %p", i, 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 87a5e36..7b0525d 100644 --- a/packaging/gst-openmax.spec +++ b/packaging/gst-openmax.spec @@ -1,13 +1,10 @@ -%define _optdir /opt -%define _appdir %{_optdir}/apps - Name: gst-openmax Summary: GStreamer plug-in that allows communication with OpenMAX IL components Version: 0.10.1 -Release: 0 -Group: TO_BE/FILLED_IN -License: TO BE FILLED IN +Release: 4 +Group: Applications/Multimedia +License: LGPLv2.1 Source0: %{name}-%{version}.tar.gz BuildRequires: which BuildRequires: pkgconfig(gstreamer-0.10) @@ -15,16 +12,14 @@ BuildRequires: pkgconfig(gstreamer-plugins-base-0.10) %description gst-openmax is a GStreamer plug-in that allows communication with OpenMAX IL components. -Multiple OpenMAX IL implementations can be used, i -including but not limited to Texas Instruments and Bellagio. - +Multiple OpenMAX IL implementations can be used. %prep %setup -q %build ./autogen.sh --noconfigure -%configure --disable-static --prefix=%{_prefix} +%configure --disable-static --prefix=/usr make %{?jobs:-j%jobs} @@ -34,6 +29,6 @@ rm -rf %{buildroot} %make_install %files -%defattr(-,root,root,-) +%manifest gst-openmax.manifest %{_libdir}/gstreamer-0.10/libgstomx.so 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 -- 2.7.4