osxaudio: add a façade for the CoreAudio API

This commit is contained in:
Andoni Morales Alastruey 2013-03-05 21:17:52 +01:00 committed by Sebastian Dröge
parent 400222e29f
commit 9621074006
10 changed files with 2161 additions and 1669 deletions

View file

@ -4,6 +4,8 @@ libgstosxaudio_la_SOURCES = gstosxringbuffer.c \
gstosxaudioelement.c \ gstosxaudioelement.c \
gstosxaudiosink.c \ gstosxaudiosink.c \
gstosxaudiosrc.c \ gstosxaudiosrc.c \
gstosxcoreaudiocommon.c \
gstosxcoreaudio.c \
gstosxaudio.c gstosxaudio.c
libgstosxaudio_la_CFLAGS = $(GST_PLUGINS_BASE_CFLAGS) $(GST_CFLAGS) \ libgstosxaudio_la_CFLAGS = $(GST_PLUGINS_BASE_CFLAGS) $(GST_CFLAGS) \
@ -21,7 +23,9 @@ noinst_HEADERS = gstosxaudiosink.h \
gstosxaudioelement.h \ gstosxaudioelement.h \
gstosxringbuffer.h \ gstosxringbuffer.h \
gstosxaudiosrc.h \ gstosxaudiosrc.h \
gstosxcoreaudio.h gstosxcoreaudiocommon.h \
gstosxcoreaudio.h \
gstosxcoreaudiohal.c

View file

@ -69,8 +69,6 @@
#include <gst/gst.h> #include <gst/gst.h>
#include <gst/audio/multichannel.h> #include <gst/audio/multichannel.h>
#include <gst/audio/gstaudioiec61937.h> #include <gst/audio/gstaudioiec61937.h>
#include <CoreAudio/CoreAudio.h>
#include <CoreAudio/AudioHardware.h>
#include "gstosxaudiosink.h" #include "gstosxaudiosink.h"
#include "gstosxaudioelement.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, GST_DEBUG_CATEGORY_INIT (osx_audiosink_debug, "osxaudiosink", 0,
"OSX Audio Sink"); "OSX Audio Sink");
gst_core_audio_init_debug ();
GST_DEBUG ("Adding static interface"); GST_DEBUG ("Adding static interface");
g_type_add_interface_static (type, GST_OSX_AUDIO_ELEMENT_TYPE, g_type_add_interface_static (type, GST_OSX_AUDIO_ELEMENT_TYPE,
&osxelement_info); &osxelement_info);
@ -425,19 +424,22 @@ gst_osx_audio_sink_create_ringbuffer (GstBaseAudioSink * sink)
osxsink = GST_OSX_AUDIO_SINK (sink); osxsink = GST_OSX_AUDIO_SINK (sink);
if (!gst_osx_audio_sink_select_device (osxsink)) { if (!gst_osx_audio_sink_select_device (osxsink)) {
GST_ERROR_OBJECT (sink, "Could not select device");
return NULL; return NULL;
} }
GST_DEBUG ("Creating ringbuffer"); GST_DEBUG_OBJECT (sink, "Creating ringbuffer");
ringbuffer = g_object_new (GST_TYPE_OSX_RING_BUFFER, NULL); 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), GST_OSX_AUDIO_ELEMENT_GET_INTERFACE (osxsink),
(void *) gst_osx_audio_sink_io_proc); (void *) gst_osx_audio_sink_io_proc);
gst_osx_audio_sink_set_volume (osxsink); gst_osx_audio_sink_set_volume (osxsink);
ringbuffer->element = GST_OSX_AUDIO_ELEMENT_GET_INTERFACE (osxsink); ringbuffer->core_audio->element =
ringbuffer->device_id = osxsink->device_id; 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); return GST_RING_BUFFER (ringbuffer);
} }
@ -455,7 +457,7 @@ gst_osx_audio_sink_io_proc (GstOsxRingBuffer * buf,
guint8 *readptr; guint8 *readptr;
gint readseg; gint readseg;
gint len; gint len;
gint stream_idx = buf->stream_idx; gint stream_idx = buf->core_audio->stream_idx;
gint remaining = bufferList->mBuffers[stream_idx].mDataByteSize; gint remaining = bufferList->mBuffers[stream_idx].mDataByteSize;
gint offset = 0; gint offset = 0;
@ -500,35 +502,13 @@ gst_osx_audio_sink_osxelement_init (gpointer g_iface, gpointer iface_data)
static void static void
gst_osx_audio_sink_set_volume (GstOsxAudioSink * sink) gst_osx_audio_sink_set_volume (GstOsxAudioSink * sink)
{ {
if (!sink->audiounit) GstOsxRingBuffer *osxbuf;
osxbuf = GST_OSX_RING_BUFFER (GST_BASE_AUDIO_SINK (sink)->ringbuffer);
if (!osxbuf)
return; return;
AudioUnitSetParameter (sink->audiounit, kHALOutputParam_Volume, gst_core_audio_set_volume (osxbuf->core_audio, sink->volume);
kAudioUnitScope_Global, 0, (float) sink->volume, 0);
}
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]);
}
} }
static gboolean static gboolean
@ -554,14 +534,14 @@ gst_osx_audio_sink_allowed_caps (GstOsxAudioSink * osxsink)
}; };
/* First collect info about the HW capabilites and preferences */ /* First collect info about the HW capabilites and preferences */
spdif_allowed = _audio_device_is_spdif_avail (osxsink->device_id); spdif_allowed =
layout = _audio_device_get_channel_layout (osxsink->device_id); 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", GST_DEBUG_OBJECT (osxsink, "Selected device ID: %u SPDIF allowed: %d",
(unsigned) osxsink->device_id, spdif_allowed); (unsigned) osxsink->device_id, spdif_allowed);
if (layout) { if (layout) {
_dump_channel_layout (layout);
max_channels = layout->mNumberChannelDescriptions; max_channels = layout->mNumberChannelDescriptions;
} else { } else {
GST_WARNING_OBJECT (osxsink, "This driver does not support " GST_WARNING_OBJECT (osxsink, "This driver does not support "
@ -656,71 +636,11 @@ gst_osx_audio_sink_allowed_caps (GstOsxAudioSink * osxsink)
static gboolean static gboolean
gst_osx_audio_sink_select_device (GstOsxAudioSink * osxsink) 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; gboolean res = FALSE;
devices = _audio_system_get_devices (&ndevices); if (!gst_core_audio_select_device (&osxsink->device_id))
return FALSE;
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;
}
}
res = gst_osx_audio_sink_allowed_caps (osxsink); res = gst_osx_audio_sink_allowed_caps (osxsink);
done:
g_free (devices);
return res; return res;
} }

View file

@ -60,8 +60,6 @@
#endif #endif
#include <gst/gst.h> #include <gst/gst.h>
#include <CoreAudio/CoreAudio.h>
#include <CoreAudio/AudioHardware.h>
#include "gstosxaudiosrc.h" #include "gstosxaudiosrc.h"
#include "gstosxaudioelement.h" #include "gstosxaudioelement.h"
@ -266,9 +264,10 @@ gst_osx_audio_src_create_ringbuffer (GstBaseAudioSrc * src)
GST_OSX_AUDIO_ELEMENT_GET_INTERFACE (osxsrc), GST_OSX_AUDIO_ELEMENT_GET_INTERFACE (osxsrc),
(void *) gst_osx_audio_src_io_proc); (void *) gst_osx_audio_src_io_proc);
ringbuffer->element = GST_OSX_AUDIO_ELEMENT_GET_INTERFACE (osxsrc); ringbuffer->core_audio->element =
ringbuffer->is_src = TRUE; GST_OSX_AUDIO_ELEMENT_GET_INTERFACE (osxsrc);
ringbuffer->device_id = osxsrc->device_id; ringbuffer->core_audio->is_src = TRUE;
ringbuffer->core_audio->device_id = osxsrc->device_id;
return GST_RING_BUFFER (ringbuffer); return GST_RING_BUFFER (ringbuffer);
} }
@ -286,15 +285,15 @@ gst_osx_audio_src_io_proc (GstOsxRingBuffer * buf,
gint remaining; gint remaining;
gint offset = 0; gint offset = 0;
status = AudioUnitRender (buf->audiounit, ioActionFlags, inTimeStamp, status = AudioUnitRender (buf->core_audio->audiounit, ioActionFlags,
inBusNumber, inNumberFrames, buf->recBufferList); inTimeStamp, inBusNumber, inNumberFrames, buf->core_audio->recBufferList);
if (status) { if (status) {
GST_WARNING_OBJECT (buf, "AudioUnitRender returned %d", (int) status); GST_WARNING_OBJECT (buf, "AudioUnitRender returned %d", (int) status);
return status; return status;
} }
remaining = buf->recBufferList->mBuffers[0].mDataByteSize; remaining = buf->core_audio->recBufferList->mBuffers[0].mDataByteSize;
while (remaining) { while (remaining) {
if (!gst_ring_buffer_prepare_read (GST_RING_BUFFER (buf), if (!gst_ring_buffer_prepare_read (GST_RING_BUFFER (buf),
@ -307,7 +306,8 @@ gst_osx_audio_src_io_proc (GstOsxRingBuffer * buf,
len = remaining; len = remaining;
memcpy (writeptr + buf->segoffset, 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; buf->segoffset += len;
offset += len; offset += len;
@ -334,30 +334,5 @@ gst_osx_audio_src_osxelement_init (gpointer g_iface, gpointer iface_data)
static void static void
gst_osx_audio_src_select_device (GstOsxAudioSrc * osxsrc) gst_osx_audio_src_select_device (GstOsxAudioSrc * osxsrc)
{ {
OSStatus status; gst_core_audio_select_source_device (&osxsrc->device_id);
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);
}
} }

View file

@ -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);
}

View file

@ -2,29 +2,6 @@
* GStreamer * GStreamer
* Copyright (C) 2012 Fluendo S.A. <support@fluendo.com> * 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 * This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public * modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either * License as published by the Free Software Foundation; either
@ -40,567 +17,135 @@
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA. * 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 "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_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 struct _GstCoreAudio
_audio_system_set_runloop (CFRunLoopRef runLoop)
{ {
OSStatus status = noErr; GObject object;
gboolean res = FALSE; GstObject *osxbuf;
GstOsxAudioElementInterface *element;
AudioObjectPropertyAddress runloopAddress = { gboolean is_src;
kAudioHardwarePropertyRunLoop, gboolean is_passthrough;
kAudioObjectPropertyScopeGlobal, AudioDeviceID device_id;
kAudioObjectPropertyElementMaster AudioStreamBasicDescription stream_format;
}; gint stream_idx;
gboolean io_proc_active;
gboolean io_proc_needs_deactivation;
status = AudioObjectSetPropertyData (kAudioObjectSystemObject, /* For LPCM in/out */
&runloopAddress, 0, NULL, sizeof (CFRunLoopRef), &runLoop); AudioUnit audiounit;
if (status == noErr) { AudioBufferList *recBufferList;
res = TRUE;
} else {
GST_ERROR ("failed to set runloop to %p: %" GST_FOURCC_FORMAT,
runLoop, GST_FOURCC_ARGS (status));
}
return res; #ifndef HAVE_IOS
} /* For SPDIF out */
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; pid_t hog_pid;
UInt32 propertySize = sizeof (hog_pid); gboolean disabled_mixing;
AudioStreamID stream_id;
AudioObjectPropertyAddress audioDeviceHogModeAddress = { gboolean revert_format;
kAudioDevicePropertyHogMode, AudioStreamBasicDescription original_format;
kAudioDevicePropertyScopeOutput, AudioDeviceIOProcID procID;
kAudioObjectPropertyElementMaster #endif
}; };
status = AudioObjectGetPropertyData (device_id, struct _GstCoreAudioClass
&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; GObjectClass parent_class;
UInt32 propertySize = sizeof (hog_pid);
gboolean res = FALSE;
AudioObjectPropertyAddress audioDeviceHogModeAddress = {
kAudioDevicePropertyHogMode,
kAudioDevicePropertyScopeOutput,
kAudioObjectPropertyElementMaster
}; };
status = AudioObjectSetPropertyData (device_id, GType gst_core_audio_get_type (void);
&audioDeviceHogModeAddress, 0, NULL, propertySize, &hog_pid);
if (status == noErr) { void gst_core_audio_init_debug (void);
res = TRUE;
} else {
GST_ERROR ("failed to set hog: %" GST_FOURCC_FORMAT,
GST_FOURCC_ARGS (status));
}
return res; GstCoreAudio * gst_core_audio_new (GstObject *osxbuf);
}
static inline gboolean gboolean gst_core_audio_open (GstCoreAudio *core_audio);
_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 = { gboolean gst_core_audio_close (GstCoreAudio *core_audio);
kAudioDevicePropertySupportsMixing,
kAudioObjectPropertyScopeGlobal,
kAudioObjectPropertyElementMaster
};
if (AudioObjectHasProperty (device_id, &audioDeviceSupportsMixingAddress)) { gboolean gst_core_audio_initialize (GstCoreAudio *core_audio,
/* Set mixable to false if we are allowed to */ AudioStreamBasicDescription format,
status = AudioObjectIsPropertySettable (device_id, GstCaps *caps,
&audioDeviceSupportsMixingAddress, &writable); gboolean is_passthrough);
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) { void gst_core_audio_unitialize (GstCoreAudio *core_audio);
can_mix = enable_mix;
status = AudioObjectSetPropertyData (device_id,
&audioDeviceSupportsMixingAddress, 0, NULL, propertySize, &can_mix);
res = TRUE;
}
if (status != noErr) { gboolean gst_core_audio_start_processing (GstCoreAudio *core_audio);
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; gboolean gst_core_audio_pause_processing (GstCoreAudio *core_audio);
}
static inline gchar * gboolean gst_core_audio_stop_processing (GstCoreAudio *core_audio);
_audio_device_get_name (AudioDeviceID device_id)
{
OSStatus status = noErr;
UInt32 propertySize = 0;
gchar *device_name = NULL;
AudioObjectPropertyAddress deviceNameAddress = { gboolean gst_core_audio_get_samples_and_latency (GstCoreAudio * core_audio,
kAudioDevicePropertyDeviceName, gdouble rate,
kAudioDevicePropertyScopeOutput, guint *samples,
kAudioObjectPropertyElementMaster gdouble *latency);
};
/* Get the length of the device name */ void gst_core_audio_set_volume (GstCoreAudio *core_audio,
status = AudioObjectGetPropertyDataSize (device_id, gfloat volume);
&deviceNameAddress, 0, NULL, &propertySize);
if (status != noErr) {
goto beach;
}
/* Get the name of the device */ gboolean gst_core_audio_audio_device_is_spdif_avail (AudioDeviceID device_id);
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 gboolean gst_core_audio_select_device (AudioDeviceID *device_id);
_audio_device_has_output (AudioDeviceID device_id)
{
OSStatus status = noErr;
UInt32 propertySize;
AudioObjectPropertyAddress streamsAddress = { gboolean gst_core_audio_select_source_device (AudioDeviceID *device_id);
kAudioDevicePropertyStreams,
kAudioDevicePropertyScopeOutput,
kAudioObjectPropertyElementMaster
};
status = AudioObjectGetPropertyDataSize (device_id, AudioChannelLayout * gst_core_audio_audio_device_get_channel_layout (AudioDeviceID device_id);
&streamsAddress, 0, NULL, &propertySize);
if (status != noErr) {
return FALSE;
}
if (propertySize == 0) {
return FALSE;
}
return TRUE;
}
static inline AudioChannelLayout * G_END_DECLS
_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)
{
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 inline gboolean
_audio_device_is_spdif_avail (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;
}
#endif /* __GST_CORE_AUDIO_H__ */

View file

@ -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]);
}
}

View file

@ -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);

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -45,12 +45,14 @@
#ifndef __GST_OSX_RING_BUFFER_H__ #ifndef __GST_OSX_RING_BUFFER_H__
#define __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/gst.h>
#include <gst/audio/gstringbuffer.h> #include <gst/audio/gstringbuffer.h>
#include <CoreAudio/CoreAudio.h> #include <gstosxcoreaudio.h>
#include <AudioToolbox/AudioToolbox.h>
#include "gstosxaudioelement.h"
G_BEGIN_DECLS G_BEGIN_DECLS
@ -76,30 +78,10 @@ struct _GstOsxRingBuffer
{ {
GstRingBuffer object; GstRingBuffer object;
gboolean is_src; GstCoreAudio *core_audio;
gboolean is_passthrough;
gint stream_idx;
AudioDeviceID device_id;
gboolean io_proc_active;
gboolean io_proc_needs_deactivation;
guint buffer_len; guint buffer_len;
guint segoffset; 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 struct _GstOsxRingBufferClass