sys/osxaudio/: Rewrite osxaudio to work more flexibly and more reliably, using a different abstraction layer of corea...

Original commit message from CVS:
Patch by: Justin Karnegas <justin@affinix.com> and
Michael Smith <msmith@songbirdnest.com>
* sys/osxaudio/gstosxaudio.c:
* sys/osxaudio/gstosxaudioelement.c:
* sys/osxaudio/gstosxaudioelement.h:
* sys/osxaudio/gstosxaudiosink.c:
* sys/osxaudio/gstosxaudiosink.h:
* sys/osxaudio/gstosxaudiosrc.c:
* sys/osxaudio/gstosxaudiosrc.h:
* sys/osxaudio/gstosxringbuffer.c:
* sys/osxaudio/gstosxringbuffer.h:
Rewrite osxaudio to work more flexibly and more reliably, using a
different abstraction layer of coreaudio that is the recommended way of
doing low-level audio I/O on OSX.
Fixes byg #564948.
This commit is contained in:
Justin Karnegas 2009-01-02 20:39:34 +00:00 committed by Michael Smith
parent 996fb72681
commit 17bb67f873
10 changed files with 741 additions and 451 deletions

View file

@ -1,3 +1,21 @@
2009-01-02 Michael Smith <msmith@songbirdnest.com>
Patch by: Justin Karnegas <justin@affinix.com> and
Michael Smith <msmith@songbirdnest.com>
* sys/osxaudio/gstosxaudio.c:
* sys/osxaudio/gstosxaudioelement.c:
* sys/osxaudio/gstosxaudioelement.h:
* sys/osxaudio/gstosxaudiosink.c:
* sys/osxaudio/gstosxaudiosink.h:
* sys/osxaudio/gstosxaudiosrc.c:
* sys/osxaudio/gstosxaudiosrc.h:
* sys/osxaudio/gstosxringbuffer.c:
* sys/osxaudio/gstosxringbuffer.h:
Rewrite osxaudio to work more flexibly and more reliably, using a
different abstraction layer of coreaudio that is the recommended way of
doing low-level audio I/O on OSX.
Fixes byg #564948.
2009-01-02 Wim Taymans <wim.taymans@collabora.co.uk> 2009-01-02 Wim Taymans <wim.taymans@collabora.co.uk>
* tests/examples/rtp/server-decodebin-H263p-AMR.sh: * tests/examples/rtp/server-decodebin-H263p-AMR.sh:

View file

@ -1,5 +1,6 @@
/* GStreamer /*
* Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu> * GStreamer
* Copyright (C) 1999 Erik Walthinsen <omega@cse.ogi.edu>
* Copyright (C) 2007,2008 Pioneers of the Inevitable <songbird@songbirdnest.com> * Copyright (C) 2007,2008 Pioneers of the Inevitable <songbird@songbirdnest.com>
* *
* This library is free software; you can redistribute it and/or * 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 * License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330, * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA. * 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 * Pioneers of the Inevitable, the creators of the Songbird Music player
* *
*/ */
/** /**
@ -42,12 +43,10 @@
* Last reviewed on 2006-03-01 (0.10.4) * Last reviewed on 2006-03-01 (0.10.4)
*/ */
#ifdef HAVE_CONFIG_H #ifdef HAVE_CONFIG_H
#include "config.h" #include "config.h"
#endif #endif
#include "gstosxaudioelement.h" #include "gstosxaudioelement.h"
#include "gstosxaudiosink.h" #include "gstosxaudiosink.h"
#include "gstosxaudiosrc.h" #include "gstosxaudiosrc.h"
@ -55,7 +54,6 @@
static gboolean static gboolean
plugin_init (GstPlugin * plugin) plugin_init (GstPlugin * plugin)
{ {
if (!gst_element_register (plugin, "osxaudiosink", GST_RANK_PRIMARY, if (!gst_element_register (plugin, "osxaudiosink", GST_RANK_PRIMARY,
GST_TYPE_OSX_AUDIO_SINK)) { GST_TYPE_OSX_AUDIO_SINK)) {
return FALSE; return FALSE;

View file

@ -1,8 +1,8 @@
/* /*
* GStreamer * GStreamer
* Copyright 2006 Zaheer Abbas Merali <zaheerabbas at merali dot org> * Copyright (C) 2006 Zaheer Abbas Merali <zaheerabbas at merali dot org>
* Copyright 2007 Pioneers of the Inevitable <songbird@songbirdnest.com> * Copyright (C) 2007 Pioneers of the Inevitable <songbird@songbirdnest.com>
* *
* Permission is hereby granted, free of charge, to any person obtaining a * Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"), * copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation * to deal in the Software without restriction, including without limitation
@ -40,9 +40,10 @@
* License along with this library; if not, write to the * License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330, * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA. * 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 <gst/gst.h> #include <gst/gst.h>
@ -67,12 +68,11 @@ gst_osx_audio_element_get_type ()
0, 0,
0, 0,
NULL, NULL,
NULL
}; };
gst_osxaudioelement_type = g_type_register_static (G_TYPE_INTERFACE, gst_osxaudioelement_type = g_type_register_static (G_TYPE_INTERFACE,
"GstOsxAudioElement", &gst_osxaudioelement_info, 0); "GstOsxAudioElement", &gst_osxaudioelement_info, 0);
/*g_type_interface_add_prerequisite (gst_osxaudioelement_type,
GST_TYPE_IMPLEMENTS_INTERFACE); */
} }
return gst_osxaudioelement_type; return gst_osxaudioelement_type;
@ -84,11 +84,9 @@ gst_osx_audio_element_class_init (GstOsxAudioElementInterface * klass)
static gboolean initialized = FALSE; static gboolean initialized = FALSE;
if (!initialized) { if (!initialized) {
initialized = TRUE; initialized = TRUE;
} }
/* default virtual functions */ /* default virtual functions */
klass->io_proc = NULL; klass->io_proc = NULL;
} }

View file

@ -1,8 +1,8 @@
/* /*
* GStreamer * GStreamer
* Copyright 2006 Zaheer Abbas Merali <zaheerabbas at merali dot org> * Copyright (C) 2006 Zaheer Abbas Merali <zaheerabbas at merali dot org>
* Copyright 2007 Pioneers of the Inevitable <songbird@songbirdnest.com> * Copyright (C) 2007 Pioneers of the Inevitable <songbird@songbirdnest.com>
* *
* Permission is hereby granted, free of charge, to any person obtaining a * Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"), * copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation * to deal in the Software without restriction, including without limitation
@ -41,8 +41,9 @@
* Free Software Foundation, Inc., 59 Temple Place - Suite 330, * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA. * 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__ #ifndef __GST_OSX_AUDIO_ELEMENT_H__
@ -50,22 +51,34 @@
#include <gst/gst.h> #include <gst/gst.h>
#include <CoreAudio/CoreAudio.h> #include <CoreAudio/CoreAudio.h>
#include <AudioUnit/AudioUnit.h>
#define GST_OSX_AUDIO_ELEMENT_TYPE (gst_osx_audio_element_get_type()) G_BEGIN_DECLS
#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_TYPE \
#define GST_OSX_AUDIO_ELEMENT_GET_INTERFACE(inst) (G_TYPE_INSTANCE_GET_INTERFACE ((inst), GST_OSX_AUDIO_ELEMENT_TYPE, GstOsxAudioElementInterface)) (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; typedef struct _GstOsxAudioElementInterface GstOsxAudioElementInterface;
struct _GstOsxAudioElementInterface { struct _GstOsxAudioElementInterface
GTypeInterface parent; {
GTypeInterface parent;
OSStatus (*io_proc) (AudioDeviceID inDevice, const AudioTimeStamp *inNow, const AudioBufferList *inInputData, const AudioTimeStamp *inInputTime, AudioBufferList *outOutputData, const AudioTimeStamp *inOutputTime, void *inClientData);
OSStatus (*io_proc) (void * userdata,
AudioUnitRenderActionFlags * ioActionFlags,
const AudioTimeStamp * inTimeStamp,
UInt32 inBusNumber, UInt32 inNumberFrames,
AudioBufferList * bufferList);
}; };
GType gst_osx_audio_element_get_type (void); GType gst_osx_audio_element_get_type (void);
G_END_DECLS
#endif #endif /* __GST_OSX_AUDIO_ELEMENT_H__ */

View file

@ -1,8 +1,8 @@
/* /*
* GStreamer * GStreamer
* Copyright 2005,2006 Zaheer Abbas Merali <zaheerabbas at merali dot org> * Copyright (C) 2005,2006 Zaheer Abbas Merali <zaheerabbas at merali dot org>
* Copyright 2007 Pioneers of the Inevitable <songbird@songbirdnest.com> * Copyright (C) 2007,2008 Pioneers of the Inevitable <songbird@songbirdnest.com>
* *
* Permission is hereby granted, free of charge, to any person obtaining a * Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"), * copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation * to deal in the Software without restriction, including without limitation
@ -41,8 +41,8 @@
* Free Software Foundation, Inc., 59 Temple Place - Suite 330, * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA. * 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. * Pioneers of the Inevitable, the creators of the Songbird Music player
* *
*/ */
@ -67,8 +67,6 @@
#include <CoreAudio/CoreAudio.h> #include <CoreAudio/CoreAudio.h>
#include <CoreAudio/AudioHardware.h> #include <CoreAudio/AudioHardware.h>
#include "gstosxaudiosink.h" #include "gstosxaudiosink.h"
#include "gstosxaudiosrc.h"
#include "gstosxaudioelement.h" #include "gstosxaudioelement.h"
GST_DEBUG_CATEGORY_STATIC (osx_audiosink_debug); GST_DEBUG_CATEGORY_STATIC (osx_audiosink_debug);
@ -90,9 +88,12 @@ enum
enum enum
{ {
ARG_0, ARG_0,
ARG_DEVICE ARG_DEVICE,
ARG_VOLUME
}; };
#define DEFAULT_VOLUME 1.0
static GstStaticPadTemplate sink_factory = GST_STATIC_PAD_TEMPLATE ("sink", static GstStaticPadTemplate sink_factory = GST_STATIC_PAD_TEMPLATE ("sink",
GST_PAD_SINK, GST_PAD_SINK,
GST_PAD_ALWAYS, GST_PAD_ALWAYS,
@ -101,27 +102,28 @@ static GstStaticPadTemplate sink_factory = GST_STATIC_PAD_TEMPLATE ("sink",
"signed = (boolean) { TRUE }, " "signed = (boolean) { TRUE }, "
"width = (int) 32, " "width = (int) 32, "
"depth = (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, static void gst_osx_audio_sink_set_property (GObject * object, guint prop_id,
const GValue * value, GParamSpec * pspec); const GValue * value, GParamSpec * pspec);
static void gst_osx_audio_sink_get_property (GObject * object, guint prop_id, static void gst_osx_audio_sink_get_property (GObject * object, guint prop_id,
GValue * value, GParamSpec * pspec); GValue * value, GParamSpec * pspec);
static GstCaps *gst_osx_audio_sink_getcaps (GstBaseSink * sink);
static GstRingBuffer *gst_osx_audio_sink_create_ringbuffer (GstBaseAudioSink * static GstRingBuffer *gst_osx_audio_sink_create_ringbuffer (GstBaseAudioSink *
sink); sink);
static void gst_osx_audio_sink_osxelement_init (gpointer g_iface, static void gst_osx_audio_sink_osxelement_init (gpointer g_iface,
gpointer iface_data); 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_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 static void
gst_osx_audio_sink_osxelement_do_init (GType type) gst_osx_audio_sink_do_init (GType type)
{ {
static const GInterfaceInfo osxelement_info = { static const GInterfaceInfo osxelement_info = {
gst_osx_audio_sink_osxelement_init, 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_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 static void
gst_osx_audio_sink_base_init (gpointer g_class) 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); gst_element_class_set_details (element_class, &gst_osx_audio_sink_details);
} }
/* initialize the plugin's class */
static void static void
gst_osx_audio_sink_class_init (GstOsxAudioSinkClass * klass) 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", g_param_spec_int ("device", "Device ID", "Device ID of output device",
0, G_MAXINT, 0, G_PARAM_READWRITE)); 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 = gstbaseaudiosink_class->create_ringbuffer =
GST_DEBUG_FUNCPTR (gst_osx_audio_sink_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 static void
gst_osx_audio_sink_init (GstOsxAudioSink * sink, GstOsxAudioSinkClass * gclass) gst_osx_audio_sink_init (GstOsxAudioSink * sink, GstOsxAudioSinkClass * gclass)
{ {
/* GstElementClass *klass = GST_ELEMENT_GET_CLASS (sink); */
GST_DEBUG ("Initialising object"); GST_DEBUG ("Initialising object");
sink->device_id = kAudioDeviceUnknown; sink->device_id = kAudioDeviceUnknown;
sink->stream_id = kAudioStreamUnknown; sink->volume = DEFAULT_VOLUME;
} }
static void static void
@ -207,6 +203,10 @@ gst_osx_audio_sink_set_property (GObject * object, guint prop_id,
case ARG_DEVICE: case ARG_DEVICE:
sink->device_id = g_value_get_int (value); sink->device_id = g_value_get_int (value);
break; break;
case ARG_VOLUME:
sink->volume = g_value_get_double (value);
gst_osx_audio_sink_set_volume (sink);
break;
default: default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break; break;
@ -222,109 +222,15 @@ gst_osx_audio_sink_get_property (GObject * object, guint prop_id,
case ARG_DEVICE: case ARG_DEVICE:
g_value_set_int (value, sink->device_id); g_value_set_int (value, sink->device_id);
break; break;
case ARG_VOLUME:
g_value_set_double (value, sink->volume);
break;
default: default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break; 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 * static GstRingBuffer *
gst_osx_audio_sink_create_ringbuffer (GstBaseAudioSink * sink) 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_DEBUG ("osx sink 0x%p element 0x%p ioproc 0x%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);
ringbuffer->element = GST_OSX_AUDIO_ELEMENT_GET_INTERFACE (osxsink); ringbuffer->element = GST_OSX_AUDIO_ELEMENT_GET_INTERFACE (osxsink);
ringbuffer->device_id = osxsink->device_id; ringbuffer->device_id = osxsink->device_id;
ringbuffer->stream_id = osxsink->stream_id;
return GST_RING_BUFFER (ringbuffer); return GST_RING_BUFFER (ringbuffer);
} }
OSStatus /* HALOutput AudioUnit will request fairly arbitrarily-sized chunks of data,
gst_osx_audio_sink_io_proc (AudioDeviceID inDevice, * not of a fixed size. So, we keep track of where in the current ringbuffer
const AudioTimeStamp * inNow, const AudioBufferList * inInputData, * segment we are, and only advance the segment once we've read the whole
const AudioTimeStamp * inInputTime, AudioBufferList * outOutputData, * thing */
const AudioTimeStamp * inOutputTime, void *inClientData) 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; guint8 *readptr;
gint readseg; gint readseg;
gint len; gint len;
gint remaining = bufferList->mBuffers[0].mDataByteSize;
gint offset = 0;
if (gst_ring_buffer_prepare_read (GST_RING_BUFFER (buf), &readseg, &readptr, while (remaining) {
&len)) { if (!gst_ring_buffer_prepare_read (GST_RING_BUFFER (buf),
outOutputData->mBuffers[0].mDataByteSize = len; &readseg, &readptr, &len))
memcpy ((char *) outOutputData->mBuffers[0].mData, readptr, len); return 0;
/* clear written samples */ len -= buf->segoffset;
gst_ring_buffer_clear (GST_RING_BUFFER (buf), readseg);
/* we wrote one segment */ if (len > remaining)
gst_ring_buffer_advance (GST_RING_BUFFER (buf), 1); 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; return 0;
} }
@ -378,9 +306,18 @@ gst_osx_audio_sink_osxelement_init (gpointer g_iface, gpointer iface_data)
{ {
GstOsxAudioElementInterface *iface = (GstOsxAudioElementInterface *) g_iface; 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 static void
gst_osx_audio_sink_select_device (GstOsxAudioSink * osxsink) gst_osx_audio_sink_select_device (GstOsxAudioSink * osxsink)
@ -389,67 +326,27 @@ gst_osx_audio_sink_select_device (GstOsxAudioSink * osxsink)
UInt32 propertySize; UInt32 propertySize;
if (osxsink->device_id == kAudioDeviceUnknown) { 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"); GST_DEBUG_OBJECT (osxsink, "Selecting device for OSXAudioSink");
propertySize = sizeof (osxsink->device_id); propertySize = sizeof (osxsink->device_id);
status = status =
AudioHardwareGetProperty (kAudioHardwarePropertyDefaultOutputDevice, AudioHardwareGetProperty (kAudioHardwarePropertyDefaultOutputDevice,
&propertySize, &osxsink->device_id); &propertySize, &osxsink->device_id);
if (status) if (status) {
GST_WARNING_OBJECT (osxsink, GST_WARNING_OBJECT (osxsink,
"AudioHardwareGetProperty returned %d", (int) status); "AudioHardwareGetProperty returned %d", (int) status);
else } else {
GST_DEBUG_OBJECT (osxsink, "AudioHardwareGetProperty returned 0"); GST_DEBUG_OBJECT (osxsink, "AudioHardwareGetProperty returned 0");
}
if (osxsink->device_id == kAudioDeviceUnknown) if (osxsink->device_id == kAudioDeviceUnknown) {
GST_WARNING_OBJECT (osxsink, GST_WARNING_OBJECT (osxsink,
"AudioHardwareGetProperty: device_id is kAudioDeviceUnknown"); "AudioHardwareGetProperty: device_id is kAudioDeviceUnknown");
}
GST_DEBUG_OBJECT (osxsink, "AudioHardwareGetProperty: device_id is %lu", GST_DEBUG_OBJECT (osxsink, "AudioHardwareGetProperty: device_id is %lu",
(long) osxsink->device_id); (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);
}
} }

View file

@ -1,8 +1,8 @@
/* /*
* GStreamer * GStreamer
* Copyright 2005-2006 Zaheer Abbas Merali <zaheerabbas at merali dot org> * Copyright (C) 2005-2006 Zaheer Abbas Merali <zaheerabbas at merali dot org>
* Copyright 2007 Pioneers of the Inevitable <songbird@songbirdnest.com> * Copyright (C) 2007 Pioneers of the Inevitable <songbird@songbirdnest.com>
* *
* Permission is hereby granted, free of charge, to any person obtaining a * Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"), * copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation * to deal in the Software without restriction, including without limitation
@ -40,11 +40,10 @@
* License along with this library; if not, write to the * License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330, * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA. * 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__ #ifndef __GST_OSXAUDIOSINK_H__
@ -56,7 +55,6 @@
G_BEGIN_DECLS G_BEGIN_DECLS
/* #defines don't like whitespacey bits */
#define GST_TYPE_OSX_AUDIO_SINK \ #define GST_TYPE_OSX_AUDIO_SINK \
(gst_osx_audio_sink_get_type()) (gst_osx_audio_sink_get_type())
#define GST_OSX_AUDIO_SINK(obj) \ #define GST_OSX_AUDIO_SINK(obj) \
@ -64,7 +62,7 @@ G_BEGIN_DECLS
#define GST_OSX_AUDIO_SINK_CLASS(klass) \ #define GST_OSX_AUDIO_SINK_CLASS(klass) \
(G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_OSX_AUDIO_SINK,GstOsxAudioSinkClass)) (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_OSX_AUDIO_SINK,GstOsxAudioSinkClass))
typedef struct _GstOsxAudioSink GstOsxAudioSink; typedef struct _GstOsxAudioSink GstOsxAudioSink;
typedef struct _GstOsxAudioSinkClass GstOsxAudioSinkClass; typedef struct _GstOsxAudioSinkClass GstOsxAudioSinkClass;
struct _GstOsxAudioSink struct _GstOsxAudioSink
@ -72,7 +70,8 @@ struct _GstOsxAudioSink
GstBaseAudioSink sink; GstBaseAudioSink sink;
AudioDeviceID device_id; AudioDeviceID device_id;
AudioStreamID stream_id; AudioUnit audiounit;
double volume;
}; };
struct _GstOsxAudioSinkClass struct _GstOsxAudioSinkClass

View file

@ -1,8 +1,8 @@
/* /*
* GStreamer * GStreamer
* Copyright 2005,2006 Zaheer Abbas Merali <zaheerabbas at merali dot org> * Copyright (C) 2005,2006 Zaheer Abbas Merali <zaheerabbas at merali dot org>
* 2008 Pioneers of the Inevitable <songbird@songbirdnest.com> * Copyright (C) 2008 Pioneers of the Inevitable <songbird@songbirdnest.com>
* *
* Permission is hereby granted, free of charge, to any person obtaining a * Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"), * copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation * to deal in the Software without restriction, including without limitation
@ -61,6 +61,7 @@
#include <gst/gst.h> #include <gst/gst.h>
#include <CoreAudio/CoreAudio.h> #include <CoreAudio/CoreAudio.h>
#include <CoreAudio/AudioHardware.h>
#include "gstosxaudiosrc.h" #include "gstosxaudiosrc.h"
#include "gstosxaudioelement.h" #include "gstosxaudioelement.h"
@ -94,7 +95,7 @@ static GstStaticPadTemplate src_factory = GST_STATIC_PAD_TEMPLATE ("src",
"signed = (boolean) { TRUE }, " "signed = (boolean) { TRUE }, "
"width = (int) 32, " "width = (int) 32, "
"depth = (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, 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, static void gst_osx_audio_src_get_property (GObject * object, guint prop_id,
GValue * value, GParamSpec * pspec); GValue * value, GParamSpec * pspec);
static GstCaps *gst_osx_audio_src_get_caps (GstBaseSrc * src);
static GstRingBuffer *gst_osx_audio_src_create_ringbuffer (GstBaseAudioSrc * static GstRingBuffer *gst_osx_audio_src_create_ringbuffer (GstBaseAudioSrc *
src); src);
static void gst_osx_audio_src_osxelement_init (gpointer g_iface, static void gst_osx_audio_src_osxelement_init (gpointer g_iface,
gpointer iface_data); gpointer iface_data);
OSStatus gst_osx_audio_src_io_proc (AudioDeviceID inDevice, static OSStatus gst_osx_audio_src_io_proc (GstOsxRingBuffer * buf,
const AudioTimeStamp * inNow, const AudioBufferList * inInputData, AudioUnitRenderActionFlags * ioActionFlags,
const AudioTimeStamp * inInputTime, AudioBufferList * outOutputData, const AudioTimeStamp * inTimeStamp, UInt32 inBusNumber,
const AudioTimeStamp * inOutputTime, void *inClientData); UInt32 inNumberFrames, AudioBufferList * bufferList);
static void gst_osx_audio_src_select_device (GstOsxAudioSrc * osxsrc); static void gst_osx_audio_src_select_device (GstOsxAudioSrc * osxsrc);
static void static void
gst_osx_audio_src_osxelement_do_init (GType type) gst_osx_audio_src_do_init (GType type)
{ {
static const GInterfaceInfo osxelement_info = { static const GInterfaceInfo osxelement_info = {
gst_osx_audio_src_osxelement_init, 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_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 static void
gst_osx_audio_src_base_init (gpointer g_class) 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); gst_element_class_set_details (element_class, &gst_osx_audio_src_details);
} }
/* initialize the plugin's class */
static void static void
gst_osx_audio_src_class_init (GstOsxAudioSrcClass * klass) gst_osx_audio_src_class_init (GstOsxAudioSrcClass * klass)
{ {
GObjectClass *gobject_class; GObjectClass *gobject_class;
GstElementClass *gstelement_class; GstElementClass *gstelement_class;
GstBaseSrcClass *gstbasesrc_class;
GstBaseAudioSrcClass *gstbaseaudiosrc_class; GstBaseAudioSrcClass *gstbaseaudiosrc_class;
gobject_class = (GObjectClass *) klass; gobject_class = (GObjectClass *) klass;
gstelement_class = (GstElementClass *) klass; gstelement_class = (GstElementClass *) klass;
gstbasesrc_class = (GstBaseSrcClass *) klass;
gstbaseaudiosrc_class = (GstBaseAudioSrcClass *) klass; gstbaseaudiosrc_class = (GstBaseAudioSrcClass *) klass;
parent_class = g_type_class_peek_parent (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 = gobject_class->get_property =
GST_DEBUG_FUNCPTR (gst_osx_audio_src_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_object_class_install_property (gobject_class, ARG_DEVICE,
g_param_spec_int ("device", "Device ID", "Device ID of input device", g_param_spec_int ("device", "Device ID", "Device ID of input device",
0, G_MAXINT, 0, G_PARAM_READWRITE)); 0, G_MAXINT, 0, G_PARAM_READWRITE));
gstbaseaudiosrc_class->create_ringbuffer = gstbaseaudiosrc_class->create_ringbuffer =
GST_DEBUG_FUNCPTR (gst_osx_audio_src_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 static void
gst_osx_audio_src_init (GstOsxAudioSrc * src, GstOsxAudioSrcClass * gclass) 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); gst_base_src_set_live (GST_BASE_SRC (src), TRUE);
src->device_id = kAudioDeviceUnknown; src->device_id = kAudioDeviceUnknown;
src->stream_id = kAudioStreamUnknown; src->deviceChannels = -1;
} }
static void 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 * static GstRingBuffer *
gst_osx_audio_src_create_ringbuffer (GstBaseAudioSrc * src) gst_osx_audio_src_create_ringbuffer (GstBaseAudioSrc * src)
{ {
@ -235,42 +265,63 @@ gst_osx_audio_src_create_ringbuffer (GstBaseAudioSrc * src)
GST_DEBUG ("Creating ringbuffer"); GST_DEBUG ("Creating ringbuffer");
ringbuffer = g_object_new (GST_TYPE_OSX_RING_BUFFER, NULL); 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_DEBUG ("osx src 0x%p element 0x%p ioproc 0x%p", osxsrc,
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->element = GST_OSX_AUDIO_ELEMENT_GET_INTERFACE (osxsrc);
ringbuffer->is_src = TRUE;
ringbuffer->device_id = osxsrc->device_id; ringbuffer->device_id = osxsrc->device_id;
ringbuffer->stream_id = osxsrc->stream_id;
return GST_RING_BUFFER (ringbuffer); return GST_RING_BUFFER (ringbuffer);
} }
OSStatus static OSStatus
gst_osx_audio_src_io_proc (AudioDeviceID inDevice, const AudioTimeStamp * inNow, gst_osx_audio_src_io_proc (GstOsxRingBuffer * buf,
const AudioBufferList * inInputData, const AudioTimeStamp * inInputTime, AudioUnitRenderActionFlags * ioActionFlags,
AudioBufferList * outOutputData, const AudioTimeStamp * inOutputTime, const AudioTimeStamp * inTimeStamp,
void *inClientData) UInt32 inBusNumber, UInt32 inNumberFrames, AudioBufferList * bufferList)
{ {
GstOsxRingBuffer *buf = GST_OSX_RING_BUFFER (inClientData); OSStatus status;
guint8 *writeptr; guint8 *writeptr;
gint writeseg; gint writeseg;
gint len; gint len;
gint bytesToCopy; gint remaining;
gint offset = 0;
if (gst_ring_buffer_prepare_read (GST_RING_BUFFER (buf), &writeseg, &writeptr, status = AudioUnitRender (buf->audiounit, ioActionFlags, inTimeStamp,
&len)) { inBusNumber, inNumberFrames, buf->recBufferList);
bytesToCopy = inInputData->mBuffers[0].mDataByteSize;
memcpy (writeptr, (char *) inInputData->mBuffers[0].mData, bytesToCopy);
/* clear written samples */ if (status) {
/*gst_ring_buffer_clear (GST_RING_BUFFER(buf), writeseg); */ GST_WARNING_OBJECT (buf, "AudioUnitRender returned %d", (int) status);
return status;
}
/* we wrote one segment */ remaining = buf->recBufferList->mBuffers[0].mDataByteSize;
gst_ring_buffer_advance (GST_RING_BUFFER (buf), 1);
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; return 0;
} }
@ -280,7 +331,7 @@ gst_osx_audio_src_osxelement_init (gpointer g_iface, gpointer iface_data)
{ {
GstOsxAudioElementInterface *iface = (GstOsxAudioElementInterface *) g_iface; 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 static void
@ -290,66 +341,26 @@ gst_osx_audio_src_select_device (GstOsxAudioSrc * osxsrc)
UInt32 propertySize; UInt32 propertySize;
if (osxsrc->device_id == kAudioDeviceUnknown) { 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"); GST_DEBUG_OBJECT (osxsrc, "Selecting device for OSXAudioSrc");
propertySize = sizeof (osxsrc->device_id); propertySize = sizeof (osxsrc->device_id);
status = AudioHardwareGetProperty (kAudioHardwarePropertyDefaultInputDevice, status = AudioHardwareGetProperty (kAudioHardwarePropertyDefaultInputDevice,
&propertySize, &osxsrc->device_id); &propertySize, &osxsrc->device_id);
if (status) if (status) {
GST_WARNING_OBJECT (osxsrc, GST_WARNING_OBJECT (osxsrc,
"AudioHardwareGetProperty returned %d", (int) status); "AudioHardwareGetProperty returned %d", (int) status);
else } else {
GST_DEBUG_OBJECT (osxsrc, "AudioHardwareGetProperty returned 0"); GST_DEBUG_OBJECT (osxsrc, "AudioHardwareGetProperty returned 0");
}
if (osxsrc->device_id == kAudioDeviceUnknown) if (osxsrc->device_id == kAudioDeviceUnknown) {
GST_WARNING_OBJECT (osxsrc, GST_WARNING_OBJECT (osxsrc,
"AudioHardwareGetProperty: device_id is kAudioDeviceUnknown"); "AudioHardwareGetProperty: device_id is kAudioDeviceUnknown");
}
GST_DEBUG_OBJECT (osxsrc, "AudioHardwareGetProperty: device_id is %lu", GST_DEBUG_OBJECT (osxsrc, "AudioHardwareGetProperty: device_id is %lu",
(long) osxsrc->device_id); (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);
}
} }

View file

@ -1,7 +1,7 @@
/* /*
* GStreamer * GStreamer
* Copyright 2005-2006 Zaheer Abbas Merali <zaheerabbas at merali dot org> * Copyright (C) 2005-2006 Zaheer Abbas Merali <zaheerabbas at merali dot org>
* *
* Permission is hereby granted, free of charge, to any person obtaining a * Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"), * copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation * to deal in the Software without restriction, including without limitation
@ -50,7 +50,6 @@
G_BEGIN_DECLS G_BEGIN_DECLS
/* #defines don't like whitespacey bits */
#define GST_TYPE_OSX_AUDIO_SRC \ #define GST_TYPE_OSX_AUDIO_SRC \
(gst_osx_audio_src_get_type()) (gst_osx_audio_src_get_type())
#define GST_OSX_AUDIO_SRC(obj) \ #define GST_OSX_AUDIO_SRC(obj) \
@ -58,7 +57,7 @@ G_BEGIN_DECLS
#define GST_OSX_AUDIO_SRC_CLASS(klass) \ #define GST_OSX_AUDIO_SRC_CLASS(klass) \
(G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_OSX_AUDIO_SRC,GstOsxAudioSrcClass)) (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_OSX_AUDIO_SRC,GstOsxAudioSrcClass))
typedef struct _GstOsxAudioSrc GstOsxAudioSrc; typedef struct _GstOsxAudioSrc GstOsxAudioSrc;
typedef struct _GstOsxAudioSrcClass GstOsxAudioSrcClass; typedef struct _GstOsxAudioSrcClass GstOsxAudioSrcClass;
struct _GstOsxAudioSrc struct _GstOsxAudioSrc
@ -66,7 +65,9 @@ struct _GstOsxAudioSrc
GstBaseAudioSrc src; GstBaseAudioSrc src;
AudioDeviceID device_id; AudioDeviceID device_id;
AudioStreamID stream_id;
/* actual number of channels reported by input device */
int deviceChannels;
}; };
struct _GstOsxAudioSrcClass struct _GstOsxAudioSrcClass

View file

@ -1,7 +1,8 @@
/* /*
* GStreamer * GStreamer
* Copyright 2006 Zaheer Abbas Merali <zaheerabbas at merali dot org> * Copyright (C) 2006 Zaheer Abbas Merali <zaheerabbas at merali dot org>
* * Copyright (C) 2008 Pioneers of the Inevitable <songbird@songbirdnest.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a * Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"), * copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation * to deal in the Software without restriction, including without limitation
@ -43,14 +44,14 @@
#include <CoreAudio/CoreAudio.h> #include <CoreAudio/CoreAudio.h>
#include <gst/gst.h> #include <gst/gst.h>
#include <gst/audio/multichannel.h>
#include "gstosxringbuffer.h" #include "gstosxringbuffer.h"
#include "gstosxaudiosink.h"
#include "gstosxaudiosrc.h"
GST_DEBUG_CATEGORY_STATIC (osx_audio_debug); GST_DEBUG_CATEGORY_STATIC (osx_audio_debug);
#define GST_CAT_DEFAULT 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_dispose (GObject * object);
static void gst_osx_ring_buffer_finalize (GObject * object); static void gst_osx_ring_buffer_finalize (GObject * object);
static gboolean gst_osx_ring_buffer_open_device (GstRingBuffer * buf); 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); GstRingBufferSpec * spec);
static gboolean gst_osx_ring_buffer_release (GstRingBuffer * buf); 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_start (GstRingBuffer * buf);
static gboolean gst_osx_ring_buffer_pause (GstRingBuffer * buf); static gboolean gst_osx_ring_buffer_pause (GstRingBuffer * buf);
static gboolean gst_osx_ring_buffer_stop (GstRingBuffer * buf); static gboolean gst_osx_ring_buffer_stop (GstRingBuffer * buf);
static guint gst_osx_ring_buffer_delay (GstRingBuffer * buf); static guint gst_osx_ring_buffer_delay (GstRingBuffer * buf);
static GstRingBufferClass *ring_parent_class = NULL; 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 static AudioBufferList *buffer_list_alloc (int channels, int size);
gst_osx_ring_buffer_get_type (void) 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) { GST_BOILERPLATE_FULL (GstOsxRingBuffer, gst_osx_ring_buffer, GstRingBuffer,
static const GTypeInfo ringbuffer_info = { GST_TYPE_RING_BUFFER, gst_osx_ring_buffer_do_init);
sizeof (GstOsxRingBufferClass),
NULL,
NULL,
(GClassInitFunc) gst_osx_ring_buffer_class_init,
NULL,
NULL,
sizeof (GstOsxRingBuffer),
0,
(GInstanceInitFunc) gst_osx_ring_buffer_init,
NULL
};
GST_DEBUG_CATEGORY_INIT (osx_audio_debug, "osxaudio", 0,
"OSX Audio Elements");
GST_DEBUG ("Creating osx ring buffer type");
ringbuffer_type = static void
g_type_register_static (GST_TYPE_RING_BUFFER, "GstOsxRingBuffer", gst_osx_ring_buffer_base_init (gpointer g_class)
&ringbuffer_info, 0); {
} /* Nothing to do right now */
return ringbuffer_type;
} }
static void static void
@ -152,159 +144,463 @@ gst_osx_ring_buffer_finalize (GObject * object)
G_OBJECT_CLASS (ring_parent_class)->finalize (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 static gboolean
gst_osx_ring_buffer_open_device (GstRingBuffer * buf) 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; return TRUE;
} }
static gboolean static gboolean
gst_osx_ring_buffer_close_device (GstRingBuffer * buf) 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; 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 static gboolean
gst_osx_ring_buffer_acquire (GstRingBuffer * buf, GstRingBufferSpec * spec) gst_osx_ring_buffer_acquire (GstRingBuffer * buf, GstRingBufferSpec * spec)
{ {
/* Configure the output stream and allocate ringbuffer memory */ /* Configure the output stream and allocate ringbuffer memory */
GstOsxRingBuffer *osxbuf; GstOsxRingBuffer *osxbuf;
AudioStreamBasicDescription asbd; AudioStreamBasicDescription format;
AudioChannelLayout *layout = NULL;
OSStatus status; OSStatus status;
UInt32 buffer_len;
UInt32 propertySize; 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); osxbuf = GST_OSX_RING_BUFFER (buf);
/* Fill out the audio description we're going to be using */ /* Fill out the audio description we're going to be using */
asbd.mFormatID = kAudioFormatLinearPCM; format.mFormatID = kAudioFormatLinearPCM;
asbd.mSampleRate = (double) spec->rate; format.mSampleRate = (double) spec->rate;
asbd.mChannelsPerFrame = spec->channels; format.mChannelsPerFrame = spec->channels;
asbd.mFormatFlags = kAudioFormatFlagsNativeFloatPacked; format.mFormatFlags = kAudioFormatFlagsNativeFloatPacked;
asbd.mBytesPerFrame = spec->channels * sizeof (float); format.mBytesPerFrame = spec->channels * sizeof (float);
asbd.mBitsPerChannel = sizeof (float) * 8; format.mBitsPerChannel = sizeof (float) * 8;
asbd.mBytesPerPacket = spec->channels * sizeof (float); format.mBytesPerPacket = spec->channels * sizeof (float);
asbd.mFramesPerPacket = 1; format.mFramesPerPacket = 1;
asbd.mReserved = 0; 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", GST_LOG_OBJECT (osxbuf, "Format: %x, %f, %u, %x, %d, %d, %d, %d, %d",
(unsigned int) asbd.mFormatID, (unsigned int) format.mFormatID,
asbd.mSampleRate, format.mSampleRate,
(unsigned int) asbd.mChannelsPerFrame, (unsigned int) format.mChannelsPerFrame,
(unsigned int) asbd.mFormatFlags, (unsigned int) format.mFormatFlags,
(unsigned int) asbd.mBytesPerFrame, (unsigned int) format.mBytesPerFrame,
(unsigned int) asbd.mBitsPerChannel, (unsigned int) format.mBitsPerChannel,
(unsigned int) asbd.mBytesPerPacket, (unsigned int) format.mBytesPerPacket,
(unsigned int) asbd.mFramesPerPacket, (unsigned int) asbd.mReserved); (unsigned int) format.mFramesPerPacket, (unsigned int) format.mReserved);
GST_DEBUG_OBJECT (osxbuf, "Using stream_id %d, setting output format", GST_DEBUG_OBJECT (osxbuf, "Setting format for AudioUnit");
(int) osxbuf->stream_id);
propertySize = sizeof (asbd); scope = osxbuf->is_src ? kAudioUnitScope_Output : kAudioUnitScope_Input;
status = AudioStreamSetProperty (osxbuf->stream_id, NULL, /* Change immediately */ element = osxbuf->is_src ? 1 : 0;
0, /* Master channel */
kAudioStreamPropertyVirtualFormat, propertySize, &asbd); propertySize = sizeof (format);
status = AudioUnitSetProperty (osxbuf->audiounit,
kAudioUnitProperty_StreamFormat, scope, element, &format, propertySize);
if (status) { 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); status);
return FALSE; goto done;
} }
/* get requested buffer length to use */ spec->segsize = 4096;
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->segtotal = 16; 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); buf->data = gst_buffer_new_and_alloc (spec->segtotal * spec->segsize);
memset (GST_BUFFER_DATA (buf->data), 0, GST_BUFFER_SIZE (buf->data)); 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 static gboolean
gst_osx_ring_buffer_release (GstRingBuffer * buf) gst_osx_ring_buffer_release (GstRingBuffer * buf)
{ {
/* stub, we need to deallocate ringbuffer memory */
GstOsxRingBuffer *osxbuf; GstOsxRingBuffer *osxbuf;
osxbuf = GST_OSX_RING_BUFFER (buf); osxbuf = GST_OSX_RING_BUFFER (buf);
AudioUnitUninitialize (osxbuf->audiounit);
gst_buffer_unref (buf->data); gst_buffer_unref (buf->data);
buf->data = NULL; buf->data = NULL;
if (osxbuf->recBufferList) {
buffer_list_free (osxbuf->recBufferList);
osxbuf->recBufferList = NULL;
}
return TRUE; 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 static gboolean
gst_osx_ring_buffer_start (GstRingBuffer * buf) gst_osx_ring_buffer_start (GstRingBuffer * buf)
{ {
/* stub */
OSStatus status; OSStatus status;
GstOsxRingBuffer *osxbuf; GstOsxRingBuffer *osxbuf;
AURenderCallbackStruct input;
AudioUnitPropertyID callback_type;
osxbuf = GST_OSX_RING_BUFFER (buf); osxbuf = GST_OSX_RING_BUFFER (buf);
GST_DEBUG ("osx ring buffer start ioproc: 0x%p device_id %lu", GST_DEBUG ("osx ring buffer start ioproc: 0x%p device_id %lu",
osxbuf->element->io_proc, osxbuf->device_id); osxbuf->element->io_proc, osxbuf->device_id);
if (!osxbuf->io_proc_active) { if (!osxbuf->io_proc_active) {
status = callback_type = osxbuf->is_src ?
AudioDeviceAddIOProc (osxbuf->device_id, osxbuf->element->io_proc, kAudioOutputUnitProperty_SetInputCallback :
osxbuf); 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) { if (status) {
GST_WARNING ("AudioDeviceAddIOProc returned %" GST_FOURCC_FORMAT, GST_WARNING ("AudioUnitSetProperty returned %d", (int) status);
GST_FOURCC_ARGS (status));
return FALSE; 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) { if (status) {
GST_WARNING ("AudioDeviceStart returned %d", (int) status); GST_WARNING ("AudioOutputUnitStart returned %d", (int) status);
return FALSE; return FALSE;
} }
return TRUE; return TRUE;
} }
// ###
static gboolean static gboolean
gst_osx_ring_buffer_pause (GstRingBuffer * buf) gst_osx_ring_buffer_pause (GstRingBuffer * buf)
{ {
/* stub */
/* stop callback */
OSErr status;
GstOsxRingBuffer *osxbuf = GST_OSX_RING_BUFFER (buf); GstOsxRingBuffer *osxbuf = GST_OSX_RING_BUFFER (buf);
GST_DEBUG ("osx ring buffer pause ioproc: 0x%p device_id %lu", GST_DEBUG ("osx ring buffer pause ioproc: 0x%p device_id %lu",
osxbuf->element->io_proc, osxbuf->device_id); osxbuf->element->io_proc, osxbuf->device_id);
if (osxbuf->io_proc_active) { if (osxbuf->io_proc_active) {
status = /* CoreAudio isn't threadsafe enough to do this here; we must deactivate
AudioDeviceRemoveIOProc (osxbuf->device_id, osxbuf->element->io_proc); * the render callback elsewhere. See:
if (status) * http://lists.apple.com/archives/Coreaudio-api/2006/Mar/msg00010.html
GST_WARNING ("AudioDeviceRemoveIOProc " "returned %d", (int) status); */
osxbuf->io_proc_active = FALSE; osxbuf->io_proc_needs_deactivation = TRUE;
} }
return TRUE; return TRUE;
} }
// ###
static gboolean static gboolean
gst_osx_ring_buffer_stop (GstRingBuffer * buf) gst_osx_ring_buffer_stop (GstRingBuffer * buf)
{ {
/* stub */
OSErr status; OSErr status;
GstOsxRingBuffer *osxbuf; 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", GST_DEBUG ("osx ring buffer stop ioproc: 0x%p device_id %lu",
osxbuf->element->io_proc, osxbuf->device_id); 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) { if (osxbuf->io_proc_active) {
status = gst_osx_ring_buffer_remove_render_callback (osxbuf);
AudioDeviceRemoveIOProc (osxbuf->device_id, osxbuf->element->io_proc);
if (status)
GST_WARNING ("AudioDeviceRemoveIOProc " "returned %d", (int) status);
osxbuf->io_proc_active = FALSE;
} }
return TRUE; return TRUE;
} }
@ -330,6 +623,57 @@ gst_osx_ring_buffer_stop (GstRingBuffer * buf)
static guint static guint
gst_osx_ring_buffer_delay (GstRingBuffer * buf) gst_osx_ring_buffer_delay (GstRingBuffer * buf)
{ {
/* stub */ double latency;
return 0; 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);
} }

View file

@ -1,7 +1,7 @@
/* /*
* GStreamer * GStreamer
* Copyright 2006 Zaheer Abbas Merali <zaheerabbas at merali dot org> * Copyright (C) 2006 Zaheer Abbas Merali <zaheerabbas at merali dot org>
* *
* Permission is hereby granted, free of charge, to any person obtaining a * Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"), * copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation * to deal in the Software without restriction, including without limitation
@ -47,37 +47,48 @@
#include <gst/gst.h> #include <gst/gst.h>
#include <gst/audio/gstringbuffer.h> #include <gst/audio/gstringbuffer.h>
#include <CoreAudio/CoreAudio.h> #include <CoreAudio/CoreAudio.h>
#include "gstosxaudioelement.h" #include "gstosxaudioelement.h"
G_BEGIN_DECLS G_BEGIN_DECLS
#define GST_TYPE_OSX_RING_BUFFER (gst_osx_ring_buffer_get_type()) #define GST_TYPE_OSX_RING_BUFFER \
#define GST_OSX_RING_BUFFER(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_OSX_RING_BUFFER,GstOsxRingBuffer)) (gst_osx_ring_buffer_get_type())
#define GST_OSX_RING_BUFFER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_OSX_RING_BUFFER,GstOsxRingBufferClass)) #define GST_OSX_RING_BUFFER(obj) \
#define GST_OSX_RING_BUFFER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj),GST_TYPE_OSX_RING_BUFFER,GstOsxRingBufferClass)) (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_OSX_RING_BUFFER,GstOsxRingBuffer))
#define GST_IS_OSX_RING_BUFFER(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_OSX_RING_BUFFER)) #define GST_OSX_RING_BUFFER_CLASS(klass) \
#define GST_IS_OSX_RING_BUFFER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_OSX_RING_BUFFER)) (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 _GstOsxRingBuffer GstOsxRingBuffer;
typedef struct _GstOsxRingBufferClass GstOsxRingBufferClass; typedef struct _GstOsxRingBufferClass GstOsxRingBufferClass;
struct _GstOsxRingBuffer
{
GstRingBuffer object;
struct _GstOsxRingBuffer { gboolean is_src;
GstRingBuffer object; AudioUnit audiounit;
AudioDeviceID device_id;
AudioDeviceID device_id; gboolean io_proc_active;
AudioStreamID stream_id; gboolean io_proc_needs_deactivation;
gboolean io_proc_active; guint buffer_len;
guint buffer_len; guint segoffset;
GstOsxAudioElementInterface* element; AudioBufferList * recBufferList;
GstOsxAudioElementInterface * element;
}; };
struct _GstOsxRingBufferClass { struct _GstOsxRingBufferClass
GstRingBufferClass parent_class; {
GstRingBufferClass parent_class;
}; };
GType gst_osx_ring_buffer_get_type (void); GType gst_osx_ring_buffer_get_type (void);
G_END_DECLS G_END_DECLS
#endif #endif /* __GST_OSX_RING_BUFFER_H__ */