f4b72c4ab9f5f925a211f4233fcf9b0a3156918b
[framework/multimedia/gst-openmax.git] / 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       GST_ELEMENT_ERROR (self, STREAM, FAILED, (NULL), ("%s", error_msg));
221       ret = GST_FLOW_ERROR;
222     }
223
224     if (buf)
225       gst_buffer_unref (buf);
226     goto leave;
227   }
228 }
229
230 static GstStateChangeReturn
231 change_state (GstElement * element, GstStateChange transition)
232 {
233   GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
234   GstOmxBaseFilter *self;
235   GOmxCore *core;
236
237   self = GST_OMX_BASE_FILTER (element);
238   core = self->gomx;
239
240   GST_LOG_OBJECT (self, "begin");
241
242   GST_INFO_OBJECT (self, "changing state %s - %s",
243       gst_element_state_get_name (GST_STATE_TRANSITION_CURRENT (transition)),
244       gst_element_state_get_name (GST_STATE_TRANSITION_NEXT (transition)));
245
246   switch (transition) {
247     case GST_STATE_CHANGE_NULL_TO_READY:
248       GST_INFO_OBJECT (self, "GST_STATE_CHANGE_NULL_TO_READY");
249       if (core->omx_state != OMX_StateLoaded) {
250         ret = GST_STATE_CHANGE_FAILURE;
251         goto leave;
252       }
253
254       if (self->adapter_size > 0) {
255         GST_LOG_OBJECT (self, "gst_adapter_new. size: %d", self->adapter_size);
256         self->adapter = gst_adapter_new();
257         if (self->adapter == NULL)
258           GST_ERROR_OBJECT (self, "Failed to create adapter!!");
259       }
260       break;
261
262     case GST_STATE_CHANGE_READY_TO_PAUSED:
263       GST_INFO_OBJECT (self, "GST_STATE_CHANGE_READY_TO_PAUSED");
264
265       core->omx_unrecover_err_cnt = 0;
266
267       /* MODIFICATION: state tuning */
268       if (self->use_state_tuning) {
269         GST_INFO_OBJECT (self, "use state-tuning feature");
270         /* to handle abnormal state change. */
271         if (self->gomx != self->in_port->core) {
272           GST_ERROR_OBJECT(self, "self->gomx != self->in_port->core. start new in_port");
273           self->in_port = g_omx_core_new_port (self->gomx, 0);
274         }
275         if (self->gomx != self->out_port->core) {
276           GST_ERROR_OBJECT(self, "self->gomx != self->out_port->core. start new out_port");
277           self->out_port = g_omx_core_new_port (self->gomx, 1);
278         }
279
280         omx_change_state(self, GstOmx_LodedToIdle, NULL, NULL);
281
282         if (core->omx_state != OMX_StateIdle) {
283           GST_ERROR_OBJECT(self, "fail to move from OMX state Loaded to Idle");
284           g_omx_port_finish(self->in_port);
285           g_omx_port_finish(self->out_port);
286           g_omx_core_stop(core);
287           g_omx_core_unload(core);
288           ret = GST_STATE_CHANGE_FAILURE;
289           goto leave;
290         }
291       }
292       break;
293
294     default:
295       break;
296   }
297
298   ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
299
300   if (ret == GST_STATE_CHANGE_FAILURE)
301     goto leave;
302
303   switch (transition) {
304     case GST_STATE_CHANGE_PAUSED_TO_READY:
305       GST_INFO_OBJECT (self, "GST_STATE_CHANGE_PAUSED_TO_READY");
306       g_mutex_lock (self->ready_lock);
307       if (self->ready) {
308         g_omx_port_finish (self->in_port);
309         g_omx_port_finish (self->out_port);
310
311         g_omx_core_stop (core);
312         g_omx_core_unload (core);
313         self->ready = FALSE;
314       }
315       g_mutex_unlock (self->ready_lock);
316       if (core->omx_state != OMX_StateLoaded &&
317           core->omx_state != OMX_StateInvalid) {
318         ret = GST_STATE_CHANGE_FAILURE;
319         goto leave;
320       }
321       break;
322
323     case GST_STATE_CHANGE_READY_TO_NULL:
324       GST_INFO_OBJECT (self, "GST_STATE_CHANGE_READY_TO_NULL");
325       if (self->adapter) {
326         gst_adapter_clear(self->adapter);
327         g_object_unref(self->adapter);
328         self->adapter = NULL;
329       }
330       break;
331
332     default:
333       break;
334   }
335
336 leave:
337   GST_LOG_OBJECT (self, "end");
338
339   return ret;
340 }
341
342 static void
343 finalize (GObject * obj)
344 {
345   GstOmxBaseFilter *self;
346
347   self = GST_OMX_BASE_FILTER (obj);
348
349   if (self->adapter) {
350     gst_adapter_clear(self->adapter);
351     g_object_unref(self->adapter);
352     self->adapter = NULL;
353   }
354
355   if (self->codec_data) {
356     gst_buffer_unref (self->codec_data);
357     self->codec_data = NULL;
358   }
359
360   g_omx_core_free (self->gomx);
361
362   g_mutex_free (self->ready_lock);
363
364   G_OBJECT_CLASS (parent_class)->finalize (obj);
365 }
366
367 static void
368 set_property (GObject * obj,
369     guint prop_id, const GValue * value, GParamSpec * pspec)
370 {
371   GstOmxBaseFilter *self;
372
373   self = GST_OMX_BASE_FILTER (obj);
374
375   switch (prop_id) {
376     case ARG_USE_TIMESTAMPS:
377       self->use_timestamps = g_value_get_boolean (value);
378       break;
379     case ARG_NUM_INPUT_BUFFERS:
380     case ARG_NUM_OUTPUT_BUFFERS:
381     {
382       OMX_PARAM_PORTDEFINITIONTYPE param;
383       OMX_HANDLETYPE omx_handle = self->gomx->omx_handle;
384       OMX_U32 nBufferCountActual;
385       GOmxPort *port = (prop_id == ARG_NUM_INPUT_BUFFERS) ?
386           self->in_port : self->out_port;
387
388       if (G_UNLIKELY (!omx_handle)) {
389         GST_WARNING_OBJECT (self, "no component");
390         break;
391       }
392
393       nBufferCountActual = g_value_get_uint (value);
394
395       G_OMX_INIT_PARAM (param);
396
397       param.nPortIndex = port->port_index;
398       OMX_GetParameter (omx_handle, OMX_IndexParamPortDefinition, &param);
399
400       if (nBufferCountActual < param.nBufferCountMin) {
401         GST_ERROR_OBJECT (self, "buffer count %lu is less than minimum %lu",
402             nBufferCountActual, param.nBufferCountMin);
403         return;
404       }
405
406       param.nBufferCountActual = nBufferCountActual;
407
408       OMX_SetParameter (omx_handle, OMX_IndexParamPortDefinition, &param);
409     }
410       break;
411     default:
412       G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, prop_id, pspec);
413       break;
414   }
415 }
416
417 static void
418 get_property (GObject * obj, guint prop_id, GValue * value, GParamSpec * pspec)
419 {
420   GstOmxBaseFilter *self;
421
422   self = GST_OMX_BASE_FILTER (obj);
423
424   if (gstomx_get_property_helper (self->gomx, prop_id, value))
425     return;
426
427   switch (prop_id) {
428     case ARG_USE_TIMESTAMPS:
429       g_value_set_boolean (value, self->use_timestamps);
430       break;
431     case ARG_NUM_INPUT_BUFFERS:
432     case ARG_NUM_OUTPUT_BUFFERS:
433     {
434       OMX_PARAM_PORTDEFINITIONTYPE param;
435       OMX_HANDLETYPE omx_handle = self->gomx->omx_handle;
436       GOmxPort *port = (prop_id == ARG_NUM_INPUT_BUFFERS) ?
437           self->in_port : self->out_port;
438
439       if (G_UNLIKELY (!omx_handle)) {
440         GST_WARNING_OBJECT (self, "no component");
441         g_value_set_uint (value, 0);
442         break;
443       }
444
445       G_OMX_INIT_PARAM (param);
446
447       param.nPortIndex = port->port_index;
448       OMX_GetParameter (omx_handle, OMX_IndexParamPortDefinition, &param);
449
450       g_value_set_uint (value, param.nBufferCountActual);
451     }
452       break;
453     default:
454       G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, prop_id, pspec);
455       break;
456   }
457 }
458
459 static void
460 type_base_init (gpointer g_class)
461 {
462 }
463
464 static void
465 type_class_init (gpointer g_class, gpointer class_data)
466 {
467   GObjectClass *gobject_class;
468   GstElementClass *gstelement_class;
469
470   gobject_class = G_OBJECT_CLASS (g_class);
471   gstelement_class = GST_ELEMENT_CLASS (g_class);
472
473   gobject_class->finalize = finalize;
474   gstelement_class->change_state = change_state;
475
476   /* Properties stuff */
477   {
478     gobject_class->set_property = set_property;
479     gobject_class->get_property = get_property;
480
481     gstomx_install_property_helper (gobject_class);
482
483     g_object_class_install_property (gobject_class, ARG_USE_TIMESTAMPS,
484         g_param_spec_boolean ("use-timestamps", "Use timestamps",
485             "Whether or not to use timestamps",
486             TRUE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
487
488     g_object_class_install_property (gobject_class, ARG_NUM_INPUT_BUFFERS,
489         g_param_spec_uint ("input-buffers", "Input buffers",
490             "The number of OMX input buffers",
491             1, 10, 4, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
492     g_object_class_install_property (gobject_class, ARG_NUM_OUTPUT_BUFFERS,
493         g_param_spec_uint ("output-buffers", "Output buffers",
494             "The number of OMX output buffers",
495             1, 10, 4, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
496
497   }
498 }
499
500 static inline GstFlowReturn
501 push_buffer (GstOmxBaseFilter * self, GstBuffer * buf, OMX_BUFFERHEADERTYPE * omx_buffer)
502 {
503   GstFlowReturn ret = GST_FLOW_OK;
504   GstOmxBaseFilterClass *basefilter_class;
505
506   basefilter_class = GST_OMX_BASE_FILTER_GET_CLASS (self);
507   /* process output gst buffer before gst_pad_push */
508   if (basefilter_class->process_output_buf) {
509     GstOmxReturn ret = GSTOMX_RETURN_OK;
510     ret = basefilter_class->process_output_buf(self, &buf, omx_buffer);
511     if (ret == GSTOMX_RETURN_SKIP) {
512       gst_buffer_unref (buf);
513       goto leave;
514     }
515   }
516
517   /* set average duration for memsink. need to check */
518   GST_BUFFER_DURATION(buf) = self->duration;
519
520   GST_LOG_OBJECT (self, "OUT_BUFFER: timestamp = %" GST_TIME_FORMAT " size = %lu",
521       GST_TIME_ARGS(GST_BUFFER_TIMESTAMP (buf)), GST_BUFFER_SIZE (buf));
522   ret = gst_pad_push (self->srcpad, buf);
523   GST_LOG_OBJECT (self, "gst_pad_push end. ret = %d", ret);
524
525 leave:
526   return ret;
527 }
528
529 static void
530 output_loop (gpointer data)
531 {
532   GstPad *pad;
533   GOmxCore *gomx;
534   GOmxPort *out_port;
535   GstOmxBaseFilter *self;
536   GstFlowReturn ret = GST_FLOW_OK;
537
538   pad = data;
539   self = GST_OMX_BASE_FILTER (gst_pad_get_parent (pad));
540   gomx = self->gomx;
541
542   GST_LOG_OBJECT (self, "begin");
543
544   /* do not bother if we have been setup to bail out */
545   if ((ret = g_atomic_int_get (&self->last_pad_push_return)) != GST_FLOW_OK)
546     goto leave;
547
548   if (!self->ready) {
549     g_error ("not ready");
550     return;
551   }
552
553   out_port = self->out_port;
554
555   if (G_LIKELY (out_port->enabled)) {
556     OMX_BUFFERHEADERTYPE *omx_buffer = NULL;
557
558     GST_LOG_OBJECT (self, "request buffer");
559     omx_buffer = g_omx_port_request_buffer (out_port);
560
561     GST_LOG_OBJECT (self, "omx_buffer: %p", omx_buffer);
562
563     if (G_UNLIKELY (!omx_buffer)) {
564       GST_WARNING_OBJECT (self, "null buffer: leaving");
565       ret = GST_FLOW_WRONG_STATE;
566       goto leave;
567     }
568
569     log_buffer (self, omx_buffer, "output_loop");
570
571     if (G_LIKELY (omx_buffer->nFilledLen > 0)) {
572       GstBuffer *buf;
573
574 #if 1
575             /** @todo remove this check */
576       if (G_LIKELY (self->in_port->enabled)) {
577         GstCaps *caps = NULL;
578
579         caps = gst_pad_get_negotiated_caps (self->srcpad);
580
581         if (!caps) {
582                     /** @todo We shouldn't be doing this. */
583           GST_WARNING_OBJECT (self, "faking settings changed notification");
584           if (gomx->settings_changed_cb)
585             gomx->settings_changed_cb (gomx);
586         } else {
587           GST_LOG_OBJECT (self, "caps already fixed: %" GST_PTR_FORMAT, caps);
588           gst_caps_unref (caps);
589         }
590       }
591 #endif
592
593       /* buf is always null when the output buffer pointer isn't shared. */
594       buf = omx_buffer->pAppPrivate;
595
596             /** @todo we need to move all the caps handling to one single
597              * place, in the output loop probably. */
598       if (G_UNLIKELY (omx_buffer->nFlags & OMX_BUFFERFLAG_CODECCONFIG)) {
599         /* modification: to handle both byte-stream and packetized codec_data */
600         GstOmxBaseFilterClass *basefilter_class;
601
602         basefilter_class = GST_OMX_BASE_FILTER_GET_CLASS (self);
603         if (basefilter_class->process_output_caps) {
604           basefilter_class->process_output_caps(self, omx_buffer);
605         }
606         /* MODIFICATION: to handle output ST12 HW addr (dec) */
607       } else if (is_extended_color_format(self, self->out_port)) {
608         GstCaps *caps = NULL;
609         GstStructure *structure;
610         gint width = 0, height = 0;
611         SCMN_IMGB *outbuf = NULL;
612
613         if (G_UNLIKELY (omx_buffer->nFlags & OMX_BUFFERFLAG_DECODEONLY)) {
614           GST_INFO_OBJECT (self, "Decodeonly flag was set from component");
615           g_omx_port_release_buffer (out_port, omx_buffer);
616           goto leave;
617         }
618
619         caps = gst_pad_get_negotiated_caps(self->srcpad);
620         structure = gst_caps_get_structure(caps, 0);
621
622         gst_structure_get_int(structure, "width", &width);
623         gst_structure_get_int(structure, "height", &height);
624
625         if (G_LIKELY((width > 0) && (height > 0))) {
626           buf = gst_buffer_new_and_alloc(width * height * 3 / 2);
627         } else {
628           GST_ERROR_OBJECT (self, "invalid buffer size");
629           ret = GST_FLOW_UNEXPECTED;
630           goto leave;
631         }
632
633         outbuf = (SCMN_IMGB*)(omx_buffer->pBuffer);
634         if (outbuf->buf_share_method == 1) {
635           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", 
636               outbuf->fd[0], outbuf->fd[1], outbuf->fd[2], outbuf->w[0], outbuf->h[0], outbuf->buf_share_method);
637         } else if (outbuf->buf_share_method == 0) {
638           GST_LOG_OBJECT (self, "dec output uses hw addr");
639         } else {
640           GST_WARNING_OBJECT (self, "dec output buf has wrong buf_share_method");
641         }
642         memcpy (GST_BUFFER_MALLOCDATA(buf), omx_buffer->pBuffer, omx_buffer->nFilledLen);
643
644         if (self->use_timestamps) {
645           GST_BUFFER_TIMESTAMP (buf) =
646               gst_util_uint64_scale_int (omx_buffer->nTimeStamp, GST_SECOND,
647               OMX_TICKS_PER_SECOND);
648         }
649         gst_buffer_set_caps(buf, GST_PAD_CAPS(self->srcpad));
650         gst_caps_unref (caps);
651
652         ret = push_buffer (self, buf, omx_buffer);
653       } else if (buf && !(omx_buffer->nFlags & OMX_BUFFERFLAG_EOS)) {
654         GST_BUFFER_SIZE (buf) = 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);
659         }
660
661         omx_buffer->pAppPrivate = NULL;
662         omx_buffer->pBuffer = NULL;
663
664         ret = push_buffer (self, buf, omx_buffer);
665
666       } else {
667         /* This is only meant for the first OpenMAX buffers,
668          * which need to be pre-allocated. */
669         /* Also for the very last one. */
670         ret = gst_pad_alloc_buffer_and_set_caps (self->srcpad,
671             GST_BUFFER_OFFSET_NONE,
672             omx_buffer->nFilledLen, GST_PAD_CAPS (self->srcpad), &buf);
673
674         if (G_LIKELY (buf)) {
675           memcpy (GST_BUFFER_DATA (buf),
676               omx_buffer->pBuffer + omx_buffer->nOffset,
677               omx_buffer->nFilledLen);
678           if (self->use_timestamps) {
679             GST_BUFFER_TIMESTAMP (buf) =
680                 gst_util_uint64_scale_int (omx_buffer->nTimeStamp, GST_SECOND,
681                 OMX_TICKS_PER_SECOND);
682           }
683
684           if (self->out_port->shared_buffer) {
685             GST_WARNING_OBJECT (self, "couldn't zero-copy");
686             /* If pAppPrivate is NULL, it means it was a dummy
687              * allocation, free it. */
688             if (!omx_buffer->pAppPrivate) {
689               g_free (omx_buffer->pBuffer);
690               omx_buffer->pBuffer = NULL;
691             }
692           }
693
694           ret = push_buffer (self, buf, omx_buffer);
695         } else {
696           GST_WARNING_OBJECT (self, "couldn't allocate buffer of size %lu",
697               omx_buffer->nFilledLen);
698         }
699       }
700     } else {
701       GST_WARNING_OBJECT (self, "empty buffer");
702     }
703
704     if (self->out_port->shared_buffer &&
705         !omx_buffer->pBuffer && omx_buffer->nOffset == 0) {
706       GstBuffer *buf;
707       GstFlowReturn result;
708
709       GST_LOG_OBJECT (self, "allocate buffer");
710       result = gst_pad_alloc_buffer_and_set_caps (self->srcpad,
711           GST_BUFFER_OFFSET_NONE,
712           omx_buffer->nAllocLen, GST_PAD_CAPS (self->srcpad), &buf);
713
714       if (G_LIKELY (result == GST_FLOW_OK)) {
715         omx_buffer->pAppPrivate = buf;
716
717         omx_buffer->pBuffer = GST_BUFFER_DATA (buf);
718         omx_buffer->nAllocLen = GST_BUFFER_SIZE (buf);
719       } else {
720         GST_WARNING_OBJECT (self,
721             "could not pad allocate buffer, using malloc");
722         omx_buffer->pBuffer = g_malloc (omx_buffer->nAllocLen);
723       }
724     }
725
726     if (self->out_port->shared_buffer && !omx_buffer->pBuffer) {
727       GST_ERROR_OBJECT (self, "no input buffer to share");
728     }
729
730     if (G_UNLIKELY (omx_buffer->nFlags & OMX_BUFFERFLAG_EOS)) {
731       GST_DEBUG_OBJECT (self, "got eos");
732       gst_pad_push_event (self->srcpad, gst_event_new_eos ());
733       omx_buffer->nFlags &= ~OMX_BUFFERFLAG_EOS;
734       ret = GST_FLOW_UNEXPECTED;
735     }
736
737     omx_buffer->nFilledLen = 0;
738     GST_LOG_OBJECT (self, "release_buffer");
739     g_omx_port_release_buffer (out_port, omx_buffer);
740   }
741
742 leave:
743
744   self->last_pad_push_return = ret;
745
746   if (gomx->omx_error != OMX_ErrorNone)
747     ret = GST_FLOW_ERROR;
748
749   if (ret != GST_FLOW_OK) {
750     GST_INFO_OBJECT (self, "pause task, reason:  %s", gst_flow_get_name (ret));
751     gst_pad_pause_task (self->srcpad);
752   }
753
754   GST_LOG_OBJECT (self, "end");
755
756   gst_object_unref (self);
757 }
758
759 static GstFlowReturn
760 pad_chain (GstPad * pad, GstBuffer * buf)
761 {
762   GOmxCore *gomx;
763   GOmxPort *in_port;
764   GstOmxBaseFilter *self;
765   GstOmxBaseFilterClass *basefilter_class;
766   GstFlowReturn ret = GST_FLOW_OK;
767   GstBuffer *adapter_buf = NULL;
768
769   self = GST_OMX_BASE_FILTER (GST_OBJECT_PARENT (pad));
770
771   gomx = self->gomx;
772
773   GST_LOG_OBJECT (self, "IN_BUFFER: timestamp = %" GST_TIME_FORMAT " size = %lu, state:%d",
774       GST_TIME_ARGS(GST_BUFFER_TIMESTAMP (buf)), GST_BUFFER_SIZE (buf), gomx->omx_state);
775
776   /* STATE_TUNING */
777   if (!self->use_state_tuning) {
778     if (G_UNLIKELY (gomx->omx_state == OMX_StateLoaded))
779       omx_change_state(self, GstOmx_LodedToIdle, NULL, NULL);
780   }
781
782   in_port = self->in_port;
783
784   if (G_LIKELY (in_port->enabled)) {
785     guint buffer_offset = 0;
786     guint8 *src_data = NULL;
787     guint src_size = 0;
788     GstClockTime src_timestamp = 0;
789     GstClockTime src_duration = 0;
790
791     if (G_UNLIKELY (gomx->omx_state == OMX_StateIdle))
792       omx_change_state(self, GstOmx_IdleToExcuting,in_port, buf);
793
794     if (G_UNLIKELY (gomx->omx_state != OMX_StateExecuting)) {
795       GST_ERROR_OBJECT (self, "Whoa! very wrong");
796     }
797
798     basefilter_class = GST_OMX_BASE_FILTER_GET_CLASS (self);
799     /* process input gst buffer before OMX_EmptyThisBuffer */
800     if (basefilter_class->process_input_buf)
801     {
802       GstOmxReturn ret = GSTOMX_RETURN_OK;
803       ret = basefilter_class->process_input_buf(self,&buf);
804       if (ret == GSTOMX_RETURN_SKIP) {
805         gst_buffer_unref(buf);
806         goto leave;
807       }
808     }
809
810     if (self->adapter_size > 0) {
811       if (!self->adapter) {
812         GST_WARNING_OBJECT (self, "adapter is NULL. retry gst_adapter_new");
813         self->adapter = gst_adapter_new();
814       }
815
816       if (GST_BUFFER_IS_DISCONT(buf))
817       {
818         GST_INFO_OBJECT (self, "got GST_BUFFER_IS_DISCONT.");
819         gst_adapter_clear(self->adapter);
820       }
821
822       gst_adapter_push(self->adapter, buf);
823
824       src_size = gst_adapter_available(self->adapter);
825       if (src_size < self->adapter_size) {
826         GST_LOG_OBJECT (self, "Not enough data in adapter to feed to decoder.");
827         goto leave;
828       }
829
830       if (src_size > self->adapter_size) {
831         src_size = src_size - GST_BUFFER_SIZE(buf);
832         GST_LOG_OBJECT (self, "take buffer from adapter. size=%d", src_size);
833       }
834
835       src_timestamp = gst_adapter_prev_timestamp(self->adapter, NULL);
836       adapter_buf = gst_adapter_take_buffer(self->adapter, src_size);
837       src_data = GST_BUFFER_DATA(adapter_buf);
838       src_duration = GST_BUFFER_TIMESTAMP (buf) - src_timestamp;
839     } else {
840       src_data = GST_BUFFER_DATA (buf);
841       src_size = GST_BUFFER_SIZE (buf);
842       src_timestamp = GST_BUFFER_TIMESTAMP (buf);
843       src_duration = GST_BUFFER_DURATION (buf);
844     }
845
846     while (G_LIKELY (buffer_offset < src_size)) {
847       OMX_BUFFERHEADERTYPE *omx_buffer;
848
849       if (self->last_pad_push_return != GST_FLOW_OK ||
850           !(gomx->omx_state == OMX_StateExecuting ||
851               gomx->omx_state == OMX_StatePause)) {
852         goto out_flushing;
853       }
854
855       GST_LOG_OBJECT (self, "request buffer");
856       omx_buffer = g_omx_port_request_buffer (in_port);
857
858       GST_LOG_OBJECT (self, "omx_buffer: %p", omx_buffer);
859
860       if (G_LIKELY (omx_buffer)) {
861         log_buffer (self, omx_buffer, "pad_chain");
862
863         /* MODIFICATION: to handle input SN12 HW addr. (enc) */
864         if (is_extended_color_format(self, self->in_port)) {
865           SCMN_IMGB *inbuf = NULL;
866
867           if (!GST_BUFFER_MALLOCDATA(buf)) {
868               GST_WARNING_OBJECT (self, "null MALLOCDATA in hw color format. skip this.");
869               goto out_flushing;
870           }
871
872           inbuf = (SCMN_IMGB*)(GST_BUFFER_MALLOCDATA(buf));
873           if (inbuf != NULL && inbuf->buf_share_method == 1) {
874             GST_LOG_OBJECT (self, "enc. fd[0]:%d  fd[1]:%d  fd[2]:%d  w[0]:%d  h[0]:%d   buf_share_method:%d", 
875                 inbuf->fd[0], inbuf->fd[1], inbuf->fd[2], inbuf->w[0], inbuf->h[0], inbuf->buf_share_method);
876           } else if (inbuf != NULL && inbuf->buf_share_method == 0) {
877             GST_LOG_OBJECT (self, "enc input buf uses hw addr");
878           } else {
879             GST_WARNING_OBJECT (self, "enc input buf has wrong buf_share_method");
880           }
881
882           memcpy (omx_buffer->pBuffer, GST_BUFFER_MALLOCDATA(buf), sizeof(SCMN_IMGB));
883           omx_buffer->nAllocLen = sizeof(SCMN_IMGB);
884           omx_buffer->nFilledLen = sizeof(SCMN_IMGB);
885         } else if (omx_buffer->nOffset == 0 && self->in_port->shared_buffer) {
886           {
887             GstBuffer *old_buf;
888             old_buf = omx_buffer->pAppPrivate;
889
890             if (old_buf) {
891               gst_buffer_unref ((GstBuffer *)old_buf);
892             } else if (omx_buffer->pBuffer) {
893               g_free (omx_buffer->pBuffer);
894               omx_buffer->pBuffer = NULL;
895             }
896           }
897
898           omx_buffer->pBuffer = src_data;
899           omx_buffer->nAllocLen = src_size;
900           omx_buffer->nFilledLen = src_size;
901           omx_buffer->pAppPrivate = (self->adapter_size > 0) ? (OMX_PTR)adapter_buf : (OMX_PTR)buf;
902         } else {
903           omx_buffer->nFilledLen = MIN (src_size - buffer_offset,
904               omx_buffer->nAllocLen - omx_buffer->nOffset);
905           memcpy (omx_buffer->pBuffer + omx_buffer->nOffset,
906               src_data + buffer_offset, omx_buffer->nFilledLen);
907         }
908
909         if (self->use_timestamps) {
910           GstClockTime timestamp_offset = 0;
911
912           if (buffer_offset && src_duration != GST_CLOCK_TIME_NONE) {
913             timestamp_offset = gst_util_uint64_scale_int (buffer_offset,
914                 src_duration, src_size);
915           }
916
917           omx_buffer->nTimeStamp =
918               gst_util_uint64_scale_int (src_timestamp +
919               timestamp_offset, OMX_TICKS_PER_SECOND, GST_SECOND);
920         }
921
922         /* MODIFICATION: hw addr */
923         if (is_extended_color_format(self, self->in_port)) {
924           buffer_offset = GST_BUFFER_SIZE (buf);
925         } else {
926           buffer_offset += omx_buffer->nFilledLen;
927         }
928
929         GST_LOG_OBJECT (self, "release_buffer");
930                 /** @todo untaint buffer */
931         g_omx_port_release_buffer (in_port, omx_buffer);
932       } else {
933         GST_WARNING_OBJECT (self, "null buffer");
934         ret = GST_FLOW_WRONG_STATE;
935         goto out_flushing;
936       }
937     }
938   } else {
939     GST_WARNING_OBJECT (self, "done");
940     ret = GST_FLOW_UNEXPECTED;
941   }
942
943   if (!self->in_port->shared_buffer) {
944     if (self->adapter_size > 0 && adapter_buf) {
945       gst_buffer_unref (adapter_buf);
946       adapter_buf = NULL;
947     } else {
948       gst_buffer_unref (buf);
949     }
950   }
951
952 leave:
953
954   GST_LOG_OBJECT (self, "end");
955
956   return ret;
957
958   /* special conditions */
959 out_flushing:
960   {
961     const gchar *error_msg = NULL;
962
963     GST_LOG_OBJECT(self, "out_flushing");
964
965     if (gomx->omx_error) {
966       error_msg = "Error from OpenMAX component";
967     } else if (gomx->omx_state != OMX_StateExecuting &&
968         gomx->omx_state != OMX_StatePause) {
969       error_msg = "OpenMAX component in wrong state";
970     }
971
972     if (error_msg) {
973       GST_ELEMENT_ERROR (self, STREAM, FAILED, (NULL), ("%s", error_msg));
974       ret = GST_FLOW_ERROR;
975     }
976
977     if (self->adapter_size > 0 && adapter_buf) {
978       gst_buffer_unref (adapter_buf);
979       adapter_buf = NULL;
980     } else {
981       gst_buffer_unref (buf);
982     }
983
984     goto leave;
985   }
986 }
987
988 static gboolean
989 pad_event (GstPad * pad, GstEvent * event)
990 {
991   GstOmxBaseFilter *self;
992   GOmxCore *gomx;
993   GOmxPort *in_port;
994   gboolean ret = TRUE;
995
996   self = GST_OMX_BASE_FILTER (GST_OBJECT_PARENT (pad));
997   gomx = self->gomx;
998   in_port = self->in_port;
999
1000   GST_LOG_OBJECT (self, "begin");
1001
1002   GST_INFO_OBJECT (self, "event: %s", GST_EVENT_TYPE_NAME (event));
1003
1004   if (self->pad_event) {
1005     if (!self->pad_event(pad, event))
1006       return TRUE;
1007   }
1008
1009   switch (GST_EVENT_TYPE (event)) {
1010     case GST_EVENT_EOS:
1011       /* if we are init'ed, and there is a running loop; then
1012        * if we get a buffer to inform it of EOS, let it handle the rest
1013        * in any other case, we send EOS */
1014       if (self->ready && self->last_pad_push_return == GST_FLOW_OK) {
1015         /* send buffer with eos flag */
1016                 /** @todo move to util */
1017         {
1018           OMX_BUFFERHEADERTYPE *omx_buffer;
1019
1020           GST_LOG_OBJECT (self, "request buffer");
1021           omx_buffer = g_omx_port_request_buffer (in_port);
1022
1023           if (G_LIKELY (omx_buffer)) {
1024
1025             if (self->adapter_size > 0 && self->adapter) {
1026               guint src_len = 0;
1027               GstBuffer *adapter_buf = NULL;
1028
1029               src_len = gst_adapter_available(self->adapter);
1030               if (src_len > 0 && src_len < self->adapter_size) {
1031                 omx_buffer->nTimeStamp = gst_util_uint64_scale_int(
1032                    gst_adapter_prev_timestamp(self->adapter, NULL),
1033                    OMX_TICKS_PER_SECOND, GST_SECOND);
1034                 adapter_buf = gst_adapter_take_buffer(self->adapter, src_len);
1035                 omx_buffer->pBuffer = GST_BUFFER_DATA(adapter_buf);
1036                 omx_buffer->nAllocLen = src_len;
1037                 omx_buffer->nFilledLen = src_len;
1038                 omx_buffer->pAppPrivate = adapter_buf;
1039               }
1040               gst_adapter_clear(self->adapter);
1041             }
1042             omx_buffer->nFlags |= OMX_BUFFERFLAG_EOS;
1043
1044             GST_LOG_OBJECT (self, "release_buffer in EOS. size=%d", omx_buffer->nFilledLen);
1045             /* foo_buffer_untaint (omx_buffer); */
1046             g_omx_port_release_buffer (in_port, omx_buffer);
1047             /* loop handles EOS, eat it here */
1048             gst_event_unref (event);
1049             break;
1050           }
1051         }
1052       }
1053
1054       /* we tried, but it's up to us here */
1055       ret = gst_pad_push_event (self->srcpad, event);
1056       break;
1057
1058     case GST_EVENT_FLUSH_START:
1059       if (gomx->omx_state == OMX_StatePause || gomx->omx_state == OMX_StateExecuting) {
1060         gst_pad_push_event (self->srcpad, event);
1061         self->last_pad_push_return = GST_FLOW_WRONG_STATE;
1062
1063         g_omx_core_flush_start (gomx);
1064
1065         gst_pad_pause_task (self->srcpad);
1066
1067         ret = TRUE;
1068       } else {
1069         GST_ERROR_OBJECT (self, "flush start in wrong omx state");
1070         ret = FALSE;
1071       }
1072       break;
1073
1074     case GST_EVENT_FLUSH_STOP:
1075       if (gomx->omx_state == OMX_StatePause || gomx->omx_state == OMX_StateExecuting) {
1076         gst_pad_push_event (self->srcpad, event);
1077         self->last_pad_push_return = GST_FLOW_OK;
1078
1079         g_omx_core_flush_stop (gomx);
1080
1081         if (self->adapter_size > 0 && self->adapter) {
1082           gst_adapter_clear(self->adapter);
1083         }
1084
1085         if (self->ready)
1086           gst_pad_start_task (self->srcpad, output_loop, self->srcpad);
1087
1088         ret = TRUE;
1089       } else {
1090         GST_ERROR_OBJECT (self, "flush start in wrong omx state");
1091         ret = FALSE;
1092       }
1093       break;
1094
1095     case GST_EVENT_NEWSEGMENT:
1096       ret = gst_pad_push_event (self->srcpad, event);
1097       break;
1098
1099     default:
1100       ret = gst_pad_push_event (self->srcpad, event);
1101       break;
1102   }
1103
1104   GST_LOG_OBJECT (self, "end");
1105
1106   return ret;
1107 }
1108
1109 static gboolean
1110 activate_push (GstPad * pad, gboolean active)
1111 {
1112   gboolean result = TRUE;
1113   GstOmxBaseFilter *self;
1114
1115   self = GST_OMX_BASE_FILTER (gst_pad_get_parent (pad));
1116
1117   if (active) {
1118     GST_DEBUG_OBJECT (self, "activate");
1119     /* task may carry on */
1120     g_atomic_int_set (&self->last_pad_push_return, GST_FLOW_OK);
1121
1122     /* we do not start the task yet if the pad is not connected */
1123     if (gst_pad_is_linked (pad)) {
1124       if (self->ready) {
1125                 /** @todo link callback function also needed */
1126         g_omx_port_resume (self->in_port);
1127         g_omx_port_resume (self->out_port);
1128
1129         result = gst_pad_start_task (pad, output_loop, pad);
1130       }
1131     }
1132   } else {
1133     GST_DEBUG_OBJECT (self, "deactivate");
1134
1135     /* persuade task to bail out */
1136     g_atomic_int_set (&self->last_pad_push_return, GST_FLOW_WRONG_STATE);
1137
1138     if (self->ready) {
1139             /** @todo disable this until we properly reinitialize the buffers. */
1140 #if 0
1141       /* flush all buffers */
1142       OMX_SendCommand (self->gomx->omx_handle, OMX_CommandFlush, OMX_ALL, NULL);
1143 #endif
1144
1145       /* unlock loops */
1146       g_omx_port_pause (self->in_port);
1147       g_omx_port_pause (self->out_port);
1148     }
1149
1150     /* make sure streaming finishes */
1151     result = gst_pad_stop_task (pad);
1152   }
1153
1154   gst_object_unref (self);
1155
1156   return result;
1157 }
1158
1159 static void
1160 type_instance_init (GTypeInstance * instance, gpointer g_class)
1161 {
1162   GstOmxBaseFilter *self;
1163   GstElementClass *element_class;
1164
1165   element_class = GST_ELEMENT_CLASS (g_class);
1166
1167   self = GST_OMX_BASE_FILTER (instance);
1168
1169   GST_LOG_OBJECT (self, "begin");
1170
1171   self->use_timestamps = TRUE;
1172   self->use_state_tuning = FALSE;
1173   self->adapter_size = 0;
1174   self->adapter = NULL;
1175   self->duration = 0;
1176
1177   self->gomx = gstomx_core_new (self, G_TYPE_FROM_CLASS (g_class));
1178   self->in_port = g_omx_core_new_port (self->gomx, 0);
1179   self->out_port = g_omx_core_new_port (self->gomx, 1);
1180
1181   self->ready_lock = g_mutex_new ();
1182
1183   self->sinkpad =
1184       gst_pad_new_from_template (gst_element_class_get_pad_template
1185       (element_class, "sink"), "sink");
1186
1187   gst_pad_set_chain_function (self->sinkpad, pad_chain);
1188   gst_pad_set_event_function (self->sinkpad, pad_event);
1189
1190   self->srcpad =
1191       gst_pad_new_from_template (gst_element_class_get_pad_template
1192       (element_class, "src"), "src");
1193
1194   gst_pad_set_activatepush_function (self->srcpad, activate_push);
1195
1196   gst_pad_use_fixed_caps (self->srcpad);
1197
1198   gst_element_add_pad (GST_ELEMENT (self), self->sinkpad);
1199   gst_element_add_pad (GST_ELEMENT (self), self->srcpad);
1200
1201   GST_LOG_OBJECT (self, "end");
1202 }
1203
1204 static void
1205 omx_interface_init (GstImplementsInterfaceClass * klass)
1206 {
1207 }
1208
1209 static gboolean
1210 interface_supported (GstImplementsInterface * iface, GType type)
1211 {
1212   g_assert (type == GST_TYPE_OMX);
1213   return TRUE;
1214 }
1215
1216 static void
1217 interface_init (GstImplementsInterfaceClass * klass)
1218 {
1219   klass->supported = interface_supported;
1220 }
1221
1222 static void
1223 init_interfaces (GType type)
1224 {
1225   GInterfaceInfo *iface_info;
1226   GInterfaceInfo *omx_info;
1227
1228
1229   iface_info = g_new0 (GInterfaceInfo, 1);
1230   iface_info->interface_init = (GInterfaceInitFunc) interface_init;
1231
1232   g_type_add_interface_static (type, GST_TYPE_IMPLEMENTS_INTERFACE, iface_info);
1233   g_free (iface_info);
1234
1235   omx_info = g_new0 (GInterfaceInfo, 1);
1236   omx_info->interface_init = (GInterfaceInitFunc) omx_interface_init;
1237
1238   g_type_add_interface_static (type, GST_TYPE_OMX, omx_info);
1239   g_free (omx_info);
1240 }