diff options
author | Justin Karnegas <justin@affinix.com> | 2009-01-02 20:39:34 +0000 |
---|---|---|
committer | Michael Smith <msmith@xiph.org> | 2009-01-02 20:39:34 +0000 |
commit | 17bb67f873a0bd3c4e631ed510a15799c253fc18 (patch) | |
tree | 710ea439dbb43826a10e0037ee144b6133f30b70 /sys/osxaudio/gstosxringbuffer.c | |
parent | 996fb72681d3da4fce8cb2e94957fe72aa832d8b (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.c | 556 |
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); } |