summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJosep Torra <n770galaxy@gmail.com>2014-04-04 14:11:58 +0200
committerJosep Torra <n770galaxy@gmail.com>2014-05-09 13:15:18 +0200
commitb3eb4d897d83713d858839ebd8b01bad510e13b0 (patch)
treece1e598c13dd0e99260e606ebacddee66679dd65
parent893587f69c64f17bb29605bfcb868d2f20a9a4d2 (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.conf20
-rw-r--r--omx/Makefile.am10
-rw-r--r--omx/gstomx.c4
-rw-r--r--omx/gstomxanalogaudiosink.c64
-rw-r--r--omx/gstomxanalogaudiosink.h61
-rw-r--r--omx/gstomxaudiosink.c1224
-rw-r--r--omx/gstomxaudiosink.h103
-rw-r--r--omx/gstomxhdmiaudiosink.c66
-rw-r--r--omx/gstomxhdmiaudiosink.h61
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 (&param);
+ param.nPortIndex = self->in_port->index;
+ param.bMute = (mute ? OMX_TRUE : OMX_FALSE);
+ err = gst_omx_component_set_config (self->comp,
+ OMX_IndexConfigAudioMute, &param);
+ 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 (&param);
+ 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, &param);
+ 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 (&param);
+
+ err =
+ gst_omx_component_get_parameter (self->comp, OMX_IndexParamAudioInit,
+ &param);
+ if (err != OMX_ErrorNone) {
+ GST_WARNING_OBJECT (self, "Couldn't get port information: %s (0x%08x)",
+ gst_omx_error_to_string (err), err);
+ /* Fallback */
+ 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 (&param);
+
+ err =
+ gst_omx_component_get_parameter (self->comp, OMX_IndexParamAudioInit,
+ &param);
+ if (err != OMX_ErrorNone) {
+ GST_WARNING_OBJECT (self, "Couldn't get port information: %s (0x%08x)",
+ gst_omx_error_to_string (err), err);
+ /* Fallback */
+ 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 (&param);
+ 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, &param.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,
+ &param);
+ 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 (&param);
+ strcpy ((char *) param.sName, klass->destination);
+ err = gst_omx_component_set_config (self->comp,
+ OMX_IndexConfigBrcmAudioDestination, &param);
+ 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 (&param);
+ param.nPortIndex = self->in_port->index;
+ param.nU32 = 0;
+ err = gst_omx_component_get_config (self->comp,
+ OMX_IndexConfigAudioRenderingLatency, &param);
+ 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__ */
+