From b3eb4d897d83713d858839ebd8b01bad510e13b0 Mon Sep 17 00:00:00 2001 From: Josep Torra Date: Fri, 4 Apr 2014 14:11:58 +0200 Subject: [PATCH] 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 --- config/rpi/gstomx.conf | 20 + omx/Makefile.am | 10 +- omx/gstomx.c | 4 + omx/gstomxanalogaudiosink.c | 64 ++ omx/gstomxanalogaudiosink.h | 61 ++ omx/gstomxaudiosink.c | 1224 +++++++++++++++++++++++++++++++++++ omx/gstomxaudiosink.h | 103 +++ omx/gstomxhdmiaudiosink.c | 66 ++ omx/gstomxhdmiaudiosink.h | 61 ++ 9 files changed, 1611 insertions(+), 2 deletions(-) create mode 100644 omx/gstomxanalogaudiosink.c create mode 100644 omx/gstomxanalogaudiosink.h create mode 100644 omx/gstomxaudiosink.c create mode 100644 omx/gstomxaudiosink.h create mode 100644 omx/gstomxhdmiaudiosink.c create mode 100644 omx/gstomxhdmiaudiosink.h diff --git a/config/rpi/gstomx.conf b/config/rpi/gstomx.conf index a4b6f26a8c..8dc98b557e 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 eb18e124b6..c9dcbb4bf1 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 a37bb6bc3c..4c05c0eb53 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 0000000000..7c8c885273 --- /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 + * + * 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 + +#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 "); + + 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 0000000000..7f5704812f --- /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 + * + * 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 +#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 0000000000..f9330779bd --- /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 + * + * 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 +#include + +#include + +#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 0000000000..481b18a513 --- /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 + * + * 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 +#include + +#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 0000000000..211b719343 --- /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 + * + * 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 + +#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 "); + + 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 0000000000..e45e56b3ce --- /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 + * + * 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 +#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__ */ +