diff options
| author | Josep Torra <n770galaxy@gmail.com> | 2014-04-04 14:11:58 +0200 | 
|---|---|---|
| committer | Josep Torra <n770galaxy@gmail.com> | 2014-05-09 13:15:18 +0200 | 
| commit | b3eb4d897d83713d858839ebd8b01bad510e13b0 (patch) | |
| tree | ce1e598c13dd0e99260e606ebacddee66679dd65 | |
| parent | 893587f69c64f17bb29605bfcb868d2f20a9a4d2 (diff) | |
omxaudiosink: Implements OpenMAX based audio sinks
Provides omxanalogaudiosink and omxhdmiaudiosink elements on
the Raspberry PI.
- omxanalogaudiosink is capable to render raw mono or stereo audio
through the jack output.
- omxhdmiaudiosink is capable to render raw audio up to 8 channels
and transmit ac3/dts(IEC 61937) through the HDMI output.
- sinks provide a clock derived from rendered samples
- sinks support the GstStreamVolume interface by implementing
the volume and mute properties.
https://bugzilla.gnome.org/show_bug.cgi?id=728962
| -rw-r--r-- | config/rpi/gstomx.conf | 20 | ||||
| -rw-r--r-- | omx/Makefile.am | 10 | ||||
| -rw-r--r-- | omx/gstomx.c | 4 | ||||
| -rw-r--r-- | omx/gstomxanalogaudiosink.c | 64 | ||||
| -rw-r--r-- | omx/gstomxanalogaudiosink.h | 61 | ||||
| -rw-r--r-- | omx/gstomxaudiosink.c | 1224 | ||||
| -rw-r--r-- | omx/gstomxaudiosink.h | 103 | ||||
| -rw-r--r-- | omx/gstomxhdmiaudiosink.c | 66 | ||||
| -rw-r--r-- | omx/gstomxhdmiaudiosink.h | 61 | 
9 files changed, 1611 insertions, 2 deletions
| diff --git a/config/rpi/gstomx.conf b/config/rpi/gstomx.conf index a4b6f26..8dc98b5 100644 --- a/config/rpi/gstomx.conf +++ b/config/rpi/gstomx.conf @@ -80,3 +80,23 @@ in-port-index=200  out-port-index=201  hacks=no-component-role +[omxanalogaudiosink] +type-name=GstOMXAnalogAudioSink +core-name=/opt/vc/lib/libopenmaxil.so +component-name=OMX.broadcom.audio_render +rank=256 +in-port-index=100 +out-port-index=101 +hacks=no-component-role +sink-template-caps=audio/x-raw,format=(string){S16LE,S32LE},layout=(string)interleaved,rate=(int){8000,11025,16000,22050,24000,32000,41400,48000,88200,96000,176400,192000},channels=(int)[1,2] + +[omxhdmiaudiosink] +type-name=GstOMXHdmiAudioSink +core-name=/opt/vc/lib/libopenmaxil.so +component-name=OMX.broadcom.audio_render +rank=257 +in-port-index=100 +out-port-index=101 +hacks=no-component-role +sink-template-caps=audio/x-raw,format=(string){S16LE,S32LE},layout=(string)interleaved,rate=(int){8000,11025,16000,22050,24000,32000,41400,48000,88200,96000,176400,192000},channels=(int)[1,8];audio/x-ac3,framed=(boolean)true;audio/x-dts,framed=(boolean)true,block-size=(int){512,1024,2048} + diff --git a/omx/Makefile.am b/omx/Makefile.am index eb18e12..c9dcbb4 100644 --- a/omx/Makefile.am +++ b/omx/Makefile.am @@ -28,7 +28,10 @@ libgstomx_la_SOURCES = \  	gstomxmpeg4videoenc.c \  	gstomxh264enc.c \  	gstomxh263enc.c \ -	gstomxaacenc.c +	gstomxaacenc.c \ +	gstomxaudiosink.c \ +	gstomxanalogaudiosink.c \ +	gstomxhdmiaudiosink.c	  noinst_HEADERS = \  	gstomx.h \ @@ -47,7 +50,10 @@ noinst_HEADERS = \  	gstomxmpeg4videoenc.h \  	gstomxh264enc.h \  	gstomxh263enc.h \ -	gstomxaacenc.h +	gstomxaacenc.h \ +	gstomxaudiosink.h \ +	gstomxanalogaudiosink.h \ +	gstomxhdmiaudiosink.h 	  if !HAVE_EXTERNAL_OMX  OMX_INCLUDEPATH = -I$(abs_srcdir)/openmax diff --git a/omx/gstomx.c b/omx/gstomx.c index a37bb6b..4c05c0e 100644 --- a/omx/gstomx.c +++ b/omx/gstomx.c @@ -40,6 +40,8 @@  #include "gstomxh264enc.h"  #include "gstomxh263enc.h"  #include "gstomxaacenc.h" +#include "gstomxanalogaudiosink.h" +#include "gstomxhdmiaudiosink.h"  GST_DEBUG_CATEGORY (gstomx_debug);  #define GST_CAT_DEFAULT gstomx_debug @@ -2246,6 +2248,7 @@ done:  typedef GType (*GGetTypeFunction) (void);  static const GGetTypeFunction types[] = { +  gst_omx_analog_audio_sink_get_type, gst_omx_hdmi_audio_sink_get_type,    gst_omx_mpeg2_video_dec_get_type, gst_omx_mpeg4_video_dec_get_type,    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, @@ -2266,6 +2269,7 @@ struct TypeOffest  };  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_enc_get_type, G_STRUCT_OFFSET (GstOMXAudioEncClass, cdata)}, diff --git a/omx/gstomxanalogaudiosink.c b/omx/gstomxanalogaudiosink.c new file mode 100644 index 0000000..7c8c885 --- /dev/null +++ b/omx/gstomxanalogaudiosink.c @@ -0,0 +1,64 @@ +/* + * Copyright (C) 2014, Fluendo, S.A. + * Copyright (C) 2014, Metrological Media Innovations B.V. + *   Author: Josep Torra <josep@fluendo.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 "gstomxanalogaudiosink.h" + +GST_DEBUG_CATEGORY_STATIC (gst_omx_analog_audio_sink_debug_category); +#define GST_CAT_DEFAULT gst_omx_analog_audio_sink_debug_category + +/* class initialization */ + +#define DEBUG_INIT \ +  GST_DEBUG_CATEGORY_INIT (gst_omx_analog_audio_sink_debug_category, \ +      "omxanalogaudiosink", 0, "debug category for gst-omx analog audio sink"); + +G_DEFINE_TYPE_WITH_CODE (GstOMXAnalogAudioSink, gst_omx_analog_audio_sink, +    GST_TYPE_OMX_AUDIO_SINK, DEBUG_INIT); + +static void +gst_omx_analog_audio_sink_class_init (GstOMXAnalogAudioSinkClass * klass) +{ +  GstOMXAudioSinkClass *audiosink_class = GST_OMX_AUDIO_SINK_CLASS (klass); +  GstElementClass *element_class = GST_ELEMENT_CLASS (klass); + +  audiosink_class->cdata.default_sink_template_caps = "audio/x-raw, " +      "format = (string) " GST_AUDIO_FORMATS_ALL ", " +      "layout = (string) interleaved, " +      "rate = (int) [ 1, MAX ], " "channels = (int) [ 1, 2 ] "; +  audiosink_class->destination = "local"; + +  gst_element_class_set_static_metadata (element_class, +      "OpenMAX Analog Audio Sink", +      "Sink/Audio", "Output analog audio", "Josep Torra <josep@fluendo.com>"); + +  gst_omx_set_default_role (&audiosink_class->cdata, "audio_render.local"); +} + +static void +gst_omx_analog_audio_sink_init (GstOMXAnalogAudioSink * self) +{ +} diff --git a/omx/gstomxanalogaudiosink.h b/omx/gstomxanalogaudiosink.h new file mode 100644 index 0000000..7f57048 --- /dev/null +++ b/omx/gstomxanalogaudiosink.h @@ -0,0 +1,61 @@ +/* + * Copyright (C) 2014, Fluendo, S.A. + * Copyright (C) 2014, Metrological Media Innovations B.V. + *   Author: Josep Torra <josep@fluendo.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_ANALOG_AUDIO_SINK_H__ +#define __GST_OMX_ANALOG_AUDIO_SINK_H__ + +#include <gst/gst.h> +#include "gstomxaudiosink.h" + +G_BEGIN_DECLS + +#define GST_TYPE_OMX_ANALOG_AUDIO_SINK \ +  (gst_omx_analog_audio_sink_get_type()) +#define GST_OMX_ANALOG_AUDIO_SINK(obj) \ +  (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_OMX_ANALOG_AUDIO_SINK,GstOMXAnalogAudioSink)) +#define GST_OMX_ANALOG_AUDIO_SINK_CLASS(klass) \ +  (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_OMX_ANALOG_AUDIO_SINK,GstOMXAnalogAudioSinkClass)) +#define GST_OMX_ANALOG_AUDIO_SINK_GET_CLASS(obj) \ +  (G_TYPE_INSTANCE_GET_CLASS((obj),GST_TYPE_OMX_ANALOG_AUDIO_SINK,GstOMXAnalogAudioSinkClass)) +#define GST_IS_OMX_ANALOG_AUDIO_SINK(obj) \ +  (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_OMX_ANALOG_AUDIO_SINK)) +#define GST_IS_OMX_ANALOG_AUDIO_SINK_CLASS(obj) \ +  (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_OMX_ANALOG_AUDIO_SINK)) + +typedef struct _GstOMXAnalogAudioSink GstOMXAnalogAudioSink; +typedef struct _GstOMXAnalogAudioSinkClass GstOMXAnalogAudioSinkClass; + +struct _GstOMXAnalogAudioSink +{ +  GstOMXAudioSink parent; +}; + +struct _GstOMXAnalogAudioSinkClass +{ +  GstOMXAudioSinkClass parent_class; +}; + +GType gst_omx_analog_audio_sink_get_type (void); + +G_END_DECLS + +#endif /* __GST_OMX_ANALOG_AUDIO_SINK_H__ */ + diff --git a/omx/gstomxaudiosink.c b/omx/gstomxaudiosink.c new file mode 100644 index 0000000..f933077 --- /dev/null +++ b/omx/gstomxaudiosink.c @@ -0,0 +1,1224 @@ +/* + * Copyright (C) 2014, Fluendo, S.A. + * Copyright (C) 2014, Metrological Media Innovations B.V. + *   Author: Josep Torra <josep@fluendo.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 <gst/audio/audio.h> + +#include <math.h> + +#include "gstomxaudiosink.h" + +GST_DEBUG_CATEGORY_STATIC (gst_omx_audio_sink_debug_category); +#define GST_CAT_DEFAULT gst_omx_audio_sink_debug_category + +#define DEBUG_INIT \ +  GST_DEBUG_CATEGORY_INIT (gst_omx_audio_sink_debug_category, "omxaudiosink", \ +      0, "debug category for gst-omx audio sink base class"); + +#define DEFAULT_PROP_MUTE       FALSE +#define DEFAULT_PROP_VOLUME     1.0 + +#define VOLUME_MAX_DOUBLE       10.0 +#define OUT_CHANNELS(num_channels) ((num_channels) > 4 ? 8: (num_channels) > 2 ? 4: (num_channels)) + +enum +{ +  PROP_0, +  PROP_MUTE, +  PROP_VOLUME +}; + +#define gst_omx_audio_sink_parent_class parent_class +G_DEFINE_ABSTRACT_TYPE_WITH_CODE (GstOMXAudioSink, gst_omx_audio_sink, +    GST_TYPE_AUDIO_SINK, G_IMPLEMENT_INTERFACE (GST_TYPE_STREAM_VOLUME, NULL); +    DEBUG_INIT); + +#define transform_3_4(type) \ +static inline void \ +transform_3_4_##type (gpointer psrc, gpointer pdst, guint len) \ +{ \ +  g##type *src = (g##type *) psrc; \ +  g##type *dst = (g##type *) pdst; \ +  for (; len > 0; len--) { \ +    dst[0] = src[0]; \ +    dst[1] = src[1]; \ +    dst[2] = src[2]; \ +    dst[3] = 0; \ +    src += 3; \ +    dst += 4; \ +  } \ +} + +#define transform_5_8(type) \ +static inline void \ +transform_5_8_##type (gpointer psrc, gpointer pdst, guint len) \ +{ \ +  g##type *src = (g##type *) psrc; \ +  g##type *dst = (g##type *) pdst; \ +  for (; len > 0; len--) { \ +    dst[0] = src[0]; \ +    dst[1] = src[1]; \ +    dst[2] = src[2]; \ +    dst[3] = src[3]; \ +    dst[4] = src[4]; \ +    dst[5] = 0; \ +    dst[6] = 0; \ +    dst[7] = 0; \ +    src += 5; \ +    dst += 8; \ +  } \ +} + +#define transform_6_8(type) \ +static inline void \ +transform_6_8_##type (gpointer psrc, gpointer pdst, guint len) \ +{ \ +  g##type *src = (g##type *) psrc; \ +  g##type *dst = (g##type *) pdst; \ +  for (; len > 0; len--) { \ +    dst[0] = src[0]; \ +    dst[1] = src[1]; \ +    dst[2] = src[2]; \ +    dst[3] = src[3]; \ +    dst[4] = src[4]; \ +    dst[5] = src[5]; \ +    dst[6] = 0; \ +    dst[7] = 0; \ +    src += 6; \ +    dst += 8; \ +  } \ +} + +#define transform_7_8(type) \ +static inline void \ +transform_7_8_##type (gpointer psrc, gpointer pdst, guint len) \ +{ \ +  g##type *src = (g##type *) psrc; \ +  g##type *dst = (g##type *) pdst; \ +  for (; len > 0; len--) { \ +    dst[0] = src[0]; \ +    dst[1] = src[1]; \ +    dst[2] = src[2]; \ +    dst[3] = src[3]; \ +    dst[4] = src[4]; \ +    dst[5] = src[5]; \ +    dst[6] = src[6]; \ +    dst[7] = 0; \ +    src += 7; \ +    dst += 8; \ +  } \ +} + +transform_3_4 (int16); +transform_5_8 (int16); +transform_6_8 (int16); +transform_7_8 (int16); + +transform_3_4 (int32); +transform_5_8 (int32); +transform_6_8 (int32); +transform_7_8 (int32); + +static void inline +transform (guint in_chan, guint width, gpointer psrc, gpointer pdst, guint len) +{ +  guint out_chan = OUT_CHANNELS (in_chan); +  if (width == 16) { +    switch (out_chan) { +      case 4: +        if (in_chan == 3) { +          transform_3_4_int16 (psrc, pdst, len); +        } else { +          g_assert (FALSE); +        } +        break; +      case 8: +        switch (in_chan) { +          case 5: +            transform_5_8_int16 (psrc, pdst, len); +            break; +          case 6: +            transform_6_8_int16 (psrc, pdst, len); +            break; +          case 7: +            transform_7_8_int16 (psrc, pdst, len); +            break; +          default: +            g_assert (FALSE); +        } +        break; +      default: +        g_assert (FALSE); +    } +  } else if (width == 32) { +    switch (out_chan) { +      case 4: +        if (in_chan == 3) { +          transform_3_4_int32 (psrc, pdst, len); +        } else { +          g_assert (FALSE); +        } +        break; +      case 8: +        switch (in_chan) { +          case 5: +            transform_5_8_int32 (psrc, pdst, len); +            break; +          case 6: +            transform_6_8_int32 (psrc, pdst, len); +            break; +          case 7: +            transform_7_8_int32 (psrc, pdst, len); +            break; +          default: +            g_assert (FALSE); +        } +        break; +      default: +        g_assert (FALSE); +    } +  } else { +    g_assert (FALSE); +  } +} + +static void +gst_omx_audio_sink_mute_set (GstOMXAudioSink * self, gboolean mute) +{ +  if (self->comp) { +    OMX_ERRORTYPE err; +    OMX_AUDIO_CONFIG_MUTETYPE param; + +    GST_OMX_INIT_STRUCT (¶m); +    param.nPortIndex = self->in_port->index; +    param.bMute = (mute ? OMX_TRUE : OMX_FALSE); +    err = gst_omx_component_set_config (self->comp, +        OMX_IndexConfigAudioMute, ¶m); +    if (err != OMX_ErrorNone) { +      GST_ERROR_OBJECT (self, "Failed to set mute to %d: %s (0x%08x)", +          param.bMute, gst_omx_error_to_string (err), err); +    } +  } +  self->mute = mute; +} + +static void +gst_omx_audio_sink_volume_set (GstOMXAudioSink * self, gdouble volume) +{ +  if (self->comp) { +    OMX_ERRORTYPE err; +    OMX_AUDIO_CONFIG_VOLUMETYPE param; +    GST_OMX_INIT_STRUCT (¶m); +    param.nPortIndex = self->in_port->index; +    param.bLinear = OMX_TRUE; +    param.sVolume.nValue = volume * 100; +    err = gst_omx_component_set_config (self->comp, +        OMX_IndexConfigAudioVolume, ¶m); +    if (err != OMX_ErrorNone) { +      GST_ERROR_OBJECT (self, "Failed to set volume to %d: %s (0x%08x)", +          param.sVolume.nValue, gst_omx_error_to_string (err), err); +    } +  } +  self->volume = volume; +} + +static gboolean +gst_omx_audio_sink_open (GstAudioSink * audiosink) +{ +  GstOMXAudioSink *self = GST_OMX_AUDIO_SINK (audiosink); +  GstOMXAudioSinkClass *klass = GST_OMX_AUDIO_SINK_GET_CLASS (self); +  gint port_index; +  OMX_ERRORTYPE err; + +  GST_DEBUG_OBJECT (self, "Opening audio sink"); + +  self->comp = +      gst_omx_component_new (GST_OBJECT_CAST (self), klass->cdata.core_name, +      klass->cdata.component_name, klass->cdata.component_role, +      klass->cdata.hacks); + +  if (!self->comp) +    return FALSE; + +  if (gst_omx_component_get_state (self->comp, +          GST_CLOCK_TIME_NONE) != OMX_StateLoaded) +    return FALSE; + +  port_index = klass->cdata.in_port_index; + +  if (port_index == -1) { +    OMX_PORT_PARAM_TYPE param; + +    GST_OMX_INIT_STRUCT (¶m); + +    err = +        gst_omx_component_get_parameter (self->comp, OMX_IndexParamAudioInit, +        ¶m); +    if (err != OMX_ErrorNone) { +      GST_WARNING_OBJECT (self, "Couldn't get port information: %s (0x%08x)", +          gst_omx_error_to_string (err), err); +      /* Fallback */ +      port_index = 0; +    } else { +      GST_DEBUG_OBJECT (self, "Detected %u ports, starting at %u", +          (guint) param.nPorts, (guint) param.nStartPortNumber); +      port_index = param.nStartPortNumber + 0; +    } +  } +  self->in_port = gst_omx_component_add_port (self->comp, port_index); + +  port_index = klass->cdata.out_port_index; + +  if (port_index == -1) { +    OMX_PORT_PARAM_TYPE param; + +    GST_OMX_INIT_STRUCT (¶m); + +    err = +        gst_omx_component_get_parameter (self->comp, OMX_IndexParamAudioInit, +        ¶m); +    if (err != OMX_ErrorNone) { +      GST_WARNING_OBJECT (self, "Couldn't get port information: %s (0x%08x)", +          gst_omx_error_to_string (err), err); +      /* Fallback */ +      port_index = 0; +    } else { +      GST_DEBUG_OBJECT (self, "Detected %u ports, starting at %u", +          (guint) param.nPorts, (guint) param.nStartPortNumber); +      port_index = param.nStartPortNumber + 1; +    } +  } +  self->out_port = gst_omx_component_add_port (self->comp, port_index); + +  if (!self->in_port || !self->out_port) +    return FALSE; + +  err = gst_omx_port_set_enabled (self->in_port, FALSE); +  if (err != OMX_ErrorNone) { +    GST_ERROR_OBJECT (self, "Failed to enable port: %s (0x%08x)", +        gst_omx_error_to_string (err), err); +    return FALSE; +  } + +  err = gst_omx_port_set_enabled (self->out_port, FALSE); +  if (err != OMX_ErrorNone) { +    GST_ERROR_OBJECT (self, "Failed to enable port: %s (0x%08x)", +        gst_omx_error_to_string (err), err); +    return FALSE; +  } + +  GST_DEBUG_OBJECT (self, "Opened audio sink"); + +  return TRUE; +} + +static gboolean +gst_omx_audio_sink_close (GstAudioSink * audiosink) +{ +  GstOMXAudioSink *self = GST_OMX_AUDIO_SINK (audiosink); +  OMX_STATETYPE state; + +  GST_DEBUG_OBJECT (self, "Closing audio sink"); + +  state = gst_omx_component_get_state (self->comp, 0); +  if (state > OMX_StateLoaded || state == OMX_StateInvalid) { +    if (state > OMX_StateIdle) { +      gst_omx_component_set_state (self->comp, OMX_StateIdle); +      gst_omx_component_get_state (self->comp, 5 * GST_SECOND); +    } +    gst_omx_component_set_state (self->comp, OMX_StateLoaded); +    gst_omx_port_deallocate_buffers (self->in_port); +    if (state > OMX_StateLoaded) +      gst_omx_component_get_state (self->comp, 5 * GST_SECOND); +  } + +  self->in_port = NULL; +  self->out_port = NULL; +  if (self->comp) +    gst_omx_component_free (self->comp); +  self->comp = NULL; + +  GST_DEBUG_OBJECT (self, "Closed audio sink"); + +  return TRUE; +} + +static gboolean +gst_omx_audio_sink_parse_spec (GstOMXAudioSink * self, +    GstAudioRingBufferSpec * spec) +{ +  self->iec61937 = FALSE; +  self->endianness = GST_AUDIO_INFO_ENDIANNESS (&spec->info); +  self->rate = GST_AUDIO_INFO_RATE (&spec->info); +  self->channels = GST_AUDIO_INFO_CHANNELS (&spec->info); +  self->width = GST_AUDIO_INFO_WIDTH (&spec->info); +  self->is_signed = GST_AUDIO_INFO_IS_SIGNED (&spec->info); +  self->is_float = GST_AUDIO_INFO_IS_FLOAT (&spec->info); + +  switch (spec->type) { +    case GST_AUDIO_RING_BUFFER_FORMAT_TYPE_RAW: +    { +      guint out_channels = OUT_CHANNELS (self->channels); + +      self->samples = spec->segsize / self->channels / (self->width >> 3); +      if (self->channels == out_channels) { +        self->buffer_size = spec->segsize; +      } else { +        self->buffer_size = (spec->segsize / self->channels) * out_channels; +      } +      break; +    } +    case GST_AUDIO_RING_BUFFER_FORMAT_TYPE_AC3: +    case GST_AUDIO_RING_BUFFER_FORMAT_TYPE_EAC3: +    case GST_AUDIO_RING_BUFFER_FORMAT_TYPE_DTS: +    case GST_AUDIO_RING_BUFFER_FORMAT_TYPE_MPEG: +      self->iec61937 = TRUE; +      self->endianness = G_LITTLE_ENDIAN; +      self->channels = 2; +      self->width = 16; +      self->is_signed = TRUE; +      self->is_float = FALSE; +      self->buffer_size = spec->segsize; +      break; +    default: +      return FALSE; +  } + +  return TRUE; +} + +static inline void +channel_mapping (GstAudioRingBufferSpec * spec, +    OMX_AUDIO_CHANNELTYPE * eChannelMapping) +{ +  gint i, nchan = GST_AUDIO_INFO_CHANNELS (&spec->info); + +  for (i = 0; i < nchan; i++) { +    OMX_AUDIO_CHANNELTYPE pos; + +    switch (GST_AUDIO_INFO_POSITION (&spec->info, i)) { +      case GST_AUDIO_CHANNEL_POSITION_MONO: +      case GST_AUDIO_CHANNEL_POSITION_FRONT_CENTER: +        pos = OMX_AUDIO_ChannelCF; +        break; +      case GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT: +        pos = OMX_AUDIO_ChannelLF; +        break; +      case GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT: +        pos = OMX_AUDIO_ChannelRF; +        break; +      case GST_AUDIO_CHANNEL_POSITION_SIDE_LEFT: +        pos = OMX_AUDIO_ChannelLS; +        break; +      case GST_AUDIO_CHANNEL_POSITION_SIDE_RIGHT: +        pos = OMX_AUDIO_ChannelRS; +        break; +      case GST_AUDIO_CHANNEL_POSITION_LFE1: +        pos = OMX_AUDIO_ChannelLFE; +        break; +      case GST_AUDIO_CHANNEL_POSITION_REAR_CENTER: +        pos = OMX_AUDIO_ChannelCS; +        break; +      case GST_AUDIO_CHANNEL_POSITION_REAR_LEFT: +        pos = OMX_AUDIO_ChannelLR; +        break; +      case GST_AUDIO_CHANNEL_POSITION_REAR_RIGHT: +        pos = OMX_AUDIO_ChannelRR; +        break; +      default: +        pos = OMX_AUDIO_ChannelNone; +        break; +    } +    eChannelMapping[i] = pos; +  } +} + +static inline const gchar * +ch2str (OMX_AUDIO_CHANNELTYPE ch) +{ +  switch (ch) { +    case OMX_AUDIO_ChannelNone: +      return "OMX_AUDIO_ChannelNone"; +    case OMX_AUDIO_ChannelLF: +      return "OMX_AUDIO_ChannelLF"; +    case OMX_AUDIO_ChannelRF: +      return "OMX_AUDIO_ChannelRF"; +    case OMX_AUDIO_ChannelCF: +      return "OMX_AUDIO_ChannelCF"; +    case OMX_AUDIO_ChannelLS: +      return "OMX_AUDIO_ChannelLS"; +    case OMX_AUDIO_ChannelRS: +      return "OMX_AUDIO_ChannelRS"; +    case OMX_AUDIO_ChannelLFE: +      return "OMX_AUDIO_ChannelLFE"; +    case OMX_AUDIO_ChannelCS: +      return "OMX_AUDIO_ChannelCS"; +    case OMX_AUDIO_ChannelLR: +      return "OMX_AUDIO_ChannelLR"; +    case OMX_AUDIO_ChannelRR: +      return "OMX_AUDIO_ChannelRR"; +    default: +      return "Invalid value"; +  } +} + +static inline gboolean +gst_omx_audio_sink_configure_pcm (GstOMXAudioSink * self, +    GstAudioRingBufferSpec * spec) +{ +  OMX_AUDIO_PARAM_PCMMODETYPE param; +  OMX_ERRORTYPE err; + +  GST_OMX_INIT_STRUCT (¶m); +  param.nPortIndex = self->in_port->index; +  param.nChannels = OUT_CHANNELS (self->channels); +  param.eNumData = +      (self->is_signed ? OMX_NumericalDataSigned : OMX_NumericalDataUnsigned); +  param.eEndian = +      ((self->endianness == +          G_LITTLE_ENDIAN) ? OMX_EndianLittle : OMX_EndianBig); +  param.bInterleaved = OMX_TRUE; +  param.nBitPerSample = self->width; +  param.nSamplingRate = self->rate; + +  if (self->is_float) { +    /* This is cherrypicked from xbmc but it doesn't seems to be valid on my RPI. +     * https://github.com/xbmc/xbmc/blob/master/xbmc/cores/AudioEngine/Sinks/AESinkPi.cpp +     */ +    param.ePCMMode = (OMX_AUDIO_PCMMODETYPE) 0x8000; +  } else { +    param.ePCMMode = OMX_AUDIO_PCMModeLinear; +  } + +  if (spec->type == GST_AUDIO_RING_BUFFER_FORMAT_TYPE_RAW) { +    channel_mapping (spec, ¶m.eChannelMapping[0]); +  } + +  GST_DEBUG_OBJECT (self, "Setting PCM parameters"); +  GST_DEBUG_OBJECT (self, "  nChannels: %d", param.nChannels); +  GST_DEBUG_OBJECT (self, "  eNumData: %s", +      (param.eNumData == OMX_NumericalDataSigned ? "signed" : "unsigned")); +  GST_DEBUG_OBJECT (self, "  eEndian: %s", +      (param.eEndian == OMX_EndianLittle ? "little endian" : "big endian")); +  GST_DEBUG_OBJECT (self, "  bInterleaved: %d", param.bInterleaved); +  GST_DEBUG_OBJECT (self, "  nBitPerSample: %d", param.nBitPerSample); +  GST_DEBUG_OBJECT (self, "  nSamplingRate: %d", param.nSamplingRate); +  GST_DEBUG_OBJECT (self, "  ePCMMode: %04x", param.ePCMMode); +  GST_DEBUG_OBJECT (self, "  eChannelMapping: {%s, %s, %s, %s, %s, %s, %s, %s}", +      ch2str (param.eChannelMapping[0]), ch2str (param.eChannelMapping[1]), +      ch2str (param.eChannelMapping[2]), ch2str (param.eChannelMapping[3]), +      ch2str (param.eChannelMapping[4]), ch2str (param.eChannelMapping[5]), +      ch2str (param.eChannelMapping[6]), ch2str (param.eChannelMapping[7])); + +  err = +      gst_omx_component_set_parameter (self->comp, OMX_IndexParamAudioPcm, +      ¶m); +  if (err != OMX_ErrorNone) { +    GST_ERROR_OBJECT (self, "Failed to set PCM parameters: %s (0x%08x)", +        gst_omx_error_to_string (err), err); +    return FALSE; +  } + +  return TRUE; +} + +static gboolean +gst_omx_audio_sink_prepare (GstAudioSink * audiosink, +    GstAudioRingBufferSpec * spec) +{ +  GstOMXAudioSink *self = GST_OMX_AUDIO_SINK (audiosink); +  OMX_PARAM_PORTDEFINITIONTYPE port_def; +  OMX_ERRORTYPE err; + +  if (!gst_omx_audio_sink_parse_spec (self, spec)) +    goto spec_parse; + +  gst_omx_port_get_port_definition (self->in_port, &port_def); + +  port_def.nBufferSize = self->buffer_size; +  /* Only allocate a min number of buffers for transfers from our ringbuffer to +   * the hw ringbuffer as we want to keep our small */ +  port_def.nBufferCountActual = MAX (port_def.nBufferCountMin, 2); +  port_def.format.audio.eEncoding = OMX_AUDIO_CodingPCM; + +  GST_DEBUG_OBJECT (self, "Updating outport port definition"); +  GST_DEBUG_OBJECT (self, "  nBufferSize: %d", port_def.nBufferSize); +  GST_DEBUG_OBJECT (self, "  nBufferCountActual: %d", +      port_def.nBufferCountActual); +  GST_DEBUG_OBJECT (self, "  audio.eEncoding: 0x%08x", +      port_def.format.audio.eEncoding); + +  err = gst_omx_port_update_port_definition (self->in_port, &port_def); +  if (err != OMX_ErrorNone) { +    GST_ERROR_OBJECT (self, "Failed to configure port: %s (0x%08x)", +        gst_omx_error_to_string (err), err); +    goto configuration; +  } + +  if (!gst_omx_audio_sink_configure_pcm (self, spec)) { +    goto configuration; +  } + +  err = gst_omx_component_set_state (self->comp, OMX_StateIdle); +  if (err != OMX_ErrorNone) { +    GST_ERROR_OBJECT (self, "Failed to set state idle: %s (0x%08x)", +        gst_omx_error_to_string (err), err); +    goto activation; +  } + +  err = gst_omx_port_set_enabled (self->in_port, TRUE); +  if (err != OMX_ErrorNone) { +    GST_ERROR_OBJECT (self, "Failed to enable port: %s (0x%08x)", +        gst_omx_error_to_string (err), err); +    goto activation; +  } + +  GST_DEBUG_OBJECT (self, "Allocate buffers"); +  err = gst_omx_port_allocate_buffers (self->in_port); +  if (err != OMX_ErrorNone) { +    GST_ERROR_OBJECT (self, "Failed on buffer allocation: %s (0x%08x)", +        gst_omx_error_to_string (err), err); +    goto activation; +  } + +  err = gst_omx_port_wait_enabled (self->in_port, 5 * GST_SECOND); +  if (err != OMX_ErrorNone) { +    GST_ERROR_OBJECT (self, "port not enabled: %s (0x%08x)", +        gst_omx_error_to_string (err), err); +    goto activation; +  } + +  err = gst_omx_port_mark_reconfigured (self->in_port); +  if (err != OMX_ErrorNone) { +    GST_ERROR_OBJECT (self, "Couln't mark port as reconfigured: %s (0x%08x)", +        gst_omx_error_to_string (err), err); +    goto activation; +  } + +  err = gst_omx_component_set_state (self->comp, OMX_StatePause); +  if (err != OMX_ErrorNone) { +    GST_ERROR_OBJECT (self, "Failed to set state paused: %s (0x%08x)", +        gst_omx_error_to_string (err), err); +    goto activation; +  } + +  if (gst_omx_component_get_state (self->comp, +          GST_CLOCK_TIME_NONE) != OMX_StatePause) +    goto activation; + +  /* Configure some parameters */ +  GST_OBJECT_LOCK (self); +  gst_omx_audio_sink_mute_set (self, self->mute); +  gst_omx_audio_sink_volume_set (self, self->volume); +  GST_OBJECT_UNLOCK (self); + +#if defined (USE_OMX_TARGET_RPI) +  { +    GstOMXAudioSinkClass *klass = GST_OMX_AUDIO_SINK_GET_CLASS (self); +    OMX_ERRORTYPE err; +    OMX_CONFIG_BRCMAUDIODESTINATIONTYPE param; + +    if (klass->destination +        && strlen (klass->destination) < sizeof (param.sName)) { +      GST_DEBUG_OBJECT (self, "Setting destination: %s", klass->destination); +      GST_OMX_INIT_STRUCT (¶m); +      strcpy ((char *) param.sName, klass->destination); +      err = gst_omx_component_set_config (self->comp, +          OMX_IndexConfigBrcmAudioDestination, ¶m); +      if (err != OMX_ErrorNone) { +        GST_ERROR_OBJECT (self, +            "Failed to configuring destination: %s (0x%08x)", +            gst_omx_error_to_string (err), err); +        goto activation; +      } +    } +  } +#endif + +  return TRUE; + +  /* ERRORS */ +spec_parse: +  { +    GST_ELEMENT_ERROR (self, RESOURCE, SETTINGS, (NULL), +        ("Error parsing spec")); +    return FALSE; +  } + +configuration: +  { +    GST_ELEMENT_ERROR (self, RESOURCE, SETTINGS, (NULL), +        ("Configuration failed")); +    return FALSE; +  } +activation: +  { +    GST_ELEMENT_ERROR (self, RESOURCE, SETTINGS, (NULL), +        ("Component activation failed")); +    return FALSE; +  } +} + +static gboolean +gst_omx_audio_sink_unprepare (GstAudioSink * audiosink) +{ +  GstOMXAudioSink *self = GST_OMX_AUDIO_SINK (audiosink); +  OMX_ERRORTYPE err; + +  if (gst_omx_component_get_state (self->comp, 0) == OMX_StateIdle) +    return TRUE; + +  err = gst_omx_port_set_flushing (self->in_port, 5 * GST_SECOND, TRUE); +  if (err != OMX_ErrorNone) { +    GST_ERROR_OBJECT (self, "Failed to set port flushing: %s (0x%08x)", +        gst_omx_error_to_string (err), err); +    goto failed; +  } + +  err = gst_omx_component_set_state (self->comp, OMX_StateIdle); +  if (err != OMX_ErrorNone) { +    GST_ERROR_OBJECT (self, "Failed to set state idle: %s (0x%08x)", +        gst_omx_error_to_string (err), err); +    goto failed; +  } + +  err = gst_omx_port_set_enabled (self->in_port, FALSE); +  if (err != OMX_ErrorNone) { +    GST_ERROR_OBJECT (self, "Failed to set port disabled: %s (0x%08x)", +        gst_omx_error_to_string (err), err); +    goto failed; +  } + +  err = gst_omx_port_wait_buffers_released (self->in_port, 5 * GST_SECOND); +  if (err != OMX_ErrorNone) { +    goto failed; +  } + +  err = gst_omx_port_deallocate_buffers (self->in_port); +  if (err != OMX_ErrorNone) { +    GST_ERROR_OBJECT (self, "Couldn't deallocate buffers: %s (0x%08x)", +        gst_omx_error_to_string (err), err); +    goto failed; +  } + +  err = gst_omx_port_wait_enabled (self->in_port, 1 * GST_SECOND); +  if (err != OMX_ErrorNone) { +    goto failed; +  } + +  err = gst_omx_port_set_flushing (self->in_port, 5 * GST_SECOND, FALSE); +  if (err != OMX_ErrorNone) { +    GST_ERROR_OBJECT (self, "Failed to set port not flushing: %s (0x%08x)", +        gst_omx_error_to_string (err), err); +    goto failed; +  } + +  gst_omx_component_get_state (self->comp, GST_CLOCK_TIME_NONE); + +  return TRUE; + +  /* ERRORS */ +failed: +  { +    GST_ELEMENT_ERROR (self, LIBRARY, FAILED, (NULL), +        ("OpenMAX component in error state %s (0x%08x)", +            gst_omx_component_get_last_error_string (self->comp), +            gst_omx_component_get_last_error (self->comp))); +    return FALSE; +  } +} + +static GstOMXBuffer * +gst_omx_audio_sink_acquire_buffer (GstOMXAudioSink * self) +{ +  GstOMXAcquireBufferReturn acq_ret = GST_OMX_ACQUIRE_BUFFER_ERROR; +  GstOMXPort *port = self->in_port; +  OMX_ERRORTYPE err; +  GstOMXBuffer *buf = NULL; + +  while (!buf) { +    acq_ret = gst_omx_port_acquire_buffer (port, &buf); +    if (acq_ret == GST_OMX_ACQUIRE_BUFFER_ERROR) { +      goto component_error; +    } else if (acq_ret == GST_OMX_ACQUIRE_BUFFER_FLUSHING) { +      GST_DEBUG_OBJECT (self, "Flushing..."); +      goto flushing; +    } else if (acq_ret == GST_OMX_ACQUIRE_BUFFER_RECONFIGURE) { +      GST_DEBUG_OBJECT (self, "Reconfigure..."); +      /* Reallocate all buffers */ +      err = gst_omx_port_set_enabled (port, FALSE); +      if (err != OMX_ErrorNone) { +        GST_ERROR_OBJECT (self, "Failed to set port disabled: %s (0x%08x)", +            gst_omx_error_to_string (err), err); +        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) { +        GST_ERROR_OBJECT (self, "Couldn't deallocate buffers: %s (0x%08x)", +            gst_omx_error_to_string (err), err); +        goto reconfigure_error; +      } + +      err = gst_omx_port_wait_enabled (port, 1 * GST_SECOND); +      if (err != OMX_ErrorNone) { +        goto reconfigure_error; +      } + +      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_mark_reconfigured (port); +      if (err != OMX_ErrorNone) { +        goto reconfigure_error; +      } +      continue; +    } +  } + +  return buf; + +  /* ERRORS */ +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->comp), +            gst_omx_component_get_last_error (self->comp))); +    return NULL; +  } +reconfigure_error: +  { +    GST_ELEMENT_ERROR (self, LIBRARY, SETTINGS, (NULL), +        ("Unable to reconfigure input port")); +    return NULL; +  } +flushing: +  { +    return NULL; +  } +} + +static gint +gst_omx_audio_sink_write (GstAudioSink * audiosink, gpointer data, guint length) +{ +  GstOMXAudioSink *self = GST_OMX_AUDIO_SINK (audiosink); +  GstOMXBuffer *buf; +  OMX_ERRORTYPE err; + +  GST_LOG_OBJECT (self, "received audio samples buffer of %u bytes", length); + +  GST_OMX_AUDIO_SINK_LOCK (self); + +  if (!(buf = gst_omx_audio_sink_acquire_buffer (self))) { +    goto beach; +  } + +  if (buf->omx_buf->nAllocLen == length) { +    memcpy (buf->omx_buf->pBuffer + buf->omx_buf->nOffset, data, length); +  } else { +    transform (self->channels, self->width, data, +        buf->omx_buf->pBuffer + buf->omx_buf->nOffset, self->samples); +  } +  buf->omx_buf->nFilledLen = buf->omx_buf->nAllocLen; + +  err = gst_omx_port_release_buffer (self->in_port, buf); +  if (err != OMX_ErrorNone) +    goto release_error; + +beach: + +  GST_OMX_AUDIO_SINK_UNLOCK (self); + +  return length; + +  /* ERRORS */ +release_error: +  { +    GST_OMX_AUDIO_SINK_UNLOCK (self); +    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 0; +  } +} + +static guint +gst_omx_audio_sink_delay (GstAudioSink * audiosink) +{ +  GstOMXAudioSink *self = GST_OMX_AUDIO_SINK (audiosink); +  OMX_PARAM_U32TYPE param; +  OMX_ERRORTYPE err; + +  GST_OMX_INIT_STRUCT (¶m); +  param.nPortIndex = self->in_port->index; +  param.nU32 = 0; +  err = gst_omx_component_get_config (self->comp, +      OMX_IndexConfigAudioRenderingLatency, ¶m); +  if (err != OMX_ErrorNone) { +    GST_ERROR_OBJECT (self, "Failed to get rendering latency: %s (0x%08x)", +        gst_omx_error_to_string (err), err); +    param.nU32 = 0; +  } + +  GST_DEBUG_OBJECT (self, "reported delay %d samples", param.nU32); +  return param.nU32; +} + +static void +gst_omx_audio_sink_reset (GstAudioSink * audiosink) +{ +  GstOMXAudioSink *self = GST_OMX_AUDIO_SINK (audiosink); +  OMX_STATETYPE state; + +  GST_DEBUG_OBJECT (self, "Flushing sink"); + +  gst_omx_port_set_flushing (self->in_port, 5 * GST_SECOND, TRUE); + +  GST_OMX_AUDIO_SINK_LOCK (self); +  if ((state = gst_omx_component_get_state (self->comp, 0)) > OMX_StatePause) { +    gst_omx_component_set_state (self->comp, OMX_StatePause); +    gst_omx_component_get_state (self->comp, GST_CLOCK_TIME_NONE); +  } + +  gst_omx_component_set_state (self->comp, state); +  gst_omx_component_get_state (self->comp, GST_CLOCK_TIME_NONE); + +  gst_omx_port_set_flushing (self->in_port, 5 * GST_SECOND, FALSE); + +  GST_OMX_AUDIO_SINK_UNLOCK (self); +} + +static GstBuffer * +gst_omx_audio_sink_payload (GstAudioBaseSink * audiobasesink, GstBuffer * buf) +{ +  GstOMXAudioSink *self = GST_OMX_AUDIO_SINK (audiobasesink); + +  if (self->iec61937) { +    GstBuffer *out; +    gint framesize; +    GstMapInfo iinfo, oinfo; +    GstAudioRingBufferSpec *spec = &audiobasesink->ringbuffer->spec; + +    framesize = gst_audio_iec61937_frame_size (spec); +    if (framesize <= 0) +      return NULL; + +    out = gst_buffer_new_and_alloc (framesize); + +    gst_buffer_map (buf, &iinfo, GST_MAP_READ); +    gst_buffer_map (out, &oinfo, GST_MAP_WRITE); + +    if (!gst_audio_iec61937_payload (iinfo.data, iinfo.size, +            oinfo.data, oinfo.size, spec, G_BIG_ENDIAN)) { +      gst_buffer_unref (out); +      return NULL; +    } + +    gst_buffer_unmap (buf, &iinfo); +    gst_buffer_unmap (out, &oinfo); + +    gst_buffer_copy_into (out, buf, GST_BUFFER_COPY_METADATA, 0, -1); +    return out; +  } + +  return gst_buffer_ref (buf); +} + +static gboolean +gst_omx_audio_sink_accept_caps (GstOMXAudioSink * self, GstCaps * caps) +{ +  GstPad *pad = GST_BASE_SINK (self)->sinkpad; +  GstCaps *pad_caps; +  GstStructure *st; +  gboolean ret = FALSE; +  GstAudioRingBufferSpec spec = { 0 }; + +  pad_caps = gst_pad_query_caps (pad, caps); +  if (!pad_caps || gst_caps_is_empty (pad_caps)) { +    if (pad_caps) +      gst_caps_unref (pad_caps); +    ret = FALSE; +    goto done; +  } +  gst_caps_unref (pad_caps); + +  /* If we've not got fixed caps, creating a stream might fail, so let's just +   * return from here with default acceptcaps behaviour */ +  if (!gst_caps_is_fixed (caps)) +    goto done; + +  /* parse helper expects this set, so avoid nasty warning +   * will be set properly later on anyway  */ +  spec.latency_time = GST_SECOND; +  if (!gst_audio_ring_buffer_parse_caps (&spec, caps)) +    goto done; + +  /* Make sure input is framed (one frame per buffer) and can be payloaded */ +  switch (spec.type) { +    case GST_AUDIO_RING_BUFFER_FORMAT_TYPE_AC3: +    case GST_AUDIO_RING_BUFFER_FORMAT_TYPE_EAC3: +    case GST_AUDIO_RING_BUFFER_FORMAT_TYPE_DTS: +    case GST_AUDIO_RING_BUFFER_FORMAT_TYPE_MPEG: +    { +      gboolean framed = FALSE, parsed = FALSE; +      st = gst_caps_get_structure (caps, 0); + +      gst_structure_get_boolean (st, "framed", &framed); +      gst_structure_get_boolean (st, "parsed", &parsed); +      if ((!framed && !parsed) || gst_audio_iec61937_frame_size (&spec) <= 0) +        goto done; +    } +    default:{ +    } +  } +  ret = TRUE; + +done: +  gst_caps_replace (&spec.caps, NULL); +  return ret; +} + +static gboolean +gst_omx_audio_sink_query (GstBaseSink * basesink, GstQuery * query) +{ +  GstOMXAudioSink *self = GST_OMX_AUDIO_SINK (basesink); +  gboolean ret; + +  switch (GST_QUERY_TYPE (query)) { +    case GST_QUERY_ACCEPT_CAPS: +    { +      GstCaps *caps; + +      gst_query_parse_accept_caps (query, &caps); +      ret = gst_omx_audio_sink_accept_caps (self, caps); +      gst_query_set_accept_caps_result (query, ret); +      ret = TRUE; +      break; +    } +    default: +      ret = GST_BASE_SINK_CLASS (parent_class)->query (basesink, query); +      break; +  } +  return ret; +} + +static void +gst_omx_audio_sink_set_property (GObject * object, guint prop_id, +    const GValue * value, GParamSpec * pspec) +{ +  GstOMXAudioSink *self = GST_OMX_AUDIO_SINK (object); + +  switch (prop_id) { +    case PROP_MUTE: +    { +      gboolean mute = g_value_get_boolean (value); +      GST_OBJECT_LOCK (self); +      if (self->mute != mute) { +        gst_omx_audio_sink_mute_set (self, mute); +      } +      GST_OBJECT_UNLOCK (self); +      break; +    } +    case PROP_VOLUME: +    { +      gdouble volume = g_value_get_double (value); +      GST_OBJECT_LOCK (self); +      if (volume != self->volume) { +        gst_omx_audio_sink_volume_set (self, volume); +      } +      GST_OBJECT_UNLOCK (self); +      break; +    } +    default: +      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); +      break; +  } +} + +static void +gst_omx_audio_sink_get_property (GObject * object, guint prop_id, +    GValue * value, GParamSpec * pspec) +{ +  GstOMXAudioSink *self = GST_OMX_AUDIO_SINK (object); + +  switch (prop_id) { +    case PROP_MUTE: +      GST_OBJECT_LOCK (self); +      g_value_set_boolean (value, self->mute); +      GST_OBJECT_UNLOCK (self); +      break; +    case PROP_VOLUME: +      GST_OBJECT_LOCK (self); +      g_value_set_double (value, self->volume); +      GST_OBJECT_UNLOCK (self); +      break; +    default: +      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); +      break; +  } +} + +static GstStateChangeReturn +gst_omx_audio_sink_change_state (GstElement * element, +    GstStateChange transition) +{ +  GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS; +  GstOMXAudioSink *self = GST_OMX_AUDIO_SINK (element); +  OMX_ERRORTYPE err; + +  switch (transition) { +    case GST_STATE_CHANGE_PAUSED_TO_PLAYING: +    { +      GST_DEBUG_OBJECT (self, "going to PLAYING state"); +      err = gst_omx_component_set_state (self->comp, OMX_StateExecuting); +      if (err != OMX_ErrorNone) { +        GST_ERROR_OBJECT (self, "Failed to set state executing: %s (0x%08x)", +            gst_omx_error_to_string (err), err); +        return GST_STATE_CHANGE_FAILURE; +      } + +      if (gst_omx_component_get_state (self->comp, +              GST_CLOCK_TIME_NONE) != OMX_StateExecuting) { +        return GST_STATE_CHANGE_FAILURE; +      } +      GST_DEBUG_OBJECT (self, "in PLAYING state"); +      break; +    } +    default: +      break; +  } + +  ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition); + +  switch (transition) { +    case GST_STATE_CHANGE_PLAYING_TO_PAUSED: +    { +      GST_DEBUG_OBJECT (self, "going to PAUSED state"); +      err = gst_omx_component_set_state (self->comp, OMX_StatePause); +      if (err != OMX_ErrorNone) { +        GST_ERROR_OBJECT (self, "Failed to set state paused: %s (0x%08x)", +            gst_omx_error_to_string (err), err); +        return GST_STATE_CHANGE_FAILURE; +      } + +      if (gst_omx_component_get_state (self->comp, +              GST_CLOCK_TIME_NONE) != OMX_StatePause) { +        return GST_STATE_CHANGE_FAILURE; +      } +      GST_DEBUG_OBJECT (self, "in PAUSED state"); +      break; +    } +    default: +      break; +  } + +  return ret; +} + +static void +gst_omx_audio_sink_finalize (GObject * object) +{ +  GstOMXAudioSink *self = GST_OMX_AUDIO_SINK (object); + +  g_mutex_clear (&self->lock); + +  G_OBJECT_CLASS (parent_class)->finalize (object); +} + +static void +gst_omx_audio_sink_class_init (GstOMXAudioSinkClass * klass) +{ +  GObjectClass *gobject_class = G_OBJECT_CLASS (klass); +  GstElementClass *element_class = GST_ELEMENT_CLASS (klass); +  GstBaseSinkClass *basesink_class = GST_BASE_SINK_CLASS (klass); +  GstAudioBaseSinkClass *baudiosink_class = GST_AUDIO_BASE_SINK_CLASS (klass); +  GstAudioSinkClass *audiosink_class = GST_AUDIO_SINK_CLASS (klass); + +  gobject_class->set_property = gst_omx_audio_sink_set_property; +  gobject_class->get_property = gst_omx_audio_sink_get_property; +  gobject_class->finalize = gst_omx_audio_sink_finalize; + +  g_object_class_install_property (gobject_class, PROP_MUTE, +      g_param_spec_boolean ("mute", "Mute", "mute channel", +          DEFAULT_PROP_MUTE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + +  g_object_class_install_property (gobject_class, PROP_VOLUME, +      g_param_spec_double ("volume", "Volume", "volume factor, 1.0=100%", +          0.0, VOLUME_MAX_DOUBLE, DEFAULT_PROP_VOLUME, +          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + +  element_class->change_state = +      GST_DEBUG_FUNCPTR (gst_omx_audio_sink_change_state); + +  basesink_class->query = GST_DEBUG_FUNCPTR (gst_omx_audio_sink_query); + +  baudiosink_class->payload = GST_DEBUG_FUNCPTR (gst_omx_audio_sink_payload); + +  audiosink_class->open = GST_DEBUG_FUNCPTR (gst_omx_audio_sink_open); +  audiosink_class->close = GST_DEBUG_FUNCPTR (gst_omx_audio_sink_close); +  audiosink_class->prepare = GST_DEBUG_FUNCPTR (gst_omx_audio_sink_prepare); +  audiosink_class->unprepare = GST_DEBUG_FUNCPTR (gst_omx_audio_sink_unprepare); +  audiosink_class->write = GST_DEBUG_FUNCPTR (gst_omx_audio_sink_write); +  audiosink_class->delay = GST_DEBUG_FUNCPTR (gst_omx_audio_sink_delay); +  audiosink_class->reset = GST_DEBUG_FUNCPTR (gst_omx_audio_sink_reset); + + +  klass->cdata.type = GST_OMX_COMPONENT_TYPE_SINK; +} + +static void +gst_omx_audio_sink_init (GstOMXAudioSink * self) +{ +  g_mutex_init (&self->lock); + +  self->mute = DEFAULT_PROP_MUTE; +  self->volume = DEFAULT_PROP_VOLUME; + +  /* For the Raspberry PI there's a big hw buffer and 400 ms seems a good +   * size for our ringbuffer. OpenSL ES Sink also allocates a buffer of 400 ms +   * in Android so I guess that this should be a sane value for OpenMax in +   * general. */ +  GST_AUDIO_BASE_SINK (self)->buffer_time = 400000; +  gst_audio_base_sink_set_provide_clock (GST_AUDIO_BASE_SINK (self), TRUE); +} diff --git a/omx/gstomxaudiosink.h b/omx/gstomxaudiosink.h new file mode 100644 index 0000000..481b18a --- /dev/null +++ b/omx/gstomxaudiosink.h @@ -0,0 +1,103 @@ +/* + * Copyright (C) 2014, Fluendo, S.A. + * Copyright (C) 2014, Metrological Media Innovations B.V. + *   Author: Josep Torra <josep@fluendo.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_SINK_H__ +#define __GST_OMX_AUDIO_SINK_H__ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <gst/gst.h> +#include <gst/audio/audio.h> + +#include "gstomx.h" + +G_BEGIN_DECLS + +#define GST_TYPE_OMX_AUDIO_SINK \ +  (gst_omx_audio_sink_get_type()) +#define GST_OMX_AUDIO_SINK(obj) \ +  (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_OMX_AUDIO_SINK,GstOMXAudioSink)) +#define GST_OMX_AUDIO_SINK_CLASS(klass) \ +  (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_OMX_AUDIO_SINK,GstOMXAudioSinkClass)) +#define GST_OMX_AUDIO_SINK_GET_CLASS(obj) \ +  (G_TYPE_INSTANCE_GET_CLASS((obj),GST_TYPE_OMX_AUDIO_SINK,GstOMXAudioSinkClass)) +#define GST_IS_OMX_AUDIO_SINK(obj) \ +  (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_OMX_AUDIO_SINK)) +#define GST_IS_OMX_AUDIO_SINK_CLASS(obj) \ +  (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_OMX_AUDIO_SINK)) +#define GST_OMX_AUDIO_SINK_CAST(obj)      ((GstOMXAudioSink *) (obj)) + +#define GST_OMX_AUDIO_SINK_GET_LOCK(obj)	(&GST_OMX_AUDIO_SINK_CAST (obj)->lock) +#define GST_OMX_AUDIO_SINK_LOCK(obj)	    (g_mutex_lock (GST_OMX_AUDIO_SINK_GET_LOCK (obj))) +#define GST_OMX_AUDIO_SINK_UNLOCK(obj)    (g_mutex_unlock (GST_OMX_AUDIO_SINK_GET_LOCK (obj))) + +#define PASSTHROUGH_CAPS \ +    "audio/x-ac3, framed = (boolean) true;" \ +    "audio/x-eac3, framed = (boolean) true; " \ +    "audio/x-dts, framed = (boolean) true, " \ +      "block-size = (int) { 512, 1024, 2048 }; " \ +    "audio/mpeg, mpegversion = (int) 1, " \ +      "mpegaudioversion = (int) [ 1, 2 ], parsed = (boolean) true;" + +typedef struct _GstOMXAudioSink GstOMXAudioSink; +typedef struct _GstOMXAudioSinkClass GstOMXAudioSinkClass; + +struct _GstOMXAudioSink +{ +  GstAudioSink parent; + +  /* < protected > */ +  GstOMXComponent *comp; +  GstOMXPort *in_port, *out_port; +   +  gboolean mute; +  gdouble volume; + +  gboolean iec61937; +  guint endianness; +  guint rate; +  guint channels; +  guint width; +  gboolean is_signed; +  gboolean is_float; + +  guint buffer_size; +  guint samples; + +  GMutex lock; +}; + +struct _GstOMXAudioSinkClass +{ +  GstAudioSinkClass parent_class; + +  GstOMXClassData cdata; +  const gchar * destination; +}; + +GType gst_omx_audio_sink_get_type (void); + +G_END_DECLS + +#endif /* __GST_OMX_AUDIO_SINK_H__ */ + diff --git a/omx/gstomxhdmiaudiosink.c b/omx/gstomxhdmiaudiosink.c new file mode 100644 index 0000000..211b719 --- /dev/null +++ b/omx/gstomxhdmiaudiosink.c @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2014, Fluendo, S.A. + * Copyright (C) 2014, Metrological Media Innovations B.V. + *   Author: Josep Torra <josep@fluendo.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 "gstomxhdmiaudiosink.h" + +GST_DEBUG_CATEGORY_STATIC (gst_omx_hdmi_audio_sink_debug_category); +#define GST_CAT_DEFAULT gst_omx_hdmi_audio_sink_debug_category + +/* class initialization */ + +#define DEBUG_INIT \ +  GST_DEBUG_CATEGORY_INIT (gst_omx_hdmi_audio_sink_debug_category, \ +      "omxhdmiaudiosink", 0, "debug category for gst-omx hdmi audio sink"); + +G_DEFINE_TYPE_WITH_CODE (GstOMXHdmiAudioSink, gst_omx_hdmi_audio_sink, +    GST_TYPE_OMX_AUDIO_SINK, DEBUG_INIT); + +static void +gst_omx_hdmi_audio_sink_class_init (GstOMXHdmiAudioSinkClass * klass) +{ +  GstOMXAudioSinkClass *audiosink_class = GST_OMX_AUDIO_SINK_CLASS (klass); +  GstElementClass *element_class = GST_ELEMENT_CLASS (klass); + +  audiosink_class->cdata.default_sink_template_caps = "audio/x-raw, " +      "format = (string) " GST_AUDIO_FORMATS_ALL ", " +      "layout = (string) interleaved, " +      "rate = (int) [ 1, MAX ], " "channels = (int) [ 1, MAX ]; " +      PASSTHROUGH_CAPS; +  audiosink_class->destination = "hdmi"; + +  gst_element_class_set_static_metadata (element_class, +      "OpenMAX HDMI Audio Sink", +      "Sink/Audio", +      "Output audio through HDMI", "Josep Torra <josep@fluendo.com>"); + +  gst_omx_set_default_role (&audiosink_class->cdata, "audio_render.hdmi"); +} + +static void +gst_omx_hdmi_audio_sink_init (GstOMXHdmiAudioSink * self) +{ +} diff --git a/omx/gstomxhdmiaudiosink.h b/omx/gstomxhdmiaudiosink.h new file mode 100644 index 0000000..e45e56b --- /dev/null +++ b/omx/gstomxhdmiaudiosink.h @@ -0,0 +1,61 @@ +/* + * Copyright (C) 2014, Fluendo, S.A. + * Copyright (C) 2014, Metrological Media Innovations B.V. + *   Author: Josep Torra <josep@fluendo.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_HDMI_AUDIO_SINK_H__ +#define __GST_OMX_HDMI_AUDIO_SINK_H__ + +#include <gst/gst.h> +#include "gstomxaudiosink.h" + +G_BEGIN_DECLS + +#define GST_TYPE_OMX_HDMI_AUDIO_SINK \ +  (gst_omx_hdmi_audio_sink_get_type()) +#define GST_OMX_HDMI_AUDIO_SINK(obj) \ +  (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_OMX_HDMI_AUDIO_SINK,GstOMXHdmiAudioSink)) +#define GST_OMX_HDMI_AUDIO_SINK_CLASS(klass) \ +  (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_OMX_HDMI_AUDIO_SINK,GstOMXHdmiAudioSinkClass)) +#define GST_OMX_HDMI_AUDIO_SINK_GET_CLASS(obj) \ +  (G_TYPE_INSTANCE_GET_CLASS((obj),GST_TYPE_OMX_HDMI_AUDIO_SINK,GstOMXHdmiAudioSinkClass)) +#define GST_IS_OMX_HDMI_AUDIO_SINK(obj) \ +  (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_OMX_HDMI_AUDIO_SINK)) +#define GST_IS_OMX_HDMI_AUDIO_SINK_CLASS(obj) \ +  (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_OMX_HDMI_AUDIO_SINK)) + +typedef struct _GstOMXHdmiAudioSink GstOMXHdmiAudioSink; +typedef struct _GstOMXHdmiAudioSinkClass GstOMXHdmiAudioSinkClass; + +struct _GstOMXHdmiAudioSink +{ +  GstOMXAudioSink parent; +}; + +struct _GstOMXHdmiAudioSinkClass +{ +  GstOMXAudioSinkClass parent_class; +}; + +GType gst_omx_hdmi_audio_sink_get_type (void); + +G_END_DECLS + +#endif /* __GST_OMX_HDMI_AUDIO_SINK_H__ */ + | 
