Initialize Tizen 2.3
[framework/multimedia/gst-openmax.git] / mobile / omx / gstomx_base_filter.c
1 /*
2  * Copyright (C) 2007-2009 Nokia Corporation.
3  *
4  * Author: Felipe Contreras <felipe.contreras@nokia.com>
5  *
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.
10  *
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.
15  *
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
19  *
20  */
21
22 #include "gstomx_base_filter.h"
23 #include "gstomx.h"
24 #include "gstomx_interface.h"
25
26 #include <string.h>             /* for memcpy */
27
28 /* MODIFICATION: for state-tuning */
29 static void output_loop (gpointer data);
30
31 enum
32 {
33   ARG_USE_TIMESTAMPS = GSTOMX_NUM_COMMON_PROP,
34   ARG_NUM_INPUT_BUFFERS,
35   ARG_NUM_OUTPUT_BUFFERS,
36 };
37
38 static void init_interfaces (GType type);
39 GSTOMX_BOILERPLATE_FULL (GstOmxBaseFilter, gst_omx_base_filter, GstElement,
40     GST_TYPE_ELEMENT, init_interfaces);
41
42 static inline void
43 log_buffer (GstOmxBaseFilter * self, OMX_BUFFERHEADERTYPE * omx_buffer, const gchar *name)
44 {
45   GST_DEBUG_OBJECT (self, "%s: omx_buffer: "
46       "size=%lu, "
47       "len=%lu, "
48       "flags=%lu, "
49       "offset=%lu, "
50       "timestamp=%lld",
51       name, omx_buffer->nAllocLen, omx_buffer->nFilledLen, omx_buffer->nFlags,
52       omx_buffer->nOffset, omx_buffer->nTimeStamp);
53 }
54
55 /* Add_code_for_extended_color_format */
56 static gboolean
57 is_extended_color_format(GstOmxBaseFilter * self, GOmxPort * port)
58 {
59   OMX_PARAM_PORTDEFINITIONTYPE param;
60   OMX_HANDLETYPE omx_handle = self->gomx->omx_handle;
61
62   if (G_UNLIKELY (!omx_handle)) {
63     GST_WARNING_OBJECT (self, "no component");
64     return FALSE;
65   }
66
67   G_OMX_INIT_PARAM (param);
68
69   param.nPortIndex = port->port_index;
70   OMX_GetParameter (omx_handle, OMX_IndexParamPortDefinition, &param);
71
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:
78       return TRUE;
79     default:
80       return FALSE;
81   }
82 }
83
84 static void
85 setup_ports (GstOmxBaseFilter * self)
86 {
87   /* Input port configuration. */
88   g_omx_port_setup (self->in_port);
89   gst_pad_set_element_private (self->sinkpad, self->in_port);
90
91   /* Output port configuration. */
92   g_omx_port_setup (self->out_port);
93   gst_pad_set_element_private (self->srcpad, self->out_port);
94
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))
113         ? FALSE : TRUE;
114     self->out_port->shared_buffer = (is_extended_color_format(self, self->out_port))
115         ? FALSE : TRUE;
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;
122   } else {
123     GST_DEBUG_OBJECT (self, "default sharing and allocation");
124   }
125
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);
130 }
131
132 static GstFlowReturn
133 omx_change_state(GstOmxBaseFilter * self,GstOmxChangeState transition, GOmxPort *in_port, GstBuffer * buf)
134 {
135   GOmxCore *gomx;
136   GstFlowReturn ret = GST_FLOW_OK;
137
138   gomx = self->gomx;
139
140   switch (transition) {
141   case GstOmx_LodedToIdle:
142     {
143       g_mutex_lock (self->ready_lock);
144
145       GST_INFO_OBJECT (self, "omx: prepare");
146
147       /** @todo this should probably go after doing preparations. */
148       if (self->omx_setup) {
149         self->omx_setup (self);
150       }
151
152       setup_ports (self);
153
154       g_omx_core_prepare (self->gomx);
155
156       if (gomx->omx_state == OMX_StateIdle) {
157         self->ready = TRUE;
158         gst_pad_start_task (self->srcpad, output_loop, self->srcpad);
159       }
160
161       g_mutex_unlock (self->ready_lock);
162
163       if (gomx->omx_state != OMX_StateIdle)
164         goto out_flushing;
165     }
166     break;
167
168   case GstOmx_IdleToExcuting:
169     {
170       GST_INFO_OBJECT (self, "omx: play");
171       g_omx_core_start (gomx);
172
173       if (gomx->omx_state != OMX_StateExecuting)
174         goto out_flushing;
175
176       /* send buffer with codec data flag */
177       /** @todo move to util */
178       if (self->codec_data) {
179         OMX_BUFFERHEADERTYPE *omx_buffer;
180
181         GST_LOG_OBJECT (self, "request buffer");
182         omx_buffer = g_omx_port_request_buffer (in_port);
183
184         if (G_LIKELY (omx_buffer)) {
185           omx_buffer->nFlags |= OMX_BUFFERFLAG_CODECCONFIG;
186
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);
190
191           GST_LOG_OBJECT (self, "release_buffer");
192           g_omx_port_release_buffer (in_port, omx_buffer);
193         }
194       }
195     }
196     break;
197
198   default:
199     break;
200   }
201
202 leave:
203
204   GST_LOG_OBJECT (self, "end");
205   return ret;
206
207   /* special conditions */
208 out_flushing:
209   {
210     const gchar *error_msg = NULL;
211
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";
217     }
218
219     if (error_msg) {
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;
225       } else {
226         GST_ERROR_OBJECT (self, "GST_ELEMENT_ERROR is already posted. skip this (%s)", error_msg);
227       }
228     }
229
230     if (buf)
231       gst_buffer_unref (buf);
232     goto leave;
233   }
234 }
235
236 static GstStateChangeReturn
237 change_state (GstElement * element, GstStateChange transition)
238 {
239   GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
240   GstOmxBaseFilter *self;
241   GOmxCore *core;
242
243   self = GST_OMX_BASE_FILTER (element);
244   core = self->gomx;
245
246   GST_LOG_OBJECT (self, "begin");
247
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)));
251
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;
257
258       if (core->omx_state != OMX_StateLoaded) {
259         ret = GST_STATE_CHANGE_FAILURE;
260         goto leave;
261       }
262
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!!");
268       }
269       break;
270
271     case GST_STATE_CHANGE_READY_TO_PAUSED:
272       GST_INFO_OBJECT (self, "GST_STATE_CHANGE_READY_TO_PAUSED");
273
274       core->omx_unrecover_err_cnt = 0;
275
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);
283         }
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);
287         }
288
289         omx_change_state(self, GstOmx_LodedToIdle, NULL, NULL);
290
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;
298           goto leave;
299         }
300       }
301       break;
302
303     default:
304       break;
305   }
306
307   ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
308
309   if (ret == GST_STATE_CHANGE_FAILURE)
310     goto leave;
311
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);
316       if (self->ready) {
317         g_omx_port_finish (self->in_port);
318         g_omx_port_finish (self->out_port);
319
320         g_omx_core_stop (core);
321         g_omx_core_unload (core);
322         self->ready = FALSE;
323       }
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;
328         goto leave;
329       }
330       break;
331
332     case GST_STATE_CHANGE_READY_TO_NULL:
333       GST_INFO_OBJECT (self, "GST_STATE_CHANGE_READY_TO_NULL");
334       if (self->adapter) {
335         gst_adapter_clear(self->adapter);
336         g_object_unref(self->adapter);
337         self->adapter = NULL;
338       }
339       core->omx_unrecover_err_cnt = 0;
340       core->post_gst_element_error = FALSE;
341       break;
342
343     default:
344       break;
345   }
346
347 leave:
348   GST_LOG_OBJECT (self, "end");
349
350   return ret;
351 }
352
353 static void
354 finalize (GObject * obj)
355 {
356   GstOmxBaseFilter *self;
357
358   self = GST_OMX_BASE_FILTER (obj);
359
360   if (self->adapter) {
361     gst_adapter_clear(self->adapter);
362     g_object_unref(self->adapter);
363     self->adapter = NULL;
364   }
365
366   if (self->codec_data) {
367     gst_buffer_unref (self->codec_data);
368     self->codec_data = NULL;
369   }
370
371   g_omx_core_free (self->gomx);
372
373   g_mutex_free (self->ready_lock);
374
375   G_OBJECT_CLASS (parent_class)->finalize (obj);
376 }
377
378 static void
379 set_property (GObject * obj,
380     guint prop_id, const GValue * value, GParamSpec * pspec)
381 {
382   GstOmxBaseFilter *self;
383
384   self = GST_OMX_BASE_FILTER (obj);
385
386   switch (prop_id) {
387     case ARG_USE_TIMESTAMPS:
388       self->use_timestamps = g_value_get_boolean (value);
389       break;
390     case ARG_NUM_INPUT_BUFFERS:
391     case ARG_NUM_OUTPUT_BUFFERS:
392     {
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;
398
399       if (G_UNLIKELY (!omx_handle)) {
400         GST_WARNING_OBJECT (self, "no component");
401         break;
402       }
403
404       nBufferCountActual = g_value_get_uint (value);
405
406       G_OMX_INIT_PARAM (param);
407
408       param.nPortIndex = port->port_index;
409       OMX_GetParameter (omx_handle, OMX_IndexParamPortDefinition, &param);
410
411       if (nBufferCountActual < param.nBufferCountMin) {
412         GST_ERROR_OBJECT (self, "buffer count %lu is less than minimum %lu",
413             nBufferCountActual, param.nBufferCountMin);
414         return;
415       }
416
417       param.nBufferCountActual = nBufferCountActual;
418
419       OMX_SetParameter (omx_handle, OMX_IndexParamPortDefinition, &param);
420     }
421       break;
422     default:
423       G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, prop_id, pspec);
424       break;
425   }
426 }
427
428 static void
429 get_property (GObject * obj, guint prop_id, GValue * value, GParamSpec * pspec)
430 {
431   GstOmxBaseFilter *self;
432
433   self = GST_OMX_BASE_FILTER (obj);
434
435   if (gstomx_get_property_helper (self->gomx, prop_id, value))
436     return;
437
438   switch (prop_id) {
439     case ARG_USE_TIMESTAMPS:
440       g_value_set_boolean (value, self->use_timestamps);
441       break;
442     case ARG_NUM_INPUT_BUFFERS:
443     case ARG_NUM_OUTPUT_BUFFERS:
444     {
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;
449
450       if (G_UNLIKELY (!omx_handle)) {
451         GST_WARNING_OBJECT (self, "no component");
452         g_value_set_uint (value, 0);
453         break;
454       }
455
456       G_OMX_INIT_PARAM (param);
457
458       param.nPortIndex = port->port_index;
459       OMX_GetParameter (omx_handle, OMX_IndexParamPortDefinition, &param);
460
461       g_value_set_uint (value, param.nBufferCountActual);
462     }
463       break;
464     default:
465       G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, prop_id, pspec);
466       break;
467   }
468 }
469
470 static void
471 type_base_init (gpointer g_class)
472 {
473 }
474
475 static void
476 type_class_init (gpointer g_class, gpointer class_data)
477 {
478   GObjectClass *gobject_class;
479   GstElementClass *gstelement_class;
480
481   gobject_class = G_OBJECT_CLASS (g_class);
482   gstelement_class = GST_ELEMENT_CLASS (g_class);
483
484   gobject_class->finalize = finalize;
485   gstelement_class->change_state = change_state;
486
487   /* Properties stuff */
488   {
489     gobject_class->set_property = set_property;
490     gobject_class->get_property = get_property;
491
492     gstomx_install_property_helper (gobject_class);
493
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));
498
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));
507
508   }
509 }
510
511 static inline GstFlowReturn
512 push_buffer (GstOmxBaseFilter * self, GstBuffer * buf, OMX_BUFFERHEADERTYPE * omx_buffer)
513 {
514   GstFlowReturn ret = GST_FLOW_OK;
515   GstOmxBaseFilterClass *basefilter_class;
516
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);
524       goto leave;
525     }
526   }
527
528   /* set average duration for memsink. need to check */
529   GST_BUFFER_DURATION(buf) = self->duration;
530
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);
535
536 leave:
537   return ret;
538 }
539
540 static void
541 output_loop (gpointer data)
542 {
543   GstPad *pad;
544   GOmxCore *gomx;
545   GOmxPort *out_port;
546   GstOmxBaseFilter *self;
547   GstFlowReturn ret = GST_FLOW_OK;
548
549   pad = data;
550   self = GST_OMX_BASE_FILTER (gst_pad_get_parent (pad));
551   gomx = self->gomx;
552
553   GST_LOG_OBJECT (self, "begin");
554
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)
557     goto leave;
558
559   if (!self->ready) {
560     g_error ("not ready");
561     return;
562   }
563
564   out_port = self->out_port;
565
566   if (G_LIKELY (out_port->enabled)) {
567     OMX_BUFFERHEADERTYPE *omx_buffer = NULL;
568
569     GST_LOG_OBJECT (self, "request buffer");
570     omx_buffer = g_omx_port_request_buffer (out_port);
571
572     GST_LOG_OBJECT (self, "omx_buffer: %p", omx_buffer);
573
574     if (G_UNLIKELY (!omx_buffer)) {
575       GST_WARNING_OBJECT (self, "null buffer: leaving");
576       ret = GST_FLOW_WRONG_STATE;
577       goto leave;
578     }
579
580     log_buffer (self, omx_buffer, "output_loop");
581
582     if (G_LIKELY (omx_buffer->nFilledLen > 0)) {
583       GstBuffer *buf;
584
585 #if 1
586             /** @todo remove this check */
587       if (G_LIKELY (self->in_port->enabled)) {
588         GstCaps *caps = NULL;
589
590         caps = gst_pad_get_negotiated_caps (self->srcpad);
591
592         if (!caps) {
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);
597         } else {
598           GST_LOG_OBJECT (self, "caps already fixed: %" GST_PTR_FORMAT, caps);
599           gst_caps_unref (caps);
600         }
601       }
602 #endif
603
604       /* buf is always null when the output buffer pointer isn't shared. */
605       buf = omx_buffer->pAppPrivate;
606
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;
612
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);
616         }
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;
623
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);
627           goto leave;
628         }
629
630         caps = gst_pad_get_negotiated_caps(self->srcpad);
631         structure = gst_caps_get_structure(caps, 0);
632
633         gst_structure_get_int(structure, "width", &width);
634         gst_structure_get_int(structure, "height", &height);
635
636         if (G_LIKELY((width > 0) && (height > 0))) {
637           buf = gst_buffer_new_and_alloc(width * height * 3 / 2);
638         } else {
639           GST_ERROR_OBJECT (self, "invalid buffer size");
640           ret = GST_FLOW_UNEXPECTED;
641           goto leave;
642         }
643
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");
650         } else {
651           GST_WARNING_OBJECT (self, "dec output buf has wrong buf_share_method");
652         }
653         memcpy (GST_BUFFER_MALLOCDATA(buf), omx_buffer->pBuffer, omx_buffer->nFilledLen);
654
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);
659         }
660         gst_buffer_set_caps(buf, GST_PAD_CAPS(self->srcpad));
661         gst_caps_unref (caps);
662
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);
670         }
671
672         omx_buffer->pAppPrivate = NULL;
673         omx_buffer->pBuffer = NULL;
674
675         ret = push_buffer (self, buf, omx_buffer);
676
677       } else {
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);
684
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);
693           }
694
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;
702             }
703           }
704
705           ret = push_buffer (self, buf, omx_buffer);
706         } else {
707           GST_WARNING_OBJECT (self, "couldn't allocate buffer of size %lu",
708               omx_buffer->nFilledLen);
709         }
710       }
711     } else {
712       GST_WARNING_OBJECT (self, "empty buffer");
713     }
714
715     if (self->out_port->shared_buffer &&
716         !omx_buffer->pBuffer && omx_buffer->nOffset == 0) {
717       GstBuffer *buf;
718       GstFlowReturn result;
719
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);
724
725       if (G_LIKELY (result == GST_FLOW_OK)) {
726         omx_buffer->pAppPrivate = buf;
727
728         omx_buffer->pBuffer = GST_BUFFER_DATA (buf);
729         omx_buffer->nAllocLen = GST_BUFFER_SIZE (buf);
730       } else {
731         GST_WARNING_OBJECT (self,
732             "could not pad allocate buffer, using malloc");
733         omx_buffer->pBuffer = g_malloc (omx_buffer->nAllocLen);
734       }
735     }
736
737     if (self->out_port->shared_buffer && !omx_buffer->pBuffer) {
738       GST_ERROR_OBJECT (self, "no input buffer to share");
739     }
740
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;
746     }
747
748     omx_buffer->nFilledLen = 0;
749     GST_LOG_OBJECT (self, "release_buffer");
750     g_omx_port_release_buffer (out_port, omx_buffer);
751   }
752
753 leave:
754
755   self->last_pad_push_return = ret;
756
757   if (gomx->omx_error != OMX_ErrorNone)
758     ret = GST_FLOW_ERROR;
759
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);
763   }
764
765   GST_LOG_OBJECT (self, "end");
766
767   gst_object_unref (self);
768 }
769
770 static GstFlowReturn
771 pad_chain (GstPad * pad, GstBuffer * buf)
772 {
773   GOmxCore *gomx;
774   GOmxPort *in_port;
775   GstOmxBaseFilter *self;
776   GstOmxBaseFilterClass *basefilter_class;
777   GstFlowReturn ret = GST_FLOW_OK;
778   GstBuffer *adapter_buf = NULL;
779
780   self = GST_OMX_BASE_FILTER (GST_OBJECT_PARENT (pad));
781
782   gomx = self->gomx;
783
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);
786
787   /* STATE_TUNING */
788   if (!self->use_state_tuning) {
789     if (G_UNLIKELY (gomx->omx_state == OMX_StateLoaded))
790       omx_change_state(self, GstOmx_LodedToIdle, NULL, NULL);
791   }
792
793   in_port = self->in_port;
794
795   if (G_LIKELY (in_port->enabled)) {
796     guint buffer_offset = 0;
797     guint8 *src_data = NULL;
798     guint src_size = 0;
799     GstClockTime src_timestamp = 0;
800     GstClockTime src_duration = 0;
801
802     if (G_UNLIKELY (gomx->omx_state == OMX_StateIdle))
803       omx_change_state(self, GstOmx_IdleToExcuting,in_port, buf);
804
805     if (G_UNLIKELY (gomx->omx_state != OMX_StateExecuting)) {
806       GST_ERROR_OBJECT (self, "Whoa! very wrong");
807     }
808
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)
812     {
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);
817         goto leave;
818       }
819     }
820
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();
825       }
826
827       if (GST_BUFFER_IS_DISCONT(buf))
828       {
829         GST_INFO_OBJECT (self, "got GST_BUFFER_IS_DISCONT.");
830         gst_adapter_clear(self->adapter);
831       }
832
833       gst_adapter_push(self->adapter, buf);
834
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.");
838         goto leave;
839       }
840
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);
844       }
845
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;
850     } else {
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);
855     }
856
857     while (G_LIKELY (buffer_offset < src_size)) {
858       OMX_BUFFERHEADERTYPE *omx_buffer;
859
860       if (self->last_pad_push_return != GST_FLOW_OK ||
861           !(gomx->omx_state == OMX_StateExecuting ||
862               gomx->omx_state == OMX_StatePause)) {
863         goto out_flushing;
864       }
865
866       GST_LOG_OBJECT (self, "request buffer");
867       omx_buffer = g_omx_port_request_buffer (in_port);
868
869       GST_LOG_OBJECT (self, "omx_buffer: %p", omx_buffer);
870
871       if (G_LIKELY (omx_buffer)) {
872         log_buffer (self, omx_buffer, "pad_chain");
873
874         /* MODIFICATION: to handle input SN12 HW addr. (enc) */
875         if (is_extended_color_format(self, self->in_port)) {
876           SCMN_IMGB *inbuf = NULL;
877
878           if (!GST_BUFFER_MALLOCDATA(buf)) {
879               GST_WARNING_OBJECT (self, "null MALLOCDATA in hw color format. skip this.");
880               goto out_flushing;
881           }
882
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");
889           } else {
890             GST_WARNING_OBJECT (self, "enc input buf has wrong buf_share_method");
891           }
892
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) {
897           {
898             GstBuffer *old_buf;
899             old_buf = omx_buffer->pAppPrivate;
900
901             if (old_buf) {
902               gst_buffer_unref ((GstBuffer *)old_buf);
903             } else if (omx_buffer->pBuffer) {
904               g_free (omx_buffer->pBuffer);
905               omx_buffer->pBuffer = NULL;
906             }
907           }
908
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;
913         } else {
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);
918         }
919
920         if (self->use_timestamps) {
921           GstClockTime timestamp_offset = 0;
922
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);
926           }
927
928           omx_buffer->nTimeStamp =
929               gst_util_uint64_scale_int (src_timestamp +
930               timestamp_offset, OMX_TICKS_PER_SECOND, GST_SECOND);
931         }
932
933         /* MODIFICATION: hw addr */
934         if (is_extended_color_format(self, self->in_port)) {
935           buffer_offset = GST_BUFFER_SIZE (buf);
936         } else {
937           buffer_offset += omx_buffer->nFilledLen;
938         }
939
940         GST_LOG_OBJECT (self, "release_buffer");
941                 /** @todo untaint buffer */
942         g_omx_port_release_buffer (in_port, omx_buffer);
943       } else {
944         GST_WARNING_OBJECT (self, "null buffer");
945         ret = GST_FLOW_WRONG_STATE;
946         goto out_flushing;
947       }
948     }
949   } else {
950     GST_WARNING_OBJECT (self, "done");
951     ret = GST_FLOW_UNEXPECTED;
952   }
953
954   if (!self->in_port->shared_buffer) {
955     if (self->adapter_size > 0 && adapter_buf) {
956       gst_buffer_unref (adapter_buf);
957       adapter_buf = NULL;
958     } else {
959       gst_buffer_unref (buf);
960     }
961   }
962
963 leave:
964
965   GST_LOG_OBJECT (self, "end");
966
967   return ret;
968
969   /* special conditions */
970 out_flushing:
971   {
972     const gchar *error_msg = NULL;
973
974     GST_LOG_OBJECT(self, "out_flushing");
975
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";
981     }
982
983     if (error_msg) {
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;
989       } else {
990         GST_ERROR_OBJECT (self, "GST_ELEMENT_ERROR is already posted. skip this (%s)", error_msg);
991       }
992     }
993
994     if (self->adapter_size > 0 && adapter_buf) {
995       gst_buffer_unref (adapter_buf);
996       adapter_buf = NULL;
997     } else {
998       gst_buffer_unref (buf);
999     }
1000
1001     goto leave;
1002   }
1003 }
1004
1005 static gboolean
1006 pad_event (GstPad * pad, GstEvent * event)
1007 {
1008   GstOmxBaseFilter *self;
1009   GOmxCore *gomx;
1010   GOmxPort *in_port;
1011   gboolean ret = TRUE;
1012
1013   self = GST_OMX_BASE_FILTER (GST_OBJECT_PARENT (pad));
1014   gomx = self->gomx;
1015   in_port = self->in_port;
1016
1017   GST_LOG_OBJECT (self, "begin");
1018
1019   GST_INFO_OBJECT (self, "event: %s", GST_EVENT_TYPE_NAME (event));
1020
1021   if (self->pad_event) {
1022     if (!self->pad_event(pad, event))
1023       return TRUE;
1024   }
1025
1026   switch (GST_EVENT_TYPE (event)) {
1027     case GST_EVENT_EOS:
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 */
1034         {
1035           OMX_BUFFERHEADERTYPE *omx_buffer;
1036
1037           GST_LOG_OBJECT (self, "request buffer");
1038           omx_buffer = g_omx_port_request_buffer (in_port);
1039
1040           if (G_LIKELY (omx_buffer)) {
1041
1042             if (self->adapter_size > 0 && self->adapter) {
1043               guint src_len = 0;
1044               GstBuffer *adapter_buf = NULL;
1045
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;
1056               }
1057               gst_adapter_clear(self->adapter);
1058             }
1059             omx_buffer->nFlags |= OMX_BUFFERFLAG_EOS;
1060
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);
1066             break;
1067           }
1068         }
1069       }
1070
1071       /* we tried, but it's up to us here */
1072       ret = gst_pad_push_event (self->srcpad, event);
1073       break;
1074
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;
1079
1080         g_omx_core_flush_start (gomx);
1081
1082         gst_pad_pause_task (self->srcpad);
1083
1084         ret = TRUE;
1085       } else {
1086         GST_ERROR_OBJECT (self, "flush start in wrong omx state");
1087         ret = FALSE;
1088       }
1089       break;
1090
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;
1095
1096         g_omx_core_flush_stop (gomx);
1097
1098         if (self->adapter_size > 0 && self->adapter) {
1099           gst_adapter_clear(self->adapter);
1100         }
1101
1102         if (self->ready)
1103           gst_pad_start_task (self->srcpad, output_loop, self->srcpad);
1104
1105         ret = TRUE;
1106       } else {
1107         GST_ERROR_OBJECT (self, "flush start in wrong omx state");
1108         ret = FALSE;
1109       }
1110       break;
1111
1112     case GST_EVENT_NEWSEGMENT:
1113       ret = gst_pad_push_event (self->srcpad, event);
1114       break;
1115
1116     default:
1117       ret = gst_pad_push_event (self->srcpad, event);
1118       break;
1119   }
1120
1121   GST_LOG_OBJECT (self, "end");
1122
1123   return ret;
1124 }
1125
1126 static gboolean
1127 activate_push (GstPad * pad, gboolean active)
1128 {
1129   gboolean result = TRUE;
1130   GstOmxBaseFilter *self;
1131
1132   self = GST_OMX_BASE_FILTER (gst_pad_get_parent (pad));
1133
1134   if (active) {
1135     GST_DEBUG_OBJECT (self, "activate");
1136     /* task may carry on */
1137     g_atomic_int_set (&self->last_pad_push_return, GST_FLOW_OK);
1138
1139     /* we do not start the task yet if the pad is not connected */
1140     if (gst_pad_is_linked (pad)) {
1141       if (self->ready) {
1142                 /** @todo link callback function also needed */
1143         g_omx_port_resume (self->in_port);
1144         g_omx_port_resume (self->out_port);
1145
1146         result = gst_pad_start_task (pad, output_loop, pad);
1147       }
1148     }
1149   } else {
1150     GST_DEBUG_OBJECT (self, "deactivate");
1151
1152     /* persuade task to bail out */
1153     g_atomic_int_set (&self->last_pad_push_return, GST_FLOW_WRONG_STATE);
1154
1155     if (self->ready) {
1156             /** @todo disable this until we properly reinitialize the buffers. */
1157 #if 0
1158       /* flush all buffers */
1159       OMX_SendCommand (self->gomx->omx_handle, OMX_CommandFlush, OMX_ALL, NULL);
1160 #endif
1161
1162       /* unlock loops */
1163       g_omx_port_pause (self->in_port);
1164       g_omx_port_pause (self->out_port);
1165     }
1166
1167     /* make sure streaming finishes */
1168     result = gst_pad_stop_task (pad);
1169   }
1170
1171   gst_object_unref (self);
1172
1173   return result;
1174 }
1175
1176 static void
1177 type_instance_init (GTypeInstance * instance, gpointer g_class)
1178 {
1179   GstOmxBaseFilter *self;
1180   GstElementClass *element_class;
1181
1182   element_class = GST_ELEMENT_CLASS (g_class);
1183
1184   self = GST_OMX_BASE_FILTER (instance);
1185
1186   GST_LOG_OBJECT (self, "begin");
1187
1188   self->use_timestamps = TRUE;
1189   self->use_state_tuning = FALSE;
1190   self->adapter_size = 0;
1191   self->adapter = NULL;
1192   self->duration = 0;
1193
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);
1197
1198   self->ready_lock = g_mutex_new ();
1199
1200   self->sinkpad =
1201       gst_pad_new_from_template (gst_element_class_get_pad_template
1202       (element_class, "sink"), "sink");
1203
1204   gst_pad_set_chain_function (self->sinkpad, pad_chain);
1205   gst_pad_set_event_function (self->sinkpad, pad_event);
1206
1207   self->srcpad =
1208       gst_pad_new_from_template (gst_element_class_get_pad_template
1209       (element_class, "src"), "src");
1210
1211   gst_pad_set_activatepush_function (self->srcpad, activate_push);
1212
1213   gst_pad_use_fixed_caps (self->srcpad);
1214
1215   gst_element_add_pad (GST_ELEMENT (self), self->sinkpad);
1216   gst_element_add_pad (GST_ELEMENT (self), self->srcpad);
1217
1218   GST_LOG_OBJECT (self, "end");
1219 }
1220
1221 static void
1222 omx_interface_init (GstImplementsInterfaceClass * klass)
1223 {
1224 }
1225
1226 static gboolean
1227 interface_supported (GstImplementsInterface * iface, GType type)
1228 {
1229   g_assert (type == GST_TYPE_OMX);
1230   return TRUE;
1231 }
1232
1233 static void
1234 interface_init (GstImplementsInterfaceClass * klass)
1235 {
1236   klass->supported = interface_supported;
1237 }
1238
1239 static void
1240 init_interfaces (GType type)
1241 {
1242   GInterfaceInfo *iface_info;
1243   GInterfaceInfo *omx_info;
1244
1245
1246   iface_info = g_new0 (GInterfaceInfo, 1);
1247   iface_info->interface_init = (GInterfaceInitFunc) interface_init;
1248
1249   g_type_add_interface_static (type, GST_TYPE_IMPLEMENTS_INTERFACE, iface_info);
1250   g_free (iface_info);
1251
1252   omx_info = g_new0 (GInterfaceInfo, 1);
1253   omx_info->interface_init = (GInterfaceInitFunc) omx_interface_init;
1254
1255   g_type_add_interface_static (type, GST_TYPE_OMX, omx_info);
1256   g_free (omx_info);
1257 }