diff --git a/ChangeLog b/ChangeLog index bdfdaedf06..ff7a370662 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,21 @@ +2009-01-02 Michael Smith + + Patch by: Justin Karnegas and + Michael Smith + * 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. + 2009-01-02 Wim Taymans * tests/examples/rtp/server-decodebin-H263p-AMR.sh: diff --git a/sys/osxaudio/gstosxaudio.c b/sys/osxaudio/gstosxaudio.c index 9d63929736..262c7d91cd 100644 --- a/sys/osxaudio/gstosxaudio.c +++ b/sys/osxaudio/gstosxaudio.c @@ -1,5 +1,6 @@ -/* GStreamer - * Copyright (C) <1999> Erik Walthinsen +/* + * GStreamer + * Copyright (C) 1999 Erik Walthinsen * Copyright (C) 2007,2008 Pioneers of the Inevitable * * This library is free software; you can redistribute it and/or @@ -16,10 +17,10 @@ * License along with this library; if not, write to the * Free Software Foundation, Inc., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. - * - * The development of this code was made possible due to the involvement of + * + * The development of this code was made possible due to the involvement of * Pioneers of the Inevitable, the creators of the Songbird Music player - * + * */ /** @@ -42,12 +43,10 @@ * Last reviewed on 2006-03-01 (0.10.4) */ - #ifdef HAVE_CONFIG_H #include "config.h" #endif - #include "gstosxaudioelement.h" #include "gstosxaudiosink.h" #include "gstosxaudiosrc.h" @@ -55,7 +54,6 @@ static gboolean plugin_init (GstPlugin * plugin) { - if (!gst_element_register (plugin, "osxaudiosink", GST_RANK_PRIMARY, GST_TYPE_OSX_AUDIO_SINK)) { return FALSE; diff --git a/sys/osxaudio/gstosxaudioelement.c b/sys/osxaudio/gstosxaudioelement.c index c3c28336e7..ceff749ef7 100644 --- a/sys/osxaudio/gstosxaudioelement.c +++ b/sys/osxaudio/gstosxaudioelement.c @@ -1,8 +1,8 @@ /* * GStreamer - * Copyright 2006 Zaheer Abbas Merali - * Copyright 2007 Pioneers of the Inevitable - * + * Copyright (C) 2006 Zaheer Abbas Merali + * Copyright (C) 2007 Pioneers of the Inevitable + * * 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 @@ -40,9 +40,10 @@ * License along with this library; if not, write to the * Free Software Foundation, Inc., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, 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 - * + * + * The development of this code was made possible due to the involvement of + * Pioneers of the Inevitable, the creators of the Songbird Music player + * */ #include @@ -67,12 +68,11 @@ gst_osx_audio_element_get_type () 0, 0, NULL, + NULL }; gst_osxaudioelement_type = g_type_register_static (G_TYPE_INTERFACE, "GstOsxAudioElement", &gst_osxaudioelement_info, 0); - /*g_type_interface_add_prerequisite (gst_osxaudioelement_type, - GST_TYPE_IMPLEMENTS_INTERFACE); */ } return gst_osxaudioelement_type; @@ -84,11 +84,9 @@ gst_osx_audio_element_class_init (GstOsxAudioElementInterface * klass) static gboolean initialized = FALSE; if (!initialized) { - initialized = TRUE; } /* default virtual functions */ klass->io_proc = NULL; - } diff --git a/sys/osxaudio/gstosxaudioelement.h b/sys/osxaudio/gstosxaudioelement.h index 1ab487a28b..b5d90c0c6b 100644 --- a/sys/osxaudio/gstosxaudioelement.h +++ b/sys/osxaudio/gstosxaudioelement.h @@ -1,8 +1,8 @@ /* * GStreamer - * Copyright 2006 Zaheer Abbas Merali - * Copyright 2007 Pioneers of the Inevitable - * + * Copyright (C) 2006 Zaheer Abbas Merali + * Copyright (C) 2007 Pioneers of the Inevitable + * * 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 @@ -41,8 +41,9 @@ * Free Software Foundation, Inc., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, 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 - * + * 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_OSX_AUDIO_ELEMENT_H__ @@ -50,22 +51,34 @@ #include #include +#include -#define GST_OSX_AUDIO_ELEMENT_TYPE (gst_osx_audio_element_get_type()) -#define GST_OSX_AUDIO_ELEMENT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_OSX_AUDIO_ELEMENT_TYPE, GstOsxAudioElementInterface)) -#define GST_IS_OSX_AUDIO_ELEMENT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_OSX_AUDIO_ELEMENT_TYPE)) -#define GST_OSX_AUDIO_ELEMENT_GET_INTERFACE(inst) (G_TYPE_INSTANCE_GET_INTERFACE ((inst), GST_OSX_AUDIO_ELEMENT_TYPE, GstOsxAudioElementInterface)) +G_BEGIN_DECLS + +#define GST_OSX_AUDIO_ELEMENT_TYPE \ + (gst_osx_audio_element_get_type()) +#define GST_OSX_AUDIO_ELEMENT(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_OSX_AUDIO_ELEMENT_TYPE,GstOsxAudioElementInterface)) +#define GST_IS_OSX_AUDIO_ELEMENT(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_OSX_AUDIO_ELEMENT_TYPE)) +#define GST_OSX_AUDIO_ELEMENT_GET_INTERFACE(inst) \ + (G_TYPE_INSTANCE_GET_INTERFACE((inst),GST_OSX_AUDIO_ELEMENT_TYPE,GstOsxAudioElementInterface)) typedef struct _GstOsxAudioElementInterface GstOsxAudioElementInterface; -struct _GstOsxAudioElementInterface { - GTypeInterface parent; - - OSStatus (*io_proc) (AudioDeviceID inDevice, const AudioTimeStamp *inNow, const AudioBufferList *inInputData, const AudioTimeStamp *inInputTime, AudioBufferList *outOutputData, const AudioTimeStamp *inOutputTime, void *inClientData); - +struct _GstOsxAudioElementInterface +{ + GTypeInterface parent; + + OSStatus (*io_proc) (void * userdata, + AudioUnitRenderActionFlags * ioActionFlags, + const AudioTimeStamp * inTimeStamp, + UInt32 inBusNumber, UInt32 inNumberFrames, + AudioBufferList * bufferList); }; GType gst_osx_audio_element_get_type (void); +G_END_DECLS -#endif +#endif /* __GST_OSX_AUDIO_ELEMENT_H__ */ diff --git a/sys/osxaudio/gstosxaudiosink.c b/sys/osxaudio/gstosxaudiosink.c index 46d229b545..aec8874d09 100644 --- a/sys/osxaudio/gstosxaudiosink.c +++ b/sys/osxaudio/gstosxaudiosink.c @@ -1,8 +1,8 @@ /* * GStreamer - * Copyright 2005,2006 Zaheer Abbas Merali - * Copyright 2007 Pioneers of the Inevitable - * + * Copyright (C) 2005,2006 Zaheer Abbas Merali + * Copyright (C) 2007,2008 Pioneers of the Inevitable + * * 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 @@ -41,8 +41,8 @@ * Free Software Foundation, Inc., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, 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. + * The development of this code was made possible due to the involvement of + * Pioneers of the Inevitable, the creators of the Songbird Music player * */ @@ -67,8 +67,6 @@ #include #include #include "gstosxaudiosink.h" -#include "gstosxaudiosrc.h" - #include "gstosxaudioelement.h" GST_DEBUG_CATEGORY_STATIC (osx_audiosink_debug); @@ -90,9 +88,12 @@ enum enum { ARG_0, - ARG_DEVICE + ARG_DEVICE, + ARG_VOLUME }; +#define DEFAULT_VOLUME 1.0 + static GstStaticPadTemplate sink_factory = GST_STATIC_PAD_TEMPLATE ("sink", GST_PAD_SINK, GST_PAD_ALWAYS, @@ -101,27 +102,28 @@ static GstStaticPadTemplate sink_factory = GST_STATIC_PAD_TEMPLATE ("sink", "signed = (boolean) { TRUE }, " "width = (int) 32, " "depth = (int) 32, " - "rate = (int) [1, MAX], " "channels = (int) [1, 2]") + "rate = (int) [1, MAX], " "channels = (int) [1, MAX]") ); static void gst_osx_audio_sink_set_property (GObject * object, guint prop_id, const GValue * value, GParamSpec * pspec); static void gst_osx_audio_sink_get_property (GObject * object, guint prop_id, GValue * value, GParamSpec * pspec); -static GstCaps *gst_osx_audio_sink_getcaps (GstBaseSink * sink); static GstRingBuffer *gst_osx_audio_sink_create_ringbuffer (GstBaseAudioSink * sink); static void gst_osx_audio_sink_osxelement_init (gpointer g_iface, gpointer iface_data); -OSStatus gst_osx_audio_sink_io_proc (AudioDeviceID inDevice, - const AudioTimeStamp * inNow, const AudioBufferList * inInputData, - const AudioTimeStamp * inInputTime, AudioBufferList * outOutputData, - const AudioTimeStamp * inOutputTime, void *inClientData); static void gst_osx_audio_sink_select_device (GstOsxAudioSink * osxsink); +static void gst_osx_audio_sink_set_volume (GstOsxAudioSink * sink); + +static OSStatus gst_osx_audio_sink_io_proc (GstOsxRingBuffer * buf, + AudioUnitRenderActionFlags * ioActionFlags, + const AudioTimeStamp * inTimeStamp, + UInt32 inBusNumber, UInt32 inNumberFrames, AudioBufferList * bufferList); static void -gst_osx_audio_sink_osxelement_do_init (GType type) +gst_osx_audio_sink_do_init (GType type) { static const GInterfaceInfo osxelement_info = { gst_osx_audio_sink_osxelement_init, @@ -137,8 +139,7 @@ gst_osx_audio_sink_osxelement_do_init (GType type) } GST_BOILERPLATE_FULL (GstOsxAudioSink, gst_osx_audio_sink, GstBaseAudioSink, - GST_TYPE_BASE_AUDIO_SINK, gst_osx_audio_sink_osxelement_do_init); - + GST_TYPE_BASE_AUDIO_SINK, gst_osx_audio_sink_do_init); static void gst_osx_audio_sink_base_init (gpointer g_class) @@ -151,7 +152,6 @@ gst_osx_audio_sink_base_init (gpointer g_class) gst_element_class_set_details (element_class, &gst_osx_audio_sink_details); } -/* initialize the plugin's class */ static void gst_osx_audio_sink_class_init (GstOsxAudioSinkClass * klass) { @@ -176,25 +176,21 @@ gst_osx_audio_sink_class_init (GstOsxAudioSinkClass * klass) g_param_spec_int ("device", "Device ID", "Device ID of output device", 0, G_MAXINT, 0, G_PARAM_READWRITE)); - gstbasesink_class->get_caps = GST_DEBUG_FUNCPTR (gst_osx_audio_sink_getcaps); + g_object_class_install_property (gobject_class, ARG_VOLUME, + g_param_spec_double ("volume", "Volume", "Volume of this stream", + 0, 1.0, 1.0, G_PARAM_READWRITE)); + gstbaseaudiosink_class->create_ringbuffer = GST_DEBUG_FUNCPTR (gst_osx_audio_sink_create_ringbuffer); - } -/* initialize the new element - * instantiate pads and add them to element - * set functions - * initialize structure - */ static void gst_osx_audio_sink_init (GstOsxAudioSink * sink, GstOsxAudioSinkClass * gclass) { -/* GstElementClass *klass = GST_ELEMENT_GET_CLASS (sink); */ GST_DEBUG ("Initialising object"); sink->device_id = kAudioDeviceUnknown; - sink->stream_id = kAudioStreamUnknown; + sink->volume = DEFAULT_VOLUME; } static void @@ -207,6 +203,10 @@ gst_osx_audio_sink_set_property (GObject * object, guint prop_id, case ARG_DEVICE: sink->device_id = g_value_get_int (value); break; + case ARG_VOLUME: + sink->volume = g_value_get_double (value); + gst_osx_audio_sink_set_volume (sink); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -222,109 +222,15 @@ gst_osx_audio_sink_get_property (GObject * object, guint prop_id, case ARG_DEVICE: g_value_set_int (value, sink->device_id); break; + case ARG_VOLUME: + g_value_set_double (value, sink->volume); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } -/* GstElement vmethod implementations */ - -/* GstBaseSink vmethod implementations */ -static GstCaps * -gst_osx_audio_sink_getcaps (GstBaseSink * sink) -{ - GstCaps *caps = NULL; - GstOsxAudioSink *osxsink; - OSStatus status; - AudioValueRange *rates = NULL; - UInt32 propertySize; - int i; - gboolean foundFixedRate = FALSE; - GstStructure *structure; - GValue rate_v = { 0 }; - GValue rates_v = { 0 }; - - osxsink = GST_OSX_AUDIO_SINK (sink); - - gst_osx_audio_sink_select_device (osxsink); - - GST_DEBUG_OBJECT (osxsink, "Using device_id %d", (int) osxsink->device_id); - - status = AudioDeviceGetPropertyInfo (osxsink->device_id, 0, /* Master channel */ - FALSE, /* isInput */ - kAudioDevicePropertyAvailableNominalSampleRates, &propertySize, NULL); - - if (status) { - GST_WARNING_OBJECT (osxsink, "Failed to get sample rates size: %ld", - status); - goto done; - } - - GST_DEBUG_OBJECT (osxsink, "Allocating %d bytes for sizes", - (int) propertySize); - rates = g_malloc (propertySize); - - status = AudioDeviceGetProperty (osxsink->device_id, 0, /* Master channel */ - FALSE, /* isInput */ - kAudioDevicePropertyAvailableNominalSampleRates, &propertySize, rates); - - if (status) { - GST_WARNING_OBJECT (osxsink, "Failed to get sample rates: %ld", status); - goto done; - } - - GST_DEBUG_OBJECT (osxsink, "Used %d bytes for sizes", (int) propertySize); - - if (propertySize < sizeof (AudioValueRange)) { - GST_WARNING_OBJECT (osxsink, "Zero sample rates available"); - goto done; - } - - /* Create base caps object, then modify to suit. */ - caps = gst_caps_copy (gst_pad_get_pad_template_caps (GST_BASE_SINK_PAD - (sink))); - structure = gst_caps_get_structure (caps, 0); - - GST_DEBUG - ("Getting available sample rates: Status: %ld number of ranges: %lu", - status, propertySize / sizeof (AudioValueRange)); - - g_value_init (&rates_v, GST_TYPE_LIST); - g_value_init (&rate_v, G_TYPE_INT); - - for (i = 0; i < propertySize / sizeof (AudioValueRange); i++) { - GST_LOG_OBJECT (osxsink, "Range from %f to %f", rates[i].mMinimum, - rates[i].mMaximum); - if (rates[i].mMinimum == rates[i].mMaximum) { - /* For now, we only support these in this form. If there are none - * in this form, we use the first (only) as a range. */ - foundFixedRate = TRUE; - - g_value_set_int (&rate_v, rates[i].mMinimum); - gst_value_list_append_value (&rates_v, &rate_v); - } - } - - g_value_unset (&rate_v); - - if (foundFixedRate) { - gst_structure_set_value (structure, "rate", &rates_v); - } else { - gst_structure_set (structure, "rate", GST_TYPE_INT_RANGE, - rates[0].mMinimum, rates[0].mMaximum, NULL); - } - - g_value_unset (&rates_v); - -done: - if (rates) - g_free (rates); - - return caps; -} - -/* GstBaseAudioSink vmethod implementations */ static GstRingBuffer * gst_osx_audio_sink_create_ringbuffer (GstBaseAudioSink * sink) { @@ -340,35 +246,57 @@ gst_osx_audio_sink_create_ringbuffer (GstBaseAudioSink * sink) GST_DEBUG ("osx sink 0x%p element 0x%p ioproc 0x%p", osxsink, GST_OSX_AUDIO_ELEMENT_GET_INTERFACE (osxsink), (void *) gst_osx_audio_sink_io_proc); + + gst_osx_audio_sink_set_volume (osxsink); + ringbuffer->element = GST_OSX_AUDIO_ELEMENT_GET_INTERFACE (osxsink); ringbuffer->device_id = osxsink->device_id; - ringbuffer->stream_id = osxsink->stream_id; return GST_RING_BUFFER (ringbuffer); } -OSStatus -gst_osx_audio_sink_io_proc (AudioDeviceID inDevice, - const AudioTimeStamp * inNow, const AudioBufferList * inInputData, - const AudioTimeStamp * inInputTime, AudioBufferList * outOutputData, - const AudioTimeStamp * inOutputTime, void *inClientData) +/* HALOutput AudioUnit will request fairly arbitrarily-sized chunks of data, + * not of a fixed size. So, we keep track of where in the current ringbuffer + * segment we are, and only advance the segment once we've read the whole + * thing */ +static OSStatus +gst_osx_audio_sink_io_proc (GstOsxRingBuffer * buf, + AudioUnitRenderActionFlags * ioActionFlags, + const AudioTimeStamp * inTimeStamp, + UInt32 inBusNumber, UInt32 inNumberFrames, AudioBufferList * bufferList) { - GstOsxRingBuffer *buf = GST_OSX_RING_BUFFER (inClientData); - guint8 *readptr; gint readseg; gint len; + gint remaining = bufferList->mBuffers[0].mDataByteSize; + gint offset = 0; - if (gst_ring_buffer_prepare_read (GST_RING_BUFFER (buf), &readseg, &readptr, - &len)) { - outOutputData->mBuffers[0].mDataByteSize = len; - memcpy ((char *) outOutputData->mBuffers[0].mData, readptr, len); + while (remaining) { + if (!gst_ring_buffer_prepare_read (GST_RING_BUFFER (buf), + &readseg, &readptr, &len)) + return 0; - /* clear written samples */ - gst_ring_buffer_clear (GST_RING_BUFFER (buf), readseg); + len -= buf->segoffset; - /* we wrote one segment */ - gst_ring_buffer_advance (GST_RING_BUFFER (buf), 1); + if (len > remaining) + len = remaining; + + memcpy ((char *) bufferList->mBuffers[0].mData + offset, + readptr + buf->segoffset, len); + + buf->segoffset += len; + offset += len; + remaining -= len; + + if ((gint) buf->segoffset == GST_RING_BUFFER (buf)->spec.segsize) { + /* clear written samples */ + gst_ring_buffer_clear (GST_RING_BUFFER (buf), readseg); + + /* we wrote one segment */ + gst_ring_buffer_advance (GST_RING_BUFFER (buf), 1); + + buf->segoffset = 0; + } } return 0; } @@ -378,9 +306,18 @@ gst_osx_audio_sink_osxelement_init (gpointer g_iface, gpointer iface_data) { GstOsxAudioElementInterface *iface = (GstOsxAudioElementInterface *) g_iface; - iface->io_proc = gst_osx_audio_sink_io_proc; + iface->io_proc = (AURenderCallback) gst_osx_audio_sink_io_proc; } +static void +gst_osx_audio_sink_set_volume (GstOsxAudioSink * sink) +{ + if (!sink->audiounit) + return; + + AudioUnitSetParameter (sink->audiounit, kHALOutputParam_Volume, + kAudioUnitScope_Global, 0, (float) sink->volume, 0); +} static void gst_osx_audio_sink_select_device (GstOsxAudioSink * osxsink) @@ -389,67 +326,27 @@ gst_osx_audio_sink_select_device (GstOsxAudioSink * osxsink) UInt32 propertySize; if (osxsink->device_id == kAudioDeviceUnknown) { + /* If no specific device has been selected by the user, then pick the + * default device */ GST_DEBUG_OBJECT (osxsink, "Selecting device for OSXAudioSink"); propertySize = sizeof (osxsink->device_id); status = AudioHardwareGetProperty (kAudioHardwarePropertyDefaultOutputDevice, &propertySize, &osxsink->device_id); - if (status) + if (status) { GST_WARNING_OBJECT (osxsink, "AudioHardwareGetProperty returned %d", (int) status); - else + } else { GST_DEBUG_OBJECT (osxsink, "AudioHardwareGetProperty returned 0"); + } - if (osxsink->device_id == kAudioDeviceUnknown) + if (osxsink->device_id == kAudioDeviceUnknown) { GST_WARNING_OBJECT (osxsink, "AudioHardwareGetProperty: device_id is kAudioDeviceUnknown"); + } GST_DEBUG_OBJECT (osxsink, "AudioHardwareGetProperty: device_id is %lu", (long) osxsink->device_id); } - - if (osxsink->stream_id == kAudioStreamUnknown) { - AudioStreamID *streams; - - GST_DEBUG_OBJECT (osxsink, "Getting streamid"); - status = AudioDeviceGetPropertyInfo (osxsink->device_id, 0, /* Master channel */ - FALSE, /* isInput */ - kAudioDevicePropertyStreams, &propertySize, NULL); - - if (status) { - GST_WARNING_OBJECT (osxsink, - "AudioDeviceGetProperty returned %d", (int) status); - return; - } - - GST_DEBUG_OBJECT (osxsink, - "Getting available streamids from %d (%d bytes)", - (int) (propertySize / sizeof (AudioStreamID)), - (unsigned int) propertySize); - streams = g_malloc (propertySize); - status = AudioDeviceGetProperty (osxsink->device_id, 0, /* Master channel */ - FALSE, /* isInput */ - kAudioDevicePropertyStreams, &propertySize, streams); - - if (status) { - GST_WARNING_OBJECT (osxsink, - "AudioDeviceGetProperty returned %d", (int) status); - g_free (streams); - return; - } - - GST_DEBUG_OBJECT (osxsink, "Getting streamid from %d (%d bytes)", - (int) (propertySize / sizeof (AudioStreamID)), - (unsigned int) propertySize); - - if (propertySize >= sizeof (AudioStreamID)) { - osxsink->stream_id = streams[0]; - GST_DEBUG_OBJECT (osxsink, "Selected stream %d of %d: %d", 0, - (int) (propertySize / sizeof (AudioStreamID)), - (int) osxsink->stream_id); - } - - g_free (streams); - } } diff --git a/sys/osxaudio/gstosxaudiosink.h b/sys/osxaudio/gstosxaudiosink.h index a46d244895..aac9719f9d 100644 --- a/sys/osxaudio/gstosxaudiosink.h +++ b/sys/osxaudio/gstosxaudiosink.h @@ -1,8 +1,8 @@ /* * GStreamer - * Copyright 2005-2006 Zaheer Abbas Merali - * Copyright 2007 Pioneers of the Inevitable - * + * Copyright (C) 2005-2006 Zaheer Abbas Merali + * Copyright (C) 2007 Pioneers of the Inevitable + * * 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 @@ -40,11 +40,10 @@ * License along with this library; if not, write to the * Free Software Foundation, Inc., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, 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 * - + * 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_OSXAUDIOSINK_H__ @@ -56,7 +55,6 @@ G_BEGIN_DECLS -/* #defines don't like whitespacey bits */ #define GST_TYPE_OSX_AUDIO_SINK \ (gst_osx_audio_sink_get_type()) #define GST_OSX_AUDIO_SINK(obj) \ @@ -64,7 +62,7 @@ G_BEGIN_DECLS #define GST_OSX_AUDIO_SINK_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_OSX_AUDIO_SINK,GstOsxAudioSinkClass)) -typedef struct _GstOsxAudioSink GstOsxAudioSink; +typedef struct _GstOsxAudioSink GstOsxAudioSink; typedef struct _GstOsxAudioSinkClass GstOsxAudioSinkClass; struct _GstOsxAudioSink @@ -72,7 +70,8 @@ struct _GstOsxAudioSink GstBaseAudioSink sink; AudioDeviceID device_id; - AudioStreamID stream_id; + AudioUnit audiounit; + double volume; }; struct _GstOsxAudioSinkClass diff --git a/sys/osxaudio/gstosxaudiosrc.c b/sys/osxaudio/gstosxaudiosrc.c index c89bcdb224..1d99e8d892 100644 --- a/sys/osxaudio/gstosxaudiosrc.c +++ b/sys/osxaudio/gstosxaudiosrc.c @@ -1,8 +1,8 @@ /* * GStreamer - * Copyright 2005,2006 Zaheer Abbas Merali - * 2008 Pioneers of the Inevitable - * + * Copyright (C) 2005,2006 Zaheer Abbas Merali + * Copyright (C) 2008 Pioneers of the Inevitable + * * 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 @@ -61,6 +61,7 @@ #include #include +#include #include "gstosxaudiosrc.h" #include "gstosxaudioelement.h" @@ -94,7 +95,7 @@ static GstStaticPadTemplate src_factory = GST_STATIC_PAD_TEMPLATE ("src", "signed = (boolean) { TRUE }, " "width = (int) 32, " "depth = (int) 32, " - "rate = (int) [1, MAX], " "channels = (int) [1, 2]") + "rate = (int) [1, MAX], " "channels = (int) [1, MAX]") ); static void gst_osx_audio_src_set_property (GObject * object, guint prop_id, @@ -102,19 +103,20 @@ static void gst_osx_audio_src_set_property (GObject * object, guint prop_id, static void gst_osx_audio_src_get_property (GObject * object, guint prop_id, GValue * value, GParamSpec * pspec); +static GstCaps *gst_osx_audio_src_get_caps (GstBaseSrc * src); static GstRingBuffer *gst_osx_audio_src_create_ringbuffer (GstBaseAudioSrc * src); static void gst_osx_audio_src_osxelement_init (gpointer g_iface, gpointer iface_data); -OSStatus gst_osx_audio_src_io_proc (AudioDeviceID inDevice, - const AudioTimeStamp * inNow, const AudioBufferList * inInputData, - const AudioTimeStamp * inInputTime, AudioBufferList * outOutputData, - const AudioTimeStamp * inOutputTime, void *inClientData); +static OSStatus gst_osx_audio_src_io_proc (GstOsxRingBuffer * buf, + AudioUnitRenderActionFlags * ioActionFlags, + const AudioTimeStamp * inTimeStamp, UInt32 inBusNumber, + UInt32 inNumberFrames, AudioBufferList * bufferList); static void gst_osx_audio_src_select_device (GstOsxAudioSrc * osxsrc); static void -gst_osx_audio_src_osxelement_do_init (GType type) +gst_osx_audio_src_do_init (GType type) { static const GInterfaceInfo osxelement_info = { gst_osx_audio_src_osxelement_init, @@ -130,8 +132,7 @@ gst_osx_audio_src_osxelement_do_init (GType type) } GST_BOILERPLATE_FULL (GstOsxAudioSrc, gst_osx_audio_src, GstBaseAudioSrc, - GST_TYPE_BASE_AUDIO_SRC, gst_osx_audio_src_osxelement_do_init); - + GST_TYPE_BASE_AUDIO_SRC, gst_osx_audio_src_do_init); static void gst_osx_audio_src_base_init (gpointer g_class) @@ -144,16 +145,17 @@ gst_osx_audio_src_base_init (gpointer g_class) gst_element_class_set_details (element_class, &gst_osx_audio_src_details); } -/* initialize the plugin's class */ static void gst_osx_audio_src_class_init (GstOsxAudioSrcClass * klass) { GObjectClass *gobject_class; GstElementClass *gstelement_class; + GstBaseSrcClass *gstbasesrc_class; GstBaseAudioSrcClass *gstbaseaudiosrc_class; gobject_class = (GObjectClass *) klass; gstelement_class = (GstElementClass *) klass; + gstbasesrc_class = (GstBaseSrcClass *) klass; gstbaseaudiosrc_class = (GstBaseAudioSrcClass *) klass; parent_class = g_type_class_peek_parent (klass); @@ -163,28 +165,23 @@ gst_osx_audio_src_class_init (GstOsxAudioSrcClass * klass) gobject_class->get_property = GST_DEBUG_FUNCPTR (gst_osx_audio_src_get_property); + gstbasesrc_class->get_caps = GST_DEBUG_FUNCPTR (gst_osx_audio_src_get_caps); + g_object_class_install_property (gobject_class, ARG_DEVICE, g_param_spec_int ("device", "Device ID", "Device ID of input device", 0, G_MAXINT, 0, G_PARAM_READWRITE)); gstbaseaudiosrc_class->create_ringbuffer = GST_DEBUG_FUNCPTR (gst_osx_audio_src_create_ringbuffer); - } -/* initialize the new element - * instantiate pads and add them to element - * set functions - * initialize structure - */ static void gst_osx_audio_src_init (GstOsxAudioSrc * src, GstOsxAudioSrcClass * gclass) { -/* GstElementClass *klass = GST_ELEMENT_GET_CLASS (src); */ gst_base_src_set_live (GST_BASE_SRC (src), TRUE); src->device_id = kAudioDeviceUnknown; - src->stream_id = kAudioStreamUnknown; + src->deviceChannels = -1; } static void @@ -219,10 +216,43 @@ gst_osx_audio_src_get_property (GObject * object, guint prop_id, } } -/* GstElement vmethod implementations */ +static GstCaps * +gst_osx_audio_src_get_caps (GstBaseSrc * src) +{ + GstElementClass *gstelement_class; + GstOsxAudioSrc *osxsrc; + GstPadTemplate *pad_template; + GstCaps *caps; + GstStructure *structure; + gint min, max; + gstelement_class = GST_ELEMENT_GET_CLASS (src); + osxsrc = GST_OSX_AUDIO_SRC (src); + + if (osxsrc->deviceChannels == -1) { + /* -1 means we don't know the number of channels yet. for now, return + * template caps. + */ + return NULL; + } + + max = osxsrc->deviceChannels; + if (max < 1) + max = 1; /* 0 channels means 1 channel? */ + + min = MIN (1, max); + + pad_template = gst_element_class_get_pad_template (gstelement_class, "src"); + g_return_val_if_fail (pad_template != NULL, NULL); + + caps = gst_caps_copy (gst_pad_template_get_caps (pad_template)); + + structure = gst_caps_get_structure (caps, 0); + gst_structure_set (structure, "channels", GST_TYPE_INT_RANGE, min, max, NULL); + + return caps; +} -/* GstBaseAudioSrc vmethod implementations */ static GstRingBuffer * gst_osx_audio_src_create_ringbuffer (GstBaseAudioSrc * src) { @@ -235,42 +265,63 @@ gst_osx_audio_src_create_ringbuffer (GstBaseAudioSrc * src) GST_DEBUG ("Creating ringbuffer"); ringbuffer = g_object_new (GST_TYPE_OSX_RING_BUFFER, NULL); - - /* change the device to the Default Input Device */ GST_DEBUG ("osx src 0x%p element 0x%p ioproc 0x%p", osxsrc, GST_OSX_AUDIO_ELEMENT_GET_INTERFACE (osxsrc), (void *) gst_osx_audio_src_io_proc); ringbuffer->element = GST_OSX_AUDIO_ELEMENT_GET_INTERFACE (osxsrc); + ringbuffer->is_src = TRUE; ringbuffer->device_id = osxsrc->device_id; - ringbuffer->stream_id = osxsrc->stream_id; return GST_RING_BUFFER (ringbuffer); } -OSStatus -gst_osx_audio_src_io_proc (AudioDeviceID inDevice, const AudioTimeStamp * inNow, - const AudioBufferList * inInputData, const AudioTimeStamp * inInputTime, - AudioBufferList * outOutputData, const AudioTimeStamp * inOutputTime, - void *inClientData) +static OSStatus +gst_osx_audio_src_io_proc (GstOsxRingBuffer * buf, + AudioUnitRenderActionFlags * ioActionFlags, + const AudioTimeStamp * inTimeStamp, + UInt32 inBusNumber, UInt32 inNumberFrames, AudioBufferList * bufferList) { - GstOsxRingBuffer *buf = GST_OSX_RING_BUFFER (inClientData); - + OSStatus status; guint8 *writeptr; gint writeseg; gint len; - gint bytesToCopy; + gint remaining; + gint offset = 0; - if (gst_ring_buffer_prepare_read (GST_RING_BUFFER (buf), &writeseg, &writeptr, - &len)) { - bytesToCopy = inInputData->mBuffers[0].mDataByteSize; - memcpy (writeptr, (char *) inInputData->mBuffers[0].mData, bytesToCopy); + status = AudioUnitRender (buf->audiounit, ioActionFlags, inTimeStamp, + inBusNumber, inNumberFrames, buf->recBufferList); - /* clear written samples */ - /*gst_ring_buffer_clear (GST_RING_BUFFER(buf), writeseg); */ + if (status) { + GST_WARNING_OBJECT (buf, "AudioUnitRender returned %d", (int) status); + return status; + } - /* we wrote one segment */ - gst_ring_buffer_advance (GST_RING_BUFFER (buf), 1); + remaining = buf->recBufferList->mBuffers[0].mDataByteSize; + + while (remaining) { + if (!gst_ring_buffer_prepare_read (GST_RING_BUFFER (buf), + &writeseg, &writeptr, &len)) + return 0; + + len -= buf->segoffset; + + if (len > remaining) + len = remaining; + + memcpy (writeptr + buf->segoffset, + (char *) buf->recBufferList->mBuffers[0].mData + offset, len); + + buf->segoffset += len; + offset += len; + remaining -= len; + + if ((gint) buf->segoffset == GST_RING_BUFFER (buf)->spec.segsize) { + /* we wrote one segment */ + gst_ring_buffer_advance (GST_RING_BUFFER (buf), 1); + + buf->segoffset = 0; + } } return 0; } @@ -280,7 +331,7 @@ gst_osx_audio_src_osxelement_init (gpointer g_iface, gpointer iface_data) { GstOsxAudioElementInterface *iface = (GstOsxAudioElementInterface *) g_iface; - iface->io_proc = gst_osx_audio_src_io_proc; + iface->io_proc = (AURenderCallback) gst_osx_audio_src_io_proc; } static void @@ -290,66 +341,26 @@ gst_osx_audio_src_select_device (GstOsxAudioSrc * osxsrc) 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) + if (status) { GST_WARNING_OBJECT (osxsrc, "AudioHardwareGetProperty returned %d", (int) status); - else + } else { GST_DEBUG_OBJECT (osxsrc, "AudioHardwareGetProperty returned 0"); + } - if (osxsrc->device_id == kAudioDeviceUnknown) + 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); } - - if (osxsrc->stream_id == kAudioStreamUnknown) { - AudioStreamID *streams; - - GST_DEBUG_OBJECT (osxsrc, "Getting streamid"); - status = AudioDeviceGetPropertyInfo (osxsrc->device_id, 0, /* Master channel */ - FALSE, /* isInput */ - kAudioDevicePropertyStreams, &propertySize, NULL); - - if (status) { - GST_WARNING_OBJECT (osxsrc, - "AudioDeviceGetProperty returned %d", (int) status); - return; - } - - GST_DEBUG_OBJECT (osxsrc, - "Getting available streamids from %d (%d bytes)", - (int) (propertySize / sizeof (AudioStreamID)), - (unsigned int) propertySize); - streams = g_malloc (propertySize); - status = AudioDeviceGetProperty (osxsrc->device_id, 0, /* Master channel */ - FALSE, /* isInput */ - kAudioDevicePropertyStreams, &propertySize, streams); - - if (status) { - GST_WARNING_OBJECT (osxsrc, - "AudioDeviceGetProperty returned %d", (int) status); - g_free (streams); - return; - } - - GST_DEBUG_OBJECT (osxsrc, "Getting streamid from %d (%d bytes)", - (int) (propertySize / sizeof (AudioStreamID)), - (unsigned int) propertySize); - - if (propertySize >= sizeof (AudioStreamID)) { - osxsrc->stream_id = streams[0]; - GST_DEBUG_OBJECT (osxsrc, "Selected stream %d of %d: %d", 0, - (int) (propertySize / sizeof (AudioStreamID)), - (int) osxsrc->stream_id); - } - - g_free (streams); - } } diff --git a/sys/osxaudio/gstosxaudiosrc.h b/sys/osxaudio/gstosxaudiosrc.h index 7336c82f19..a812d9efe9 100644 --- a/sys/osxaudio/gstosxaudiosrc.h +++ b/sys/osxaudio/gstosxaudiosrc.h @@ -1,7 +1,7 @@ /* * GStreamer - * Copyright 2005-2006 Zaheer Abbas Merali - * + * Copyright (C) 2005-2006 Zaheer Abbas Merali + * * 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 @@ -50,7 +50,6 @@ G_BEGIN_DECLS -/* #defines don't like whitespacey bits */ #define GST_TYPE_OSX_AUDIO_SRC \ (gst_osx_audio_src_get_type()) #define GST_OSX_AUDIO_SRC(obj) \ @@ -58,7 +57,7 @@ G_BEGIN_DECLS #define GST_OSX_AUDIO_SRC_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_OSX_AUDIO_SRC,GstOsxAudioSrcClass)) -typedef struct _GstOsxAudioSrc GstOsxAudioSrc; +typedef struct _GstOsxAudioSrc GstOsxAudioSrc; typedef struct _GstOsxAudioSrcClass GstOsxAudioSrcClass; struct _GstOsxAudioSrc @@ -66,7 +65,9 @@ struct _GstOsxAudioSrc GstBaseAudioSrc src; AudioDeviceID device_id; - AudioStreamID stream_id; + + /* actual number of channels reported by input device */ + int deviceChannels; }; struct _GstOsxAudioSrcClass diff --git a/sys/osxaudio/gstosxringbuffer.c b/sys/osxaudio/gstosxringbuffer.c index c99e3f37ad..f6fd2e520f 100644 --- a/sys/osxaudio/gstosxringbuffer.c +++ b/sys/osxaudio/gstosxringbuffer.c @@ -1,7 +1,8 @@ /* * GStreamer - * Copyright 2006 Zaheer Abbas Merali - * + * Copyright (C) 2006 Zaheer Abbas Merali + * Copyright (C) 2008 Pioneers of the Inevitable + * * 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 #include +#include #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); -GType -gst_osx_ring_buffer_get_type (void) +static AudioBufferList *buffer_list_alloc (int channels, int size); +static void buffer_list_free (AudioBufferList * list); + +static void +gst_osx_ring_buffer_do_init (GType type) { - static GType ringbuffer_type = 0; + GST_DEBUG_CATEGORY_INIT (osx_audio_debug, "osxaudio", 0, + "OSX Audio Elements"); +} - 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"); +GST_BOILERPLATE_FULL (GstOsxRingBuffer, gst_osx_ring_buffer, GstRingBuffer, + GST_TYPE_RING_BUFFER, gst_osx_ring_buffer_do_init); - ringbuffer_type = - g_type_register_static (GST_TYPE_RING_BUFFER, "GstOsxRingBuffer", - &ringbuffer_info, 0); - } - return ringbuffer_type; +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); + (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, "Using stream_id %d, setting output format", - (int) osxbuf->stream_id); + GST_DEBUG_OBJECT (osxbuf, "Setting format for AudioUnit"); - propertySize = sizeof (asbd); - status = AudioStreamSetProperty (osxbuf->stream_id, NULL, /* Change immediately */ - 0, /* Master channel */ - kAudioStreamPropertyVirtualFormat, propertySize, &asbd); + 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); - - if (status) { - GST_WARNING_OBJECT (osxbuf, - "AudioDeviceGetProperty returned %d when getting " - "kAudioDevicePropertyBufferSize", (int) status); - } - GST_DEBUG_OBJECT (osxbuf, "%5d osxbuf->buffer_len", (int) buffer_len); - spec->segsize = buffer_len; + spec->segsize = 4096; spec->segtotal = 16; - GST_DEBUG_OBJECT (osxbuf, "osx ring buffer acquired"); + /* 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)); - return TRUE; + 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, + "Failed to initialise AudioUnit: %d", (int) status); + goto done; + } + + GST_DEBUG_OBJECT (osxbuf, "osx ring buffer acquired"); + + ret = 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 ("AudioDeviceAddIOProc returned %" GST_FOURCC_FORMAT, - GST_FOURCC_ARGS (status)); + GST_WARNING ("AudioUnitSetProperty returned %d", (int) status); return FALSE; } - osxbuf->io_proc_active = TRUE; - } + // ### does it make sense to do this notify stuff for input mode? + status = AudioUnitAddRenderNotify (osxbuf->audiounit, + (AURenderCallback) gst_osx_ring_buffer_render_notify, osxbuf); - status = AudioDeviceStart (osxbuf->device_id, osxbuf->element->io_proc); + if (status) { + GST_WARNING ("AudioUnitAddRenderNotify returned %d", (int) status); + return FALSE; + } + + osxbuf->io_proc_active = TRUE; + } else + osxbuf->io_proc_needs_deactivation = FALSE; + + 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); - if (status) - GST_WARNING ("AudioDeviceStop returned %d", (int) status); + status = AudioOutputUnitStop (osxbuf->audiounit); + if (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); } diff --git a/sys/osxaudio/gstosxringbuffer.h b/sys/osxaudio/gstosxringbuffer.h index 40a299ff4a..5e6dbe41c5 100644 --- a/sys/osxaudio/gstosxringbuffer.h +++ b/sys/osxaudio/gstosxringbuffer.h @@ -1,7 +1,7 @@ /* * GStreamer - * Copyright 2006 Zaheer Abbas Merali - * + * Copyright (C) 2006 Zaheer Abbas Merali + * * 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 @@ -47,37 +47,48 @@ #include #include #include - #include "gstosxaudioelement.h" G_BEGIN_DECLS -#define GST_TYPE_OSX_RING_BUFFER (gst_osx_ring_buffer_get_type()) -#define GST_OSX_RING_BUFFER(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_OSX_RING_BUFFER,GstOsxRingBuffer)) -#define GST_OSX_RING_BUFFER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_OSX_RING_BUFFER,GstOsxRingBufferClass)) -#define GST_OSX_RING_BUFFER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj),GST_TYPE_OSX_RING_BUFFER,GstOsxRingBufferClass)) -#define GST_IS_OSX_RING_BUFFER(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_OSX_RING_BUFFER)) -#define GST_IS_OSX_RING_BUFFER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_OSX_RING_BUFFER)) +#define GST_TYPE_OSX_RING_BUFFER \ + (gst_osx_ring_buffer_get_type()) +#define GST_OSX_RING_BUFFER(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_OSX_RING_BUFFER,GstOsxRingBuffer)) +#define GST_OSX_RING_BUFFER_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_OSX_RING_BUFFER,GstOsxRingBufferClass)) +#define GST_OSX_RING_BUFFER_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS((obj),GST_TYPE_OSX_RING_BUFFER,GstOsxRingBufferClass)) +#define GST_IS_OSX_RING_BUFFER(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_OSX_RING_BUFFER)) +#define GST_IS_OSX_RING_BUFFER_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_OSX_RING_BUFFER)) typedef struct _GstOsxRingBuffer GstOsxRingBuffer; typedef struct _GstOsxRingBufferClass GstOsxRingBufferClass; +struct _GstOsxRingBuffer +{ + GstRingBuffer object; -struct _GstOsxRingBuffer { - GstRingBuffer object; - - AudioDeviceID device_id; - AudioStreamID stream_id; - gboolean io_proc_active; - guint buffer_len; - GstOsxAudioElementInterface* element; + gboolean is_src; + AudioUnit audiounit; + AudioDeviceID device_id; + gboolean io_proc_active; + gboolean io_proc_needs_deactivation; + guint buffer_len; + guint segoffset; + AudioBufferList * recBufferList; + GstOsxAudioElementInterface * element; }; -struct _GstOsxRingBufferClass { - GstRingBufferClass parent_class; +struct _GstOsxRingBufferClass +{ + GstRingBufferClass parent_class; }; GType gst_osx_ring_buffer_get_type (void); + G_END_DECLS -#endif +#endif /* __GST_OSX_RING_BUFFER_H__ */