mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-11-30 05:31:15 +00:00
osxaudio: add a façade for the CoreAudio API
This commit is contained in:
parent
400222e29f
commit
9621074006
10 changed files with 2161 additions and 1669 deletions
|
@ -4,6 +4,8 @@ libgstosxaudio_la_SOURCES = gstosxringbuffer.c \
|
||||||
gstosxaudioelement.c \
|
gstosxaudioelement.c \
|
||||||
gstosxaudiosink.c \
|
gstosxaudiosink.c \
|
||||||
gstosxaudiosrc.c \
|
gstosxaudiosrc.c \
|
||||||
|
gstosxcoreaudiocommon.c \
|
||||||
|
gstosxcoreaudio.c \
|
||||||
gstosxaudio.c
|
gstosxaudio.c
|
||||||
|
|
||||||
libgstosxaudio_la_CFLAGS = $(GST_PLUGINS_BASE_CFLAGS) $(GST_CFLAGS) \
|
libgstosxaudio_la_CFLAGS = $(GST_PLUGINS_BASE_CFLAGS) $(GST_CFLAGS) \
|
||||||
|
@ -21,7 +23,9 @@ noinst_HEADERS = gstosxaudiosink.h \
|
||||||
gstosxaudioelement.h \
|
gstosxaudioelement.h \
|
||||||
gstosxringbuffer.h \
|
gstosxringbuffer.h \
|
||||||
gstosxaudiosrc.h \
|
gstosxaudiosrc.h \
|
||||||
gstosxcoreaudio.h
|
gstosxcoreaudiocommon.h \
|
||||||
|
gstosxcoreaudio.h \
|
||||||
|
gstosxcoreaudiohal.c
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -69,8 +69,6 @@
|
||||||
#include <gst/gst.h>
|
#include <gst/gst.h>
|
||||||
#include <gst/audio/multichannel.h>
|
#include <gst/audio/multichannel.h>
|
||||||
#include <gst/audio/gstaudioiec61937.h>
|
#include <gst/audio/gstaudioiec61937.h>
|
||||||
#include <CoreAudio/CoreAudio.h>
|
|
||||||
#include <CoreAudio/AudioHardware.h>
|
|
||||||
|
|
||||||
#include "gstosxaudiosink.h"
|
#include "gstosxaudiosink.h"
|
||||||
#include "gstosxaudioelement.h"
|
#include "gstosxaudioelement.h"
|
||||||
|
@ -171,6 +169,7 @@ gst_osx_audio_sink_do_init (GType type)
|
||||||
|
|
||||||
GST_DEBUG_CATEGORY_INIT (osx_audiosink_debug, "osxaudiosink", 0,
|
GST_DEBUG_CATEGORY_INIT (osx_audiosink_debug, "osxaudiosink", 0,
|
||||||
"OSX Audio Sink");
|
"OSX Audio Sink");
|
||||||
|
gst_core_audio_init_debug ();
|
||||||
GST_DEBUG ("Adding static interface");
|
GST_DEBUG ("Adding static interface");
|
||||||
g_type_add_interface_static (type, GST_OSX_AUDIO_ELEMENT_TYPE,
|
g_type_add_interface_static (type, GST_OSX_AUDIO_ELEMENT_TYPE,
|
||||||
&osxelement_info);
|
&osxelement_info);
|
||||||
|
@ -425,19 +424,22 @@ gst_osx_audio_sink_create_ringbuffer (GstBaseAudioSink * sink)
|
||||||
osxsink = GST_OSX_AUDIO_SINK (sink);
|
osxsink = GST_OSX_AUDIO_SINK (sink);
|
||||||
|
|
||||||
if (!gst_osx_audio_sink_select_device (osxsink)) {
|
if (!gst_osx_audio_sink_select_device (osxsink)) {
|
||||||
|
GST_ERROR_OBJECT (sink, "Could not select device");
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
GST_DEBUG ("Creating ringbuffer");
|
GST_DEBUG_OBJECT (sink, "Creating ringbuffer");
|
||||||
ringbuffer = g_object_new (GST_TYPE_OSX_RING_BUFFER, NULL);
|
ringbuffer = g_object_new (GST_TYPE_OSX_RING_BUFFER, NULL);
|
||||||
GST_DEBUG ("osx sink %p element %p ioproc %p", osxsink,
|
GST_DEBUG_OBJECT (sink, "osx sink %p element %p ioproc %p", osxsink,
|
||||||
GST_OSX_AUDIO_ELEMENT_GET_INTERFACE (osxsink),
|
GST_OSX_AUDIO_ELEMENT_GET_INTERFACE (osxsink),
|
||||||
(void *) gst_osx_audio_sink_io_proc);
|
(void *) gst_osx_audio_sink_io_proc);
|
||||||
|
|
||||||
gst_osx_audio_sink_set_volume (osxsink);
|
gst_osx_audio_sink_set_volume (osxsink);
|
||||||
|
|
||||||
ringbuffer->element = GST_OSX_AUDIO_ELEMENT_GET_INTERFACE (osxsink);
|
ringbuffer->core_audio->element =
|
||||||
ringbuffer->device_id = osxsink->device_id;
|
GST_OSX_AUDIO_ELEMENT_GET_INTERFACE (osxsink);
|
||||||
|
ringbuffer->core_audio->device_id = osxsink->device_id;
|
||||||
|
ringbuffer->core_audio->is_src = FALSE;
|
||||||
|
|
||||||
return GST_RING_BUFFER (ringbuffer);
|
return GST_RING_BUFFER (ringbuffer);
|
||||||
}
|
}
|
||||||
|
@ -455,7 +457,7 @@ gst_osx_audio_sink_io_proc (GstOsxRingBuffer * buf,
|
||||||
guint8 *readptr;
|
guint8 *readptr;
|
||||||
gint readseg;
|
gint readseg;
|
||||||
gint len;
|
gint len;
|
||||||
gint stream_idx = buf->stream_idx;
|
gint stream_idx = buf->core_audio->stream_idx;
|
||||||
gint remaining = bufferList->mBuffers[stream_idx].mDataByteSize;
|
gint remaining = bufferList->mBuffers[stream_idx].mDataByteSize;
|
||||||
gint offset = 0;
|
gint offset = 0;
|
||||||
|
|
||||||
|
@ -500,35 +502,13 @@ gst_osx_audio_sink_osxelement_init (gpointer g_iface, gpointer iface_data)
|
||||||
static void
|
static void
|
||||||
gst_osx_audio_sink_set_volume (GstOsxAudioSink * sink)
|
gst_osx_audio_sink_set_volume (GstOsxAudioSink * sink)
|
||||||
{
|
{
|
||||||
if (!sink->audiounit)
|
GstOsxRingBuffer *osxbuf;
|
||||||
|
|
||||||
|
osxbuf = GST_OSX_RING_BUFFER (GST_BASE_AUDIO_SINK (sink)->ringbuffer);
|
||||||
|
if (!osxbuf)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
AudioUnitSetParameter (sink->audiounit, kHALOutputParam_Volume,
|
gst_core_audio_set_volume (osxbuf->core_audio, sink->volume);
|
||||||
kAudioUnitScope_Global, 0, (float) sink->volume, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline void
|
|
||||||
_dump_channel_layout (AudioChannelLayout * channel_layout)
|
|
||||||
{
|
|
||||||
UInt32 i;
|
|
||||||
|
|
||||||
GST_DEBUG ("mChannelLayoutTag: 0x%lx",
|
|
||||||
(unsigned long) channel_layout->mChannelLayoutTag);
|
|
||||||
GST_DEBUG ("mChannelBitmap: 0x%lx",
|
|
||||||
(unsigned long) channel_layout->mChannelBitmap);
|
|
||||||
GST_DEBUG ("mNumberChannelDescriptions: %lu",
|
|
||||||
(unsigned long) channel_layout->mNumberChannelDescriptions);
|
|
||||||
for (i = 0; i < channel_layout->mNumberChannelDescriptions; i++) {
|
|
||||||
AudioChannelDescription *channel_desc =
|
|
||||||
&channel_layout->mChannelDescriptions[i];
|
|
||||||
GST_DEBUG (" mChannelLabel: 0x%lx mChannelFlags: 0x%lx "
|
|
||||||
"mCoordinates[0]: %f mCoordinates[1]: %f "
|
|
||||||
"mCoordinates[2]: %f",
|
|
||||||
(unsigned long) channel_desc->mChannelLabel,
|
|
||||||
(unsigned long) channel_desc->mChannelFlags,
|
|
||||||
channel_desc->mCoordinates[0], channel_desc->mCoordinates[1],
|
|
||||||
channel_desc->mCoordinates[2]);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static gboolean
|
static gboolean
|
||||||
|
@ -554,14 +534,14 @@ gst_osx_audio_sink_allowed_caps (GstOsxAudioSink * osxsink)
|
||||||
};
|
};
|
||||||
|
|
||||||
/* First collect info about the HW capabilites and preferences */
|
/* First collect info about the HW capabilites and preferences */
|
||||||
spdif_allowed = _audio_device_is_spdif_avail (osxsink->device_id);
|
spdif_allowed =
|
||||||
layout = _audio_device_get_channel_layout (osxsink->device_id);
|
gst_core_audio_audio_device_is_spdif_avail (osxsink->device_id);
|
||||||
|
layout = gst_core_audio_audio_device_get_channel_layout (osxsink->device_id);
|
||||||
|
|
||||||
GST_DEBUG_OBJECT (osxsink, "Selected device ID: %u SPDIF allowed: %d",
|
GST_DEBUG_OBJECT (osxsink, "Selected device ID: %u SPDIF allowed: %d",
|
||||||
(unsigned) osxsink->device_id, spdif_allowed);
|
(unsigned) osxsink->device_id, spdif_allowed);
|
||||||
|
|
||||||
if (layout) {
|
if (layout) {
|
||||||
_dump_channel_layout (layout);
|
|
||||||
max_channels = layout->mNumberChannelDescriptions;
|
max_channels = layout->mNumberChannelDescriptions;
|
||||||
} else {
|
} else {
|
||||||
GST_WARNING_OBJECT (osxsink, "This driver does not support "
|
GST_WARNING_OBJECT (osxsink, "This driver does not support "
|
||||||
|
@ -656,71 +636,11 @@ gst_osx_audio_sink_allowed_caps (GstOsxAudioSink * osxsink)
|
||||||
static gboolean
|
static gboolean
|
||||||
gst_osx_audio_sink_select_device (GstOsxAudioSink * osxsink)
|
gst_osx_audio_sink_select_device (GstOsxAudioSink * osxsink)
|
||||||
{
|
{
|
||||||
AudioDeviceID *devices = NULL;
|
|
||||||
AudioDeviceID default_device_id = 0;
|
|
||||||
AudioChannelLayout *channel_layout;
|
|
||||||
gint i, ndevices = 0;
|
|
||||||
gboolean res = FALSE;
|
gboolean res = FALSE;
|
||||||
|
|
||||||
devices = _audio_system_get_devices (&ndevices);
|
if (!gst_core_audio_select_device (&osxsink->device_id))
|
||||||
|
return FALSE;
|
||||||
if (ndevices < 1) {
|
|
||||||
GST_ERROR_OBJECT (osxsink, "no audio output devices found");
|
|
||||||
goto done;
|
|
||||||
}
|
|
||||||
|
|
||||||
GST_DEBUG_OBJECT (osxsink, "found %d audio device(s)", ndevices);
|
|
||||||
|
|
||||||
for (i = 0; i < ndevices; i++) {
|
|
||||||
gchar *device_name;
|
|
||||||
|
|
||||||
if ((device_name = _audio_device_get_name (devices[i]))) {
|
|
||||||
if (!_audio_device_has_output (devices[i])) {
|
|
||||||
GST_DEBUG_OBJECT (osxsink, "Input Device ID: %u Name: %s",
|
|
||||||
(unsigned) devices[i], device_name);
|
|
||||||
} else {
|
|
||||||
GST_DEBUG_OBJECT (osxsink, "Output Device ID: %u Name: %s",
|
|
||||||
(unsigned) devices[i], device_name);
|
|
||||||
|
|
||||||
channel_layout = _audio_device_get_channel_layout (devices[i]);
|
|
||||||
if (channel_layout) {
|
|
||||||
_dump_channel_layout (channel_layout);
|
|
||||||
g_free (channel_layout);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
g_free (device_name);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Find the ID of the default output device */
|
|
||||||
default_device_id = _audio_system_get_default_output ();
|
|
||||||
|
|
||||||
/* Here we decide if selected device is valid or autoselect
|
|
||||||
* the default one when required */
|
|
||||||
if (osxsink->device_id == kAudioDeviceUnknown) {
|
|
||||||
if (default_device_id != kAudioDeviceUnknown) {
|
|
||||||
osxsink->device_id = default_device_id;
|
|
||||||
res = TRUE;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
for (i = 0; i < ndevices; i++) {
|
|
||||||
if (osxsink->device_id == devices[i]) {
|
|
||||||
res = TRUE;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (res && !_audio_device_is_alive (osxsink->device_id)) {
|
|
||||||
GST_ERROR_OBJECT (osxsink, "Requested device not usable");
|
|
||||||
res = FALSE;
|
|
||||||
goto done;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
res = gst_osx_audio_sink_allowed_caps (osxsink);
|
res = gst_osx_audio_sink_allowed_caps (osxsink);
|
||||||
|
|
||||||
done:
|
|
||||||
g_free (devices);
|
|
||||||
|
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
|
@ -60,8 +60,6 @@
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include <gst/gst.h>
|
#include <gst/gst.h>
|
||||||
#include <CoreAudio/CoreAudio.h>
|
|
||||||
#include <CoreAudio/AudioHardware.h>
|
|
||||||
#include "gstosxaudiosrc.h"
|
#include "gstosxaudiosrc.h"
|
||||||
#include "gstosxaudioelement.h"
|
#include "gstosxaudioelement.h"
|
||||||
|
|
||||||
|
@ -266,9 +264,10 @@ gst_osx_audio_src_create_ringbuffer (GstBaseAudioSrc * src)
|
||||||
GST_OSX_AUDIO_ELEMENT_GET_INTERFACE (osxsrc),
|
GST_OSX_AUDIO_ELEMENT_GET_INTERFACE (osxsrc),
|
||||||
(void *) gst_osx_audio_src_io_proc);
|
(void *) gst_osx_audio_src_io_proc);
|
||||||
|
|
||||||
ringbuffer->element = GST_OSX_AUDIO_ELEMENT_GET_INTERFACE (osxsrc);
|
ringbuffer->core_audio->element =
|
||||||
ringbuffer->is_src = TRUE;
|
GST_OSX_AUDIO_ELEMENT_GET_INTERFACE (osxsrc);
|
||||||
ringbuffer->device_id = osxsrc->device_id;
|
ringbuffer->core_audio->is_src = TRUE;
|
||||||
|
ringbuffer->core_audio->device_id = osxsrc->device_id;
|
||||||
|
|
||||||
return GST_RING_BUFFER (ringbuffer);
|
return GST_RING_BUFFER (ringbuffer);
|
||||||
}
|
}
|
||||||
|
@ -286,15 +285,15 @@ gst_osx_audio_src_io_proc (GstOsxRingBuffer * buf,
|
||||||
gint remaining;
|
gint remaining;
|
||||||
gint offset = 0;
|
gint offset = 0;
|
||||||
|
|
||||||
status = AudioUnitRender (buf->audiounit, ioActionFlags, inTimeStamp,
|
status = AudioUnitRender (buf->core_audio->audiounit, ioActionFlags,
|
||||||
inBusNumber, inNumberFrames, buf->recBufferList);
|
inTimeStamp, inBusNumber, inNumberFrames, buf->core_audio->recBufferList);
|
||||||
|
|
||||||
if (status) {
|
if (status) {
|
||||||
GST_WARNING_OBJECT (buf, "AudioUnitRender returned %d", (int) status);
|
GST_WARNING_OBJECT (buf, "AudioUnitRender returned %d", (int) status);
|
||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
|
|
||||||
remaining = buf->recBufferList->mBuffers[0].mDataByteSize;
|
remaining = buf->core_audio->recBufferList->mBuffers[0].mDataByteSize;
|
||||||
|
|
||||||
while (remaining) {
|
while (remaining) {
|
||||||
if (!gst_ring_buffer_prepare_read (GST_RING_BUFFER (buf),
|
if (!gst_ring_buffer_prepare_read (GST_RING_BUFFER (buf),
|
||||||
|
@ -307,7 +306,8 @@ gst_osx_audio_src_io_proc (GstOsxRingBuffer * buf,
|
||||||
len = remaining;
|
len = remaining;
|
||||||
|
|
||||||
memcpy (writeptr + buf->segoffset,
|
memcpy (writeptr + buf->segoffset,
|
||||||
(char *) buf->recBufferList->mBuffers[0].mData + offset, len);
|
(char *) buf->core_audio->recBufferList->mBuffers[0].mData + offset,
|
||||||
|
len);
|
||||||
|
|
||||||
buf->segoffset += len;
|
buf->segoffset += len;
|
||||||
offset += len;
|
offset += len;
|
||||||
|
@ -334,30 +334,5 @@ gst_osx_audio_src_osxelement_init (gpointer g_iface, gpointer iface_data)
|
||||||
static void
|
static void
|
||||||
gst_osx_audio_src_select_device (GstOsxAudioSrc * osxsrc)
|
gst_osx_audio_src_select_device (GstOsxAudioSrc * osxsrc)
|
||||||
{
|
{
|
||||||
OSStatus status;
|
gst_core_audio_select_source_device (&osxsrc->device_id);
|
||||||
UInt32 propertySize;
|
|
||||||
|
|
||||||
if (osxsrc->device_id == kAudioDeviceUnknown) {
|
|
||||||
/* If no specific device has been selected by the user, then pick the
|
|
||||||
* default device */
|
|
||||||
GST_DEBUG_OBJECT (osxsrc, "Selecting device for OSXAudioSrc");
|
|
||||||
propertySize = sizeof (osxsrc->device_id);
|
|
||||||
status = AudioHardwareGetProperty (kAudioHardwarePropertyDefaultInputDevice,
|
|
||||||
&propertySize, &osxsrc->device_id);
|
|
||||||
|
|
||||||
if (status) {
|
|
||||||
GST_WARNING_OBJECT (osxsrc,
|
|
||||||
"AudioHardwareGetProperty returned %d", (int) status);
|
|
||||||
} else {
|
|
||||||
GST_DEBUG_OBJECT (osxsrc, "AudioHardwareGetProperty returned 0");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (osxsrc->device_id == kAudioDeviceUnknown) {
|
|
||||||
GST_WARNING_OBJECT (osxsrc,
|
|
||||||
"AudioHardwareGetProperty: device_id is kAudioDeviceUnknown");
|
|
||||||
}
|
|
||||||
|
|
||||||
GST_DEBUG_OBJECT (osxsrc, "AudioHardwareGetProperty: device_id is %lu",
|
|
||||||
(long) osxsrc->device_id);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
218
sys/osxaudio/gstosxcoreaudio.c
Normal file
218
sys/osxaudio/gstosxcoreaudio.c
Normal file
|
@ -0,0 +1,218 @@
|
||||||
|
/*
|
||||||
|
* GStreamer
|
||||||
|
* Copyright (C) 2012-2013 Fluendo S.A. <support@fluendo.com>
|
||||||
|
* Authors: Josep Torra Vallès <josep@fluendo.com>
|
||||||
|
* Andoni Morales Alastruey <amorales@fluendo.com>
|
||||||
|
*
|
||||||
|
* This library is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU Library General Public
|
||||||
|
* License as published by the Free Software Foundation; either
|
||||||
|
* version 2 of the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This library is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* Library General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Library General Public
|
||||||
|
* License along with this library; if not, write to the
|
||||||
|
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||||||
|
* Boston, MA 02111-1307, USA.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "gstosxcoreaudio.h"
|
||||||
|
#include "gstosxcoreaudiocommon.h"
|
||||||
|
#include "gstosxaudiosrc.h"
|
||||||
|
|
||||||
|
GST_DEBUG_CATEGORY_STATIC (osx_audio_debug);
|
||||||
|
#define GST_CAT_DEFAULT osx_audio_debug
|
||||||
|
|
||||||
|
G_DEFINE_TYPE (GstCoreAudio, gst_core_audio, G_TYPE_OBJECT);
|
||||||
|
|
||||||
|
#include "gstosxcoreaudiohal.c"
|
||||||
|
|
||||||
|
|
||||||
|
static void
|
||||||
|
gst_core_audio_class_init (GstCoreAudioClass * klass)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
gst_core_audio_init (GstCoreAudio * core_audio)
|
||||||
|
{
|
||||||
|
core_audio->is_passthrough = FALSE;
|
||||||
|
core_audio->device_id = kAudioDeviceUnknown;
|
||||||
|
core_audio->is_src = FALSE;
|
||||||
|
core_audio->audiounit = NULL;
|
||||||
|
#ifndef HAVE_IOS
|
||||||
|
core_audio->hog_pid = -1;
|
||||||
|
core_audio->disabled_mixing = FALSE;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
/**************************
|
||||||
|
* Public API *
|
||||||
|
*************************/
|
||||||
|
|
||||||
|
GstCoreAudio *
|
||||||
|
gst_core_audio_new (GstObject * osxbuf)
|
||||||
|
{
|
||||||
|
GstCoreAudio *core_audio;
|
||||||
|
|
||||||
|
core_audio = g_object_new (GST_TYPE_CORE_AUDIO, NULL);
|
||||||
|
core_audio->osxbuf = osxbuf;
|
||||||
|
return core_audio;
|
||||||
|
}
|
||||||
|
|
||||||
|
gboolean
|
||||||
|
gst_core_audio_close (GstCoreAudio * core_audio)
|
||||||
|
{
|
||||||
|
AudioComponentInstanceDispose (core_audio->audiounit);
|
||||||
|
core_audio->audiounit = NULL;
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
gboolean
|
||||||
|
gst_core_audio_open (GstCoreAudio * core_audio)
|
||||||
|
{
|
||||||
|
|
||||||
|
if (!gst_core_audio_open_impl (core_audio))
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
if (core_audio->is_src) {
|
||||||
|
AudioStreamBasicDescription asbd_in;
|
||||||
|
UInt32 propertySize;
|
||||||
|
OSStatus status;
|
||||||
|
|
||||||
|
GstOsxAudioSrc *src =
|
||||||
|
GST_OSX_AUDIO_SRC (GST_OBJECT_PARENT (core_audio->osxbuf));
|
||||||
|
|
||||||
|
propertySize = sizeof (asbd_in);
|
||||||
|
status = AudioUnitGetProperty (core_audio->audiounit,
|
||||||
|
kAudioUnitProperty_StreamFormat,
|
||||||
|
kAudioUnitScope_Input, 1, &asbd_in, &propertySize);
|
||||||
|
|
||||||
|
if (status) {
|
||||||
|
AudioComponentInstanceDispose (core_audio->audiounit);
|
||||||
|
core_audio->audiounit = NULL;
|
||||||
|
GST_WARNING_OBJECT (core_audio,
|
||||||
|
"Unable to obtain device properties: %" GST_FOURCC_FORMAT,
|
||||||
|
GST_FOURCC_ARGS (status));
|
||||||
|
return FALSE;
|
||||||
|
} else {
|
||||||
|
src->deviceChannels = asbd_in.mChannelsPerFrame;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
gboolean
|
||||||
|
gst_core_audio_start_processing (GstCoreAudio * core_audio)
|
||||||
|
{
|
||||||
|
return gst_core_audio_start_processing_impl (core_audio);
|
||||||
|
}
|
||||||
|
|
||||||
|
gboolean
|
||||||
|
gst_core_audio_pause_processing (GstCoreAudio * core_audio)
|
||||||
|
{
|
||||||
|
return gst_core_audio_pause_processing_impl (core_audio);
|
||||||
|
}
|
||||||
|
|
||||||
|
gboolean
|
||||||
|
gst_core_audio_stop_processing (GstCoreAudio * core_audio)
|
||||||
|
{
|
||||||
|
return gst_core_audio_stop_processing_impl (core_audio);
|
||||||
|
}
|
||||||
|
|
||||||
|
gboolean
|
||||||
|
gst_core_audio_get_samples_and_latency (GstCoreAudio * core_audio,
|
||||||
|
gdouble rate, guint * samples, gdouble * latency)
|
||||||
|
{
|
||||||
|
return gst_core_audio_get_samples_and_latency_impl (core_audio, rate,
|
||||||
|
samples, latency);
|
||||||
|
}
|
||||||
|
|
||||||
|
gboolean
|
||||||
|
gst_core_audio_initialize (GstCoreAudio * core_audio,
|
||||||
|
AudioStreamBasicDescription format, GstCaps * caps, gboolean is_passthrough)
|
||||||
|
{
|
||||||
|
guint32 frame_size;
|
||||||
|
OSStatus status;
|
||||||
|
|
||||||
|
GST_DEBUG_OBJECT (core_audio,
|
||||||
|
"Initializing: passthrough:%d caps:%" GST_PTR_FORMAT, is_passthrough,
|
||||||
|
caps);
|
||||||
|
|
||||||
|
if (!gst_core_audio_initialize_impl (core_audio, format, caps,
|
||||||
|
is_passthrough, &frame_size)) {
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (core_audio->is_src) {
|
||||||
|
/* create AudioBufferList needed for recording */
|
||||||
|
core_audio->recBufferList =
|
||||||
|
buffer_list_alloc (format.mChannelsPerFrame,
|
||||||
|
frame_size * format.mBytesPerFrame);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Initialize the AudioUnit */
|
||||||
|
status = AudioUnitInitialize (core_audio->audiounit);
|
||||||
|
if (status) {
|
||||||
|
GST_ERROR_OBJECT (core_audio, "Failed to initialise AudioUnit: %"
|
||||||
|
GST_FOURCC_FORMAT, GST_FOURCC_ARGS (status));
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
return TRUE;
|
||||||
|
|
||||||
|
error:
|
||||||
|
if (core_audio->is_src && core_audio->recBufferList) {
|
||||||
|
buffer_list_free (core_audio->recBufferList);
|
||||||
|
core_audio->recBufferList = NULL;
|
||||||
|
}
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
gst_core_audio_unitialize (GstCoreAudio * core_audio)
|
||||||
|
{
|
||||||
|
AudioUnitUninitialize (core_audio->audiounit);
|
||||||
|
|
||||||
|
if (core_audio->recBufferList) {
|
||||||
|
buffer_list_free (core_audio->recBufferList);
|
||||||
|
core_audio->recBufferList = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
gst_core_audio_set_volume (GstCoreAudio * core_audio, gfloat volume)
|
||||||
|
{
|
||||||
|
AudioUnitSetParameter (core_audio->audiounit, kHALOutputParam_Volume,
|
||||||
|
kAudioUnitScope_Global, 0, (float) volume, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
gboolean
|
||||||
|
gst_core_audio_select_device (AudioDeviceID * device_id)
|
||||||
|
{
|
||||||
|
return gst_core_audio_select_device_impl (device_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
gboolean
|
||||||
|
gst_core_audio_select_source_device (AudioDeviceID * device_id)
|
||||||
|
{
|
||||||
|
return gst_core_audio_select_source_device_impl (device_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
gst_core_audio_init_debug (void)
|
||||||
|
{
|
||||||
|
GST_DEBUG_CATEGORY_INIT (osx_audio_debug, "osxaudio", 0,
|
||||||
|
"OSX Audio Elements");
|
||||||
|
}
|
||||||
|
|
||||||
|
gboolean
|
||||||
|
gst_core_audio_audio_device_is_spdif_avail (AudioDeviceID device_id)
|
||||||
|
{
|
||||||
|
return gst_core_audio_audio_device_is_spdif_avail_impl (device_id);
|
||||||
|
}
|
|
@ -2,29 +2,6 @@
|
||||||
* GStreamer
|
* GStreamer
|
||||||
* Copyright (C) 2012 Fluendo S.A. <support@fluendo.com>
|
* Copyright (C) 2012 Fluendo S.A. <support@fluendo.com>
|
||||||
*
|
*
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
|
||||||
* copy of this software and associated documentation files (the "Software"),
|
|
||||||
* to deal in the Software without restriction, including without limitation
|
|
||||||
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
|
||||||
* and/or sell copies of the Software, and to permit persons to whom the
|
|
||||||
* Software is furnished to do so, subject to the following conditions:
|
|
||||||
*
|
|
||||||
* The above copyright notice and this permission notice shall be included in
|
|
||||||
* all copies or substantial portions of the Software.
|
|
||||||
*
|
|
||||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|
||||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
|
||||||
* DEALINGS IN THE SOFTWARE.
|
|
||||||
*
|
|
||||||
* Alternatively, the contents of this file may be used under the
|
|
||||||
* GNU Lesser General Public License Version 2.1 (the "LGPL"), in
|
|
||||||
* which case the following provisions apply instead of the ones
|
|
||||||
* mentioned above:
|
|
||||||
*
|
|
||||||
* This library is free software; you can redistribute it and/or
|
* This library is free software; you can redistribute it and/or
|
||||||
* modify it under the terms of the GNU Library General Public
|
* modify it under the terms of the GNU Library General Public
|
||||||
* License as published by the Free Software Foundation; either
|
* License as published by the Free Software Foundation; either
|
||||||
|
@ -40,567 +17,135 @@
|
||||||
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
|
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
|
||||||
* Boston, MA 02110-1301, USA.
|
* Boston, MA 02110-1301, USA.
|
||||||
*
|
*
|
||||||
* The development of this code was made possible due to the involvement of
|
|
||||||
* Pioneers of the Inevitable, the creators of the Songbird Music player
|
|
||||||
*
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#ifndef __GST_CORE_AUDIO_H__
|
||||||
|
#define __GST_CORE_AUDIO_H__
|
||||||
|
|
||||||
|
#ifdef HAVE_CONFIG_H
|
||||||
|
# include <config.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <gst/gst.h>
|
||||||
|
#ifdef HAVE_IOS
|
||||||
|
#include <CoreAudio/CoreAudioTypes.h>
|
||||||
|
#define AudioDeviceID gint
|
||||||
|
#define kAudioDeviceUnknown 0
|
||||||
|
#else
|
||||||
|
#include <CoreAudio/CoreAudio.h>
|
||||||
|
#include <AudioToolbox/AudioToolbox.h>
|
||||||
|
#if MAC_OS_X_VERSION_MIN_REQUIRED <= MAC_OS_X_VERSION_10_5
|
||||||
|
#include <CoreServices/CoreServices.h>
|
||||||
|
#define AudioComponentFindNext FindNextComponent
|
||||||
|
#define AudioComponentInstanceNew OpenAComponent
|
||||||
|
#define AudioComponentInstanceDispose CloseComponent
|
||||||
|
#define AudioComponent Component
|
||||||
|
#define AudioComponentDescription ComponentDescription
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
#include <AudioUnit/AudioUnit.h>
|
||||||
|
#include "gstosxaudioelement.h"
|
||||||
|
|
||||||
|
|
||||||
|
G_BEGIN_DECLS
|
||||||
|
|
||||||
|
#define GST_TYPE_CORE_AUDIO \
|
||||||
|
(gst_core_audio_get_type())
|
||||||
|
#define GST_CORE_AUDIO(obj) \
|
||||||
|
(G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_CORE_AUDIO,GstCoreAudio))
|
||||||
|
#define GST_CORE_AUDIO_CLASS(klass) \
|
||||||
|
(G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_CORE_AUDIO,GstCoreAudioClass))
|
||||||
|
#define GST_CORE_AUDIO_GET_CLASS(obj) \
|
||||||
|
(G_TYPE_INSTANCE_GET_CLASS((obj),GST_TYPE_CORE_AUDIO,GstCoreAudioClass))
|
||||||
|
#define GST_IS_CORE_AUDIO(obj) \
|
||||||
|
(G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_CORE_AUDIO))
|
||||||
|
#define GST_IS_CORE_AUDIO_CLASS(klass) \
|
||||||
|
(G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_CORE_AUDIO))
|
||||||
|
|
||||||
|
#define CORE_AUDIO_FORMAT_IS_SPDIF(f) ((f).mFormat.mFormatID == 'IAC3' || (f).mFormat.mFormatID == 'iac3' || (f).mFormat.mFormatID == kAudioFormat60958AC3 || (f).mFormat.mFormatID == kAudioFormatAC3)
|
||||||
|
|
||||||
#define CORE_AUDIO_FORMAT "FormatID: %" GST_FOURCC_FORMAT " rate: %f flags: 0x%x BytesPerPacket: %u FramesPerPacket: %u BytesPerFrame: %u ChannelsPerFrame: %u BitsPerChannel: %u"
|
#define CORE_AUDIO_FORMAT "FormatID: %" GST_FOURCC_FORMAT " rate: %f flags: 0x%x BytesPerPacket: %u FramesPerPacket: %u BytesPerFrame: %u ChannelsPerFrame: %u BitsPerChannel: %u"
|
||||||
#define CORE_AUDIO_FORMAT_ARGS(f) GST_FOURCC_ARGS((f).mFormatID),(f).mSampleRate,(unsigned)(f).mFormatFlags,(unsigned)(f).mBytesPerPacket,(unsigned)(f).mFramesPerPacket,(unsigned)(f).mBytesPerFrame,(unsigned)(f).mChannelsPerFrame,(unsigned)(f).mBitsPerChannel
|
#define CORE_AUDIO_FORMAT_ARGS(f) GST_FOURCC_ARGS((f).mFormatID),(f).mSampleRate,(unsigned)(f).mFormatFlags,(unsigned)(f).mBytesPerPacket,(unsigned)(f).mFramesPerPacket,(unsigned)(f).mBytesPerFrame,(unsigned)(f).mChannelsPerFrame,(unsigned)(f).mBitsPerChannel
|
||||||
|
|
||||||
#define CORE_AUDIO_FORMAT_IS_SPDIF(f) ((f).mFormat.mFormatID == 'IAC3' || (f).mFormat.mFormatID == 'iac3' || (f).mFormat.mFormatID == kAudioFormat60958AC3 || (f).mFormat.mFormatID == kAudioFormatAC3)
|
typedef struct _GstCoreAudio GstCoreAudio;
|
||||||
|
typedef struct _GstCoreAudioClass GstCoreAudioClass;
|
||||||
|
|
||||||
static inline gboolean
|
struct _GstCoreAudio
|
||||||
_audio_system_set_runloop (CFRunLoopRef runLoop)
|
|
||||||
{
|
{
|
||||||
OSStatus status = noErr;
|
GObject object;
|
||||||
|
|
||||||
gboolean res = FALSE;
|
GstObject *osxbuf;
|
||||||
|
GstOsxAudioElementInterface *element;
|
||||||
|
|
||||||
AudioObjectPropertyAddress runloopAddress = {
|
gboolean is_src;
|
||||||
kAudioHardwarePropertyRunLoop,
|
gboolean is_passthrough;
|
||||||
kAudioObjectPropertyScopeGlobal,
|
AudioDeviceID device_id;
|
||||||
kAudioObjectPropertyElementMaster
|
AudioStreamBasicDescription stream_format;
|
||||||
};
|
gint stream_idx;
|
||||||
|
gboolean io_proc_active;
|
||||||
|
gboolean io_proc_needs_deactivation;
|
||||||
|
|
||||||
status = AudioObjectSetPropertyData (kAudioObjectSystemObject,
|
/* For LPCM in/out */
|
||||||
&runloopAddress, 0, NULL, sizeof (CFRunLoopRef), &runLoop);
|
AudioUnit audiounit;
|
||||||
if (status == noErr) {
|
AudioBufferList *recBufferList;
|
||||||
res = TRUE;
|
|
||||||
} else {
|
|
||||||
GST_ERROR ("failed to set runloop to %p: %" GST_FOURCC_FORMAT,
|
|
||||||
runLoop, GST_FOURCC_ARGS (status));
|
|
||||||
}
|
|
||||||
|
|
||||||
return res;
|
#ifndef HAVE_IOS
|
||||||
}
|
/* For SPDIF out */
|
||||||
|
|
||||||
static inline AudioDeviceID
|
|
||||||
_audio_system_get_default_output (void)
|
|
||||||
{
|
|
||||||
OSStatus status = noErr;
|
|
||||||
UInt32 propertySize = sizeof (AudioDeviceID);
|
|
||||||
AudioDeviceID device_id = kAudioDeviceUnknown;
|
|
||||||
|
|
||||||
AudioObjectPropertyAddress defaultDeviceAddress = {
|
|
||||||
kAudioHardwarePropertyDefaultOutputDevice,
|
|
||||||
kAudioDevicePropertyScopeOutput,
|
|
||||||
kAudioObjectPropertyElementMaster
|
|
||||||
};
|
|
||||||
|
|
||||||
status = AudioObjectGetPropertyData (kAudioObjectSystemObject,
|
|
||||||
&defaultDeviceAddress, 0, NULL, &propertySize, &device_id);
|
|
||||||
if (status != noErr) {
|
|
||||||
GST_ERROR ("failed getting default output device: %"
|
|
||||||
GST_FOURCC_FORMAT, GST_FOURCC_ARGS (status));
|
|
||||||
}
|
|
||||||
|
|
||||||
return device_id;
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline AudioDeviceID *
|
|
||||||
_audio_system_get_devices (gint * ndevices)
|
|
||||||
{
|
|
||||||
OSStatus status = noErr;
|
|
||||||
UInt32 propertySize = 0;
|
|
||||||
AudioDeviceID *devices = NULL;
|
|
||||||
|
|
||||||
AudioObjectPropertyAddress audioDevicesAddress = {
|
|
||||||
kAudioHardwarePropertyDevices,
|
|
||||||
kAudioDevicePropertyScopeOutput,
|
|
||||||
kAudioObjectPropertyElementMaster
|
|
||||||
};
|
|
||||||
|
|
||||||
status = AudioObjectGetPropertyDataSize (kAudioObjectSystemObject,
|
|
||||||
&audioDevicesAddress, 0, NULL, &propertySize);
|
|
||||||
if (status != noErr) {
|
|
||||||
GST_WARNING ("failed getting number of devices: %"
|
|
||||||
GST_FOURCC_FORMAT, GST_FOURCC_ARGS (status));
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
*ndevices = propertySize / sizeof (AudioDeviceID);
|
|
||||||
|
|
||||||
devices = (AudioDeviceID *) g_malloc (propertySize);
|
|
||||||
if (devices) {
|
|
||||||
status = AudioObjectGetPropertyData (kAudioObjectSystemObject,
|
|
||||||
&audioDevicesAddress, 0, NULL, &propertySize, devices);
|
|
||||||
if (status != noErr) {
|
|
||||||
GST_WARNING ("failed getting the list of devices: %"
|
|
||||||
GST_FOURCC_FORMAT, GST_FOURCC_ARGS (status));
|
|
||||||
g_free (devices);
|
|
||||||
*ndevices = 0;
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return devices;
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline gboolean
|
|
||||||
_audio_device_is_alive (AudioDeviceID device_id)
|
|
||||||
{
|
|
||||||
OSStatus status = noErr;
|
|
||||||
int alive = FALSE;
|
|
||||||
UInt32 propertySize = sizeof (alive);
|
|
||||||
|
|
||||||
AudioObjectPropertyAddress audioDeviceAliveAddress = {
|
|
||||||
kAudioDevicePropertyDeviceIsAlive,
|
|
||||||
kAudioDevicePropertyScopeOutput,
|
|
||||||
kAudioObjectPropertyElementMaster
|
|
||||||
};
|
|
||||||
|
|
||||||
status = AudioObjectGetPropertyData (device_id,
|
|
||||||
&audioDeviceAliveAddress, 0, NULL, &propertySize, &alive);
|
|
||||||
if (status != noErr) {
|
|
||||||
alive = FALSE;
|
|
||||||
}
|
|
||||||
|
|
||||||
return alive;
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline guint
|
|
||||||
_audio_device_get_latency (AudioDeviceID device_id)
|
|
||||||
{
|
|
||||||
OSStatus status = noErr;
|
|
||||||
UInt32 latency = 0;
|
|
||||||
UInt32 propertySize = sizeof (latency);
|
|
||||||
|
|
||||||
AudioObjectPropertyAddress audioDeviceLatencyAddress = {
|
|
||||||
kAudioDevicePropertyLatency,
|
|
||||||
kAudioDevicePropertyScopeOutput,
|
|
||||||
kAudioObjectPropertyElementMaster
|
|
||||||
};
|
|
||||||
|
|
||||||
status = AudioObjectGetPropertyData (device_id,
|
|
||||||
&audioDeviceLatencyAddress, 0, NULL, &propertySize, &latency);
|
|
||||||
if (status != noErr) {
|
|
||||||
GST_ERROR ("failed to get latency: %" GST_FOURCC_FORMAT,
|
|
||||||
GST_FOURCC_ARGS (status));
|
|
||||||
latency = -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
return latency;
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline pid_t
|
|
||||||
_audio_device_get_hog (AudioDeviceID device_id)
|
|
||||||
{
|
|
||||||
OSStatus status = noErr;
|
|
||||||
pid_t hog_pid;
|
pid_t hog_pid;
|
||||||
UInt32 propertySize = sizeof (hog_pid);
|
gboolean disabled_mixing;
|
||||||
|
AudioStreamID stream_id;
|
||||||
AudioObjectPropertyAddress audioDeviceHogModeAddress = {
|
gboolean revert_format;
|
||||||
kAudioDevicePropertyHogMode,
|
AudioStreamBasicDescription original_format;
|
||||||
kAudioDevicePropertyScopeOutput,
|
AudioDeviceIOProcID procID;
|
||||||
kAudioObjectPropertyElementMaster
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
status = AudioObjectGetPropertyData (device_id,
|
struct _GstCoreAudioClass
|
||||||
&audioDeviceHogModeAddress, 0, NULL, &propertySize, &hog_pid);
|
|
||||||
if (status != noErr) {
|
|
||||||
GST_ERROR ("failed to get hog: %" GST_FOURCC_FORMAT,
|
|
||||||
GST_FOURCC_ARGS (status));
|
|
||||||
hog_pid = -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
return hog_pid;
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline gboolean
|
|
||||||
_audio_device_set_hog (AudioDeviceID device_id, pid_t hog_pid)
|
|
||||||
{
|
{
|
||||||
OSStatus status = noErr;
|
GObjectClass parent_class;
|
||||||
UInt32 propertySize = sizeof (hog_pid);
|
|
||||||
gboolean res = FALSE;
|
|
||||||
|
|
||||||
AudioObjectPropertyAddress audioDeviceHogModeAddress = {
|
|
||||||
kAudioDevicePropertyHogMode,
|
|
||||||
kAudioDevicePropertyScopeOutput,
|
|
||||||
kAudioObjectPropertyElementMaster
|
|
||||||
};
|
};
|
||||||
|
|
||||||
status = AudioObjectSetPropertyData (device_id,
|
GType gst_core_audio_get_type (void);
|
||||||
&audioDeviceHogModeAddress, 0, NULL, propertySize, &hog_pid);
|
|
||||||
|
|
||||||
if (status == noErr) {
|
void gst_core_audio_init_debug (void);
|
||||||
res = TRUE;
|
|
||||||
} else {
|
|
||||||
GST_ERROR ("failed to set hog: %" GST_FOURCC_FORMAT,
|
|
||||||
GST_FOURCC_ARGS (status));
|
|
||||||
}
|
|
||||||
|
|
||||||
return res;
|
GstCoreAudio * gst_core_audio_new (GstObject *osxbuf);
|
||||||
}
|
|
||||||
|
|
||||||
static inline gboolean
|
gboolean gst_core_audio_open (GstCoreAudio *core_audio);
|
||||||
_audio_device_set_mixing (AudioDeviceID device_id, gboolean enable_mix)
|
|
||||||
{
|
|
||||||
OSStatus status = noErr;
|
|
||||||
UInt32 propertySize = 0, can_mix = enable_mix;
|
|
||||||
Boolean writable = FALSE;
|
|
||||||
gboolean res = FALSE;
|
|
||||||
|
|
||||||
AudioObjectPropertyAddress audioDeviceSupportsMixingAddress = {
|
gboolean gst_core_audio_close (GstCoreAudio *core_audio);
|
||||||
kAudioDevicePropertySupportsMixing,
|
|
||||||
kAudioObjectPropertyScopeGlobal,
|
|
||||||
kAudioObjectPropertyElementMaster
|
|
||||||
};
|
|
||||||
|
|
||||||
if (AudioObjectHasProperty (device_id, &audioDeviceSupportsMixingAddress)) {
|
gboolean gst_core_audio_initialize (GstCoreAudio *core_audio,
|
||||||
/* Set mixable to false if we are allowed to */
|
AudioStreamBasicDescription format,
|
||||||
status = AudioObjectIsPropertySettable (device_id,
|
GstCaps *caps,
|
||||||
&audioDeviceSupportsMixingAddress, &writable);
|
gboolean is_passthrough);
|
||||||
if (status) {
|
|
||||||
GST_DEBUG ("AudioObjectIsPropertySettable: %" GST_FOURCC_FORMAT,
|
|
||||||
GST_FOURCC_ARGS (status));
|
|
||||||
}
|
|
||||||
status = AudioObjectGetPropertyDataSize (device_id,
|
|
||||||
&audioDeviceSupportsMixingAddress, 0, NULL, &propertySize);
|
|
||||||
if (status) {
|
|
||||||
GST_DEBUG ("AudioObjectGetPropertyDataSize: %" GST_FOURCC_FORMAT,
|
|
||||||
GST_FOURCC_ARGS (status));
|
|
||||||
}
|
|
||||||
status = AudioObjectGetPropertyData (device_id,
|
|
||||||
&audioDeviceSupportsMixingAddress, 0, NULL, &propertySize, &can_mix);
|
|
||||||
if (status) {
|
|
||||||
GST_DEBUG ("AudioObjectGetPropertyData: %" GST_FOURCC_FORMAT,
|
|
||||||
GST_FOURCC_ARGS (status));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (status == noErr && writable) {
|
void gst_core_audio_unitialize (GstCoreAudio *core_audio);
|
||||||
can_mix = enable_mix;
|
|
||||||
status = AudioObjectSetPropertyData (device_id,
|
|
||||||
&audioDeviceSupportsMixingAddress, 0, NULL, propertySize, &can_mix);
|
|
||||||
res = TRUE;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (status != noErr) {
|
gboolean gst_core_audio_start_processing (GstCoreAudio *core_audio);
|
||||||
GST_ERROR ("failed to set mixmode: %" GST_FOURCC_FORMAT,
|
|
||||||
GST_FOURCC_ARGS (status));
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
GST_DEBUG ("property not found, mixing coudln't be changed");
|
|
||||||
}
|
|
||||||
|
|
||||||
return res;
|
gboolean gst_core_audio_pause_processing (GstCoreAudio *core_audio);
|
||||||
}
|
|
||||||
|
|
||||||
static inline gchar *
|
gboolean gst_core_audio_stop_processing (GstCoreAudio *core_audio);
|
||||||
_audio_device_get_name (AudioDeviceID device_id)
|
|
||||||
{
|
|
||||||
OSStatus status = noErr;
|
|
||||||
UInt32 propertySize = 0;
|
|
||||||
gchar *device_name = NULL;
|
|
||||||
|
|
||||||
AudioObjectPropertyAddress deviceNameAddress = {
|
gboolean gst_core_audio_get_samples_and_latency (GstCoreAudio * core_audio,
|
||||||
kAudioDevicePropertyDeviceName,
|
gdouble rate,
|
||||||
kAudioDevicePropertyScopeOutput,
|
guint *samples,
|
||||||
kAudioObjectPropertyElementMaster
|
gdouble *latency);
|
||||||
};
|
|
||||||
|
|
||||||
/* Get the length of the device name */
|
void gst_core_audio_set_volume (GstCoreAudio *core_audio,
|
||||||
status = AudioObjectGetPropertyDataSize (device_id,
|
gfloat volume);
|
||||||
&deviceNameAddress, 0, NULL, &propertySize);
|
|
||||||
if (status != noErr) {
|
|
||||||
goto beach;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Get the name of the device */
|
gboolean gst_core_audio_audio_device_is_spdif_avail (AudioDeviceID device_id);
|
||||||
device_name = (gchar *) g_malloc (propertySize);
|
|
||||||
status = AudioObjectGetPropertyData (device_id,
|
|
||||||
&deviceNameAddress, 0, NULL, &propertySize, device_name);
|
|
||||||
if (status != noErr) {
|
|
||||||
g_free (device_name);
|
|
||||||
device_name = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
beach:
|
|
||||||
return device_name;
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline gboolean
|
gboolean gst_core_audio_select_device (AudioDeviceID *device_id);
|
||||||
_audio_device_has_output (AudioDeviceID device_id)
|
|
||||||
{
|
|
||||||
OSStatus status = noErr;
|
|
||||||
UInt32 propertySize;
|
|
||||||
|
|
||||||
AudioObjectPropertyAddress streamsAddress = {
|
gboolean gst_core_audio_select_source_device (AudioDeviceID *device_id);
|
||||||
kAudioDevicePropertyStreams,
|
|
||||||
kAudioDevicePropertyScopeOutput,
|
|
||||||
kAudioObjectPropertyElementMaster
|
|
||||||
};
|
|
||||||
|
|
||||||
status = AudioObjectGetPropertyDataSize (device_id,
|
AudioChannelLayout * gst_core_audio_audio_device_get_channel_layout (AudioDeviceID device_id);
|
||||||
&streamsAddress, 0, NULL, &propertySize);
|
|
||||||
if (status != noErr) {
|
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
if (propertySize == 0) {
|
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
|
|
||||||
return TRUE;
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline AudioChannelLayout *
|
G_END_DECLS
|
||||||
_audio_device_get_channel_layout (AudioDeviceID device_id)
|
|
||||||
{
|
|
||||||
OSStatus status = noErr;
|
|
||||||
UInt32 propertySize = 0;
|
|
||||||
AudioChannelLayout *layout = NULL;
|
|
||||||
|
|
||||||
AudioObjectPropertyAddress channelLayoutAddress = {
|
|
||||||
kAudioDevicePropertyPreferredChannelLayout,
|
|
||||||
kAudioDevicePropertyScopeOutput,
|
|
||||||
kAudioObjectPropertyElementMaster
|
|
||||||
};
|
|
||||||
|
|
||||||
/* Get the length of the default channel layout structure */
|
|
||||||
status = AudioObjectGetPropertyDataSize (device_id,
|
|
||||||
&channelLayoutAddress, 0, NULL, &propertySize);
|
|
||||||
if (status != noErr) {
|
|
||||||
GST_ERROR ("failed to get prefered layout: %" GST_FOURCC_FORMAT,
|
|
||||||
GST_FOURCC_ARGS (status));
|
|
||||||
goto beach;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Get the default channel layout of the device */
|
|
||||||
layout = (AudioChannelLayout *) g_malloc (propertySize);
|
|
||||||
status = AudioObjectGetPropertyData (device_id,
|
|
||||||
&channelLayoutAddress, 0, NULL, &propertySize, layout);
|
|
||||||
if (status != noErr) {
|
|
||||||
GST_ERROR ("failed to get prefered layout: %" GST_FOURCC_FORMAT,
|
|
||||||
GST_FOURCC_ARGS (status));
|
|
||||||
goto failed;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (layout->mChannelLayoutTag == kAudioChannelLayoutTag_UseChannelBitmap) {
|
|
||||||
/* bitmap defined channellayout */
|
|
||||||
status =
|
|
||||||
AudioFormatGetProperty (kAudioFormatProperty_ChannelLayoutForBitmap,
|
|
||||||
sizeof (UInt32), &layout->mChannelBitmap, &propertySize, layout);
|
|
||||||
if (status != noErr) {
|
|
||||||
GST_ERROR ("failed to get layout for bitmap: %" GST_FOURCC_FORMAT,
|
|
||||||
GST_FOURCC_ARGS (status));
|
|
||||||
goto failed;
|
|
||||||
}
|
|
||||||
} else if (layout->mChannelLayoutTag !=
|
|
||||||
kAudioChannelLayoutTag_UseChannelDescriptions) {
|
|
||||||
/* layouttags defined channellayout */
|
|
||||||
status = AudioFormatGetProperty (kAudioFormatProperty_ChannelLayoutForTag,
|
|
||||||
sizeof(AudioChannelLayoutTag), &layout->mChannelLayoutTag,
|
|
||||||
&propertySize, layout);
|
|
||||||
if (status != noErr) {
|
|
||||||
GST_ERROR ("failed to get layout for tag: %" GST_FOURCC_FORMAT,
|
|
||||||
GST_FOURCC_ARGS (status));
|
|
||||||
goto failed;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
beach:
|
|
||||||
return layout;
|
|
||||||
|
|
||||||
failed:
|
|
||||||
g_free (layout);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline AudioStreamID *
|
|
||||||
_audio_device_get_streams (AudioDeviceID device_id, gint * nstreams)
|
|
||||||
{
|
|
||||||
OSStatus status = noErr;
|
|
||||||
UInt32 propertySize = 0;
|
|
||||||
AudioStreamID *streams = NULL;
|
|
||||||
|
|
||||||
AudioObjectPropertyAddress streamsAddress = {
|
|
||||||
kAudioDevicePropertyStreams,
|
|
||||||
kAudioDevicePropertyScopeOutput,
|
|
||||||
kAudioObjectPropertyElementMaster
|
|
||||||
};
|
|
||||||
|
|
||||||
status = AudioObjectGetPropertyDataSize (device_id,
|
|
||||||
&streamsAddress, 0, NULL, &propertySize);
|
|
||||||
if (status != noErr) {
|
|
||||||
GST_WARNING ("failed getting number of streams: %"
|
|
||||||
GST_FOURCC_FORMAT, GST_FOURCC_ARGS (status));
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
*nstreams = propertySize / sizeof (AudioStreamID);
|
|
||||||
streams = (AudioStreamID *) g_malloc (propertySize);
|
|
||||||
|
|
||||||
if (streams) {
|
|
||||||
status = AudioObjectGetPropertyData (device_id,
|
|
||||||
&streamsAddress, 0, NULL, &propertySize, streams);
|
|
||||||
if (status != noErr) {
|
|
||||||
GST_WARNING ("failed getting the list of streams: %"
|
|
||||||
GST_FOURCC_FORMAT, GST_FOURCC_ARGS (status));
|
|
||||||
g_free (streams);
|
|
||||||
*nstreams = 0;
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return streams;
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline guint
|
|
||||||
_audio_stream_get_latency (AudioStreamID stream_id)
|
|
||||||
{
|
|
||||||
OSStatus status = noErr;
|
|
||||||
UInt32 latency;
|
|
||||||
UInt32 propertySize = sizeof (latency);
|
|
||||||
|
|
||||||
AudioObjectPropertyAddress latencyAddress = {
|
|
||||||
kAudioStreamPropertyLatency,
|
|
||||||
kAudioObjectPropertyScopeGlobal,
|
|
||||||
kAudioObjectPropertyElementMaster
|
|
||||||
};
|
|
||||||
|
|
||||||
status = AudioObjectGetPropertyData (stream_id,
|
|
||||||
&latencyAddress, 0, NULL, &propertySize, &latency);
|
|
||||||
if (status != noErr) {
|
|
||||||
GST_ERROR ("failed to get latency: %" GST_FOURCC_FORMAT,
|
|
||||||
GST_FOURCC_ARGS (status));
|
|
||||||
latency = -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
return latency;
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline gboolean
|
|
||||||
_audio_stream_get_current_format (AudioStreamID stream_id,
|
|
||||||
AudioStreamBasicDescription * format)
|
|
||||||
{
|
|
||||||
OSStatus status = noErr;
|
|
||||||
UInt32 propertySize = sizeof (AudioStreamBasicDescription);
|
|
||||||
|
|
||||||
AudioObjectPropertyAddress formatAddress = {
|
|
||||||
kAudioStreamPropertyPhysicalFormat,
|
|
||||||
kAudioObjectPropertyScopeGlobal,
|
|
||||||
kAudioObjectPropertyElementMaster
|
|
||||||
};
|
|
||||||
|
|
||||||
status = AudioObjectGetPropertyData (stream_id,
|
|
||||||
&formatAddress, 0, NULL, &propertySize, format);
|
|
||||||
if (status != noErr) {
|
|
||||||
GST_ERROR ("failed to get current format: %" GST_FOURCC_FORMAT,
|
|
||||||
GST_FOURCC_ARGS (status));
|
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
|
|
||||||
return TRUE;
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline gboolean
|
|
||||||
_audio_stream_set_current_format (AudioStreamID stream_id,
|
|
||||||
AudioStreamBasicDescription format)
|
|
||||||
{
|
|
||||||
OSStatus status = noErr;
|
|
||||||
UInt32 propertySize = sizeof (AudioStreamBasicDescription);
|
|
||||||
|
|
||||||
AudioObjectPropertyAddress formatAddress = {
|
|
||||||
kAudioStreamPropertyPhysicalFormat,
|
|
||||||
kAudioObjectPropertyScopeGlobal,
|
|
||||||
kAudioObjectPropertyElementMaster
|
|
||||||
};
|
|
||||||
|
|
||||||
status = AudioObjectSetPropertyData (stream_id,
|
|
||||||
&formatAddress, 0, NULL, propertySize, &format);
|
|
||||||
if (status != noErr) {
|
|
||||||
GST_ERROR ("failed to set current format: %" GST_FOURCC_FORMAT,
|
|
||||||
GST_FOURCC_ARGS (status));
|
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
|
|
||||||
return TRUE;
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline AudioStreamRangedDescription *
|
|
||||||
_audio_stream_get_formats (AudioStreamID stream_id, gint * nformats)
|
|
||||||
{
|
|
||||||
OSStatus status = noErr;
|
|
||||||
UInt32 propertySize = 0;
|
|
||||||
AudioStreamRangedDescription *formats = NULL;
|
|
||||||
|
|
||||||
AudioObjectPropertyAddress formatsAddress = {
|
|
||||||
kAudioStreamPropertyAvailablePhysicalFormats,
|
|
||||||
kAudioObjectPropertyScopeGlobal,
|
|
||||||
kAudioObjectPropertyElementMaster
|
|
||||||
};
|
|
||||||
|
|
||||||
status = AudioObjectGetPropertyDataSize (stream_id,
|
|
||||||
&formatsAddress, 0, NULL, &propertySize);
|
|
||||||
if (status != noErr) {
|
|
||||||
GST_WARNING ("failed getting number of stream formats: %"
|
|
||||||
GST_FOURCC_FORMAT, GST_FOURCC_ARGS (status));
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
*nformats = propertySize / sizeof (AudioStreamRangedDescription);
|
|
||||||
|
|
||||||
formats = (AudioStreamRangedDescription *) g_malloc (propertySize);
|
|
||||||
if (formats) {
|
|
||||||
status = AudioObjectGetPropertyData (stream_id,
|
|
||||||
&formatsAddress, 0, NULL, &propertySize, formats);
|
|
||||||
if (status != noErr) {
|
|
||||||
GST_WARNING ("failed getting the list of stream formats: %"
|
|
||||||
GST_FOURCC_FORMAT, GST_FOURCC_ARGS (status));
|
|
||||||
g_free (formats);
|
|
||||||
*nformats = 0;
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return formats;
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline gboolean
|
|
||||||
_audio_stream_is_spdif_avail (AudioStreamID stream_id)
|
|
||||||
{
|
|
||||||
AudioStreamRangedDescription *formats;
|
|
||||||
gint i, nformats = 0;
|
|
||||||
gboolean res = FALSE;
|
|
||||||
|
|
||||||
formats = _audio_stream_get_formats (stream_id, &nformats);
|
|
||||||
GST_DEBUG ("found %d stream formats", nformats);
|
|
||||||
|
|
||||||
if (formats) {
|
|
||||||
GST_DEBUG ("formats supported on stream ID: %u",
|
|
||||||
(unsigned) stream_id);
|
|
||||||
|
|
||||||
for (i = 0; i < nformats; i++) {
|
|
||||||
GST_DEBUG (" " CORE_AUDIO_FORMAT,
|
|
||||||
CORE_AUDIO_FORMAT_ARGS (formats[i].mFormat));
|
|
||||||
|
|
||||||
if (CORE_AUDIO_FORMAT_IS_SPDIF (formats[i])) {
|
|
||||||
res = TRUE;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
g_free (formats);
|
|
||||||
}
|
|
||||||
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline gboolean
|
|
||||||
_audio_device_is_spdif_avail (AudioDeviceID device_id)
|
|
||||||
{
|
|
||||||
AudioStreamID *streams = NULL;
|
|
||||||
gint i, nstreams = 0;
|
|
||||||
gboolean res = FALSE;
|
|
||||||
|
|
||||||
streams = _audio_device_get_streams (device_id, &nstreams);
|
|
||||||
GST_DEBUG ("found %d streams", nstreams);
|
|
||||||
if (streams) {
|
|
||||||
for (i = 0; i < nstreams; i++) {
|
|
||||||
if (_audio_stream_is_spdif_avail (streams[i])) {
|
|
||||||
res = TRUE;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
g_free (streams);
|
|
||||||
}
|
|
||||||
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
#endif /* __GST_CORE_AUDIO_H__ */
|
||||||
|
|
431
sys/osxaudio/gstosxcoreaudiocommon.c
Normal file
431
sys/osxaudio/gstosxcoreaudiocommon.c
Normal file
|
@ -0,0 +1,431 @@
|
||||||
|
/*
|
||||||
|
* GStreamer
|
||||||
|
* Copyright (C) 2012-2013 Fluendo S.A. <support@fluendo.com>
|
||||||
|
* Authors: Josep Torra Vallès <josep@fluendo.com>
|
||||||
|
* Andoni Morales Alastruey <amorales@fluendo.com>
|
||||||
|
*
|
||||||
|
* This library is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU Library General Public
|
||||||
|
* License as published by the Free Software Foundation; either
|
||||||
|
* version 2 of the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This library is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* Library General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Library General Public
|
||||||
|
* License along with this library; if not, write to the
|
||||||
|
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||||||
|
* Boston, MA 02111-1307, USA.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "gstosxcoreaudiocommon.h"
|
||||||
|
|
||||||
|
void
|
||||||
|
gst_core_audio_remove_render_callback (GstCoreAudio * core_audio)
|
||||||
|
{
|
||||||
|
AURenderCallbackStruct input;
|
||||||
|
OSStatus status;
|
||||||
|
|
||||||
|
/* Deactivate the render callback by calling SetRenderCallback
|
||||||
|
* with a NULL inputProc.
|
||||||
|
*/
|
||||||
|
input.inputProc = NULL;
|
||||||
|
input.inputProcRefCon = NULL;
|
||||||
|
|
||||||
|
status = AudioUnitSetProperty (core_audio->audiounit, kAudioUnitProperty_SetRenderCallback, kAudioUnitScope_Global, 0, /* N/A for global */
|
||||||
|
&input, sizeof (input));
|
||||||
|
|
||||||
|
if (status) {
|
||||||
|
GST_WARNING_OBJECT (core_audio->osxbuf, "Failed to remove render callback %"
|
||||||
|
GST_FOURCC_FORMAT, GST_FOURCC_ARGS (status));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Remove the RenderNotify too */
|
||||||
|
status = AudioUnitRemoveRenderNotify (core_audio->audiounit,
|
||||||
|
(AURenderCallback) gst_core_audio_render_notify, core_audio);
|
||||||
|
|
||||||
|
if (status) {
|
||||||
|
GST_WARNING_OBJECT (core_audio->osxbuf,
|
||||||
|
"Failed to remove render notify callback %" GST_FOURCC_FORMAT,
|
||||||
|
GST_FOURCC_ARGS (status));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* We're deactivated.. */
|
||||||
|
core_audio->io_proc_needs_deactivation = FALSE;
|
||||||
|
core_audio->io_proc_active = FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
OSStatus
|
||||||
|
gst_core_audio_render_notify (GstCoreAudio * core_audio,
|
||||||
|
AudioUnitRenderActionFlags * ioActionFlags,
|
||||||
|
const AudioTimeStamp * inTimeStamp,
|
||||||
|
unsigned int inBusNumber,
|
||||||
|
unsigned int inNumberFrames, AudioBufferList * ioData)
|
||||||
|
{
|
||||||
|
/* Before rendering a frame, we get the PreRender notification.
|
||||||
|
* Here, we detach the RenderCallback if we've been paused.
|
||||||
|
*
|
||||||
|
* This is necessary (rather than just directly detaching it) to
|
||||||
|
* work around some thread-safety issues in CoreAudio
|
||||||
|
*/
|
||||||
|
if ((*ioActionFlags) & kAudioUnitRenderAction_PreRender) {
|
||||||
|
if (core_audio->io_proc_needs_deactivation) {
|
||||||
|
gst_core_audio_remove_render_callback (core_audio);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return noErr;
|
||||||
|
}
|
||||||
|
|
||||||
|
gboolean
|
||||||
|
gst_core_audio_io_proc_start (GstCoreAudio * core_audio)
|
||||||
|
{
|
||||||
|
OSStatus status;
|
||||||
|
AURenderCallbackStruct input;
|
||||||
|
AudioUnitPropertyID callback_type;
|
||||||
|
|
||||||
|
GST_DEBUG_OBJECT (core_audio->osxbuf,
|
||||||
|
"osx ring buffer start ioproc: %p device_id %lu",
|
||||||
|
core_audio->element->io_proc, (gulong) core_audio->device_id);
|
||||||
|
if (!core_audio->io_proc_active) {
|
||||||
|
callback_type = core_audio->is_src ?
|
||||||
|
kAudioOutputUnitProperty_SetInputCallback :
|
||||||
|
kAudioUnitProperty_SetRenderCallback;
|
||||||
|
|
||||||
|
input.inputProc = (AURenderCallback) core_audio->element->io_proc;
|
||||||
|
input.inputProcRefCon = core_audio->osxbuf;
|
||||||
|
|
||||||
|
status = AudioUnitSetProperty (core_audio->audiounit, callback_type, kAudioUnitScope_Global, 0, /* N/A for global */
|
||||||
|
&input, sizeof (input));
|
||||||
|
|
||||||
|
if (status) {
|
||||||
|
GST_ERROR_OBJECT (core_audio->osxbuf,
|
||||||
|
"AudioUnitSetProperty failed: %" GST_FOURCC_FORMAT,
|
||||||
|
GST_FOURCC_ARGS (status));
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
// ### does it make sense to do this notify stuff for input mode?
|
||||||
|
status = AudioUnitAddRenderNotify (core_audio->audiounit,
|
||||||
|
(AURenderCallback) gst_core_audio_render_notify, core_audio);
|
||||||
|
|
||||||
|
if (status) {
|
||||||
|
GST_ERROR_OBJECT (core_audio->osxbuf,
|
||||||
|
"AudioUnitAddRenderNotify failed %"
|
||||||
|
GST_FOURCC_FORMAT, GST_FOURCC_ARGS (status));
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
core_audio->io_proc_active = TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
core_audio->io_proc_needs_deactivation = FALSE;
|
||||||
|
|
||||||
|
status = AudioOutputUnitStart (core_audio->audiounit);
|
||||||
|
if (status) {
|
||||||
|
GST_ERROR_OBJECT (core_audio->osxbuf, "AudioOutputUnitStart failed: %"
|
||||||
|
GST_FOURCC_FORMAT, GST_FOURCC_ARGS (status));
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
gboolean
|
||||||
|
gst_core_audio_io_proc_stop (GstCoreAudio * core_audio)
|
||||||
|
{
|
||||||
|
OSErr status;
|
||||||
|
|
||||||
|
GST_DEBUG_OBJECT (core_audio->osxbuf,
|
||||||
|
"osx ring buffer stop ioproc: %p device_id %lu",
|
||||||
|
core_audio->element->io_proc, (gulong) core_audio->device_id);
|
||||||
|
|
||||||
|
status = AudioOutputUnitStop (core_audio->audiounit);
|
||||||
|
if (status) {
|
||||||
|
GST_WARNING_OBJECT (core_audio->osxbuf,
|
||||||
|
"AudioOutputUnitStop failed: %" GST_FOURCC_FORMAT,
|
||||||
|
GST_FOURCC_ARGS (status));
|
||||||
|
}
|
||||||
|
// ###: why is it okay to directly remove from here but not from pause() ?
|
||||||
|
if (core_audio->io_proc_active) {
|
||||||
|
gst_core_audio_remove_render_callback (core_audio);
|
||||||
|
}
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
AudioBufferList *
|
||||||
|
buffer_list_alloc (int channels, int size)
|
||||||
|
{
|
||||||
|
AudioBufferList *list;
|
||||||
|
int total_size;
|
||||||
|
int n;
|
||||||
|
|
||||||
|
total_size = sizeof (AudioBufferList) + 1 * sizeof (AudioBuffer);
|
||||||
|
list = (AudioBufferList *) g_malloc (total_size);
|
||||||
|
|
||||||
|
list->mNumberBuffers = 1;
|
||||||
|
for (n = 0; n < (int) list->mNumberBuffers; ++n) {
|
||||||
|
list->mBuffers[n].mNumberChannels = channels;
|
||||||
|
list->mBuffers[n].mDataByteSize = size;
|
||||||
|
list->mBuffers[n].mData = g_malloc (size);
|
||||||
|
}
|
||||||
|
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
buffer_list_free (AudioBufferList * list)
|
||||||
|
{
|
||||||
|
int n;
|
||||||
|
|
||||||
|
for (n = 0; n < (int) list->mNumberBuffers; ++n) {
|
||||||
|
if (list->mBuffers[n].mData)
|
||||||
|
g_free (list->mBuffers[n].mData);
|
||||||
|
}
|
||||||
|
|
||||||
|
g_free (list);
|
||||||
|
}
|
||||||
|
|
||||||
|
gboolean
|
||||||
|
gst_core_audio_bind_device (GstCoreAudio * core_audio)
|
||||||
|
{
|
||||||
|
OSStatus status;
|
||||||
|
|
||||||
|
/* Specify which device we're using. */
|
||||||
|
GST_DEBUG_OBJECT (core_audio->osxbuf, "Bind AudioUnit to device %d",
|
||||||
|
(int) core_audio->device_id);
|
||||||
|
status = AudioUnitSetProperty (core_audio->audiounit,
|
||||||
|
kAudioOutputUnitProperty_CurrentDevice, kAudioUnitScope_Global, 0,
|
||||||
|
&core_audio->device_id, sizeof (AudioDeviceID));
|
||||||
|
if (status) {
|
||||||
|
GST_ERROR_OBJECT (core_audio->osxbuf, "Failed binding to device: %"
|
||||||
|
GST_FOURCC_FORMAT, GST_FOURCC_ARGS (status));
|
||||||
|
goto audiounit_error;
|
||||||
|
}
|
||||||
|
return TRUE;
|
||||||
|
|
||||||
|
audiounit_error:
|
||||||
|
if (core_audio->recBufferList) {
|
||||||
|
buffer_list_free (core_audio->recBufferList);
|
||||||
|
core_audio->recBufferList = NULL;
|
||||||
|
}
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
gboolean
|
||||||
|
gst_core_audio_set_channels_layout (GstCoreAudio * core_audio,
|
||||||
|
gint channels, GstCaps * caps)
|
||||||
|
{
|
||||||
|
/* Configure the output stream and allocate ringbuffer memory */
|
||||||
|
AudioChannelLayout *layout = NULL;
|
||||||
|
OSStatus status;
|
||||||
|
int layoutSize, element, i;
|
||||||
|
AudioUnitScope scope;
|
||||||
|
GstStructure *structure;
|
||||||
|
GstAudioChannelPosition *positions;
|
||||||
|
|
||||||
|
/* Describe channels */
|
||||||
|
layoutSize = sizeof (AudioChannelLayout) +
|
||||||
|
channels * sizeof (AudioChannelDescription);
|
||||||
|
layout = g_malloc (layoutSize);
|
||||||
|
|
||||||
|
structure = gst_caps_get_structure (caps, 0);
|
||||||
|
positions = gst_audio_get_channel_positions (structure);
|
||||||
|
|
||||||
|
layout->mChannelLayoutTag = kAudioChannelLayoutTag_UseChannelDescriptions;
|
||||||
|
layout->mChannelBitmap = 0; /* Not used */
|
||||||
|
layout->mNumberChannelDescriptions = channels;
|
||||||
|
for (i = 0; i < channels; i++) {
|
||||||
|
if (positions) {
|
||||||
|
layout->mChannelDescriptions[i].mChannelLabel =
|
||||||
|
gst_audio_channel_position_to_coreaudio_channel_label (positions[i],
|
||||||
|
i);
|
||||||
|
} else {
|
||||||
|
/* Discrete channel numbers are ORed into this */
|
||||||
|
layout->mChannelDescriptions[i].mChannelLabel =
|
||||||
|
kAudioChannelLabel_Discrete_0 | i;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Others unused */
|
||||||
|
layout->mChannelDescriptions[i].mChannelFlags = 0;
|
||||||
|
layout->mChannelDescriptions[i].mCoordinates[0] = 0.f;
|
||||||
|
layout->mChannelDescriptions[i].mCoordinates[1] = 0.f;
|
||||||
|
layout->mChannelDescriptions[i].mCoordinates[2] = 0.f;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (positions) {
|
||||||
|
g_free (positions);
|
||||||
|
positions = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
scope = core_audio->is_src ? kAudioUnitScope_Output : kAudioUnitScope_Input;
|
||||||
|
element = core_audio->is_src ? 1 : 0;
|
||||||
|
|
||||||
|
if (layoutSize) {
|
||||||
|
status = AudioUnitSetProperty (core_audio->audiounit,
|
||||||
|
kAudioUnitProperty_AudioChannelLayout,
|
||||||
|
scope, element, layout, layoutSize);
|
||||||
|
if (status) {
|
||||||
|
GST_WARNING_OBJECT (core_audio->osxbuf,
|
||||||
|
"Failed to set output channel layout: %" GST_FOURCC_FORMAT,
|
||||||
|
GST_FOURCC_ARGS (status));
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
g_free (layout);
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
gboolean
|
||||||
|
gst_core_audio_set_format (GstCoreAudio * core_audio,
|
||||||
|
AudioStreamBasicDescription format)
|
||||||
|
{
|
||||||
|
/* Configure the output stream and allocate ringbuffer memory */
|
||||||
|
OSStatus status;
|
||||||
|
UInt32 propertySize;
|
||||||
|
int element;
|
||||||
|
AudioUnitScope scope;
|
||||||
|
|
||||||
|
GST_DEBUG_OBJECT (core_audio->osxbuf, "Setting format for AudioUnit");
|
||||||
|
|
||||||
|
scope = core_audio->is_src ? kAudioUnitScope_Output : kAudioUnitScope_Input;
|
||||||
|
element = core_audio->is_src ? 1 : 0;
|
||||||
|
|
||||||
|
propertySize = sizeof (AudioStreamBasicDescription);
|
||||||
|
status = AudioUnitSetProperty (core_audio->audiounit,
|
||||||
|
kAudioUnitProperty_StreamFormat, scope, element, &format, propertySize);
|
||||||
|
|
||||||
|
if (status) {
|
||||||
|
GST_WARNING_OBJECT (core_audio->osxbuf,
|
||||||
|
"Failed to set audio description: %" GST_FOURCC_FORMAT,
|
||||||
|
GST_FOURCC_ARGS (status));
|
||||||
|
return FALSE;;
|
||||||
|
}
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
gboolean
|
||||||
|
gst_core_audio_open_device (GstCoreAudio * core_audio, OSType sub_type,
|
||||||
|
const gchar * adesc)
|
||||||
|
{
|
||||||
|
AudioComponentDescription desc;
|
||||||
|
AudioComponent comp;
|
||||||
|
OSStatus status;
|
||||||
|
AudioUnit unit;
|
||||||
|
UInt32 enableIO;
|
||||||
|
|
||||||
|
desc.componentType = kAudioUnitType_Output;
|
||||||
|
desc.componentSubType = sub_type;
|
||||||
|
desc.componentManufacturer = kAudioUnitManufacturer_Apple;
|
||||||
|
desc.componentFlags = 0;
|
||||||
|
desc.componentFlagsMask = 0;
|
||||||
|
|
||||||
|
comp = AudioComponentFindNext (NULL, &desc);
|
||||||
|
|
||||||
|
if (comp == NULL) {
|
||||||
|
GST_WARNING_OBJECT (core_audio->osxbuf, "Couldn't find %s component",
|
||||||
|
adesc);
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
status = AudioComponentInstanceNew (comp, &unit);
|
||||||
|
|
||||||
|
if (status) {
|
||||||
|
GST_ERROR_OBJECT (core_audio->osxbuf, "Couldn't open %s component %"
|
||||||
|
GST_FOURCC_FORMAT, adesc, GST_FOURCC_ARGS (status));
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (core_audio->is_src) {
|
||||||
|
/* enable input */
|
||||||
|
enableIO = 1;
|
||||||
|
status = AudioUnitSetProperty (unit, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Input, 1, /* 1 = input element */
|
||||||
|
&enableIO, sizeof (enableIO));
|
||||||
|
|
||||||
|
if (status) {
|
||||||
|
AudioComponentInstanceDispose (unit);
|
||||||
|
GST_WARNING_OBJECT (core_audio->osxbuf, "Failed to enable input: %"
|
||||||
|
GST_FOURCC_FORMAT, GST_FOURCC_ARGS (status));
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* disable output */
|
||||||
|
enableIO = 0;
|
||||||
|
status = AudioUnitSetProperty (unit, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Output, 0, /* 0 = output element */
|
||||||
|
&enableIO, sizeof (enableIO));
|
||||||
|
|
||||||
|
if (status) {
|
||||||
|
AudioComponentInstanceDispose (unit);
|
||||||
|
GST_WARNING_OBJECT (core_audio->osxbuf, "Failed to disable output: %"
|
||||||
|
GST_FOURCC_FORMAT, GST_FOURCC_ARGS (status));
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
GST_DEBUG_OBJECT (core_audio->osxbuf, "Created %s AudioUnit: %p", adesc,
|
||||||
|
unit);
|
||||||
|
core_audio->audiounit = unit;
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
AudioChannelLabel
|
||||||
|
gst_audio_channel_position_to_coreaudio_channel_label (GstAudioChannelPosition
|
||||||
|
position, int channel)
|
||||||
|
{
|
||||||
|
switch (position) {
|
||||||
|
case GST_AUDIO_CHANNEL_POSITION_NONE:
|
||||||
|
return kAudioChannelLabel_Discrete_0 | channel;
|
||||||
|
case GST_AUDIO_CHANNEL_POSITION_FRONT_MONO:
|
||||||
|
return kAudioChannelLabel_Mono;
|
||||||
|
case GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT:
|
||||||
|
return kAudioChannelLabel_Left;
|
||||||
|
case GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT:
|
||||||
|
return kAudioChannelLabel_Right;
|
||||||
|
case GST_AUDIO_CHANNEL_POSITION_REAR_CENTER:
|
||||||
|
return kAudioChannelLabel_CenterSurround;
|
||||||
|
case GST_AUDIO_CHANNEL_POSITION_REAR_LEFT:
|
||||||
|
return kAudioChannelLabel_LeftSurround;
|
||||||
|
case GST_AUDIO_CHANNEL_POSITION_REAR_RIGHT:
|
||||||
|
return kAudioChannelLabel_RightSurround;
|
||||||
|
case GST_AUDIO_CHANNEL_POSITION_LFE:
|
||||||
|
return kAudioChannelLabel_LFEScreen;
|
||||||
|
case GST_AUDIO_CHANNEL_POSITION_FRONT_CENTER:
|
||||||
|
return kAudioChannelLabel_Center;
|
||||||
|
case GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER:
|
||||||
|
return kAudioChannelLabel_Center; // ???
|
||||||
|
case GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER:
|
||||||
|
return kAudioChannelLabel_Center; // ???
|
||||||
|
case GST_AUDIO_CHANNEL_POSITION_SIDE_LEFT:
|
||||||
|
return kAudioChannelLabel_LeftSurroundDirect;
|
||||||
|
case GST_AUDIO_CHANNEL_POSITION_SIDE_RIGHT:
|
||||||
|
return kAudioChannelLabel_RightSurroundDirect;
|
||||||
|
default:
|
||||||
|
return kAudioChannelLabel_Unknown;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
gst_core_audio_dump_channel_layout (AudioChannelLayout * channel_layout)
|
||||||
|
{
|
||||||
|
UInt32 i;
|
||||||
|
|
||||||
|
GST_DEBUG ("mChannelLayoutTag: 0x%lx",
|
||||||
|
(unsigned long) channel_layout->mChannelLayoutTag);
|
||||||
|
GST_DEBUG ("mChannelBitmap: 0x%lx",
|
||||||
|
(unsigned long) channel_layout->mChannelBitmap);
|
||||||
|
GST_DEBUG ("mNumberChannelDescriptions: %lu",
|
||||||
|
(unsigned long) channel_layout->mNumberChannelDescriptions);
|
||||||
|
for (i = 0; i < channel_layout->mNumberChannelDescriptions; i++) {
|
||||||
|
AudioChannelDescription *channel_desc =
|
||||||
|
&channel_layout->mChannelDescriptions[i];
|
||||||
|
GST_DEBUG (" mChannelLabel: 0x%lx mChannelFlags: 0x%lx "
|
||||||
|
"mCoordinates[0]: %f mCoordinates[1]: %f "
|
||||||
|
"mCoordinates[2]: %f",
|
||||||
|
(unsigned long) channel_desc->mChannelLabel,
|
||||||
|
(unsigned long) channel_desc->mChannelFlags,
|
||||||
|
channel_desc->mCoordinates[0], channel_desc->mCoordinates[1],
|
||||||
|
channel_desc->mCoordinates[2]);
|
||||||
|
}
|
||||||
|
}
|
65
sys/osxaudio/gstosxcoreaudiocommon.h
Normal file
65
sys/osxaudio/gstosxcoreaudiocommon.h
Normal file
|
@ -0,0 +1,65 @@
|
||||||
|
/*
|
||||||
|
* GStreamer
|
||||||
|
* Copyright (C) 2012-2013 Fluendo S.A. <support@fluendo.com>
|
||||||
|
* Authors: Josep Torra Vallès <josep@fluendo.com>
|
||||||
|
* Andoni Morales Alastruey <amorales@fluendo.com>
|
||||||
|
*
|
||||||
|
* This library is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU Library General Public
|
||||||
|
* License as published by the Free Software Foundation; either
|
||||||
|
* version 2 of the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This library is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* Library General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Library General Public
|
||||||
|
* License along with this library; if not, write to the
|
||||||
|
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||||||
|
* Boston, MA 02111-1307, USA.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <gst/audio/multichannel.h>
|
||||||
|
#include "gstosxcoreaudio.h"
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
GMutex *lock;
|
||||||
|
GCond *cond;
|
||||||
|
} PropertyMutex;
|
||||||
|
|
||||||
|
gboolean gst_core_audio_bind_device (GstCoreAudio *core_audio);
|
||||||
|
|
||||||
|
void gst_core_audio_dump_channel_layout (AudioChannelLayout * channel_layout);
|
||||||
|
|
||||||
|
void gst_core_audio_remove_render_callback (GstCoreAudio * core_audio);
|
||||||
|
|
||||||
|
gboolean gst_core_audio_io_proc_start (GstCoreAudio * core_audio);
|
||||||
|
|
||||||
|
gboolean gst_core_audio_io_proc_stop (GstCoreAudio * core_audio);
|
||||||
|
|
||||||
|
AudioBufferList * buffer_list_alloc (int channels, int size);
|
||||||
|
|
||||||
|
void buffer_list_free (AudioBufferList * list);
|
||||||
|
|
||||||
|
gboolean gst_core_audio_set_format (GstCoreAudio * core_audio,
|
||||||
|
AudioStreamBasicDescription format);
|
||||||
|
|
||||||
|
gboolean gst_core_audio_set_channels_layout (GstCoreAudio * core_audio,
|
||||||
|
gint channels, GstCaps * caps);
|
||||||
|
|
||||||
|
gboolean gst_core_audio_open_device (GstCoreAudio *core_audio,
|
||||||
|
OSType sub_type,
|
||||||
|
const gchar *adesc);
|
||||||
|
|
||||||
|
OSStatus gst_core_audio_render_notify (GstCoreAudio * core_audio,
|
||||||
|
AudioUnitRenderActionFlags * ioActionFlags,
|
||||||
|
const AudioTimeStamp * inTimeStamp,
|
||||||
|
unsigned int inBusNumber,
|
||||||
|
unsigned int inNumberFrames,
|
||||||
|
AudioBufferList * ioData);
|
||||||
|
|
||||||
|
AudioChannelLabel gst_audio_channel_position_to_coreaudio_channel_label (GstAudioChannelPosition position, int channel);
|
||||||
|
|
1281
sys/osxaudio/gstosxcoreaudiohal.c
Normal file
1281
sys/osxaudio/gstosxcoreaudiohal.c
Normal file
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
|
@ -45,12 +45,14 @@
|
||||||
#ifndef __GST_OSX_RING_BUFFER_H__
|
#ifndef __GST_OSX_RING_BUFFER_H__
|
||||||
#define __GST_OSX_RING_BUFFER_H__
|
#define __GST_OSX_RING_BUFFER_H__
|
||||||
|
|
||||||
|
#ifdef HAVE_CONFIG_H
|
||||||
|
# include <config.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
#include <gst/gst.h>
|
#include <gst/gst.h>
|
||||||
#include <gst/audio/gstringbuffer.h>
|
#include <gst/audio/gstringbuffer.h>
|
||||||
#include <CoreAudio/CoreAudio.h>
|
#include <gstosxcoreaudio.h>
|
||||||
#include <AudioToolbox/AudioToolbox.h>
|
|
||||||
|
|
||||||
#include "gstosxaudioelement.h"
|
|
||||||
|
|
||||||
G_BEGIN_DECLS
|
G_BEGIN_DECLS
|
||||||
|
|
||||||
|
@ -76,30 +78,10 @@ struct _GstOsxRingBuffer
|
||||||
{
|
{
|
||||||
GstRingBuffer object;
|
GstRingBuffer object;
|
||||||
|
|
||||||
gboolean is_src;
|
GstCoreAudio *core_audio;
|
||||||
gboolean is_passthrough;
|
|
||||||
gint stream_idx;
|
|
||||||
|
|
||||||
AudioDeviceID device_id;
|
|
||||||
gboolean io_proc_active;
|
|
||||||
gboolean io_proc_needs_deactivation;
|
|
||||||
guint buffer_len;
|
guint buffer_len;
|
||||||
guint segoffset;
|
guint segoffset;
|
||||||
|
|
||||||
GstOsxAudioElementInterface *element;
|
|
||||||
|
|
||||||
/* For LPCM in/out */
|
|
||||||
AudioUnit audiounit;
|
|
||||||
AudioBufferList *recBufferList;
|
|
||||||
|
|
||||||
/* For SPDIF out */
|
|
||||||
pid_t hog_pid;
|
|
||||||
gboolean disabled_mixing;
|
|
||||||
AudioStreamID stream_id;
|
|
||||||
gboolean revert_format;
|
|
||||||
AudioStreamBasicDescription stream_format;
|
|
||||||
AudioStreamBasicDescription original_format;
|
|
||||||
AudioDeviceIOProcID procID;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
struct _GstOsxRingBufferClass
|
struct _GstOsxRingBufferClass
|
||||||
|
|
Loading…
Reference in a new issue