2013-03-05 20:17:52 +00:00
|
|
|
/*
|
|
|
|
* 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"
|
|
|
|
|
2023-07-25 14:21:24 +00:00
|
|
|
GST_DEBUG_CATEGORY_EXTERN (osx_coreaudio_debug);
|
|
|
|
#define GST_CAT_DEFAULT osx_coreaudio_debug
|
|
|
|
|
2013-03-05 20:17:52 +00:00
|
|
|
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) {
|
2014-09-17 10:55:18 +00:00
|
|
|
GST_WARNING_OBJECT (core_audio->osxbuf,
|
|
|
|
"Failed to remove render callback %d", (int) status);
|
2013-03-05 20:17:52 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* 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,
|
2014-09-17 10:55:18 +00:00
|
|
|
"Failed to remove render notify callback %d", (int) status);
|
2013-03-05 20:17:52 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* 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,
|
2014-09-17 10:55:18 +00:00
|
|
|
"AudioUnitSetProperty failed: %d", (int) status);
|
2013-03-05 20:17:52 +00:00
|
|
|
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,
|
2014-09-17 10:55:18 +00:00
|
|
|
"AudioUnitAddRenderNotify failed %d", (int) status);
|
2013-03-05 20:17:52 +00:00
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
core_audio->io_proc_active = TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
core_audio->io_proc_needs_deactivation = FALSE;
|
|
|
|
|
|
|
|
status = AudioOutputUnitStart (core_audio->audiounit);
|
|
|
|
if (status) {
|
2014-09-17 10:55:18 +00:00
|
|
|
GST_ERROR_OBJECT (core_audio->osxbuf, "AudioOutputUnitStart failed: %d",
|
|
|
|
(int) status);
|
2013-03-05 20:17:52 +00:00
|
|
|
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,
|
2014-09-17 10:55:18 +00:00
|
|
|
"AudioOutputUnitStop failed: %d", (int) status);
|
2013-03-05 20:17:52 +00:00
|
|
|
}
|
|
|
|
// ###: 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 *
|
2015-02-24 03:57:24 +00:00
|
|
|
buffer_list_alloc (UInt32 channels, UInt32 size, gboolean interleaved)
|
2013-03-05 20:17:52 +00:00
|
|
|
{
|
|
|
|
AudioBufferList *list;
|
2015-02-24 03:57:24 +00:00
|
|
|
gsize list_size;
|
|
|
|
UInt32 num_buffers, n;
|
|
|
|
|
|
|
|
num_buffers = interleaved ? 1 : channels;
|
2015-03-09 21:34:06 +00:00
|
|
|
/* AudioBufferList member mBuffers is variable-length array */
|
2015-02-24 03:57:24 +00:00
|
|
|
list_size = G_STRUCT_OFFSET (AudioBufferList, mBuffers[num_buffers]);
|
|
|
|
list = (AudioBufferList *) g_malloc (list_size);
|
|
|
|
|
|
|
|
list->mNumberBuffers = num_buffers;
|
|
|
|
for (n = 0; n < num_buffers; ++n) {
|
|
|
|
/* See http://lists.apple.com/archives/coreaudio-api/2015/Feb/msg00027.html */
|
|
|
|
list->mBuffers[n].mNumberChannels = interleaved ? channels : 1;
|
|
|
|
/* AudioUnitRender will keep overwriting mDataByteSize */
|
2013-03-05 20:17:52 +00:00
|
|
|
list->mBuffers[n].mDataByteSize = size;
|
|
|
|
list->mBuffers[n].mData = g_malloc (size);
|
|
|
|
}
|
|
|
|
|
|
|
|
return list;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
buffer_list_free (AudioBufferList * list)
|
|
|
|
{
|
2015-02-24 03:57:24 +00:00
|
|
|
UInt32 n;
|
|
|
|
|
|
|
|
if (list == NULL)
|
|
|
|
return;
|
2013-03-05 20:17:52 +00:00
|
|
|
|
2015-02-24 03:57:24 +00:00
|
|
|
for (n = 0; n < list->mNumberBuffers; ++n) {
|
|
|
|
g_free (list->mBuffers[n].mData);
|
2013-03-05 20:17:52 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
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) {
|
2014-09-17 10:55:18 +00:00
|
|
|
GST_ERROR_OBJECT (core_audio->osxbuf, "Failed binding to device: %d",
|
|
|
|
(int) status);
|
2013-03-05 20:17:52 +00:00
|
|
|
goto audiounit_error;
|
|
|
|
}
|
|
|
|
return TRUE;
|
|
|
|
|
|
|
|
audiounit_error:
|
|
|
|
if (core_audio->recBufferList) {
|
|
|
|
buffer_list_free (core_audio->recBufferList);
|
|
|
|
core_audio->recBufferList = NULL;
|
|
|
|
}
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
2015-03-09 21:34:06 +00:00
|
|
|
static gboolean
|
|
|
|
_core_audio_set_property (GstCoreAudio * core_audio, AudioUnitPropertyID inID,
|
|
|
|
void *inData, UInt32 inDataSize)
|
|
|
|
{
|
|
|
|
OSStatus status;
|
|
|
|
AudioUnitScope scope;
|
|
|
|
AudioUnitElement element;
|
|
|
|
|
|
|
|
scope = CORE_AUDIO_INNER_SCOPE (core_audio);
|
|
|
|
element = CORE_AUDIO_ELEMENT (core_audio);
|
|
|
|
|
|
|
|
status =
|
|
|
|
AudioUnitSetProperty (core_audio->audiounit, inID, scope, element, inData,
|
|
|
|
inDataSize);
|
|
|
|
|
|
|
|
if (status != noErr) {
|
|
|
|
GST_WARNING_OBJECT (core_audio->osxbuf,
|
|
|
|
"Failed to set Audio Unit property: %d", (int) status);
|
|
|
|
return FALSE;;
|
|
|
|
}
|
|
|
|
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
2015-04-06 10:52:34 +00:00
|
|
|
/* The AudioUnit must be uninitialized before calling this */
|
2013-03-05 20:17:52 +00:00
|
|
|
gboolean
|
2015-03-09 20:51:51 +00:00
|
|
|
gst_core_audio_set_channel_layout (GstCoreAudio * core_audio,
|
2013-03-05 20:17:52 +00:00
|
|
|
gint channels, GstCaps * caps)
|
|
|
|
{
|
|
|
|
AudioChannelLayout *layout = NULL;
|
2015-03-09 21:34:06 +00:00
|
|
|
gboolean ret;
|
|
|
|
gsize layoutSize;
|
|
|
|
gint i;
|
2013-03-05 20:17:52 +00:00
|
|
|
GstStructure *structure;
|
2015-03-09 21:34:06 +00:00
|
|
|
GstAudioChannelPosition positions[GST_OSX_AUDIO_MAX_CHANNEL];
|
2013-03-30 13:49:34 +00:00
|
|
|
guint64 channel_mask;
|
2013-03-05 20:17:52 +00:00
|
|
|
|
2015-03-09 21:34:06 +00:00
|
|
|
g_return_val_if_fail (channels <= GST_OSX_AUDIO_MAX_CHANNEL, FALSE);
|
2013-03-05 20:17:52 +00:00
|
|
|
|
2015-03-09 21:34:06 +00:00
|
|
|
/* Determine the channel positions */
|
2013-03-05 20:17:52 +00:00
|
|
|
structure = gst_caps_get_structure (caps, 0);
|
2015-03-09 21:34:06 +00:00
|
|
|
channel_mask = 0;
|
|
|
|
gst_structure_get (structure, "channel-mask", GST_TYPE_BITMASK, &channel_mask,
|
|
|
|
NULL);
|
|
|
|
|
|
|
|
if (channel_mask != 0)
|
2013-03-30 13:49:34 +00:00
|
|
|
gst_audio_channel_positions_from_mask (channels, channel_mask, positions);
|
2013-03-05 20:17:52 +00:00
|
|
|
|
2015-03-09 21:34:06 +00:00
|
|
|
/* AudioChannelLayout member mChannelDescriptions is variable-length array */
|
|
|
|
layoutSize =
|
|
|
|
G_STRUCT_OFFSET (AudioChannelLayout, mChannelDescriptions[channels]);
|
|
|
|
layout = g_malloc (layoutSize);
|
|
|
|
|
|
|
|
/* Fill out the AudioChannelLayout */
|
2013-03-05 20:17:52 +00:00
|
|
|
layout->mChannelLayoutTag = kAudioChannelLayoutTag_UseChannelDescriptions;
|
|
|
|
layout->mChannelBitmap = 0; /* Not used */
|
|
|
|
layout->mNumberChannelDescriptions = channels;
|
|
|
|
for (i = 0; i < channels; i++) {
|
2015-03-09 21:34:06 +00:00
|
|
|
if (channel_mask != 0) {
|
2013-03-05 20:17:52 +00:00
|
|
|
layout->mChannelDescriptions[i].mChannelLabel =
|
2015-03-09 21:34:06 +00:00
|
|
|
gst_audio_channel_position_to_core_audio (positions[i], i);
|
2013-03-05 20:17:52 +00:00
|
|
|
} else {
|
|
|
|
/* Discrete channel numbers are ORed into this */
|
|
|
|
layout->mChannelDescriptions[i].mChannelLabel =
|
|
|
|
kAudioChannelLabel_Discrete_0 | i;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Others unused */
|
2015-03-09 21:34:06 +00:00
|
|
|
layout->mChannelDescriptions[i].mChannelFlags = kAudioChannelFlags_AllOff;
|
2013-03-05 20:17:52 +00:00
|
|
|
layout->mChannelDescriptions[i].mCoordinates[0] = 0.f;
|
|
|
|
layout->mChannelDescriptions[i].mCoordinates[1] = 0.f;
|
|
|
|
layout->mChannelDescriptions[i].mCoordinates[2] = 0.f;
|
|
|
|
}
|
|
|
|
|
2015-03-09 21:34:06 +00:00
|
|
|
/* Sets GStreamer-ordered channel layout on the inner scope.
|
|
|
|
* Reordering between the inner scope and outer scope is handled
|
|
|
|
* by the Audio Unit itself. */
|
|
|
|
ret = _core_audio_set_property (core_audio,
|
|
|
|
kAudioUnitProperty_AudioChannelLayout, layout, layoutSize);
|
2013-03-05 20:17:52 +00:00
|
|
|
|
|
|
|
g_free (layout);
|
2015-03-09 21:34:06 +00:00
|
|
|
return ret;
|
2013-03-05 20:17:52 +00:00
|
|
|
}
|
|
|
|
|
2015-04-06 10:52:34 +00:00
|
|
|
/* The AudioUnit must be uninitialized before calling this */
|
2013-03-05 20:17:52 +00:00
|
|
|
gboolean
|
|
|
|
gst_core_audio_set_format (GstCoreAudio * core_audio,
|
|
|
|
AudioStreamBasicDescription format)
|
|
|
|
{
|
|
|
|
GST_DEBUG_OBJECT (core_audio->osxbuf, "Setting format for AudioUnit");
|
|
|
|
|
2015-03-09 21:34:06 +00:00
|
|
|
return _core_audio_set_property (core_audio, kAudioUnitProperty_StreamFormat,
|
|
|
|
&format, sizeof (AudioStreamBasicDescription));
|
2013-03-05 20:17:52 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
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) {
|
2014-09-17 10:55:18 +00:00
|
|
|
GST_ERROR_OBJECT (core_audio->osxbuf, "Couldn't open %s component %d",
|
|
|
|
adesc, (int) status);
|
2013-03-05 20:17:52 +00:00
|
|
|
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);
|
2014-09-17 10:55:18 +00:00
|
|
|
GST_WARNING_OBJECT (core_audio->osxbuf, "Failed to enable input: %d",
|
|
|
|
(int) status);
|
2013-03-05 20:17:52 +00:00
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* disable output */
|
|
|
|
enableIO = 0;
|
|
|
|
status = AudioUnitSetProperty (unit, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Output, 0, /* 0 = output element */
|
|
|
|
&enableIO, sizeof (enableIO));
|
|
|
|
|
|
|
|
if (status) {
|
|
|
|
AudioComponentInstanceDispose (unit);
|
2014-09-17 10:55:18 +00:00
|
|
|
GST_WARNING_OBJECT (core_audio->osxbuf, "Failed to disable output: %d",
|
|
|
|
(int) status);
|
2013-03-05 20:17:52 +00:00
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
GST_DEBUG_OBJECT (core_audio->osxbuf, "Created %s AudioUnit: %p", adesc,
|
|
|
|
unit);
|
|
|
|
core_audio->audiounit = unit;
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
AudioChannelLabel
|
2015-03-09 21:34:06 +00:00
|
|
|
gst_audio_channel_position_to_core_audio (GstAudioChannelPosition
|
2013-03-05 20:17:52 +00:00
|
|
|
position, int channel)
|
|
|
|
{
|
|
|
|
switch (position) {
|
|
|
|
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:
|
2023-11-29 15:40:52 +00:00
|
|
|
return kAudioChannelLabel_RearSurroundLeft;
|
2013-03-05 20:17:52 +00:00
|
|
|
case GST_AUDIO_CHANNEL_POSITION_REAR_RIGHT:
|
2023-11-29 15:40:52 +00:00
|
|
|
return kAudioChannelLabel_RearSurroundRight;
|
2013-03-30 13:49:34 +00:00
|
|
|
case GST_AUDIO_CHANNEL_POSITION_LFE1:
|
2013-03-05 20:17:52 +00:00
|
|
|
return kAudioChannelLabel_LFEScreen;
|
|
|
|
case GST_AUDIO_CHANNEL_POSITION_FRONT_CENTER:
|
|
|
|
return kAudioChannelLabel_Center;
|
|
|
|
case GST_AUDIO_CHANNEL_POSITION_SIDE_LEFT:
|
|
|
|
return kAudioChannelLabel_LeftSurroundDirect;
|
|
|
|
case GST_AUDIO_CHANNEL_POSITION_SIDE_RIGHT:
|
|
|
|
return kAudioChannelLabel_RightSurroundDirect;
|
2015-03-09 21:34:06 +00:00
|
|
|
case GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER:
|
|
|
|
return kAudioChannelLabel_LeftCenter;
|
|
|
|
case GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER:
|
|
|
|
return kAudioChannelLabel_RightCenter;
|
|
|
|
case GST_AUDIO_CHANNEL_POSITION_TOP_REAR_LEFT:
|
|
|
|
return kAudioChannelLabel_TopBackLeft;
|
|
|
|
case GST_AUDIO_CHANNEL_POSITION_TOP_REAR_CENTER:
|
|
|
|
return kAudioChannelLabel_TopBackCenter;
|
|
|
|
case GST_AUDIO_CHANNEL_POSITION_TOP_REAR_RIGHT:
|
|
|
|
return kAudioChannelLabel_TopBackRight;
|
|
|
|
case GST_AUDIO_CHANNEL_POSITION_WIDE_LEFT:
|
|
|
|
return kAudioChannelLabel_LeftWide;
|
|
|
|
case GST_AUDIO_CHANNEL_POSITION_WIDE_RIGHT:
|
|
|
|
return kAudioChannelLabel_RightWide;
|
|
|
|
case GST_AUDIO_CHANNEL_POSITION_LFE2:
|
|
|
|
return kAudioChannelLabel_LFE2;
|
|
|
|
case GST_AUDIO_CHANNEL_POSITION_TOP_FRONT_LEFT:
|
|
|
|
return kAudioChannelLabel_VerticalHeightLeft;
|
|
|
|
case GST_AUDIO_CHANNEL_POSITION_TOP_FRONT_RIGHT:
|
|
|
|
return kAudioChannelLabel_VerticalHeightRight;
|
|
|
|
case GST_AUDIO_CHANNEL_POSITION_TOP_FRONT_CENTER:
|
|
|
|
return kAudioChannelLabel_VerticalHeightCenter;
|
2023-11-29 15:43:30 +00:00
|
|
|
case GST_AUDIO_CHANNEL_POSITION_TOP_CENTER:
|
|
|
|
return kAudioChannelLabel_TopCenterSurround;
|
|
|
|
case GST_AUDIO_CHANNEL_POSITION_SURROUND_LEFT:
|
|
|
|
return kAudioChannelLabel_LeftSurround;
|
|
|
|
case GST_AUDIO_CHANNEL_POSITION_SURROUND_RIGHT:
|
|
|
|
return kAudioChannelLabel_RightSurround;
|
2015-03-09 21:34:06 +00:00
|
|
|
|
|
|
|
/* Special position values */
|
|
|
|
case GST_AUDIO_CHANNEL_POSITION_NONE:
|
|
|
|
return kAudioChannelLabel_Discrete_0 | channel;
|
|
|
|
case GST_AUDIO_CHANNEL_POSITION_MONO:
|
|
|
|
return kAudioChannelLabel_Mono;
|
|
|
|
|
|
|
|
/* Following positions are unmapped --
|
|
|
|
* i.e. mapped to kAudioChannelLabel_Unknown: */
|
|
|
|
case GST_AUDIO_CHANNEL_POSITION_TOP_SIDE_LEFT:
|
|
|
|
case GST_AUDIO_CHANNEL_POSITION_TOP_SIDE_RIGHT:
|
|
|
|
case GST_AUDIO_CHANNEL_POSITION_BOTTOM_FRONT_CENTER:
|
|
|
|
case GST_AUDIO_CHANNEL_POSITION_BOTTOM_FRONT_LEFT:
|
|
|
|
case GST_AUDIO_CHANNEL_POSITION_BOTTOM_FRONT_RIGHT:
|
2013-03-05 20:17:52 +00:00
|
|
|
default:
|
|
|
|
return kAudioChannelLabel_Unknown;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-03-09 21:34:06 +00:00
|
|
|
/* Performs a best-effort conversion. 'channel' is used for warnings only. */
|
|
|
|
GstAudioChannelPosition
|
|
|
|
gst_core_audio_channel_label_to_gst (AudioChannelLabel label,
|
|
|
|
int channel, gboolean warn)
|
|
|
|
{
|
|
|
|
switch (label) {
|
|
|
|
case kAudioChannelLabel_Left:
|
|
|
|
return GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT;
|
|
|
|
case kAudioChannelLabel_Right:
|
|
|
|
return GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT;
|
|
|
|
case kAudioChannelLabel_Center:
|
|
|
|
return GST_AUDIO_CHANNEL_POSITION_FRONT_CENTER;
|
|
|
|
case kAudioChannelLabel_LFEScreen:
|
|
|
|
return GST_AUDIO_CHANNEL_POSITION_LFE1;
|
|
|
|
case kAudioChannelLabel_LeftSurround:
|
2023-11-29 15:40:52 +00:00
|
|
|
return GST_AUDIO_CHANNEL_POSITION_SURROUND_LEFT;
|
2015-03-09 21:34:06 +00:00
|
|
|
case kAudioChannelLabel_RightSurround:
|
2023-11-29 15:40:52 +00:00
|
|
|
return GST_AUDIO_CHANNEL_POSITION_SURROUND_RIGHT;
|
2015-03-09 21:34:06 +00:00
|
|
|
case kAudioChannelLabel_LeftSurroundDirect:
|
|
|
|
return GST_AUDIO_CHANNEL_POSITION_SIDE_LEFT;
|
|
|
|
case kAudioChannelLabel_RightSurroundDirect:
|
|
|
|
return GST_AUDIO_CHANNEL_POSITION_SIDE_RIGHT;
|
|
|
|
case kAudioChannelLabel_CenterSurround:
|
|
|
|
return GST_AUDIO_CHANNEL_POSITION_REAR_CENTER;
|
|
|
|
case kAudioChannelLabel_LeftCenter:
|
|
|
|
return GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER;
|
|
|
|
case kAudioChannelLabel_RightCenter:
|
|
|
|
return GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER;
|
|
|
|
case kAudioChannelLabel_TopBackLeft:
|
|
|
|
return GST_AUDIO_CHANNEL_POSITION_TOP_REAR_LEFT;
|
|
|
|
case kAudioChannelLabel_TopBackCenter:
|
|
|
|
return GST_AUDIO_CHANNEL_POSITION_TOP_REAR_CENTER;
|
|
|
|
case kAudioChannelLabel_TopBackRight:
|
|
|
|
return GST_AUDIO_CHANNEL_POSITION_TOP_REAR_RIGHT;
|
|
|
|
case kAudioChannelLabel_LeftWide:
|
|
|
|
return GST_AUDIO_CHANNEL_POSITION_WIDE_LEFT;
|
|
|
|
case kAudioChannelLabel_RightWide:
|
|
|
|
return GST_AUDIO_CHANNEL_POSITION_WIDE_RIGHT;
|
|
|
|
case kAudioChannelLabel_LFE2:
|
|
|
|
return GST_AUDIO_CHANNEL_POSITION_LFE2;
|
|
|
|
case kAudioChannelLabel_VerticalHeightLeft:
|
|
|
|
return GST_AUDIO_CHANNEL_POSITION_TOP_FRONT_LEFT;
|
|
|
|
case kAudioChannelLabel_VerticalHeightRight:
|
|
|
|
return GST_AUDIO_CHANNEL_POSITION_TOP_FRONT_RIGHT;
|
|
|
|
case kAudioChannelLabel_VerticalHeightCenter:
|
|
|
|
return GST_AUDIO_CHANNEL_POSITION_TOP_FRONT_CENTER;
|
2023-11-29 15:43:30 +00:00
|
|
|
case kAudioChannelLabel_RearSurroundLeft:
|
|
|
|
return GST_AUDIO_CHANNEL_POSITION_REAR_LEFT;
|
|
|
|
case kAudioChannelLabel_RearSurroundRight:
|
|
|
|
return GST_AUDIO_CHANNEL_POSITION_REAR_RIGHT;
|
|
|
|
case kAudioChannelLabel_TopCenterSurround:
|
|
|
|
return GST_AUDIO_CHANNEL_POSITION_TOP_CENTER;
|
2015-03-09 21:34:06 +00:00
|
|
|
|
|
|
|
/* Special position values */
|
|
|
|
|
|
|
|
case kAudioChannelLabel_Mono:
|
|
|
|
/* GST_AUDIO_CHANNEL_POSITION_MONO is only for 1-channel layouts */
|
|
|
|
return GST_AUDIO_CHANNEL_POSITION_INVALID;
|
|
|
|
case kAudioChannelLabel_Discrete:
|
|
|
|
return GST_AUDIO_CHANNEL_POSITION_NONE;
|
|
|
|
|
|
|
|
/*
|
|
|
|
Following labels are unmapped --
|
|
|
|
i.e. mapped to GST_AUDIO_CHANNEL_POSITION_INVALID:
|
|
|
|
*/
|
|
|
|
case kAudioChannelLabel_LeftTotal:
|
|
|
|
case kAudioChannelLabel_RightTotal:
|
|
|
|
case kAudioChannelLabel_HearingImpaired:
|
|
|
|
case kAudioChannelLabel_Narration:
|
|
|
|
case kAudioChannelLabel_DialogCentricMix:
|
|
|
|
case kAudioChannelLabel_CenterSurroundDirect:
|
|
|
|
case kAudioChannelLabel_Haptic:
|
|
|
|
default:
|
|
|
|
if (label >> 16 != 0) { /* kAudioChannelLabel_Discrete_N */
|
|
|
|
/* no way to store discrete channel order */
|
|
|
|
if (warn)
|
|
|
|
GST_WARNING
|
|
|
|
("Core Audio channel %u labeled kAudioChannelLabel_Discrete_%u -- discrete order will be lost",
|
|
|
|
channel, ((unsigned int) label) & 0xFFFF);
|
|
|
|
return GST_AUDIO_CHANNEL_POSITION_NONE;
|
|
|
|
} else {
|
|
|
|
if (warn)
|
|
|
|
GST_WARNING
|
|
|
|
("Core Audio channel %u has unsupported label %d and will be skipped",
|
|
|
|
channel, (int) label);
|
|
|
|
return GST_AUDIO_CHANNEL_POSITION_INVALID;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-03-05 20:17:52 +00:00
|
|
|
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]);
|
|
|
|
}
|
|
|
|
}
|
2024-07-30 18:39:21 +00:00
|
|
|
|
2024-08-12 17:03:42 +00:00
|
|
|
#ifndef HAVE_IOS
|
2024-07-30 18:39:21 +00:00
|
|
|
char *
|
|
|
|
gst_core_audio_device_get_prop (AudioDeviceID device_id,
|
|
|
|
AudioObjectPropertyElement prop_id)
|
|
|
|
{
|
|
|
|
OSStatus status = noErr;
|
|
|
|
UInt32 propertySize = 0;
|
|
|
|
CFStringRef prop_val;
|
|
|
|
gchar *result = NULL;
|
|
|
|
|
|
|
|
AudioObjectPropertyAddress propAddress = {
|
|
|
|
prop_id,
|
|
|
|
kAudioDevicePropertyScopeOutput,
|
|
|
|
kAudioObjectPropertyElementMain
|
|
|
|
};
|
|
|
|
|
|
|
|
propAddress.mScope = kAudioObjectPropertyScopeGlobal;
|
|
|
|
|
|
|
|
/* Get the length of the device name */
|
|
|
|
status = AudioObjectGetPropertyDataSize (device_id,
|
|
|
|
&propAddress, 0, NULL, &propertySize);
|
|
|
|
if (status != noErr) {
|
|
|
|
goto beach;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Get the requested property */
|
|
|
|
status = AudioObjectGetPropertyData (device_id,
|
|
|
|
&propAddress, 0, NULL, &propertySize, &prop_val);
|
|
|
|
if (status != noErr) {
|
|
|
|
goto beach;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Convert to UTF-8 C String */
|
|
|
|
CFIndex prop_len = CFStringGetLength (prop_val);
|
|
|
|
CFIndex max_size =
|
|
|
|
CFStringGetMaximumSizeForEncoding (prop_len, kCFStringEncodingUTF8) + 1;
|
|
|
|
result = g_malloc (max_size);
|
|
|
|
|
|
|
|
if (!CFStringGetCString (prop_val, result, max_size, kCFStringEncodingUTF8)) {
|
|
|
|
g_free (result);
|
|
|
|
result = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
CFRelease (prop_val);
|
|
|
|
|
|
|
|
beach:
|
|
|
|
return result;
|
|
|
|
}
|
2024-08-12 17:03:42 +00:00
|
|
|
#endif
|