Source: gst-openmax
Maintainer: Hyunseok Lee <hs7388.lee@samsung.com>, JongHyuk Choi <jhchoi.choi@samsung.com>
-Uploaders: Hyunseok Lee <hs7388.lee@samsung.com>, Dowan Kim <dowan2171.kim@samsung.com>, Sunghyun Eum <sunghyun.eum@samsung.com>, Seongho Jeong(sh33.jeong@samsung.com)
+Uploaders: Hyunseok Lee <hs7388.lee@samsung.com>, Dowan Kim <dowan2171.kim@samsung.com>, Sunghyun Eum <sunghyun.eum@samsung.com>
Section: libs
Priority: optional
Standards-Version: 3.7.3
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 \
static gchar *
get_config_path (void)
{
+/* MODIFICATION */
#if 1 /* Fix_config_path */
return g_build_filename (OMX_CONFIG_DIRPATH, OMX_CONFIG_FILENAME, NULL);
#else
#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);
{
}
+/* 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
#include <string.h> /* for memcpy */
-/* STATE_TUNING */
+/* MODIFICATION: for state-tuning */
static void output_loop (gpointer data);
enum
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);
}
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");
}
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
ret = GST_FLOW_ERROR;
}
- gst_buffer_unref (buf);
+ if (buf)
+ gst_buffer_unref (buf);
goto leave;
}
}
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;
break;
case GST_STATE_CHANGE_READY_TO_PAUSED:
- /* STATE_TUNING */
- if (self->use_state_tuning) {
- GST_INFO_OBJECT (self, "use state-tuning feature");
- omx_change_state(self, GstOmx_LodedToIdle, NULL, NULL);
-
- if (core->omx_state == OMX_StateIdle) {
- self->ready = TRUE;
- gst_pad_start_task (self->srcpad, output_loop, self->srcpad);
- } else {
- GST_ERROR_OBJECT(self, "fail to move from OMX state Loaded to Idle");
- g_omx_port_finish(self->in_port);
- g_omx_port_finish(self->out_port);
- g_omx_core_stop(core);
- g_omx_core_unload(core);
- ret = GST_STATE_CHANGE_FAILURE;
- goto leave;
- }
+ GST_INFO_OBJECT (self, "GST_STATE_CHANGE_READY_TO_PAUSED");
+ /* MODIFICATION: state tuning */
+ if (self->use_state_tuning) {
+ GST_INFO_OBJECT (self, "use state-tuning feature");
+ /* to handle abnormal state change. */
+ if (self->gomx != self->in_port->core) {
+ GST_ERROR_OBJECT(self, "self->gomx != self->in_port->core. start new in_port");
+ self->in_port = g_omx_core_new_port (self->gomx, 0);
+ }
+ if (self->gomx != self->out_port->core) {
+ GST_ERROR_OBJECT(self, "self->gomx != self->out_port->core. start new out_port");
+ self->out_port = g_omx_core_new_port (self->gomx, 1);
+ }
+
+ omx_change_state(self, GstOmx_LodedToIdle, NULL, NULL);
+
+ if (core->omx_state != OMX_StateIdle) {
+ GST_ERROR_OBJECT(self, "fail to move from OMX state Loaded to Idle");
+ g_omx_port_finish(self->in_port);
+ g_omx_port_finish(self->out_port);
+ g_omx_core_stop(core);
+ g_omx_core_unload(core);
+ ret = GST_STATE_CHANGE_FAILURE;
+ goto leave;
+ }
}
break;
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 */
}
break;
+ case GST_STATE_CHANGE_READY_TO_NULL:
+ GST_INFO_OBJECT (self, "GST_STATE_CHANGE_READY_TO_NULL");
+ break;
+
default:
break;
}
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;
basefilter_class = GST_OMX_BASE_FILTER_GET_CLASS (self);
/* process output gst buffer before gst_pad_push */
- if (basefilter_class->process_output_buf)
- {
- basefilter_class->process_output_buf(self, buf, omx_buffer);
+ if (basefilter_class->process_output_buf) {
+ basefilter_class->process_output_buf(self, &buf, omx_buffer);
}
- /** @todo check if tainted */
- GST_LOG_OBJECT (self, "begin");
+ GST_LOG_OBJECT (self, "OUT_BUFFER: timestamp = %" GST_TIME_FORMAT " size = %lu",
+ GST_TIME_ARGS(GST_BUFFER_TIMESTAMP (buf)), GST_BUFFER_SIZE (buf));
ret = gst_pad_push (self->srcpad, buf);
- GST_LOG_OBJECT (self, "end");
+ GST_LOG_OBJECT (self, "gst_pad_push end. ret = %d", ret);
return ret;
}
goto leave;
}
- log_buffer (self, omx_buffer);
+ log_buffer (self, omx_buffer, "output_loop");
if (G_LIKELY (omx_buffer->nFilledLen > 0)) {
GstBuffer *buf;
/** @todo we need to move all the caps handling to one single
* place, in the output loop probably. */
if (G_UNLIKELY (omx_buffer->nFlags & OMX_BUFFERFLAG_CODECCONFIG)) {
- GstCaps *caps = NULL;
- GstStructure *structure;
- GValue value = { 0, {{0}
- }
- };
-
- caps = gst_pad_get_negotiated_caps (self->srcpad);
- caps = gst_caps_make_writable (caps);
- structure = gst_caps_get_structure (caps, 0);
-
- g_value_init (&value, GST_TYPE_BUFFER);
- buf = gst_buffer_new_and_alloc (omx_buffer->nFilledLen);
- memcpy (GST_BUFFER_DATA (buf),
- omx_buffer->pBuffer + omx_buffer->nOffset, omx_buffer->nFilledLen);
- gst_value_set_buffer (&value, buf);
- gst_buffer_unref (buf);
- gst_structure_set_value (structure, "codec_data", &value);
- g_value_unset (&value);
-
- gst_pad_set_caps (self->srcpad, caps);
+ /* modification: to handle both byte-stream and packetized codec_data */
+ GstOmxBaseFilterClass *basefilter_class;
+ basefilter_class = GST_OMX_BASE_FILTER_GET_CLASS (self);
+ if (basefilter_class->process_output_caps) {
+ basefilter_class->process_output_caps(self, omx_buffer);
+ }
+ /* MODIFICATION: to handle output ST12 HW addr (dec) */
} else if (is_extended_color_format(self, self->out_port)) {
GstCaps *caps = NULL;
GstStructure *structure;
gint width = 0, height = 0;
+ if (G_UNLIKELY (omx_buffer->nFlags & OMX_BUFFERFLAG_DECODEONLY))
+ goto leave;
+
caps = gst_pad_get_negotiated_caps(self->srcpad);
structure = gst_caps_get_structure(caps, 0);
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;
OMX_TICKS_PER_SECOND);
}
gst_buffer_set_caps(buf, GST_PAD_CAPS(self->srcpad));
+ gst_caps_unref (caps);
ret = push_buffer (self, buf, omx_buffer);
} else if (buf && !(omx_buffer->nFlags & OMX_BUFFERFLAG_EOS)) {
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. */
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;
}
}
- 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");
}
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) {
/* process input gst buffer before OMX_EmptyThisBuffer */
if (basefilter_class->process_input_buf)
{
- basefilter_class->process_input_buf(self,buf);
+ basefilter_class->process_input_buf(self,&buf);
}
if (self->adapter_size > 0) {
src_size = gst_adapter_available(self->adapter);
if (src_size + GST_BUFFER_SIZE(buf) <= self->adapter_size) {
gst_adapter_push(self->adapter, buf);
- gst_buffer_ref(buf);
goto leave;
}
src_data = (guint8*) gst_adapter_peek(self->adapter, src_size);
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.");
sizeof (void*));
omx_buffer->nAllocLen = sizeof (void*) * 2;
omx_buffer->nFilledLen = sizeof (void*) * 2;
- } else if (omx_buffer->nOffset == 0 && self->share_input_buffer) {
+ } else if (omx_buffer->nOffset == 0 && self->in_port->shared_buffer) {
{
GstBuffer *old_buf;
old_buf = omx_buffer->pAppPrivate;
}
} else if (omx_buffer->pBuffer) {
g_free (omx_buffer->pBuffer);
+ omx_buffer->pBuffer = NULL;
}
}
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 {
}
if (self->adapter_size > 0) {
- if (!self->share_input_buffer) {
+ if (!self->in_port->shared_buffer) {
gst_adapter_clear(self->adapter);
} else {
self->adapter = gst_adapter_new();
}
gst_adapter_push(self->adapter, buf);
- gst_buffer_ref(buf);
} else {
- if (!self->share_input_buffer) {
+ if (!self->in_port->shared_buffer) {
gst_buffer_unref (buf);
}
}
GST_INFO_OBJECT (self, "event: %s", GST_EVENT_TYPE_NAME (event));
- if (self->omx_event) {
- if (!self->omx_event(pad, event))
+ if (self->pad_event) {
+ if (!self->pad_event(pad, event))
return TRUE;
}
typedef gboolean (*GstOmxBaseFilterEventCb) (GstPad * pad, GstEvent * event);
-/* Add extended_color_format */
+/* MODIFICATION: Add extended_color_format */
typedef enum _EXT_OMX_COLOR_FORMATTYPE {
OMX_EXT_COLOR_FormatNV12TPhysicalAddress = 0x7F000001, /**< Reserved region for introducing Vendor Extensions */
OMX_EXT_COLOR_FormatNV12LPhysicalAddress = 0x7F000002,
GMutex *ready_lock;
GstOmxBaseFilterCb omx_setup;
- GstOmxBaseFilterEventCb omx_event;
+ GstOmxBaseFilterEventCb pad_event;
GstFlowReturn last_pad_push_return;
GstBuffer *codec_data;
- /** @todo these are hacks, OpenMAX IL spec should be revised. */
- gboolean share_input_buffer;
- gboolean share_output_buffer;
-
-
+ /* MODIFICATION: state-tuning */
gboolean use_state_tuning;
GstAdapter *adapter; /* adapter */
{
GstElementClass parent_class;
- void (*process_input_buf)(GstOmxBaseFilter * omx_base_filter, GstBuffer * buf);
- void (*process_output_buf)(GstOmxBaseFilter * omx_base_filter, GstBuffer * buf, OMX_BUFFERHEADERTYPE *omx_buffer);
+ void (*process_input_buf)(GstOmxBaseFilter *omx_base_filter, GstBuffer **buf);
+ void (*process_output_buf)(GstOmxBaseFilter *omx_base_filter, GstBuffer **buf, OMX_BUFFERHEADERTYPE *omx_buffer);
+ void (*process_output_caps)(GstOmxBaseFilter *omx_base_filter, OMX_BUFFERHEADERTYPE *omx_buffer);
+
};
GType gst_omx_base_filter_get_type (void);
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);
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;
gst_buffer_unref (old_buf);
} else if (omx_buffer->pBuffer) {
g_free (omx_buffer->pBuffer);
+ omx_buffer->pBuffer = NULL;
}
}
GST_OMX_BASE_FILTER_TYPE);
static void
-process_input_buf (GstOmxBaseFilter * omx_base_filter, GstBuffer * buf)
+process_input_buf (GstOmxBaseFilter * omx_base_filter, GstBuffer **buf)
{
}
{
}
-
-/* add state tuning property */
+/* MODIFICATION: add state tuning property */
static void
set_property (GObject * obj,
guint prop_id, const GValue * value, GParamSpec * pspec)
case OMX_COLOR_FormatCbYCrY:
format = GST_MAKE_FOURCC ('U', 'Y', 'V', 'Y');
break;
- /* Add extended_color_format */
+ /* MODIFICATION: Add extended_color_format */
case OMX_EXT_COLOR_FormatNV12TPhysicalAddress:
format = GST_MAKE_FOURCC ('S', 'T', '1', '2');
break;
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 */
}
}
{
ARG_0,
ARG_BITRATE,
+ ARG_FORCE_KEY_FRAME,
};
#define DEFAULT_BITRATE 0
GSTOMX_BOILERPLATE (GstOmxBaseVideoEnc, gst_omx_base_videoenc, GstOmxBaseFilter,
GST_OMX_BASE_FILTER_TYPE);
+/* modification: postprocess for outputbuf. in this videoenc case, set sync frame */
static void
-process_output_buf(GstOmxBaseFilter * omx_base, GstBuffer * buf, OMX_BUFFERHEADERTYPE *omx_buffer)
+process_output_buf(GstOmxBaseFilter * omx_base, GstBuffer **buf, OMX_BUFFERHEADERTYPE *omx_buffer)
{
GstOmxBaseVideoEnc *self;
GST_LOG_OBJECT (self, "base videoenc process_output_buf enter");
- /* MODIFICATION: Set sync frame info while encoding */
+ /* modification: set sync frame info while encoding */
if (omx_buffer->nFlags & OMX_BUFFERFLAG_SYNCFRAME) {
- GST_BUFFER_FLAG_UNSET(buf, GST_BUFFER_FLAG_DELTA_UNIT);
+ GST_BUFFER_FLAG_UNSET(*buf, GST_BUFFER_FLAG_DELTA_UNIT);
} else {
- GST_BUFFER_FLAG_SET(buf, GST_BUFFER_FLAG_DELTA_UNIT);
+ GST_BUFFER_FLAG_SET(*buf, GST_BUFFER_FLAG_DELTA_UNIT);
}
}
+/* modification: user force I frame */
+static void
+add_force_key_frame(GstOmxBaseVideoEnc *enc)
+{
+ GstOmxBaseFilter *omx_base;
+ GOmxCore *gomx;
+ OMX_CONFIG_INTRAREFRESHVOPTYPE config;
+ omx_base = GST_OMX_BASE_FILTER (enc);
+ gomx = (GOmxCore *) omx_base->gomx;
+
+ GST_INFO_OBJECT (enc, "set forcing I frame.");
+
+ if (!omx_base->out_port || !gomx->omx_handle) {
+ GST_WARNING_OBJECT (enc, "failed to set force-i-frame...");
+ return;
+ }
+
+ G_OMX_INIT_PARAM (config);
+ config.nPortIndex = omx_base->out_port->port_index;
+
+ OMX_GetConfig (gomx->omx_handle, OMX_IndexConfigVideoIntraVOPRefresh, &config);
+ config.IntraRefreshVOP = OMX_TRUE;
+
+ OMX_SetConfig (gomx->omx_handle, OMX_IndexConfigVideoIntraVOPRefresh, &config);
+}
+
+/* modification: get codec_data from omx component and set it caps */
+static void
+process_output_caps(GstOmxBaseFilter * self, OMX_BUFFERHEADERTYPE *omx_buffer)
+{
+ GstBuffer *buf;
+ GstCaps *caps = NULL;
+ GstStructure *structure;
+ GValue value = { 0, {{0}
+ }
+ };
+
+ caps = gst_pad_get_negotiated_caps (self->srcpad);
+ caps = gst_caps_make_writable (caps);
+ structure = gst_caps_get_structure (caps, 0);
+
+ g_value_init (&value, GST_TYPE_BUFFER);
+ buf = gst_buffer_new_and_alloc (omx_buffer->nFilledLen);
+ memcpy (GST_BUFFER_DATA (buf),
+ omx_buffer->pBuffer + omx_buffer->nOffset, omx_buffer->nFilledLen);
+ gst_value_set_buffer (&value, buf);
+ gst_buffer_unref (buf);
+ gst_structure_set_value (structure, "codec_data", &value);
+ g_value_unset (&value);
+
+ gst_pad_set_caps (self->srcpad, caps);
+ gst_caps_unref (caps);
+}
+
static void
type_base_init (gpointer g_class)
{
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;
{
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 */
{
"Encoding bit-rate",
0, G_MAXUINT, DEFAULT_BITRATE,
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
+ g_object_class_install_property (gobject_class, ARG_FORCE_KEY_FRAME,
+ g_param_spec_boolean ("force-i-frame", "force the encoder to produce I frame",
+ "force the encoder to produce I frame",
+ FALSE,
+ G_PARAM_WRITABLE | G_PARAM_STATIC_STRINGS));
}
basefilter_class->process_output_buf = process_output_buf;
+ basefilter_class->process_output_caps = process_output_caps;
}
static gboolean
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;
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);
}
param.format.video.eCompressionFormat = self->compression_format;
- if (self->bitrate != 0)
+ if (self->bitrate > 0)
param.format.video.nBitrate = self->bitrate;
OMX_SetParameter (gomx->omx_handle, OMX_IndexParamPortDefinition, ¶m);
}
}
+ /* modification: set bitrate by using OMX_IndexParamVideoBitrate macro*/
+ if (self->bitrate > 0) {
+ OMX_VIDEO_PARAM_BITRATETYPE param;
+ G_OMX_INIT_PARAM (param);
+
+ param.nPortIndex = omx_base->out_port->port_index;
+ OMX_GetParameter (gomx->omx_handle, OMX_IndexParamVideoBitrate, ¶m);
+
+ param.nTargetBitrate = self->bitrate;
+ param.eControlRate = OMX_Video_ControlRateConstant;
+ GST_INFO_OBJECT (self, "set bitrate (OMX_Video_ControlRateConstant): %d", param.nTargetBitrate);
+
+ OMX_SetParameter (gomx->omx_handle, OMX_IndexParamVideoBitrate, ¶m);
+ }
+
GST_INFO_OBJECT (omx_base, "end");
}
gst_pad_set_setcaps_function (omx_base->sinkpad, sink_setcaps);
self->bitrate = DEFAULT_BITRATE;
+ self->use_force_key_frame = FALSE;
}
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;
guint bitrate;
gint framerate_num;
gint framerate_denom;
+ gboolean use_force_key_frame;
};
struct GstOmxBaseVideoEncClass
--- /dev/null
+/*
+ * Copyright (C) 2012 Samsung Electronics Co., Ltd. All rights reserved.
+ *
+ * Author: Sunghyun Eum <sunghyun.eum@samsung.com>
+ *
+ * This library is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by the
+ * Free Software Foundation; either version 2.1 of the License, or (at your option)
+ * any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
+ * License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, write to the Free Software Foundation, Inc., 51
+ * Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#ifndef GSTOMX_H264_H
+#define GSTOMX_H264_H
+
+G_BEGIN_DECLS
+
+/* modification: byte-stream(nal) - packetized (3gpp) converting (h264dec, h264enc) */
+#define GSTOMX_H264_MDATA 6 /* packtized meta data size. (ver, profile...) */
+#define GSTOMX_H264_A_DCI_LEN 4 /* 4-byte sps, pps size field in append dci*/
+#define GSTOMX_H264_C_DCI_LEN 2 /* 2-byte sps, pps size field in codec data*/
+#define GSTOMX_H264_CNT_LEN 1 /* 1-byte sps, pps count field */
+
+#define GSTOMX_H264_NAL_START_LEN 4
+#define GSTOMX_H264_SPSPPS_LEN 2
+
+typedef enum
+{
+ GSTOMX_H264_NUT_UNKNOWN = 0,
+ GSTOMX_H264_NUT_SLICE = 1,
+ GSTOMX_H264_NUT_DPA = 2,
+ GSTOMX_H264_NUT_DPB = 3,
+ GSTOMX_H264_NUT_DPC = 4,
+ GSTOMX_H264_NUT_IDR = 5,
+ GSTOMX_H264_NUT_SEI = 6,
+ GSTOMX_H264_NUT_SPS = 7,
+ GSTOMX_H264_NUT_PPS = 8,
+ GSTOMX_H264_NUT_AUD = 9,
+ GSTOMX_H264_NUT_EOSEQ = 10,
+ GSTOMX_H264_NUT_EOSTREAM = 11,
+ GSTOMX_H264_NUT_FILL = 12,
+ GSTOMX_H264_NUT_MIXED = 24,
+} GSTOMX_H264_NAL_UNIT_TYPE;
+
+typedef enum
+{
+ GSTOMX_H264_FORMAT_UNKNOWN,
+ GSTOMX_H264_FORMAT_3GPP,
+ GSTOMX_H264_FORMAT_NALU
+} GSTOMX_H264_STREAM_FORMAT;
+
+#define GSTOMX_H264_WB32(p, d) \
+ do { \
+ ((unsigned char*)(p))[3] = (d); \
+ ((unsigned char*)(p))[2] = (d)>>8; \
+ ((unsigned char*)(p))[1] = (d)>>16; \
+ ((unsigned char*)(p))[0] = (d)>>24; \
+ } while(0);
+
+#define GSTOMX_H264_WB16(p, d) \
+ do { \
+ ((unsigned char*)(p))[1] = (d); \
+ ((unsigned char*)(p))[0] = (d)>>8; \
+ } while(0);
+
+#define GSTOMX_H264_RB16(x) ((((const unsigned char*)(x))[0] << 8) | ((const unsigned char*)(x))[1])
+
+#define GSTOMX_H264_RB32(x) ((((const unsigned char*)(x))[0] << 24) | \
+ (((const unsigned char*)(x))[1] << 16) | \
+ (((const unsigned char*)(x))[2] << 8) | \
+ ((const unsigned char*)(x))[3])
+
+G_END_DECLS
+#endif /* GSTOMX_H264_H */
GSTOMX_BOILERPLATE (GstOmxH264Dec, gst_omx_h264dec, GstOmxBaseVideoDec,
GST_OMX_BASE_VIDEODEC_TYPE);
+/*
+ * description : find stream format(3gpp or nalu)
+ * params : @self : GstOmxH264Dec, @buf: input gstbuffer in pad_chain
+ * return : none
+ * comments : finding whether the stream format of input buf is 3GPP or Elementary Stream(nalu)
+ */
+static void
+check_frame (GstOmxH264Dec *self, GstBuffer * buf)
+{
+ guint buf_size = GST_BUFFER_SIZE (buf);
+ guint8 *buf_data = GST_BUFFER_DATA (buf);
+ unsigned int checklen = 0;
+
+ if (buf_data == NULL || buf_size < GSTOMX_H264_NAL_START_LEN) {
+ self->h264Format = GSTOMX_H264_FORMAT_UNKNOWN;
+ GST_INFO_OBJECT(self, "H264 format is unknown");
+ return;
+ }
+
+ self->h264Format = GSTOMX_H264_FORMAT_3GPP;
+
+ do
+ {
+ if (buf_data[checklen] == 0x00 &&
+ buf_data[checklen+1] == 0x00 &&
+ buf_data[checklen+2] == 0x00 &&
+ buf_data[checklen+3] == 0x01) {
+ self->h264Format = GSTOMX_H264_FORMAT_NALU;
+ GST_INFO_OBJECT(self, "H264 format is NALU");
+ break;
+ } else {
+ checklen++;
+ }
+ } while(checklen < (buf_size - GSTOMX_H264_NAL_START_LEN));
+
+ if (GSTOMX_H264_FORMAT_3GPP == self->h264Format)
+ GST_INFO_OBJECT(self, "H264 format is 3GPP");
+}
+
+/*
+ * description : convert input 3gpp buffer to nalu based buffer
+ * params : @self : GstOmxH264Dec, @buf: buffer to be converted
+ * return : none
+ * comments : none
+ */
+static void
+convert_frame (GstOmxH264Dec *self, GstBuffer **buf)
+{
+ OMX_U8 frameType;
+ OMX_U32 nalSize = 0;
+ OMX_U32 cumulSize = 0;
+ OMX_U32 offset = 0;
+ OMX_U32 nalHeaderSize = 0;
+ OMX_U32 outSize = 0;
+ OMX_U8 *frame_3gpp = GST_BUFFER_DATA(*buf);
+ OMX_U32 frame_3gpp_size = GST_BUFFER_SIZE(*buf);
+ GstBuffer *nalu_next_buf = NULL;
+ GstBuffer *nalu_buf = NULL;
+
+ do {
+ /* get NAL Length based on length of length*/
+ if (self->h264NalLengthSize == 1) {
+ nalSize = frame_3gpp[0];
+ } else if (self->h264NalLengthSize == 2) {
+ nalSize = GSTOMX_H264_RB16(frame_3gpp);
+ } else {
+ nalSize = GSTOMX_H264_RB32(frame_3gpp);
+ }
+
+ GST_LOG_OBJECT(self, "packetized frame size = %d", nalSize);
+
+ frame_3gpp += self->h264NalLengthSize;
+
+ /* Checking frame type */
+ frameType = *frame_3gpp & 0x1f;
+
+ switch (frameType)
+ {
+ case GSTOMX_H264_NUT_SLICE:
+ GST_LOG_OBJECT(self, "Frame is non-IDR frame...");
+ break;
+ case GSTOMX_H264_NUT_IDR:
+ GST_LOG_OBJECT(self, "Frame is an IDR frame...");
+ break;
+ case GSTOMX_H264_NUT_SEI:
+ GST_LOG_OBJECT(self, "Found SEI Data...");
+ break;
+ case GSTOMX_H264_NUT_SPS:
+ GST_LOG_OBJECT(self, "Found SPS data...");
+ break;
+ case GSTOMX_H264_NUT_PPS:
+ GST_LOG_OBJECT(self, "Found PPS data...");
+ break;
+ case GSTOMX_H264_NUT_EOSEQ:
+ GST_LOG_OBJECT(self, "End of sequence...");
+ break;
+ case GSTOMX_H264_NUT_EOSTREAM:
+ GST_LOG_OBJECT(self, "End of stream...");
+ break;
+ case GSTOMX_H264_NUT_DPA:
+ case GSTOMX_H264_NUT_DPB:
+ case GSTOMX_H264_NUT_DPC:
+ case GSTOMX_H264_NUT_AUD:
+ case GSTOMX_H264_NUT_FILL:
+ case GSTOMX_H264_NUT_MIXED:
+ break;
+ default:
+ GST_INFO_OBJECT(self, "Unknown Frame type: %d\n", frameType);
+ goto EXIT;
+ }
+
+ /* if nal size is same, we can change only start code */
+ if((nalSize + GSTOMX_H264_NAL_START_LEN) == frame_3gpp_size) {
+ GST_LOG_OBJECT(self, "only change start code");
+ GSTOMX_H264_WB32(GST_BUFFER_DATA(*buf), 1);
+ return;
+ }
+
+ /* Convert 3GPP Frame to NALU Frame */
+ offset = outSize;
+ nalHeaderSize = offset ? 3 : 4;
+
+ outSize += nalSize + nalHeaderSize;
+
+ if ((nalSize > frame_3gpp_size)||(outSize < 0)) {
+ GST_ERROR_OBJECT(self, "out of bounds Error. frame_nalu_size=%d", outSize);
+ goto EXIT;
+ }
+
+ if (nalu_buf) {
+ nalu_next_buf= gst_buffer_new_and_alloc(nalSize + nalHeaderSize);
+ if (nalu_next_buf == NULL) {
+ GST_ERROR_OBJECT(self, "gst_buffer_new_and_alloc failed.(nalu_next_buf)");
+ goto EXIT;
+ }
+ } else {
+ nalu_buf = gst_buffer_new_and_alloc(outSize);
+ }
+
+ if (nalu_buf == NULL) {
+ GST_ERROR_OBJECT(self, "gst_buffer_new_and_alloc failed.(nalu_buf)");
+ goto EXIT;
+ }
+
+ if (!offset) {
+ memcpy(GST_BUFFER_DATA(nalu_buf)+nalHeaderSize, frame_3gpp, nalSize);
+ GSTOMX_H264_WB32(GST_BUFFER_DATA(nalu_buf), 1);
+ } else {
+ if (nalu_next_buf) {
+ GstBuffer *nalu_joined_buf = gst_buffer_join(nalu_buf,nalu_next_buf);
+ nalu_buf = nalu_joined_buf;
+ nalu_next_buf = NULL;
+ }
+ memcpy(GST_BUFFER_DATA(nalu_buf)+nalHeaderSize+offset, frame_3gpp, nalSize);
+ (GST_BUFFER_DATA(nalu_buf)+offset)[0] = (GST_BUFFER_DATA(nalu_buf)+offset)[1] = 0;
+ (GST_BUFFER_DATA(nalu_buf)+offset)[2] = 1;
+ }
+
+ frame_3gpp += nalSize;
+ cumulSize += nalSize + self->h264NalLengthSize;
+ GST_LOG_OBJECT(self, "frame_3gpp_size = %d => frame_nalu_size=%d", frame_3gpp_size, outSize);
+ } while (cumulSize < frame_3gpp_size);
+
+ gst_buffer_copy_metadata(nalu_buf, *buf, GST_BUFFER_COPY_ALL);
+
+ if (*buf) { gst_buffer_unref (*buf); }
+ *buf = nalu_buf;
+
+ return;
+
+EXIT:
+ if (nalu_buf) { gst_buffer_unref (nalu_buf); }
+ GST_ERROR_OBJECT(self, "converting frame error.");
+
+ return;
+}
+
+/*
+ * description : convert input 3gpp buffer(codec data) to nalu based buffer
+ * params : @self : GstOmxH264Dec, @buf: buffer to be converted, @dci_nalu: converted buffer
+ * return : true on successes / false on failure
+ * comments : none
+ */
+static gboolean
+convert_dci (GstOmxH264Dec *self, GstBuffer *buf, GstBuffer **dci_nalu)
+{
+ gboolean ret = FALSE;
+ OMX_U8 *out = NULL;
+ OMX_U16 unitSize = 0;
+ OMX_U32 totalSize = 0;
+ OMX_U8 unitNb = 0;
+ OMX_U8 spsDone = 0;
+
+ OMX_U8 *pInputStream = GST_BUFFER_DATA(buf);
+ OMX_U32 pBuffSize = GST_BUFFER_SIZE(buf);
+
+ const OMX_U8 *extraData = (guchar*)pInputStream + 4;
+ static const OMX_U8 naluHeader[GSTOMX_H264_NAL_START_LEN] = {0, 0, 0, 1};
+
+ if (pInputStream != NULL) {
+ /* retrieve Length of Length*/
+ self->h264NalLengthSize = (*extraData++ & 0x03) + 1;
+ GST_INFO("Length Of Length is %d", self->h264NalLengthSize);
+ if (self->h264NalLengthSize == 3)
+ {
+ GST_INFO("LengthOfLength is WRONG...");
+ goto EXIT;
+ }
+ /* retrieve sps and pps unit(s) */
+ unitNb = *extraData++ & 0x1f;
+ GST_INFO("No. of SPS units = %u", unitNb);
+ if (!unitNb) {
+ GST_INFO("SPS is not present...");
+ goto EXIT;
+ }
+
+ while (unitNb--) {
+ /* get SPS/PPS data Length*/
+ unitSize = GSTOMX_H264_RB16(extraData);
+
+ /* Extra 4 bytes for adding delimiter */
+ totalSize += unitSize + GSTOMX_H264_NAL_START_LEN;
+
+ /* Check if SPS/PPS Data Length crossed buffer Length */
+ if ((extraData + 2 + unitSize) > (pInputStream + pBuffSize)) {
+ GST_INFO("SPS length is wrong in DCI...");
+ goto EXIT;
+ }
+
+ if (out)
+ out = g_realloc(out, totalSize);
+ else
+ out = g_malloc(totalSize);
+
+ if (!out) {
+ GST_INFO("realloc failed...");
+ goto EXIT;
+ }
+
+ /* Copy NALU header */
+ memcpy(out + totalSize - unitSize - GSTOMX_H264_NAL_START_LEN,
+ naluHeader, GSTOMX_H264_NAL_START_LEN);
+
+ /* Copy SPS/PPS Length and data */
+ memcpy(out + totalSize - unitSize, extraData + GSTOMX_H264_SPSPPS_LEN, unitSize);
+
+ extraData += (GSTOMX_H264_SPSPPS_LEN + unitSize);
+
+ if (!unitNb && !spsDone++)
+ {
+ /* Completed reading SPS data, now read PPS data */
+ unitNb = *extraData++; /* number of pps unit(s) */
+ GST_INFO( "No. of PPS units = %d", unitNb);
+ }
+ }
+
+ *dci_nalu = gst_buffer_new_and_alloc(totalSize);
+ if (*dci_nalu == NULL) {
+ GST_ERROR_OBJECT(self, " gst_buffer_new_and_alloc failed...\n");
+ goto EXIT;
+ }
+
+ memcpy(GST_BUFFER_DATA(*dci_nalu), out, totalSize);
+ GST_INFO( "Total SPS+PPS size = %d", totalSize);
+ }
+
+ ret = TRUE;
+
+EXIT:
+ if (out) {
+ g_free(out);
+ }
+ return ret;
+}
+
+
+static void
+process_input_buf (GstOmxBaseFilter * omx_base_filter, GstBuffer **buf)
+{
+ GstOmxH264Dec *h264_self;
+
+ h264_self = GST_OMX_H264DEC (omx_base_filter);
+
+ if (h264_self->h264Format == GSTOMX_H264_FORMAT_UNKNOWN) {
+ check_frame(h264_self, *buf);
+ }
+
+ if (h264_self->h264Format == GSTOMX_H264_FORMAT_3GPP) {
+
+ if (omx_base_filter->last_pad_push_return != GST_FLOW_OK ||
+ !(omx_base_filter->gomx->omx_state == OMX_StateExecuting ||
+ omx_base_filter->gomx->omx_state == OMX_StatePause)) {
+ GST_LOG_OBJECT(h264_self, "this frame will not be converted and go to out_flushing");
+ return;
+ }
+
+ GST_LOG_OBJECT(h264_self, "H264 format is 3GPP. convert to NALU");
+ convert_frame(h264_self, buf);
+ }
+
+ GST_OMX_BASE_FILTER_CLASS (parent_class)->process_input_buf (omx_base_filter, buf);
+}
+
static void
type_base_init (gpointer g_class)
{
static void
type_class_init (gpointer g_class, gpointer class_data)
{
+ GstOmxBaseFilterClass *basefilter_class;
+
+ basefilter_class = GST_OMX_BASE_FILTER_CLASS (g_class);
+
+ basefilter_class->process_input_buf = process_input_buf;
+}
+
+/* h264 dec has its own sink_setcaps for supporting nalu convert codec data */
+static gboolean
+sink_setcaps (GstPad * pad, GstCaps * caps)
+{
+ GstStructure *structure;
+ GstOmxBaseVideoDec *self;
+ GstOmxH264Dec *h264_self;
+ GstOmxBaseFilter *omx_base;
+ GOmxCore *gomx;
+ OMX_PARAM_PORTDEFINITIONTYPE param;
+ gint width = 0;
+ gint height = 0;
+
+ self = GST_OMX_BASE_VIDEODEC (GST_PAD_PARENT (pad));
+ h264_self = GST_OMX_H264DEC (GST_PAD_PARENT (pad));
+ omx_base = GST_OMX_BASE_FILTER (self);
+
+ gomx = (GOmxCore *) omx_base->gomx;
+
+ GST_INFO_OBJECT (self, "setcaps (sink)(h264): %" GST_PTR_FORMAT, caps);
+
+ g_return_val_if_fail (gst_caps_get_size (caps) == 1, FALSE);
+
+ structure = gst_caps_get_structure (caps, 0);
+
+ gst_structure_get_int (structure, "width", &width);
+ gst_structure_get_int (structure, "height", &height);
+
+ {
+ const GValue *framerate = NULL;
+ framerate = gst_structure_get_value (structure, "framerate");
+ if (framerate) {
+ self->framerate_num = gst_value_get_fraction_numerator (framerate);
+ self->framerate_denom = gst_value_get_fraction_denominator (framerate);
+ }
+ }
+
+ G_OMX_INIT_PARAM (param);
+
+ {
+ const GValue *codec_data;
+ GstBuffer *buffer;
+ gboolean ret = FALSE;
+ guint8 *buf_data = NULL;
+
+ codec_data = gst_structure_get_value (structure, "codec_data");
+ if (codec_data) {
+ buffer = gst_value_get_buffer (codec_data);
+
+ buf_data = GST_BUFFER_DATA(buffer);
+
+ if (GST_BUFFER_SIZE(buffer) < 4) GST_ERROR("codec data size is less than 4!!"); //check this.
+
+ if ((buf_data[0] == 0x00)&&(buf_data[1] == 0x00)&&
+ ((buf_data[2] == 0x01)||((buf_data[2] == 0x00)&&(buf_data[3] == 0x01)))) {
+ h264_self->h264Format = GSTOMX_H264_FORMAT_NALU;
+ GST_INFO_OBJECT(self, "H264 format is NALU");
+ } else {
+ h264_self->h264Format = GSTOMX_H264_FORMAT_3GPP;
+ GST_INFO_OBJECT(self, "H264 format is 3GPP");
+ }
+
+
+ /* if codec data is 3gpp format, convert nalu format */
+ if(h264_self->h264Format == GSTOMX_H264_FORMAT_3GPP) {
+ GstBuffer *nalu_dci = NULL;
+
+ ret = convert_dci(h264_self, buffer, &nalu_dci);
+
+ if (ret) {
+ omx_base->codec_data = nalu_dci;
+ } else {
+ if (nalu_dci) { gst_buffer_unref (nalu_dci); }
+ GST_ERROR_OBJECT(h264_self, "converting dci error.");
+ omx_base->codec_data = buffer;
+ gst_buffer_ref (buffer);
+ }
+
+ } else { /* not 3GPP format */
+ omx_base->codec_data = buffer;
+ gst_buffer_ref (buffer);
+ }
+ h264_self->h264Format = GSTOMX_H264_FORMAT_UNKNOWN;
+ }
+ }
+ /* Input port configuration. */
+ {
+ param.nPortIndex = omx_base->in_port->port_index;
+ OMX_GetParameter (gomx->omx_handle, OMX_IndexParamPortDefinition, ¶m);
+
+ param.format.video.nFrameWidth = width;
+ param.format.video.nFrameHeight = height;
+
+ OMX_SetParameter (gomx->omx_handle, OMX_IndexParamPortDefinition, ¶m);
+ }
+ return gst_pad_set_caps (pad, caps);
}
static void
type_instance_init (GTypeInstance * instance, gpointer g_class)
{
GstOmxBaseVideoDec *omx_base;
+ GstOmxBaseFilter *omx_base_filter;
omx_base = GST_OMX_BASE_VIDEODEC (instance);
+ omx_base_filter = GST_OMX_BASE_FILTER (instance);
omx_base->compression_format = OMX_VIDEO_CodingAVC;
+
+ gst_pad_set_setcaps_function (omx_base_filter->sinkpad, sink_setcaps);
}
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
#include "gstomx_h264enc.h"
#include "gstomx.h"
+enum
+{
+ ARG_0,
+ ARG_APPEND_DCI,
+ ARG_BYTE_STREAM,
+ ARG_SLICE_MODE,
+ ARG_SLICE_SIZE,
+};
+
GSTOMX_BOILERPLATE (GstOmxH264Enc, gst_omx_h264enc, GstOmxBaseVideoEnc,
GST_OMX_BASE_VIDEOENC_TYPE);
+/*
+ * description : convert byte-stream format to packetized frame
+ * params : @self : GstOmxH264Enc, @buf: byte-stream buf, @sync: notify this buf is sync frame
+ * return : none
+ * comments :
+ */
+static void
+convert_to_packetized_frame (GstOmxH264Enc *self, GstBuffer **buf)
+{
+ unsigned char *data = GST_BUFFER_DATA (*buf);
+ unsigned int size = GST_BUFFER_SIZE(*buf);
+ int idx = 0;
+ gint start_idx = -1;
+ unsigned char *nalu_start = NULL;
+ GstOmxBaseFilter *omx_base = GST_OMX_BASE_FILTER(self);
+
+ GST_LOG_OBJECT (self, "convert_to_packtized format. size=%d sliceMode=%d",
+ GST_BUFFER_SIZE(*buf), self->slice_fmo.eSliceMode);
+
+ if (omx_base->gomx->component_vendor == GOMX_VENDOR_SLSI &&
+ self->slice_fmo.eSliceMode == OMX_VIDEO_SLICEMODE_AVCDefault) { /* 1 slice per frame */
+ GST_LOG_OBJECT (self, " handle single NALU per buffer");
+ while (idx < size - GSTOMX_H264_NAL_START_LEN) {
+ if (((data[idx] == 0x00) && (data[idx+1] == 0x00) && (data[idx+2] == 0x00)&& (data[idx+3] == 0x01)) ||
+ ((data[idx] == 0x00) && (data[idx+1] == 0x00) && (data[idx+2] == 0x01))) {
+ if (data[idx+2] == 0x01) {
+ GST_ERROR_OBJECT (self, "ERROR : NALU header is 3-bytes, yet to support !!");
+ } else {
+ GSTOMX_H264_WB32(data + idx, size - idx - GSTOMX_H264_NAL_START_LEN);
+ return;
+ }
+ }
+ idx++;
+ } /* while */
+ } else { /* handle multiple NALUs in one buffer */
+ GST_LOG_OBJECT (self, " handle multiple NALUs per buffer");
+ while (idx < size - GSTOMX_H264_NAL_START_LEN) {
+ if (((data[idx] == 0x00) && (data[idx+1] == 0x00) && (data[idx+2] == 0x00)&& (data[idx+3] == 0x01)) ||
+ ((data[idx] == 0x00) && (data[idx+1] == 0x00) && (data[idx+2] == 0x01))) {
+ if (data[idx+2] == 0x01) {
+ GST_ERROR_OBJECT (self, "ERROR : NALU header is 3-bytes, yet to support !!");
+ } else {
+ if (start_idx >= 0) {
+ if (idx <= start_idx) {
+ GST_ERROR_OBJECT (self, "ERROR : idx <= start_idx !!");
+ return;
+ }
+ GST_LOG_OBJECT (self, "size of current nal unit = %d", idx-start_idx);
+ GSTOMX_H264_WB32(nalu_start, idx - start_idx - GSTOMX_H264_NAL_START_LEN);
+ }
+ start_idx = idx;
+ nalu_start = data + idx;
+ idx++;
+ }
+ }
+ idx++;
+ } /* while */
+ idx += GSTOMX_H264_NAL_START_LEN;
+
+ /* converting last nal unit */
+ if (start_idx >= 0) {
+ GST_LOG_OBJECT (self, "size of current nal unit = %d", idx-start_idx);
+ GSTOMX_H264_WB32(nalu_start, idx - start_idx - GSTOMX_H264_NAL_START_LEN);
+ }
+ }
+}
+
+/*
+ * description : convert byte-stream codec data to packtized codec_data
+ * params : @self : GstOmxH264Enc, @inbuf: byte-stream codec data (omx buf), @outbuf: packetized codec_data
+ * return : true on successes / false on failure
+ * comments :
+ */
+static gboolean
+convert_to_packetized_dci (GstOmxH264Enc *self, unsigned char *nalu_dci, unsigned nalu_dci_len,
+ GstBuffer **packetized_dci, gint *out_sps_cnt, gint *out_pps_cnt)
+{
+ gint idx = 0;
+ gint sps_cnt = 0;
+ gint pps_cnt = 0;
+ GQueue *sps_queue = 0;
+ GQueue *pps_queue = 0;
+ unsigned char *packet_dci = NULL;
+ gint prev_nalu_start = 0;
+ gint prev_nalu_type = GSTOMX_H264_NUT_UNKNOWN;
+ gint sps_size = 0;
+ gint pps_size = 0;
+ GstBuffer *sps_data = NULL;
+ GstBuffer *pps_data = NULL;
+ GstBuffer *queue_data = NULL;
+ gint nal_type = GSTOMX_H264_NUT_UNKNOWN;
+ unsigned char profile = 0;
+ unsigned char level = 0;
+ unsigned char profile_comp = 0;
+ gboolean bret = TRUE;
+ gboolean single_sps_pps = FALSE; /* if there is only 1 sps, pps set, */
+
+ sps_queue = g_queue_new ();
+ pps_queue = g_queue_new ();
+
+ /* find no.of SPS & PPS units */
+ while (idx < nalu_dci_len) {
+ if ((nalu_dci[idx] == 0x00) && (nalu_dci[idx+1] == 0x00) && (nalu_dci[idx+2] == 0x01)) {
+ /* copy previous nal unit */
+ if (prev_nalu_start && prev_nalu_type == GSTOMX_H264_NUT_SPS) {
+ if (nalu_dci[idx -1] == 0x00) {
+ sps_size = idx -1 - prev_nalu_start;
+ } else {
+ sps_size = idx - prev_nalu_start;
+ }
+ sps_data = gst_buffer_new_and_alloc (sps_size + GSTOMX_H264_C_DCI_LEN);
+ if (!sps_data) {
+ GST_ERROR_OBJECT (self, "failed to allocate memory..");
+ bret = FALSE;
+ goto exit;
+ }
+ GSTOMX_H264_WB16(GST_BUFFER_DATA(sps_data), sps_size);
+ memcpy (GST_BUFFER_DATA(sps_data) + GSTOMX_H264_C_DCI_LEN, nalu_dci + prev_nalu_start, sps_size);
+ g_queue_push_tail (sps_queue, sps_data);
+ } else if (prev_nalu_start && prev_nalu_type == GSTOMX_H264_NUT_PPS) {
+ if (nalu_dci[idx -1] == 0x00) {
+ pps_size = idx -1 - prev_nalu_start;
+ } else {
+ pps_size = idx - prev_nalu_start;
+ }
+ pps_data = gst_buffer_new_and_alloc (pps_size + GSTOMX_H264_C_DCI_LEN);
+ if (!pps_data) {
+ GST_ERROR_OBJECT (self, "failed to allocate memory..");
+ bret = FALSE;
+ goto exit;
+ }
+ GSTOMX_H264_WB16(GST_BUFFER_DATA(pps_data), pps_size);
+ memcpy (GST_BUFFER_DATA(pps_data) + GSTOMX_H264_C_DCI_LEN, nalu_dci + prev_nalu_start, pps_size);
+ g_queue_push_tail (pps_queue, pps_data);
+ }
+ /* present nalu type */
+ nal_type = nalu_dci[idx+3] & 0x1f;
+
+ if (nal_type == GSTOMX_H264_NUT_SPS) {
+ sps_cnt++;
+ prev_nalu_start = idx + 3;
+ prev_nalu_type =GSTOMX_H264_NUT_SPS;
+ profile = nalu_dci[idx+4];
+ level = nalu_dci[idx+6];
+ GST_INFO_OBJECT (self, "Profile Number = %d and Level = %d...", nalu_dci[idx+4], level);
+ GST_INFO_OBJECT (self, "Profile is %s", (profile == 66) ? "BaseLine Profile": (profile == 77)? "Main Profile": profile==88 ?
+ "Extended Profile": profile==100 ? "High Profile": "Not Supported codec");
+ } else if ((nalu_dci[idx+3] & 0x1f) == GSTOMX_H264_NUT_PPS) {
+ pps_cnt++;
+ prev_nalu_start = idx + 3;
+ prev_nalu_type = GSTOMX_H264_NUT_PPS;
+ }
+ }
+ idx++;
+ }
+
+ /* copy previous nal unit */
+ if (prev_nalu_start && prev_nalu_type == GSTOMX_H264_NUT_SPS) {
+ sps_size = idx - prev_nalu_start;
+
+ sps_data = gst_buffer_new_and_alloc (sps_size + GSTOMX_H264_C_DCI_LEN);
+ if (!sps_data) {
+ GST_ERROR_OBJECT (self, "failed to allocate memory..");
+ bret = FALSE;
+ goto exit;
+ }
+
+ GSTOMX_H264_WB16(GST_BUFFER_DATA(sps_data), sps_size);
+ memcpy (GST_BUFFER_DATA(sps_data) + GSTOMX_H264_C_DCI_LEN, nalu_dci + prev_nalu_start, sps_size);
+ g_queue_push_tail (sps_queue, sps_data);
+
+ } else if (prev_nalu_start && prev_nalu_type == GSTOMX_H264_NUT_PPS) {
+ pps_size = idx - prev_nalu_start;
+
+ pps_data = gst_buffer_new_and_alloc (pps_size + GSTOMX_H264_C_DCI_LEN);
+ if (!pps_data) {
+ GST_ERROR_OBJECT (self, "failed to allocate memory..");
+ bret = FALSE;
+ goto exit;
+ }
+
+ GSTOMX_H264_WB16(GST_BUFFER_DATA(pps_data), pps_size);
+ memcpy (GST_BUFFER_DATA(pps_data) + GSTOMX_H264_C_DCI_LEN, nalu_dci + prev_nalu_start, pps_size);
+ g_queue_push_tail (pps_queue, pps_data);
+ }
+
+ /* make packetized codec data */
+ if (sps_cnt == 1 && pps_cnt == 1) {
+ single_sps_pps = TRUE;
+ *packetized_dci = gst_buffer_new_and_alloc(GSTOMX_H264_MDATA + GSTOMX_H264_C_DCI_LEN + sps_size + GSTOMX_H264_CNT_LEN + GSTOMX_H264_C_DCI_LEN + pps_size);
+ GST_LOG_OBJECT(self, "allocate packetized_dci in case of single sps, pps. (size=%d)", GST_BUFFER_SIZE(*packetized_dci));
+ } else {
+ *packetized_dci = gst_buffer_new_and_alloc(GSTOMX_H264_MDATA);
+ GST_LOG_OBJECT(self, "allocate packetized_dci in case of multi sps, pps");
+ }
+
+ if (*packetized_dci == NULL) {
+ GST_ERROR_OBJECT (self, "Failed to allocate memory..");
+ bret = FALSE;
+ goto exit;
+ }
+
+ packet_dci = GST_BUFFER_DATA(*packetized_dci);
+ packet_dci[0] = 0x01; /* configurationVersion */
+ packet_dci[1] = profile; /* AVCProfileIndication */
+ packet_dci[2] = profile_comp; /* profile_compatibility*/
+ packet_dci[3] = level; /* AVCLevelIndication */
+ packet_dci[4] = 0xff; /* Reserver (6bits.111111) + LengthSizeMinusOne (2bits). lengthoflength = 4 for present */
+ packet_dci[5] = 0xe0 | sps_cnt; /* Reserver (3bits. 111) + numOfSequenceParameterSets (5bits) */
+
+ /* copy SPS sets */
+ while (!g_queue_is_empty (sps_queue)) {
+ sps_data = g_queue_pop_head (sps_queue);
+
+ if (TRUE == single_sps_pps) {
+ memcpy(packet_dci + GSTOMX_H264_MDATA, GST_BUFFER_DATA(sps_data), GST_BUFFER_SIZE(sps_data));
+ gst_buffer_unref(sps_data);
+ sps_data = NULL;
+ } else {
+ *packetized_dci = gst_buffer_join(*packetized_dci, sps_data);
+ }
+ }
+
+ /* add number of PPS sets (1byte)*/
+ if (TRUE == single_sps_pps) {
+ packet_dci[GSTOMX_H264_MDATA + GSTOMX_H264_C_DCI_LEN + sps_size] = pps_cnt;
+ } else {
+ GstBuffer *next_data = gst_buffer_new_and_alloc(GSTOMX_H264_CNT_LEN);
+ if (!next_data) {
+ GST_ERROR_OBJECT (self, "Failed to allocate memory..");
+ bret = FALSE;
+ goto exit;
+ }
+ GST_BUFFER_DATA(next_data)[0] = pps_cnt;
+ *packetized_dci = gst_buffer_join(*packetized_dci, next_data);
+ }
+
+ /* copy PPS sets */
+ while (!g_queue_is_empty (pps_queue)) {
+ pps_data = g_queue_pop_head (pps_queue);
+
+ if (TRUE == single_sps_pps) {
+ memcpy(packet_dci + GSTOMX_H264_MDATA + GSTOMX_H264_C_DCI_LEN + sps_size + GSTOMX_H264_CNT_LEN,
+ GST_BUFFER_DATA(pps_data), GST_BUFFER_SIZE(pps_data));
+ gst_buffer_unref(pps_data);
+ pps_data = NULL;
+ } else {
+ *packetized_dci = gst_buffer_join(*packetized_dci, pps_data);
+ }
+ }
+
+exit:
+ while (!g_queue_is_empty (sps_queue)) {
+ queue_data = g_queue_pop_head (sps_queue);
+ gst_buffer_unref (queue_data);
+ queue_data = NULL;
+ }
+ g_queue_free (sps_queue);
+
+ while (!g_queue_is_empty (pps_queue)) {
+ queue_data = g_queue_pop_head (pps_queue);
+ gst_buffer_unref (queue_data);
+ queue_data = NULL;
+ }
+ g_queue_free (pps_queue);
+
+ *out_sps_cnt = sps_cnt;
+ *out_pps_cnt = sps_cnt;
+
+ return bret;
+}
+
+/*
+ * description : resotre packtized dci (codec_data)
+ * params : @self : GstOmxH264Enc, @inbuf: codec data, @outbuf: h264enc->dci
+ * return : none
+ * comments :
+ * from original packetized codec_data: METADATA(6) + SPS_CNT(0) + {SPS_SIZE(2)+SPS_DATA(sps_size)}*n +
+ PPS_CNT(1) + {PPS_SIZE(2)+PPS_DATA(pps_size)}*n
+ * to estore packetized dci: {SPS_SIZE(4)+SPS_DATA(sps_size)}*n + {PPS_SIZE(4)+PPS_DATA(pps_size)}*n
+ */
+static gboolean
+restore_packetized_dci (GstOmxH264Enc *self, GstBuffer *inbuf, GstBuffer **outbuf, gint sps_cnt, gint pps_cnt)
+{
+ unsigned char *indata = GST_BUFFER_DATA(inbuf);
+ guint sps_size =0;
+ guint pps_size =0;
+ gint i = 0;
+ GstBuffer *next_data = NULL;
+
+ GST_INFO_OBJECT (self, "restore_packetized_dci. sps_cnt=%d, pps_cnt=%d", sps_cnt, pps_cnt);
+
+ if (sps_cnt == 1 && pps_cnt == 1) {
+ sps_size = GSTOMX_H264_RB16(indata + GSTOMX_H264_MDATA);
+ pps_size = GSTOMX_H264_RB16(indata + GSTOMX_H264_MDATA + GSTOMX_H264_C_DCI_LEN + sps_size + GSTOMX_H264_CNT_LEN);
+
+ *outbuf = gst_buffer_new_and_alloc (GSTOMX_H264_A_DCI_LEN + sps_size + GSTOMX_H264_A_DCI_LEN + pps_size);
+ if (!*outbuf) {
+ GST_ERROR_OBJECT (self, "Failed to allocate memory in gst_buffer_new_and_alloc.");
+ goto error_exit;
+ }
+
+ GSTOMX_H264_WB32(GST_BUFFER_DATA(*outbuf), sps_size);
+ indata += GSTOMX_H264_MDATA + GSTOMX_H264_C_DCI_LEN;
+ memcpy (GST_BUFFER_DATA(*outbuf) + GSTOMX_H264_A_DCI_LEN, indata, sps_size);
+ indata += sps_size;
+
+ GSTOMX_H264_WB32(GST_BUFFER_DATA(*outbuf) + GSTOMX_H264_A_DCI_LEN + sps_size, pps_size);
+ indata += GSTOMX_H264_CNT_LEN + GSTOMX_H264_C_DCI_LEN;
+ memcpy (GST_BUFFER_DATA(*outbuf) + GSTOMX_H264_A_DCI_LEN + sps_size + GSTOMX_H264_A_DCI_LEN, indata, pps_size);
+ indata += pps_size;
+ } else {
+ /* in this case, dci has multi nalu. ex) sps + sps + sps + pps + pps */
+ indata += GSTOMX_H264_MDATA;
+ *outbuf = gst_buffer_new ();
+
+ for (i = 0; i < sps_cnt; i++) {
+ sps_size = GSTOMX_H264_RB16(indata); /* metadata(6) */
+
+ next_data = gst_buffer_new_and_alloc(GSTOMX_H264_A_DCI_LEN + sps_size);
+ if (!next_data) {
+ GST_ERROR_OBJECT (self, "Failed to allocate memory in gst_buffer_new_and_alloc.");
+ goto error_exit;
+ }
+ GSTOMX_H264_WB32(GST_BUFFER_DATA(next_data), sps_size);
+ indata += GSTOMX_H264_C_DCI_LEN; /* sps size field (2 byte) */
+ memcpy (GST_BUFFER_DATA(next_data) + GSTOMX_H264_A_DCI_LEN, indata, sps_size);
+
+ *outbuf = gst_buffer_join(*outbuf, next_data);
+ indata += sps_size;
+ }
+ indata += GSTOMX_H264_CNT_LEN; /* pps cnt field (1 byte) */
+
+ for (i = 0; i < pps_cnt; i++) {
+ pps_size = GSTOMX_H264_RB16(indata);
+
+ next_data = gst_buffer_new_and_alloc(GSTOMX_H264_A_DCI_LEN + pps_size);
+ if (!next_data) {
+ GST_ERROR_OBJECT (self, "Failed to allocate memory in gst_buffer_new_and_alloc.");
+ goto error_exit;
+ }
+ GSTOMX_H264_WB32(GST_BUFFER_DATA(next_data), pps_size);
+ indata += GSTOMX_H264_C_DCI_LEN;
+ memcpy (GST_BUFFER_DATA(next_data) + GSTOMX_H264_A_DCI_LEN, indata, pps_size);
+
+ *outbuf = gst_buffer_join(*outbuf, next_data);
+ indata += pps_size;
+ }
+ }
+
+ return TRUE;
+
+error_exit:
+ if (*outbuf) {
+ gst_buffer_unref(*outbuf);
+ *outbuf = NULL;
+ }
+ return FALSE;
+}
+
+/*
+ * description : set sync frame. if needed, convert output to packetized format, append dci every I frame.
+ * params : @self : GstOmxBaseFilter, @buf: encoder output frame, @omx_buffer: omx_buffer
+ * return : none
+ * comments :
+ */
+static void
+process_output_buf(GstOmxBaseFilter * omx_base, GstBuffer **buf, OMX_BUFFERHEADERTYPE *omx_buffer)
+{
+ GstOmxH264Enc *self;
+ self = GST_OMX_H264ENC (omx_base);
+
+ if (!self->byte_stream) { /* Packtized Format */
+ convert_to_packetized_frame (self, buf); /* convert byte stream to packetized stream */
+ GST_LOG_OBJECT (self, "output buffer is converted to Packtized format.");
+ } else {
+ GST_LOG_OBJECT (self, "output buffer is Byte-stream format.");
+ }
+
+ if ((self->first_frame) ||(self->append_dci && omx_buffer->nFlags & OMX_BUFFERFLAG_SYNCFRAME)) {
+ GstBuffer *newbuf = NULL;
+ GST_LOG_OBJECT (self, "append dci at %s by gst-openmax.", (self->first_frame == TRUE) ? "first frame": "every I frame");
+
+ if (self->dci == NULL) {
+ GST_ERROR_OBJECT (self, "dci is null. can not append dci.");
+ self->append_dci = FALSE;
+ } else {
+ newbuf = gst_buffer_merge (self->dci, *buf);
+ if (newbuf == NULL) {
+ GST_ERROR_OBJECT (self, "Failed to gst_buffer_merge.");
+ return;
+ }
+ gst_buffer_copy_metadata(newbuf, *buf, GST_BUFFER_COPY_ALL);
+ if (*buf)
+ gst_buffer_unref(*buf);
+ *buf = newbuf;
+ }
+ }
+
+ if (self->first_frame == TRUE)
+ self->first_frame = FALSE;
+
+ /* Set sync frame info while encoding */
+ if (omx_buffer->nFlags & OMX_BUFFERFLAG_SYNCFRAME) {
+ GST_BUFFER_FLAG_UNSET(*buf, GST_BUFFER_FLAG_DELTA_UNIT);
+ } else {
+ GST_BUFFER_FLAG_SET(*buf, GST_BUFFER_FLAG_DELTA_UNIT);
+ }
+}
+
+/*
+ * description : if needed, convert byte-stream codec_data to packetized format, save dci.
+ * params : @self : GstOmxBaseFilter, @omx_buffer: omx_buffer
+ * return : none
+ * comments :
+ */
+static void
+process_output_caps(GstOmxBaseFilter * base, OMX_BUFFERHEADERTYPE *omx_buffer)
+{
+ GstOmxH264Enc *h264enc;
+ gboolean ret = FALSE;
+
+ h264enc = GST_OMX_H264ENC (base);
+
+ if (!h264enc->byte_stream) { /* Packtized Format */
+ GstCaps *caps = NULL;
+ GstStructure *structure;
+ GstBuffer *codec_data = NULL;
+ gint sps_cnt = 0;
+ gint pps_cnt = 0;
+ GValue value = { 0, {{0}
+ }
+ };
+ g_value_init (&value, GST_TYPE_BUFFER);
+
+ GST_INFO_OBJECT (h264enc, "Packtized Format: set src caps with codec-data");
+
+ /* convert codec_data to packtized format.. (metadata(cnt) + sps_size + sps + cnt + pps_size + pps) */
+ ret = convert_to_packetized_dci (h264enc, omx_buffer->pBuffer + omx_buffer->nOffset, omx_buffer->nFilledLen,
+ &codec_data, &sps_cnt, &pps_cnt);
+
+ if (FALSE == ret) {
+ GST_ERROR_OBJECT (h264enc, "ERROR: convert to packetized dci");
+ if (codec_data) {
+ gst_buffer_unref(codec_data);
+ codec_data = NULL;
+ }
+ return;
+ }
+
+ /* restore packtized format sps, pps */
+ ret = restore_packetized_dci (h264enc, codec_data, &h264enc->dci, sps_cnt, pps_cnt);
+ if (ret == FALSE) {
+ GST_ERROR_OBJECT (h264enc, "ERROR: restore packetized dci");
+ return;
+ }
+
+ GST_DEBUG_OBJECT (h264enc, "adding codec_data in caps");
+ caps = gst_pad_get_negotiated_caps (base->srcpad);
+ caps = gst_caps_make_writable (caps);
+ structure = gst_caps_get_structure (caps, 0);
+
+ /* packetized format, set codec_data field in caps */
+ gst_value_set_buffer (&value,codec_data);
+ gst_buffer_unref (codec_data);
+ codec_data = NULL;
+ gst_structure_set_value (structure, "codec_data", &value);
+ g_value_unset (&value);
+
+ gst_pad_set_caps (base->srcpad, caps);
+ gst_caps_unref (caps);
+ } else {
+ /* byte-stream format */
+ GST_INFO_OBJECT (h264enc, "Byte-stream Format");
+ h264enc->dci = gst_buffer_new_and_alloc (omx_buffer->nFilledLen);
+ if (!h264enc->dci) {
+ GST_ERROR_OBJECT (h264enc, "failed to allocate memory...");
+ return;
+ }
+ memcpy (GST_BUFFER_DATA (h264enc->dci),
+ omx_buffer->pBuffer + omx_buffer->nOffset, omx_buffer->nFilledLen);
+ }
+}
+
+static void
+finalize (GObject * obj)
+{
+ GstOmxH264Enc *h264enc;
+ h264enc = GST_OMX_H264ENC (obj);
+
+ GST_LOG_OBJECT (h264enc, "h264enc finalize enter");
+
+ if (h264enc->dci) {
+ gst_buffer_unref(h264enc->dci);
+ h264enc->dci = NULL;
+ }
+ G_OBJECT_CLASS (parent_class)->finalize (obj);
+}
+
static void
type_base_init (gpointer g_class)
{
}
static void
+set_property (GObject * obj,
+ guint prop_id, const GValue * value, GParamSpec * pspec)
+{
+ GstOmxH264Enc *self;
+ GstOmxBaseFilter *omx_base;
+
+ self = GST_OMX_H264ENC (obj);
+ omx_base = GST_OMX_BASE_FILTER (obj);
+
+ switch (prop_id) {
+ case ARG_APPEND_DCI:
+ self->append_dci = g_value_get_boolean (value);
+ GST_INFO_OBJECT (self, "Appending dci = %d", self->append_dci);
+ break;
+
+ case ARG_BYTE_STREAM:
+ self->byte_stream = g_value_get_boolean (value);
+ GST_INFO_OBJECT (self, "Byte stream = %d", self->byte_stream);
+ break;
+
+ case ARG_SLICE_MODE:
+ {
+ OMX_VIDEO_PARAM_AVCSLICEFMO param;
+ OMX_ERRORTYPE ret;
+
+ self->slice_fmo.eSliceMode = g_value_get_uint (value);
+
+ G_OMX_INIT_PARAM (param);
+ param.nPortIndex = omx_base->out_port->port_index;
+ OMX_GetParameter (omx_base->gomx->omx_handle, OMX_IndexParamVideoSliceFMO, ¶m);
+
+ param.eSliceMode = self->slice_fmo.eSliceMode;
+ param.nNumSliceGroups = 1;
+ param.nSliceGroupMapType = 1;
+ ret = OMX_SetParameter (omx_base->gomx->omx_handle, OMX_IndexParamVideoSliceFMO, ¶m);
+ if (ret != OMX_ErrorNone)
+ GST_ERROR_OBJECT (self, "failed to set eSliceMode = %d", self->slice_fmo.eSliceMode);
+ else
+ GST_INFO_OBJECT (self, "eSliceMode = %d", self->slice_fmo.eSliceMode);
+ }
+ break;
+
+ case ARG_SLICE_SIZE:
+ {
+ OMX_VIDEO_PARAM_AVCTYPE param;
+ OMX_ERRORTYPE ret;
+
+ self->h264type.nSliceHeaderSpacing = g_value_get_uint (value);
+
+ G_OMX_INIT_PARAM (param);
+ param.nPortIndex = omx_base->out_port->port_index;
+ OMX_GetParameter (omx_base->gomx->omx_handle, OMX_IndexParamVideoAvc, ¶m);
+
+ param.nSliceHeaderSpacing = self->h264type.nSliceHeaderSpacing;
+ ret= OMX_SetParameter (omx_base->gomx->omx_handle, OMX_IndexParamVideoAvc, ¶m);
+ if (ret != OMX_ErrorNone)
+ GST_ERROR_OBJECT (self, "failed to set nSliceHeaderSpacing = %d", self->h264type.nSliceHeaderSpacing);
+ else
+ GST_INFO_OBJECT (self, "nSliceHeaderSpacing = %d", self->h264type.nSliceHeaderSpacing);
+ }
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+get_property (GObject * obj, guint prop_id, GValue * value, GParamSpec * pspec)
+{
+ GstOmxH264Enc *self;
+
+ self = GST_OMX_H264ENC (obj);
+
+ switch (prop_id) {
+ case ARG_APPEND_DCI:
+ g_value_set_boolean (value, self->append_dci);
+ break;
+ case ARG_BYTE_STREAM:
+ g_value_set_boolean (value, self->byte_stream);
+ break;
+ case ARG_SLICE_MODE:
+ g_value_set_uint (value, self->slice_fmo.eSliceMode);
+ break;
+ case ARG_SLICE_SIZE:
+ g_value_set_uint (value, self->h264type.nSliceHeaderSpacing);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, prop_id, pspec);
+ break;
+ }
+}
+
+static void
type_class_init (gpointer g_class, gpointer class_data)
{
+ GObjectClass *gobject_class;
+ GstOmxBaseFilterClass *basefilter_class;
+
+ gobject_class = G_OBJECT_CLASS (g_class);
+ basefilter_class = GST_OMX_BASE_FILTER_CLASS (g_class);
+
+ {
+ gobject_class->set_property = set_property;
+ gobject_class->get_property = get_property;
+
+ g_object_class_install_property (gobject_class, ARG_APPEND_DCI,
+ g_param_spec_boolean ("append-dci", "append codec_data with every key frame for byte-stream format",
+ "append codec_data with every key frame for byte-stream format",
+ FALSE,
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
+ g_object_class_install_property (gobject_class, ARG_BYTE_STREAM,
+ g_param_spec_boolean ("byte-stream", "Output stream format",
+ "output stream format whether byte-stream format (TRUE) or packetized (FALSE)",
+ FALSE,
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
+ g_object_class_install_property (gobject_class, ARG_SLICE_MODE,
+ g_param_spec_uint ("slice-mode", "H264 encoder slice mode",
+ "slice mode: 0-Disable, 1-Fixed MB num slice, 2-Fixed bit num slice",
+ 0, G_MAXUINT, 0,
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
+ g_object_class_install_property (gobject_class, ARG_SLICE_SIZE,
+ g_param_spec_uint ("slice-size", "H264 encoder slice size",
+ "MB or bit num: MB number:1 ~ (MBCnt-1), Bit number: 1900 (bit) ~",
+ 0, G_MAXUINT, 0,
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+ }
+
+ basefilter_class->process_output_buf = process_output_buf;
+ basefilter_class->process_output_caps = process_output_caps;
+
+ gobject_class->finalize = finalize;
+
}
static void
{
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");
}
{
- 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,
"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);
}
{
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;
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
}
static void
-process_input_buf (GstOmxBaseFilter * omx_base_filter, GstBuffer * buf)
+process_input_buf (GstOmxBaseFilter * omx_base_filter, GstBuffer **buf)
{
GstOmxMpeg4Dec *self;
/* decrypt DivX DRM buffer if this is DRM */
if (self->drmContext) {
- if (DRM_SUCCESS == self->divx_sym_table.decrypt_video (self->drmContext, GST_BUFFER_DATA(buf), GST_BUFFER_SIZE(buf))) {
- GST_DEBUG_OBJECT (self, "##### DivX DRM Mode ##### decrypt video success : buffer = %d", GST_BUFFER_SIZE(buf));
+ if (DRM_SUCCESS == self->divx_sym_table.decrypt_video (self->drmContext, GST_BUFFER_DATA(*buf), GST_BUFFER_SIZE(*buf))) {
+ GST_DEBUG_OBJECT (self, "##### DivX DRM Mode ##### decrypt video success : buffer = %d", GST_BUFFER_SIZE(*buf));
} else {
- GST_ERROR_OBJECT (self, "##### DivX DRM Mode ##### decrypt video failed : buffer = %d", GST_BUFFER_SIZE(buf));
+ GST_ERROR_OBJECT (self, "##### DivX DRM Mode ##### decrypt video failed : buffer = %d", GST_BUFFER_SIZE(*buf));
}
}
}
if (i == 0) {
- g_print ("%16s: %s\n", gst_tag_get_nick (tag), str);
+ GST_LOG_OBJECT(self, "%16s: %s", gst_tag_get_nick (tag), str);
if (strcmp (gst_tag_get_nick(tag), "DRM DivX") == 0) {
if (self->drmContext == NULL) {
}
} else {
GST_LOG_OBJECT(self, "tag is not DRM Divx");
- g_print ("%16s: %s\n", "", str);
}
g_free (str);
omx_base = GST_OMX_BASE_VIDEODEC (instance);
omx_base_filter = GST_OMX_BASE_FILTER (instance);
- omx_base_filter->omx_event = mpeg4_pad_event;
+ omx_base_filter->pad_event = mpeg4_pad_event;
omx_base->compression_format = OMX_VIDEO_CodingMPEG4;
}
g_ptr_array_free (core->ports, TRUE);
g_free (core);
+ core = NULL;
}
void
¶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;
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;
port->num_buffers = 0;
port->buffer_size = 0;
port->buffers = NULL;
+ port->shared_buffer = FALSE;
port->enabled = TRUE;
port->queue = async_queue_new ();
async_queue_free (port->queue);
g_free (port->buffers);
+ port->buffers = NULL;
g_free (port);
+ port = NULL;
}
void
{
guint i;
+ if (port->type == GOMX_PORT_INPUT) {
+ GST_INFO_OBJECT(port->core->object, "Input port free buffers.");
+ } else {
+ GST_INFO_OBJECT(port->core->object, "Output port free buffers.");
+ }
+
for (i = 0; i < port->num_buffers; i++) {
OMX_BUFFERHEADERTYPE *omx_buffer;
omx_buffer = port->buffers[i];
if (omx_buffer) {
-#if 0
- /** @todo how shall we free that buffer? */
- if (!port->omx_allocate) {
- g_free (omx_buffer->pBuffer);
- omx_buffer->pBuffer = NULL;
+
+ if (port->shared_buffer) {
+ /* Modification: free pAppPrivate when input/output buffer is shared */
+ GST_INFO_OBJECT(port->core->object,
+ " %d: unref shared buffer (pAppPrivate) %p", i, omx_buffer->pAppPrivate);
+ if(omx_buffer->pAppPrivate) {
+ gst_buffer_unref(omx_buffer->pAppPrivate);
+ omx_buffer->pAppPrivate = NULL;
+ }
+
+ } else { /* this is not shared buffer */
+ if (!port->omx_allocate) {
+ /* Modification: free pBuffer allocated in plugin when OMX_UseBuffer.
+ * the component shall free only buffer header if it allocated only buffer header.*/
+ GST_INFO_OBJECT(port->core->object,
+ " %d: free buffer (pBuffer) %p", i, omx_buffer->pBuffer);
+ if (omx_buffer->pBuffer) {
+ g_free (omx_buffer->pBuffer);
+ omx_buffer->pBuffer = NULL;
+ }
+ }
}
-#endif
OMX_FreeBuffer (port->core->omx_handle, port->port_index, omx_buffer);
port->buffers[i] = NULL;
typedef struct GOmxImp GOmxImp;
typedef struct GOmxSymbolTable GOmxSymbolTable;
typedef enum GOmxPortType GOmxPortType;
+/* MODIFICATION: omx vender */
typedef enum GOmxVendor GOmxVendor;
typedef void (*GOmxCb) (GOmxCore * core);
enum GOmxVendor
{
GOMX_VENDOR_DEFAULT,
- GOMX_VENDOR_SLSI
+ GOMX_VENDOR_SLSI,
+ GOMX_VENDOR_QCT
};
/* Structures. */
gchar *library_name;
gchar *component_name;
gchar *component_role;
- GOmxVendor component_vendor; /* Add_component_vendor */
+ /* MODIFICATION: omx vender */
+ GOmxVendor component_vendor;
};
struct GOmxPort
gboolean enabled;
gboolean omx_allocate; /**< Setup with OMX_AllocateBuffer rather than OMX_UseBuffer */
AsyncQueue *queue;
+
+ gboolean shared_buffer; /* Modification */
};
/* Functions. */
Name: gst-openmax
Summary: GStreamer plug-in that allows communication with OpenMAX IL components
Version: 0.10.1
-Release: 0
+Release: 1
Group: TO_BE/FILLED_IN
License: LGPLv2.1
Source0: %{name}-%{version}.tar.gz
g_cond_free (sem->condition);
g_mutex_free (sem->mutex);
g_free (sem);
+ sem = NULL;
}
void