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