summaryrefslogtreecommitdiff
path: root/sys/osxaudio/gstosxcoreaudiocommon.c
diff options
context:
space:
mode:
Diffstat (limited to 'sys/osxaudio/gstosxcoreaudiocommon.c')
-rw-r--r--sys/osxaudio/gstosxcoreaudiocommon.c431
1 files changed, 431 insertions, 0 deletions
diff --git a/sys/osxaudio/gstosxcoreaudiocommon.c b/sys/osxaudio/gstosxcoreaudiocommon.c
new file mode 100644
index 000000000..5200b2ea1
--- /dev/null
+++ b/sys/osxaudio/gstosxcoreaudiocommon.c
@@ -0,0 +1,431 @@
+/*
+ * GStreamer
+ * Copyright (C) 2012-2013 Fluendo S.A. <support@fluendo.com>
+ * Authors: Josep Torra Vallès <josep@fluendo.com>
+ * Andoni Morales Alastruey <amorales@fluendo.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ */
+
+#include "gstosxcoreaudiocommon.h"
+
+void
+gst_core_audio_remove_render_callback (GstCoreAudio * core_audio)
+{
+ AURenderCallbackStruct input;
+ OSStatus status;
+
+ /* Deactivate the render callback by calling SetRenderCallback
+ * with a NULL inputProc.
+ */
+ input.inputProc = NULL;
+ input.inputProcRefCon = NULL;
+
+ status = AudioUnitSetProperty (core_audio->audiounit, kAudioUnitProperty_SetRenderCallback, kAudioUnitScope_Global, 0, /* N/A for global */
+ &input, sizeof (input));
+
+ if (status) {
+ GST_WARNING_OBJECT (core_audio->osxbuf, "Failed to remove render callback %"
+ GST_FOURCC_FORMAT, GST_FOURCC_ARGS (status));
+ }
+
+ /* Remove the RenderNotify too */
+ status = AudioUnitRemoveRenderNotify (core_audio->audiounit,
+ (AURenderCallback) gst_core_audio_render_notify, core_audio);
+
+ if (status) {
+ GST_WARNING_OBJECT (core_audio->osxbuf,
+ "Failed to remove render notify callback %" GST_FOURCC_FORMAT,
+ GST_FOURCC_ARGS (status));
+ }
+
+ /* We're deactivated.. */
+ core_audio->io_proc_needs_deactivation = FALSE;
+ core_audio->io_proc_active = FALSE;
+}
+
+OSStatus
+gst_core_audio_render_notify (GstCoreAudio * core_audio,
+ AudioUnitRenderActionFlags * ioActionFlags,
+ const AudioTimeStamp * inTimeStamp,
+ unsigned int inBusNumber,
+ unsigned int inNumberFrames, AudioBufferList * ioData)
+{
+ /* Before rendering a frame, we get the PreRender notification.
+ * Here, we detach the RenderCallback if we've been paused.
+ *
+ * This is necessary (rather than just directly detaching it) to
+ * work around some thread-safety issues in CoreAudio
+ */
+ if ((*ioActionFlags) & kAudioUnitRenderAction_PreRender) {
+ if (core_audio->io_proc_needs_deactivation) {
+ gst_core_audio_remove_render_callback (core_audio);
+ }
+ }
+
+ return noErr;
+}
+
+gboolean
+gst_core_audio_io_proc_start (GstCoreAudio * core_audio)
+{
+ OSStatus status;
+ AURenderCallbackStruct input;
+ AudioUnitPropertyID callback_type;
+
+ GST_DEBUG_OBJECT (core_audio->osxbuf,
+ "osx ring buffer start ioproc: %p device_id %lu",
+ core_audio->element->io_proc, (gulong) core_audio->device_id);
+ if (!core_audio->io_proc_active) {
+ callback_type = core_audio->is_src ?
+ kAudioOutputUnitProperty_SetInputCallback :
+ kAudioUnitProperty_SetRenderCallback;
+
+ input.inputProc = (AURenderCallback) core_audio->element->io_proc;
+ input.inputProcRefCon = core_audio->osxbuf;
+
+ status = AudioUnitSetProperty (core_audio->audiounit, callback_type, kAudioUnitScope_Global, 0, /* N/A for global */
+ &input, sizeof (input));
+
+ if (status) {
+ GST_ERROR_OBJECT (core_audio->osxbuf,
+ "AudioUnitSetProperty failed: %" GST_FOURCC_FORMAT,
+ GST_FOURCC_ARGS (status));
+ return FALSE;
+ }
+ // ### does it make sense to do this notify stuff for input mode?
+ status = AudioUnitAddRenderNotify (core_audio->audiounit,
+ (AURenderCallback) gst_core_audio_render_notify, core_audio);
+
+ if (status) {
+ GST_ERROR_OBJECT (core_audio->osxbuf,
+ "AudioUnitAddRenderNotify failed %"
+ GST_FOURCC_FORMAT, GST_FOURCC_ARGS (status));
+ return FALSE;
+ }
+ core_audio->io_proc_active = TRUE;
+ }
+
+ core_audio->io_proc_needs_deactivation = FALSE;
+
+ status = AudioOutputUnitStart (core_audio->audiounit);
+ if (status) {
+ GST_ERROR_OBJECT (core_audio->osxbuf, "AudioOutputUnitStart failed: %"
+ GST_FOURCC_FORMAT, GST_FOURCC_ARGS (status));
+ return FALSE;
+ }
+ return TRUE;
+}
+
+gboolean
+gst_core_audio_io_proc_stop (GstCoreAudio * core_audio)
+{
+ OSErr status;
+
+ GST_DEBUG_OBJECT (core_audio->osxbuf,
+ "osx ring buffer stop ioproc: %p device_id %lu",
+ core_audio->element->io_proc, (gulong) core_audio->device_id);
+
+ status = AudioOutputUnitStop (core_audio->audiounit);
+ if (status) {
+ GST_WARNING_OBJECT (core_audio->osxbuf,
+ "AudioOutputUnitStop failed: %" GST_FOURCC_FORMAT,
+ GST_FOURCC_ARGS (status));
+ }
+ // ###: why is it okay to directly remove from here but not from pause() ?
+ if (core_audio->io_proc_active) {
+ gst_core_audio_remove_render_callback (core_audio);
+ }
+ return TRUE;
+}
+
+AudioBufferList *
+buffer_list_alloc (int channels, int size)
+{
+ AudioBufferList *list;
+ int total_size;
+ int n;
+
+ total_size = sizeof (AudioBufferList) + 1 * sizeof (AudioBuffer);
+ list = (AudioBufferList *) g_malloc (total_size);
+
+ list->mNumberBuffers = 1;
+ for (n = 0; n < (int) list->mNumberBuffers; ++n) {
+ list->mBuffers[n].mNumberChannels = channels;
+ list->mBuffers[n].mDataByteSize = size;
+ list->mBuffers[n].mData = g_malloc (size);
+ }
+
+ return list;
+}
+
+void
+buffer_list_free (AudioBufferList * list)
+{
+ int n;
+
+ for (n = 0; n < (int) list->mNumberBuffers; ++n) {
+ if (list->mBuffers[n].mData)
+ g_free (list->mBuffers[n].mData);
+ }
+
+ g_free (list);
+}
+
+gboolean
+gst_core_audio_bind_device (GstCoreAudio * core_audio)
+{
+ OSStatus status;
+
+ /* Specify which device we're using. */
+ GST_DEBUG_OBJECT (core_audio->osxbuf, "Bind AudioUnit to device %d",
+ (int) core_audio->device_id);
+ status = AudioUnitSetProperty (core_audio->audiounit,
+ kAudioOutputUnitProperty_CurrentDevice, kAudioUnitScope_Global, 0,
+ &core_audio->device_id, sizeof (AudioDeviceID));
+ if (status) {
+ GST_ERROR_OBJECT (core_audio->osxbuf, "Failed binding to device: %"
+ GST_FOURCC_FORMAT, GST_FOURCC_ARGS (status));
+ goto audiounit_error;
+ }
+ return TRUE;
+
+audiounit_error:
+ if (core_audio->recBufferList) {
+ buffer_list_free (core_audio->recBufferList);
+ core_audio->recBufferList = NULL;
+ }
+ return FALSE;
+}
+
+gboolean
+gst_core_audio_set_channels_layout (GstCoreAudio * core_audio,
+ gint channels, GstCaps * caps)
+{
+ /* Configure the output stream and allocate ringbuffer memory */
+ AudioChannelLayout *layout = NULL;
+ OSStatus status;
+ int layoutSize, element, i;
+ AudioUnitScope scope;
+ GstStructure *structure;
+ GstAudioChannelPosition *positions;
+
+ /* Describe channels */
+ layoutSize = sizeof (AudioChannelLayout) +
+ channels * sizeof (AudioChannelDescription);
+ layout = g_malloc (layoutSize);
+
+ structure = gst_caps_get_structure (caps, 0);
+ positions = gst_audio_get_channel_positions (structure);
+
+ layout->mChannelLayoutTag = kAudioChannelLayoutTag_UseChannelDescriptions;
+ layout->mChannelBitmap = 0; /* Not used */
+ layout->mNumberChannelDescriptions = channels;
+ for (i = 0; i < channels; i++) {
+ if (positions) {
+ layout->mChannelDescriptions[i].mChannelLabel =
+ gst_audio_channel_position_to_coreaudio_channel_label (positions[i],
+ i);
+ } else {
+ /* Discrete channel numbers are ORed into this */
+ layout->mChannelDescriptions[i].mChannelLabel =
+ kAudioChannelLabel_Discrete_0 | i;
+ }
+
+ /* Others unused */
+ layout->mChannelDescriptions[i].mChannelFlags = 0;
+ layout->mChannelDescriptions[i].mCoordinates[0] = 0.f;
+ layout->mChannelDescriptions[i].mCoordinates[1] = 0.f;
+ layout->mChannelDescriptions[i].mCoordinates[2] = 0.f;
+ }
+
+ if (positions) {
+ g_free (positions);
+ positions = NULL;
+ }
+
+ scope = core_audio->is_src ? kAudioUnitScope_Output : kAudioUnitScope_Input;
+ element = core_audio->is_src ? 1 : 0;
+
+ if (layoutSize) {
+ status = AudioUnitSetProperty (core_audio->audiounit,
+ kAudioUnitProperty_AudioChannelLayout,
+ scope, element, layout, layoutSize);
+ if (status) {
+ GST_WARNING_OBJECT (core_audio->osxbuf,
+ "Failed to set output channel layout: %" GST_FOURCC_FORMAT,
+ GST_FOURCC_ARGS (status));
+ return FALSE;
+ }
+ }
+
+ g_free (layout);
+ return TRUE;
+}
+
+gboolean
+gst_core_audio_set_format (GstCoreAudio * core_audio,
+ AudioStreamBasicDescription format)
+{
+ /* Configure the output stream and allocate ringbuffer memory */
+ OSStatus status;
+ UInt32 propertySize;
+ int element;
+ AudioUnitScope scope;
+
+ GST_DEBUG_OBJECT (core_audio->osxbuf, "Setting format for AudioUnit");
+
+ scope = core_audio->is_src ? kAudioUnitScope_Output : kAudioUnitScope_Input;
+ element = core_audio->is_src ? 1 : 0;
+
+ propertySize = sizeof (AudioStreamBasicDescription);
+ status = AudioUnitSetProperty (core_audio->audiounit,
+ kAudioUnitProperty_StreamFormat, scope, element, &format, propertySize);
+
+ if (status) {
+ GST_WARNING_OBJECT (core_audio->osxbuf,
+ "Failed to set audio description: %" GST_FOURCC_FORMAT,
+ GST_FOURCC_ARGS (status));
+ return FALSE;;
+ }
+
+ return TRUE;
+}
+
+gboolean
+gst_core_audio_open_device (GstCoreAudio * core_audio, OSType sub_type,
+ const gchar * adesc)
+{
+ AudioComponentDescription desc;
+ AudioComponent comp;
+ OSStatus status;
+ AudioUnit unit;
+ UInt32 enableIO;
+
+ desc.componentType = kAudioUnitType_Output;
+ desc.componentSubType = sub_type;
+ desc.componentManufacturer = kAudioUnitManufacturer_Apple;
+ desc.componentFlags = 0;
+ desc.componentFlagsMask = 0;
+
+ comp = AudioComponentFindNext (NULL, &desc);
+
+ if (comp == NULL) {
+ GST_WARNING_OBJECT (core_audio->osxbuf, "Couldn't find %s component",
+ adesc);
+ return FALSE;
+ }
+
+ status = AudioComponentInstanceNew (comp, &unit);
+
+ if (status) {
+ GST_ERROR_OBJECT (core_audio->osxbuf, "Couldn't open %s component %"
+ GST_FOURCC_FORMAT, adesc, GST_FOURCC_ARGS (status));
+ return FALSE;
+ }
+
+ if (core_audio->is_src) {
+ /* enable input */
+ enableIO = 1;
+ status = AudioUnitSetProperty (unit, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Input, 1, /* 1 = input element */
+ &enableIO, sizeof (enableIO));
+
+ if (status) {
+ AudioComponentInstanceDispose (unit);
+ GST_WARNING_OBJECT (core_audio->osxbuf, "Failed to enable input: %"
+ GST_FOURCC_FORMAT, GST_FOURCC_ARGS (status));
+ return FALSE;
+ }
+
+ /* disable output */
+ enableIO = 0;
+ status = AudioUnitSetProperty (unit, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Output, 0, /* 0 = output element */
+ &enableIO, sizeof (enableIO));
+
+ if (status) {
+ AudioComponentInstanceDispose (unit);
+ GST_WARNING_OBJECT (core_audio->osxbuf, "Failed to disable output: %"
+ GST_FOURCC_FORMAT, GST_FOURCC_ARGS (status));
+ return FALSE;
+ }
+ }
+
+ GST_DEBUG_OBJECT (core_audio->osxbuf, "Created %s AudioUnit: %p", adesc,
+ unit);
+ core_audio->audiounit = unit;
+ return TRUE;
+}
+
+AudioChannelLabel
+gst_audio_channel_position_to_coreaudio_channel_label (GstAudioChannelPosition
+ position, int channel)
+{
+ switch (position) {
+ case GST_AUDIO_CHANNEL_POSITION_NONE:
+ return kAudioChannelLabel_Discrete_0 | channel;
+ case GST_AUDIO_CHANNEL_POSITION_FRONT_MONO:
+ return kAudioChannelLabel_Mono;
+ case GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT:
+ return kAudioChannelLabel_Left;
+ case GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT:
+ return kAudioChannelLabel_Right;
+ case GST_AUDIO_CHANNEL_POSITION_REAR_CENTER:
+ return kAudioChannelLabel_CenterSurround;
+ case GST_AUDIO_CHANNEL_POSITION_REAR_LEFT:
+ return kAudioChannelLabel_LeftSurround;
+ case GST_AUDIO_CHANNEL_POSITION_REAR_RIGHT:
+ return kAudioChannelLabel_RightSurround;
+ case GST_AUDIO_CHANNEL_POSITION_LFE:
+ return kAudioChannelLabel_LFEScreen;
+ case GST_AUDIO_CHANNEL_POSITION_FRONT_CENTER:
+ return kAudioChannelLabel_Center;
+ case GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER:
+ return kAudioChannelLabel_Center; // ???
+ case GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER:
+ return kAudioChannelLabel_Center; // ???
+ case GST_AUDIO_CHANNEL_POSITION_SIDE_LEFT:
+ return kAudioChannelLabel_LeftSurroundDirect;
+ case GST_AUDIO_CHANNEL_POSITION_SIDE_RIGHT:
+ return kAudioChannelLabel_RightSurroundDirect;
+ default:
+ return kAudioChannelLabel_Unknown;
+ }
+}
+
+void
+gst_core_audio_dump_channel_layout (AudioChannelLayout * channel_layout)
+{
+ UInt32 i;
+
+ GST_DEBUG ("mChannelLayoutTag: 0x%lx",
+ (unsigned long) channel_layout->mChannelLayoutTag);
+ GST_DEBUG ("mChannelBitmap: 0x%lx",
+ (unsigned long) channel_layout->mChannelBitmap);
+ GST_DEBUG ("mNumberChannelDescriptions: %lu",
+ (unsigned long) channel_layout->mNumberChannelDescriptions);
+ for (i = 0; i < channel_layout->mNumberChannelDescriptions; i++) {
+ AudioChannelDescription *channel_desc =
+ &channel_layout->mChannelDescriptions[i];
+ GST_DEBUG (" mChannelLabel: 0x%lx mChannelFlags: 0x%lx "
+ "mCoordinates[0]: %f mCoordinates[1]: %f "
+ "mCoordinates[2]: %f",
+ (unsigned long) channel_desc->mChannelLabel,
+ (unsigned long) channel_desc->mChannelFlags,
+ channel_desc->mCoordinates[0], channel_desc->mCoordinates[1],
+ channel_desc->mCoordinates[2]);
+ }
+}