summaryrefslogtreecommitdiffstats
path: root/sys/osxaudio/gstosxringbuffer.c
diff options
context:
space:
mode:
authorJustin Karnegas <justin@affinix.com>2009-01-02 20:39:34 +0000
committerMichael Smith <msmith@xiph.org>2009-01-02 20:39:34 +0000
commit17bb67f873a0bd3c4e631ed510a15799c253fc18 (patch)
tree710ea439dbb43826a10e0037ee144b6133f30b70 /sys/osxaudio/gstosxringbuffer.c
parent996fb72681d3da4fce8cb2e94957fe72aa832d8b (diff)
sys/osxaudio/: Rewrite osxaudio to work more flexibly and more reliably, using a different abstraction layer of corea...
Original commit message from CVS: Patch by: Justin Karnegas <justin@affinix.com> and Michael Smith <msmith@songbirdnest.com> * sys/osxaudio/gstosxaudio.c: * sys/osxaudio/gstosxaudioelement.c: * sys/osxaudio/gstosxaudioelement.h: * sys/osxaudio/gstosxaudiosink.c: * sys/osxaudio/gstosxaudiosink.h: * sys/osxaudio/gstosxaudiosrc.c: * sys/osxaudio/gstosxaudiosrc.h: * sys/osxaudio/gstosxringbuffer.c: * sys/osxaudio/gstosxringbuffer.h: Rewrite osxaudio to work more flexibly and more reliably, using a different abstraction layer of coreaudio that is the recommended way of doing low-level audio I/O on OSX. Fixes byg #564948.
Diffstat (limited to 'sys/osxaudio/gstosxringbuffer.c')
-rw-r--r--sys/osxaudio/gstosxringbuffer.c556
1 files changed, 450 insertions, 106 deletions
diff --git a/sys/osxaudio/gstosxringbuffer.c b/sys/osxaudio/gstosxringbuffer.c
index c99e3f37..f6fd2e52 100644
--- a/sys/osxaudio/gstosxringbuffer.c
+++ b/sys/osxaudio/gstosxringbuffer.c
@@ -1,7 +1,8 @@
/*
* GStreamer
- * Copyright 2006 Zaheer Abbas Merali <zaheerabbas at merali dot org>
- *
+ * Copyright (C) 2006 Zaheer Abbas Merali <zaheerabbas at merali dot org>
+ * Copyright (C) 2008 Pioneers of the Inevitable <songbird@songbirdnest.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
@@ -43,14 +44,14 @@
#include <CoreAudio/CoreAudio.h>
#include <gst/gst.h>
+#include <gst/audio/multichannel.h>
#include "gstosxringbuffer.h"
+#include "gstosxaudiosink.h"
+#include "gstosxaudiosrc.h"
GST_DEBUG_CATEGORY_STATIC (osx_audio_debug);
#define GST_CAT_DEFAULT osx_audio_debug
-static void gst_osx_ring_buffer_class_init (GstOsxRingBufferClass * klass);
-static void gst_osx_ring_buffer_init (GstOsxRingBuffer * ringbuffer,
- GstOsxRingBufferClass * g_class);
static void gst_osx_ring_buffer_dispose (GObject * object);
static void gst_osx_ring_buffer_finalize (GObject * object);
static gboolean gst_osx_ring_buffer_open_device (GstRingBuffer * buf);
@@ -60,43 +61,34 @@ static gboolean gst_osx_ring_buffer_acquire (GstRingBuffer * buf,
GstRingBufferSpec * spec);
static gboolean gst_osx_ring_buffer_release (GstRingBuffer * buf);
-/* static gboolean gst_osx_ring_buffer_device_is_acquired (GstRingBuffer * buf); */
-
static gboolean gst_osx_ring_buffer_start (GstRingBuffer * buf);
static gboolean gst_osx_ring_buffer_pause (GstRingBuffer * buf);
static gboolean gst_osx_ring_buffer_stop (GstRingBuffer * buf);
static guint gst_osx_ring_buffer_delay (GstRingBuffer * buf);
static GstRingBufferClass *ring_parent_class = NULL;
-/* ringbuffer abstract base class */
+static OSStatus gst_osx_ring_buffer_render_notify (GstOsxRingBuffer * osxbuf,
+ AudioUnitRenderActionFlags * ioActionFlags,
+ const AudioTimeStamp * inTimeStamp, unsigned int inBusNumber,
+ unsigned int inNumberFrames, AudioBufferList * ioData);
+
+static AudioBufferList *buffer_list_alloc (int channels, int size);
+static void buffer_list_free (AudioBufferList * list);
-GType
-gst_osx_ring_buffer_get_type (void)
+static void
+gst_osx_ring_buffer_do_init (GType type)
{
- static GType ringbuffer_type = 0;
-
- if (!ringbuffer_type) {
- static const GTypeInfo ringbuffer_info = {
- sizeof (GstOsxRingBufferClass),
- NULL,
- NULL,
- (GClassInitFunc) gst_osx_ring_buffer_class_init,
- NULL,
- NULL,
- sizeof (GstOsxRingBuffer),
- 0,
- (GInstanceInitFunc) gst_osx_ring_buffer_init,
- NULL
- };
- GST_DEBUG_CATEGORY_INIT (osx_audio_debug, "osxaudio", 0,
- "OSX Audio Elements");
- GST_DEBUG ("Creating osx ring buffer type");
-
- ringbuffer_type =
- g_type_register_static (GST_TYPE_RING_BUFFER, "GstOsxRingBuffer",
- &ringbuffer_info, 0);
- }
- return ringbuffer_type;
+ GST_DEBUG_CATEGORY_INIT (osx_audio_debug, "osxaudio", 0,
+ "OSX Audio Elements");
+}
+
+GST_BOILERPLATE_FULL (GstOsxRingBuffer, gst_osx_ring_buffer, GstRingBuffer,
+ GST_TYPE_RING_BUFFER, gst_osx_ring_buffer_do_init);
+
+static void
+gst_osx_ring_buffer_base_init (gpointer g_class)
+{
+ /* Nothing to do right now */
}
static void
@@ -152,159 +144,463 @@ 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;
+
+ /* 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_WARNING_OBJECT (osxbuf, "Couldn't open HALOutput component");
+ 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: %lx", 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: %lx", status);
+ return NULL;
+ }
+ }
+
+ /* Specify which device we're using. */
+ GST_DEBUG_OBJECT (osxbuf, "Setting device to %d", (int) device_id);
+ status = AudioUnitSetProperty (unit, kAudioOutputUnitProperty_CurrentDevice, kAudioUnitScope_Global, 0, /* N/A for global */
+ &device_id, sizeof (AudioDeviceID));
+
+ if (status) {
+ CloseComponent (unit);
+ GST_WARNING_OBJECT (osxbuf, "Failed to set device: %lx", status);
+ return NULL;
+ }
+
+ GST_DEBUG_OBJECT (osxbuf, "Create HALOutput AudioUnit: %p", unit);
+
+ return unit;
+}
+
static gboolean
gst_osx_ring_buffer_open_device (GstRingBuffer * buf)
{
- /* stub, we need to open device..maybe do nothing */
+ GstOsxRingBuffer *osxbuf;
+ GstOsxAudioSink *sink;
+ GstOsxAudioSrc *src;
+ AudioStreamBasicDescription asbd_in;
+ OSStatus status;
+ UInt32 propertySize;
+
+ osxbuf = GST_OSX_RING_BUFFER (buf);
+ sink = NULL;
+ src = NULL;
+
+ osxbuf->audiounit = gst_osx_ring_buffer_create_audio_unit (osxbuf,
+ osxbuf->is_src, osxbuf->device_id);
+
+ if (osxbuf->is_src) {
+ src = GST_OSX_AUDIO_SRC (GST_OBJECT_PARENT (buf));
+
+ propertySize = sizeof (asbd_in);
+ status = AudioUnitGetProperty (osxbuf->audiounit,
+ kAudioUnitProperty_StreamFormat,
+ kAudioUnitScope_Input, 1, &asbd_in, &propertySize);
+
+ if (status) {
+ CloseComponent (osxbuf->audiounit);
+ osxbuf->audiounit = NULL;
+ GST_WARNING_OBJECT (osxbuf, "Unable to obtain device properties: %lx",
+ status);
+ return FALSE;
+ }
+
+ src->deviceChannels = asbd_in.mChannelsPerFrame;
+ } else {
+ sink = GST_OSX_AUDIO_SINK (GST_OBJECT_PARENT (buf));
+
+ /* needed for the sink's volume control */
+ sink->audiounit = osxbuf->audiounit;
+ }
+
return TRUE;
}
static gboolean
gst_osx_ring_buffer_close_device (GstRingBuffer * buf)
{
- /* stub, we need to close device..maybe do nothing */
+ 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 gboolean
gst_osx_ring_buffer_acquire (GstRingBuffer * buf, GstRingBufferSpec * spec)
{
/* Configure the output stream and allocate ringbuffer memory */
GstOsxRingBuffer *osxbuf;
- AudioStreamBasicDescription asbd;
+ AudioStreamBasicDescription format;
+ AudioChannelLayout *layout = NULL;
OSStatus status;
- UInt32 buffer_len;
UInt32 propertySize;
+ int layoutSize;
+ int element;
+ int i;
+ AudioUnitScope scope;
+ gboolean ret = FALSE;
+ GstStructure *structure;
+ GstAudioChannelPosition *positions;
+ UInt32 frameSize;
osxbuf = GST_OSX_RING_BUFFER (buf);
/* Fill out the audio description we're going to be using */
- asbd.mFormatID = kAudioFormatLinearPCM;
- asbd.mSampleRate = (double) spec->rate;
- asbd.mChannelsPerFrame = spec->channels;
- asbd.mFormatFlags = kAudioFormatFlagsNativeFloatPacked;
- asbd.mBytesPerFrame = spec->channels * sizeof (float);
- asbd.mBitsPerChannel = sizeof (float) * 8;
- asbd.mBytesPerPacket = spec->channels * sizeof (float);
- asbd.mFramesPerPacket = 1;
- asbd.mReserved = 0;
+ format.mFormatID = kAudioFormatLinearPCM;
+ format.mSampleRate = (double) spec->rate;
+ format.mChannelsPerFrame = spec->channels;
+ format.mFormatFlags = kAudioFormatFlagsNativeFloatPacked;
+ format.mBytesPerFrame = spec->channels * sizeof (float);
+ format.mBitsPerChannel = sizeof (float) * 8;
+ format.mBytesPerPacket = spec->channels * sizeof (float);
+ format.mFramesPerPacket = 1;
+ format.mReserved = 0;
+
+ /* Describe channels */
+ layoutSize = sizeof (AudioChannelLayout) +
+ spec->channels * sizeof (AudioChannelDescription);
+ layout = g_malloc (layoutSize);
+
+ structure = gst_caps_get_structure (spec->caps, 0);
+ positions = gst_audio_get_channel_positions (structure);
+
+ layout->mChannelLayoutTag = kAudioChannelLayoutTag_UseChannelDescriptions;
+ layout->mChannelBitmap = 0; /* Not used */
+ layout->mNumberChannelDescriptions = spec->channels;
+ for (i = 0; i < spec->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_LOG_OBJECT (osxbuf, "Format: %x, %f, %u, %x, %d, %d, %d, %d, %d",
- (unsigned int) asbd.mFormatID,
- asbd.mSampleRate,
- (unsigned int) asbd.mChannelsPerFrame,
- (unsigned int) asbd.mFormatFlags,
- (unsigned int) asbd.mBytesPerFrame,
- (unsigned int) asbd.mBitsPerChannel,
- (unsigned int) asbd.mBytesPerPacket,
- (unsigned int) asbd.mFramesPerPacket, (unsigned int) asbd.mReserved);
-
- GST_DEBUG_OBJECT (osxbuf, "Using stream_id %d, setting output format",
- (int) osxbuf->stream_id);
-
- propertySize = sizeof (asbd);
- status = AudioStreamSetProperty (osxbuf->stream_id, NULL, /* Change immediately */
- 0, /* Master channel */
- kAudioStreamPropertyVirtualFormat, propertySize, &asbd);
+ (unsigned int) format.mFormatID,
+ format.mSampleRate,
+ (unsigned int) format.mChannelsPerFrame,
+ (unsigned int) format.mFormatFlags,
+ (unsigned int) format.mBytesPerFrame,
+ (unsigned int) format.mBitsPerChannel,
+ (unsigned int) format.mBytesPerPacket,
+ (unsigned int) format.mFramesPerPacket, (unsigned int) format.mReserved);
+
+ GST_DEBUG_OBJECT (osxbuf, "Setting format for AudioUnit");
+
+ scope = osxbuf->is_src ? kAudioUnitScope_Output : kAudioUnitScope_Input;
+ element = osxbuf->is_src ? 1 : 0;
+
+ propertySize = sizeof (format);
+ status = AudioUnitSetProperty (osxbuf->audiounit,
+ kAudioUnitProperty_StreamFormat, scope, element, &format, propertySize);
if (status) {
- GST_WARNING_OBJECT (osxbuf, "Failed to set output description: %lx",
+ GST_WARNING_OBJECT (osxbuf, "Failed to set audio description: %lx", status);
+ goto done;
+ }
+
+ status = AudioUnitSetProperty (osxbuf->audiounit,
+ kAudioUnitProperty_AudioChannelLayout,
+ scope, element, layout, layoutSize);
+ if (status) {
+ GST_WARNING_OBJECT (osxbuf, "Failed to set output channel layout: %lx",
status);
- return FALSE;
+ goto done;
}
- /* get requested buffer length to use */
- propertySize = sizeof (buffer_len);
- status = AudioDeviceGetProperty (osxbuf->device_id, 0, false, /* TODO, this should be true for the source element */
- kAudioDevicePropertyBufferSize, &propertySize, &buffer_len);
+ spec->segsize = 4096;
+ spec->segtotal = 16;
+ /* 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: %lx", status);
+ goto done;
+ }
+
+ osxbuf->recBufferList = buffer_list_alloc (format.mChannelsPerFrame,
+ frameSize * format.mBytesPerFrame);
+ }
+
+ buf->data = gst_buffer_new_and_alloc (spec->segtotal * spec->segsize);
+ memset (GST_BUFFER_DATA (buf->data), 0, GST_BUFFER_SIZE (buf->data));
+
+ status = AudioUnitInitialize (osxbuf->audiounit);
if (status) {
+ gst_buffer_unref (buf->data);
+ buf->data = NULL;
+
+ if (osxbuf->recBufferList) {
+ buffer_list_free (osxbuf->recBufferList);
+ osxbuf->recBufferList = NULL;
+ }
+
GST_WARNING_OBJECT (osxbuf,
- "AudioDeviceGetProperty returned %d when getting "
- "kAudioDevicePropertyBufferSize", (int) status);
+ "Failed to initialise AudioUnit: %d", (int) status);
+ goto done;
}
- GST_DEBUG_OBJECT (osxbuf, "%5d osxbuf->buffer_len", (int) buffer_len);
- spec->segsize = buffer_len;
- spec->segtotal = 16;
GST_DEBUG_OBJECT (osxbuf, "osx ring buffer acquired");
- buf->data = gst_buffer_new_and_alloc (spec->segtotal * spec->segsize);
- memset (GST_BUFFER_DATA (buf->data), 0, GST_BUFFER_SIZE (buf->data));
+ ret = TRUE;
- return TRUE;
+done:
+ g_free (layout);
+ return ret;
}
static gboolean
gst_osx_ring_buffer_release (GstRingBuffer * buf)
{
- /* stub, we need to deallocate ringbuffer memory */
GstOsxRingBuffer *osxbuf;
osxbuf = GST_OSX_RING_BUFFER (buf);
+ AudioUnitUninitialize (osxbuf->audiounit);
+
gst_buffer_unref (buf->data);
buf->data = NULL;
+ if (osxbuf->recBufferList) {
+ buffer_list_free (osxbuf->recBufferList);
+ osxbuf->recBufferList = NULL;
+ }
+
return TRUE;
}
+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");
+ }
+
+ /* 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");
+ }
+
+ /* We're deactivated.. */
+ osxbuf->io_proc_needs_deactivation = FALSE;
+ osxbuf->io_proc_active = FALSE;
+}
+
+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 gboolean
gst_osx_ring_buffer_start (GstRingBuffer * buf)
{
- /* stub */
OSStatus status;
GstOsxRingBuffer *osxbuf;
+ AURenderCallbackStruct input;
+ AudioUnitPropertyID callback_type;
osxbuf = GST_OSX_RING_BUFFER (buf);
GST_DEBUG ("osx ring buffer start ioproc: 0x%p device_id %lu",
osxbuf->element->io_proc, osxbuf->device_id);
if (!osxbuf->io_proc_active) {
- status =
- AudioDeviceAddIOProc (osxbuf->device_id, osxbuf->element->io_proc,
- osxbuf);
+ 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_WARNING ("AudioUnitSetProperty returned %d", (int) 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_WARNING ("AudioDeviceAddIOProc returned %" GST_FOURCC_FORMAT,
- GST_FOURCC_ARGS (status));
+ GST_WARNING ("AudioUnitAddRenderNotify returned %d", (int) status);
return FALSE;
}
+
osxbuf->io_proc_active = TRUE;
- }
+ } else
+ osxbuf->io_proc_needs_deactivation = FALSE;
- status = AudioDeviceStart (osxbuf->device_id, osxbuf->element->io_proc);
+ status = AudioOutputUnitStart (osxbuf->audiounit);
if (status) {
- GST_WARNING ("AudioDeviceStart returned %d", (int) status);
+ GST_WARNING ("AudioOutputUnitStart returned %d", (int) status);
return FALSE;
}
return TRUE;
}
+// ###
static gboolean
gst_osx_ring_buffer_pause (GstRingBuffer * buf)
{
- /* stub */
- /* stop callback */
- OSErr status;
GstOsxRingBuffer *osxbuf = GST_OSX_RING_BUFFER (buf);
GST_DEBUG ("osx ring buffer pause ioproc: 0x%p device_id %lu",
osxbuf->element->io_proc, osxbuf->device_id);
if (osxbuf->io_proc_active) {
- status =
- AudioDeviceRemoveIOProc (osxbuf->device_id, osxbuf->element->io_proc);
- if (status)
- GST_WARNING ("AudioDeviceRemoveIOProc " "returned %d", (int) status);
- osxbuf->io_proc_active = FALSE;
+ /* 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;
}
+// ###
static gboolean
gst_osx_ring_buffer_stop (GstRingBuffer * buf)
{
- /* stub */
OSErr status;
GstOsxRingBuffer *osxbuf;
@@ -312,17 +608,14 @@ gst_osx_ring_buffer_stop (GstRingBuffer * buf)
GST_DEBUG ("osx ring buffer stop ioproc: 0x%p device_id %lu",
osxbuf->element->io_proc, osxbuf->device_id);
- /* stop callback */
- status = AudioDeviceStop (osxbuf->device_id, osxbuf->element->io_proc);
+
+ status = AudioOutputUnitStop (osxbuf->audiounit);
if (status)
- GST_WARNING ("AudioDeviceStop returned %d", (int) status);
+ GST_WARNING ("AudioOutputUnitStop returned %d", (int) status);
+ // ###: why is it okay to directly remove from here but not from pause() ?
if (osxbuf->io_proc_active) {
- status =
- AudioDeviceRemoveIOProc (osxbuf->device_id, osxbuf->element->io_proc);
- if (status)
- GST_WARNING ("AudioDeviceRemoveIOProc " "returned %d", (int) status);
- osxbuf->io_proc_active = FALSE;
+ gst_osx_ring_buffer_remove_render_callback (osxbuf);
}
return TRUE;
}
@@ -330,6 +623,57 @@ gst_osx_ring_buffer_stop (GstRingBuffer * buf)
static guint
gst_osx_ring_buffer_delay (GstRingBuffer * buf)
{
- /* stub */
- return 0;
+ double latency;
+ UInt32 size = sizeof (double);
+ GstOsxRingBuffer *osxbuf;
+ OSStatus status;
+ guint samples;
+
+ osxbuf = GST_OSX_RING_BUFFER (buf);
+
+ 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: %d", (int) status);
+ return 0;
+ }
+
+ samples = latency * GST_RING_BUFFER (buf)->spec.rate;
+ GST_DEBUG_OBJECT (buf, "Got latency: %f seconds -> %d samples", latency,
+ samples);
+ return samples;
+}
+
+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);
}