mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-11-27 04:01:08 +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 \
|
||||
gstosxaudiosink.c \
|
||||
gstosxaudiosrc.c \
|
||||
gstosxcoreaudiocommon.c \
|
||||
gstosxcoreaudio.c \
|
||||
gstosxaudio.c
|
||||
|
||||
libgstosxaudio_la_CFLAGS = $(GST_PLUGINS_BASE_CFLAGS) $(GST_CFLAGS) \
|
||||
|
@ -21,7 +23,9 @@ noinst_HEADERS = gstosxaudiosink.h \
|
|||
gstosxaudioelement.h \
|
||||
gstosxringbuffer.h \
|
||||
gstosxaudiosrc.h \
|
||||
gstosxcoreaudio.h
|
||||
gstosxcoreaudiocommon.h \
|
||||
gstosxcoreaudio.h \
|
||||
gstosxcoreaudiohal.c
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -69,8 +69,6 @@
|
|||
#include <gst/gst.h>
|
||||
#include <gst/audio/multichannel.h>
|
||||
#include <gst/audio/gstaudioiec61937.h>
|
||||
#include <CoreAudio/CoreAudio.h>
|
||||
#include <CoreAudio/AudioHardware.h>
|
||||
|
||||
#include "gstosxaudiosink.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,
|
||||
"OSX Audio Sink");
|
||||
gst_core_audio_init_debug ();
|
||||
GST_DEBUG ("Adding static interface");
|
||||
g_type_add_interface_static (type, GST_OSX_AUDIO_ELEMENT_TYPE,
|
||||
&osxelement_info);
|
||||
|
@ -425,19 +424,22 @@ gst_osx_audio_sink_create_ringbuffer (GstBaseAudioSink * sink)
|
|||
osxsink = GST_OSX_AUDIO_SINK (sink);
|
||||
|
||||
if (!gst_osx_audio_sink_select_device (osxsink)) {
|
||||
GST_ERROR_OBJECT (sink, "Could not select device");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
GST_DEBUG ("Creating ringbuffer");
|
||||
GST_DEBUG_OBJECT (sink, "Creating ringbuffer");
|
||||
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),
|
||||
(void *) gst_osx_audio_sink_io_proc);
|
||||
|
||||
gst_osx_audio_sink_set_volume (osxsink);
|
||||
|
||||
ringbuffer->element = GST_OSX_AUDIO_ELEMENT_GET_INTERFACE (osxsink);
|
||||
ringbuffer->device_id = osxsink->device_id;
|
||||
ringbuffer->core_audio->element =
|
||||
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);
|
||||
}
|
||||
|
@ -455,7 +457,7 @@ gst_osx_audio_sink_io_proc (GstOsxRingBuffer * buf,
|
|||
guint8 *readptr;
|
||||
gint readseg;
|
||||
gint len;
|
||||
gint stream_idx = buf->stream_idx;
|
||||
gint stream_idx = buf->core_audio->stream_idx;
|
||||
gint remaining = bufferList->mBuffers[stream_idx].mDataByteSize;
|
||||
gint offset = 0;
|
||||
|
||||
|
@ -500,35 +502,13 @@ gst_osx_audio_sink_osxelement_init (gpointer g_iface, gpointer iface_data)
|
|||
static void
|
||||
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;
|
||||
|
||||
AudioUnitSetParameter (sink->audiounit, kHALOutputParam_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]);
|
||||
}
|
||||
gst_core_audio_set_volume (osxbuf->core_audio, sink->volume);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
|
@ -554,14 +534,14 @@ gst_osx_audio_sink_allowed_caps (GstOsxAudioSink * osxsink)
|
|||
};
|
||||
|
||||
/* First collect info about the HW capabilites and preferences */
|
||||
spdif_allowed = _audio_device_is_spdif_avail (osxsink->device_id);
|
||||
layout = _audio_device_get_channel_layout (osxsink->device_id);
|
||||
spdif_allowed =
|
||||
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",
|
||||
(unsigned) osxsink->device_id, spdif_allowed);
|
||||
|
||||
if (layout) {
|
||||
_dump_channel_layout (layout);
|
||||
max_channels = layout->mNumberChannelDescriptions;
|
||||
} else {
|
||||
GST_WARNING_OBJECT (osxsink, "This driver does not support "
|
||||
|
@ -656,71 +636,11 @@ gst_osx_audio_sink_allowed_caps (GstOsxAudioSink * osxsink)
|
|||
static gboolean
|
||||
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;
|
||||
|
||||
devices = _audio_system_get_devices (&ndevices);
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
if (!gst_core_audio_select_device (&osxsink->device_id))
|
||||
return FALSE;
|
||||
res = gst_osx_audio_sink_allowed_caps (osxsink);
|
||||
|
||||
done:
|
||||
g_free (devices);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
|
|
@ -60,8 +60,6 @@
|
|||
#endif
|
||||
|
||||
#include <gst/gst.h>
|
||||
#include <CoreAudio/CoreAudio.h>
|
||||
#include <CoreAudio/AudioHardware.h>
|
||||
#include "gstosxaudiosrc.h"
|
||||
#include "gstosxaudioelement.h"
|
||||
|
||||
|
@ -266,9 +264,10 @@ gst_osx_audio_src_create_ringbuffer (GstBaseAudioSrc * src)
|
|||
GST_OSX_AUDIO_ELEMENT_GET_INTERFACE (osxsrc),
|
||||
(void *) gst_osx_audio_src_io_proc);
|
||||
|
||||
ringbuffer->element = GST_OSX_AUDIO_ELEMENT_GET_INTERFACE (osxsrc);
|
||||
ringbuffer->is_src = TRUE;
|
||||
ringbuffer->device_id = osxsrc->device_id;
|
||||
ringbuffer->core_audio->element =
|
||||
GST_OSX_AUDIO_ELEMENT_GET_INTERFACE (osxsrc);
|
||||
ringbuffer->core_audio->is_src = TRUE;
|
||||
ringbuffer->core_audio->device_id = osxsrc->device_id;
|
||||
|
||||
return GST_RING_BUFFER (ringbuffer);
|
||||
}
|
||||
|
@ -286,15 +285,15 @@ gst_osx_audio_src_io_proc (GstOsxRingBuffer * buf,
|
|||
gint remaining;
|
||||
gint offset = 0;
|
||||
|
||||
status = AudioUnitRender (buf->audiounit, ioActionFlags, inTimeStamp,
|
||||
inBusNumber, inNumberFrames, buf->recBufferList);
|
||||
status = AudioUnitRender (buf->core_audio->audiounit, ioActionFlags,
|
||||
inTimeStamp, inBusNumber, inNumberFrames, buf->core_audio->recBufferList);
|
||||
|
||||
if (status) {
|
||||
GST_WARNING_OBJECT (buf, "AudioUnitRender returned %d", (int) status);
|
||||
return status;
|
||||
}
|
||||
|
||||
remaining = buf->recBufferList->mBuffers[0].mDataByteSize;
|
||||
remaining = buf->core_audio->recBufferList->mBuffers[0].mDataByteSize;
|
||||
|
||||
while (remaining) {
|
||||
if (!gst_ring_buffer_prepare_read (GST_RING_BUFFER (buf),
|
||||
|
@ -307,7 +306,8 @@ gst_osx_audio_src_io_proc (GstOsxRingBuffer * buf,
|
|||
len = remaining;
|
||||
|
||||
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;
|
||||
offset += len;
|
||||
|
@ -334,30 +334,5 @@ gst_osx_audio_src_osxelement_init (gpointer g_iface, gpointer iface_data)
|
|||
static void
|
||||
gst_osx_audio_src_select_device (GstOsxAudioSrc * osxsrc)
|
||||
{
|
||||
OSStatus status;
|
||||
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);
|
||||
}
|
||||
gst_core_audio_select_source_device (&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
|
||||
* 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
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
|
@ -40,567 +17,135 @@
|
|||
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
|
||||
* 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_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
|
||||
_audio_system_set_runloop (CFRunLoopRef runLoop)
|
||||
struct _GstCoreAudio
|
||||
{
|
||||
OSStatus status = noErr;
|
||||
GObject object;
|
||||
|
||||
gboolean res = FALSE;
|
||||
GstObject *osxbuf;
|
||||
GstOsxAudioElementInterface *element;
|
||||
|
||||
AudioObjectPropertyAddress runloopAddress = {
|
||||
kAudioHardwarePropertyRunLoop,
|
||||
kAudioObjectPropertyScopeGlobal,
|
||||
kAudioObjectPropertyElementMaster
|
||||
};
|
||||
gboolean is_src;
|
||||
gboolean is_passthrough;
|
||||
AudioDeviceID device_id;
|
||||
AudioStreamBasicDescription stream_format;
|
||||
gint stream_idx;
|
||||
gboolean io_proc_active;
|
||||
gboolean io_proc_needs_deactivation;
|
||||
|
||||
status = AudioObjectSetPropertyData (kAudioObjectSystemObject,
|
||||
&runloopAddress, 0, NULL, sizeof (CFRunLoopRef), &runLoop);
|
||||
if (status == noErr) {
|
||||
res = TRUE;
|
||||
} else {
|
||||
GST_ERROR ("failed to set runloop to %p: %" GST_FOURCC_FORMAT,
|
||||
runLoop, GST_FOURCC_ARGS (status));
|
||||
}
|
||||
/* For LPCM in/out */
|
||||
AudioUnit audiounit;
|
||||
AudioBufferList *recBufferList;
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
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;
|
||||
#ifndef HAVE_IOS
|
||||
/* For SPDIF out */
|
||||
pid_t hog_pid;
|
||||
UInt32 propertySize = sizeof (hog_pid);
|
||||
gboolean disabled_mixing;
|
||||
AudioStreamID stream_id;
|
||||
gboolean revert_format;
|
||||
AudioStreamBasicDescription original_format;
|
||||
AudioDeviceIOProcID procID;
|
||||
#endif
|
||||
};
|
||||
|
||||
AudioObjectPropertyAddress audioDeviceHogModeAddress = {
|
||||
kAudioDevicePropertyHogMode,
|
||||
kAudioDevicePropertyScopeOutput,
|
||||
kAudioObjectPropertyElementMaster
|
||||
};
|
||||
|
||||
status = AudioObjectGetPropertyData (device_id,
|
||||
&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)
|
||||
struct _GstCoreAudioClass
|
||||
{
|
||||
OSStatus status = noErr;
|
||||
UInt32 propertySize = sizeof (hog_pid);
|
||||
gboolean res = FALSE;
|
||||
GObjectClass parent_class;
|
||||
};
|
||||
|
||||
AudioObjectPropertyAddress audioDeviceHogModeAddress = {
|
||||
kAudioDevicePropertyHogMode,
|
||||
kAudioDevicePropertyScopeOutput,
|
||||
kAudioObjectPropertyElementMaster
|
||||
};
|
||||
GType gst_core_audio_get_type (void);
|
||||
|
||||
status = AudioObjectSetPropertyData (device_id,
|
||||
&audioDeviceHogModeAddress, 0, NULL, propertySize, &hog_pid);
|
||||
void gst_core_audio_init_debug (void);
|
||||
|
||||
if (status == noErr) {
|
||||
res = TRUE;
|
||||
} else {
|
||||
GST_ERROR ("failed to set hog: %" GST_FOURCC_FORMAT,
|
||||
GST_FOURCC_ARGS (status));
|
||||
}
|
||||
GstCoreAudio * gst_core_audio_new (GstObject *osxbuf);
|
||||
|
||||
return res;
|
||||
}
|
||||
gboolean gst_core_audio_open (GstCoreAudio *core_audio);
|
||||
|
||||
static inline gboolean
|
||||
_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;
|
||||
gboolean gst_core_audio_close (GstCoreAudio *core_audio);
|
||||
|
||||
AudioObjectPropertyAddress audioDeviceSupportsMixingAddress = {
|
||||
kAudioDevicePropertySupportsMixing,
|
||||
kAudioObjectPropertyScopeGlobal,
|
||||
kAudioObjectPropertyElementMaster
|
||||
};
|
||||
gboolean gst_core_audio_initialize (GstCoreAudio *core_audio,
|
||||
AudioStreamBasicDescription format,
|
||||
GstCaps *caps,
|
||||
gboolean is_passthrough);
|
||||
|
||||
if (AudioObjectHasProperty (device_id, &audioDeviceSupportsMixingAddress)) {
|
||||
/* Set mixable to false if we are allowed to */
|
||||
status = AudioObjectIsPropertySettable (device_id,
|
||||
&audioDeviceSupportsMixingAddress, &writable);
|
||||
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));
|
||||
}
|
||||
void gst_core_audio_unitialize (GstCoreAudio *core_audio);
|
||||
|
||||
if (status == noErr && writable) {
|
||||
can_mix = enable_mix;
|
||||
status = AudioObjectSetPropertyData (device_id,
|
||||
&audioDeviceSupportsMixingAddress, 0, NULL, propertySize, &can_mix);
|
||||
res = TRUE;
|
||||
}
|
||||
gboolean gst_core_audio_start_processing (GstCoreAudio *core_audio);
|
||||
|
||||
if (status != noErr) {
|
||||
GST_ERROR ("failed to set mixmode: %" GST_FOURCC_FORMAT,
|
||||
GST_FOURCC_ARGS (status));
|
||||
}
|
||||
} else {
|
||||
GST_DEBUG ("property not found, mixing coudln't be changed");
|
||||
}
|
||||
gboolean gst_core_audio_pause_processing (GstCoreAudio *core_audio);
|
||||
|
||||
return res;
|
||||
}
|
||||
gboolean gst_core_audio_stop_processing (GstCoreAudio *core_audio);
|
||||
|
||||
static inline gchar *
|
||||
_audio_device_get_name (AudioDeviceID device_id)
|
||||
{
|
||||
OSStatus status = noErr;
|
||||
UInt32 propertySize = 0;
|
||||
gchar *device_name = NULL;
|
||||
gboolean gst_core_audio_get_samples_and_latency (GstCoreAudio * core_audio,
|
||||
gdouble rate,
|
||||
guint *samples,
|
||||
gdouble *latency);
|
||||
|
||||
AudioObjectPropertyAddress deviceNameAddress = {
|
||||
kAudioDevicePropertyDeviceName,
|
||||
kAudioDevicePropertyScopeOutput,
|
||||
kAudioObjectPropertyElementMaster
|
||||
};
|
||||
void gst_core_audio_set_volume (GstCoreAudio *core_audio,
|
||||
gfloat volume);
|
||||
|
||||
/* Get the length of the device name */
|
||||
status = AudioObjectGetPropertyDataSize (device_id,
|
||||
&deviceNameAddress, 0, NULL, &propertySize);
|
||||
if (status != noErr) {
|
||||
goto beach;
|
||||
}
|
||||
gboolean gst_core_audio_audio_device_is_spdif_avail (AudioDeviceID device_id);
|
||||
|
||||
/* Get the name of the device */
|
||||
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;
|
||||
}
|
||||
gboolean gst_core_audio_select_device (AudioDeviceID *device_id);
|
||||
|
||||
static inline gboolean
|
||||
_audio_device_has_output (AudioDeviceID device_id)
|
||||
{
|
||||
OSStatus status = noErr;
|
||||
UInt32 propertySize;
|
||||
gboolean gst_core_audio_select_source_device (AudioDeviceID *device_id);
|
||||
|
||||
AudioObjectPropertyAddress streamsAddress = {
|
||||
kAudioDevicePropertyStreams,
|
||||
kAudioDevicePropertyScopeOutput,
|
||||
kAudioObjectPropertyElementMaster
|
||||
};
|
||||
AudioChannelLayout * gst_core_audio_audio_device_get_channel_layout (AudioDeviceID device_id);
|
||||
|
||||
status = AudioObjectGetPropertyDataSize (device_id,
|
||||
&streamsAddress, 0, NULL, &propertySize);
|
||||
if (status != noErr) {
|
||||
return FALSE;
|
||||
}
|
||||
if (propertySize == 0) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static inline AudioChannelLayout *
|
||||
_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;
|
||||
}
|
||||
G_END_DECLS
|
||||
|
||||
#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__
|
||||
#define __GST_OSX_RING_BUFFER_H__
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
# include <config.h>
|
||||
#endif
|
||||
|
||||
#include <gst/gst.h>
|
||||
#include <gst/audio/gstringbuffer.h>
|
||||
#include <CoreAudio/CoreAudio.h>
|
||||
#include <AudioToolbox/AudioToolbox.h>
|
||||
#include <gstosxcoreaudio.h>
|
||||
|
||||
#include "gstosxaudioelement.h"
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
|
@ -76,30 +78,10 @@ struct _GstOsxRingBuffer
|
|||
{
|
||||
GstRingBuffer object;
|
||||
|
||||
gboolean is_src;
|
||||
gboolean is_passthrough;
|
||||
gint stream_idx;
|
||||
GstCoreAudio *core_audio;
|
||||
|
||||
AudioDeviceID device_id;
|
||||
gboolean io_proc_active;
|
||||
gboolean io_proc_needs_deactivation;
|
||||
guint buffer_len;
|
||||
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
|
||||
|
|
Loading…
Reference in a new issue