omx: Add audio decoder base class and a subclass for MP3
authorSebastian Dröge <sebastian@centricular.com>
Sat, 10 May 2014 21:12:54 +0000 (23:12 +0200)
committerSebastian Dröge <sebastian@centricular.com>
Wed, 2 Jul 2014 07:21:00 +0000 (09:21 +0200)
omx/Makefile.am
omx/gstomx.c
omx/gstomxaudiodec.c [new file with mode: 0644]
omx/gstomxaudiodec.h [new file with mode: 0644]
omx/gstomxmp3dec.c [new file with mode: 0644]
omx/gstomxmp3dec.h [new file with mode: 0644]

index 5d90520..203d28f 100644 (file)
@@ -16,6 +16,7 @@ libgstomx_la_SOURCES = \
        gstomxvideo.c \
        gstomxvideodec.c \
        gstomxvideoenc.c \
+       gstomxaudiodec.c \
        gstomxaudioenc.c \
        gstomxmjpegdec.c \
        gstomxmpeg4videodec.c \
@@ -28,6 +29,7 @@ libgstomx_la_SOURCES = \
        gstomxmpeg4videoenc.c \
        gstomxh264enc.c \
        gstomxh263enc.c \
+       gstomxmp3dec.c \
        gstomxaacenc.c \
        gstomxaudiosink.c \
        gstomxanalogaudiosink.c \
@@ -38,6 +40,7 @@ noinst_HEADERS = \
        gstomxvideo.h \
        gstomxvideodec.h \
        gstomxvideoenc.h \
+       gstomxaudiodec.h \
        gstomxaudioenc.h \
        gstomxmjpegdec.h \
        gstomxmpeg2videodec.h \
@@ -50,6 +53,7 @@ noinst_HEADERS = \
        gstomxmpeg4videoenc.h \
        gstomxh264enc.h \
        gstomxh263enc.h \
+       gstomxmp3dec.h \
        gstomxaacenc.h \
        gstomxaudiosink.h \
        gstomxanalogaudiosink.h \
index 7766eab..d611de3 100644 (file)
@@ -39,6 +39,7 @@
 #include "gstomxmpeg4videoenc.h"
 #include "gstomxh264enc.h"
 #include "gstomxh263enc.h"
+#include "gstomxmp3dec.h"
 #include "gstomxaacenc.h"
 #include "gstomxanalogaudiosink.h"
 #include "gstomxhdmiaudiosink.h"
@@ -2244,7 +2245,8 @@ static const GGetTypeFunction types[] = {
   gst_omx_h264_dec_get_type, gst_omx_h263_dec_get_type,
   gst_omx_wmv_dec_get_type, gst_omx_mpeg4_video_enc_get_type,
   gst_omx_h264_enc_get_type, gst_omx_h263_enc_get_type,
-  gst_omx_aac_enc_get_type, gst_omx_mjpeg_dec_get_type
+  gst_omx_aac_enc_get_type, gst_omx_mjpeg_dec_get_type,
+  gst_omx_mp3_dec_get_type
 #ifdef HAVE_VP8
       , gst_omx_vp8_dec_get_type
 #endif
@@ -2263,6 +2265,7 @@ static const struct TypeOffest base_types[] = {
   {gst_omx_audio_sink_get_type, G_STRUCT_OFFSET (GstOMXAudioSinkClass, cdata)},
   {gst_omx_video_dec_get_type, G_STRUCT_OFFSET (GstOMXVideoDecClass, cdata)},
   {gst_omx_video_enc_get_type, G_STRUCT_OFFSET (GstOMXVideoEncClass, cdata)},
+  {gst_omx_audio_dec_get_type, G_STRUCT_OFFSET (GstOMXAudioDecClass, cdata)},
   {gst_omx_audio_enc_get_type, G_STRUCT_OFFSET (GstOMXAudioEncClass, cdata)},
 };
 
diff --git a/omx/gstomxaudiodec.c b/omx/gstomxaudiodec.c
new file mode 100644 (file)
index 0000000..d155017
--- /dev/null
@@ -0,0 +1,1299 @@
+/*
+ * Copyright (C) 2011, Hewlett-Packard Development Company, L.P.
+ *   Author: Sebastian Dröge <sebastian.droege@collabora.co.uk>, Collabora Ltd.
+ * Copyright (C) 2013, Collabora Ltd.
+ *   Author: Sebastian Dröge <sebastian.droege@collabora.co.uk>
+ * Copyright (C) 2014, Sebastian Dröge <sebastian@centricular.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation
+ * version 2.1 of the License.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <gst/gst.h>
+
+#include <string.h>
+
+#include "gstomxaudiodec.h"
+
+GST_DEBUG_CATEGORY_STATIC (gst_omx_audio_dec_debug_category);
+#define GST_CAT_DEFAULT gst_omx_audio_dec_debug_category
+
+/* prototypes */
+static void gst_omx_audio_dec_finalize (GObject * object);
+
+static GstStateChangeReturn
+gst_omx_audio_dec_change_state (GstElement * element,
+    GstStateChange transition);
+
+static gboolean gst_omx_audio_dec_open (GstAudioDecoder * decoder);
+static gboolean gst_omx_audio_dec_close (GstAudioDecoder * decoder);
+static gboolean gst_omx_audio_dec_start (GstAudioDecoder * decoder);
+static gboolean gst_omx_audio_dec_stop (GstAudioDecoder * decoder);
+static gboolean gst_omx_audio_dec_set_format (GstAudioDecoder * decoder,
+    GstCaps * caps);
+static void gst_omx_audio_dec_flush (GstAudioDecoder * decoder, gboolean hard);
+static GstFlowReturn gst_omx_audio_dec_handle_frame (GstAudioDecoder * decoder,
+    GstBuffer * buffer);
+static GstFlowReturn gst_omx_audio_dec_drain (GstOMXAudioDec * self,
+    gboolean is_eos);
+
+enum
+{
+  PROP_0
+};
+
+/* class initialization */
+
+#define DEBUG_INIT \
+  GST_DEBUG_CATEGORY_INIT (gst_omx_audio_dec_debug_category, "omxaudiodec", 0, \
+      "debug category for gst-omx audio decoder base class");
+
+
+G_DEFINE_ABSTRACT_TYPE_WITH_CODE (GstOMXAudioDec, gst_omx_audio_dec,
+    GST_TYPE_AUDIO_DECODER, DEBUG_INIT);
+
+static void
+gst_omx_audio_dec_class_init (GstOMXAudioDecClass * klass)
+{
+  GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+  GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
+  GstAudioDecoderClass *audio_decoder_class = GST_AUDIO_DECODER_CLASS (klass);
+
+  gobject_class->finalize = gst_omx_audio_dec_finalize;
+
+  element_class->change_state =
+      GST_DEBUG_FUNCPTR (gst_omx_audio_dec_change_state);
+
+  audio_decoder_class->open = GST_DEBUG_FUNCPTR (gst_omx_audio_dec_open);
+  audio_decoder_class->close = GST_DEBUG_FUNCPTR (gst_omx_audio_dec_close);
+  audio_decoder_class->start = GST_DEBUG_FUNCPTR (gst_omx_audio_dec_start);
+  audio_decoder_class->stop = GST_DEBUG_FUNCPTR (gst_omx_audio_dec_stop);
+  audio_decoder_class->flush = GST_DEBUG_FUNCPTR (gst_omx_audio_dec_flush);
+  audio_decoder_class->set_format =
+      GST_DEBUG_FUNCPTR (gst_omx_audio_dec_set_format);
+  audio_decoder_class->handle_frame =
+      GST_DEBUG_FUNCPTR (gst_omx_audio_dec_handle_frame);
+
+  klass->cdata.type = GST_OMX_COMPONENT_TYPE_FILTER;
+  klass->cdata.default_src_template_caps =
+      "audio/x-raw, "
+      "rate = (int) [ 1, MAX ], "
+      "channels = (int) [ 1, " G_STRINGIFY (OMX_AUDIO_MAXCHANNELS) " ], "
+      "format = (string) " GST_AUDIO_FORMATS_ALL;
+}
+
+static void
+gst_omx_audio_dec_init (GstOMXAudioDec * self)
+{
+  gst_audio_decoder_set_needs_format (GST_AUDIO_DECODER (self), TRUE);
+  gst_audio_decoder_set_drainable (GST_AUDIO_DECODER (self), TRUE);
+
+  g_mutex_init (&self->drain_lock);
+  g_cond_init (&self->drain_cond);
+}
+
+static gboolean
+gst_omx_audio_dec_open (GstAudioDecoder * decoder)
+{
+  GstOMXAudioDec *self = GST_OMX_AUDIO_DEC (decoder);
+  GstOMXAudioDecClass *klass = GST_OMX_AUDIO_DEC_GET_CLASS (self);
+  gint in_port_index, out_port_index;
+
+  GST_DEBUG_OBJECT (self, "Opening decoder");
+
+  self->dec =
+      gst_omx_component_new (GST_OBJECT_CAST (self), klass->cdata.core_name,
+      klass->cdata.component_name, klass->cdata.component_role,
+      klass->cdata.hacks);
+  self->started = FALSE;
+
+  if (!self->dec)
+    return FALSE;
+
+  if (gst_omx_component_get_state (self->dec,
+          GST_CLOCK_TIME_NONE) != OMX_StateLoaded)
+    return FALSE;
+
+  in_port_index = klass->cdata.in_port_index;
+  out_port_index = klass->cdata.out_port_index;
+
+  if (in_port_index == -1 || out_port_index == -1) {
+    OMX_PORT_PARAM_TYPE param;
+    OMX_ERRORTYPE err;
+
+    GST_OMX_INIT_STRUCT (&param);
+
+    err =
+        gst_omx_component_get_parameter (self->dec, OMX_IndexParamAudioInit,
+        &param);
+    if (err != OMX_ErrorNone) {
+      GST_WARNING_OBJECT (self, "Couldn't get port information: %s (0x%08x)",
+          gst_omx_error_to_string (err), err);
+      /* Fallback */
+      in_port_index = 0;
+      out_port_index = 1;
+    } else {
+      GST_DEBUG_OBJECT (self, "Detected %u ports, starting at %u",
+          (guint) param.nPorts, (guint) param.nStartPortNumber);
+      in_port_index = param.nStartPortNumber + 0;
+      out_port_index = param.nStartPortNumber + 1;
+    }
+  }
+  self->dec_in_port = gst_omx_component_add_port (self->dec, in_port_index);
+  self->dec_out_port = gst_omx_component_add_port (self->dec, out_port_index);
+
+  if (!self->dec_in_port || !self->dec_out_port)
+    return FALSE;
+
+  GST_DEBUG_OBJECT (self, "Opened decoder");
+
+  return TRUE;
+}
+
+static gboolean
+gst_omx_audio_dec_shutdown (GstOMXAudioDec * self)
+{
+  OMX_STATETYPE state;
+
+  GST_DEBUG_OBJECT (self, "Shutting down decoder");
+
+  state = gst_omx_component_get_state (self->dec, 0);
+  if (state > OMX_StateLoaded || state == OMX_StateInvalid) {
+    if (state > OMX_StateIdle) {
+      gst_omx_component_set_state (self->dec, OMX_StateIdle);
+      gst_omx_component_get_state (self->dec, 5 * GST_SECOND);
+    }
+    gst_omx_component_set_state (self->dec, OMX_StateLoaded);
+    gst_omx_port_deallocate_buffers (self->dec_in_port);
+    gst_omx_port_deallocate_buffers (self->dec_out_port);
+    if (state > OMX_StateLoaded)
+      gst_omx_component_get_state (self->dec, 5 * GST_SECOND);
+  }
+
+  return TRUE;
+}
+
+static gboolean
+gst_omx_audio_dec_close (GstAudioDecoder * decoder)
+{
+  GstOMXAudioDec *self = GST_OMX_AUDIO_DEC (decoder);
+
+  GST_DEBUG_OBJECT (self, "Closing decoder");
+
+  if (!gst_omx_audio_dec_shutdown (self))
+    return FALSE;
+
+  self->dec_in_port = NULL;
+  self->dec_out_port = NULL;
+  if (self->dec)
+    gst_omx_component_free (self->dec);
+  self->dec = NULL;
+
+  self->started = FALSE;
+
+  GST_DEBUG_OBJECT (self, "Closed decoder");
+
+  return TRUE;
+}
+
+static void
+gst_omx_audio_dec_finalize (GObject * object)
+{
+  GstOMXAudioDec *self = GST_OMX_AUDIO_DEC (object);
+
+  g_mutex_clear (&self->drain_lock);
+  g_cond_clear (&self->drain_cond);
+
+  G_OBJECT_CLASS (gst_omx_audio_dec_parent_class)->finalize (object);
+}
+
+static GstStateChangeReturn
+gst_omx_audio_dec_change_state (GstElement * element, GstStateChange transition)
+{
+  GstOMXAudioDec *self;
+  GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
+
+  g_return_val_if_fail (GST_IS_OMX_AUDIO_DEC (element),
+      GST_STATE_CHANGE_FAILURE);
+  self = GST_OMX_AUDIO_DEC (element);
+
+  switch (transition) {
+    case GST_STATE_CHANGE_NULL_TO_READY:
+      break;
+    case GST_STATE_CHANGE_READY_TO_PAUSED:
+      self->downstream_flow_ret = GST_FLOW_OK;
+      self->draining = FALSE;
+      self->started = FALSE;
+      break;
+    case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
+      break;
+    case GST_STATE_CHANGE_PAUSED_TO_READY:
+      if (self->dec_in_port)
+        gst_omx_port_set_flushing (self->dec_in_port, 5 * GST_SECOND, TRUE);
+      if (self->dec_out_port)
+        gst_omx_port_set_flushing (self->dec_out_port, 5 * GST_SECOND, TRUE);
+
+      g_mutex_lock (&self->drain_lock);
+      self->draining = FALSE;
+      g_cond_broadcast (&self->drain_cond);
+      g_mutex_unlock (&self->drain_lock);
+      break;
+    default:
+      break;
+  }
+
+  if (ret == GST_STATE_CHANGE_FAILURE)
+    return ret;
+
+  ret =
+      GST_ELEMENT_CLASS (gst_omx_audio_dec_parent_class)->change_state
+      (element, transition);
+
+  if (ret == GST_STATE_CHANGE_FAILURE)
+    return ret;
+
+  switch (transition) {
+    case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
+      break;
+    case GST_STATE_CHANGE_PAUSED_TO_READY:
+      self->downstream_flow_ret = GST_FLOW_FLUSHING;
+      self->started = FALSE;
+
+      if (!gst_omx_audio_dec_shutdown (self))
+        ret = GST_STATE_CHANGE_FAILURE;
+      break;
+    case GST_STATE_CHANGE_READY_TO_NULL:
+      break;
+    default:
+      break;
+  }
+
+  return ret;
+}
+
+static void
+gst_omx_audio_dec_loop (GstOMXAudioDec * self)
+{
+  GstOMXPort *port = self->dec_out_port;
+  GstOMXBuffer *buf = NULL;
+  GstFlowReturn flow_ret = GST_FLOW_OK;
+  GstOMXAcquireBufferReturn acq_return;
+  OMX_ERRORTYPE err;
+
+  acq_return = gst_omx_port_acquire_buffer (port, &buf);
+  if (acq_return == GST_OMX_ACQUIRE_BUFFER_ERROR) {
+    goto component_error;
+  } else if (acq_return == GST_OMX_ACQUIRE_BUFFER_FLUSHING) {
+    goto flushing;
+  } else if (acq_return == GST_OMX_ACQUIRE_BUFFER_EOS) {
+    goto eos;
+  }
+
+  if (!gst_pad_has_current_caps (GST_AUDIO_DECODER_SRC_PAD (self)) ||
+      acq_return == GST_OMX_ACQUIRE_BUFFER_RECONFIGURE) {
+    OMX_PARAM_PORTDEFINITIONTYPE port_def;
+    OMX_AUDIO_PARAM_PCMMODETYPE pcm_param;
+    GstAudioChannelPosition omx_position[OMX_AUDIO_MAXCHANNELS];
+    gint i;
+
+    GST_DEBUG_OBJECT (self, "Port settings have changed, updating caps");
+
+    /* Reallocate all buffers */
+    if (acq_return == GST_OMX_ACQUIRE_BUFFER_RECONFIGURE
+        && gst_omx_port_is_enabled (port)) {
+      err = gst_omx_port_set_enabled (port, FALSE);
+      if (err != OMX_ErrorNone)
+        goto reconfigure_error;
+
+      err = gst_omx_port_wait_buffers_released (port, 5 * GST_SECOND);
+      if (err != OMX_ErrorNone)
+        goto reconfigure_error;
+
+      err = gst_omx_port_deallocate_buffers (port);
+      if (err != OMX_ErrorNone)
+        goto reconfigure_error;
+
+      err = gst_omx_port_wait_enabled (port, 1 * GST_SECOND);
+      if (err != OMX_ErrorNone)
+        goto reconfigure_error;
+    }
+
+    /* Just update caps */
+    GST_AUDIO_DECODER_STREAM_LOCK (self);
+
+    gst_omx_port_get_port_definition (port, &port_def);
+    g_assert (port_def.format.audio.eEncoding == OMX_AUDIO_CodingPCM);
+
+    GST_OMX_INIT_STRUCT (&pcm_param);
+    pcm_param.nPortIndex = self->dec_in_port->index;
+    err =
+        gst_omx_component_get_parameter (self->dec, OMX_IndexParamAudioPcm,
+        &pcm_param);
+    if (err != OMX_ErrorNone) {
+      GST_ERROR_OBJECT (self, "Failed to get PCM parameters: %s (0x%08x)",
+          gst_omx_error_to_string (err), err);
+      goto caps_failed;
+    }
+
+    g_assert (pcm_param.ePCMMode == OMX_AUDIO_PCMModeLinear);
+    g_assert (pcm_param.bInterleaved == OMX_TRUE);
+
+    gst_audio_info_init (&self->info);
+
+    for (i = 0; i < pcm_param.nChannels; i++) {
+      switch (pcm_param.eChannelMapping[i]) {
+        case OMX_AUDIO_ChannelLF:
+          omx_position[i] = GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT;
+          break;
+        case OMX_AUDIO_ChannelRF:
+          omx_position[i] = GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT;
+          break;
+        case OMX_AUDIO_ChannelCF:
+          omx_position[i] = GST_AUDIO_CHANNEL_POSITION_FRONT_CENTER;
+          break;
+        case OMX_AUDIO_ChannelLS:
+          omx_position[i] = GST_AUDIO_CHANNEL_POSITION_SIDE_LEFT;
+          break;
+        case OMX_AUDIO_ChannelRS:
+          omx_position[i] = GST_AUDIO_CHANNEL_POSITION_SIDE_RIGHT;
+          break;
+        case OMX_AUDIO_ChannelLFE:
+          omx_position[i] = GST_AUDIO_CHANNEL_POSITION_LFE1;
+          break;
+        case OMX_AUDIO_ChannelCS:
+          omx_position[i] = GST_AUDIO_CHANNEL_POSITION_REAR_CENTER;
+          break;
+        case OMX_AUDIO_ChannelLR:
+          omx_position[i] = GST_AUDIO_CHANNEL_POSITION_REAR_LEFT;
+          break;
+        case OMX_AUDIO_ChannelRR:
+          omx_position[i] = GST_AUDIO_CHANNEL_POSITION_REAR_RIGHT;
+          break;
+        case OMX_AUDIO_ChannelNone:
+        default:
+          /* This will break the outer loop too as the
+           * i == pcm_param.nChannels afterwards */
+          for (i = 0; i < pcm_param.nChannels; i++)
+            omx_position[i] = GST_AUDIO_CHANNEL_POSITION_NONE;
+          break;
+      }
+    }
+    if (pcm_param.nChannels == 1
+        && omx_position[0] == GST_AUDIO_CHANNEL_POSITION_FRONT_CENTER)
+      omx_position[0] = GST_AUDIO_CHANNEL_POSITION_MONO;
+
+    memcpy (self->position, omx_position, sizeof (omx_position));
+    gst_audio_channel_positions_to_valid_order (self->position,
+        pcm_param.nChannels);
+    self->needs_reorder =
+        (memcmp (self->position, omx_position,
+            sizeof (GstAudioChannelPosition) * pcm_param.nChannels) != 0);
+    if (self->needs_reorder)
+      gst_audio_get_channel_reorder_map (pcm_param.nChannels, self->position,
+          omx_position, self->reorder_map);
+
+    gst_audio_info_set_format (&self->info,
+        gst_audio_format_build_integer (pcm_param.eNumData ==
+            OMX_NumericalDataSigned,
+            pcm_param.eEndian ==
+            OMX_EndianLittle ? G_LITTLE_ENDIAN : G_BIG_ENDIAN,
+            pcm_param.nBitPerSample, pcm_param.nBitPerSample),
+        pcm_param.nSamplingRate, pcm_param.nChannels, self->position);
+
+    GST_DEBUG_OBJECT (self,
+        "Setting output state: format %s, rate %u, channels %u",
+        gst_audio_format_to_string (self->info.finfo->format),
+        (guint) pcm_param.nSamplingRate, (guint) pcm_param.nChannels);
+
+    if (!gst_audio_decoder_set_output_format (GST_AUDIO_DECODER (self),
+            &self->info)
+        || !gst_audio_decoder_negotiate (GST_AUDIO_DECODER (self))) {
+      if (buf)
+        gst_omx_port_release_buffer (port, buf);
+      goto caps_failed;
+    }
+
+    GST_AUDIO_DECODER_STREAM_UNLOCK (self);
+
+    if (acq_return == GST_OMX_ACQUIRE_BUFFER_RECONFIGURE) {
+      err = gst_omx_port_set_enabled (port, TRUE);
+      if (err != OMX_ErrorNone)
+        goto reconfigure_error;
+
+      err = gst_omx_port_allocate_buffers (port);
+      if (err != OMX_ErrorNone)
+        goto reconfigure_error;
+
+      err = gst_omx_port_wait_enabled (port, 5 * GST_SECOND);
+      if (err != OMX_ErrorNone)
+        goto reconfigure_error;
+
+      err = gst_omx_port_populate (port);
+      if (err != OMX_ErrorNone)
+        goto reconfigure_error;
+
+      err = gst_omx_port_mark_reconfigured (port);
+      if (err != OMX_ErrorNone)
+        goto reconfigure_error;
+    }
+
+    /* Now get a buffer */
+    if (acq_return != GST_OMX_ACQUIRE_BUFFER_OK) {
+      return;
+    }
+  }
+
+  g_assert (acq_return == GST_OMX_ACQUIRE_BUFFER_OK);
+  if (!buf) {
+    g_assert ((klass->cdata.hacks & GST_OMX_HACK_NO_EMPTY_EOS_BUFFER));
+    GST_AUDIO_DECODER_STREAM_LOCK (self);
+    goto eos;
+  }
+
+  /* This prevents a deadlock between the srcpad stream
+   * lock and the audiocodec stream lock, if ::reset()
+   * is called at the wrong time
+   */
+  if (gst_omx_port_is_flushing (port)) {
+    GST_DEBUG_OBJECT (self, "Flushing");
+    gst_omx_port_release_buffer (port, buf);
+    goto flushing;
+  }
+
+  GST_DEBUG_OBJECT (self, "Handling buffer: 0x%08x %" G_GUINT64_FORMAT,
+      (guint) buf->omx_buf->nFlags, (guint64) buf->omx_buf->nTimeStamp);
+
+  GST_AUDIO_DECODER_STREAM_LOCK (self);
+
+  if (buf->omx_buf->nFilledLen > 0) {
+    GstBuffer *outbuf;
+    gint nframes, spf;
+    GstMapInfo minfo;
+    GstOMXAudioDecClass *klass = GST_OMX_AUDIO_DEC_GET_CLASS (self);
+
+    GST_DEBUG_OBJECT (self, "Handling output data");
+
+    if (buf->omx_buf->nFilledLen % self->info.bpf != 0) {
+      gst_omx_port_release_buffer (port, buf);
+      goto invalid_buffer;
+    }
+
+    outbuf =
+        gst_audio_decoder_allocate_output_buffer (GST_AUDIO_DECODER (self),
+        buf->omx_buf->nFilledLen);
+
+    gst_buffer_map (outbuf, &minfo, GST_MAP_WRITE);
+    if (self->needs_reorder) {
+      gint i, n_samples, c, n_channels;
+      gint *reorder_map = self->reorder_map;
+      gint16 *dest, *source;
+
+      dest = (gint16 *) minfo.data;
+      source = (gint16 *) (buf->omx_buf->pBuffer + buf->omx_buf->nOffset);
+      n_samples = buf->omx_buf->nFilledLen / self->info.bpf;
+      n_channels = self->info.channels;
+
+      for (i = 0; i < n_samples; i++) {
+        for (c = 0; c < n_channels; c++) {
+          dest[i * n_channels + reorder_map[c]] = source[i * n_channels + c];
+        }
+      }
+    } else {
+      memcpy (minfo.data, buf->omx_buf->pBuffer + buf->omx_buf->nOffset,
+          buf->omx_buf->nFilledLen);
+    }
+    gst_buffer_unmap (outbuf, &minfo);
+
+    nframes = 1;
+    spf = klass->get_samples_per_frame (self, self->dec_out_port);
+    if (spf != -1) {
+      nframes = buf->omx_buf->nFilledLen / self->info.bpf;
+      if (nframes % spf != 0)
+        GST_WARNING_OBJECT (self, "Output buffer does not contain an integer "
+            "number of input frames (frames: %d, spf: %d)", nframes, spf);
+      nframes = (nframes + spf - 1) / spf;
+    }
+
+    GST_BUFFER_TIMESTAMP (outbuf) =
+        gst_util_uint64_scale (buf->omx_buf->nTimeStamp, GST_SECOND,
+        OMX_TICKS_PER_SECOND);
+    if (buf->omx_buf->nTickCount != 0)
+      GST_BUFFER_DURATION (outbuf) =
+          gst_util_uint64_scale (buf->omx_buf->nTickCount, GST_SECOND,
+          OMX_TICKS_PER_SECOND);
+
+    flow_ret =
+        gst_audio_decoder_finish_frame (GST_AUDIO_DECODER (self), outbuf,
+        nframes);
+  }
+
+  GST_DEBUG_OBJECT (self, "Read frame from component");
+
+  GST_DEBUG_OBJECT (self, "Finished frame: %s", gst_flow_get_name (flow_ret));
+
+  if (buf) {
+    err = gst_omx_port_release_buffer (port, buf);
+    if (err != OMX_ErrorNone)
+      goto release_error;
+  }
+
+  self->downstream_flow_ret = flow_ret;
+
+  if (flow_ret != GST_FLOW_OK)
+    goto flow_error;
+
+  GST_AUDIO_DECODER_STREAM_UNLOCK (self);
+
+  return;
+
+component_error:
+  {
+    GST_ELEMENT_ERROR (self, LIBRARY, FAILED, (NULL),
+        ("OpenMAX component in error state %s (0x%08x)",
+            gst_omx_component_get_last_error_string (self->dec),
+            gst_omx_component_get_last_error (self->dec)));
+    gst_pad_push_event (GST_AUDIO_DECODER_SRC_PAD (self), gst_event_new_eos ());
+    gst_pad_pause_task (GST_AUDIO_DECODER_SRC_PAD (self));
+    self->downstream_flow_ret = GST_FLOW_ERROR;
+    self->started = FALSE;
+    return;
+  }
+
+flushing:
+  {
+    GST_DEBUG_OBJECT (self, "Flushing -- stopping task");
+    gst_pad_pause_task (GST_AUDIO_DECODER_SRC_PAD (self));
+    self->downstream_flow_ret = GST_FLOW_FLUSHING;
+    self->started = FALSE;
+    return;
+  }
+
+eos:
+  {
+    g_mutex_lock (&self->drain_lock);
+    if (self->draining) {
+      GST_DEBUG_OBJECT (self, "Drained");
+      self->draining = FALSE;
+      g_cond_broadcast (&self->drain_cond);
+      flow_ret = GST_FLOW_OK;
+      gst_pad_pause_task (GST_AUDIO_DECODER_SRC_PAD (self));
+    } else {
+      GST_DEBUG_OBJECT (self, "Component signalled EOS");
+      flow_ret = GST_FLOW_EOS;
+    }
+    g_mutex_unlock (&self->drain_lock);
+
+    GST_AUDIO_DECODER_STREAM_LOCK (self);
+    self->downstream_flow_ret = flow_ret;
+
+    /* Here we fallback and pause the task for the EOS case */
+    if (flow_ret != GST_FLOW_OK)
+      goto flow_error;
+
+    GST_AUDIO_DECODER_STREAM_UNLOCK (self);
+
+    return;
+  }
+
+flow_error:
+  {
+    if (flow_ret == GST_FLOW_EOS) {
+      GST_DEBUG_OBJECT (self, "EOS");
+
+      gst_pad_push_event (GST_AUDIO_DECODER_SRC_PAD (self),
+          gst_event_new_eos ());
+      gst_pad_pause_task (GST_AUDIO_DECODER_SRC_PAD (self));
+      self->started = FALSE;
+    } else if (flow_ret < GST_FLOW_EOS) {
+      GST_ELEMENT_ERROR (self, STREAM, FAILED,
+          ("Internal data stream error."), ("stream stopped, reason %s",
+              gst_flow_get_name (flow_ret)));
+
+      gst_pad_push_event (GST_AUDIO_DECODER_SRC_PAD (self),
+          gst_event_new_eos ());
+      gst_pad_pause_task (GST_AUDIO_DECODER_SRC_PAD (self));
+      self->started = FALSE;
+    } else if (flow_ret == GST_FLOW_FLUSHING) {
+      GST_DEBUG_OBJECT (self, "Flushing -- stopping task");
+      gst_pad_pause_task (GST_AUDIO_DECODER_SRC_PAD (self));
+      self->started = FALSE;
+    }
+    GST_AUDIO_DECODER_STREAM_UNLOCK (self);
+    return;
+  }
+
+reconfigure_error:
+  {
+    GST_ELEMENT_ERROR (self, LIBRARY, SETTINGS, (NULL),
+        ("Unable to reconfigure output port"));
+    gst_pad_push_event (GST_AUDIO_DECODER_SRC_PAD (self), gst_event_new_eos ());
+    gst_pad_pause_task (GST_AUDIO_DECODER_SRC_PAD (self));
+    self->downstream_flow_ret = GST_FLOW_ERROR;
+    self->started = FALSE;
+    return;
+  }
+
+invalid_buffer:
+  {
+    GST_ELEMENT_ERROR (self, LIBRARY, SETTINGS, (NULL),
+        ("Invalid sized input buffer"));
+    gst_pad_push_event (GST_AUDIO_DECODER_SRC_PAD (self), gst_event_new_eos ());
+    gst_pad_pause_task (GST_AUDIO_DECODER_SRC_PAD (self));
+    self->downstream_flow_ret = GST_FLOW_NOT_NEGOTIATED;
+    self->started = FALSE;
+    GST_AUDIO_DECODER_STREAM_UNLOCK (self);
+    return;
+  }
+
+caps_failed:
+  {
+    GST_ELEMENT_ERROR (self, LIBRARY, SETTINGS, (NULL), ("Failed to set caps"));
+    gst_pad_push_event (GST_AUDIO_DECODER_SRC_PAD (self), gst_event_new_eos ());
+    gst_pad_pause_task (GST_AUDIO_DECODER_SRC_PAD (self));
+    GST_AUDIO_DECODER_STREAM_UNLOCK (self);
+    self->downstream_flow_ret = GST_FLOW_NOT_NEGOTIATED;
+    self->started = FALSE;
+    return;
+  }
+release_error:
+  {
+    GST_ELEMENT_ERROR (self, LIBRARY, SETTINGS, (NULL),
+        ("Failed to relase output buffer to component: %s (0x%08x)",
+            gst_omx_error_to_string (err), err));
+    gst_pad_push_event (GST_AUDIO_DECODER_SRC_PAD (self), gst_event_new_eos ());
+    gst_pad_pause_task (GST_AUDIO_DECODER_SRC_PAD (self));
+    self->downstream_flow_ret = GST_FLOW_ERROR;
+    self->started = FALSE;
+    GST_AUDIO_DECODER_STREAM_UNLOCK (self);
+    return;
+  }
+}
+
+static gboolean
+gst_omx_audio_dec_start (GstAudioDecoder * decoder)
+{
+  GstOMXAudioDec *self;
+
+  self = GST_OMX_AUDIO_DEC (decoder);
+
+  self->last_upstream_ts = 0;
+  self->eos = FALSE;
+  self->downstream_flow_ret = GST_FLOW_OK;
+
+  return TRUE;
+}
+
+static gboolean
+gst_omx_audio_dec_stop (GstAudioDecoder * decoder)
+{
+  GstOMXAudioDec *self;
+
+  self = GST_OMX_AUDIO_DEC (decoder);
+
+  GST_DEBUG_OBJECT (self, "Stopping decoder");
+
+  gst_omx_port_set_flushing (self->dec_in_port, 5 * GST_SECOND, TRUE);
+  gst_omx_port_set_flushing (self->dec_out_port, 5 * GST_SECOND, TRUE);
+
+  gst_pad_stop_task (GST_AUDIO_DECODER_SRC_PAD (decoder));
+
+  if (gst_omx_component_get_state (self->dec, 0) > OMX_StateIdle)
+    gst_omx_component_set_state (self->dec, OMX_StateIdle);
+
+  self->downstream_flow_ret = GST_FLOW_FLUSHING;
+  self->started = FALSE;
+  self->eos = FALSE;
+
+  g_mutex_lock (&self->drain_lock);
+  self->draining = FALSE;
+  g_cond_broadcast (&self->drain_cond);
+  g_mutex_unlock (&self->drain_lock);
+
+  gst_omx_component_get_state (self->dec, 5 * GST_SECOND);
+
+  gst_buffer_replace (&self->codec_data, NULL);
+
+  GST_DEBUG_OBJECT (self, "Stopped decoder");
+
+  return TRUE;
+}
+
+static gboolean
+gst_omx_audio_dec_set_format (GstAudioDecoder * decoder, GstCaps * caps)
+{
+  GstOMXAudioDec *self;
+  GstOMXAudioDecClass *klass;
+  GstStructure *s;
+  const GValue *codec_data;
+  gboolean is_format_change = FALSE;
+  gboolean needs_disable = FALSE;
+
+  self = GST_OMX_AUDIO_DEC (decoder);
+  klass = GST_OMX_AUDIO_DEC_GET_CLASS (decoder);
+
+  GST_DEBUG_OBJECT (self, "Setting new caps %" GST_PTR_FORMAT, caps);
+
+  /* Check if the caps change is a real format change or if only irrelevant
+   * parts of the caps have changed or nothing at all.
+   */
+  if (klass->is_format_change)
+    is_format_change = klass->is_format_change (self, self->dec_in_port, caps);
+
+  needs_disable =
+      gst_omx_component_get_state (self->dec,
+      GST_CLOCK_TIME_NONE) != OMX_StateLoaded;
+  /* If the component is not in Loaded state and a real format change happens
+   * we have to disable the port and re-allocate all buffers. If no real
+   * format change happened we can just exit here.
+   */
+  if (needs_disable && !is_format_change) {
+    GST_DEBUG_OBJECT (self,
+        "Already running and caps did not change the format");
+    return TRUE;
+  }
+
+  if (needs_disable && is_format_change) {
+    GstOMXPort *out_port = self->dec_out_port;
+
+    GST_DEBUG_OBJECT (self, "Need to disable and drain decoder");
+
+    gst_omx_audio_dec_drain (self, FALSE);
+    gst_omx_audio_dec_flush (decoder, FALSE);
+    gst_omx_port_set_flushing (out_port, 5 * GST_SECOND, TRUE);
+
+    if (klass->cdata.hacks & GST_OMX_HACK_NO_COMPONENT_RECONFIGURE) {
+      GST_AUDIO_DECODER_STREAM_UNLOCK (self);
+      gst_omx_audio_dec_stop (GST_AUDIO_DECODER (self));
+      gst_omx_audio_dec_close (GST_AUDIO_DECODER (self));
+      GST_AUDIO_DECODER_STREAM_LOCK (self);
+
+      if (!gst_omx_audio_dec_open (GST_AUDIO_DECODER (self)))
+        return FALSE;
+      needs_disable = FALSE;
+    } else {
+      if (gst_omx_port_set_enabled (self->dec_in_port, FALSE) != OMX_ErrorNone)
+        return FALSE;
+      if (gst_omx_port_set_enabled (out_port, FALSE) != OMX_ErrorNone)
+        return FALSE;
+      if (gst_omx_port_wait_buffers_released (self->dec_in_port,
+              5 * GST_SECOND) != OMX_ErrorNone)
+        return FALSE;
+      if (gst_omx_port_wait_buffers_released (out_port,
+              1 * GST_SECOND) != OMX_ErrorNone)
+        return FALSE;
+      if (gst_omx_port_deallocate_buffers (self->dec_in_port) != OMX_ErrorNone)
+        return FALSE;
+      if (gst_omx_port_deallocate_buffers (self->dec_out_port) != OMX_ErrorNone)
+        return FALSE;
+      if (gst_omx_port_wait_enabled (self->dec_in_port,
+              1 * GST_SECOND) != OMX_ErrorNone)
+        return FALSE;
+      if (gst_omx_port_wait_enabled (out_port, 1 * GST_SECOND) != OMX_ErrorNone)
+        return FALSE;
+    }
+
+    GST_DEBUG_OBJECT (self, "Decoder drained and disabled");
+  }
+
+  if (klass->set_format) {
+    if (!klass->set_format (self, self->dec_in_port, caps)) {
+      GST_ERROR_OBJECT (self, "Subclass failed to set the new format");
+      return FALSE;
+    }
+  }
+
+  GST_DEBUG_OBJECT (self, "Updating outport port definition");
+  if (gst_omx_port_update_port_definition (self->dec_out_port,
+          NULL) != OMX_ErrorNone)
+    return FALSE;
+
+  /* Get codec data from caps */
+  gst_buffer_replace (&self->codec_data, NULL);
+  s = gst_caps_get_structure (caps, 0);
+  codec_data = gst_structure_get_value (s, "codec_data");
+  if (codec_data) {
+    /* Vorbis and some other codecs have multiple buffers in
+     * the stream-header field */
+    self->codec_data = gst_value_get_buffer (codec_data);
+    if (self->codec_data)
+      gst_buffer_ref (self->codec_data);
+  }
+
+  GST_DEBUG_OBJECT (self, "Enabling component");
+
+  if (needs_disable) {
+    if (gst_omx_port_set_enabled (self->dec_in_port, TRUE) != OMX_ErrorNone)
+      return FALSE;
+    if (gst_omx_port_allocate_buffers (self->dec_in_port) != OMX_ErrorNone)
+      return FALSE;
+    if (gst_omx_port_wait_enabled (self->dec_in_port,
+            5 * GST_SECOND) != OMX_ErrorNone)
+      return FALSE;
+    if (gst_omx_port_mark_reconfigured (self->dec_in_port) != OMX_ErrorNone)
+      return FALSE;
+  } else {
+    /* Disable output port */
+    if (gst_omx_port_set_enabled (self->dec_out_port, FALSE) != OMX_ErrorNone)
+      return FALSE;
+
+    if (gst_omx_port_wait_enabled (self->dec_out_port,
+            1 * GST_SECOND) != OMX_ErrorNone)
+      return FALSE;
+
+    if (gst_omx_component_set_state (self->dec, OMX_StateIdle) != OMX_ErrorNone)
+      return FALSE;
+
+    /* Need to allocate buffers to reach Idle state */
+    if (gst_omx_port_allocate_buffers (self->dec_in_port) != OMX_ErrorNone)
+      return FALSE;
+
+    if (gst_omx_component_get_state (self->dec,
+            GST_CLOCK_TIME_NONE) != OMX_StateIdle)
+      return FALSE;
+
+    if (gst_omx_component_set_state (self->dec,
+            OMX_StateExecuting) != OMX_ErrorNone)
+      return FALSE;
+
+    if (gst_omx_component_get_state (self->dec,
+            GST_CLOCK_TIME_NONE) != OMX_StateExecuting)
+      return FALSE;
+  }
+
+  /* Unset flushing to allow ports to accept data again */
+  gst_omx_port_set_flushing (self->dec_in_port, 5 * GST_SECOND, FALSE);
+  gst_omx_port_set_flushing (self->dec_out_port, 5 * GST_SECOND, FALSE);
+
+  if (gst_omx_component_get_last_error (self->dec) != OMX_ErrorNone) {
+    GST_ERROR_OBJECT (self, "Component in error state: %s (0x%08x)",
+        gst_omx_component_get_last_error_string (self->dec),
+        gst_omx_component_get_last_error (self->dec));
+    return FALSE;
+  }
+
+  /* Start the srcpad loop again */
+  GST_DEBUG_OBJECT (self, "Starting task again");
+  self->downstream_flow_ret = GST_FLOW_OK;
+  gst_pad_start_task (GST_AUDIO_DECODER_SRC_PAD (self),
+      (GstTaskFunction) gst_omx_audio_dec_loop, decoder, NULL);
+
+  return TRUE;
+}
+
+static void
+gst_omx_audio_dec_flush (GstAudioDecoder * decoder, gboolean hard)
+{
+  GstOMXAudioDec *self = GST_OMX_AUDIO_DEC (decoder);
+
+  GST_DEBUG_OBJECT (self, "Resetting decoder");
+
+  gst_omx_port_set_flushing (self->dec_in_port, 5 * GST_SECOND, TRUE);
+  gst_omx_port_set_flushing (self->dec_out_port, 5 * GST_SECOND, TRUE);
+
+  /* Wait until the srcpad loop is finished */
+  GST_AUDIO_ENCODER_STREAM_UNLOCK (self);
+  GST_PAD_STREAM_LOCK (GST_AUDIO_ENCODER_SRC_PAD (self));
+  GST_PAD_STREAM_UNLOCK (GST_AUDIO_ENCODER_SRC_PAD (self));
+  GST_AUDIO_ENCODER_STREAM_LOCK (self);
+
+  gst_omx_port_set_flushing (self->dec_in_port, 5 * GST_SECOND, FALSE);
+  gst_omx_port_set_flushing (self->dec_out_port, 5 * GST_SECOND, FALSE);
+  gst_omx_port_populate (self->dec_out_port);
+
+  /* Start the srcpad loop again */
+  self->last_upstream_ts = 0;
+  self->downstream_flow_ret = GST_FLOW_OK;
+  self->eos = FALSE;
+  gst_pad_start_task (GST_AUDIO_DECODER_SRC_PAD (self),
+      (GstTaskFunction) gst_omx_audio_dec_loop, decoder, NULL);
+}
+
+static GstFlowReturn
+gst_omx_audio_dec_handle_frame (GstAudioDecoder * decoder, GstBuffer * inbuf)
+{
+  GstOMXAcquireBufferReturn acq_ret = GST_OMX_ACQUIRE_BUFFER_ERROR;
+  GstOMXAudioDec *self;
+  GstOMXPort *port;
+  GstOMXBuffer *buf;
+  GstBuffer *codec_data = NULL;
+  guint offset = 0;
+  GstClockTime timestamp, duration;
+  OMX_ERRORTYPE err;
+  GstMapInfo minfo;
+
+  self = GST_OMX_AUDIO_DEC (decoder);
+
+  GST_DEBUG_OBJECT (self, "Handling frame");
+
+  /* Make sure to keep a reference to the input here,
+   * it can be unreffed from the other thread if
+   * finish_frame() is called */
+  if (inbuf)
+    gst_buffer_ref (inbuf);
+
+  if (self->eos) {
+    GST_WARNING_OBJECT (self, "Got frame after EOS");
+    if (inbuf)
+      gst_buffer_unref (inbuf);
+    return GST_FLOW_EOS;
+  }
+
+  if (self->downstream_flow_ret != GST_FLOW_OK) {
+    if (inbuf)
+      gst_buffer_unref (inbuf);
+    return self->downstream_flow_ret;
+  }
+
+  if (inbuf == NULL)
+    return gst_omx_audio_dec_drain (self, TRUE);
+
+  timestamp = GST_BUFFER_TIMESTAMP (inbuf);
+  duration = GST_BUFFER_DURATION (inbuf);
+
+  port = self->dec_in_port;
+
+  gst_buffer_map (inbuf, &minfo, GST_MAP_READ);
+
+  while (offset < minfo.size) {
+    /* Make sure to release the base class stream lock, otherwise
+     * _loop() can't call _finish_frame() and we might block forever
+     * because no input buffers are released */
+    GST_AUDIO_DECODER_STREAM_UNLOCK (self);
+    acq_ret = gst_omx_port_acquire_buffer (port, &buf);
+
+    if (acq_ret == GST_OMX_ACQUIRE_BUFFER_ERROR) {
+      GST_AUDIO_DECODER_STREAM_LOCK (self);
+      goto component_error;
+    } else if (acq_ret == GST_OMX_ACQUIRE_BUFFER_FLUSHING) {
+      GST_AUDIO_DECODER_STREAM_LOCK (self);
+      goto flushing;
+    } else if (acq_ret == GST_OMX_ACQUIRE_BUFFER_RECONFIGURE) {
+      /* Reallocate all buffers */
+      err = gst_omx_port_set_enabled (port, FALSE);
+      if (err != OMX_ErrorNone) {
+        GST_AUDIO_DECODER_STREAM_LOCK (self);
+        goto reconfigure_error;
+      }
+
+      err = gst_omx_port_wait_buffers_released (port, 5 * GST_SECOND);
+      if (err != OMX_ErrorNone) {
+        GST_AUDIO_DECODER_STREAM_LOCK (self);
+        goto reconfigure_error;
+      }
+
+      err = gst_omx_port_deallocate_buffers (port);
+      if (err != OMX_ErrorNone) {
+        GST_AUDIO_DECODER_STREAM_LOCK (self);
+        goto reconfigure_error;
+      }
+
+      err = gst_omx_port_wait_enabled (port, 1 * GST_SECOND);
+      if (err != OMX_ErrorNone) {
+        GST_AUDIO_DECODER_STREAM_LOCK (self);
+        goto reconfigure_error;
+      }
+
+      err = gst_omx_port_set_enabled (port, TRUE);
+      if (err != OMX_ErrorNone) {
+        GST_AUDIO_DECODER_STREAM_LOCK (self);
+        goto reconfigure_error;
+      }
+
+      err = gst_omx_port_allocate_buffers (port);
+      if (err != OMX_ErrorNone) {
+        GST_AUDIO_DECODER_STREAM_LOCK (self);
+        goto reconfigure_error;
+      }
+
+      err = gst_omx_port_wait_enabled (port, 5 * GST_SECOND);
+      if (err != OMX_ErrorNone) {
+        GST_AUDIO_DECODER_STREAM_LOCK (self);
+        goto reconfigure_error;
+      }
+
+      err = gst_omx_port_mark_reconfigured (port);
+      if (err != OMX_ErrorNone) {
+        GST_AUDIO_DECODER_STREAM_LOCK (self);
+        goto reconfigure_error;
+      }
+
+      /* Now get a new buffer and fill it */
+      GST_AUDIO_DECODER_STREAM_LOCK (self);
+      continue;
+    }
+    GST_AUDIO_DECODER_STREAM_LOCK (self);
+
+    g_assert (acq_ret == GST_OMX_ACQUIRE_BUFFER_OK && buf != NULL);
+
+    if (buf->omx_buf->nAllocLen - buf->omx_buf->nOffset <= 0) {
+      gst_omx_port_release_buffer (port, buf);
+      goto full_buffer;
+    }
+
+    if (self->downstream_flow_ret != GST_FLOW_OK) {
+      gst_omx_port_release_buffer (port, buf);
+      goto flow_error;
+    }
+
+    if (self->codec_data) {
+      GST_DEBUG_OBJECT (self, "Passing codec data to the component");
+
+      codec_data = self->codec_data;
+
+      if (buf->omx_buf->nAllocLen - buf->omx_buf->nOffset <
+          gst_buffer_get_size (codec_data)) {
+        gst_omx_port_release_buffer (port, buf);
+        goto too_large_codec_data;
+      }
+
+      buf->omx_buf->nFlags |= OMX_BUFFERFLAG_CODECCONFIG;
+      buf->omx_buf->nFlags |= OMX_BUFFERFLAG_ENDOFFRAME;
+      buf->omx_buf->nFilledLen = gst_buffer_get_size (codec_data);
+      gst_buffer_extract (codec_data, 0,
+          buf->omx_buf->pBuffer + buf->omx_buf->nOffset,
+          buf->omx_buf->nFilledLen);
+
+      if (GST_CLOCK_TIME_IS_VALID (timestamp))
+        buf->omx_buf->nTimeStamp =
+            gst_util_uint64_scale (timestamp, OMX_TICKS_PER_SECOND, GST_SECOND);
+      else
+        buf->omx_buf->nTimeStamp = 0;
+      buf->omx_buf->nTickCount = 0;
+
+      self->started = TRUE;
+      err = gst_omx_port_release_buffer (port, buf);
+      gst_buffer_replace (&self->codec_data, NULL);
+      if (err != OMX_ErrorNone)
+        goto release_error;
+      /* Acquire new buffer for the actual frame */
+      continue;
+    }
+
+    /* Now handle the frame */
+    GST_DEBUG_OBJECT (self, "Passing frame offset %d to the component", offset);
+
+    /* Copy the buffer content in chunks of size as requested
+     * by the port */
+    buf->omx_buf->nFilledLen =
+        MIN (minfo.size - offset,
+        buf->omx_buf->nAllocLen - buf->omx_buf->nOffset);
+    gst_buffer_extract (inbuf, offset,
+        buf->omx_buf->pBuffer + buf->omx_buf->nOffset,
+        buf->omx_buf->nFilledLen);
+
+    if (timestamp != GST_CLOCK_TIME_NONE) {
+      buf->omx_buf->nTimeStamp =
+          gst_util_uint64_scale (timestamp, OMX_TICKS_PER_SECOND, GST_SECOND);
+      self->last_upstream_ts = timestamp;
+    } else {
+      buf->omx_buf->nTimeStamp = 0;
+    }
+
+    if (duration != GST_CLOCK_TIME_NONE && offset == 0) {
+      buf->omx_buf->nTickCount =
+          gst_util_uint64_scale (duration, OMX_TICKS_PER_SECOND, GST_SECOND);
+      self->last_upstream_ts += duration;
+    } else {
+      buf->omx_buf->nTickCount = 0;
+    }
+
+    if (offset == 0)
+      buf->omx_buf->nFlags |= OMX_BUFFERFLAG_SYNCFRAME;
+
+    /* TODO: Set flags
+     *   - OMX_BUFFERFLAG_DECODEONLY for buffers that are outside
+     *     the segment
+     */
+
+    offset += buf->omx_buf->nFilledLen;
+
+    if (offset == minfo.size)
+      buf->omx_buf->nFlags |= OMX_BUFFERFLAG_ENDOFFRAME;
+
+    self->started = TRUE;
+    err = gst_omx_port_release_buffer (port, buf);
+    if (err != OMX_ErrorNone)
+      goto release_error;
+  }
+
+  GST_DEBUG_OBJECT (self, "Passed frame to component");
+  if (inbuf)
+    gst_buffer_unref (inbuf);
+
+  return self->downstream_flow_ret;
+
+full_buffer:
+  {
+    if (inbuf)
+      gst_buffer_unref (inbuf);
+
+    GST_ELEMENT_ERROR (self, LIBRARY, FAILED, (NULL),
+        ("Got OpenMAX buffer with no free space (%p, %u/%u)", buf,
+            (guint) buf->omx_buf->nOffset, (guint) buf->omx_buf->nAllocLen));
+    return GST_FLOW_ERROR;
+  }
+
+flow_error:
+  {
+    if (inbuf)
+      gst_buffer_unref (inbuf);
+
+    return self->downstream_flow_ret;
+  }
+
+too_large_codec_data:
+  {
+    if (inbuf)
+      gst_buffer_unref (inbuf);
+
+    GST_ELEMENT_ERROR (self, STREAM, FORMAT, (NULL),
+        ("codec_data larger than supported by OpenMAX port "
+            "(%" G_GSIZE_FORMAT " > %u)", gst_buffer_get_size (codec_data),
+            (guint) self->dec_in_port->port_def.nBufferSize));
+    return GST_FLOW_ERROR;
+  }
+
+component_error:
+  {
+    if (inbuf)
+      gst_buffer_unref (inbuf);
+
+    GST_ELEMENT_ERROR (self, LIBRARY, FAILED, (NULL),
+        ("OpenMAX component in error state %s (0x%08x)",
+            gst_omx_component_get_last_error_string (self->dec),
+            gst_omx_component_get_last_error (self->dec)));
+    return GST_FLOW_ERROR;
+  }
+
+flushing:
+  {
+    if (inbuf)
+      gst_buffer_unref (inbuf);
+
+    GST_DEBUG_OBJECT (self, "Flushing -- returning FLUSHING");
+    return GST_FLOW_FLUSHING;
+  }
+reconfigure_error:
+  {
+    if (inbuf)
+      gst_buffer_unref (inbuf);
+
+    GST_ELEMENT_ERROR (self, LIBRARY, SETTINGS, (NULL),
+        ("Unable to reconfigure input port"));
+    return GST_FLOW_ERROR;
+  }
+release_error:
+  {
+    if (inbuf)
+      gst_buffer_unref (inbuf);
+
+    GST_ELEMENT_ERROR (self, LIBRARY, SETTINGS, (NULL),
+        ("Failed to relase input buffer to component: %s (0x%08x)",
+            gst_omx_error_to_string (err), err));
+
+    return GST_FLOW_ERROR;
+  }
+}
+
+static GstFlowReturn
+gst_omx_audio_dec_drain (GstOMXAudioDec * self, gboolean is_eos)
+{
+  GstOMXAudioDecClass *klass;
+  GstOMXBuffer *buf;
+  GstOMXAcquireBufferReturn acq_ret;
+  OMX_ERRORTYPE err;
+
+  GST_DEBUG_OBJECT (self, "Draining component");
+
+  klass = GST_OMX_AUDIO_DEC_GET_CLASS (self);
+
+  if (!self->started) {
+    GST_DEBUG_OBJECT (self, "Component not started yet");
+    return GST_FLOW_OK;
+  }
+  self->started = FALSE;
+
+  /* Don't send EOS buffer twice, this doesn't work */
+  if (self->eos) {
+    GST_DEBUG_OBJECT (self, "Component is EOS already");
+    return GST_FLOW_OK;
+  }
+  if (is_eos)
+    self->eos = TRUE;
+
+  if ((klass->cdata.hacks & GST_OMX_HACK_NO_EMPTY_EOS_BUFFER)) {
+    GST_WARNING_OBJECT (self, "Component does not support empty EOS buffers");
+    return GST_FLOW_OK;
+  }
+
+  /* Make sure to release the base class stream lock, otherwise
+   * _loop() can't call _finish_frame() and we might block forever
+   * because no input buffers are released */
+  GST_AUDIO_DECODER_STREAM_UNLOCK (self);
+
+  /* Send an EOS buffer to the component and let the base
+   * class drop the EOS event. We will send it later when
+   * the EOS buffer arrives on the output port. */
+  acq_ret = gst_omx_port_acquire_buffer (self->dec_in_port, &buf);
+  if (acq_ret != GST_OMX_ACQUIRE_BUFFER_OK) {
+    GST_AUDIO_DECODER_STREAM_LOCK (self);
+    GST_ERROR_OBJECT (self, "Failed to acquire buffer for draining: %d",
+        acq_ret);
+    return GST_FLOW_ERROR;
+  }
+
+  g_mutex_lock (&self->drain_lock);
+  self->draining = TRUE;
+  buf->omx_buf->nFilledLen = 0;
+  buf->omx_buf->nTimeStamp =
+      gst_util_uint64_scale (self->last_upstream_ts, OMX_TICKS_PER_SECOND,
+      GST_SECOND);
+  buf->omx_buf->nTickCount = 0;
+  buf->omx_buf->nFlags |= OMX_BUFFERFLAG_EOS;
+  err = gst_omx_port_release_buffer (self->dec_in_port, buf);
+  if (err != OMX_ErrorNone) {
+    GST_ERROR_OBJECT (self, "Failed to drain component: %s (0x%08x)",
+        gst_omx_error_to_string (err), err);
+    g_mutex_unlock (&self->drain_lock);
+    GST_AUDIO_DECODER_STREAM_LOCK (self);
+    return GST_FLOW_ERROR;
+  }
+
+  GST_DEBUG_OBJECT (self, "Waiting until component is drained");
+
+  if (G_UNLIKELY (self->dec->hacks & GST_OMX_HACK_DRAIN_MAY_NOT_RETURN)) {
+    gint64 wait_until = g_get_monotonic_time () + G_TIME_SPAN_SECOND / 2;
+
+    if (!g_cond_wait_until (&self->drain_cond, &self->drain_lock, wait_until))
+      GST_WARNING_OBJECT (self, "Drain timed out");
+    else
+      GST_DEBUG_OBJECT (self, "Drained component");
+
+  } else {
+    g_cond_wait (&self->drain_cond, &self->drain_lock);
+    GST_DEBUG_OBJECT (self, "Drained component");
+  }
+
+  g_mutex_unlock (&self->drain_lock);
+  GST_AUDIO_DECODER_STREAM_LOCK (self);
+
+  self->started = FALSE;
+
+  return GST_FLOW_OK;
+}
diff --git a/omx/gstomxaudiodec.h b/omx/gstomxaudiodec.h
new file mode 100644 (file)
index 0000000..e7d5a26
--- /dev/null
@@ -0,0 +1,100 @@
+/*
+ * Copyright (C) 2014, Sebastian Dröge <sebastian@centricular.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation
+ * version 2.1 of the License.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA
+ *
+ */
+
+#ifndef __GST_OMX_AUDIO_DEC_H__
+#define __GST_OMX_AUDIO_DEC_H__
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <gst/gst.h>
+#include <gst/audio/audio.h>
+#include <gst/audio/gstaudiodecoder.h>
+
+#include "gstomx.h"
+
+G_BEGIN_DECLS
+
+#define GST_TYPE_OMX_AUDIO_DEC \
+  (gst_omx_audio_dec_get_type())
+#define GST_OMX_AUDIO_DEC(obj) \
+  (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_OMX_AUDIO_DEC,GstOMXAudioDec))
+#define GST_OMX_AUDIO_DEC_CLASS(klass) \
+  (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_OMX_AUDIO_DEC,GstOMXAudioDecClass))
+#define GST_OMX_AUDIO_DEC_GET_CLASS(obj) \
+  (G_TYPE_INSTANCE_GET_CLASS((obj),GST_TYPE_OMX_AUDIO_DEC,GstOMXAudioDecClass))
+#define GST_IS_OMX_AUDIO_DEC(obj) \
+  (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_OMX_AUDIO_DEC))
+#define GST_IS_OMX_AUDIO_DEC_CLASS(obj) \
+  (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_OMX_AUDIO_DEC))
+
+typedef struct _GstOMXAudioDec GstOMXAudioDec;
+typedef struct _GstOMXAudioDecClass GstOMXAudioDecClass;
+
+struct _GstOMXAudioDec
+{
+  GstAudioDecoder parent;
+
+  /* < protected > */
+  GstOMXComponent *dec;
+  GstOMXPort *dec_in_port, *dec_out_port;
+  
+  GstBufferPool *in_port_pool, *out_port_pool;
+
+  /* < private > */
+  GstAudioInfo info;
+  GstAudioChannelPosition position[OMX_AUDIO_MAXCHANNELS];
+  gint reorder_map[OMX_AUDIO_MAXCHANNELS];
+  gboolean needs_reorder;
+  GstBuffer *codec_data;
+  /* TRUE if the component is configured and saw
+   * the first buffer */
+  gboolean started;
+
+  GstClockTime last_upstream_ts;
+
+  /* Draining state */
+  GMutex drain_lock;
+  GCond drain_cond;
+  /* TRUE if EOS buffers shouldn't be forwarded */
+  gboolean draining;
+
+  /* TRUE if upstream is EOS */
+  gboolean eos;
+
+  GstFlowReturn downstream_flow_ret;
+};
+
+struct _GstOMXAudioDecClass
+{
+  GstAudioDecoderClass parent_class;
+
+  GstOMXClassData cdata;
+
+  gboolean (*is_format_change) (GstOMXAudioDec * self, GstOMXPort * port, GstCaps * caps);
+  gboolean (*set_format)       (GstOMXAudioDec * self, GstOMXPort * port, GstCaps * caps);
+  gint     (*get_samples_per_frame) (GstOMXAudioDec * self, GstOMXPort * port);
+};
+
+GType gst_omx_audio_dec_get_type (void);
+
+G_END_DECLS
+
+#endif /* __GST_OMX_AUDIO_DEC_H__ */
diff --git a/omx/gstomxmp3dec.c b/omx/gstomxmp3dec.c
new file mode 100644 (file)
index 0000000..f52a8f5
--- /dev/null
@@ -0,0 +1,209 @@
+/*
+ * Copyright (C) 2014, Sebastian Dröge <sebastian@centricular.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation
+ * version 2.1 of the License.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <gst/gst.h>
+
+#include "gstomxmp3dec.h"
+
+GST_DEBUG_CATEGORY_STATIC (gst_omx_mp3_dec_debug_category);
+#define GST_CAT_DEFAULT gst_omx_mp3_dec_debug_category
+
+/* prototypes */
+static gboolean gst_omx_mp3_dec_set_format (GstOMXAudioDec * dec,
+    GstOMXPort * port, GstCaps * caps);
+static gboolean gst_omx_mp3_dec_is_format_change (GstOMXAudioDec * dec,
+    GstOMXPort * port, GstCaps * caps);
+static gint gst_omx_mp3_dec_get_samples_per_frame (GstOMXAudioDec * dec,
+    GstOMXPort * port);
+
+/* class initialization */
+
+#define DEBUG_INIT \
+  GST_DEBUG_CATEGORY_INIT (gst_omx_mp3_dec_debug_category, "omxmp3dec", 0, \
+      "debug category for gst-omx audio decoder base class");
+
+G_DEFINE_TYPE_WITH_CODE (GstOMXMP3Dec, gst_omx_mp3_dec,
+    GST_TYPE_OMX_AUDIO_DEC, DEBUG_INIT);
+
+
+static void
+gst_omx_mp3_dec_class_init (GstOMXMP3DecClass * klass)
+{
+  GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
+  GstOMXAudioDecClass *audiodec_class = GST_OMX_AUDIO_DEC_CLASS (klass);
+
+  audiodec_class->set_format = GST_DEBUG_FUNCPTR (gst_omx_mp3_dec_set_format);
+  audiodec_class->is_format_change =
+      GST_DEBUG_FUNCPTR (gst_omx_mp3_dec_is_format_change);
+  audiodec_class->get_samples_per_frame =
+      GST_DEBUG_FUNCPTR (gst_omx_mp3_dec_get_samples_per_frame);
+
+  audiodec_class->cdata.default_sink_template_caps = "audio/mpeg, "
+      "mpegversion=(int)1, "
+      "layer=(int)3, "
+      "mpegaudioversion=(int)[1,3], "
+      "rate=(int)[8000,48000], "
+      "channels=(int)[1,2], " "parsed=(boolean) true";
+
+  gst_element_class_set_static_metadata (element_class,
+      "OpenMAX MP3 Audio Decoder",
+      "Codec/Decoder/Audio",
+      "Decode MP3 audio streams",
+      "Sebastian Dröge <sebastian@centricular.com>");
+
+  gst_omx_set_default_role (&audiodec_class->cdata, "audio_decoder.mp3");
+}
+
+static void
+gst_omx_mp3_dec_init (GstOMXMP3Dec * self)
+{
+  self->spf = -1;
+}
+
+static gboolean
+gst_omx_mp3_dec_set_format (GstOMXAudioDec * dec, GstOMXPort * port,
+    GstCaps * caps)
+{
+  GstOMXMP3Dec *self = GST_OMX_MP3_DEC (dec);
+  OMX_PARAM_PORTDEFINITIONTYPE port_def;
+  OMX_AUDIO_PARAM_MP3TYPE mp3_param;
+  OMX_ERRORTYPE err;
+  GstStructure *s;
+  gint rate, channels, layer, mpegaudioversion;
+
+  gst_omx_port_get_port_definition (port, &port_def);
+  port_def.format.audio.eEncoding = OMX_AUDIO_CodingMP3;
+  err = gst_omx_port_update_port_definition (port, &port_def);
+  if (err != OMX_ErrorNone) {
+    GST_ERROR_OBJECT (self,
+        "Failed to set MP3 format on component: %s (0x%08x)",
+        gst_omx_error_to_string (err), err);
+    return FALSE;
+  }
+
+  GST_OMX_INIT_STRUCT (&mp3_param);
+  mp3_param.nPortIndex = port->index;
+
+  err =
+      gst_omx_component_get_parameter (dec->dec, OMX_IndexParamAudioMp3,
+      &mp3_param);
+  if (err != OMX_ErrorNone) {
+    GST_ERROR_OBJECT (self,
+        "Failed to get MP3 parameters from component: %s (0x%08x)",
+        gst_omx_error_to_string (err), err);
+    return FALSE;
+  }
+
+  s = gst_caps_get_structure (caps, 0);
+
+  if (!gst_structure_get_int (s, "mpegaudioversion", &mpegaudioversion) ||
+      !gst_structure_get_int (s, "layer", &layer) ||
+      !gst_structure_get_int (s, "rate", &rate) ||
+      !gst_structure_get_int (s, "channels", &channels)) {
+    GST_ERROR_OBJECT (self, "Incomplete caps");
+    return FALSE;
+  }
+
+  self->spf = (mpegaudioversion == 1 ? 1152 : 576);
+
+  mp3_param.nChannels = channels;
+  mp3_param.nBitRate = 0;       /* unknown */
+  mp3_param.nSampleRate = rate;
+  mp3_param.nAudioBandWidth = 0;        /* decoder decision */
+  mp3_param.eChannelMode = 0;   /* FIXME */
+  if (mpegaudioversion == 1)
+    mp3_param.eFormat = OMX_AUDIO_MP3StreamFormatMP1Layer3;
+  else if (mpegaudioversion == 2)
+    mp3_param.eFormat = OMX_AUDIO_MP3StreamFormatMP2Layer3;
+  else
+    mp3_param.eFormat = OMX_AUDIO_MP3StreamFormatMP2_5Layer3;
+
+  err =
+      gst_omx_component_set_parameter (dec->dec, OMX_IndexParamAudioMp3,
+      &mp3_param);
+  if (err != OMX_ErrorNone) {
+    GST_ERROR_OBJECT (self, "Error setting MP3 parameters: %s (0x%08x)",
+        gst_omx_error_to_string (err), err);
+    return FALSE;
+  }
+
+  return TRUE;
+}
+
+static gboolean
+gst_omx_mp3_dec_is_format_change (GstOMXAudioDec * dec, GstOMXPort * port,
+    GstCaps * caps)
+{
+  GstOMXMP3Dec *self = GST_OMX_MP3_DEC (dec);
+  OMX_AUDIO_PARAM_MP3TYPE mp3_param;
+  OMX_ERRORTYPE err;
+  GstStructure *s;
+  gint rate, channels, layer, mpegaudioversion;
+
+  GST_OMX_INIT_STRUCT (&mp3_param);
+  mp3_param.nPortIndex = port->index;
+
+  err =
+      gst_omx_component_get_parameter (dec->dec, OMX_IndexParamAudioMp3,
+      &mp3_param);
+  if (err != OMX_ErrorNone) {
+    GST_ERROR_OBJECT (self,
+        "Failed to get MP3 parameters from component: %s (0x%08x)",
+        gst_omx_error_to_string (err), err);
+    return FALSE;
+  }
+
+  s = gst_caps_get_structure (caps, 0);
+
+  if (!gst_structure_get_int (s, "mpegaudioversion", &mpegaudioversion) ||
+      !gst_structure_get_int (s, "layer", &layer) ||
+      !gst_structure_get_int (s, "rate", &rate) ||
+      !gst_structure_get_int (s, "channels", &channels)) {
+    GST_ERROR_OBJECT (self, "Incomplete caps");
+    return FALSE;
+  }
+
+  if (mp3_param.nChannels != channels)
+    return TRUE;
+
+  if (mp3_param.nSampleRate != rate)
+    return TRUE;
+
+  if (mpegaudioversion == 1
+      && mp3_param.eFormat != OMX_AUDIO_MP3StreamFormatMP1Layer3)
+    return TRUE;
+  if (mpegaudioversion == 2
+      && mp3_param.eFormat != OMX_AUDIO_MP3StreamFormatMP2Layer3)
+    return TRUE;
+  if (mpegaudioversion == 3
+      && mp3_param.eFormat != OMX_AUDIO_MP3StreamFormatMP2_5Layer3)
+    return TRUE;
+
+  return FALSE;
+}
+
+static gint
+gst_omx_mp3_dec_get_samples_per_frame (GstOMXAudioDec * dec, GstOMXPort * port)
+{
+  return GST_OMX_MP3_DEC (dec)->spf;
+}
diff --git a/omx/gstomxmp3dec.h b/omx/gstomxmp3dec.h
new file mode 100644 (file)
index 0000000..4f07659
--- /dev/null
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2014, Sebastian Dröge <sebastian@centricular.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation
+ * version 2.1 of the License.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA
+ *
+ */
+
+#ifndef __GST_OMX_MP3_DEC_H__
+#define __GST_OMX_MP3_DEC_H__
+
+#include <gst/gst.h>
+#include "gstomxaudiodec.h"
+
+G_BEGIN_DECLS
+
+#define GST_TYPE_OMX_MP3_DEC \
+  (gst_omx_mp3_dec_get_type())
+#define GST_OMX_MP3_DEC(obj) \
+  (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_OMX_MP3_DEC,GstOMXMP3Dec))
+#define GST_OMX_MP3_DEC_CLASS(klass) \
+  (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_OMX_MP3_DEC,GstOMXMP3DecClass))
+#define GST_OMX_MP3_DEC_GET_CLASS(obj) \
+  (G_TYPE_INSTANCE_GET_CLASS((obj),GST_TYPE_OMX_MP3_DEC,GstOMXMP3DecClass))
+#define GST_IS_OMX_MP3_DEC(obj) \
+  (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_OMX_MP3_DEC))
+#define GST_IS_OMX_MP3_DEC_CLASS(obj) \
+  (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_OMX_MP3_DEC))
+
+typedef struct _GstOMXMP3Dec GstOMXMP3Dec;
+typedef struct _GstOMXMP3DecClass GstOMXMP3DecClass;
+
+struct _GstOMXMP3Dec
+{
+  GstOMXAudioDec parent;
+  gint spf;
+};
+
+struct _GstOMXMP3DecClass
+{
+  GstOMXAudioDecClass parent_class;
+};
+
+GType gst_omx_mp3_dec_get_type (void);
+
+G_END_DECLS
+
+#endif /* __GST_OMX_MP3_DEC_H__ */
+