diff options
author | Andoni Morales Alastruey <ylatuya@gmail.com> | 2013-03-05 21:17:52 +0100 |
---|---|---|
committer | Sebastian Dröge <sebastian.droege@collabora.co.uk> | 2013-03-08 12:13:55 +0100 |
commit | 9621074006488753486dbe037975dd282c088aec (patch) | |
tree | cc1c9427f35958851c614c430ae0fc3602f2dcc3 | |
parent | 400222e29fbc15f4b4f311a32ecd1ee0922a893f (diff) |
osxaudio: add a façade for the CoreAudio API
-rw-r--r-- | sys/osxaudio/Makefile.am | 6 | ||||
-rw-r--r-- | sys/osxaudio/gstosxaudiosink.c | 118 | ||||
-rw-r--r-- | sys/osxaudio/gstosxaudiosrc.c | 45 | ||||
-rw-r--r-- | sys/osxaudio/gstosxcoreaudio.c | 218 | ||||
-rw-r--r-- | sys/osxaudio/gstosxcoreaudio.h | 681 | ||||
-rw-r--r-- | sys/osxaudio/gstosxcoreaudiocommon.c | 431 | ||||
-rw-r--r-- | sys/osxaudio/gstosxcoreaudiocommon.h | 65 | ||||
-rw-r--r-- | sys/osxaudio/gstosxcoreaudiohal.c | 1281 | ||||
-rw-r--r-- | sys/osxaudio/gstosxringbuffer.c | 985 | ||||
-rw-r--r-- | sys/osxaudio/gstosxringbuffer.h | 30 |
10 files changed, 2176 insertions, 1684 deletions
diff --git a/sys/osxaudio/Makefile.am b/sys/osxaudio/Makefile.am index 138e5aef9..caddda2e4 100644 --- a/sys/osxaudio/Makefile.am +++ b/sys/osxaudio/Makefile.am @@ -4,6 +4,8 @@ libgstosxaudio_la_SOURCES = gstosxringbuffer.c \ gstosxaudioelement.c \ gstosxaudiosink.c \ gstosxaudiosrc.c \ + gstosxcoreaudiocommon.c \ + gstosxcoreaudio.c \ gstosxaudio.c libgstosxaudio_la_CFLAGS = $(GST_PLUGINS_BASE_CFLAGS) $(GST_CFLAGS) \ @@ -21,7 +23,9 @@ noinst_HEADERS = gstosxaudiosink.h \ gstosxaudioelement.h \ gstosxringbuffer.h \ gstosxaudiosrc.h \ - gstosxcoreaudio.h + gstosxcoreaudiocommon.h \ + gstosxcoreaudio.h \ + gstosxcoreaudiohal.c diff --git a/sys/osxaudio/gstosxaudiosink.c b/sys/osxaudio/gstosxaudiosink.c index 4ddd07630..43a9d30a2 100644 --- a/sys/osxaudio/gstosxaudiosink.c +++ b/sys/osxaudio/gstosxaudiosink.c @@ -69,8 +69,6 @@ #include <gst/gst.h> #include <gst/audio/multichannel.h> #include <gst/audio/gstaudioiec61937.h> -#include <CoreAudio/CoreAudio.h> -#include <CoreAudio/AudioHardware.h> #include "gstosxaudiosink.h" #include "gstosxaudioelement.h" @@ -171,6 +169,7 @@ gst_osx_audio_sink_do_init (GType type) GST_DEBUG_CATEGORY_INIT (osx_audiosink_debug, "osxaudiosink", 0, "OSX Audio Sink"); + gst_core_audio_init_debug (); GST_DEBUG ("Adding static interface"); g_type_add_interface_static (type, GST_OSX_AUDIO_ELEMENT_TYPE, &osxelement_info); @@ -425,19 +424,22 @@ gst_osx_audio_sink_create_ringbuffer (GstBaseAudioSink * sink) osxsink = GST_OSX_AUDIO_SINK (sink); if (!gst_osx_audio_sink_select_device (osxsink)) { + GST_ERROR_OBJECT (sink, "Could not select device"); return NULL; } - GST_DEBUG ("Creating ringbuffer"); + GST_DEBUG_OBJECT (sink, "Creating ringbuffer"); ringbuffer = g_object_new (GST_TYPE_OSX_RING_BUFFER, NULL); - GST_DEBUG ("osx sink %p element %p ioproc %p", osxsink, + GST_DEBUG_OBJECT (sink, "osx sink %p element %p ioproc %p", osxsink, GST_OSX_AUDIO_ELEMENT_GET_INTERFACE (osxsink), (void *) gst_osx_audio_sink_io_proc); gst_osx_audio_sink_set_volume (osxsink); - ringbuffer->element = GST_OSX_AUDIO_ELEMENT_GET_INTERFACE (osxsink); - ringbuffer->device_id = osxsink->device_id; + ringbuffer->core_audio->element = + GST_OSX_AUDIO_ELEMENT_GET_INTERFACE (osxsink); + ringbuffer->core_audio->device_id = osxsink->device_id; + ringbuffer->core_audio->is_src = FALSE; return GST_RING_BUFFER (ringbuffer); } @@ -455,7 +457,7 @@ gst_osx_audio_sink_io_proc (GstOsxRingBuffer * buf, guint8 *readptr; gint readseg; gint len; - gint stream_idx = buf->stream_idx; + gint stream_idx = buf->core_audio->stream_idx; gint remaining = bufferList->mBuffers[stream_idx].mDataByteSize; gint offset = 0; @@ -500,35 +502,13 @@ gst_osx_audio_sink_osxelement_init (gpointer g_iface, gpointer iface_data) static void gst_osx_audio_sink_set_volume (GstOsxAudioSink * sink) { - if (!sink->audiounit) - return; + GstOsxRingBuffer *osxbuf; - AudioUnitSetParameter (sink->audiounit, kHALOutputParam_Volume, - kAudioUnitScope_Global, 0, (float) sink->volume, 0); -} + osxbuf = GST_OSX_RING_BUFFER (GST_BASE_AUDIO_SINK (sink)->ringbuffer); + if (!osxbuf) + return; -static inline void -_dump_channel_layout (AudioChannelLayout * channel_layout) -{ - UInt32 i; - - GST_DEBUG ("mChannelLayoutTag: 0x%lx", - (unsigned long) channel_layout->mChannelLayoutTag); - GST_DEBUG ("mChannelBitmap: 0x%lx", - (unsigned long) channel_layout->mChannelBitmap); - GST_DEBUG ("mNumberChannelDescriptions: %lu", - (unsigned long) channel_layout->mNumberChannelDescriptions); - for (i = 0; i < channel_layout->mNumberChannelDescriptions; i++) { - AudioChannelDescription *channel_desc = - &channel_layout->mChannelDescriptions[i]; - GST_DEBUG (" mChannelLabel: 0x%lx mChannelFlags: 0x%lx " - "mCoordinates[0]: %f mCoordinates[1]: %f " - "mCoordinates[2]: %f", - (unsigned long) channel_desc->mChannelLabel, - (unsigned long) channel_desc->mChannelFlags, - channel_desc->mCoordinates[0], channel_desc->mCoordinates[1], - channel_desc->mCoordinates[2]); - } + gst_core_audio_set_volume (osxbuf->core_audio, sink->volume); } static gboolean @@ -554,14 +534,14 @@ gst_osx_audio_sink_allowed_caps (GstOsxAudioSink * osxsink) }; /* First collect info about the HW capabilites and preferences */ - spdif_allowed = _audio_device_is_spdif_avail (osxsink->device_id); - layout = _audio_device_get_channel_layout (osxsink->device_id); + spdif_allowed = + gst_core_audio_audio_device_is_spdif_avail (osxsink->device_id); + layout = gst_core_audio_audio_device_get_channel_layout (osxsink->device_id); GST_DEBUG_OBJECT (osxsink, "Selected device ID: %u SPDIF allowed: %d", (unsigned) osxsink->device_id, spdif_allowed); if (layout) { - _dump_channel_layout (layout); max_channels = layout->mNumberChannelDescriptions; } else { GST_WARNING_OBJECT (osxsink, "This driver does not support " @@ -656,71 +636,11 @@ gst_osx_audio_sink_allowed_caps (GstOsxAudioSink * osxsink) static gboolean gst_osx_audio_sink_select_device (GstOsxAudioSink * osxsink) { - AudioDeviceID *devices = NULL; - AudioDeviceID default_device_id = 0; - AudioChannelLayout *channel_layout; - gint i, ndevices = 0; gboolean res = FALSE; - devices = _audio_system_get_devices (&ndevices); - - if (ndevices < 1) { - GST_ERROR_OBJECT (osxsink, "no audio output devices found"); - goto done; - } - - GST_DEBUG_OBJECT (osxsink, "found %d audio device(s)", ndevices); - - for (i = 0; i < ndevices; i++) { - gchar *device_name; - - if ((device_name = _audio_device_get_name (devices[i]))) { - if (!_audio_device_has_output (devices[i])) { - GST_DEBUG_OBJECT (osxsink, "Input Device ID: %u Name: %s", - (unsigned) devices[i], device_name); - } else { - GST_DEBUG_OBJECT (osxsink, "Output Device ID: %u Name: %s", - (unsigned) devices[i], device_name); - - channel_layout = _audio_device_get_channel_layout (devices[i]); - if (channel_layout) { - _dump_channel_layout (channel_layout); - g_free (channel_layout); - } - } - - g_free (device_name); - } - } - - /* Find the ID of the default output device */ - default_device_id = _audio_system_get_default_output (); - - /* Here we decide if selected device is valid or autoselect - * the default one when required */ - if (osxsink->device_id == kAudioDeviceUnknown) { - if (default_device_id != kAudioDeviceUnknown) { - osxsink->device_id = default_device_id; - res = TRUE; - } - } else { - for (i = 0; i < ndevices; i++) { - if (osxsink->device_id == devices[i]) { - res = TRUE; - } - } - - if (res && !_audio_device_is_alive (osxsink->device_id)) { - GST_ERROR_OBJECT (osxsink, "Requested device not usable"); - res = FALSE; - goto done; - } - } - + if (!gst_core_audio_select_device (&osxsink->device_id)) + return FALSE; res = gst_osx_audio_sink_allowed_caps (osxsink); -done: - g_free (devices); - return res; } diff --git a/sys/osxaudio/gstosxaudiosrc.c b/sys/osxaudio/gstosxaudiosrc.c index bb2e814f7..78b72b705 100644 --- a/sys/osxaudio/gstosxaudiosrc.c +++ b/sys/osxaudio/gstosxaudiosrc.c @@ -60,8 +60,6 @@ #endif #include <gst/gst.h> -#include <CoreAudio/CoreAudio.h> -#include <CoreAudio/AudioHardware.h> #include "gstosxaudiosrc.h" #include "gstosxaudioelement.h" @@ -266,9 +264,10 @@ gst_osx_audio_src_create_ringbuffer (GstBaseAudioSrc * src) GST_OSX_AUDIO_ELEMENT_GET_INTERFACE (osxsrc), (void *) gst_osx_audio_src_io_proc); - ringbuffer->element = GST_OSX_AUDIO_ELEMENT_GET_INTERFACE (osxsrc); - ringbuffer->is_src = TRUE; - ringbuffer->device_id = osxsrc->device_id; + ringbuffer->core_audio->element = + GST_OSX_AUDIO_ELEMENT_GET_INTERFACE (osxsrc); + ringbuffer->core_audio->is_src = TRUE; + ringbuffer->core_audio->device_id = osxsrc->device_id; return GST_RING_BUFFER (ringbuffer); } @@ -286,15 +285,15 @@ gst_osx_audio_src_io_proc (GstOsxRingBuffer * buf, gint remaining; gint offset = 0; - status = AudioUnitRender (buf->audiounit, ioActionFlags, inTimeStamp, - inBusNumber, inNumberFrames, buf->recBufferList); + status = AudioUnitRender (buf->core_audio->audiounit, ioActionFlags, + inTimeStamp, inBusNumber, inNumberFrames, buf->core_audio->recBufferList); if (status) { GST_WARNING_OBJECT (buf, "AudioUnitRender returned %d", (int) status); return status; } - remaining = buf->recBufferList->mBuffers[0].mDataByteSize; + remaining = buf->core_audio->recBufferList->mBuffers[0].mDataByteSize; while (remaining) { if (!gst_ring_buffer_prepare_read (GST_RING_BUFFER (buf), @@ -307,7 +306,8 @@ gst_osx_audio_src_io_proc (GstOsxRingBuffer * buf, len = remaining; memcpy (writeptr + buf->segoffset, - (char *) buf->recBufferList->mBuffers[0].mData + offset, len); + (char *) buf->core_audio->recBufferList->mBuffers[0].mData + offset, + len); buf->segoffset += len; offset += len; @@ -334,30 +334,5 @@ gst_osx_audio_src_osxelement_init (gpointer g_iface, gpointer iface_data) static void gst_osx_audio_src_select_device (GstOsxAudioSrc * osxsrc) { - OSStatus status; - UInt32 propertySize; - - if (osxsrc->device_id == kAudioDeviceUnknown) { - /* If no specific device has been selected by the user, then pick the - * default device */ - GST_DEBUG_OBJECT (osxsrc, "Selecting device for OSXAudioSrc"); - propertySize = sizeof (osxsrc->device_id); - status = AudioHardwareGetProperty (kAudioHardwarePropertyDefaultInputDevice, - &propertySize, &osxsrc->device_id); - - if (status) { - GST_WARNING_OBJECT (osxsrc, - "AudioHardwareGetProperty returned %d", (int) status); - } else { - GST_DEBUG_OBJECT (osxsrc, "AudioHardwareGetProperty returned 0"); - } - - if (osxsrc->device_id == kAudioDeviceUnknown) { - GST_WARNING_OBJECT (osxsrc, - "AudioHardwareGetProperty: device_id is kAudioDeviceUnknown"); - } - - GST_DEBUG_OBJECT (osxsrc, "AudioHardwareGetProperty: device_id is %lu", - (long) osxsrc->device_id); - } + gst_core_audio_select_source_device (&osxsrc->device_id); } diff --git a/sys/osxaudio/gstosxcoreaudio.c b/sys/osxaudio/gstosxcoreaudio.c new file mode 100644 index 000000000..aa08223c6 --- /dev/null +++ b/sys/osxaudio/gstosxcoreaudio.c @@ -0,0 +1,218 @@ +/* + * GStreamer + * Copyright (C) 2012-2013 Fluendo S.A. <support@fluendo.com> + * Authors: Josep Torra Vallès <josep@fluendo.com> + * Andoni Morales Alastruey <amorales@fluendo.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#include "gstosxcoreaudio.h" +#include "gstosxcoreaudiocommon.h" +#include "gstosxaudiosrc.h" + +GST_DEBUG_CATEGORY_STATIC (osx_audio_debug); +#define GST_CAT_DEFAULT osx_audio_debug + +G_DEFINE_TYPE (GstCoreAudio, gst_core_audio, G_TYPE_OBJECT); + +#include "gstosxcoreaudiohal.c" + + +static void +gst_core_audio_class_init (GstCoreAudioClass * klass) +{ +} + +static void +gst_core_audio_init (GstCoreAudio * core_audio) +{ + core_audio->is_passthrough = FALSE; + core_audio->device_id = kAudioDeviceUnknown; + core_audio->is_src = FALSE; + core_audio->audiounit = NULL; +#ifndef HAVE_IOS + core_audio->hog_pid = -1; + core_audio->disabled_mixing = FALSE; +#endif +} + +/************************** + * Public API * + *************************/ + +GstCoreAudio * +gst_core_audio_new (GstObject * osxbuf) +{ + GstCoreAudio *core_audio; + + core_audio = g_object_new (GST_TYPE_CORE_AUDIO, NULL); + core_audio->osxbuf = osxbuf; + return core_audio; +} + +gboolean +gst_core_audio_close (GstCoreAudio * core_audio) +{ + AudioComponentInstanceDispose (core_audio->audiounit); + core_audio->audiounit = NULL; + return TRUE; +} + +gboolean +gst_core_audio_open (GstCoreAudio * core_audio) +{ + + if (!gst_core_audio_open_impl (core_audio)) + return FALSE; + + if (core_audio->is_src) { + AudioStreamBasicDescription asbd_in; + UInt32 propertySize; + OSStatus status; + + GstOsxAudioSrc *src = + GST_OSX_AUDIO_SRC (GST_OBJECT_PARENT (core_audio->osxbuf)); + + propertySize = sizeof (asbd_in); + status = AudioUnitGetProperty (core_audio->audiounit, + kAudioUnitProperty_StreamFormat, + kAudioUnitScope_Input, 1, &asbd_in, &propertySize); + + if (status) { + AudioComponentInstanceDispose (core_audio->audiounit); + core_audio->audiounit = NULL; + GST_WARNING_OBJECT (core_audio, + "Unable to obtain device properties: %" GST_FOURCC_FORMAT, + GST_FOURCC_ARGS (status)); + return FALSE; + } else { + src->deviceChannels = asbd_in.mChannelsPerFrame; + } + } + + return TRUE; +} + +gboolean +gst_core_audio_start_processing (GstCoreAudio * core_audio) +{ + return gst_core_audio_start_processing_impl (core_audio); +} + +gboolean +gst_core_audio_pause_processing (GstCoreAudio * core_audio) +{ + return gst_core_audio_pause_processing_impl (core_audio); +} + +gboolean +gst_core_audio_stop_processing (GstCoreAudio * core_audio) +{ + return gst_core_audio_stop_processing_impl (core_audio); +} + +gboolean +gst_core_audio_get_samples_and_latency (GstCoreAudio * core_audio, + gdouble rate, guint * samples, gdouble * latency) +{ + return gst_core_audio_get_samples_and_latency_impl (core_audio, rate, + samples, latency); +} + +gboolean +gst_core_audio_initialize (GstCoreAudio * core_audio, + AudioStreamBasicDescription format, GstCaps * caps, gboolean is_passthrough) +{ + guint32 frame_size; + OSStatus status; + + GST_DEBUG_OBJECT (core_audio, + "Initializing: passthrough:%d caps:%" GST_PTR_FORMAT, is_passthrough, + caps); + + if (!gst_core_audio_initialize_impl (core_audio, format, caps, + is_passthrough, &frame_size)) { + goto error; + } + + if (core_audio->is_src) { + /* create AudioBufferList needed for recording */ + core_audio->recBufferList = + buffer_list_alloc (format.mChannelsPerFrame, + frame_size * format.mBytesPerFrame); + } + + /* Initialize the AudioUnit */ + status = AudioUnitInitialize (core_audio->audiounit); + if (status) { + GST_ERROR_OBJECT (core_audio, "Failed to initialise AudioUnit: %" + GST_FOURCC_FORMAT, GST_FOURCC_ARGS (status)); + goto error; + } + return TRUE; + +error: + if (core_audio->is_src && core_audio->recBufferList) { + buffer_list_free (core_audio->recBufferList); + core_audio->recBufferList = NULL; + } + return FALSE; +} + +void +gst_core_audio_unitialize (GstCoreAudio * core_audio) +{ + AudioUnitUninitialize (core_audio->audiounit); + + if (core_audio->recBufferList) { + buffer_list_free (core_audio->recBufferList); + core_audio->recBufferList = NULL; + } +} + +void +gst_core_audio_set_volume (GstCoreAudio * core_audio, gfloat volume) +{ + AudioUnitSetParameter (core_audio->audiounit, kHALOutputParam_Volume, + kAudioUnitScope_Global, 0, (float) volume, 0); +} + +gboolean +gst_core_audio_select_device (AudioDeviceID * device_id) +{ + return gst_core_audio_select_device_impl (device_id); +} + +gboolean +gst_core_audio_select_source_device (AudioDeviceID * device_id) +{ + return gst_core_audio_select_source_device_impl (device_id); +} + +void +gst_core_audio_init_debug (void) +{ + GST_DEBUG_CATEGORY_INIT (osx_audio_debug, "osxaudio", 0, + "OSX Audio Elements"); +} + +gboolean +gst_core_audio_audio_device_is_spdif_avail (AudioDeviceID device_id) +{ + return gst_core_audio_audio_device_is_spdif_avail_impl (device_id); +} diff --git a/sys/osxaudio/gstosxcoreaudio.h b/sys/osxaudio/gstosxcoreaudio.h index 7a072d604..421ff6245 100644 --- a/sys/osxaudio/gstosxcoreaudio.h +++ b/sys/osxaudio/gstosxcoreaudio.h @@ -2,29 +2,6 @@ * GStreamer * Copyright (C) 2012 Fluendo S.A. <support@fluendo.com> * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER - * DEALINGS IN THE SOFTWARE. - * - * Alternatively, the contents of this file may be used under the - * GNU Lesser General Public License Version 2.1 (the "LGPL"), in - * which case the following provisions apply instead of the ones - * mentioned above: - * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either @@ -40,567 +17,135 @@ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, * Boston, MA 02110-1301, USA. * - * The development of this code was made possible due to the involvement of - * Pioneers of the Inevitable, the creators of the Songbird Music player - * */ +#ifndef __GST_CORE_AUDIO_H__ +#define __GST_CORE_AUDIO_H__ + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif + +#include <gst/gst.h> +#ifdef HAVE_IOS + #include <CoreAudio/CoreAudioTypes.h> + #define AudioDeviceID gint + #define kAudioDeviceUnknown 0 +#else + #include <CoreAudio/CoreAudio.h> + #include <AudioToolbox/AudioToolbox.h> + #if MAC_OS_X_VERSION_MIN_REQUIRED <= MAC_OS_X_VERSION_10_5 + #include <CoreServices/CoreServices.h> + #define AudioComponentFindNext FindNextComponent + #define AudioComponentInstanceNew OpenAComponent + #define AudioComponentInstanceDispose CloseComponent + #define AudioComponent Component + #define AudioComponentDescription ComponentDescription + #endif +#endif +#include <AudioUnit/AudioUnit.h> +#include "gstosxaudioelement.h" + + +G_BEGIN_DECLS + +#define GST_TYPE_CORE_AUDIO \ + (gst_core_audio_get_type()) +#define GST_CORE_AUDIO(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_CORE_AUDIO,GstCoreAudio)) +#define GST_CORE_AUDIO_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_CORE_AUDIO,GstCoreAudioClass)) +#define GST_CORE_AUDIO_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS((obj),GST_TYPE_CORE_AUDIO,GstCoreAudioClass)) +#define GST_IS_CORE_AUDIO(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_CORE_AUDIO)) +#define GST_IS_CORE_AUDIO_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_CORE_AUDIO)) + +#define CORE_AUDIO_FORMAT_IS_SPDIF(f) ((f).mFormat.mFormatID == 'IAC3' || (f).mFormat.mFormatID == 'iac3' || (f).mFormat.mFormatID == kAudioFormat60958AC3 || (f).mFormat.mFormatID == kAudioFormatAC3) + #define CORE_AUDIO_FORMAT "FormatID: %" GST_FOURCC_FORMAT " rate: %f flags: 0x%x BytesPerPacket: %u FramesPerPacket: %u BytesPerFrame: %u ChannelsPerFrame: %u BitsPerChannel: %u" #define CORE_AUDIO_FORMAT_ARGS(f) GST_FOURCC_ARGS((f).mFormatID),(f).mSampleRate,(unsigned)(f).mFormatFlags,(unsigned)(f).mBytesPerPacket,(unsigned)(f).mFramesPerPacket,(unsigned)(f).mBytesPerFrame,(unsigned)(f).mChannelsPerFrame,(unsigned)(f).mBitsPerChannel -#define CORE_AUDIO_FORMAT_IS_SPDIF(f) ((f).mFormat.mFormatID == 'IAC3' || (f).mFormat.mFormatID == 'iac3' || (f).mFormat.mFormatID == kAudioFormat60958AC3 || (f).mFormat.mFormatID == kAudioFormatAC3) +typedef struct _GstCoreAudio GstCoreAudio; +typedef struct _GstCoreAudioClass GstCoreAudioClass; -static inline gboolean -_audio_system_set_runloop (CFRunLoopRef runLoop) -{ - OSStatus status = noErr; - - gboolean res = FALSE; - - AudioObjectPropertyAddress runloopAddress = { - kAudioHardwarePropertyRunLoop, - kAudioObjectPropertyScopeGlobal, - kAudioObjectPropertyElementMaster - }; - - status = AudioObjectSetPropertyData (kAudioObjectSystemObject, - &runloopAddress, 0, NULL, sizeof (CFRunLoopRef), &runLoop); - if (status == noErr) { - res = TRUE; - } else { - GST_ERROR ("failed to set runloop to %p: %" GST_FOURCC_FORMAT, - runLoop, GST_FOURCC_ARGS (status)); - } - - return res; -} - -static inline AudioDeviceID -_audio_system_get_default_output (void) +struct _GstCoreAudio { - OSStatus status = noErr; - UInt32 propertySize = sizeof (AudioDeviceID); - AudioDeviceID device_id = kAudioDeviceUnknown; - - AudioObjectPropertyAddress defaultDeviceAddress = { - kAudioHardwarePropertyDefaultOutputDevice, - kAudioDevicePropertyScopeOutput, - kAudioObjectPropertyElementMaster - }; - - status = AudioObjectGetPropertyData (kAudioObjectSystemObject, - &defaultDeviceAddress, 0, NULL, &propertySize, &device_id); - if (status != noErr) { - GST_ERROR ("failed getting default output device: %" - GST_FOURCC_FORMAT, GST_FOURCC_ARGS (status)); - } - - return device_id; -} - -static inline AudioDeviceID * -_audio_system_get_devices (gint * ndevices) -{ - OSStatus status = noErr; - UInt32 propertySize = 0; - AudioDeviceID *devices = NULL; - - AudioObjectPropertyAddress audioDevicesAddress = { - kAudioHardwarePropertyDevices, - kAudioDevicePropertyScopeOutput, - kAudioObjectPropertyElementMaster - }; - - status = AudioObjectGetPropertyDataSize (kAudioObjectSystemObject, - &audioDevicesAddress, 0, NULL, &propertySize); - if (status != noErr) { - GST_WARNING ("failed getting number of devices: %" - GST_FOURCC_FORMAT, GST_FOURCC_ARGS (status)); - return NULL; - } - - *ndevices = propertySize / sizeof (AudioDeviceID); - - devices = (AudioDeviceID *) g_malloc (propertySize); - if (devices) { - status = AudioObjectGetPropertyData (kAudioObjectSystemObject, - &audioDevicesAddress, 0, NULL, &propertySize, devices); - if (status != noErr) { - GST_WARNING ("failed getting the list of devices: %" - GST_FOURCC_FORMAT, GST_FOURCC_ARGS (status)); - g_free (devices); - *ndevices = 0; - return NULL; - } - } - return devices; -} - -static inline gboolean -_audio_device_is_alive (AudioDeviceID device_id) -{ - OSStatus status = noErr; - int alive = FALSE; - UInt32 propertySize = sizeof (alive); - - AudioObjectPropertyAddress audioDeviceAliveAddress = { - kAudioDevicePropertyDeviceIsAlive, - kAudioDevicePropertyScopeOutput, - kAudioObjectPropertyElementMaster - }; - - status = AudioObjectGetPropertyData (device_id, - &audioDeviceAliveAddress, 0, NULL, &propertySize, &alive); - if (status != noErr) { - alive = FALSE; - } - - return alive; -} - -static inline guint -_audio_device_get_latency (AudioDeviceID device_id) -{ - OSStatus status = noErr; - UInt32 latency = 0; - UInt32 propertySize = sizeof (latency); - - AudioObjectPropertyAddress audioDeviceLatencyAddress = { - kAudioDevicePropertyLatency, - kAudioDevicePropertyScopeOutput, - kAudioObjectPropertyElementMaster - }; - - status = AudioObjectGetPropertyData (device_id, - &audioDeviceLatencyAddress, 0, NULL, &propertySize, &latency); - if (status != noErr) { - GST_ERROR ("failed to get latency: %" GST_FOURCC_FORMAT, - GST_FOURCC_ARGS (status)); - latency = -1; - } - - return latency; -} - -static inline pid_t -_audio_device_get_hog (AudioDeviceID device_id) -{ - OSStatus status = noErr; + GObject object; + + GstObject *osxbuf; + GstOsxAudioElementInterface *element; + + gboolean is_src; + gboolean is_passthrough; + AudioDeviceID device_id; + AudioStreamBasicDescription stream_format; + gint stream_idx; + gboolean io_proc_active; + gboolean io_proc_needs_deactivation; + + /* For LPCM in/out */ + AudioUnit audiounit; + AudioBufferList *recBufferList; + +#ifndef HAVE_IOS + /* For SPDIF out */ pid_t hog_pid; - UInt32 propertySize = sizeof (hog_pid); - - AudioObjectPropertyAddress audioDeviceHogModeAddress = { - kAudioDevicePropertyHogMode, - kAudioDevicePropertyScopeOutput, - kAudioObjectPropertyElementMaster - }; - - status = AudioObjectGetPropertyData (device_id, - &audioDeviceHogModeAddress, 0, NULL, &propertySize, &hog_pid); - if (status != noErr) { - GST_ERROR ("failed to get hog: %" GST_FOURCC_FORMAT, - GST_FOURCC_ARGS (status)); - hog_pid = -1; - } - - return hog_pid; -} - -static inline gboolean -_audio_device_set_hog (AudioDeviceID device_id, pid_t hog_pid) -{ - OSStatus status = noErr; - UInt32 propertySize = sizeof (hog_pid); - gboolean res = FALSE; - - AudioObjectPropertyAddress audioDeviceHogModeAddress = { - kAudioDevicePropertyHogMode, - kAudioDevicePropertyScopeOutput, - kAudioObjectPropertyElementMaster - }; - - status = AudioObjectSetPropertyData (device_id, - &audioDeviceHogModeAddress, 0, NULL, propertySize, &hog_pid); - - if (status == noErr) { - res = TRUE; - } else { - GST_ERROR ("failed to set hog: %" GST_FOURCC_FORMAT, - GST_FOURCC_ARGS (status)); - } - - return res; -} - -static inline gboolean -_audio_device_set_mixing (AudioDeviceID device_id, gboolean enable_mix) -{ - OSStatus status = noErr; - UInt32 propertySize = 0, can_mix = enable_mix; - Boolean writable = FALSE; - gboolean res = FALSE; - - AudioObjectPropertyAddress audioDeviceSupportsMixingAddress = { - kAudioDevicePropertySupportsMixing, - kAudioObjectPropertyScopeGlobal, - kAudioObjectPropertyElementMaster - }; - - if (AudioObjectHasProperty (device_id, &audioDeviceSupportsMixingAddress)) { - /* Set mixable to false if we are allowed to */ - status = AudioObjectIsPropertySettable (device_id, - &audioDeviceSupportsMixingAddress, &writable); - if (status) { - GST_DEBUG ("AudioObjectIsPropertySettable: %" GST_FOURCC_FORMAT, - GST_FOURCC_ARGS (status)); - } - status = AudioObjectGetPropertyDataSize (device_id, - &audioDeviceSupportsMixingAddress, 0, NULL, &propertySize); - if (status) { - GST_DEBUG ("AudioObjectGetPropertyDataSize: %" GST_FOURCC_FORMAT, - GST_FOURCC_ARGS (status)); - } - status = AudioObjectGetPropertyData (device_id, - &audioDeviceSupportsMixingAddress, 0, NULL, &propertySize, &can_mix); - if (status) { - GST_DEBUG ("AudioObjectGetPropertyData: %" GST_FOURCC_FORMAT, - GST_FOURCC_ARGS (status)); - } - - if (status == noErr && writable) { - can_mix = enable_mix; - status = AudioObjectSetPropertyData (device_id, - &audioDeviceSupportsMixingAddress, 0, NULL, propertySize, &can_mix); - res = TRUE; - } - - if (status != noErr) { - GST_ERROR ("failed to set mixmode: %" GST_FOURCC_FORMAT, - GST_FOURCC_ARGS (status)); - } - } else { - GST_DEBUG ("property not found, mixing coudln't be changed"); - } - - return res; -} - -static inline gchar * -_audio_device_get_name (AudioDeviceID device_id) -{ - OSStatus status = noErr; - UInt32 propertySize = 0; - gchar *device_name = NULL; - - AudioObjectPropertyAddress deviceNameAddress = { - kAudioDevicePropertyDeviceName, - kAudioDevicePropertyScopeOutput, - kAudioObjectPropertyElementMaster - }; - - /* Get the length of the device name */ - status = AudioObjectGetPropertyDataSize (device_id, - &deviceNameAddress, 0, NULL, &propertySize); - if (status != noErr) { - goto beach; - } - - /* Get the name of the device */ - device_name = (gchar *) g_malloc (propertySize); - status = AudioObjectGetPropertyData (device_id, - &deviceNameAddress, 0, NULL, &propertySize, device_name); - if (status != noErr) { - g_free (device_name); - device_name = NULL; - } - -beach: - return device_name; -} - -static inline gboolean -_audio_device_has_output (AudioDeviceID device_id) -{ - OSStatus status = noErr; - UInt32 propertySize; - - AudioObjectPropertyAddress streamsAddress = { - kAudioDevicePropertyStreams, - kAudioDevicePropertyScopeOutput, - kAudioObjectPropertyElementMaster - }; - - status = AudioObjectGetPropertyDataSize (device_id, - &streamsAddress, 0, NULL, &propertySize); - if (status != noErr) { - return FALSE; - } - if (propertySize == 0) { - return FALSE; - } - - return TRUE; -} - -static inline AudioChannelLayout * -_audio_device_get_channel_layout (AudioDeviceID device_id) -{ - OSStatus status = noErr; - UInt32 propertySize = 0; - AudioChannelLayout *layout = NULL; - - AudioObjectPropertyAddress channelLayoutAddress = { - kAudioDevicePropertyPreferredChannelLayout, - kAudioDevicePropertyScopeOutput, - kAudioObjectPropertyElementMaster - }; - - /* Get the length of the default channel layout structure */ - status = AudioObjectGetPropertyDataSize (device_id, - &channelLayoutAddress, 0, NULL, &propertySize); - if (status != noErr) { - GST_ERROR ("failed to get prefered layout: %" GST_FOURCC_FORMAT, - GST_FOURCC_ARGS (status)); - goto beach; - } - - /* Get the default channel layout of the device */ - layout = (AudioChannelLayout *) g_malloc (propertySize); - status = AudioObjectGetPropertyData (device_id, - &channelLayoutAddress, 0, NULL, &propertySize, layout); - if (status != noErr) { - GST_ERROR ("failed to get prefered layout: %" GST_FOURCC_FORMAT, - GST_FOURCC_ARGS (status)); - goto failed; - } - - if (layout->mChannelLayoutTag == kAudioChannelLayoutTag_UseChannelBitmap) { - /* bitmap defined channellayout */ - status = - AudioFormatGetProperty (kAudioFormatProperty_ChannelLayoutForBitmap, - sizeof (UInt32), &layout->mChannelBitmap, &propertySize, layout); - if (status != noErr) { - GST_ERROR ("failed to get layout for bitmap: %" GST_FOURCC_FORMAT, - GST_FOURCC_ARGS (status)); - goto failed; - } - } else if (layout->mChannelLayoutTag != - kAudioChannelLayoutTag_UseChannelDescriptions) { - /* layouttags defined channellayout */ - status = AudioFormatGetProperty (kAudioFormatProperty_ChannelLayoutForTag, - sizeof(AudioChannelLayoutTag), &layout->mChannelLayoutTag, - &propertySize, layout); - if (status != noErr) { - GST_ERROR ("failed to get layout for tag: %" GST_FOURCC_FORMAT, - GST_FOURCC_ARGS (status)); - goto failed; - } - } - -beach: - return layout; - -failed: - g_free (layout); - return NULL; -} - -static inline AudioStreamID * -_audio_device_get_streams (AudioDeviceID device_id, gint * nstreams) -{ - OSStatus status = noErr; - UInt32 propertySize = 0; - AudioStreamID *streams = NULL; - - AudioObjectPropertyAddress streamsAddress = { - kAudioDevicePropertyStreams, - kAudioDevicePropertyScopeOutput, - kAudioObjectPropertyElementMaster - }; - - status = AudioObjectGetPropertyDataSize (device_id, - &streamsAddress, 0, NULL, &propertySize); - if (status != noErr) { - GST_WARNING ("failed getting number of streams: %" - GST_FOURCC_FORMAT, GST_FOURCC_ARGS (status)); - return NULL; - } - - *nstreams = propertySize / sizeof (AudioStreamID); - streams = (AudioStreamID *) g_malloc (propertySize); - - if (streams) { - status = AudioObjectGetPropertyData (device_id, - &streamsAddress, 0, NULL, &propertySize, streams); - if (status != noErr) { - GST_WARNING ("failed getting the list of streams: %" - GST_FOURCC_FORMAT, GST_FOURCC_ARGS (status)); - g_free (streams); - *nstreams = 0; - return NULL; - } - } - - return streams; -} - -static inline guint -_audio_stream_get_latency (AudioStreamID stream_id) -{ - OSStatus status = noErr; - UInt32 latency; - UInt32 propertySize = sizeof (latency); - - AudioObjectPropertyAddress latencyAddress = { - kAudioStreamPropertyLatency, - kAudioObjectPropertyScopeGlobal, - kAudioObjectPropertyElementMaster - }; - - status = AudioObjectGetPropertyData (stream_id, - &latencyAddress, 0, NULL, &propertySize, &latency); - if (status != noErr) { - GST_ERROR ("failed to get latency: %" GST_FOURCC_FORMAT, - GST_FOURCC_ARGS (status)); - latency = -1; - } - - return latency; -} - -static inline gboolean -_audio_stream_get_current_format (AudioStreamID stream_id, - AudioStreamBasicDescription * format) -{ - OSStatus status = noErr; - UInt32 propertySize = sizeof (AudioStreamBasicDescription); - - AudioObjectPropertyAddress formatAddress = { - kAudioStreamPropertyPhysicalFormat, - kAudioObjectPropertyScopeGlobal, - kAudioObjectPropertyElementMaster - }; - - status = AudioObjectGetPropertyData (stream_id, - &formatAddress, 0, NULL, &propertySize, format); - if (status != noErr) { - GST_ERROR ("failed to get current format: %" GST_FOURCC_FORMAT, - GST_FOURCC_ARGS (status)); - return FALSE; - } - - return TRUE; -} - -static inline gboolean -_audio_stream_set_current_format (AudioStreamID stream_id, - AudioStreamBasicDescription format) -{ - OSStatus status = noErr; - UInt32 propertySize = sizeof (AudioStreamBasicDescription); - - AudioObjectPropertyAddress formatAddress = { - kAudioStreamPropertyPhysicalFormat, - kAudioObjectPropertyScopeGlobal, - kAudioObjectPropertyElementMaster - }; - - status = AudioObjectSetPropertyData (stream_id, - &formatAddress, 0, NULL, propertySize, &format); - if (status != noErr) { - GST_ERROR ("failed to set current format: %" GST_FOURCC_FORMAT, - GST_FOURCC_ARGS (status)); - return FALSE; - } - - return TRUE; -} - -static inline AudioStreamRangedDescription * -_audio_stream_get_formats (AudioStreamID stream_id, gint * nformats) -{ - OSStatus status = noErr; - UInt32 propertySize = 0; - AudioStreamRangedDescription *formats = NULL; - - AudioObjectPropertyAddress formatsAddress = { - kAudioStreamPropertyAvailablePhysicalFormats, - kAudioObjectPropertyScopeGlobal, - kAudioObjectPropertyElementMaster - }; - - status = AudioObjectGetPropertyDataSize (stream_id, - &formatsAddress, 0, NULL, &propertySize); - if (status != noErr) { - GST_WARNING ("failed getting number of stream formats: %" - GST_FOURCC_FORMAT, GST_FOURCC_ARGS (status)); - return NULL; - } - - *nformats = propertySize / sizeof (AudioStreamRangedDescription); - - formats = (AudioStreamRangedDescription *) g_malloc (propertySize); - if (formats) { - status = AudioObjectGetPropertyData (stream_id, - &formatsAddress, 0, NULL, &propertySize, formats); - if (status != noErr) { - GST_WARNING ("failed getting the list of stream formats: %" - GST_FOURCC_FORMAT, GST_FOURCC_ARGS (status)); - g_free (formats); - *nformats = 0; - return NULL; - } - } - return formats; -} - -static inline gboolean -_audio_stream_is_spdif_avail (AudioStreamID stream_id) + gboolean disabled_mixing; + AudioStreamID stream_id; + gboolean revert_format; + AudioStreamBasicDescription original_format; + AudioDeviceIOProcID procID; +#endif +}; + +struct _GstCoreAudioClass { - AudioStreamRangedDescription *formats; - gint i, nformats = 0; - gboolean res = FALSE; + GObjectClass parent_class; +}; - formats = _audio_stream_get_formats (stream_id, &nformats); - GST_DEBUG ("found %d stream formats", nformats); +GType gst_core_audio_get_type (void); - if (formats) { - GST_DEBUG ("formats supported on stream ID: %u", - (unsigned) stream_id); +void gst_core_audio_init_debug (void); - for (i = 0; i < nformats; i++) { - GST_DEBUG (" " CORE_AUDIO_FORMAT, - CORE_AUDIO_FORMAT_ARGS (formats[i].mFormat)); +GstCoreAudio * gst_core_audio_new (GstObject *osxbuf); - if (CORE_AUDIO_FORMAT_IS_SPDIF (formats[i])) { - res = TRUE; - } - } - g_free (formats); - } +gboolean gst_core_audio_open (GstCoreAudio *core_audio); - return res; -} +gboolean gst_core_audio_close (GstCoreAudio *core_audio); -static inline gboolean -_audio_device_is_spdif_avail (AudioDeviceID device_id) -{ - AudioStreamID *streams = NULL; - gint i, nstreams = 0; - gboolean res = FALSE; +gboolean gst_core_audio_initialize (GstCoreAudio *core_audio, + AudioStreamBasicDescription format, + GstCaps *caps, + gboolean is_passthrough); + +void gst_core_audio_unitialize (GstCoreAudio *core_audio); + +gboolean gst_core_audio_start_processing (GstCoreAudio *core_audio); + +gboolean gst_core_audio_pause_processing (GstCoreAudio *core_audio); + +gboolean gst_core_audio_stop_processing (GstCoreAudio *core_audio); + +gboolean gst_core_audio_get_samples_and_latency (GstCoreAudio * core_audio, + gdouble rate, + guint *samples, + gdouble *latency); + +void gst_core_audio_set_volume (GstCoreAudio *core_audio, + gfloat volume); + +gboolean gst_core_audio_audio_device_is_spdif_avail (AudioDeviceID device_id); + + +gboolean gst_core_audio_select_device (AudioDeviceID *device_id); + +gboolean gst_core_audio_select_source_device (AudioDeviceID *device_id); - streams = _audio_device_get_streams (device_id, &nstreams); - GST_DEBUG ("found %d streams", nstreams); - if (streams) { - for (i = 0; i < nstreams; i++) { - if (_audio_stream_is_spdif_avail (streams[i])) { - res = TRUE; - } - } +AudioChannelLayout * gst_core_audio_audio_device_get_channel_layout (AudioDeviceID device_id); - g_free (streams); - } - return res; -} +G_END_DECLS +#endif /* __GST_CORE_AUDIO_H__ */ diff --git a/sys/osxaudio/gstosxcoreaudiocommon.c b/sys/osxaudio/gstosxcoreaudiocommon.c new file mode 100644 index 000000000..5200b2ea1 --- /dev/null +++ b/sys/osxaudio/gstosxcoreaudiocommon.c @@ -0,0 +1,431 @@ +/* + * GStreamer + * Copyright (C) 2012-2013 Fluendo S.A. <support@fluendo.com> + * Authors: Josep Torra Vallès <josep@fluendo.com> + * Andoni Morales Alastruey <amorales@fluendo.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#include "gstosxcoreaudiocommon.h" + +void +gst_core_audio_remove_render_callback (GstCoreAudio * core_audio) +{ + AURenderCallbackStruct input; + OSStatus status; + + /* Deactivate the render callback by calling SetRenderCallback + * with a NULL inputProc. + */ + input.inputProc = NULL; + input.inputProcRefCon = NULL; + + status = AudioUnitSetProperty (core_audio->audiounit, kAudioUnitProperty_SetRenderCallback, kAudioUnitScope_Global, 0, /* N/A for global */ + &input, sizeof (input)); + + if (status) { + GST_WARNING_OBJECT (core_audio->osxbuf, "Failed to remove render callback %" + GST_FOURCC_FORMAT, GST_FOURCC_ARGS (status)); + } + + /* Remove the RenderNotify too */ + status = AudioUnitRemoveRenderNotify (core_audio->audiounit, + (AURenderCallback) gst_core_audio_render_notify, core_audio); + + if (status) { + GST_WARNING_OBJECT (core_audio->osxbuf, + "Failed to remove render notify callback %" GST_FOURCC_FORMAT, + GST_FOURCC_ARGS (status)); + } + + /* We're deactivated.. */ + core_audio->io_proc_needs_deactivation = FALSE; + core_audio->io_proc_active = FALSE; +} + +OSStatus +gst_core_audio_render_notify (GstCoreAudio * core_audio, + AudioUnitRenderActionFlags * ioActionFlags, + const AudioTimeStamp * inTimeStamp, + unsigned int inBusNumber, + unsigned int inNumberFrames, AudioBufferList * ioData) +{ + /* Before rendering a frame, we get the PreRender notification. + * Here, we detach the RenderCallback if we've been paused. + * + * This is necessary (rather than just directly detaching it) to + * work around some thread-safety issues in CoreAudio + */ + if ((*ioActionFlags) & kAudioUnitRenderAction_PreRender) { + if (core_audio->io_proc_needs_deactivation) { + gst_core_audio_remove_render_callback (core_audio); + } + } + + return noErr; +} + +gboolean +gst_core_audio_io_proc_start (GstCoreAudio * core_audio) +{ + OSStatus status; + AURenderCallbackStruct input; + AudioUnitPropertyID callback_type; + + GST_DEBUG_OBJECT (core_audio->osxbuf, + "osx ring buffer start ioproc: %p device_id %lu", + core_audio->element->io_proc, (gulong) core_audio->device_id); + if (!core_audio->io_proc_active) { + callback_type = core_audio->is_src ? + kAudioOutputUnitProperty_SetInputCallback : + kAudioUnitProperty_SetRenderCallback; + + input.inputProc = (AURenderCallback) core_audio->element->io_proc; + input.inputProcRefCon = core_audio->osxbuf; + + status = AudioUnitSetProperty (core_audio->audiounit, callback_type, kAudioUnitScope_Global, 0, /* N/A for global */ + &input, sizeof (input)); + + if (status) { + GST_ERROR_OBJECT (core_audio->osxbuf, + "AudioUnitSetProperty failed: %" GST_FOURCC_FORMAT, + GST_FOURCC_ARGS (status)); + return FALSE; + } + // ### does it make sense to do this notify stuff for input mode? + status = AudioUnitAddRenderNotify (core_audio->audiounit, + (AURenderCallback) gst_core_audio_render_notify, core_audio); + + if (status) { + GST_ERROR_OBJECT (core_audio->osxbuf, + "AudioUnitAddRenderNotify failed %" + GST_FOURCC_FORMAT, GST_FOURCC_ARGS (status)); + return FALSE; + } + core_audio->io_proc_active = TRUE; + } + + core_audio->io_proc_needs_deactivation = FALSE; + + status = AudioOutputUnitStart (core_audio->audiounit); + if (status) { + GST_ERROR_OBJECT (core_audio->osxbuf, "AudioOutputUnitStart failed: %" + GST_FOURCC_FORMAT, GST_FOURCC_ARGS (status)); + return FALSE; + } + return TRUE; +} + +gboolean +gst_core_audio_io_proc_stop (GstCoreAudio * core_audio) +{ + OSErr status; + + GST_DEBUG_OBJECT (core_audio->osxbuf, + "osx ring buffer stop ioproc: %p device_id %lu", + core_audio->element->io_proc, (gulong) core_audio->device_id); + + status = AudioOutputUnitStop (core_audio->audiounit); + if (status) { + GST_WARNING_OBJECT (core_audio->osxbuf, + "AudioOutputUnitStop failed: %" GST_FOURCC_FORMAT, + GST_FOURCC_ARGS (status)); + } + // ###: why is it okay to directly remove from here but not from pause() ? + if (core_audio->io_proc_active) { + gst_core_audio_remove_render_callback (core_audio); + } + return TRUE; +} + +AudioBufferList * +buffer_list_alloc (int channels, int size) +{ + AudioBufferList *list; + int total_size; + int n; + + total_size = sizeof (AudioBufferList) + 1 * sizeof (AudioBuffer); + list = (AudioBufferList *) g_malloc (total_size); + + list->mNumberBuffers = 1; + for (n = 0; n < (int) list->mNumberBuffers; ++n) { + list->mBuffers[n].mNumberChannels = channels; + list->mBuffers[n].mDataByteSize = size; + list->mBuffers[n].mData = g_malloc (size); + } + + return list; +} + +void +buffer_list_free (AudioBufferList * list) +{ + int n; + + for (n = 0; n < (int) list->mNumberBuffers; ++n) { + if (list->mBuffers[n].mData) + g_free (list->mBuffers[n].mData); + } + + g_free (list); +} + +gboolean +gst_core_audio_bind_device (GstCoreAudio * core_audio) +{ + OSStatus status; + + /* Specify which device we're using. */ + GST_DEBUG_OBJECT (core_audio->osxbuf, "Bind AudioUnit to device %d", + (int) core_audio->device_id); + status = AudioUnitSetProperty (core_audio->audiounit, + kAudioOutputUnitProperty_CurrentDevice, kAudioUnitScope_Global, 0, + &core_audio->device_id, sizeof (AudioDeviceID)); + if (status) { + GST_ERROR_OBJECT (core_audio->osxbuf, "Failed binding to device: %" + GST_FOURCC_FORMAT, GST_FOURCC_ARGS (status)); + goto audiounit_error; + } + return TRUE; + +audiounit_error: + if (core_audio->recBufferList) { + buffer_list_free (core_audio->recBufferList); + core_audio->recBufferList = NULL; + } + return FALSE; +} + +gboolean +gst_core_audio_set_channels_layout (GstCoreAudio * core_audio, + gint channels, GstCaps * caps) +{ + /* Configure the output stream and allocate ringbuffer memory */ + AudioChannelLayout *layout = NULL; + OSStatus status; + int layoutSize, element, i; + AudioUnitScope scope; + GstStructure *structure; + GstAudioChannelPosition *positions; + + /* Describe channels */ + layoutSize = sizeof (AudioChannelLayout) + + channels * sizeof (AudioChannelDescription); + layout = g_malloc (layoutSize); + + structure = gst_caps_get_structure (caps, 0); + positions = gst_audio_get_channel_positions (structure); + + layout->mChannelLayoutTag = kAudioChannelLayoutTag_UseChannelDescriptions; + layout->mChannelBitmap = 0; /* Not used */ + layout->mNumberChannelDescriptions = channels; + for (i = 0; i < channels; i++) { + if (positions) { + layout->mChannelDescriptions[i].mChannelLabel = + gst_audio_channel_position_to_coreaudio_channel_label (positions[i], + i); + } else { + /* Discrete channel numbers are ORed into this */ + layout->mChannelDescriptions[i].mChannelLabel = + kAudioChannelLabel_Discrete_0 | i; + } + + /* Others unused */ + layout->mChannelDescriptions[i].mChannelFlags = 0; + layout->mChannelDescriptions[i].mCoordinates[0] = 0.f; + layout->mChannelDescriptions[i].mCoordinates[1] = 0.f; + layout->mChannelDescriptions[i].mCoordinates[2] = 0.f; + } + + if (positions) { + g_free (positions); + positions = NULL; + } + + scope = core_audio->is_src ? kAudioUnitScope_Output : kAudioUnitScope_Input; + element = core_audio->is_src ? 1 : 0; + + if (layoutSize) { + status = AudioUnitSetProperty (core_audio->audiounit, + kAudioUnitProperty_AudioChannelLayout, + scope, element, layout, layoutSize); + if (status) { + GST_WARNING_OBJECT (core_audio->osxbuf, + "Failed to set output channel layout: %" GST_FOURCC_FORMAT, + GST_FOURCC_ARGS (status)); + return FALSE; + } + } + + g_free (layout); + return TRUE; +} + +gboolean +gst_core_audio_set_format (GstCoreAudio * core_audio, + AudioStreamBasicDescription format) +{ + /* Configure the output stream and allocate ringbuffer memory */ + OSStatus status; + UInt32 propertySize; + int element; + AudioUnitScope scope; + + GST_DEBUG_OBJECT (core_audio->osxbuf, "Setting format for AudioUnit"); + + scope = core_audio->is_src ? kAudioUnitScope_Output : kAudioUnitScope_Input; + element = core_audio->is_src ? 1 : 0; + + propertySize = sizeof (AudioStreamBasicDescription); + status = AudioUnitSetProperty (core_audio->audiounit, + kAudioUnitProperty_StreamFormat, scope, element, &format, propertySize); + + if (status) { + GST_WARNING_OBJECT (core_audio->osxbuf, + "Failed to set audio description: %" GST_FOURCC_FORMAT, + GST_FOURCC_ARGS (status)); + return FALSE;; + } + + return TRUE; +} + +gboolean +gst_core_audio_open_device (GstCoreAudio * core_audio, OSType sub_type, + const gchar * adesc) +{ + AudioComponentDescription desc; + AudioComponent comp; + OSStatus status; + AudioUnit unit; + UInt32 enableIO; + + desc.componentType = kAudioUnitType_Output; + desc.componentSubType = sub_type; + desc.componentManufacturer = kAudioUnitManufacturer_Apple; + desc.componentFlags = 0; + desc.componentFlagsMask = 0; + + comp = AudioComponentFindNext (NULL, &desc); + + if (comp == NULL) { + GST_WARNING_OBJECT (core_audio->osxbuf, "Couldn't find %s component", + adesc); + return FALSE; + } + + status = AudioComponentInstanceNew (comp, &unit); + + if (status) { + GST_ERROR_OBJECT (core_audio->osxbuf, "Couldn't open %s component %" + GST_FOURCC_FORMAT, adesc, GST_FOURCC_ARGS (status)); + return FALSE; + } + + if (core_audio->is_src) { + /* enable input */ + enableIO = 1; + status = AudioUnitSetProperty (unit, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Input, 1, /* 1 = input element */ + &enableIO, sizeof (enableIO)); + + if (status) { + AudioComponentInstanceDispose (unit); + GST_WARNING_OBJECT (core_audio->osxbuf, "Failed to enable input: %" + GST_FOURCC_FORMAT, GST_FOURCC_ARGS (status)); + return FALSE; + } + + /* disable output */ + enableIO = 0; + status = AudioUnitSetProperty (unit, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Output, 0, /* 0 = output element */ + &enableIO, sizeof (enableIO)); + + if (status) { + AudioComponentInstanceDispose (unit); + GST_WARNING_OBJECT (core_audio->osxbuf, "Failed to disable output: %" + GST_FOURCC_FORMAT, GST_FOURCC_ARGS (status)); + return FALSE; + } + } + + GST_DEBUG_OBJECT (core_audio->osxbuf, "Created %s AudioUnit: %p", adesc, + unit); + core_audio->audiounit = unit; + return TRUE; +} + +AudioChannelLabel +gst_audio_channel_position_to_coreaudio_channel_label (GstAudioChannelPosition + position, int channel) +{ + switch (position) { + case GST_AUDIO_CHANNEL_POSITION_NONE: + return kAudioChannelLabel_Discrete_0 | channel; + case GST_AUDIO_CHANNEL_POSITION_FRONT_MONO: + return kAudioChannelLabel_Mono; + case GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT: + return kAudioChannelLabel_Left; + case GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT: + return kAudioChannelLabel_Right; + case GST_AUDIO_CHANNEL_POSITION_REAR_CENTER: + return kAudioChannelLabel_CenterSurround; + case GST_AUDIO_CHANNEL_POSITION_REAR_LEFT: + return kAudioChannelLabel_LeftSurround; + case GST_AUDIO_CHANNEL_POSITION_REAR_RIGHT: + return kAudioChannelLabel_RightSurround; + case GST_AUDIO_CHANNEL_POSITION_LFE: + return kAudioChannelLabel_LFEScreen; + case GST_AUDIO_CHANNEL_POSITION_FRONT_CENTER: + return kAudioChannelLabel_Center; + case GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER: + return kAudioChannelLabel_Center; // ??? + case GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER: + return kAudioChannelLabel_Center; // ??? + case GST_AUDIO_CHANNEL_POSITION_SIDE_LEFT: + return kAudioChannelLabel_LeftSurroundDirect; + case GST_AUDIO_CHANNEL_POSITION_SIDE_RIGHT: + return kAudioChannelLabel_RightSurroundDirect; + default: + return kAudioChannelLabel_Unknown; + } +} + +void +gst_core_audio_dump_channel_layout (AudioChannelLayout * channel_layout) +{ + UInt32 i; + + GST_DEBUG ("mChannelLayoutTag: 0x%lx", + (unsigned long) channel_layout->mChannelLayoutTag); + GST_DEBUG ("mChannelBitmap: 0x%lx", + (unsigned long) channel_layout->mChannelBitmap); + GST_DEBUG ("mNumberChannelDescriptions: %lu", + (unsigned long) channel_layout->mNumberChannelDescriptions); + for (i = 0; i < channel_layout->mNumberChannelDescriptions; i++) { + AudioChannelDescription *channel_desc = + &channel_layout->mChannelDescriptions[i]; + GST_DEBUG (" mChannelLabel: 0x%lx mChannelFlags: 0x%lx " + "mCoordinates[0]: %f mCoordinates[1]: %f " + "mCoordinates[2]: %f", + (unsigned long) channel_desc->mChannelLabel, + (unsigned long) channel_desc->mChannelFlags, + channel_desc->mCoordinates[0], channel_desc->mCoordinates[1], + channel_desc->mCoordinates[2]); + } +} diff --git a/sys/osxaudio/gstosxcoreaudiocommon.h b/sys/osxaudio/gstosxcoreaudiocommon.h new file mode 100644 index 000000000..f644757e4 --- /dev/null +++ b/sys/osxaudio/gstosxcoreaudiocommon.h @@ -0,0 +1,65 @@ +/* + * GStreamer + * Copyright (C) 2012-2013 Fluendo S.A. <support@fluendo.com> + * Authors: Josep Torra Vallès <josep@fluendo.com> + * Andoni Morales Alastruey <amorales@fluendo.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#include <gst/audio/multichannel.h> +#include "gstosxcoreaudio.h" + +typedef struct +{ + GMutex *lock; + GCond *cond; +} PropertyMutex; + +gboolean gst_core_audio_bind_device (GstCoreAudio *core_audio); + +void gst_core_audio_dump_channel_layout (AudioChannelLayout * channel_layout); + +void gst_core_audio_remove_render_callback (GstCoreAudio * core_audio); + +gboolean gst_core_audio_io_proc_start (GstCoreAudio * core_audio); + +gboolean gst_core_audio_io_proc_stop (GstCoreAudio * core_audio); + +AudioBufferList * buffer_list_alloc (int channels, int size); + +void buffer_list_free (AudioBufferList * list); + +gboolean gst_core_audio_set_format (GstCoreAudio * core_audio, + AudioStreamBasicDescription format); + +gboolean gst_core_audio_set_channels_layout (GstCoreAudio * core_audio, + gint channels, GstCaps * caps); + +gboolean gst_core_audio_open_device (GstCoreAudio *core_audio, + OSType sub_type, + const gchar *adesc); + +OSStatus gst_core_audio_render_notify (GstCoreAudio * core_audio, + AudioUnitRenderActionFlags * ioActionFlags, + const AudioTimeStamp * inTimeStamp, + unsigned int inBusNumber, + unsigned int inNumberFrames, + AudioBufferList * ioData); + +AudioChannelLabel gst_audio_channel_position_to_coreaudio_channel_label (GstAudioChannelPosition position, int channel); + diff --git a/sys/osxaudio/gstosxcoreaudiohal.c b/sys/osxaudio/gstosxcoreaudiohal.c new file mode 100644 index 000000000..e018c03c9 --- /dev/null +++ b/sys/osxaudio/gstosxcoreaudiohal.c @@ -0,0 +1,1281 @@ +/* + * GStreamer + * Copyright (C) 2012-2013 Fluendo S.A. <support@fluendo.com> + * Authors: Josep Torra Vallès <josep@fluendo.com> + * Andoni Morales Alastruey <amorales@fluendo.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#include "gstosxaudiosink.h" + +static inline gboolean +_audio_system_set_runloop (CFRunLoopRef runLoop) +{ + OSStatus status = noErr; + + gboolean res = FALSE; + + AudioObjectPropertyAddress runloopAddress = { + kAudioHardwarePropertyRunLoop, + kAudioObjectPropertyScopeGlobal, + kAudioObjectPropertyElementMaster + }; + + status = AudioObjectSetPropertyData (kAudioObjectSystemObject, + &runloopAddress, 0, NULL, sizeof (CFRunLoopRef), &runLoop); + if (status == noErr) { + res = TRUE; + } else { + GST_ERROR ("failed to set runloop to %p: %" GST_FOURCC_FORMAT, + runLoop, GST_FOURCC_ARGS (status)); + } + + return res; +} + +static inline AudioDeviceID +_audio_system_get_default_output (void) +{ + OSStatus status = noErr; + UInt32 propertySize = sizeof (AudioDeviceID); + AudioDeviceID device_id = kAudioDeviceUnknown; + + AudioObjectPropertyAddress defaultDeviceAddress = { + kAudioHardwarePropertyDefaultOutputDevice, + kAudioDevicePropertyScopeOutput, + kAudioObjectPropertyElementMaster + }; + + status = AudioObjectGetPropertyData (kAudioObjectSystemObject, + &defaultDeviceAddress, 0, NULL, &propertySize, &device_id); + if (status != noErr) { + GST_ERROR ("failed getting default output device: %" + GST_FOURCC_FORMAT, GST_FOURCC_ARGS (status)); + } + + return device_id; +} + +static inline AudioDeviceID * +_audio_system_get_devices (gint * ndevices) +{ + OSStatus status = noErr; + UInt32 propertySize = 0; + AudioDeviceID *devices = NULL; + + AudioObjectPropertyAddress audioDevicesAddress = { + kAudioHardwarePropertyDevices, + kAudioDevicePropertyScopeOutput, + kAudioObjectPropertyElementMaster + }; + + status = AudioObjectGetPropertyDataSize (kAudioObjectSystemObject, + &audioDevicesAddress, 0, NULL, &propertySize); + if (status != noErr) { + GST_WARNING ("failed getting number of devices: %" + GST_FOURCC_FORMAT, GST_FOURCC_ARGS (status)); + return NULL; + } + + *ndevices = propertySize / sizeof (AudioDeviceID); + + devices = (AudioDeviceID *) g_malloc (propertySize); + if (devices) { + status = AudioObjectGetPropertyData (kAudioObjectSystemObject, + &audioDevicesAddress, 0, NULL, &propertySize, devices); + if (status != noErr) { + GST_WARNING ("failed getting the list of devices: %" + GST_FOURCC_FORMAT, GST_FOURCC_ARGS (status)); + g_free (devices); + *ndevices = 0; + return NULL; + } + } + return devices; +} + +static inline gboolean +_audio_device_is_alive (AudioDeviceID device_id) +{ + OSStatus status = noErr; + int alive = FALSE; + UInt32 propertySize = sizeof (alive); + + AudioObjectPropertyAddress audioDeviceAliveAddress = { + kAudioDevicePropertyDeviceIsAlive, + kAudioDevicePropertyScopeOutput, + kAudioObjectPropertyElementMaster + }; + + status = AudioObjectGetPropertyData (device_id, + &audioDeviceAliveAddress, 0, NULL, &propertySize, &alive); + if (status != noErr) { + alive = FALSE; + } + + return alive; +} + +static inline guint +_audio_device_get_latency (AudioDeviceID device_id) +{ + OSStatus status = noErr; + UInt32 latency = 0; + UInt32 propertySize = sizeof (latency); + + AudioObjectPropertyAddress audioDeviceLatencyAddress = { + kAudioDevicePropertyLatency, + kAudioDevicePropertyScopeOutput, + kAudioObjectPropertyElementMaster + }; + + status = AudioObjectGetPropertyData (device_id, + &audioDeviceLatencyAddress, 0, NULL, &propertySize, &latency); + if (status != noErr) { + GST_ERROR ("failed to get latency: %" GST_FOURCC_FORMAT, + GST_FOURCC_ARGS (status)); + latency = -1; + } + + return latency; +} + +static inline pid_t +_audio_device_get_hog (AudioDeviceID device_id) +{ + OSStatus status = noErr; + pid_t hog_pid; + UInt32 propertySize = sizeof (hog_pid); + + AudioObjectPropertyAddress audioDeviceHogModeAddress = { + kAudioDevicePropertyHogMode, + kAudioDevicePropertyScopeOutput, + kAudioObjectPropertyElementMaster + }; + + status = AudioObjectGetPropertyData (device_id, + &audioDeviceHogModeAddress, 0, NULL, &propertySize, &hog_pid); + if (status != noErr) { + GST_ERROR ("failed to get hog: %" GST_FOURCC_FORMAT, + GST_FOURCC_ARGS (status)); + hog_pid = -1; + } + + return hog_pid; +} + +static inline gboolean +_audio_device_set_hog (AudioDeviceID device_id, pid_t hog_pid) +{ + OSStatus status = noErr; + UInt32 propertySize = sizeof (hog_pid); + gboolean res = FALSE; + + AudioObjectPropertyAddress audioDeviceHogModeAddress = { + kAudioDevicePropertyHogMode, + kAudioDevicePropertyScopeOutput, + kAudioObjectPropertyElementMaster + }; + + status = AudioObjectSetPropertyData (device_id, + &audioDeviceHogModeAddress, 0, NULL, propertySize, &hog_pid); + + if (status == noErr) { + res = TRUE; + } else { + GST_ERROR ("failed to set hog: %" GST_FOURCC_FORMAT, + GST_FOURCC_ARGS (status)); + } + + return res; +} + +static inline gboolean +_audio_device_set_mixing (AudioDeviceID device_id, gboolean enable_mix) +{ + OSStatus status = noErr; + UInt32 propertySize = 0, can_mix = enable_mix; + Boolean writable = FALSE; + gboolean res = FALSE; + + AudioObjectPropertyAddress audioDeviceSupportsMixingAddress = { + kAudioDevicePropertySupportsMixing, + kAudioObjectPropertyScopeGlobal, + kAudioObjectPropertyElementMaster + }; + + if (AudioObjectHasProperty (device_id, &audioDeviceSupportsMixingAddress)) { + /* Set mixable to false if we are allowed to */ + status = AudioObjectIsPropertySettable (device_id, + &audioDeviceSupportsMixingAddress, &writable); + if (status) { + GST_DEBUG ("AudioObjectIsPropertySettable: %" GST_FOURCC_FORMAT, + GST_FOURCC_ARGS (status)); + } + status = AudioObjectGetPropertyDataSize (device_id, + &audioDeviceSupportsMixingAddress, 0, NULL, &propertySize); + if (status) { + GST_DEBUG ("AudioObjectGetPropertyDataSize: %" GST_FOURCC_FORMAT, + GST_FOURCC_ARGS (status)); + } + status = AudioObjectGetPropertyData (device_id, + &audioDeviceSupportsMixingAddress, 0, NULL, &propertySize, &can_mix); + if (status) { + GST_DEBUG ("AudioObjectGetPropertyData: %" GST_FOURCC_FORMAT, + GST_FOURCC_ARGS (status)); + } + + if (status == noErr && writable) { + can_mix = enable_mix; + status = AudioObjectSetPropertyData (device_id, + &audioDeviceSupportsMixingAddress, 0, NULL, propertySize, &can_mix); + res = TRUE; + } + + if (status != noErr) { + GST_ERROR ("failed to set mixmode: %" GST_FOURCC_FORMAT, + GST_FOURCC_ARGS (status)); + } + } else { + GST_DEBUG ("property not found, mixing coudln't be changed"); + } + + return res; +} + +static inline gchar * +_audio_device_get_name (AudioDeviceID device_id) +{ + OSStatus status = noErr; + UInt32 propertySize = 0; + gchar *device_name = NULL; + + AudioObjectPropertyAddress deviceNameAddress = { + kAudioDevicePropertyDeviceName, + kAudioDevicePropertyScopeOutput, + kAudioObjectPropertyElementMaster + }; + + /* Get the length of the device name */ + status = AudioObjectGetPropertyDataSize (device_id, + &deviceNameAddress, 0, NULL, &propertySize); + if (status != noErr) { + goto beach; + } + + /* Get the name of the device */ + device_name = (gchar *) g_malloc (propertySize); + status = AudioObjectGetPropertyData (device_id, + &deviceNameAddress, 0, NULL, &propertySize, device_name); + if (status != noErr) { + g_free (device_name); + device_name = NULL; + } + +beach: + return device_name; +} + +static inline gboolean +_audio_device_has_output (AudioDeviceID device_id) +{ + OSStatus status = noErr; + UInt32 propertySize; + + AudioObjectPropertyAddress streamsAddress = { + kAudioDevicePropertyStreams, + kAudioDevicePropertyScopeOutput, + kAudioObjectPropertyElementMaster + }; + + status = AudioObjectGetPropertyDataSize (device_id, + &streamsAddress, 0, NULL, &propertySize); + if (status != noErr) { + return FALSE; + } + if (propertySize == 0) { + return FALSE; + } + + return TRUE; +} + +AudioChannelLayout * +gst_core_audio_audio_device_get_channel_layout (AudioDeviceID device_id) +{ + OSStatus status = noErr; + UInt32 propertySize = 0; + AudioChannelLayout *layout = NULL; + + AudioObjectPropertyAddress channelLayoutAddress = { + kAudioDevicePropertyPreferredChannelLayout, + kAudioDevicePropertyScopeOutput, + kAudioObjectPropertyElementMaster + }; + + /* Get the length of the default channel layout structure */ + status = AudioObjectGetPropertyDataSize (device_id, + &channelLayoutAddress, 0, NULL, &propertySize); + if (status != noErr) { + GST_ERROR ("failed to get prefered layout: %" GST_FOURCC_FORMAT, + GST_FOURCC_ARGS (status)); + goto beach; + } + + /* Get the default channel layout of the device */ + layout = (AudioChannelLayout *) g_malloc (propertySize); + status = AudioObjectGetPropertyData (device_id, + &channelLayoutAddress, 0, NULL, &propertySize, layout); + if (status != noErr) { + GST_ERROR ("failed to get prefered layout: %" GST_FOURCC_FORMAT, + GST_FOURCC_ARGS (status)); + goto failed; + } + + if (layout->mChannelLayoutTag == kAudioChannelLayoutTag_UseChannelBitmap) { + /* bitmap defined channellayout */ + status = + AudioFormatGetProperty (kAudioFormatProperty_ChannelLayoutForBitmap, + sizeof (UInt32), &layout->mChannelBitmap, &propertySize, layout); + if (status != noErr) { + GST_ERROR ("failed to get layout for bitmap: %" GST_FOURCC_FORMAT, + GST_FOURCC_ARGS (status)); + goto failed; + } + } else if (layout->mChannelLayoutTag != + kAudioChannelLayoutTag_UseChannelDescriptions) { + /* layouttags defined channellayout */ + status = AudioFormatGetProperty (kAudioFormatProperty_ChannelLayoutForTag, + sizeof (AudioChannelLayoutTag), &layout->mChannelLayoutTag, + &propertySize, layout); + if (status != noErr) { + GST_ERROR ("failed to get layout for tag: %" GST_FOURCC_FORMAT, + GST_FOURCC_ARGS (status)); + goto failed; + } + } + +beach: + gst_core_audio_dump_channel_layout (layout); + return layout; + +failed: + g_free (layout); + return NULL; +} + +static inline AudioStreamID * +_audio_device_get_streams (AudioDeviceID device_id, gint * nstreams) +{ + OSStatus status = noErr; + UInt32 propertySize = 0; + AudioStreamID *streams = NULL; + + AudioObjectPropertyAddress streamsAddress = { + kAudioDevicePropertyStreams, + kAudioDevicePropertyScopeOutput, + kAudioObjectPropertyElementMaster + }; + + status = AudioObjectGetPropertyDataSize (device_id, + &streamsAddress, 0, NULL, &propertySize); + if (status != noErr) { + GST_WARNING ("failed getting number of streams: %" + GST_FOURCC_FORMAT, GST_FOURCC_ARGS (status)); + return NULL; + } + + *nstreams = propertySize / sizeof (AudioStreamID); + streams = (AudioStreamID *) g_malloc (propertySize); + + if (streams) { + status = AudioObjectGetPropertyData (device_id, + &streamsAddress, 0, NULL, &propertySize, streams); + if (status != noErr) { + GST_WARNING ("failed getting the list of streams: %" + GST_FOURCC_FORMAT, GST_FOURCC_ARGS (status)); + g_free (streams); + *nstreams = 0; + return NULL; + } + } + + return streams; +} + +static inline guint +_audio_stream_get_latency (AudioStreamID stream_id) +{ + OSStatus status = noErr; + UInt32 latency; + UInt32 propertySize = sizeof (latency); + + AudioObjectPropertyAddress latencyAddress = { + kAudioStreamPropertyLatency, + kAudioObjectPropertyScopeGlobal, + kAudioObjectPropertyElementMaster + }; + + status = AudioObjectGetPropertyData (stream_id, + &latencyAddress, 0, NULL, &propertySize, &latency); + if (status != noErr) { + GST_ERROR ("failed to get latency: %" GST_FOURCC_FORMAT, + GST_FOURCC_ARGS (status)); + latency = -1; + } + + return latency; +} + +static inline gboolean +_audio_stream_get_current_format (AudioStreamID stream_id, + AudioStreamBasicDescription * format) +{ + OSStatus status = noErr; + UInt32 propertySize = sizeof (AudioStreamBasicDescription); + + AudioObjectPropertyAddress formatAddress = { + kAudioStreamPropertyPhysicalFormat, + kAudioObjectPropertyScopeGlobal, + kAudioObjectPropertyElementMaster + }; + + status = AudioObjectGetPropertyData (stream_id, + &formatAddress, 0, NULL, &propertySize, format); + if (status != noErr) { + GST_ERROR ("failed to get current format: %" GST_FOURCC_FORMAT, + GST_FOURCC_ARGS (status)); + return FALSE; + } + + return TRUE; +} + +static inline gboolean +_audio_stream_set_current_format (AudioStreamID stream_id, + AudioStreamBasicDescription format) +{ + OSStatus status = noErr; + UInt32 propertySize = sizeof (AudioStreamBasicDescription); + + AudioObjectPropertyAddress formatAddress = { + kAudioStreamPropertyPhysicalFormat, + kAudioObjectPropertyScopeGlobal, + kAudioObjectPropertyElementMaster + }; + + status = AudioObjectSetPropertyData (stream_id, + &formatAddress, 0, NULL, propertySize, &format); + if (status != noErr) { + GST_ERROR ("failed to set current format: %" GST_FOURCC_FORMAT, + GST_FOURCC_ARGS (status)); + return FALSE; + } + + return TRUE; +} + +static inline AudioStreamRangedDescription * +_audio_stream_get_formats (AudioStreamID stream_id, gint * nformats) +{ + OSStatus status = noErr; + UInt32 propertySize = 0; + AudioStreamRangedDescription *formats = NULL; + + AudioObjectPropertyAddress formatsAddress = { + kAudioStreamPropertyAvailablePhysicalFormats, + kAudioObjectPropertyScopeGlobal, + kAudioObjectPropertyElementMaster + }; + + status = AudioObjectGetPropertyDataSize (stream_id, + &formatsAddress, 0, NULL, &propertySize); + if (status != noErr) { + GST_WARNING ("failed getting number of stream formats: %" + GST_FOURCC_FORMAT, GST_FOURCC_ARGS (status)); + return NULL; + } + + *nformats = propertySize / sizeof (AudioStreamRangedDescription); + + formats = (AudioStreamRangedDescription *) g_malloc (propertySize); + if (formats) { + status = AudioObjectGetPropertyData (stream_id, + &formatsAddress, 0, NULL, &propertySize, formats); + if (status != noErr) { + GST_WARNING ("failed getting the list of stream formats: %" + GST_FOURCC_FORMAT, GST_FOURCC_ARGS (status)); + g_free (formats); + *nformats = 0; + return NULL; + } + } + return formats; +} + +static inline gboolean +_audio_stream_is_spdif_avail (AudioStreamID stream_id) +{ + AudioStreamRangedDescription *formats; + gint i, nformats = 0; + gboolean res = FALSE; + + formats = _audio_stream_get_formats (stream_id, &nformats); + GST_DEBUG ("found %d stream formats", nformats); + + if (formats) { + GST_DEBUG ("formats supported on stream ID: %u", (unsigned) stream_id); + + for (i = 0; i < nformats; i++) { + GST_DEBUG (" " CORE_AUDIO_FORMAT, + CORE_AUDIO_FORMAT_ARGS (formats[i].mFormat)); + + if (CORE_AUDIO_FORMAT_IS_SPDIF (formats[i])) { + res = TRUE; + } + } + g_free (formats); + } + + return res; +} + +static OSStatus +_audio_stream_format_listener (AudioObjectID inObjectID, + UInt32 inNumberAddresses, + const AudioObjectPropertyAddress inAddresses[], void *inClientData) +{ + OSStatus status = noErr; + guint i; + PropertyMutex *prop_mutex = inClientData; + + for (i = 0; i < inNumberAddresses; i++) { + if (inAddresses[i].mSelector == kAudioStreamPropertyPhysicalFormat) { + g_mutex_lock (prop_mutex->lock); + g_cond_signal (prop_mutex->cond); + g_mutex_unlock (prop_mutex->lock); + break; + } + } + return (status); +} + +static gboolean +_audio_stream_change_format (AudioStreamID stream_id, + AudioStreamBasicDescription format) +{ + OSStatus status = noErr; + gint i; + gboolean ret = FALSE; + AudioStreamBasicDescription cformat; + PropertyMutex prop_mutex; + + AudioObjectPropertyAddress formatAddress = { + kAudioStreamPropertyPhysicalFormat, + kAudioObjectPropertyScopeGlobal, + kAudioObjectPropertyElementMaster + }; + + GST_DEBUG ("setting stream format: " CORE_AUDIO_FORMAT, + CORE_AUDIO_FORMAT_ARGS (format)); + + /* Condition because SetProperty is asynchronous */ + prop_mutex.lock = g_mutex_new (); + prop_mutex.cond = g_cond_new (); + + g_mutex_lock (prop_mutex.lock); + + /* Install the property listener to serialize the operations */ + status = AudioObjectAddPropertyListener (stream_id, &formatAddress, + _audio_stream_format_listener, (void *) &prop_mutex); + if (status != noErr) { + GST_ERROR ("AudioObjectAddPropertyListener failed: %" + GST_FOURCC_FORMAT, GST_FOURCC_ARGS (status)); + goto done; + } + + /* Change the format */ + if (!_audio_stream_set_current_format (stream_id, format)) { + goto done; + } + + /* The AudioObjectSetProperty is not only asynchronous + * it is also not atomic in its behaviour. + * Therefore we check 4 times before we really give up. */ + for (i = 0; i < 4; i++) { + GTimeVal timeout; + + g_get_current_time (&timeout); + g_time_val_add (&timeout, 250000); + + if (!g_cond_timed_wait (prop_mutex.cond, prop_mutex.lock, &timeout)) { + GST_LOG ("timeout..."); + } + + if (_audio_stream_get_current_format (stream_id, &cformat)) { + GST_DEBUG ("current stream format: " CORE_AUDIO_FORMAT, + CORE_AUDIO_FORMAT_ARGS (cformat)); + + if (cformat.mSampleRate == format.mSampleRate && + cformat.mFormatID == format.mFormatID && + cformat.mFramesPerPacket == format.mFramesPerPacket) { + /* The right format is now active */ + break; + } + } + } + + if (cformat.mSampleRate != format.mSampleRate || + cformat.mFormatID != format.mFormatID || + cformat.mFramesPerPacket != format.mFramesPerPacket) { + goto done; + } + + ret = TRUE; + +done: + /* Removing the property listener */ + status = AudioObjectRemovePropertyListener (stream_id, + &formatAddress, _audio_stream_format_listener, (void *) &prop_mutex); + if (status != noErr) { + GST_ERROR ("AudioObjectRemovePropertyListener failed: %" + GST_FOURCC_FORMAT, GST_FOURCC_ARGS (status)); + } + /* Destroy the lock and condition */ + g_mutex_unlock (prop_mutex.lock); + g_mutex_free (prop_mutex.lock); + g_cond_free (prop_mutex.cond); + + return ret; +} + +static OSStatus +_audio_stream_hardware_changed_listener (AudioObjectID inObjectID, + UInt32 inNumberAddresses, + const AudioObjectPropertyAddress inAddresses[], void *inClientData) +{ + OSStatus status = noErr; + guint i; + GstCoreAudio *core_audio = inClientData; + + for (i = 0; i < inNumberAddresses; i++) { + if (inAddresses[i].mSelector == kAudioDevicePropertyDeviceHasChanged) { + if (!gst_core_audio_audio_device_is_spdif_avail (core_audio->device_id)) { + GstOsxAudioSink *sink = + GST_OSX_AUDIO_SINK (GST_OBJECT_PARENT (core_audio->osxbuf)); + GST_ELEMENT_ERROR (sink, RESOURCE, FAILED, + ("SPDIF output no longer available"), + ("Audio device is reporting that SPDIF output isn't available")); + } + break; + } + } + return (status); +} + +static inline gboolean +_monitorize_spdif (GstCoreAudio * core_audio) +{ + OSStatus status = noErr; + gboolean ret = TRUE; + + AudioObjectPropertyAddress propAddress = { + kAudioDevicePropertyDeviceHasChanged, + kAudioObjectPropertyScopeGlobal, + kAudioObjectPropertyElementMaster + }; + + /* Install the property listener */ + status = AudioObjectAddPropertyListener (core_audio->device_id, + &propAddress, _audio_stream_hardware_changed_listener, + (void *) core_audio); + if (status != noErr) { + GST_ERROR_OBJECT (core_audio->osxbuf, + "AudioObjectAddPropertyListener failed: %" + GST_FOURCC_FORMAT, GST_FOURCC_ARGS (status)); + ret = FALSE; + } + + return ret; +} + +static inline gboolean +_unmonitorize_spdif (GstCoreAudio * core_audio) +{ + OSStatus status = noErr; + gboolean ret = TRUE; + + AudioObjectPropertyAddress propAddress = { + kAudioDevicePropertyDeviceHasChanged, + kAudioObjectPropertyScopeGlobal, + kAudioObjectPropertyElementMaster + }; + + /* Remove the property listener */ + status = AudioObjectRemovePropertyListener (core_audio->device_id, + &propAddress, _audio_stream_hardware_changed_listener, + (void *) core_audio); + if (status != noErr) { + GST_ERROR_OBJECT (core_audio->osxbuf, + "AudioObjectRemovePropertyListener failed: %" + GST_FOURCC_FORMAT, GST_FOURCC_ARGS (status)); + ret = FALSE; + } + + return ret; +} + +static inline gboolean +_open_spdif (GstCoreAudio * core_audio) +{ + gboolean res = FALSE; + pid_t hog_pid, own_pid = getpid (); + + /* We need the device in exclusive and disable the mixing */ + hog_pid = _audio_device_get_hog (core_audio->device_id); + + if (hog_pid != -1 && hog_pid != own_pid) { + GST_DEBUG_OBJECT (core_audio, + "device is currently in use by another application"); + goto done; + } + + if (_audio_device_set_hog (core_audio->device_id, own_pid)) { + core_audio->hog_pid = own_pid; + } + + if (_audio_device_set_mixing (core_audio->device_id, FALSE)) { + GST_DEBUG_OBJECT (core_audio, "disabled mixing on the device"); + core_audio->disabled_mixing = TRUE; + } + + res = TRUE; +done: + return res; +} + +static inline gboolean +_close_spdif (GstCoreAudio * core_audio) +{ + pid_t hog_pid; + + _unmonitorize_spdif (core_audio); + + if (core_audio->revert_format) { + if (!_audio_stream_change_format (core_audio->stream_id, + core_audio->original_format)) { + GST_WARNING_OBJECT (core_audio->osxbuf, "Format revert failed"); + } + core_audio->revert_format = FALSE; + } + + if (core_audio->disabled_mixing) { + _audio_device_set_mixing (core_audio->device_id, TRUE); + core_audio->disabled_mixing = FALSE; + } + + if (core_audio->hog_pid != -1) { + hog_pid = _audio_device_get_hog (core_audio->device_id); + if (hog_pid == getpid ()) { + if (_audio_device_set_hog (core_audio->device_id, -1)) { + core_audio->hog_pid = -1; + } + } + } + + return TRUE; +} + +static OSStatus +_io_proc_spdif (AudioDeviceID inDevice, + const AudioTimeStamp * inNow, + const void *inInputData, + const AudioTimeStamp * inTimestamp, + AudioBufferList * bufferList, + const AudioTimeStamp * inOutputTime, GstCoreAudio * core_audio) +{ + OSStatus status; + + status = core_audio->element->io_proc (core_audio->osxbuf, NULL, inTimestamp, + 0, 0, bufferList); + + return status; +} + +static inline gboolean +_acquire_spdif (GstCoreAudio * core_audio, AudioStreamBasicDescription format) +{ + AudioStreamID *streams = NULL; + gint i, j, nstreams = 0; + gboolean ret = FALSE; + + if (!_open_spdif (core_audio)) + goto done; + + streams = _audio_device_get_streams (core_audio->device_id, &nstreams); + + for (i = 0; i < nstreams; i++) { + AudioStreamRangedDescription *formats = NULL; + gint nformats = 0; + + formats = _audio_stream_get_formats (streams[i], &nformats); + + if (formats) { + gboolean is_spdif = FALSE; + + /* Check if one of the supported formats is a digital format */ + for (j = 0; j < nformats; j++) { + if (CORE_AUDIO_FORMAT_IS_SPDIF (formats[j])) { + is_spdif = TRUE; + break; + } + } + + if (is_spdif) { + /* if this stream supports a digital (cac3) format, + * then go set it. */ + gint requested_rate_format = -1; + gint current_rate_format = -1; + gint backup_rate_format = -1; + + core_audio->stream_id = streams[i]; + core_audio->stream_idx = i; + + if (!core_audio->revert_format) { + if (!_audio_stream_get_current_format (core_audio->stream_id, + &core_audio->original_format)) { + GST_WARNING_OBJECT (core_audio->osxbuf, + "format could not be saved"); + g_free (formats); + continue; + } + core_audio->revert_format = TRUE; + } + + for (j = 0; j < nformats; j++) { + if (CORE_AUDIO_FORMAT_IS_SPDIF (formats[j])) { + GST_LOG_OBJECT (core_audio->osxbuf, + "found stream format: " CORE_AUDIO_FORMAT, + CORE_AUDIO_FORMAT_ARGS (formats[j].mFormat)); + + if (formats[j].mFormat.mSampleRate == format.mSampleRate) { + requested_rate_format = j; + break; + } else if (formats[j].mFormat.mSampleRate == + core_audio->original_format.mSampleRate) { + current_rate_format = j; + } else { + if (backup_rate_format < 0 || + formats[j].mFormat.mSampleRate > + formats[backup_rate_format].mFormat.mSampleRate) { + backup_rate_format = j; + } + } + } + } + + if (requested_rate_format >= 0) { + /* We prefer to output at the rate of the original audio */ + core_audio->stream_format = formats[requested_rate_format].mFormat; + } else if (current_rate_format >= 0) { + /* If not possible, we will try to use the current rate */ + core_audio->stream_format = formats[current_rate_format].mFormat; + } else { + /* And if we have to, any digital format will be just + * fine (highest rate possible) */ + core_audio->stream_format = formats[backup_rate_format].mFormat; + } + } + g_free (formats); + } + } + g_free (streams); + + GST_DEBUG_OBJECT (core_audio, + "original stream format: " CORE_AUDIO_FORMAT, + CORE_AUDIO_FORMAT_ARGS (core_audio->original_format)); + + if (!_audio_stream_change_format (core_audio->stream_id, + core_audio->stream_format)) + goto done; + + ret = TRUE; + +done: + return ret; +} + +static inline void +_remove_render_spdif_callback (GstCoreAudio * core_audio) +{ + OSStatus status; + + /* Deactivate the render callback by calling + * AudioDeviceDestroyIOProcID */ + status = + AudioDeviceDestroyIOProcID (core_audio->device_id, core_audio->procID); + if (status != noErr) { + GST_ERROR_OBJECT (core_audio->osxbuf, + "AudioDeviceDestroyIOProcID failed: %" + GST_FOURCC_FORMAT, GST_FOURCC_ARGS (status)); + } + + GST_DEBUG_OBJECT (core_audio, + "osx ring buffer removed ioproc ID: %p device_id %lu", + core_audio->procID, (gulong) core_audio->device_id); + + /* We're deactivated.. */ + core_audio->procID = 0; + core_audio->io_proc_needs_deactivation = FALSE; + core_audio->io_proc_active = FALSE; +} + +static inline gboolean +_io_proc_spdif_start (GstCoreAudio * core_audio) +{ + OSErr status; + + GST_DEBUG_OBJECT (core_audio, + "osx ring buffer start ioproc ID: %p device_id %lu", + core_audio->procID, (gulong) core_audio->device_id); + + if (!core_audio->io_proc_active) { + /* Add IOProc callback */ + status = AudioDeviceCreateIOProcID (core_audio->device_id, + (AudioDeviceIOProc) _io_proc_spdif, + (void *) core_audio, &core_audio->procID); + if (status != noErr) { + GST_ERROR_OBJECT (core_audio->osxbuf, + ":AudioDeviceCreateIOProcID failed: %" + GST_FOURCC_FORMAT, GST_FOURCC_ARGS (status)); + return FALSE; + } + core_audio->io_proc_active = TRUE; + } + + core_audio->io_proc_needs_deactivation = FALSE; + + /* Start device */ + status = AudioDeviceStart (core_audio->device_id, core_audio->procID); + if (status != noErr) { + GST_ERROR_OBJECT (core_audio->osxbuf, + "AudioDeviceStart failed: %" GST_FOURCC_FORMAT, + GST_FOURCC_ARGS (status)); + return FALSE; + } + return TRUE; +} + +static inline gboolean +_io_proc_spdif_stop (GstCoreAudio * core_audio) +{ + OSErr status; + + /* Stop device */ + status = AudioDeviceStop (core_audio->device_id, core_audio->procID); + if (status != noErr) { + GST_ERROR_OBJECT (core_audio->osxbuf, + "AudioDeviceStop failed: %" GST_FOURCC_FORMAT, + GST_FOURCC_ARGS (status)); + } + + GST_DEBUG_OBJECT (core_audio, + "osx ring buffer stop ioproc ID: %p device_id %lu", + core_audio->procID, (gulong) core_audio->device_id); + + if (core_audio->io_proc_active) { + _remove_render_spdif_callback (core_audio); + } + + _close_spdif (core_audio); + + return TRUE; +} + + +/*********************** + * Implementation * + **********************/ + +static gboolean +gst_core_audio_open_impl (GstCoreAudio * core_audio) +{ + /* The following is needed to instruct HAL to create their own + * thread to handle the notifications. */ + _audio_system_set_runloop (NULL); + + /* Create a HALOutput AudioUnit. + * This is the lowest-level output API that is actually sensibly + * usable (the lower level ones require that you do + * channel-remapping yourself, and the CoreAudio channel mapping + * is sufficiently complex that doing so would be very difficult) + * + * Note that for input we request an output unit even though + * we will do input with it. + * http://developer.apple.com/technotes/tn2002/tn2091.html + */ + return gst_core_audio_open_device (core_audio, kAudioUnitSubType_HALOutput, + "HALOutput"); +} + +static gboolean +gst_core_audio_start_processing_impl (GstCoreAudio * core_audio) +{ + if (core_audio->is_passthrough) { + return _io_proc_spdif_start (core_audio); + } else { + return gst_core_audio_io_proc_start (core_audio); + } +} + +static gboolean +gst_core_audio_pause_processing_impl (GstCoreAudio * core_audio) +{ + if (core_audio->is_passthrough) { + GST_DEBUG_OBJECT (core_audio, + "osx ring buffer pause ioproc ID: %p device_id %lu", + core_audio->procID, (gulong) core_audio->device_id); + + if (core_audio->io_proc_active) { + _remove_render_spdif_callback (core_audio); + } + } else { + GST_DEBUG_OBJECT (core_audio, + "osx ring buffer pause ioproc: %p device_id %lu", + core_audio->element->io_proc, (gulong) core_audio->device_id); + if (core_audio->io_proc_active) { + /* CoreAudio isn't threadsafe enough to do this here; + * we must deactivate the render callback elsewhere. See: + * http://lists.apple.com/archives/Coreaudio-api/2006/Mar/msg00010.html + */ + core_audio->io_proc_needs_deactivation = TRUE; + } + } + return TRUE; +} + +static gboolean +gst_core_audio_stop_processing_impl (GstCoreAudio * core_audio) +{ + if (core_audio->is_passthrough) { + _io_proc_spdif_stop (core_audio); + } else { + gst_core_audio_io_proc_stop (core_audio); + } + + return TRUE; +} + +static gboolean +gst_core_audio_get_samples_and_latency_impl (GstCoreAudio * core_audio, + gdouble rate, guint * samples, gdouble * latency) +{ + OSStatus status; + UInt32 size = sizeof (double); + + if (core_audio->is_passthrough) { + *samples = _audio_device_get_latency (core_audio->device_id); + *samples += _audio_stream_get_latency (core_audio->stream_id); + *latency = (double) *samples / rate; + } else { + status = AudioUnitGetProperty (core_audio->audiounit, kAudioUnitProperty_Latency, kAudioUnitScope_Global, 0, /* N/A for global */ + latency, &size); + + if (status) { + GST_WARNING_OBJECT (core_audio->osxbuf, "Failed to get latency: %" + GST_FOURCC_FORMAT, GST_FOURCC_ARGS (status)); + *samples = 0; + return FALSE; + } + + *samples = *latency * rate; + } + return TRUE; +} + +static gboolean +gst_core_audio_initialize_impl (GstCoreAudio * core_audio, + AudioStreamBasicDescription format, GstCaps * caps, + gboolean is_passthrough, guint32 * frame_size) +{ + gboolean ret = FALSE; + + core_audio->is_passthrough = is_passthrough; + if (is_passthrough) { + if (!_acquire_spdif (core_audio, format)) + goto done; + _monitorize_spdif (core_audio); + } else { + OSStatus status; + UInt32 propertySize; + + core_audio->stream_idx = 0; + if (!gst_core_audio_set_format (core_audio, format)) + goto done; + + if (!gst_core_audio_set_channels_layout (core_audio, + format.mChannelsPerFrame, caps)) + goto done; + + if (!gst_core_audio_bind_device (core_audio)) + goto done; + + if (core_audio->is_src) { + propertySize = sizeof (*frame_size); + status = AudioUnitGetProperty (core_audio->audiounit, kAudioDevicePropertyBufferFrameSize, kAudioUnitScope_Global, 0, /* N/A for global */ + frame_size, &propertySize); + + if (status) { + GST_WARNING_OBJECT (core_audio->osxbuf, "Failed to get frame size: %" + GST_FOURCC_FORMAT, GST_FOURCC_ARGS (status)); + goto done; + } + } + } + + ret = TRUE; + +done: + if (ret) { + GST_DEBUG_OBJECT (core_audio, "osxbuf ring buffer acquired"); + } + return ret; +} + +static gboolean +gst_core_audio_select_device_impl (AudioDeviceID * device_id) +{ + AudioDeviceID *devices = NULL; + AudioDeviceID default_device_id = 0; + AudioChannelLayout *channel_layout; + gint i, ndevices = 0; + gboolean res = FALSE; + + devices = _audio_system_get_devices (&ndevices); + + if (ndevices < 1) { + GST_ERROR ("no audio output devices found"); + goto done; + } + + GST_DEBUG ("found %d audio device(s)", ndevices); + + for (i = 0; i < ndevices; i++) { + gchar *device_name; + + if ((device_name = _audio_device_get_name (devices[i]))) { + if (!_audio_device_has_output (devices[i])) { + GST_DEBUG ("Input Device ID: %u Name: %s", + (unsigned) devices[i], device_name); + } else { + GST_DEBUG ("Output Device ID: %u Name: %s", + (unsigned) devices[i], device_name); + + channel_layout = + gst_core_audio_audio_device_get_channel_layout (devices[i]); + if (channel_layout) { + gst_core_audio_dump_channel_layout (channel_layout); + g_free (channel_layout); + } + } + + g_free (device_name); + } + } + + /* Find the ID of the default output device */ + default_device_id = _audio_system_get_default_output (); + + /* Here we decide if selected device is valid or autoselect + * the default one when required */ + if (*device_id == kAudioDeviceUnknown) { + if (default_device_id != kAudioDeviceUnknown) { + *device_id = default_device_id; + res = TRUE; + } + } else { + for (i = 0; i < ndevices; i++) { + if (*device_id == devices[i]) { + res = TRUE; + } + } + + if (res && !_audio_device_is_alive (*device_id)) { + GST_ERROR ("Requested device not usable"); + res = FALSE; + goto done; + } + } + +done: + g_free (devices); + return res; +} + +static gboolean +gst_core_audio_audio_device_is_spdif_avail_impl (AudioDeviceID device_id) +{ + AudioStreamID *streams = NULL; + gint i, nstreams = 0; + gboolean res = FALSE; + + streams = _audio_device_get_streams (device_id, &nstreams); + GST_DEBUG ("found %d streams", nstreams); + if (streams) { + for (i = 0; i < nstreams; i++) { + if (_audio_stream_is_spdif_avail (streams[i])) { + res = TRUE; + } + } + + g_free (streams); + } + + return res; +} + +static gboolean +gst_core_audio_select_source_device_impl (AudioDeviceID * device_id) +{ + OSStatus status; + UInt32 propertySize; + + if (*device_id == kAudioDeviceUnknown) { + /* If no specific device has been selected by the user, then pick the + * default device */ + GST_DEBUG ("Selecting device for OSXAudioSrc"); + propertySize = sizeof (*device_id); + status = AudioHardwareGetProperty (kAudioHardwarePropertyDefaultInputDevice, + &propertySize, device_id); + + if (status) { + GST_WARNING ("AudioHardwareGetProperty returned %d", (int) status); + } else { + GST_DEBUG ("AudioHardwareGetProperty returned 0"); + } + + if (*device_id == kAudioDeviceUnknown) { + GST_WARNING ("AudioHardwareGetProperty: device_id is " + "kAudioDeviceUnknown"); + } + + GST_DEBUG ("AudioHardwareGetProperty: device_id is %lu", (long) *device_id); + } + + return TRUE; +} diff --git a/sys/osxaudio/gstosxringbuffer.c b/sys/osxaudio/gstosxringbuffer.c index 4c27a7cdb..fd84a92a7 100644 --- a/sys/osxaudio/gstosxringbuffer.c +++ b/sys/osxaudio/gstosxringbuffer.c @@ -43,8 +43,10 @@ * Boston, MA 02110-1301, USA. */ -#include <CoreAudio/CoreAudio.h> -#include <CoreServices/CoreServices.h> +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif + #include <gst/gst.h> #include <gst/audio/multichannel.h> #include "gstosxringbuffer.h" @@ -73,9 +75,6 @@ static gboolean gst_osx_ring_buffer_stop (GstRingBuffer * buf); static guint gst_osx_ring_buffer_delay (GstRingBuffer * buf); static GstRingBufferClass *ring_parent_class = NULL; -static void gst_osx_ring_buffer_remove_render_callback (GstOsxRingBuffer * - osxbuf); - static void gst_osx_ring_buffer_do_init (GType type) { @@ -130,15 +129,20 @@ static void gst_osx_ring_buffer_init (GstOsxRingBuffer * ringbuffer, GstOsxRingBufferClass * g_class) { - /* Nothing to do right now */ - ringbuffer->is_passthrough = FALSE; - ringbuffer->hog_pid = -1; - ringbuffer->disabled_mixing = FALSE; + ringbuffer->core_audio = gst_core_audio_new (GST_OBJECT (ringbuffer)); } static void gst_osx_ring_buffer_dispose (GObject * object) { + GstOsxRingBuffer *osxbuf; + + osxbuf = GST_OSX_RING_BUFFER (object); + + if (osxbuf->core_audio) { + g_object_unref (osxbuf->core_audio); + osxbuf->core_audio = NULL; + } G_OBJECT_CLASS (ring_parent_class)->dispose (object); } @@ -148,103 +152,6 @@ gst_osx_ring_buffer_finalize (GObject * object) G_OBJECT_CLASS (ring_parent_class)->finalize (object); } -static AudioUnit -gst_osx_ring_buffer_create_audio_unit (GstOsxRingBuffer * osxbuf, - gboolean input, AudioDeviceID device_id) -{ - ComponentDescription desc; - Component comp; - OSStatus status; - AudioUnit unit; - UInt32 enableIO; - AudioStreamBasicDescription asbd_in; - UInt32 propertySize; - - /* Create a HALOutput AudioUnit. - * This is the lowest-level output API that is actually sensibly - * usable (the lower level ones require that you do - * channel-remapping yourself, and the CoreAudio channel mapping - * is sufficiently complex that doing so would be very difficult) - * - * Note that for input we request an output unit even though - * we will do input with it. - * http://developer.apple.com/technotes/tn2002/tn2091.html - */ - desc.componentType = kAudioUnitType_Output; - desc.componentSubType = kAudioUnitSubType_HALOutput; - desc.componentManufacturer = kAudioUnitManufacturer_Apple; - desc.componentFlags = 0; - desc.componentFlagsMask = 0; - - comp = FindNextComponent (NULL, &desc); - if (comp == NULL) { - GST_WARNING_OBJECT (osxbuf, "Couldn't find HALOutput component"); - return NULL; - } - - status = OpenAComponent (comp, &unit); - - if (status) { - GST_ERROR_OBJECT (osxbuf, "Couldn't open HALOutput component %" - GST_FOURCC_FORMAT, GST_FOURCC_ARGS (status)); - return NULL; - } - - if (input) { - /* enable input */ - enableIO = 1; - status = AudioUnitSetProperty (unit, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Input, 1, /* 1 = input element */ - &enableIO, sizeof (enableIO)); - - if (status) { - CloseComponent (unit); - GST_WARNING_OBJECT (osxbuf, "Failed to enable input: %" - GST_FOURCC_FORMAT, GST_FOURCC_ARGS (status)); - return NULL; - } - - /* disable output */ - enableIO = 0; - status = AudioUnitSetProperty (unit, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Output, 0, /* 0 = output element */ - &enableIO, sizeof (enableIO)); - - if (status) { - CloseComponent (unit); - GST_WARNING_OBJECT (osxbuf, "Failed to disable output: %" - GST_FOURCC_FORMAT, GST_FOURCC_ARGS (status)); - return NULL; - } - } - - GST_DEBUG_OBJECT (osxbuf, "Created HALOutput AudioUnit: %p", unit); - - if (input) { - GstOsxAudioSrc *src = GST_OSX_AUDIO_SRC (GST_OBJECT_PARENT (osxbuf)); - - propertySize = sizeof (asbd_in); - status = AudioUnitGetProperty (unit, - kAudioUnitProperty_StreamFormat, - kAudioUnitScope_Input, 1, &asbd_in, &propertySize); - - if (status) { - CloseComponent (unit); - GST_WARNING_OBJECT (osxbuf, - "Unable to obtain device properties: %" GST_FOURCC_FORMAT, - GST_FOURCC_ARGS (status)); - return NULL; - } - - src->deviceChannels = asbd_in.mChannelsPerFrame; - } else { - GstOsxAudioSink *sink = GST_OSX_AUDIO_SINK (GST_OBJECT_PARENT (osxbuf)); - - /* needed for the sink's volume control */ - sink->audiounit = unit; - } - - return unit; -} - static gboolean gst_osx_ring_buffer_open_device (GstRingBuffer * buf) { @@ -252,18 +159,7 @@ gst_osx_ring_buffer_open_device (GstRingBuffer * buf) osxbuf = GST_OSX_RING_BUFFER (buf); - /* The following is needed to instruct HAL to create their own - * thread to handle the notifications. */ - _audio_system_set_runloop (NULL); - - osxbuf->audiounit = gst_osx_ring_buffer_create_audio_unit (osxbuf, - osxbuf->is_src, osxbuf->device_id); - - if (!osxbuf->audiounit) { - return FALSE; - } - - return TRUE; + return gst_core_audio_open (osxbuf->core_audio); } static gboolean @@ -272,581 +168,13 @@ gst_osx_ring_buffer_close_device (GstRingBuffer * buf) GstOsxRingBuffer *osxbuf; osxbuf = GST_OSX_RING_BUFFER (buf); - CloseComponent (osxbuf->audiounit); - osxbuf->audiounit = NULL; - - return TRUE; -} - -static AudioChannelLabel -gst_audio_channel_position_to_coreaudio_channel_label (GstAudioChannelPosition - position, int channel) -{ - switch (position) { - case GST_AUDIO_CHANNEL_POSITION_NONE: - return kAudioChannelLabel_Discrete_0 | channel; - case GST_AUDIO_CHANNEL_POSITION_FRONT_MONO: - return kAudioChannelLabel_Mono; - case GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT: - return kAudioChannelLabel_Left; - case GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT: - return kAudioChannelLabel_Right; - case GST_AUDIO_CHANNEL_POSITION_REAR_CENTER: - return kAudioChannelLabel_CenterSurround; - case GST_AUDIO_CHANNEL_POSITION_REAR_LEFT: - return kAudioChannelLabel_LeftSurround; - case GST_AUDIO_CHANNEL_POSITION_REAR_RIGHT: - return kAudioChannelLabel_RightSurround; - case GST_AUDIO_CHANNEL_POSITION_LFE: - return kAudioChannelLabel_LFEScreen; - case GST_AUDIO_CHANNEL_POSITION_FRONT_CENTER: - return kAudioChannelLabel_Center; - case GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER: - return kAudioChannelLabel_Center; // ??? - case GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER: - return kAudioChannelLabel_Center; // ??? - case GST_AUDIO_CHANNEL_POSITION_SIDE_LEFT: - return kAudioChannelLabel_LeftSurroundDirect; - case GST_AUDIO_CHANNEL_POSITION_SIDE_RIGHT: - return kAudioChannelLabel_RightSurroundDirect; - default: - return kAudioChannelLabel_Unknown; - } -} - -static AudioBufferList * -buffer_list_alloc (int channels, int size) -{ - AudioBufferList *list; - int total_size; - int n; - - total_size = sizeof (AudioBufferList) + 1 * sizeof (AudioBuffer); - list = (AudioBufferList *) g_malloc (total_size); - - list->mNumberBuffers = 1; - for (n = 0; n < (int) list->mNumberBuffers; ++n) { - list->mBuffers[n].mNumberChannels = channels; - list->mBuffers[n].mDataByteSize = size; - list->mBuffers[n].mData = g_malloc (size); - } - - return list; -} - -static void -buffer_list_free (AudioBufferList * list) -{ - int n; - - for (n = 0; n < (int) list->mNumberBuffers; ++n) { - if (list->mBuffers[n].mData) - g_free (list->mBuffers[n].mData); - } - - g_free (list); -} - -typedef struct -{ - GMutex *lock; - GCond *cond; -} PropertyMutex; - -static OSStatus -_audio_stream_format_listener (AudioObjectID inObjectID, - UInt32 inNumberAddresses, - const AudioObjectPropertyAddress inAddresses[], void *inClientData) -{ - OSStatus status = noErr; - guint i; - PropertyMutex *prop_mutex = inClientData; - - for (i = 0; i < inNumberAddresses; i++) { - if (inAddresses[i].mSelector == kAudioStreamPropertyPhysicalFormat) { - g_mutex_lock (prop_mutex->lock); - g_cond_signal (prop_mutex->cond); - g_mutex_unlock (prop_mutex->lock); - break; - } - } - return (status); -} - -static gboolean -_audio_stream_change_format (AudioStreamID stream_id, - AudioStreamBasicDescription format) -{ - OSStatus status = noErr; - gint i; - gboolean ret = FALSE; - AudioStreamBasicDescription cformat; - PropertyMutex prop_mutex; - - AudioObjectPropertyAddress formatAddress = { - kAudioStreamPropertyPhysicalFormat, - kAudioObjectPropertyScopeGlobal, - kAudioObjectPropertyElementMaster - }; - - GST_DEBUG ("setting stream format: " CORE_AUDIO_FORMAT, - CORE_AUDIO_FORMAT_ARGS (format)); - - /* Condition because SetProperty is asynchronous */ - prop_mutex.lock = g_mutex_new (); - prop_mutex.cond = g_cond_new (); - - g_mutex_lock (prop_mutex.lock); - - /* Install the property listener to serialize the operations */ - status = AudioObjectAddPropertyListener (stream_id, &formatAddress, - _audio_stream_format_listener, (void *) &prop_mutex); - if (status != noErr) { - GST_ERROR ("AudioObjectAddPropertyListener failed: %" - GST_FOURCC_FORMAT, GST_FOURCC_ARGS (status)); - goto done; - } - - /* Change the format */ - if (!_audio_stream_set_current_format (stream_id, format)) { - goto done; - } - - /* The AudioObjectSetProperty is not only asynchronous - * it is also not atomic in its behaviour. - * Therefore we check 4 times before we really give up. */ - for (i = 0; i < 4; i++) { - GTimeVal timeout; - - g_get_current_time (&timeout); - g_time_val_add (&timeout, 250000); - - if (!g_cond_timed_wait (prop_mutex.cond, prop_mutex.lock, &timeout)) { - GST_LOG ("timeout..."); - } - - if (_audio_stream_get_current_format (stream_id, &cformat)) { - GST_DEBUG ("current stream format: " CORE_AUDIO_FORMAT, - CORE_AUDIO_FORMAT_ARGS (cformat)); - - if (cformat.mSampleRate == format.mSampleRate && - cformat.mFormatID == format.mFormatID && - cformat.mFramesPerPacket == format.mFramesPerPacket) { - /* The right format is now active */ - break; - } - } - } - - if (cformat.mSampleRate != format.mSampleRate || - cformat.mFormatID != format.mFormatID || - cformat.mFramesPerPacket != format.mFramesPerPacket) { - goto done; - } - - ret = TRUE; - -done: - /* Removing the property listener */ - status = AudioObjectRemovePropertyListener (stream_id, - &formatAddress, _audio_stream_format_listener, (void *) &prop_mutex); - if (status != noErr) { - GST_ERROR ("AudioObjectRemovePropertyListener failed: %" - GST_FOURCC_FORMAT, GST_FOURCC_ARGS (status)); - } - /* Destroy the lock and condition */ - g_mutex_unlock (prop_mutex.lock); - g_mutex_free (prop_mutex.lock); - g_cond_free (prop_mutex.cond); - - return ret; -} - -static OSStatus -_audio_stream_hardware_changed_listener (AudioObjectID inObjectID, - UInt32 inNumberAddresses, - const AudioObjectPropertyAddress inAddresses[], void *inClientData) -{ - OSStatus status = noErr; - guint i; - GstOsxRingBuffer *osxbuf = inClientData; - - for (i = 0; i < inNumberAddresses; i++) { - if (inAddresses[i].mSelector == kAudioDevicePropertyDeviceHasChanged) { - if (!_audio_device_is_spdif_avail (osxbuf->device_id)) { - GstOsxAudioSink *sink = GST_OSX_AUDIO_SINK (GST_OBJECT_PARENT (osxbuf)); - GST_ELEMENT_ERROR (sink, RESOURCE, FAILED, - ("SPDIF output no longer available"), - ("Audio device is reporting that SPDIF output isn't available")); - } - break; - } - } - return (status); -} - -static gboolean -gst_osx_ring_buffer_monitorize_spdif (GstOsxRingBuffer * osxbuf) -{ - OSStatus status = noErr; - gboolean ret = TRUE; - - AudioObjectPropertyAddress propAddress = { - kAudioDevicePropertyDeviceHasChanged, - kAudioObjectPropertyScopeGlobal, - kAudioObjectPropertyElementMaster - }; - - /* Install the property listener */ - status = AudioObjectAddPropertyListener (osxbuf->device_id, - &propAddress, _audio_stream_hardware_changed_listener, (void *) osxbuf); - if (status != noErr) { - GST_ERROR ("AudioObjectAddPropertyListener failed: %" - GST_FOURCC_FORMAT, GST_FOURCC_ARGS (status)); - ret = FALSE; - } - - return ret; -} - -static gboolean -gst_osx_ring_buffer_unmonitorize_spdif (GstOsxRingBuffer * osxbuf) -{ - OSStatus status = noErr; - gboolean ret = TRUE; - - AudioObjectPropertyAddress propAddress = { - kAudioDevicePropertyDeviceHasChanged, - kAudioObjectPropertyScopeGlobal, - kAudioObjectPropertyElementMaster - }; - - /* Remove the property listener */ - status = AudioObjectRemovePropertyListener (osxbuf->device_id, - &propAddress, _audio_stream_hardware_changed_listener, (void *) osxbuf); - if (status != noErr) { - GST_ERROR ("AudioObjectRemovePropertyListener failed: %" - GST_FOURCC_FORMAT, GST_FOURCC_ARGS (status)); - ret = FALSE; - } - - return ret; -} - -static gboolean -gst_osx_ring_buffer_open_spdif (GstOsxRingBuffer * osxbuf) -{ - gboolean res = FALSE; - pid_t hog_pid, own_pid = getpid (); - - /* We need the device in exclusive and disable the mixing */ - hog_pid = _audio_device_get_hog (osxbuf->device_id); - - if (hog_pid != -1 && hog_pid != own_pid) { - GST_DEBUG_OBJECT (osxbuf, - "device is currently in use by another application"); - goto done; - } - - if (_audio_device_set_hog (osxbuf->device_id, own_pid)) { - osxbuf->hog_pid = own_pid; - } - - if (_audio_device_set_mixing (osxbuf->device_id, FALSE)) { - GST_DEBUG_OBJECT (osxbuf, "disabled mixing on the device"); - osxbuf->disabled_mixing = TRUE; - } - - res = TRUE; -done: - return res; -} - -static gboolean -gst_osx_ring_buffer_close_spdif (GstOsxRingBuffer * osxbuf) -{ - pid_t hog_pid; - - gst_osx_ring_buffer_unmonitorize_spdif (osxbuf); - - if (osxbuf->revert_format) { - if (!_audio_stream_change_format (osxbuf->stream_id, - osxbuf->original_format)) { - GST_WARNING ("Format revert failed"); - } - osxbuf->revert_format = FALSE; - } - - if (osxbuf->disabled_mixing) { - _audio_device_set_mixing (osxbuf->device_id, TRUE); - osxbuf->disabled_mixing = FALSE; - } - - if (osxbuf->hog_pid != -1) { - hog_pid = _audio_device_get_hog (osxbuf->device_id); - if (hog_pid == getpid ()) { - if (_audio_device_set_hog (osxbuf->device_id, -1)) { - osxbuf->hog_pid = -1; - } - } - } - - return TRUE; -} - -static OSStatus -gst_osx_ring_buffer_io_proc_spdif (AudioDeviceID inDevice, - const AudioTimeStamp * inNow, - const void *inInputData, - const AudioTimeStamp * inTimestamp, - AudioBufferList * bufferList, - const AudioTimeStamp * inOutputTime, GstOsxRingBuffer * osxbuf) -{ - OSStatus status; - - status = osxbuf->element->io_proc (osxbuf, NULL, inTimestamp, 0, 0, - bufferList); - - return status; -} - -static gboolean -gst_osx_ring_buffer_acquire_spdif (GstOsxRingBuffer * osxbuf, - AudioStreamBasicDescription format) -{ - AudioStreamID *streams = NULL; - gint i, j, nstreams = 0; - gboolean ret = FALSE; - - if (!gst_osx_ring_buffer_open_spdif (osxbuf)) - goto done; - - streams = _audio_device_get_streams (osxbuf->device_id, &nstreams); - - for (i = 0; i < nstreams; i++) { - AudioStreamRangedDescription *formats = NULL; - gint nformats = 0; - - formats = _audio_stream_get_formats (streams[i], &nformats); - - if (formats) { - gboolean is_spdif = FALSE; - - /* Check if one of the supported formats is a digital format */ - for (j = 0; j < nformats; j++) { - if (CORE_AUDIO_FORMAT_IS_SPDIF (formats[j])) { - is_spdif = TRUE; - break; - } - } - - if (is_spdif) { - /* if this stream supports a digital (cac3) format, - * then go set it. */ - gint requested_rate_format = -1; - gint current_rate_format = -1; - gint backup_rate_format = -1; - - osxbuf->stream_id = streams[i]; - osxbuf->stream_idx = i; - - if (!osxbuf->revert_format) { - if (!_audio_stream_get_current_format (osxbuf->stream_id, - &osxbuf->original_format)) { - GST_WARNING ("format could not be saved"); - g_free (formats); - continue; - } - osxbuf->revert_format = TRUE; - } - - for (j = 0; j < nformats; j++) { - if (CORE_AUDIO_FORMAT_IS_SPDIF (formats[j])) { - GST_LOG ("found stream format: " CORE_AUDIO_FORMAT, - CORE_AUDIO_FORMAT_ARGS (formats[j].mFormat)); - - if (formats[j].mFormat.mSampleRate == format.mSampleRate) { - requested_rate_format = j; - break; - } else if (formats[j].mFormat.mSampleRate == - osxbuf->original_format.mSampleRate) { - current_rate_format = j; - } else { - if (backup_rate_format < 0 || - formats[j].mFormat.mSampleRate > - formats[backup_rate_format].mFormat.mSampleRate) { - backup_rate_format = j; - } - } - } - } - - if (requested_rate_format >= 0) { - /* We prefer to output at the rate of the original audio */ - osxbuf->stream_format = formats[requested_rate_format].mFormat; - } else if (current_rate_format >= 0) { - /* If not possible, we will try to use the current rate */ - osxbuf->stream_format = formats[current_rate_format].mFormat; - } else { - /* And if we have to, any digital format will be just - * fine (highest rate possible) */ - osxbuf->stream_format = formats[backup_rate_format].mFormat; - } - } - g_free (formats); - } - } - g_free (streams); - - GST_DEBUG ("original stream format: " CORE_AUDIO_FORMAT, - CORE_AUDIO_FORMAT_ARGS (osxbuf->original_format)); - - if (!_audio_stream_change_format (osxbuf->stream_id, osxbuf->stream_format)) - goto done; - - GST_DEBUG_OBJECT (osxbuf, "osx ring buffer acquired"); - - ret = TRUE; - -done: - return ret; -} - -static gboolean -gst_osx_ring_buffer_acquire_analog (GstOsxRingBuffer * osxbuf, - AudioStreamBasicDescription format, GstCaps * caps) -{ - /* Configure the output stream and allocate ringbuffer memory */ - AudioChannelLayout *layout = NULL; - OSStatus status; - UInt32 propertySize; - int channels = format.mChannelsPerFrame; - int layoutSize; - int element; - int i; - AudioUnitScope scope; - gboolean ret = FALSE; - GstStructure *structure; - GstAudioChannelPosition *positions; - UInt32 frameSize; - - /* Describe channels */ - layoutSize = sizeof (AudioChannelLayout) + - channels * sizeof (AudioChannelDescription); - layout = g_malloc (layoutSize); - - structure = gst_caps_get_structure (caps, 0); - positions = gst_audio_get_channel_positions (structure); - - layout->mChannelLayoutTag = kAudioChannelLayoutTag_UseChannelDescriptions; - layout->mChannelBitmap = 0; /* Not used */ - layout->mNumberChannelDescriptions = channels; - for (i = 0; i < channels; i++) { - if (positions) { - layout->mChannelDescriptions[i].mChannelLabel = - gst_audio_channel_position_to_coreaudio_channel_label (positions[i], - i); - } else { - /* Discrete channel numbers are ORed into this */ - layout->mChannelDescriptions[i].mChannelLabel = - kAudioChannelLabel_Discrete_0 | i; - } - - /* Others unused */ - layout->mChannelDescriptions[i].mChannelFlags = 0; - layout->mChannelDescriptions[i].mCoordinates[0] = 0.f; - layout->mChannelDescriptions[i].mCoordinates[1] = 0.f; - layout->mChannelDescriptions[i].mCoordinates[2] = 0.f; - } - - if (positions) { - g_free (positions); - positions = NULL; - } - - GST_DEBUG_OBJECT (osxbuf, "Setting format for AudioUnit"); - - scope = osxbuf->is_src ? kAudioUnitScope_Output : kAudioUnitScope_Input; - element = osxbuf->is_src ? 1 : 0; - - propertySize = sizeof (AudioStreamBasicDescription); - status = AudioUnitSetProperty (osxbuf->audiounit, - kAudioUnitProperty_StreamFormat, scope, element, &format, propertySize); - - if (status) { - GST_WARNING_OBJECT (osxbuf, - "Failed to set audio description: %" GST_FOURCC_FORMAT, - GST_FOURCC_ARGS (status)); - goto done; - } - - if (layoutSize) { - status = AudioUnitSetProperty (osxbuf->audiounit, - kAudioUnitProperty_AudioChannelLayout, - scope, element, layout, layoutSize); - if (status) { - GST_WARNING_OBJECT (osxbuf, - "Failed to set output channel layout: %" GST_FOURCC_FORMAT, - GST_FOURCC_ARGS (status)); - goto done; - } - } - - /* create AudioBufferList needed for recording */ - if (osxbuf->is_src) { - propertySize = sizeof (frameSize); - status = AudioUnitGetProperty (osxbuf->audiounit, kAudioDevicePropertyBufferFrameSize, kAudioUnitScope_Global, 0, /* N/A for global */ - &frameSize, &propertySize); - - if (status) { - GST_WARNING_OBJECT (osxbuf, "Failed to get frame size: %" - GST_FOURCC_FORMAT, GST_FOURCC_ARGS (status)); - goto done; - } - - osxbuf->recBufferList = buffer_list_alloc (channels, - frameSize * format.mBytesPerFrame); - } - - /* Specify which device we're using. */ - GST_DEBUG_OBJECT (osxbuf, "Bind AudioUnit to device %d", - (int) osxbuf->device_id); - status = AudioUnitSetProperty (osxbuf->audiounit, kAudioOutputUnitProperty_CurrentDevice, kAudioUnitScope_Global, 0, /* N/A for global */ - &osxbuf->device_id, sizeof (AudioDeviceID)); - if (status) { - GST_ERROR_OBJECT (osxbuf, "Failed binding to device: %" - GST_FOURCC_FORMAT, GST_FOURCC_ARGS (status)); - goto audiounit_error; - } - - /* Initialize the AudioUnit */ - status = AudioUnitInitialize (osxbuf->audiounit); - if (status) { - GST_ERROR_OBJECT (osxbuf, "Failed to initialise AudioUnit: %" - GST_FOURCC_FORMAT, GST_FOURCC_ARGS (status)); - goto audiounit_error; - } - - GST_DEBUG_OBJECT (osxbuf, "osx ring buffer acquired"); - - ret = TRUE; - -done: - g_free (layout); - return ret; - -audiounit_error: - if (osxbuf->recBufferList) { - buffer_list_free (osxbuf->recBufferList); - osxbuf->recBufferList = NULL; - } - return ret; + return gst_core_audio_close (osxbuf->core_audio); } static gboolean gst_osx_ring_buffer_acquire (GstRingBuffer * buf, GstRingBufferSpec * spec) { - gboolean ret = FALSE; + gboolean ret = FALSE, is_passthrough = FALSE; GstOsxRingBuffer *osxbuf; AudioStreamBasicDescription format; @@ -865,7 +193,7 @@ gst_osx_ring_buffer_acquire (GstRingBuffer * buf, GstRingBufferSpec * spec) format.mReserved = 0; spec->segsize = 6144; spec->segtotal = 10; - osxbuf->is_passthrough = TRUE; + is_passthrough = TRUE; } else { int width, depth; /* Fill out the audio description we're going to be using */ @@ -897,8 +225,7 @@ gst_osx_ring_buffer_acquire (GstRingBuffer * buf, GstRingBufferSpec * spec) (spec->latency_time * spec->rate / G_USEC_PER_SEC) * spec->bytes_per_sample; spec->segtotal = spec->buffer_time / spec->latency_time; - osxbuf->stream_idx = 0; - osxbuf->is_passthrough = FALSE; + is_passthrough = FALSE; } GST_DEBUG_OBJECT (osxbuf, "Format: " CORE_AUDIO_FORMAT, @@ -907,14 +234,8 @@ gst_osx_ring_buffer_acquire (GstRingBuffer * buf, GstRingBufferSpec * spec) buf->data = gst_buffer_new_and_alloc (spec->segtotal * spec->segsize); memset (GST_BUFFER_DATA (buf->data), 0, GST_BUFFER_SIZE (buf->data)); - if (osxbuf->is_passthrough) { - ret = gst_osx_ring_buffer_acquire_spdif (osxbuf, format); - if (ret) { - gst_osx_ring_buffer_monitorize_spdif (osxbuf); - } - } else { - ret = gst_osx_ring_buffer_acquire_analog (osxbuf, format, spec->caps); - } + ret = gst_core_audio_initialize (osxbuf->core_audio, format, spec->caps, + is_passthrough); if (!ret) { gst_buffer_unref (buf->data); @@ -933,221 +254,11 @@ gst_osx_ring_buffer_release (GstRingBuffer * buf) osxbuf = GST_OSX_RING_BUFFER (buf); - AudioUnitUninitialize (osxbuf->audiounit); + gst_core_audio_unitialize (osxbuf->core_audio); gst_buffer_unref (buf->data); buf->data = NULL; - if (osxbuf->recBufferList) { - buffer_list_free (osxbuf->recBufferList); - osxbuf->recBufferList = NULL; - } - - return TRUE; -} - -static OSStatus -gst_osx_ring_buffer_render_notify (GstOsxRingBuffer * osxbuf, - AudioUnitRenderActionFlags * ioActionFlags, - const AudioTimeStamp * inTimeStamp, - unsigned int inBusNumber, - unsigned int inNumberFrames, AudioBufferList * ioData) -{ - /* Before rendering a frame, we get the PreRender notification. - * Here, we detach the RenderCallback if we've been paused. - * - * This is necessary (rather than just directly detaching it) to - * work around some thread-safety issues in CoreAudio - */ - if ((*ioActionFlags) & kAudioUnitRenderAction_PreRender) { - if (osxbuf->io_proc_needs_deactivation) { - gst_osx_ring_buffer_remove_render_callback (osxbuf); - } - } - - return noErr; -} - -static void -gst_osx_ring_buffer_remove_render_callback (GstOsxRingBuffer * osxbuf) -{ - AURenderCallbackStruct input; - OSStatus status; - - /* Deactivate the render callback by calling SetRenderCallback - * with a NULL inputProc. - */ - input.inputProc = NULL; - input.inputProcRefCon = NULL; - - status = AudioUnitSetProperty (osxbuf->audiounit, kAudioUnitProperty_SetRenderCallback, kAudioUnitScope_Global, 0, /* N/A for global */ - &input, sizeof (input)); - - if (status) { - GST_WARNING_OBJECT (osxbuf, "Failed to remove render callback %" - GST_FOURCC_FORMAT, GST_FOURCC_ARGS (status)); - } - - /* Remove the RenderNotify too */ - status = AudioUnitRemoveRenderNotify (osxbuf->audiounit, - (AURenderCallback) gst_osx_ring_buffer_render_notify, osxbuf); - - if (status) { - GST_WARNING_OBJECT (osxbuf, - "Failed to remove render notify callback %" GST_FOURCC_FORMAT, - GST_FOURCC_ARGS (status)); - } - - /* We're deactivated.. */ - osxbuf->io_proc_needs_deactivation = FALSE; - osxbuf->io_proc_active = FALSE; -} - -static gboolean -gst_osx_ring_buffer_io_proc_start (GstOsxRingBuffer * osxbuf) -{ - OSStatus status; - AURenderCallbackStruct input; - AudioUnitPropertyID callback_type; - - GST_DEBUG ("osx ring buffer start ioproc: %p device_id %lu", - osxbuf->element->io_proc, (gulong) osxbuf->device_id); - if (!osxbuf->io_proc_active) { - callback_type = osxbuf->is_src ? - kAudioOutputUnitProperty_SetInputCallback : - kAudioUnitProperty_SetRenderCallback; - - input.inputProc = (AURenderCallback) osxbuf->element->io_proc; - input.inputProcRefCon = osxbuf; - - status = AudioUnitSetProperty (osxbuf->audiounit, callback_type, kAudioUnitScope_Global, 0, /* N/A for global */ - &input, sizeof (input)); - - if (status) { - GST_ERROR ("AudioUnitSetProperty failed: %" GST_FOURCC_FORMAT, - GST_FOURCC_ARGS (status)); - return FALSE; - } - // ### does it make sense to do this notify stuff for input mode? - status = AudioUnitAddRenderNotify (osxbuf->audiounit, - (AURenderCallback) gst_osx_ring_buffer_render_notify, osxbuf); - - if (status) { - GST_ERROR ("AudioUnitAddRenderNotify failed %" - GST_FOURCC_FORMAT, GST_FOURCC_ARGS (status)); - return FALSE; - } - - osxbuf->io_proc_active = TRUE; - } - - osxbuf->io_proc_needs_deactivation = FALSE; - - status = AudioOutputUnitStart (osxbuf->audiounit); - if (status) { - GST_ERROR ("AudioOutputUnitStart failed: %" - GST_FOURCC_FORMAT, GST_FOURCC_ARGS (status)); - return FALSE; - } - return TRUE; -} - -static gboolean -gst_osx_ring_buffer_io_proc_stop (GstOsxRingBuffer * osxbuf) -{ - OSErr status; - - GST_DEBUG ("osx ring buffer stop ioproc: %p device_id %lu", - osxbuf->element->io_proc, (gulong) osxbuf->device_id); - - status = AudioOutputUnitStop (osxbuf->audiounit); - if (status) { - GST_WARNING ("AudioOutputUnitStop failed: %" GST_FOURCC_FORMAT, - GST_FOURCC_ARGS (status)); - } - // ###: why is it okay to directly remove from here but not from pause() ? - if (osxbuf->io_proc_active) { - gst_osx_ring_buffer_remove_render_callback (osxbuf); - } - return TRUE; -} - -static void -gst_osx_ring_buffer_remove_render_spdif_callback (GstOsxRingBuffer * osxbuf) -{ - OSStatus status; - - /* Deactivate the render callback by calling - * AudioDeviceDestroyIOProcID */ - status = AudioDeviceDestroyIOProcID (osxbuf->device_id, osxbuf->procID); - if (status != noErr) { - GST_ERROR ("AudioDeviceDestroyIOProcID failed: %" - GST_FOURCC_FORMAT, GST_FOURCC_ARGS (status)); - } - - GST_DEBUG ("osx ring buffer removed ioproc ID: %p device_id %lu", - osxbuf->procID, (gulong) osxbuf->device_id); - - /* We're deactivated.. */ - osxbuf->procID = 0; - osxbuf->io_proc_needs_deactivation = FALSE; - osxbuf->io_proc_active = FALSE; -} - -static gboolean -gst_osx_ring_buffer_io_proc_spdif_start (GstOsxRingBuffer * osxbuf) -{ - OSErr status; - - GST_DEBUG ("osx ring buffer start ioproc ID: %p device_id %lu", - osxbuf->procID, (gulong) osxbuf->device_id); - - if (!osxbuf->io_proc_active) { - /* Add IOProc callback */ - status = AudioDeviceCreateIOProcID (osxbuf->device_id, - (AudioDeviceIOProc) gst_osx_ring_buffer_io_proc_spdif, - (void *) osxbuf, &osxbuf->procID); - if (status != noErr) { - GST_ERROR ("AudioDeviceCreateIOProcID failed: %" - GST_FOURCC_FORMAT, GST_FOURCC_ARGS (status)); - return FALSE; - } - osxbuf->io_proc_active = TRUE; - } - - osxbuf->io_proc_needs_deactivation = FALSE; - - /* Start device */ - status = AudioDeviceStart (osxbuf->device_id, osxbuf->procID); - if (status != noErr) { - GST_ERROR ("AudioDeviceStart failed: %" GST_FOURCC_FORMAT, - GST_FOURCC_ARGS (status)); - return FALSE; - } - return TRUE; -} - -static gboolean -gst_osx_ring_buffer_io_proc_spdif_stop (GstOsxRingBuffer * osxbuf) -{ - OSErr status; - - /* Stop device */ - status = AudioDeviceStop (osxbuf->device_id, osxbuf->procID); - if (status != noErr) { - GST_ERROR ("AudioDeviceStop failed: %" GST_FOURCC_FORMAT, - GST_FOURCC_ARGS (status)); - } - - GST_DEBUG ("osx ring buffer stop ioproc ID: %p device_id %lu", - osxbuf->procID, (gulong) osxbuf->device_id); - - if (osxbuf->io_proc_active) { - gst_osx_ring_buffer_remove_render_spdif_callback (osxbuf); - } - - gst_osx_ring_buffer_close_spdif (osxbuf); - return TRUE; } @@ -1158,11 +269,7 @@ gst_osx_ring_buffer_start (GstRingBuffer * buf) osxbuf = GST_OSX_RING_BUFFER (buf); - if (osxbuf->is_passthrough) { - return gst_osx_ring_buffer_io_proc_spdif_start (osxbuf); - } else { - return gst_osx_ring_buffer_io_proc_start (osxbuf); - } + return gst_core_audio_start_processing (osxbuf->core_audio); } static gboolean @@ -1170,25 +277,7 @@ gst_osx_ring_buffer_pause (GstRingBuffer * buf) { GstOsxRingBuffer *osxbuf = GST_OSX_RING_BUFFER (buf); - if (osxbuf->is_passthrough) { - GST_DEBUG ("osx ring buffer pause ioproc ID: %p device_id %lu", - osxbuf->procID, (gulong) osxbuf->device_id); - - if (osxbuf->io_proc_active) { - gst_osx_ring_buffer_remove_render_spdif_callback (osxbuf); - } - } else { - GST_DEBUG ("osx ring buffer pause ioproc: %p device_id %lu", - osxbuf->element->io_proc, (gulong) osxbuf->device_id); - if (osxbuf->io_proc_active) { - /* CoreAudio isn't threadsafe enough to do this here; - * we must deactivate the render callback elsewhere. See: - * http://lists.apple.com/archives/Coreaudio-api/2006/Mar/msg00010.html - */ - osxbuf->io_proc_needs_deactivation = TRUE; - } - } - return TRUE; + return gst_core_audio_pause_processing (osxbuf->core_audio); } @@ -1199,11 +288,7 @@ gst_osx_ring_buffer_stop (GstRingBuffer * buf) osxbuf = GST_OSX_RING_BUFFER (buf); - if (osxbuf->is_passthrough) { - gst_osx_ring_buffer_io_proc_spdif_stop (osxbuf); - } else { - gst_osx_ring_buffer_io_proc_stop (osxbuf); - } + gst_core_audio_stop_processing (osxbuf->core_audio); return TRUE; } @@ -1211,29 +296,15 @@ gst_osx_ring_buffer_stop (GstRingBuffer * buf) static guint gst_osx_ring_buffer_delay (GstRingBuffer * buf) { - double latency; - UInt32 size = sizeof (double); GstOsxRingBuffer *osxbuf; - OSStatus status; + double latency; guint samples; osxbuf = GST_OSX_RING_BUFFER (buf); - if (osxbuf->is_passthrough) { - samples = _audio_device_get_latency (osxbuf->device_id); - samples += _audio_stream_get_latency (osxbuf->stream_id); - latency = (double) samples / GST_RING_BUFFER (buf)->spec.rate; - } else { - status = AudioUnitGetProperty (osxbuf->audiounit, kAudioUnitProperty_Latency, kAudioUnitScope_Global, 0, /* N/A for global */ - &latency, &size); - - if (status) { - GST_WARNING_OBJECT (buf, "Failed to get latency: %" - GST_FOURCC_FORMAT, GST_FOURCC_ARGS (status)); - return 0; - } - - samples = latency * GST_RING_BUFFER (buf)->spec.rate; + if (!gst_core_audio_get_samples_and_latency (osxbuf->core_audio, + GST_RING_BUFFER (buf)->spec.rate, &samples, &latency)) { + return 0; } GST_DEBUG_OBJECT (buf, "Got latency: %f seconds -> %d samples", latency, samples); diff --git a/sys/osxaudio/gstosxringbuffer.h b/sys/osxaudio/gstosxringbuffer.h index 6b00e346b..f5f389fa1 100644 --- a/sys/osxaudio/gstosxringbuffer.h +++ b/sys/osxaudio/gstosxringbuffer.h @@ -45,12 +45,14 @@ #ifndef __GST_OSX_RING_BUFFER_H__ #define __GST_OSX_RING_BUFFER_H__ +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif + #include <gst/gst.h> #include <gst/audio/gstringbuffer.h> -#include <CoreAudio/CoreAudio.h> -#include <AudioToolbox/AudioToolbox.h> +#include <gstosxcoreaudio.h> -#include "gstosxaudioelement.h" G_BEGIN_DECLS @@ -76,30 +78,10 @@ struct _GstOsxRingBuffer { GstRingBuffer object; - gboolean is_src; - gboolean is_passthrough; - gint stream_idx; + GstCoreAudio *core_audio; - AudioDeviceID device_id; - gboolean io_proc_active; - gboolean io_proc_needs_deactivation; guint buffer_len; guint segoffset; - - GstOsxAudioElementInterface *element; - - /* For LPCM in/out */ - AudioUnit audiounit; - AudioBufferList *recBufferList; - - /* For SPDIF out */ - pid_t hog_pid; - gboolean disabled_mixing; - AudioStreamID stream_id; - gboolean revert_format; - AudioStreamBasicDescription stream_format; - AudioStreamBasicDescription original_format; - AudioDeviceIOProcID procID; }; struct _GstOsxRingBufferClass |