Initialize Tizen 2.3
[framework/multimedia/gst-openmax.git] / mobile / omx / gstomx_base_sink.c
1 /*
2  * Copyright (C) 2007-2009 Nokia Corporation.
3  * Copyright (C) 2008 NXP.
4  *
5  * Authors:
6  * Felipe Contreras <felipe.contreras@nokia.com>
7  * Frederik Vernelen <frederik.vernelen@tass.be>
8  *
9  * This library is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU Lesser General Public
11  * License as published by the Free Software Foundation
12  * version 2.1 of the License.
13  *
14  * This library is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17  * Lesser General Public License for more details.
18  *
19  * You should have received a copy of the GNU Lesser General Public
20  * License along with this library; if not, write to the Free Software
21  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
22  *
23  */
24
25 #include "gstomx_base_sink.h"
26 #include "gstomx.h"
27 #include "gstomx_interface.h"
28
29 #include <string.h>             /* for memcpy */
30
31 enum
32 {
33   ARG_NUM_INPUT_BUFFERS = GSTOMX_NUM_COMMON_PROP,
34 };
35
36 static inline gboolean omx_init (GstOmxBaseSink * self);
37
38 static void init_interfaces (GType type);
39 GSTOMX_BOILERPLATE_FULL (GstOmxBaseSink, gst_omx_base_sink, GstBaseSink,
40     GST_TYPE_BASE_SINK, init_interfaces);
41
42 static void
43 setup_ports (GstOmxBaseSink * self)
44 {
45   /* Input port configuration. */
46   g_omx_port_setup (self->in_port);
47   gst_pad_set_element_private (self->sinkpad, self->in_port);
48 }
49
50 static GstStateChangeReturn
51 change_state (GstElement * element, GstStateChange transition)
52 {
53   GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
54   GstOmxBaseSink *self;
55
56   self = GST_OMX_BASE_SINK (element);
57
58   GST_LOG_OBJECT (self, "begin");
59
60   GST_INFO_OBJECT (self, "changing state %s - %s",
61       gst_element_state_get_name (GST_STATE_TRANSITION_CURRENT (transition)),
62       gst_element_state_get_name (GST_STATE_TRANSITION_NEXT (transition)));
63
64   switch (transition) {
65     case GST_STATE_CHANGE_NULL_TO_READY:
66       if (!self->initialized) {
67         if (!omx_init (self))
68           return GST_PAD_LINK_REFUSED;
69
70         self->initialized = TRUE;
71       }
72
73       g_omx_core_prepare (self->gomx);
74       break;
75
76     case GST_STATE_CHANGE_READY_TO_PAUSED:
77       g_omx_core_start (self->gomx);
78       break;
79
80     case GST_STATE_CHANGE_PAUSED_TO_READY:
81       g_omx_port_finish (self->in_port);
82       break;
83
84     default:
85       break;
86   }
87
88   ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
89
90   if (ret == GST_STATE_CHANGE_FAILURE)
91     goto leave;
92
93   switch (transition) {
94     case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
95       g_omx_port_pause (self->in_port);
96       break;
97
98     case GST_STATE_CHANGE_PAUSED_TO_READY:
99       g_omx_core_stop (self->gomx);
100       break;
101
102     case GST_STATE_CHANGE_READY_TO_NULL:
103       g_omx_core_unload (self->gomx);
104       break;
105
106     default:
107       break;
108   }
109
110 leave:
111   GST_LOG_OBJECT (self, "end");
112
113   return ret;
114 }
115
116 static void
117 finalize (GObject * obj)
118 {
119   GstOmxBaseSink *self;
120
121   self = GST_OMX_BASE_SINK (obj);
122
123   g_omx_core_free (self->gomx);
124
125   G_OBJECT_CLASS (parent_class)->finalize (obj);
126 }
127
128 static GstFlowReturn
129 render (GstBaseSink * gst_base, GstBuffer * buf)
130 {
131   GOmxCore *gomx;
132   GOmxPort *in_port;
133   GstOmxBaseSink *self;
134   GstFlowReturn ret = GST_FLOW_OK;
135
136   self = GST_OMX_BASE_SINK (gst_base);
137
138   gomx = self->gomx;
139
140   GST_LOG_OBJECT (self, "begin");
141   GST_LOG_OBJECT (self, "gst_buffer: size=%u", GST_BUFFER_SIZE (buf));
142
143   GST_LOG_OBJECT (self, "state: %d", gomx->omx_state);
144
145   in_port = self->in_port;
146
147   if (G_LIKELY (in_port->enabled)) {
148     guint buffer_offset = 0;
149
150     while (G_LIKELY (buffer_offset < GST_BUFFER_SIZE (buf))) {
151       OMX_BUFFERHEADERTYPE *omx_buffer;
152
153       GST_LOG_OBJECT (self, "request_buffer");
154       omx_buffer = g_omx_port_request_buffer (in_port);
155
156       if (G_LIKELY (omx_buffer)) {
157         GST_DEBUG_OBJECT (self,
158             "omx_buffer: size=%lu, len=%lu, flags=%lu, offset=%lu, timestamp=%lld",
159             omx_buffer->nAllocLen, omx_buffer->nFilledLen, omx_buffer->nFlags,
160             omx_buffer->nOffset, omx_buffer->nTimeStamp);
161
162         if (omx_buffer->nOffset == 0 && self->in_port->shared_buffer) {
163           {
164             GstBuffer *old_buf;
165             old_buf = omx_buffer->pAppPrivate;
166
167             if (old_buf) {
168               gst_buffer_unref (old_buf);
169             } else if (omx_buffer->pBuffer) {
170               g_free (omx_buffer->pBuffer);
171               omx_buffer->pBuffer = NULL;
172             }
173           }
174
175           /* We are going to use this. */
176           gst_buffer_ref (buf);
177
178           omx_buffer->pBuffer = GST_BUFFER_DATA (buf);
179           omx_buffer->nAllocLen = GST_BUFFER_SIZE (buf);
180           omx_buffer->nFilledLen = GST_BUFFER_SIZE (buf);
181           omx_buffer->pAppPrivate = buf;
182         } else {
183           omx_buffer->nFilledLen = MIN (GST_BUFFER_SIZE (buf) - buffer_offset,
184               omx_buffer->nAllocLen - omx_buffer->nOffset);
185           memcpy (omx_buffer->pBuffer + omx_buffer->nOffset,
186               GST_BUFFER_DATA (buf) + buffer_offset, omx_buffer->nFilledLen);
187         }
188
189         GST_LOG_OBJECT (self, "release_buffer");
190         g_omx_port_release_buffer (in_port, omx_buffer);
191
192         buffer_offset += omx_buffer->nFilledLen;
193       } else {
194         GST_WARNING_OBJECT (self, "null buffer");
195         ret = GST_FLOW_UNEXPECTED;
196         break;
197       }
198     }
199   } else {
200     GST_WARNING_OBJECT (self, "done");
201     ret = GST_FLOW_UNEXPECTED;
202   }
203
204   GST_LOG_OBJECT (self, "end");
205
206   return ret;
207 }
208
209 static gboolean
210 handle_event (GstBaseSink * gst_base, GstEvent * event)
211 {
212   GstOmxBaseSink *self;
213   GOmxCore *gomx;
214   GOmxPort *in_port;
215
216   self = GST_OMX_BASE_SINK (gst_base);
217   gomx = self->gomx;
218   in_port = self->in_port;
219
220   GST_LOG_OBJECT (self, "begin");
221
222   GST_DEBUG_OBJECT (self, "event: %s", GST_EVENT_TYPE_NAME (event));
223
224   switch (GST_EVENT_TYPE (event)) {
225     case GST_EVENT_EOS:
226       /* Close the inpurt port. */
227       g_omx_core_set_done (gomx);
228       break;
229
230     case GST_EVENT_FLUSH_START:
231       /* unlock loops */
232       g_omx_port_pause (in_port);
233
234       /* flush all buffers */
235       OMX_SendCommand (gomx->omx_handle, OMX_CommandFlush, OMX_ALL, NULL);
236       break;
237
238     case GST_EVENT_FLUSH_STOP:
239       g_sem_down (gomx->flush_sem);
240
241       g_omx_port_resume (in_port);
242       break;
243
244     default:
245       break;
246   }
247
248   GST_LOG_OBJECT (self, "end");
249
250   return TRUE;
251 }
252
253 static void
254 set_property (GObject * obj,
255     guint prop_id, const GValue * value, GParamSpec * pspec)
256 {
257   GstOmxBaseSink *self;
258
259   self = GST_OMX_BASE_SINK (obj);
260
261   switch (prop_id) {
262     case ARG_NUM_INPUT_BUFFERS:
263     {
264       OMX_PARAM_PORTDEFINITIONTYPE param;
265       OMX_HANDLETYPE omx_handle = self->gomx->omx_handle;
266       OMX_U32 nBufferCountActual;
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 = self->in_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     default:
292       G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, prop_id, pspec);
293       break;
294   }
295 }
296
297 static void
298 get_property (GObject * obj, guint prop_id, GValue * value, GParamSpec * pspec)
299 {
300   GstOmxBaseSink *self;
301
302   self = GST_OMX_BASE_SINK (obj);
303
304   if (gstomx_get_property_helper (self->gomx, prop_id, value))
305     return;
306
307   switch (prop_id) {
308     case ARG_NUM_INPUT_BUFFERS:
309     {
310       OMX_PARAM_PORTDEFINITIONTYPE param;
311       OMX_HANDLETYPE omx_handle = self->gomx->omx_handle;
312
313       if (G_UNLIKELY (!omx_handle)) {
314         GST_WARNING_OBJECT (self, "no component");
315         g_value_set_uint (value, 0);
316         break;
317       }
318
319       G_OMX_INIT_PARAM (param);
320
321       param.nPortIndex = self->in_port->port_index;
322       OMX_GetParameter (omx_handle, OMX_IndexParamPortDefinition, &param);
323
324       g_value_set_uint (value, param.nBufferCountActual);
325     }
326       break;
327     default:
328       G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, prop_id, pspec);
329       break;
330   }
331 }
332
333 static void
334 type_base_init (gpointer g_class)
335 {
336 }
337
338 static void
339 type_class_init (gpointer g_class, gpointer class_data)
340 {
341   GObjectClass *gobject_class;
342   GstBaseSinkClass *gst_base_sink_class;
343   GstElementClass *gstelement_class;
344
345   gobject_class = G_OBJECT_CLASS (g_class);
346   gst_base_sink_class = GST_BASE_SINK_CLASS (g_class);
347   gstelement_class = GST_ELEMENT_CLASS (g_class);
348
349   gobject_class->finalize = finalize;
350
351   gstelement_class->change_state = change_state;
352
353   gst_base_sink_class->event = handle_event;
354   gst_base_sink_class->preroll = render;
355   gst_base_sink_class->render = render;
356
357   /* Properties stuff */
358   {
359     gobject_class->set_property = set_property;
360     gobject_class->get_property = get_property;
361
362     gstomx_install_property_helper (gobject_class);
363
364     g_object_class_install_property (gobject_class, ARG_NUM_INPUT_BUFFERS,
365         g_param_spec_uint ("input-buffers", "Input buffers",
366             "The number of OMX input buffers",
367             1, 10, 4, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
368   }
369 }
370
371 static gboolean
372 activate_push (GstPad * pad, gboolean active)
373 {
374   gboolean result = TRUE;
375   GstOmxBaseSink *self;
376
377   self = GST_OMX_BASE_SINK (gst_pad_get_parent (pad));
378
379   if (active) {
380     GST_DEBUG_OBJECT (self, "activate");
381
382     /* we do not start the task yet if the pad is not connected */
383     if (gst_pad_is_linked (pad)) {
384             /** @todo link callback function also needed */
385       g_omx_port_resume (self->in_port);
386     }
387   } else {
388     GST_DEBUG_OBJECT (self, "deactivate");
389
390         /** @todo disable this until we properly reinitialize the buffers. */
391 #if 0
392     /* flush all buffers */
393     OMX_SendCommand (self->gomx->omx_handle, OMX_CommandFlush, OMX_ALL, NULL);
394 #endif
395
396     /* unlock loops */
397     g_omx_port_pause (self->in_port);
398   }
399
400   gst_object_unref (self);
401
402   if (result)
403     result = self->base_activatepush (pad, active);
404
405   return result;
406 }
407
408 static inline gboolean
409 omx_init (GstOmxBaseSink * self)
410 {
411   if (self->gomx->omx_error)
412     return FALSE;
413
414   setup_ports (self);
415
416   return TRUE;
417 }
418
419 static GstPadLinkReturn
420 pad_sink_link (GstPad * pad, GstPad * peer)
421 {
422   GstOmxBaseSink *self;
423
424   self = GST_OMX_BASE_SINK (GST_OBJECT_PARENT (pad));
425
426   GST_INFO_OBJECT (self, "link");
427
428   if (!self->initialized) {
429     if (!omx_init (self))
430       return GST_PAD_LINK_REFUSED;
431     self->initialized = TRUE;
432   }
433
434   return GST_PAD_LINK_OK;
435 }
436
437 static void
438 type_instance_init (GTypeInstance * instance, gpointer g_class)
439 {
440   GstOmxBaseSink *self;
441
442   self = GST_OMX_BASE_SINK (instance);
443
444   GST_LOG_OBJECT (self, "begin");
445
446   self->gomx = gstomx_core_new (self, G_TYPE_FROM_CLASS (g_class));
447   self->in_port = g_omx_core_new_port (self->gomx, 0);
448
449   {
450     GstPad *sinkpad;
451     self->sinkpad = sinkpad = GST_BASE_SINK_PAD (self);
452     self->base_activatepush = GST_PAD_ACTIVATEPUSHFUNC (sinkpad);
453     gst_pad_set_activatepush_function (sinkpad, activate_push);
454     gst_pad_set_link_function (sinkpad, pad_sink_link);
455   }
456
457   GST_LOG_OBJECT (self, "end");
458 }
459
460 static void
461 omx_interface_init (GstImplementsInterfaceClass * klass)
462 {
463 }
464
465 static gboolean
466 interface_supported (GstImplementsInterface * iface, GType type)
467 {
468   g_assert (type == GST_TYPE_OMX);
469   return TRUE;
470 }
471
472 static void
473 interface_init (GstImplementsInterfaceClass * klass)
474 {
475   klass->supported = interface_supported;
476 }
477
478 static void
479 init_interfaces (GType type)
480 {
481   GInterfaceInfo *iface_info;
482   GInterfaceInfo *omx_info;
483
484   iface_info = g_new0 (GInterfaceInfo, 1);
485   iface_info->interface_init = (GInterfaceInitFunc) interface_init;
486
487   g_type_add_interface_static (type, GST_TYPE_IMPLEMENTS_INTERFACE, iface_info);
488   g_free (iface_info);
489
490   omx_info = g_new0 (GInterfaceInfo, 1);
491   omx_info->interface_init = (GInterfaceInitFunc) omx_interface_init;
492
493   g_type_add_interface_static (type, GST_TYPE_OMX, omx_info);
494   g_free (omx_info);
495 }