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