osxaudio: add a façade for the CoreAudio API
authorAndoni Morales Alastruey <ylatuya@gmail.com>
Tue, 5 Mar 2013 20:17:52 +0000 (21:17 +0100)
committerSebastian Dröge <sebastian.droege@collabora.co.uk>
Fri, 8 Mar 2013 11:13:55 +0000 (12:13 +0100)
sys/osxaudio/Makefile.am
sys/osxaudio/gstosxaudiosink.c
sys/osxaudio/gstosxaudiosrc.c
sys/osxaudio/gstosxcoreaudio.c [new file with mode: 0644]
sys/osxaudio/gstosxcoreaudio.h
sys/osxaudio/gstosxcoreaudiocommon.c [new file with mode: 0644]
sys/osxaudio/gstosxcoreaudiocommon.h [new file with mode: 0644]
sys/osxaudio/gstosxcoreaudiohal.c [new file with mode: 0644]
sys/osxaudio/gstosxringbuffer.c
sys/osxaudio/gstosxringbuffer.h

index 138e5ae..caddda2 100644 (file)
@@ -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
 
 
 
index 4ddd076..43a9d30 100644 (file)
@@ -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;
 }
index bb2e814..78b72b7 100644 (file)
@@ -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 (file)
index 0000000..aa08223
--- /dev/null
@@ -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);
+}
index 7a072d6..421ff62 100644 (file)
@@ -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
  * 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 (file)
index 0000000..5200b2e
--- /dev/null
@@ -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 (file)
index 0000000..f644757
--- /dev/null
@@ -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 (file)
index 0000000..e018c03
--- /dev/null
@@ -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;
+}
index 4c27a7c..fd84a92 100644 (file)
  * 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);
index 6b00e34..f5f389f 100644 (file)
 #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