tizen beta release
[profile/ivi/gst-openmax0.10.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 /* 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   ARG_USE_STATETUNING, /* STATE_TUNING */
37 };
38
39 static void init_interfaces (GType type);
40 GSTOMX_BOILERPLATE_FULL (GstOmxBaseFilter, gst_omx_base_filter, GstElement,
41     GST_TYPE_ELEMENT, init_interfaces);
42
43 static inline void
44 log_buffer (GstOmxBaseFilter * self, OMX_BUFFERHEADERTYPE * omx_buffer)
45 {
46   GST_DEBUG_OBJECT (self, "omx_buffer: "
47       "size=%lu, "
48       "len=%lu, "
49       "flags=%lu, "
50       "offset=%lu, "
51       "timestamp=%lld",
52       omx_buffer->nAllocLen, omx_buffer->nFilledLen, omx_buffer->nFlags,
53       omx_buffer->nOffset, omx_buffer->nTimeStamp);
54 }
55
56 /* Add_code_for_extended_color_format */
57 static gboolean
58 is_extended_color_format(GstOmxBaseFilter * self, GOmxPort * port)
59 {
60   OMX_PARAM_PORTDEFINITIONTYPE param;
61   OMX_HANDLETYPE omx_handle = self->gomx->omx_handle;
62
63   if (G_UNLIKELY (!omx_handle)) {
64     GST_WARNING_OBJECT (self, "no component");
65     return FALSE;
66   }
67
68   G_OMX_INIT_PARAM (param);
69
70   param.nPortIndex = port->port_index;
71   OMX_GetParameter (omx_handle, OMX_IndexParamPortDefinition, &param);
72
73   switch ((guint)param.format.video.eColorFormat) {
74     case OMX_EXT_COLOR_FormatNV12TPhysicalAddress:
75     case OMX_EXT_COLOR_FormatNV12LPhysicalAddress:
76     case OMX_EXT_COLOR_FormatNV12Tiled:
77       return TRUE;
78     default:
79       return FALSE;
80   }
81 }
82
83 static void
84 setup_ports (GstOmxBaseFilter * self)
85 {
86   /* Input port configuration. */
87   g_omx_port_setup (self->in_port);
88   gst_pad_set_element_private (self->sinkpad, self->in_port);
89
90   /* Output port configuration. */
91   g_omx_port_setup (self->out_port);
92   gst_pad_set_element_private (self->srcpad, self->out_port);
93
94   /* @todo: read from config file: */
95   if (g_getenv ("OMX_ALLOCATE_ON")) {
96     GST_DEBUG_OBJECT (self, "OMX_ALLOCATE_ON");
97     self->in_port->omx_allocate = TRUE;
98     self->out_port->omx_allocate = TRUE;
99     self->share_input_buffer = FALSE;
100     self->share_output_buffer = FALSE;
101   } else if (g_getenv ("OMX_SHARE_HACK_ON")) {
102     GST_DEBUG_OBJECT (self, "OMX_SHARE_HACK_ON");
103     self->share_input_buffer = TRUE;
104     self->share_output_buffer = TRUE;
105   } else if (g_getenv ("OMX_SHARE_HACK_OFF")) {
106     GST_DEBUG_OBJECT (self, "OMX_SHARE_HACK_OFF");
107     self->share_input_buffer = FALSE;
108     self->share_output_buffer = FALSE;
109   /* Add extended_color_format */
110   } else if (self->gomx->component_vendor == GOMX_VENDOR_SLSI) {
111     self->share_input_buffer = (is_extended_color_format(self, self->in_port))
112         ? FALSE : TRUE;
113     self->share_output_buffer = (is_extended_color_format(self, self->out_port))
114         ? FALSE : TRUE;
115   } else {
116     GST_DEBUG_OBJECT (self, "default sharing and allocation");
117   }
118
119   GST_DEBUG_OBJECT (self, "omx_allocate: in: %d, out: %d",
120       self->in_port->omx_allocate, self->out_port->omx_allocate);
121   GST_DEBUG_OBJECT (self, "share_buffer: in: %d, out: %d",
122       self->share_input_buffer, self->share_output_buffer);
123 }
124
125 static GstStateChangeReturn
126 change_state (GstElement * element, GstStateChange transition)
127 {
128   GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
129   GstOmxBaseFilter *self;
130   GOmxCore *core;
131
132   self = GST_OMX_BASE_FILTER (element);
133   core = self->gomx;
134
135   GST_LOG_OBJECT (self, "begin");
136
137   GST_INFO_OBJECT (self, "changing state %s - %s",
138       gst_element_state_get_name (GST_STATE_TRANSITION_CURRENT (transition)),
139       gst_element_state_get_name (GST_STATE_TRANSITION_NEXT (transition)));
140
141   switch (transition) {
142     case GST_STATE_CHANGE_NULL_TO_READY:
143       if (core->omx_state != OMX_StateLoaded) {
144         ret = GST_STATE_CHANGE_FAILURE;
145         goto leave;
146       }
147       break;
148
149     case GST_STATE_CHANGE_READY_TO_PAUSED:
150       /* STATE_TUNING */
151        if (self->use_state_tuning) {
152          GST_INFO_OBJECT (self, "use state-tuning feature");
153          g_mutex_lock (self->ready_lock);
154
155          self->sink_set_caps = (GstCaps *)gst_pad_peer_get_caps (self->sinkpad);
156          if (self->sink_set_caps == NULL) {
157            GST_ERROR_OBJECT (self, "Caps is NULL");
158          }
159
160          GST_INFO_OBJECT (self, "omx: prepare");
161
162          /** @todo this should probably go after doing preparations. */
163          if (self->omx_setup) {
164            self->omx_setup (self);
165          }
166
167          setup_ports (self);
168
169          g_omx_core_prepare (self->gomx);
170
171          if (core->omx_state == OMX_StateIdle) {
172            self->ready = TRUE;
173            gst_pad_start_task (self->srcpad, output_loop, self->srcpad);
174          } else {
175            GST_ERROR_OBJECT(self, "fail to move from OMX state Loaded to Idle");
176            g_omx_port_finish(self->in_port);
177            g_omx_port_finish(self->out_port);
178            g_omx_core_stop(core);
179            g_omx_core_unload(core);
180            g_mutex_unlock(self->ready_lock);
181            ret = GST_STATE_CHANGE_FAILURE;
182            goto leave;
183          }
184
185          g_mutex_unlock (self->ready_lock);
186       }
187       break;
188
189     default:
190       break;
191   }
192
193   ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
194
195   if (ret == GST_STATE_CHANGE_FAILURE)
196     goto leave;
197
198   switch (transition) {
199     case GST_STATE_CHANGE_PAUSED_TO_READY:
200       g_mutex_lock (self->ready_lock);
201       if (self->ready) {
202         /* unlock */
203         g_omx_port_finish (self->in_port);
204         g_omx_port_finish (self->out_port);
205
206         g_omx_core_stop (core);
207         g_omx_core_unload (core);
208         self->ready = FALSE;
209       }
210       g_mutex_unlock (self->ready_lock);
211       if (core->omx_state != OMX_StateLoaded &&
212           core->omx_state != OMX_StateInvalid) {
213         ret = GST_STATE_CHANGE_FAILURE;
214         goto leave;
215       }
216       break;
217
218     default:
219       break;
220   }
221
222 leave:
223   GST_LOG_OBJECT (self, "end");
224
225   return ret;
226 }
227
228 static void
229 finalize (GObject * obj)
230 {
231   GstOmxBaseFilter *self;
232
233   self = GST_OMX_BASE_FILTER (obj);
234
235   if (self->codec_data) {
236     gst_buffer_unref (self->codec_data);
237     self->codec_data = NULL;
238   }
239
240   g_omx_core_free (self->gomx);
241
242   g_mutex_free (self->ready_lock);
243
244   G_OBJECT_CLASS (parent_class)->finalize (obj);
245 }
246
247 static void
248 set_property (GObject * obj,
249     guint prop_id, const GValue * value, GParamSpec * pspec)
250 {
251   GstOmxBaseFilter *self;
252
253   self = GST_OMX_BASE_FILTER (obj);
254
255   switch (prop_id) {
256     case ARG_USE_TIMESTAMPS:
257       self->use_timestamps = g_value_get_boolean (value);
258       break;
259     case ARG_NUM_INPUT_BUFFERS:
260     case ARG_NUM_OUTPUT_BUFFERS:
261     {
262       OMX_PARAM_PORTDEFINITIONTYPE param;
263       OMX_HANDLETYPE omx_handle = self->gomx->omx_handle;
264       OMX_U32 nBufferCountActual;
265       GOmxPort *port = (prop_id == ARG_NUM_INPUT_BUFFERS) ?
266           self->in_port : self->out_port;
267
268       if (G_UNLIKELY (!omx_handle)) {
269         GST_WARNING_OBJECT (self, "no component");
270         break;
271       }
272
273       nBufferCountActual = g_value_get_uint (value);
274
275       G_OMX_INIT_PARAM (param);
276
277       param.nPortIndex = port->port_index;
278       OMX_GetParameter (omx_handle, OMX_IndexParamPortDefinition, &param);
279
280       if (nBufferCountActual < param.nBufferCountMin) {
281         GST_ERROR_OBJECT (self, "buffer count %lu is less than minimum %lu",
282             nBufferCountActual, param.nBufferCountMin);
283         return;
284       }
285
286       param.nBufferCountActual = nBufferCountActual;
287
288       OMX_SetParameter (omx_handle, OMX_IndexParamPortDefinition, &param);
289     }
290       break;
291     /* STATE_TUNING */
292     case ARG_USE_STATETUNING:
293       self->use_state_tuning = g_value_get_boolean(value);
294       break;
295     default:
296       G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, prop_id, pspec);
297       break;
298   }
299 }
300
301 static void
302 get_property (GObject * obj, guint prop_id, GValue * value, GParamSpec * pspec)
303 {
304   GstOmxBaseFilter *self;
305
306   self = GST_OMX_BASE_FILTER (obj);
307
308   if (gstomx_get_property_helper (self->gomx, prop_id, value))
309     return;
310
311   switch (prop_id) {
312     case ARG_USE_TIMESTAMPS:
313       g_value_set_boolean (value, self->use_timestamps);
314       break;
315     case ARG_NUM_INPUT_BUFFERS:
316     case ARG_NUM_OUTPUT_BUFFERS:
317     {
318       OMX_PARAM_PORTDEFINITIONTYPE param;
319       OMX_HANDLETYPE omx_handle = self->gomx->omx_handle;
320       GOmxPort *port = (prop_id == ARG_NUM_INPUT_BUFFERS) ?
321           self->in_port : self->out_port;
322
323       if (G_UNLIKELY (!omx_handle)) {
324         GST_WARNING_OBJECT (self, "no component");
325         g_value_set_uint (value, 0);
326         break;
327       }
328
329       G_OMX_INIT_PARAM (param);
330
331       param.nPortIndex = port->port_index;
332       OMX_GetParameter (omx_handle, OMX_IndexParamPortDefinition, &param);
333
334       g_value_set_uint (value, param.nBufferCountActual);
335     }
336       break;
337     /* STATE_TUNING */
338     case ARG_USE_STATETUNING:
339       g_value_set_boolean(value, self->use_state_tuning);
340       break;
341     default:
342       G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, prop_id, pspec);
343       break;
344   }
345 }
346
347 static void
348 type_base_init (gpointer g_class)
349 {
350 }
351
352 static void
353 type_class_init (gpointer g_class, gpointer class_data)
354 {
355   GObjectClass *gobject_class;
356   GstElementClass *gstelement_class;
357
358   gobject_class = G_OBJECT_CLASS (g_class);
359   gstelement_class = GST_ELEMENT_CLASS (g_class);
360
361   gobject_class->finalize = finalize;
362   gstelement_class->change_state = change_state;
363
364   /* Properties stuff */
365   {
366     gobject_class->set_property = set_property;
367     gobject_class->get_property = get_property;
368
369     gstomx_install_property_helper (gobject_class);
370
371     g_object_class_install_property (gobject_class, ARG_USE_TIMESTAMPS,
372         g_param_spec_boolean ("use-timestamps", "Use timestamps",
373             "Whether or not to use timestamps",
374             TRUE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
375
376     g_object_class_install_property (gobject_class, ARG_NUM_INPUT_BUFFERS,
377         g_param_spec_uint ("input-buffers", "Input buffers",
378             "The number of OMX input buffers",
379             1, 10, 4, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
380     g_object_class_install_property (gobject_class, ARG_NUM_OUTPUT_BUFFERS,
381         g_param_spec_uint ("output-buffers", "Output buffers",
382             "The number of OMX output buffers",
383             1, 10, 4, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
384
385     /* STATE_TUNING */
386     g_object_class_install_property (gobject_class, ARG_USE_STATETUNING,
387         g_param_spec_boolean ("state-tuning", "start omx component in gst paused state",
388         "Whether or not to use state-tuning feature",
389         FALSE, G_PARAM_READWRITE));
390   }
391 }
392
393 static inline GstFlowReturn
394 push_buffer (GstOmxBaseFilter * self, GstBuffer * buf)
395 {
396   GstFlowReturn ret;
397
398     /** @todo check if tainted */
399   GST_LOG_OBJECT (self, "begin");
400   ret = gst_pad_push (self->srcpad, buf);
401   GST_LOG_OBJECT (self, "end");
402
403   return ret;
404 }
405
406 static void
407 output_loop (gpointer data)
408 {
409   GstPad *pad;
410   GOmxCore *gomx;
411   GOmxPort *out_port;
412   GstOmxBaseFilter *self;
413   GstFlowReturn ret = GST_FLOW_OK;
414
415   pad = data;
416   self = GST_OMX_BASE_FILTER (gst_pad_get_parent (pad));
417   gomx = self->gomx;
418
419   GST_LOG_OBJECT (self, "begin");
420
421   /* do not bother if we have been setup to bail out */
422   if ((ret = g_atomic_int_get (&self->last_pad_push_return)) != GST_FLOW_OK)
423     goto leave;
424
425   if (!self->ready) {
426     g_error ("not ready");
427     return;
428   }
429
430   out_port = self->out_port;
431
432   if (G_LIKELY (out_port->enabled)) {
433     OMX_BUFFERHEADERTYPE *omx_buffer = NULL;
434
435     GST_LOG_OBJECT (self, "request buffer");
436     omx_buffer = g_omx_port_request_buffer (out_port);
437
438     GST_LOG_OBJECT (self, "omx_buffer: %p", omx_buffer);
439
440     if (G_UNLIKELY (!omx_buffer)) {
441       GST_WARNING_OBJECT (self, "null buffer: leaving");
442       ret = GST_FLOW_WRONG_STATE;
443       goto leave;
444     }
445
446     log_buffer (self, omx_buffer);
447
448     if (G_LIKELY (omx_buffer->nFilledLen > 0)) {
449       GstBuffer *buf;
450
451 #if 1
452             /** @todo remove this check */
453       if (G_LIKELY (self->in_port->enabled)) {
454         GstCaps *caps = NULL;
455
456         caps = gst_pad_get_negotiated_caps (self->srcpad);
457
458         if (!caps) {
459                     /** @todo We shouldn't be doing this. */
460           GST_WARNING_OBJECT (self, "faking settings changed notification");
461           if (gomx->settings_changed_cb)
462             gomx->settings_changed_cb (gomx);
463         } else {
464           GST_LOG_OBJECT (self, "caps already fixed: %" GST_PTR_FORMAT, caps);
465           gst_caps_unref (caps);
466         }
467       }
468 #endif
469
470       /* buf is always null when the output buffer pointer isn't shared. */
471       buf = omx_buffer->pAppPrivate;
472
473             /** @todo we need to move all the caps handling to one single
474              * place, in the output loop probably. */
475       if (G_UNLIKELY (omx_buffer->nFlags & OMX_BUFFERFLAG_CODECCONFIG)) {
476         GstCaps *caps = NULL;
477         GstStructure *structure;
478         GValue value = { 0, {{0}
479             }
480         };
481
482         caps = gst_pad_get_negotiated_caps (self->srcpad);
483         caps = gst_caps_make_writable (caps);
484         structure = gst_caps_get_structure (caps, 0);
485
486         g_value_init (&value, GST_TYPE_BUFFER);
487         buf = gst_buffer_new_and_alloc (omx_buffer->nFilledLen);
488         memcpy (GST_BUFFER_DATA (buf),
489             omx_buffer->pBuffer + omx_buffer->nOffset, omx_buffer->nFilledLen);
490         gst_value_set_buffer (&value, buf);
491         gst_buffer_unref (buf);
492         gst_structure_set_value (structure, "codec_data", &value);
493         g_value_unset (&value);
494
495         gst_pad_set_caps (self->srcpad, caps);
496
497       } else if (is_extended_color_format(self, self->out_port)) {
498         GstCaps *caps = NULL;
499         GstStructure *structure;
500         gint width = 0, height = 0;
501
502         caps = gst_pad_get_negotiated_caps(self->srcpad);
503         structure = gst_caps_get_structure(caps, 0);
504
505         gst_structure_get_int(structure, "width", &width);
506         gst_structure_get_int(structure, "height", &height);
507
508         if (G_LIKELY((width > 0) && (height > 0))) {
509           buf = gst_buffer_new_and_alloc((width * height * 3) / 2);
510         } else {
511           GST_ERROR_OBJECT (self, "invalid buffer size");
512           ret = GST_FLOW_UNEXPECTED;
513           goto leave;
514         }
515
516         memcpy (GST_BUFFER_MALLOCDATA(buf), omx_buffer->pBuffer, omx_buffer->nFilledLen);
517
518         if (self->use_timestamps) {
519           GST_BUFFER_TIMESTAMP (buf) =
520               gst_util_uint64_scale_int (omx_buffer->nTimeStamp, GST_SECOND,
521               OMX_TICKS_PER_SECOND);
522         }
523         gst_buffer_set_caps(buf, GST_PAD_CAPS(self->srcpad));
524
525         ret = push_buffer (self, buf);
526       } else if (buf && !(omx_buffer->nFlags & OMX_BUFFERFLAG_EOS)) {
527         GST_BUFFER_SIZE (buf) = omx_buffer->nFilledLen;
528         if (self->use_timestamps) {
529           GST_BUFFER_TIMESTAMP (buf) =
530               gst_util_uint64_scale_int (omx_buffer->nTimeStamp, GST_SECOND,
531               OMX_TICKS_PER_SECOND);
532         }
533
534         omx_buffer->pAppPrivate = NULL;
535         omx_buffer->pBuffer = NULL;
536
537         /* Set sync frame info while encoding */
538         if (omx_buffer->nFlags & OMX_BUFFERFLAG_SYNCFRAME) {
539           GST_BUFFER_FLAG_UNSET(buf, GST_BUFFER_FLAG_DELTA_UNIT);
540         } else {
541           GST_BUFFER_FLAG_SET(buf, GST_BUFFER_FLAG_DELTA_UNIT);
542         }
543
544         ret = push_buffer (self, buf);
545
546         gst_buffer_unref (buf);
547       } else {
548         /* This is only meant for the first OpenMAX buffers,
549          * which need to be pre-allocated. */
550         /* Also for the very last one. */
551         ret = gst_pad_alloc_buffer_and_set_caps (self->srcpad,
552             GST_BUFFER_OFFSET_NONE,
553             omx_buffer->nFilledLen, GST_PAD_CAPS (self->srcpad), &buf);
554
555         if (G_LIKELY (buf)) {
556           memcpy (GST_BUFFER_DATA (buf),
557               omx_buffer->pBuffer + omx_buffer->nOffset,
558               omx_buffer->nFilledLen);
559           if (self->use_timestamps) {
560             GST_BUFFER_TIMESTAMP (buf) =
561                 gst_util_uint64_scale_int (omx_buffer->nTimeStamp, GST_SECOND,
562                 OMX_TICKS_PER_SECOND);
563           }
564
565           if (self->share_output_buffer) {
566             GST_WARNING_OBJECT (self, "couldn't zero-copy");
567             /* If pAppPrivate is NULL, it means it was a dummy
568              * allocation, free it. */
569             if (!omx_buffer->pAppPrivate) {
570               g_free (omx_buffer->pBuffer);
571               omx_buffer->pBuffer = NULL;
572             }
573           }
574
575         /* Set sync frame info while encoding */
576         if (omx_buffer->nFlags & OMX_BUFFERFLAG_SYNCFRAME) {
577           GST_BUFFER_FLAG_UNSET(buf, GST_BUFFER_FLAG_DELTA_UNIT);
578         } else {
579           GST_BUFFER_FLAG_SET(buf, GST_BUFFER_FLAG_DELTA_UNIT);
580         }
581
582           ret = push_buffer (self, buf);
583         } else {
584           GST_WARNING_OBJECT (self, "couldn't allocate buffer of size %lu",
585               omx_buffer->nFilledLen);
586         }
587       }
588     } else {
589       GST_WARNING_OBJECT (self, "empty buffer");
590     }
591
592     if (self->share_output_buffer &&
593         !omx_buffer->pBuffer && omx_buffer->nOffset == 0) {
594       GstBuffer *buf;
595       GstFlowReturn result;
596
597       GST_LOG_OBJECT (self, "allocate buffer");
598       result = gst_pad_alloc_buffer_and_set_caps (self->srcpad,
599           GST_BUFFER_OFFSET_NONE,
600           omx_buffer->nAllocLen, GST_PAD_CAPS (self->srcpad), &buf);
601
602       if (G_LIKELY (result == GST_FLOW_OK)) {
603         gst_buffer_ref (buf);
604         omx_buffer->pAppPrivate = buf;
605
606         omx_buffer->pBuffer = GST_BUFFER_DATA (buf);
607         omx_buffer->nAllocLen = GST_BUFFER_SIZE (buf);
608       } else {
609         GST_WARNING_OBJECT (self,
610             "could not pad allocate buffer, using malloc");
611         omx_buffer->pBuffer = g_malloc (omx_buffer->nAllocLen);
612       }
613     }
614
615     if (self->share_output_buffer && !omx_buffer->pBuffer) {
616       GST_ERROR_OBJECT (self, "no input buffer to share");
617     }
618
619     if (G_UNLIKELY (omx_buffer->nFlags & OMX_BUFFERFLAG_EOS)) {
620       GST_DEBUG_OBJECT (self, "got eos");
621       gst_pad_push_event (self->srcpad, gst_event_new_eos ());
622       omx_buffer->nFlags &= ~OMX_BUFFERFLAG_EOS;
623       ret = GST_FLOW_UNEXPECTED;
624     }
625
626     omx_buffer->nFilledLen = 0;
627     GST_LOG_OBJECT (self, "release_buffer");
628     g_omx_port_release_buffer (out_port, omx_buffer);
629   }
630
631 leave:
632
633   self->last_pad_push_return = ret;
634
635   if (gomx->omx_error != OMX_ErrorNone)
636     ret = GST_FLOW_ERROR;
637
638   if (ret != GST_FLOW_OK) {
639     GST_INFO_OBJECT (self, "pause task, reason:  %s", gst_flow_get_name (ret));
640     gst_pad_pause_task (self->srcpad);
641   }
642
643   GST_LOG_OBJECT (self, "end");
644
645   gst_object_unref (self);
646 }
647
648 static GstFlowReturn
649 pad_chain (GstPad * pad, GstBuffer * buf)
650 {
651   GOmxCore *gomx;
652   GOmxPort *in_port;
653   GstOmxBaseFilter *self;
654   GstFlowReturn ret = GST_FLOW_OK;
655
656   self = GST_OMX_BASE_FILTER (GST_OBJECT_PARENT (pad));
657
658   gomx = self->gomx;
659
660   GST_LOG_OBJECT (self, "begin");
661   GST_LOG_OBJECT (self, "gst_buffer: size=%u", GST_BUFFER_SIZE (buf));
662
663   GST_LOG_OBJECT (self, "state: %d", gomx->omx_state);
664
665   /* STATE_TUNING */
666   if (!self->use_state_tuning) {
667     if (G_UNLIKELY (gomx->omx_state == OMX_StateLoaded)) {
668       g_mutex_lock (self->ready_lock);
669
670       GST_INFO_OBJECT (self, "omx: prepare");
671
672       /** @todo this should probably go after doing preparations. */
673       if (self->omx_setup) {
674         self->omx_setup (self);
675       }
676
677       setup_ports (self);
678
679       g_omx_core_prepare (self->gomx);
680
681       if (gomx->omx_state == OMX_StateIdle) {
682         self->ready = TRUE;
683         gst_pad_start_task (self->srcpad, output_loop, self->srcpad);
684       }
685
686       g_mutex_unlock (self->ready_lock);
687
688       if (gomx->omx_state != OMX_StateIdle)
689         goto out_flushing;
690     }
691   }
692
693   in_port = self->in_port;
694
695   if (G_LIKELY (in_port->enabled)) {
696     guint buffer_offset = 0;
697
698     if (G_UNLIKELY (gomx->omx_state == OMX_StateIdle)) {
699       GST_INFO_OBJECT (self, "omx: play");
700       g_omx_core_start (gomx);
701
702       if (gomx->omx_state != OMX_StateExecuting)
703         goto out_flushing;
704
705       /* send buffer with codec data flag */
706             /** @todo move to util */
707       if (self->codec_data) {
708         OMX_BUFFERHEADERTYPE *omx_buffer;
709
710         GST_LOG_OBJECT (self, "request buffer");
711         omx_buffer = g_omx_port_request_buffer (in_port);
712
713         if (G_LIKELY (omx_buffer)) {
714           omx_buffer->nFlags |= OMX_BUFFERFLAG_CODECCONFIG;     /* codec data flag */
715
716           omx_buffer->nFilledLen = GST_BUFFER_SIZE (self->codec_data);
717           memcpy (omx_buffer->pBuffer + omx_buffer->nOffset,
718               GST_BUFFER_DATA (self->codec_data), omx_buffer->nFilledLen);
719
720           GST_LOG_OBJECT (self, "release_buffer");
721           g_omx_port_release_buffer (in_port, omx_buffer);
722         }
723       }
724     }
725
726     if (G_UNLIKELY (gomx->omx_state != OMX_StateExecuting)) {
727       GST_ERROR_OBJECT (self, "Whoa! very wrong");
728     }
729
730     while (G_LIKELY (buffer_offset < GST_BUFFER_SIZE (buf))) {
731       OMX_BUFFERHEADERTYPE *omx_buffer;
732
733       if (self->last_pad_push_return != GST_FLOW_OK ||
734           !(gomx->omx_state == OMX_StateExecuting ||
735               gomx->omx_state == OMX_StatePause)) {
736         goto out_flushing;
737       }
738
739       GST_LOG_OBJECT (self, "request buffer");
740       omx_buffer = g_omx_port_request_buffer (in_port);
741
742       GST_LOG_OBJECT (self, "omx_buffer: %p", omx_buffer);
743
744       if (G_LIKELY (omx_buffer)) {
745         log_buffer (self, omx_buffer);
746
747         if (is_extended_color_format(self, self->in_port)) {
748             if(!GST_BUFFER_MALLOCDATA(buf)) {
749                 GST_WARNING_OBJECT (self, "null MALLOCDATA in hw color format. skip this.");
750                 ret = GST_FLOW_OK;
751                 goto out_flushing;
752             }
753           /* Copy p[0], p[1] of SCMN_IMGB to pAddrY, pAddrC of MFC_ENC_ADDR_INFO */
754           memcpy (omx_buffer->pBuffer,
755               GST_BUFFER_MALLOCDATA(buf)
756               + (sizeof (int) * 4 + sizeof (void*)) * 4, sizeof (void*));
757           memcpy (omx_buffer->pBuffer + sizeof (void*),
758               GST_BUFFER_MALLOCDATA(buf)
759               + (sizeof (int) * 4 + sizeof (void*)) * 4 + sizeof (void*),
760               sizeof (void*));
761           omx_buffer->nAllocLen = sizeof (void*) * 2;
762           omx_buffer->nFilledLen = sizeof (void*) * 2;
763         } else if (omx_buffer->nOffset == 0 && self->share_input_buffer) {
764           {
765             GstBuffer *old_buf;
766             old_buf = omx_buffer->pAppPrivate;
767
768             if (old_buf) {
769               gst_buffer_unref (old_buf);
770             } else if (omx_buffer->pBuffer) {
771               g_free (omx_buffer->pBuffer);
772             }
773           }
774
775           omx_buffer->pBuffer = GST_BUFFER_DATA (buf);
776           omx_buffer->nAllocLen = GST_BUFFER_SIZE (buf);
777           omx_buffer->nFilledLen = GST_BUFFER_SIZE (buf);
778           omx_buffer->pAppPrivate = buf;
779         } else {
780           omx_buffer->nFilledLen = MIN (GST_BUFFER_SIZE (buf) - buffer_offset,
781               omx_buffer->nAllocLen - omx_buffer->nOffset);
782           memcpy (omx_buffer->pBuffer + omx_buffer->nOffset,
783               GST_BUFFER_DATA (buf) + buffer_offset, omx_buffer->nFilledLen);
784         }
785
786         if (self->use_timestamps) {
787           GstClockTime timestamp_offset = 0;
788
789           if (buffer_offset && GST_BUFFER_DURATION (buf) != GST_CLOCK_TIME_NONE) {
790             timestamp_offset = gst_util_uint64_scale_int (buffer_offset,
791                 GST_BUFFER_DURATION (buf), GST_BUFFER_SIZE (buf));
792           }
793
794           omx_buffer->nTimeStamp =
795               gst_util_uint64_scale_int (GST_BUFFER_TIMESTAMP (buf) +
796               timestamp_offset, OMX_TICKS_PER_SECOND, GST_SECOND);
797         }
798
799         if (is_extended_color_format(self, self->in_port)) {
800           buffer_offset = GST_BUFFER_SIZE (buf);
801         } else {
802           buffer_offset += omx_buffer->nFilledLen;
803         }
804
805         GST_LOG_OBJECT (self, "release_buffer");
806                 /** @todo untaint buffer */
807         g_omx_port_release_buffer (in_port, omx_buffer);
808       } else {
809         GST_WARNING_OBJECT (self, "null buffer");
810         ret = GST_FLOW_WRONG_STATE;
811         goto out_flushing;
812       }
813     }
814   } else {
815     GST_WARNING_OBJECT (self, "done");
816     ret = GST_FLOW_UNEXPECTED;
817   }
818
819   if (!self->share_input_buffer) {
820     gst_buffer_unref (buf);
821   }
822
823 leave:
824
825   GST_LOG_OBJECT (self, "end");
826
827   return ret;
828
829   /* special conditions */
830 out_flushing:
831   {
832     const gchar *error_msg = NULL;
833
834     if (gomx->omx_error) {
835       error_msg = "Error from OpenMAX component";
836     } else if (gomx->omx_state != OMX_StateExecuting &&
837         gomx->omx_state != OMX_StatePause) {
838       error_msg = "OpenMAX component in wrong state";
839     }
840
841     if (error_msg) {
842       GST_ELEMENT_ERROR (self, STREAM, FAILED, (NULL), ("%s", error_msg));
843       ret = GST_FLOW_ERROR;
844     }
845
846     gst_buffer_unref (buf);
847
848     goto leave;
849   }
850 }
851
852 static gboolean
853 pad_event (GstPad * pad, GstEvent * event)
854 {
855   GstOmxBaseFilter *self;
856   GOmxCore *gomx;
857   GOmxPort *in_port;
858   gboolean ret = TRUE;
859
860   self = GST_OMX_BASE_FILTER (GST_OBJECT_PARENT (pad));
861   gomx = self->gomx;
862   in_port = self->in_port;
863
864   GST_LOG_OBJECT (self, "begin");
865
866   GST_INFO_OBJECT (self, "event: %s", GST_EVENT_TYPE_NAME (event));
867
868   switch (GST_EVENT_TYPE (event)) {
869     case GST_EVENT_EOS:
870       /* if we are init'ed, and there is a running loop; then
871        * if we get a buffer to inform it of EOS, let it handle the rest
872        * in any other case, we send EOS */
873       if (self->ready && self->last_pad_push_return == GST_FLOW_OK) {
874         /* send buffer with eos flag */
875                 /** @todo move to util */
876         {
877           OMX_BUFFERHEADERTYPE *omx_buffer;
878
879           GST_LOG_OBJECT (self, "request buffer");
880           omx_buffer = g_omx_port_request_buffer (in_port);
881
882           if (G_LIKELY (omx_buffer)) {
883             omx_buffer->nFlags |= OMX_BUFFERFLAG_EOS;
884
885             GST_LOG_OBJECT (self, "release_buffer");
886             /* foo_buffer_untaint (omx_buffer); */
887             g_omx_port_release_buffer (in_port, omx_buffer);
888             /* loop handles EOS, eat it here */
889             gst_event_unref (event);
890             break;
891           }
892         }
893       }
894
895       /* we tried, but it's up to us here */
896       ret = gst_pad_push_event (self->srcpad, event);
897       break;
898
899     case GST_EVENT_FLUSH_START:
900     if((gomx->omx_state == OMX_StatePause)||(gomx->omx_state == OMX_StateExecuting)) {
901       gst_pad_push_event (self->srcpad, event);
902       self->last_pad_push_return = GST_FLOW_WRONG_STATE;
903
904       g_omx_core_flush_start (gomx);
905
906       gst_pad_pause_task (self->srcpad);
907
908       ret = TRUE;
909     } else {
910       GST_WARNING_OBJECT (self, "flush start in wrong omx state");
911       ret = FALSE;
912     }
913       break;
914
915     case GST_EVENT_FLUSH_STOP:
916     if((gomx->omx_state == OMX_StatePause)||(gomx->omx_state == OMX_StateExecuting)) {
917       gst_pad_push_event (self->srcpad, event);
918       self->last_pad_push_return = GST_FLOW_OK;
919
920       g_omx_core_flush_stop (gomx);
921
922       if (self->ready)
923         gst_pad_start_task (self->srcpad, output_loop, self->srcpad);
924
925       ret = TRUE;
926     } else {
927       GST_WARNING_OBJECT (self, "flush start in wrong omx state");
928       ret = FALSE;
929     }
930       break;
931
932     case GST_EVENT_NEWSEGMENT:
933       ret = gst_pad_push_event (self->srcpad, event);
934       break;
935
936     default:
937       ret = gst_pad_push_event (self->srcpad, event);
938       break;
939   }
940
941   GST_LOG_OBJECT (self, "end");
942
943   return ret;
944 }
945
946 static gboolean
947 activate_push (GstPad * pad, gboolean active)
948 {
949   gboolean result = TRUE;
950   GstOmxBaseFilter *self;
951
952   self = GST_OMX_BASE_FILTER (gst_pad_get_parent (pad));
953
954   if (active) {
955     GST_DEBUG_OBJECT (self, "activate");
956     /* task may carry on */
957     g_atomic_int_set (&self->last_pad_push_return, GST_FLOW_OK);
958
959     /* we do not start the task yet if the pad is not connected */
960     if (gst_pad_is_linked (pad)) {
961       if (self->ready) {
962                 /** @todo link callback function also needed */
963         g_omx_port_resume (self->in_port);
964         g_omx_port_resume (self->out_port);
965
966         result = gst_pad_start_task (pad, output_loop, pad);
967       }
968     }
969   } else {
970     GST_DEBUG_OBJECT (self, "deactivate");
971
972     /* persuade task to bail out */
973     g_atomic_int_set (&self->last_pad_push_return, GST_FLOW_WRONG_STATE);
974
975     if (self->ready) {
976             /** @todo disable this until we properly reinitialize the buffers. */
977 #if 0
978       /* flush all buffers */
979       OMX_SendCommand (self->gomx->omx_handle, OMX_CommandFlush, OMX_ALL, NULL);
980 #endif
981
982       /* unlock loops */
983       g_omx_port_pause (self->in_port);
984       g_omx_port_pause (self->out_port);
985     }
986
987     /* make sure streaming finishes */
988     result = gst_pad_stop_task (pad);
989   }
990
991   gst_object_unref (self);
992
993   return result;
994 }
995
996 static void
997 type_instance_init (GTypeInstance * instance, gpointer g_class)
998 {
999   GstOmxBaseFilter *self;
1000   GstElementClass *element_class;
1001
1002   element_class = GST_ELEMENT_CLASS (g_class);
1003
1004   self = GST_OMX_BASE_FILTER (instance);
1005
1006   GST_LOG_OBJECT (self, "begin");
1007
1008   self->use_timestamps = TRUE;
1009   self->use_state_tuning = FALSE; /* STATE_TUNING */
1010
1011   self->gomx = gstomx_core_new (self, G_TYPE_FROM_CLASS (g_class));
1012   self->in_port = g_omx_core_new_port (self->gomx, 0);
1013   self->out_port = g_omx_core_new_port (self->gomx, 1);
1014
1015   self->ready_lock = g_mutex_new ();
1016
1017   self->sinkpad =
1018       gst_pad_new_from_template (gst_element_class_get_pad_template
1019       (element_class, "sink"), "sink");
1020
1021   gst_pad_set_chain_function (self->sinkpad, pad_chain);
1022   gst_pad_set_event_function (self->sinkpad, pad_event);
1023
1024   self->srcpad =
1025       gst_pad_new_from_template (gst_element_class_get_pad_template
1026       (element_class, "src"), "src");
1027
1028   gst_pad_set_activatepush_function (self->srcpad, activate_push);
1029
1030   gst_pad_use_fixed_caps (self->srcpad);
1031
1032   gst_element_add_pad (GST_ELEMENT (self), self->sinkpad);
1033   gst_element_add_pad (GST_ELEMENT (self), self->srcpad);
1034
1035   GST_LOG_OBJECT (self, "end");
1036 }
1037
1038 static void
1039 omx_interface_init (GstImplementsInterfaceClass * klass)
1040 {
1041 }
1042
1043 static gboolean
1044 interface_supported (GstImplementsInterface * iface, GType type)
1045 {
1046   g_assert (type == GST_TYPE_OMX);
1047   return TRUE;
1048 }
1049
1050 static void
1051 interface_init (GstImplementsInterfaceClass * klass)
1052 {
1053   klass->supported = interface_supported;
1054 }
1055
1056 static void
1057 init_interfaces (GType type)
1058 {
1059   GInterfaceInfo *iface_info;
1060   GInterfaceInfo *omx_info;
1061
1062
1063   iface_info = g_new0 (GInterfaceInfo, 1);
1064   iface_info->interface_init = (GInterfaceInitFunc) interface_init;
1065
1066   g_type_add_interface_static (type, GST_TYPE_IMPLEMENTS_INTERFACE, iface_info);
1067   g_free (iface_info);
1068
1069   omx_info = g_new0 (GInterfaceInfo, 1);
1070   omx_info->interface_init = (GInterfaceInitFunc) omx_interface_init;
1071
1072   g_type_add_interface_static (type, GST_TYPE_OMX, omx_info);
1073   g_free (omx_info);
1074 }