1229 lines
		
	
	
		
			35 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			1229 lines
		
	
	
		
			35 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /*
 | |
|  * Copyright (C) 2014, Fluendo, S.A.
 | |
|  * Copyright (C) 2014, Metrological Media Innovations B.V.
 | |
|  *   Author: Josep Torra <josep@fluendo.com>
 | |
|  *
 | |
|  * This library is free software; you can redistribute it and/or
 | |
|  * modify it under the terms of the GNU Lesser General Public
 | |
|  * License as published by the Free Software Foundation
 | |
|  * version 2.1 of the License.
 | |
|  *
 | |
|  * This library is distributed in the hope that it will be useful,
 | |
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of
 | |
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 | |
|  * Lesser General Public License for more details.
 | |
|  *
 | |
|  * You should have received a copy of the GNU Lesser General Public
 | |
|  * License along with this library; if not, write to the Free Software
 | |
|  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA
 | |
|  *
 | |
|  */
 | |
| 
 | |
| #ifdef HAVE_CONFIG_H
 | |
| #include "config.h"
 | |
| #endif
 | |
| 
 | |
| 
 | |
| #include <gst/gst.h>
 | |
| #include <gst/audio/audio.h>
 | |
| 
 | |
| #include <math.h>
 | |
| 
 | |
| #include "gstomxaudiosink.h"
 | |
| 
 | |
| GST_DEBUG_CATEGORY_STATIC (gst_omx_audio_sink_debug_category);
 | |
| #define GST_CAT_DEFAULT gst_omx_audio_sink_debug_category
 | |
| 
 | |
| #define DEBUG_INIT \
 | |
|   GST_DEBUG_CATEGORY_INIT (gst_omx_audio_sink_debug_category, "omxaudiosink", \
 | |
|       0, "debug category for gst-omx audio sink base class");
 | |
| 
 | |
| #define DEFAULT_PROP_MUTE       FALSE
 | |
| #define DEFAULT_PROP_VOLUME     1.0
 | |
| 
 | |
| #define VOLUME_MAX_DOUBLE       10.0
 | |
| #define OUT_CHANNELS(num_channels) ((num_channels) > 4 ? 8: (num_channels) > 2 ? 4: (num_channels))
 | |
| 
 | |
| enum
 | |
| {
 | |
|   PROP_0,
 | |
|   PROP_MUTE,
 | |
|   PROP_VOLUME
 | |
| };
 | |
| 
 | |
| #define gst_omx_audio_sink_parent_class parent_class
 | |
| G_DEFINE_ABSTRACT_TYPE_WITH_CODE (GstOMXAudioSink, gst_omx_audio_sink,
 | |
|     GST_TYPE_AUDIO_SINK, G_IMPLEMENT_INTERFACE (GST_TYPE_STREAM_VOLUME, NULL);
 | |
|     DEBUG_INIT);
 | |
| 
 | |
| #define transform_3_4(type) \
 | |
| static inline void \
 | |
| transform_3_4_##type (gpointer psrc, gpointer pdst, guint len) \
 | |
| { \
 | |
|   g##type *src = (g##type *) psrc; \
 | |
|   g##type *dst = (g##type *) pdst; \
 | |
|   for (; len > 0; len--) { \
 | |
|     dst[0] = src[0]; \
 | |
|     dst[1] = src[1]; \
 | |
|     dst[2] = src[2]; \
 | |
|     dst[3] = 0; \
 | |
|     src += 3; \
 | |
|     dst += 4; \
 | |
|   } \
 | |
| }
 | |
| 
 | |
| #define transform_5_8(type) \
 | |
| static inline void \
 | |
| transform_5_8_##type (gpointer psrc, gpointer pdst, guint len) \
 | |
| { \
 | |
|   g##type *src = (g##type *) psrc; \
 | |
|   g##type *dst = (g##type *) pdst; \
 | |
|   for (; len > 0; len--) { \
 | |
|     dst[0] = src[0]; \
 | |
|     dst[1] = src[1]; \
 | |
|     dst[2] = src[2]; \
 | |
|     dst[3] = src[3]; \
 | |
|     dst[4] = src[4]; \
 | |
|     dst[5] = 0; \
 | |
|     dst[6] = 0; \
 | |
|     dst[7] = 0; \
 | |
|     src += 5; \
 | |
|     dst += 8; \
 | |
|   } \
 | |
| }
 | |
| 
 | |
| #define transform_6_8(type) \
 | |
| static inline void \
 | |
| transform_6_8_##type (gpointer psrc, gpointer pdst, guint len) \
 | |
| { \
 | |
|   g##type *src = (g##type *) psrc; \
 | |
|   g##type *dst = (g##type *) pdst; \
 | |
|   for (; len > 0; len--) { \
 | |
|     dst[0] = src[0]; \
 | |
|     dst[1] = src[1]; \
 | |
|     dst[2] = src[2]; \
 | |
|     dst[3] = src[3]; \
 | |
|     dst[4] = src[4]; \
 | |
|     dst[5] = src[5]; \
 | |
|     dst[6] = 0; \
 | |
|     dst[7] = 0; \
 | |
|     src += 6; \
 | |
|     dst += 8; \
 | |
|   } \
 | |
| }
 | |
| 
 | |
| #define transform_7_8(type) \
 | |
| static inline void \
 | |
| transform_7_8_##type (gpointer psrc, gpointer pdst, guint len) \
 | |
| { \
 | |
|   g##type *src = (g##type *) psrc; \
 | |
|   g##type *dst = (g##type *) pdst; \
 | |
|   for (; len > 0; len--) { \
 | |
|     dst[0] = src[0]; \
 | |
|     dst[1] = src[1]; \
 | |
|     dst[2] = src[2]; \
 | |
|     dst[3] = src[3]; \
 | |
|     dst[4] = src[4]; \
 | |
|     dst[5] = src[5]; \
 | |
|     dst[6] = src[6]; \
 | |
|     dst[7] = 0; \
 | |
|     src += 7; \
 | |
|     dst += 8; \
 | |
|   } \
 | |
| }
 | |
| 
 | |
| transform_3_4 (int16);
 | |
| transform_5_8 (int16);
 | |
| transform_6_8 (int16);
 | |
| transform_7_8 (int16);
 | |
| 
 | |
| transform_3_4 (int32);
 | |
| transform_5_8 (int32);
 | |
| transform_6_8 (int32);
 | |
| transform_7_8 (int32);
 | |
| 
 | |
| static void inline
 | |
| transform (guint in_chan, guint width, gpointer psrc, gpointer pdst, guint len)
 | |
| {
 | |
|   guint out_chan = OUT_CHANNELS (in_chan);
 | |
|   if (width == 16) {
 | |
|     switch (out_chan) {
 | |
|       case 4:
 | |
|         if (in_chan == 3) {
 | |
|           transform_3_4_int16 (psrc, pdst, len);
 | |
|         } else {
 | |
|           g_assert (FALSE);
 | |
|         }
 | |
|         break;
 | |
|       case 8:
 | |
|         switch (in_chan) {
 | |
|           case 5:
 | |
|             transform_5_8_int16 (psrc, pdst, len);
 | |
|             break;
 | |
|           case 6:
 | |
|             transform_6_8_int16 (psrc, pdst, len);
 | |
|             break;
 | |
|           case 7:
 | |
|             transform_7_8_int16 (psrc, pdst, len);
 | |
|             break;
 | |
|           default:
 | |
|             g_assert (FALSE);
 | |
|         }
 | |
|         break;
 | |
|       default:
 | |
|         g_assert (FALSE);
 | |
|     }
 | |
|   } else if (width == 32) {
 | |
|     switch (out_chan) {
 | |
|       case 4:
 | |
|         if (in_chan == 3) {
 | |
|           transform_3_4_int32 (psrc, pdst, len);
 | |
|         } else {
 | |
|           g_assert (FALSE);
 | |
|         }
 | |
|         break;
 | |
|       case 8:
 | |
|         switch (in_chan) {
 | |
|           case 5:
 | |
|             transform_5_8_int32 (psrc, pdst, len);
 | |
|             break;
 | |
|           case 6:
 | |
|             transform_6_8_int32 (psrc, pdst, len);
 | |
|             break;
 | |
|           case 7:
 | |
|             transform_7_8_int32 (psrc, pdst, len);
 | |
|             break;
 | |
|           default:
 | |
|             g_assert (FALSE);
 | |
|         }
 | |
|         break;
 | |
|       default:
 | |
|         g_assert (FALSE);
 | |
|     }
 | |
|   } else {
 | |
|     g_assert (FALSE);
 | |
|   }
 | |
| }
 | |
| 
 | |
| static void
 | |
| gst_omx_audio_sink_mute_set (GstOMXAudioSink * self, gboolean mute)
 | |
| {
 | |
|   if (self->comp) {
 | |
|     OMX_ERRORTYPE err;
 | |
|     OMX_AUDIO_CONFIG_MUTETYPE param;
 | |
| 
 | |
|     GST_OMX_INIT_STRUCT (¶m);
 | |
|     param.nPortIndex = self->in_port->index;
 | |
|     param.bMute = (mute ? OMX_TRUE : OMX_FALSE);
 | |
|     err = gst_omx_component_set_config (self->comp,
 | |
|         OMX_IndexConfigAudioMute, ¶m);
 | |
|     if (err != OMX_ErrorNone) {
 | |
|       GST_ERROR_OBJECT (self, "Failed to set mute to %d: %s (0x%08x)",
 | |
|           param.bMute, gst_omx_error_to_string (err), err);
 | |
|     }
 | |
|   }
 | |
|   self->mute = mute;
 | |
| }
 | |
| 
 | |
| static void
 | |
| gst_omx_audio_sink_volume_set (GstOMXAudioSink * self, gdouble volume)
 | |
| {
 | |
|   if (self->comp) {
 | |
|     OMX_ERRORTYPE err;
 | |
|     OMX_AUDIO_CONFIG_VOLUMETYPE param;
 | |
|     GST_OMX_INIT_STRUCT (¶m);
 | |
|     param.nPortIndex = self->in_port->index;
 | |
|     param.bLinear = OMX_TRUE;
 | |
|     param.sVolume.nValue = volume * 100;
 | |
|     err = gst_omx_component_set_config (self->comp,
 | |
|         OMX_IndexConfigAudioVolume, ¶m);
 | |
|     if (err != OMX_ErrorNone) {
 | |
|       GST_ERROR_OBJECT (self, "Failed to set volume to %d: %s (0x%08x)",
 | |
|           (gint) 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 disable 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 disable 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_unref (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: %u", (guint) 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: %u", (guint) param.nBitPerSample);
 | |
|   GST_DEBUG_OBJECT (self, "  nSamplingRate: %u", (guint) 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: %u", (guint) port_def.nBufferSize);
 | |
|   GST_DEBUG_OBJECT (self, "  nBufferCountActual: %u", (guint)
 | |
|       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_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 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;
 | |
|   }
 | |
| 
 | |
|   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, GST_OMX_WAIT);
 | |
|     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)
 | |
| {
 | |
| #if defined (USE_OMX_TARGET_RPI)
 | |
|   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 %u samples", (guint) param.nU32);
 | |
|   return param.nU32;
 | |
| #else
 | |
|   return 0;
 | |
| #endif
 | |
| }
 | |
| 
 | |
| 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);
 | |
| }
 |