2 * Copyright (C) 2007-2009 Nokia Corporation.
4 * Author: Felipe Contreras <felipe.contreras@nokia.com>
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation
9 * version 2.1 of the License.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
22 #include "gstomx_base_filter.h"
24 #include "gstomx_interface.h"
26 #include <string.h> /* for memcpy */
28 /* MODIFICATION: for state-tuning */
29 static void output_loop (gpointer data);
33 ARG_USE_TIMESTAMPS = GSTOMX_NUM_COMMON_PROP,
34 ARG_NUM_INPUT_BUFFERS,
35 ARG_NUM_OUTPUT_BUFFERS,
38 static void init_interfaces (GType type);
39 GSTOMX_BOILERPLATE_FULL (GstOmxBaseFilter, gst_omx_base_filter, GstElement,
40 GST_TYPE_ELEMENT, init_interfaces);
43 log_buffer (GstOmxBaseFilter * self, OMX_BUFFERHEADERTYPE * omx_buffer, const gchar *name)
45 GST_DEBUG_OBJECT (self, "%s: omx_buffer: "
51 name, omx_buffer->nAllocLen, omx_buffer->nFilledLen, omx_buffer->nFlags,
52 omx_buffer->nOffset, omx_buffer->nTimeStamp);
55 /* Add_code_for_extended_color_format */
57 is_extended_color_format(GstOmxBaseFilter * self, GOmxPort * port)
59 OMX_PARAM_PORTDEFINITIONTYPE param;
60 OMX_HANDLETYPE omx_handle = self->gomx->omx_handle;
62 if (G_UNLIKELY (!omx_handle)) {
63 GST_WARNING_OBJECT (self, "no component");
67 G_OMX_INIT_PARAM (param);
69 param.nPortIndex = port->port_index;
70 OMX_GetParameter (omx_handle, OMX_IndexParamPortDefinition, ¶m);
72 switch ((guint)param.format.video.eColorFormat) {
73 case OMX_EXT_COLOR_FormatNV12TPhysicalAddress:
74 case OMX_EXT_COLOR_FormatNV12LPhysicalAddress:
75 case OMX_EXT_COLOR_FormatNV12Tiled:
76 case OMX_EXT_COLOR_FormatNV12TFdValue:
77 case OMX_EXT_COLOR_FormatNV12LFdValue:
85 setup_ports (GstOmxBaseFilter * self)
87 /* Input port configuration. */
88 g_omx_port_setup (self->in_port);
89 gst_pad_set_element_private (self->sinkpad, self->in_port);
91 /* Output port configuration. */
92 g_omx_port_setup (self->out_port);
93 gst_pad_set_element_private (self->srcpad, self->out_port);
95 /* @todo: read from config file: */
96 if (g_getenv ("OMX_ALLOCATE_ON")) {
97 GST_DEBUG_OBJECT (self, "OMX_ALLOCATE_ON");
98 self->in_port->omx_allocate = TRUE;
99 self->out_port->omx_allocate = TRUE;
100 self->in_port->shared_buffer = FALSE;
101 self->out_port->shared_buffer = FALSE;
102 } else if (g_getenv ("OMX_SHARE_HACK_ON")) {
103 GST_DEBUG_OBJECT (self, "OMX_SHARE_HACK_ON");
104 self->in_port->shared_buffer = TRUE;
105 self->out_port->shared_buffer = TRUE;
106 } else if (g_getenv ("OMX_SHARE_HACK_OFF")) {
107 GST_DEBUG_OBJECT (self, "OMX_SHARE_HACK_OFF");
108 self->in_port->shared_buffer = FALSE;
109 self->out_port->shared_buffer = FALSE;
110 /* MODIFICATION: Add extended_color_format */
111 } else if (self->gomx->component_vendor == GOMX_VENDOR_SLSI) {
112 self->in_port->shared_buffer = (is_extended_color_format(self, self->in_port))
114 self->out_port->shared_buffer = (is_extended_color_format(self, self->out_port))
116 } else if (self->gomx->component_vendor == GOMX_VENDOR_QCT) {
117 GST_DEBUG_OBJECT (self, "GOMX_VENDOR_QCT");
118 self->in_port->omx_allocate = TRUE;
119 self->out_port->omx_allocate = TRUE;
120 self->in_port->shared_buffer = FALSE;
121 self->out_port->shared_buffer = FALSE;
123 GST_DEBUG_OBJECT (self, "default sharing and allocation");
126 GST_DEBUG_OBJECT (self, "omx_allocate: in: %d, out: %d",
127 self->in_port->omx_allocate, self->out_port->omx_allocate);
128 GST_DEBUG_OBJECT (self, "share_buffer: in: %d, out: %d",
129 self->in_port->shared_buffer, self->out_port->shared_buffer);
133 omx_change_state(GstOmxBaseFilter * self,GstOmxChangeState transition, GOmxPort *in_port, GstBuffer * buf)
136 GstFlowReturn ret = GST_FLOW_OK;
140 switch (transition) {
141 case GstOmx_LodedToIdle:
143 g_mutex_lock (self->ready_lock);
145 GST_INFO_OBJECT (self, "omx: prepare");
147 /** @todo this should probably go after doing preparations. */
148 if (self->omx_setup) {
149 self->omx_setup (self);
154 g_omx_core_prepare (self->gomx);
156 if (gomx->omx_state == OMX_StateIdle) {
158 gst_pad_start_task (self->srcpad, output_loop, self->srcpad);
161 g_mutex_unlock (self->ready_lock);
163 if (gomx->omx_state != OMX_StateIdle)
168 case GstOmx_IdleToExcuting:
170 GST_INFO_OBJECT (self, "omx: play");
171 g_omx_core_start (gomx);
173 if (gomx->omx_state != OMX_StateExecuting)
176 /* send buffer with codec data flag */
177 /** @todo move to util */
178 if (self->codec_data) {
179 OMX_BUFFERHEADERTYPE *omx_buffer;
181 GST_LOG_OBJECT (self, "request buffer");
182 omx_buffer = g_omx_port_request_buffer (in_port);
184 if (G_LIKELY (omx_buffer)) {
185 omx_buffer->nFlags |= OMX_BUFFERFLAG_CODECCONFIG;
187 omx_buffer->nFilledLen = GST_BUFFER_SIZE (self->codec_data);
188 memcpy (omx_buffer->pBuffer + omx_buffer->nOffset,
189 GST_BUFFER_DATA (self->codec_data), omx_buffer->nFilledLen);
191 GST_LOG_OBJECT (self, "release_buffer");
192 g_omx_port_release_buffer (in_port, omx_buffer);
204 GST_LOG_OBJECT (self, "end");
207 /* special conditions */
210 const gchar *error_msg = NULL;
212 if (gomx->omx_error) {
213 error_msg = "Error from OpenMAX component";
214 } else if (gomx->omx_state != OMX_StateExecuting &&
215 gomx->omx_state != OMX_StatePause) {
216 error_msg = "OpenMAX component in wrong state";
220 if (gomx->post_gst_element_error == FALSE) {
221 GST_ERROR_OBJECT (self, "post GST_ELEMENT_ERROR as %s", error_msg);
222 GST_ELEMENT_ERROR (self, STREAM, FAILED, (NULL), ("%s", error_msg));
223 gomx->post_gst_element_error = TRUE;
224 ret = GST_FLOW_ERROR;
226 GST_ERROR_OBJECT (self, "GST_ELEMENT_ERROR is already posted. skip this (%s)", error_msg);
231 gst_buffer_unref (buf);
236 static GstStateChangeReturn
237 change_state (GstElement * element, GstStateChange transition)
239 GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
240 GstOmxBaseFilter *self;
243 self = GST_OMX_BASE_FILTER (element);
246 GST_LOG_OBJECT (self, "begin");
248 GST_INFO_OBJECT (self, "changing state %s - %s",
249 gst_element_state_get_name (GST_STATE_TRANSITION_CURRENT (transition)),
250 gst_element_state_get_name (GST_STATE_TRANSITION_NEXT (transition)));
252 switch (transition) {
253 case GST_STATE_CHANGE_NULL_TO_READY:
254 GST_INFO_OBJECT (self, "GST_STATE_CHANGE_NULL_TO_READY");
255 core->omx_unrecover_err_cnt = 0;
256 core->post_gst_element_error = FALSE;
258 if (core->omx_state != OMX_StateLoaded) {
259 ret = GST_STATE_CHANGE_FAILURE;
263 if (self->adapter_size > 0) {
264 GST_LOG_OBJECT (self, "gst_adapter_new. size: %d", self->adapter_size);
265 self->adapter = gst_adapter_new();
266 if (self->adapter == NULL)
267 GST_ERROR_OBJECT (self, "Failed to create adapter!!");
271 case GST_STATE_CHANGE_READY_TO_PAUSED:
272 GST_INFO_OBJECT (self, "GST_STATE_CHANGE_READY_TO_PAUSED");
274 core->omx_unrecover_err_cnt = 0;
276 /* MODIFICATION: state tuning */
277 if (self->use_state_tuning) {
278 GST_INFO_OBJECT (self, "use state-tuning feature");
279 /* to handle abnormal state change. */
280 if (self->gomx != self->in_port->core) {
281 GST_ERROR_OBJECT(self, "self->gomx != self->in_port->core. start new in_port");
282 self->in_port = g_omx_core_new_port (self->gomx, 0);
284 if (self->gomx != self->out_port->core) {
285 GST_ERROR_OBJECT(self, "self->gomx != self->out_port->core. start new out_port");
286 self->out_port = g_omx_core_new_port (self->gomx, 1);
289 omx_change_state(self, GstOmx_LodedToIdle, NULL, NULL);
291 if (core->omx_state != OMX_StateIdle) {
292 GST_ERROR_OBJECT(self, "fail to move from OMX state Loaded to Idle");
293 g_omx_port_finish(self->in_port);
294 g_omx_port_finish(self->out_port);
295 g_omx_core_stop(core);
296 g_omx_core_unload(core);
297 ret = GST_STATE_CHANGE_FAILURE;
307 ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
309 if (ret == GST_STATE_CHANGE_FAILURE)
312 switch (transition) {
313 case GST_STATE_CHANGE_PAUSED_TO_READY:
314 GST_INFO_OBJECT (self, "GST_STATE_CHANGE_PAUSED_TO_READY");
315 g_mutex_lock (self->ready_lock);
317 g_omx_port_finish (self->in_port);
318 g_omx_port_finish (self->out_port);
320 g_omx_core_stop (core);
321 g_omx_core_unload (core);
324 g_mutex_unlock (self->ready_lock);
325 if (core->omx_state != OMX_StateLoaded &&
326 core->omx_state != OMX_StateInvalid) {
327 ret = GST_STATE_CHANGE_FAILURE;
332 case GST_STATE_CHANGE_READY_TO_NULL:
333 GST_INFO_OBJECT (self, "GST_STATE_CHANGE_READY_TO_NULL");
335 gst_adapter_clear(self->adapter);
336 g_object_unref(self->adapter);
337 self->adapter = NULL;
339 core->omx_unrecover_err_cnt = 0;
340 core->post_gst_element_error = FALSE;
348 GST_LOG_OBJECT (self, "end");
354 finalize (GObject * obj)
356 GstOmxBaseFilter *self;
358 self = GST_OMX_BASE_FILTER (obj);
361 gst_adapter_clear(self->adapter);
362 g_object_unref(self->adapter);
363 self->adapter = NULL;
366 if (self->codec_data) {
367 gst_buffer_unref (self->codec_data);
368 self->codec_data = NULL;
371 g_omx_core_free (self->gomx);
373 g_mutex_free (self->ready_lock);
375 G_OBJECT_CLASS (parent_class)->finalize (obj);
379 set_property (GObject * obj,
380 guint prop_id, const GValue * value, GParamSpec * pspec)
382 GstOmxBaseFilter *self;
384 self = GST_OMX_BASE_FILTER (obj);
387 case ARG_USE_TIMESTAMPS:
388 self->use_timestamps = g_value_get_boolean (value);
390 case ARG_NUM_INPUT_BUFFERS:
391 case ARG_NUM_OUTPUT_BUFFERS:
393 OMX_PARAM_PORTDEFINITIONTYPE param;
394 OMX_HANDLETYPE omx_handle = self->gomx->omx_handle;
395 OMX_U32 nBufferCountActual;
396 GOmxPort *port = (prop_id == ARG_NUM_INPUT_BUFFERS) ?
397 self->in_port : self->out_port;
399 if (G_UNLIKELY (!omx_handle)) {
400 GST_WARNING_OBJECT (self, "no component");
404 nBufferCountActual = g_value_get_uint (value);
406 G_OMX_INIT_PARAM (param);
408 param.nPortIndex = port->port_index;
409 OMX_GetParameter (omx_handle, OMX_IndexParamPortDefinition, ¶m);
411 if (nBufferCountActual < param.nBufferCountMin) {
412 GST_ERROR_OBJECT (self, "buffer count %lu is less than minimum %lu",
413 nBufferCountActual, param.nBufferCountMin);
417 param.nBufferCountActual = nBufferCountActual;
419 OMX_SetParameter (omx_handle, OMX_IndexParamPortDefinition, ¶m);
423 G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, prop_id, pspec);
429 get_property (GObject * obj, guint prop_id, GValue * value, GParamSpec * pspec)
431 GstOmxBaseFilter *self;
433 self = GST_OMX_BASE_FILTER (obj);
435 if (gstomx_get_property_helper (self->gomx, prop_id, value))
439 case ARG_USE_TIMESTAMPS:
440 g_value_set_boolean (value, self->use_timestamps);
442 case ARG_NUM_INPUT_BUFFERS:
443 case ARG_NUM_OUTPUT_BUFFERS:
445 OMX_PARAM_PORTDEFINITIONTYPE param;
446 OMX_HANDLETYPE omx_handle = self->gomx->omx_handle;
447 GOmxPort *port = (prop_id == ARG_NUM_INPUT_BUFFERS) ?
448 self->in_port : self->out_port;
450 if (G_UNLIKELY (!omx_handle)) {
451 GST_WARNING_OBJECT (self, "no component");
452 g_value_set_uint (value, 0);
456 G_OMX_INIT_PARAM (param);
458 param.nPortIndex = port->port_index;
459 OMX_GetParameter (omx_handle, OMX_IndexParamPortDefinition, ¶m);
461 g_value_set_uint (value, param.nBufferCountActual);
465 G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, prop_id, pspec);
471 type_base_init (gpointer g_class)
476 type_class_init (gpointer g_class, gpointer class_data)
478 GObjectClass *gobject_class;
479 GstElementClass *gstelement_class;
481 gobject_class = G_OBJECT_CLASS (g_class);
482 gstelement_class = GST_ELEMENT_CLASS (g_class);
484 gobject_class->finalize = finalize;
485 gstelement_class->change_state = change_state;
487 /* Properties stuff */
489 gobject_class->set_property = set_property;
490 gobject_class->get_property = get_property;
492 gstomx_install_property_helper (gobject_class);
494 g_object_class_install_property (gobject_class, ARG_USE_TIMESTAMPS,
495 g_param_spec_boolean ("use-timestamps", "Use timestamps",
496 "Whether or not to use timestamps",
497 TRUE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
499 g_object_class_install_property (gobject_class, ARG_NUM_INPUT_BUFFERS,
500 g_param_spec_uint ("input-buffers", "Input buffers",
501 "The number of OMX input buffers",
502 1, 10, 4, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
503 g_object_class_install_property (gobject_class, ARG_NUM_OUTPUT_BUFFERS,
504 g_param_spec_uint ("output-buffers", "Output buffers",
505 "The number of OMX output buffers",
506 1, 10, 4, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
511 static inline GstFlowReturn
512 push_buffer (GstOmxBaseFilter * self, GstBuffer * buf, OMX_BUFFERHEADERTYPE * omx_buffer)
514 GstFlowReturn ret = GST_FLOW_OK;
515 GstOmxBaseFilterClass *basefilter_class;
517 basefilter_class = GST_OMX_BASE_FILTER_GET_CLASS (self);
518 /* process output gst buffer before gst_pad_push */
519 if (basefilter_class->process_output_buf) {
520 GstOmxReturn ret = GSTOMX_RETURN_OK;
521 ret = basefilter_class->process_output_buf(self, &buf, omx_buffer);
522 if (ret == GSTOMX_RETURN_SKIP) {
523 gst_buffer_unref (buf);
528 /* set average duration for memsink. need to check */
529 GST_BUFFER_DURATION(buf) = self->duration;
531 GST_LOG_OBJECT (self, "OUT_BUFFER: timestamp = %" GST_TIME_FORMAT " size = %lu",
532 GST_TIME_ARGS(GST_BUFFER_TIMESTAMP (buf)), GST_BUFFER_SIZE (buf));
533 ret = gst_pad_push (self->srcpad, buf);
534 GST_LOG_OBJECT (self, "gst_pad_push end. ret = %d", ret);
541 output_loop (gpointer data)
546 GstOmxBaseFilter *self;
547 GstFlowReturn ret = GST_FLOW_OK;
550 self = GST_OMX_BASE_FILTER (gst_pad_get_parent (pad));
553 GST_LOG_OBJECT (self, "begin");
555 /* do not bother if we have been setup to bail out */
556 if ((ret = g_atomic_int_get (&self->last_pad_push_return)) != GST_FLOW_OK)
560 g_error ("not ready");
564 out_port = self->out_port;
566 if (G_LIKELY (out_port->enabled)) {
567 OMX_BUFFERHEADERTYPE *omx_buffer = NULL;
569 GST_LOG_OBJECT (self, "request buffer");
570 omx_buffer = g_omx_port_request_buffer (out_port);
572 GST_LOG_OBJECT (self, "omx_buffer: %p", omx_buffer);
574 if (G_UNLIKELY (!omx_buffer)) {
575 GST_WARNING_OBJECT (self, "null buffer: leaving");
576 ret = GST_FLOW_WRONG_STATE;
580 log_buffer (self, omx_buffer, "output_loop");
582 if (G_LIKELY (omx_buffer->nFilledLen > 0)) {
586 /** @todo remove this check */
587 if (G_LIKELY (self->in_port->enabled)) {
588 GstCaps *caps = NULL;
590 caps = gst_pad_get_negotiated_caps (self->srcpad);
593 /** @todo We shouldn't be doing this. */
594 GST_WARNING_OBJECT (self, "faking settings changed notification");
595 if (gomx->settings_changed_cb)
596 gomx->settings_changed_cb (gomx);
598 GST_LOG_OBJECT (self, "caps already fixed: %" GST_PTR_FORMAT, caps);
599 gst_caps_unref (caps);
604 /* buf is always null when the output buffer pointer isn't shared. */
605 buf = omx_buffer->pAppPrivate;
607 /** @todo we need to move all the caps handling to one single
608 * place, in the output loop probably. */
609 if (G_UNLIKELY (omx_buffer->nFlags & OMX_BUFFERFLAG_CODECCONFIG)) {
610 /* modification: to handle both byte-stream and packetized codec_data */
611 GstOmxBaseFilterClass *basefilter_class;
613 basefilter_class = GST_OMX_BASE_FILTER_GET_CLASS (self);
614 if (basefilter_class->process_output_caps) {
615 basefilter_class->process_output_caps(self, omx_buffer);
617 /* MODIFICATION: to handle output ST12 HW addr (dec) */
618 } else if (is_extended_color_format(self, self->out_port)) {
619 GstCaps *caps = NULL;
620 GstStructure *structure;
621 gint width = 0, height = 0;
622 SCMN_IMGB *outbuf = NULL;
624 if (G_UNLIKELY (omx_buffer->nFlags & OMX_BUFFERFLAG_DECODEONLY)) {
625 GST_INFO_OBJECT (self, "Decodeonly flag was set from component");
626 g_omx_port_release_buffer (out_port, omx_buffer);
630 caps = gst_pad_get_negotiated_caps(self->srcpad);
631 structure = gst_caps_get_structure(caps, 0);
633 gst_structure_get_int(structure, "width", &width);
634 gst_structure_get_int(structure, "height", &height);
636 if (G_LIKELY((width > 0) && (height > 0))) {
637 buf = gst_buffer_new_and_alloc(width * height * 3 / 2);
639 GST_ERROR_OBJECT (self, "invalid buffer size");
640 ret = GST_FLOW_UNEXPECTED;
644 outbuf = (SCMN_IMGB*)(omx_buffer->pBuffer);
645 if (outbuf->buf_share_method == 1) {
646 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",
647 outbuf->fd[0], outbuf->fd[1], outbuf->fd[2], outbuf->w[0], outbuf->h[0], outbuf->buf_share_method);
648 } else if (outbuf->buf_share_method == 0) {
649 GST_LOG_OBJECT (self, "dec output uses hw addr");
651 GST_WARNING_OBJECT (self, "dec output buf has wrong buf_share_method");
653 memcpy (GST_BUFFER_MALLOCDATA(buf), omx_buffer->pBuffer, omx_buffer->nFilledLen);
655 if (self->use_timestamps) {
656 GST_BUFFER_TIMESTAMP (buf) =
657 gst_util_uint64_scale_int (omx_buffer->nTimeStamp, GST_SECOND,
658 OMX_TICKS_PER_SECOND);
660 gst_buffer_set_caps(buf, GST_PAD_CAPS(self->srcpad));
661 gst_caps_unref (caps);
663 ret = push_buffer (self, buf, omx_buffer);
664 } else if (buf && !(omx_buffer->nFlags & OMX_BUFFERFLAG_EOS)) {
665 GST_BUFFER_SIZE (buf) = omx_buffer->nFilledLen;
666 if (self->use_timestamps) {
667 GST_BUFFER_TIMESTAMP (buf) =
668 gst_util_uint64_scale_int (omx_buffer->nTimeStamp, GST_SECOND,
669 OMX_TICKS_PER_SECOND);
672 omx_buffer->pAppPrivate = NULL;
673 omx_buffer->pBuffer = NULL;
675 ret = push_buffer (self, buf, omx_buffer);
678 /* This is only meant for the first OpenMAX buffers,
679 * which need to be pre-allocated. */
680 /* Also for the very last one. */
681 ret = gst_pad_alloc_buffer_and_set_caps (self->srcpad,
682 GST_BUFFER_OFFSET_NONE,
683 omx_buffer->nFilledLen, GST_PAD_CAPS (self->srcpad), &buf);
685 if (G_LIKELY (buf)) {
686 memcpy (GST_BUFFER_DATA (buf),
687 omx_buffer->pBuffer + omx_buffer->nOffset,
688 omx_buffer->nFilledLen);
689 if (self->use_timestamps) {
690 GST_BUFFER_TIMESTAMP (buf) =
691 gst_util_uint64_scale_int (omx_buffer->nTimeStamp, GST_SECOND,
692 OMX_TICKS_PER_SECOND);
695 if (self->out_port->shared_buffer) {
696 GST_WARNING_OBJECT (self, "couldn't zero-copy");
697 /* If pAppPrivate is NULL, it means it was a dummy
698 * allocation, free it. */
699 if (!omx_buffer->pAppPrivate) {
700 g_free (omx_buffer->pBuffer);
701 omx_buffer->pBuffer = NULL;
705 ret = push_buffer (self, buf, omx_buffer);
707 GST_WARNING_OBJECT (self, "couldn't allocate buffer of size %lu",
708 omx_buffer->nFilledLen);
712 GST_WARNING_OBJECT (self, "empty buffer");
715 if (self->out_port->shared_buffer &&
716 !omx_buffer->pBuffer && omx_buffer->nOffset == 0) {
718 GstFlowReturn result;
720 GST_LOG_OBJECT (self, "allocate buffer");
721 result = gst_pad_alloc_buffer_and_set_caps (self->srcpad,
722 GST_BUFFER_OFFSET_NONE,
723 omx_buffer->nAllocLen, GST_PAD_CAPS (self->srcpad), &buf);
725 if (G_LIKELY (result == GST_FLOW_OK)) {
726 omx_buffer->pAppPrivate = buf;
728 omx_buffer->pBuffer = GST_BUFFER_DATA (buf);
729 omx_buffer->nAllocLen = GST_BUFFER_SIZE (buf);
731 GST_WARNING_OBJECT (self,
732 "could not pad allocate buffer, using malloc");
733 omx_buffer->pBuffer = g_malloc (omx_buffer->nAllocLen);
737 if (self->out_port->shared_buffer && !omx_buffer->pBuffer) {
738 GST_ERROR_OBJECT (self, "no input buffer to share");
741 if (G_UNLIKELY (omx_buffer->nFlags & OMX_BUFFERFLAG_EOS)) {
742 GST_DEBUG_OBJECT (self, "got eos");
743 gst_pad_push_event (self->srcpad, gst_event_new_eos ());
744 omx_buffer->nFlags &= ~OMX_BUFFERFLAG_EOS;
745 ret = GST_FLOW_UNEXPECTED;
748 omx_buffer->nFilledLen = 0;
749 GST_LOG_OBJECT (self, "release_buffer");
750 g_omx_port_release_buffer (out_port, omx_buffer);
755 self->last_pad_push_return = ret;
757 if (gomx->omx_error != OMX_ErrorNone)
758 ret = GST_FLOW_ERROR;
760 if (ret != GST_FLOW_OK) {
761 GST_INFO_OBJECT (self, "pause task, reason: %s", gst_flow_get_name (ret));
762 gst_pad_pause_task (self->srcpad);
765 GST_LOG_OBJECT (self, "end");
767 gst_object_unref (self);
771 pad_chain (GstPad * pad, GstBuffer * buf)
775 GstOmxBaseFilter *self;
776 GstOmxBaseFilterClass *basefilter_class;
777 GstFlowReturn ret = GST_FLOW_OK;
778 GstBuffer *adapter_buf = NULL;
780 self = GST_OMX_BASE_FILTER (GST_OBJECT_PARENT (pad));
784 GST_LOG_OBJECT (self, "IN_BUFFER: timestamp = %" GST_TIME_FORMAT " size = %lu, state:%d",
785 GST_TIME_ARGS(GST_BUFFER_TIMESTAMP (buf)), GST_BUFFER_SIZE (buf), gomx->omx_state);
788 if (!self->use_state_tuning) {
789 if (G_UNLIKELY (gomx->omx_state == OMX_StateLoaded))
790 omx_change_state(self, GstOmx_LodedToIdle, NULL, NULL);
793 in_port = self->in_port;
795 if (G_LIKELY (in_port->enabled)) {
796 guint buffer_offset = 0;
797 guint8 *src_data = NULL;
799 GstClockTime src_timestamp = 0;
800 GstClockTime src_duration = 0;
802 if (G_UNLIKELY (gomx->omx_state == OMX_StateIdle))
803 omx_change_state(self, GstOmx_IdleToExcuting,in_port, buf);
805 if (G_UNLIKELY (gomx->omx_state != OMX_StateExecuting)) {
806 GST_ERROR_OBJECT (self, "Whoa! very wrong");
809 basefilter_class = GST_OMX_BASE_FILTER_GET_CLASS (self);
810 /* process input gst buffer before OMX_EmptyThisBuffer */
811 if (basefilter_class->process_input_buf)
813 GstOmxReturn ret = GSTOMX_RETURN_OK;
814 ret = basefilter_class->process_input_buf(self,&buf);
815 if (ret == GSTOMX_RETURN_SKIP) {
816 gst_buffer_unref(buf);
821 if (self->adapter_size > 0) {
822 if (!self->adapter) {
823 GST_WARNING_OBJECT (self, "adapter is NULL. retry gst_adapter_new");
824 self->adapter = gst_adapter_new();
827 if (GST_BUFFER_IS_DISCONT(buf))
829 GST_INFO_OBJECT (self, "got GST_BUFFER_IS_DISCONT.");
830 gst_adapter_clear(self->adapter);
833 gst_adapter_push(self->adapter, buf);
835 src_size = gst_adapter_available(self->adapter);
836 if (src_size < self->adapter_size) {
837 GST_LOG_OBJECT (self, "Not enough data in adapter to feed to decoder.");
841 if (src_size > self->adapter_size) {
842 src_size = src_size - GST_BUFFER_SIZE(buf);
843 GST_LOG_OBJECT (self, "take buffer from adapter. size=%d", src_size);
846 src_timestamp = gst_adapter_prev_timestamp(self->adapter, NULL);
847 adapter_buf = gst_adapter_take_buffer(self->adapter, src_size);
848 src_data = GST_BUFFER_DATA(adapter_buf);
849 src_duration = GST_BUFFER_TIMESTAMP (buf) - src_timestamp;
851 src_data = GST_BUFFER_DATA (buf);
852 src_size = GST_BUFFER_SIZE (buf);
853 src_timestamp = GST_BUFFER_TIMESTAMP (buf);
854 src_duration = GST_BUFFER_DURATION (buf);
857 while (G_LIKELY (buffer_offset < src_size)) {
858 OMX_BUFFERHEADERTYPE *omx_buffer;
860 if (self->last_pad_push_return != GST_FLOW_OK ||
861 !(gomx->omx_state == OMX_StateExecuting ||
862 gomx->omx_state == OMX_StatePause)) {
866 GST_LOG_OBJECT (self, "request buffer");
867 omx_buffer = g_omx_port_request_buffer (in_port);
869 GST_LOG_OBJECT (self, "omx_buffer: %p", omx_buffer);
871 if (G_LIKELY (omx_buffer)) {
872 log_buffer (self, omx_buffer, "pad_chain");
874 /* MODIFICATION: to handle input SN12 HW addr. (enc) */
875 if (is_extended_color_format(self, self->in_port)) {
876 SCMN_IMGB *inbuf = NULL;
878 if (!GST_BUFFER_MALLOCDATA(buf)) {
879 GST_WARNING_OBJECT (self, "null MALLOCDATA in hw color format. skip this.");
883 inbuf = (SCMN_IMGB*)(GST_BUFFER_MALLOCDATA(buf));
884 if (inbuf != NULL && inbuf->buf_share_method == 1) {
885 GST_LOG_OBJECT (self, "enc. fd[0]:%d fd[1]:%d fd[2]:%d w[0]:%d h[0]:%d buf_share_method:%d",
886 inbuf->fd[0], inbuf->fd[1], inbuf->fd[2], inbuf->w[0], inbuf->h[0], inbuf->buf_share_method);
887 } else if (inbuf != NULL && inbuf->buf_share_method == 0) {
888 GST_LOG_OBJECT (self, "enc input buf uses hw addr");
890 GST_WARNING_OBJECT (self, "enc input buf has wrong buf_share_method");
893 memcpy (omx_buffer->pBuffer, GST_BUFFER_MALLOCDATA(buf), sizeof(SCMN_IMGB));
894 omx_buffer->nAllocLen = sizeof(SCMN_IMGB);
895 omx_buffer->nFilledLen = sizeof(SCMN_IMGB);
896 } else if (omx_buffer->nOffset == 0 && self->in_port->shared_buffer) {
899 old_buf = omx_buffer->pAppPrivate;
902 gst_buffer_unref ((GstBuffer *)old_buf);
903 } else if (omx_buffer->pBuffer) {
904 g_free (omx_buffer->pBuffer);
905 omx_buffer->pBuffer = NULL;
909 omx_buffer->pBuffer = src_data;
910 omx_buffer->nAllocLen = src_size;
911 omx_buffer->nFilledLen = src_size;
912 omx_buffer->pAppPrivate = (self->adapter_size > 0) ? (OMX_PTR)adapter_buf : (OMX_PTR)buf;
914 omx_buffer->nFilledLen = MIN (src_size - buffer_offset,
915 omx_buffer->nAllocLen - omx_buffer->nOffset);
916 memcpy (omx_buffer->pBuffer + omx_buffer->nOffset,
917 src_data + buffer_offset, omx_buffer->nFilledLen);
920 if (self->use_timestamps) {
921 GstClockTime timestamp_offset = 0;
923 if (buffer_offset && src_duration != GST_CLOCK_TIME_NONE) {
924 timestamp_offset = gst_util_uint64_scale_int (buffer_offset,
925 src_duration, src_size);
928 omx_buffer->nTimeStamp =
929 gst_util_uint64_scale_int (src_timestamp +
930 timestamp_offset, OMX_TICKS_PER_SECOND, GST_SECOND);
933 /* MODIFICATION: hw addr */
934 if (is_extended_color_format(self, self->in_port)) {
935 buffer_offset = GST_BUFFER_SIZE (buf);
937 buffer_offset += omx_buffer->nFilledLen;
940 GST_LOG_OBJECT (self, "release_buffer");
941 /** @todo untaint buffer */
942 g_omx_port_release_buffer (in_port, omx_buffer);
944 GST_WARNING_OBJECT (self, "null buffer");
945 ret = GST_FLOW_WRONG_STATE;
950 GST_WARNING_OBJECT (self, "done");
951 ret = GST_FLOW_UNEXPECTED;
954 if (!self->in_port->shared_buffer) {
955 if (self->adapter_size > 0 && adapter_buf) {
956 gst_buffer_unref (adapter_buf);
959 gst_buffer_unref (buf);
965 GST_LOG_OBJECT (self, "end");
969 /* special conditions */
972 const gchar *error_msg = NULL;
974 GST_LOG_OBJECT(self, "out_flushing");
976 if (gomx->omx_error) {
977 error_msg = "Error from OpenMAX component";
978 } else if (gomx->omx_state != OMX_StateExecuting &&
979 gomx->omx_state != OMX_StatePause) {
980 error_msg = "OpenMAX component in wrong state";
984 if (gomx->post_gst_element_error == FALSE) {
985 GST_ERROR_OBJECT (self, "post GST_ELEMENT_ERROR as %s", error_msg);
986 GST_ELEMENT_ERROR (self, STREAM, FAILED, (NULL), ("%s", error_msg));
987 gomx->post_gst_element_error = TRUE;
988 ret = GST_FLOW_ERROR;
990 GST_ERROR_OBJECT (self, "GST_ELEMENT_ERROR is already posted. skip this (%s)", error_msg);
994 if (self->adapter_size > 0 && adapter_buf) {
995 gst_buffer_unref (adapter_buf);
998 gst_buffer_unref (buf);
1006 pad_event (GstPad * pad, GstEvent * event)
1008 GstOmxBaseFilter *self;
1011 gboolean ret = TRUE;
1013 self = GST_OMX_BASE_FILTER (GST_OBJECT_PARENT (pad));
1015 in_port = self->in_port;
1017 GST_LOG_OBJECT (self, "begin");
1019 GST_INFO_OBJECT (self, "event: %s", GST_EVENT_TYPE_NAME (event));
1021 if (self->pad_event) {
1022 if (!self->pad_event(pad, event))
1026 switch (GST_EVENT_TYPE (event)) {
1028 /* if we are init'ed, and there is a running loop; then
1029 * if we get a buffer to inform it of EOS, let it handle the rest
1030 * in any other case, we send EOS */
1031 if (self->ready && self->last_pad_push_return == GST_FLOW_OK) {
1032 /* send buffer with eos flag */
1033 /** @todo move to util */
1035 OMX_BUFFERHEADERTYPE *omx_buffer;
1037 GST_LOG_OBJECT (self, "request buffer");
1038 omx_buffer = g_omx_port_request_buffer (in_port);
1040 if (G_LIKELY (omx_buffer)) {
1042 if (self->adapter_size > 0 && self->adapter) {
1044 GstBuffer *adapter_buf = NULL;
1046 src_len = gst_adapter_available(self->adapter);
1047 if (src_len > 0 && src_len < self->adapter_size) {
1048 omx_buffer->nTimeStamp = gst_util_uint64_scale_int(
1049 gst_adapter_prev_timestamp(self->adapter, NULL),
1050 OMX_TICKS_PER_SECOND, GST_SECOND);
1051 adapter_buf = gst_adapter_take_buffer(self->adapter, src_len);
1052 omx_buffer->pBuffer = GST_BUFFER_DATA(adapter_buf);
1053 omx_buffer->nAllocLen = src_len;
1054 omx_buffer->nFilledLen = src_len;
1055 omx_buffer->pAppPrivate = adapter_buf;
1057 gst_adapter_clear(self->adapter);
1059 omx_buffer->nFlags |= OMX_BUFFERFLAG_EOS;
1061 GST_LOG_OBJECT (self, "release_buffer in EOS. size=%d", omx_buffer->nFilledLen);
1062 /* foo_buffer_untaint (omx_buffer); */
1063 g_omx_port_release_buffer (in_port, omx_buffer);
1064 /* loop handles EOS, eat it here */
1065 gst_event_unref (event);
1071 /* we tried, but it's up to us here */
1072 ret = gst_pad_push_event (self->srcpad, event);
1075 case GST_EVENT_FLUSH_START:
1076 if (gomx->omx_state == OMX_StatePause || gomx->omx_state == OMX_StateExecuting) {
1077 gst_pad_push_event (self->srcpad, event);
1078 self->last_pad_push_return = GST_FLOW_WRONG_STATE;
1080 g_omx_core_flush_start (gomx);
1082 gst_pad_pause_task (self->srcpad);
1086 GST_ERROR_OBJECT (self, "flush start in wrong omx state");
1091 case GST_EVENT_FLUSH_STOP:
1092 if (gomx->omx_state == OMX_StatePause || gomx->omx_state == OMX_StateExecuting) {
1093 gst_pad_push_event (self->srcpad, event);
1094 self->last_pad_push_return = GST_FLOW_OK;
1096 g_omx_core_flush_stop (gomx);
1098 if (self->adapter_size > 0 && self->adapter) {
1099 gst_adapter_clear(self->adapter);
1103 gst_pad_start_task (self->srcpad, output_loop, self->srcpad);
1107 GST_ERROR_OBJECT (self, "flush start in wrong omx state");
1112 case GST_EVENT_NEWSEGMENT:
1113 ret = gst_pad_push_event (self->srcpad, event);
1117 ret = gst_pad_push_event (self->srcpad, event);
1121 GST_LOG_OBJECT (self, "end");
1127 activate_push (GstPad * pad, gboolean active)
1129 gboolean result = TRUE;
1130 GstOmxBaseFilter *self;
1132 self = GST_OMX_BASE_FILTER (gst_pad_get_parent (pad));
1135 GST_DEBUG_OBJECT (self, "activate");
1136 /* task may carry on */
1137 g_atomic_int_set (&self->last_pad_push_return, GST_FLOW_OK);
1139 /* we do not start the task yet if the pad is not connected */
1140 if (gst_pad_is_linked (pad)) {
1142 /** @todo link callback function also needed */
1143 g_omx_port_resume (self->in_port);
1144 g_omx_port_resume (self->out_port);
1146 result = gst_pad_start_task (pad, output_loop, pad);
1150 GST_DEBUG_OBJECT (self, "deactivate");
1152 /* persuade task to bail out */
1153 g_atomic_int_set (&self->last_pad_push_return, GST_FLOW_WRONG_STATE);
1156 /** @todo disable this until we properly reinitialize the buffers. */
1158 /* flush all buffers */
1159 OMX_SendCommand (self->gomx->omx_handle, OMX_CommandFlush, OMX_ALL, NULL);
1163 g_omx_port_pause (self->in_port);
1164 g_omx_port_pause (self->out_port);
1167 /* make sure streaming finishes */
1168 result = gst_pad_stop_task (pad);
1171 gst_object_unref (self);
1177 type_instance_init (GTypeInstance * instance, gpointer g_class)
1179 GstOmxBaseFilter *self;
1180 GstElementClass *element_class;
1182 element_class = GST_ELEMENT_CLASS (g_class);
1184 self = GST_OMX_BASE_FILTER (instance);
1186 GST_LOG_OBJECT (self, "begin");
1188 self->use_timestamps = TRUE;
1189 self->use_state_tuning = FALSE;
1190 self->adapter_size = 0;
1191 self->adapter = NULL;
1194 self->gomx = gstomx_core_new (self, G_TYPE_FROM_CLASS (g_class));
1195 self->in_port = g_omx_core_new_port (self->gomx, 0);
1196 self->out_port = g_omx_core_new_port (self->gomx, 1);
1198 self->ready_lock = g_mutex_new ();
1201 gst_pad_new_from_template (gst_element_class_get_pad_template
1202 (element_class, "sink"), "sink");
1204 gst_pad_set_chain_function (self->sinkpad, pad_chain);
1205 gst_pad_set_event_function (self->sinkpad, pad_event);
1208 gst_pad_new_from_template (gst_element_class_get_pad_template
1209 (element_class, "src"), "src");
1211 gst_pad_set_activatepush_function (self->srcpad, activate_push);
1213 gst_pad_use_fixed_caps (self->srcpad);
1215 gst_element_add_pad (GST_ELEMENT (self), self->sinkpad);
1216 gst_element_add_pad (GST_ELEMENT (self), self->srcpad);
1218 GST_LOG_OBJECT (self, "end");
1222 omx_interface_init (GstImplementsInterfaceClass * klass)
1227 interface_supported (GstImplementsInterface * iface, GType type)
1229 g_assert (type == GST_TYPE_OMX);
1234 interface_init (GstImplementsInterfaceClass * klass)
1236 klass->supported = interface_supported;
1240 init_interfaces (GType type)
1242 GInterfaceInfo *iface_info;
1243 GInterfaceInfo *omx_info;
1246 iface_info = g_new0 (GInterfaceInfo, 1);
1247 iface_info->interface_init = (GInterfaceInitFunc) interface_init;
1249 g_type_add_interface_static (type, GST_TYPE_IMPLEMENTS_INTERFACE, iface_info);
1250 g_free (iface_info);
1252 omx_info = g_new0 (GInterfaceInfo, 1);
1253 omx_info->interface_init = (GInterfaceInitFunc) omx_interface_init;
1255 g_type_add_interface_static (type, GST_TYPE_OMX, omx_info);