From d2c6cc3a3959f7c1b594988a913ef452134a396c Mon Sep 17 00:00:00 2001 From: Josep Torra Date: Tue, 19 Jun 2012 12:15:33 +0200 Subject: [PATCH] osxaudiosink: respect the prefered channel layout In OSX is allowed to configure the default audio output device, prefered channel layout and speaker positions through the tool "Audio MIDI Setup". --- sys/osxaudio/Makefile.am | 2 +- sys/osxaudio/gstosxaudiosink.c | 179 ++++++++++++++++++++++++-------- sys/osxaudio/gstosxaudiosink.h | 1 + sys/osxaudio/gstosxcoreaudio.h | 42 ++++++-- sys/osxaudio/gstosxringbuffer.c | 8 -- sys/osxaudio/gstosxringbuffer.h | 3 +- 6 files changed, 174 insertions(+), 61 deletions(-) diff --git a/sys/osxaudio/Makefile.am b/sys/osxaudio/Makefile.am index 6c168a92d2..138e5aef98 100644 --- a/sys/osxaudio/Makefile.am +++ b/sys/osxaudio/Makefile.am @@ -14,7 +14,7 @@ libgstosxaudio_la_LIBADD = \ $(GST_PLUGINS_BASE_LIBS) \ $(GST_BASE_LIBS) \ $(GST_LIBS) -libgstosxaudio_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS) -Wl,-framework -Wl,CoreAudio -Wl,-framework -Wl,AudioUnit -Wl,-framework -Wl,CoreServices +libgstosxaudio_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS) -Wl,-framework -Wl,CoreAudio -Wl,-framework -Wl,AudioUnit -Wl,-framework -Wl,AudioToolbox -Wl,-framework -Wl,CoreServices libgstosxaudio_la_LIBTOOLFLAGS = --tag=disable-static noinst_HEADERS = gstosxaudiosink.h \ diff --git a/sys/osxaudio/gstosxaudiosink.c b/sys/osxaudio/gstosxaudiosink.c index aee25c9e9f..99284d060c 100644 --- a/sys/osxaudio/gstosxaudiosink.c +++ b/sys/osxaudio/gstosxaudiosink.c @@ -67,13 +67,14 @@ #endif #include +#include +#include #include #include + #include "gstosxaudiosink.h" #include "gstosxaudioelement.h" -#include - GST_DEBUG_CATEGORY_STATIC (osx_audiosink_debug); #define GST_CAT_DEFAULT osx_audiosink_debug @@ -104,28 +105,28 @@ static GstStaticPadTemplate sink_factory = GST_STATIC_PAD_TEMPLATE ("sink", "width = (int) 32, " "depth = (int) 32, " "rate = (int) [1, MAX], " - "channels = (int) [1, MAX];" + "channels = (int) [1, 9];" "audio/x-raw-int, " "endianness = (int) {" G_STRINGIFY (G_BYTE_ORDER) " }, " "signed = (boolean) { TRUE }, " "width = (int) 32, " "depth = (int) 32, " "rate = (int) [1, MAX], " - "channels = (int) [1, MAX];" + "channels = (int) [1, 9];" "audio/x-raw-int, " "endianness = (int) {" G_STRINGIFY (G_BYTE_ORDER) " }, " "signed = (boolean) { TRUE }, " "width = (int) 24, " "depth = (int) 24, " "rate = (int) [1, MAX], " - "channels = (int) [1, MAX];" + "channels = (int) [1, 9];" "audio/x-raw-int, " "endianness = (int) {" G_STRINGIFY (G_BYTE_ORDER) " }, " "signed = (boolean) { TRUE }, " "width = (int) 16, " "depth = (int) 16, " "rate = (int) [1, MAX], " - "channels = (int) [1, MAX];" + "channels = (int) [1, 9];" "audio/x-raw-int, " "endianness = (int) {" G_STRINGIFY (G_BYTE_ORDER) " }, " "signed = (boolean) { TRUE }, " @@ -296,19 +297,8 @@ static GstCaps * gst_osx_audio_sink_getcaps (GstBaseSink * base) { GstOsxAudioSink *sink = GST_OSX_AUDIO_SINK (base); - GstOsxRingBuffer *osxbuf; - GstElementClass *element_class; - GstPadTemplate *pad_template; - GstCaps *caps; gchar *caps_string = NULL; - osxbuf = GST_OSX_RING_BUFFER (GST_BASE_AUDIO_SINK (sink)->ringbuffer); - - if (!osxbuf) { - GST_DEBUG_OBJECT (sink, "device not open, using template caps"); - return NULL; /* base class will get template caps for us */ - } - if (sink->cached_caps) { caps_string = gst_caps_to_string (sink->cached_caps); GST_DEBUG_OBJECT (sink, "using cached caps: %s", caps_string); @@ -316,28 +306,8 @@ gst_osx_audio_sink_getcaps (GstBaseSink * base) return gst_caps_ref (sink->cached_caps); } - element_class = GST_ELEMENT_GET_CLASS (sink); - pad_template = gst_element_class_get_pad_template (element_class, "sink"); - g_return_val_if_fail (pad_template != NULL, NULL); - - caps = gst_caps_copy (gst_pad_template_get_caps (pad_template)); - - if (caps) { - if (!osxbuf->is_spdif_capable) { - GstCaps *sub_caps, *orig_caps = caps; - - sub_caps = gst_caps_from_string ("audio/x-ac3;audio/x-dts"); - caps = gst_caps_subtract (orig_caps, sub_caps); - gst_caps_unref (sub_caps); - gst_caps_unref (orig_caps); - } - sink->cached_caps = gst_caps_ref (caps); - caps_string = gst_caps_to_string (caps); - GST_DEBUG_OBJECT (sink, "cached caps: %s", caps_string); - g_free (caps_string); - } - - return caps; + GST_DEBUG_OBJECT (sink, "using template caps"); + return NULL; } static gboolean @@ -383,9 +353,6 @@ gst_osx_audio_sink_acceptcaps (GstPad * pad, GstCaps * caps) { gboolean framed = FALSE; - if (!osxbuf->is_spdif_capable) - goto done; - st = gst_caps_get_structure (caps, 0); gst_structure_get_boolean (st, "framed", &framed); @@ -397,9 +364,6 @@ gst_osx_audio_sink_acceptcaps (GstPad * pad, GstCaps * caps) { gboolean parsed = FALSE; - if (!osxbuf->is_spdif_capable) - goto done; - st = gst_caps_get_structure (caps, 0); gst_structure_get_boolean (st, "parsed", &parsed); @@ -566,6 +530,128 @@ _dump_channel_layout (AudioChannelLayout * channel_layout) } } +static gboolean +gst_osx_audio_sink_allowed_caps (GstOsxAudioSink * osxsink) +{ + gint i, max_channels = 0; + gboolean spdif_allowed, use_positions = FALSE; + AudioChannelLayout *layout; + GstElementClass *element_class; + GstPadTemplate *pad_template; + GstCaps *caps, *in_caps; + + GstAudioChannelPosition pos[9] = { + GST_AUDIO_CHANNEL_POSITION_INVALID, + GST_AUDIO_CHANNEL_POSITION_INVALID, + GST_AUDIO_CHANNEL_POSITION_INVALID, + GST_AUDIO_CHANNEL_POSITION_INVALID, + GST_AUDIO_CHANNEL_POSITION_INVALID, + GST_AUDIO_CHANNEL_POSITION_INVALID, + GST_AUDIO_CHANNEL_POSITION_INVALID, + GST_AUDIO_CHANNEL_POSITION_INVALID, + GST_AUDIO_CHANNEL_POSITION_INVALID + }; + + /* 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); + + 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 " + "kAudioDevicePropertyPreferredChannelLayout."); + max_channels = 2; + } + + if (max_channels > 2) { + max_channels = MIN (max_channels, 9); + use_positions = TRUE; + for (i = 0; i < max_channels; i++) { + switch (layout->mChannelDescriptions[i].mChannelLabel) { + case kAudioChannelLabel_Left: + pos[i] = GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT; + break; + case kAudioChannelLabel_Right: + pos[i] = GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT; + break; + case kAudioChannelLabel_Center: + pos[i] = GST_AUDIO_CHANNEL_POSITION_FRONT_CENTER; + break; + case kAudioChannelLabel_LFEScreen: + pos[i] = GST_AUDIO_CHANNEL_POSITION_LFE; + break; + case kAudioChannelLabel_LeftSurround: + pos[i] = GST_AUDIO_CHANNEL_POSITION_REAR_LEFT; + break; + case kAudioChannelLabel_RightSurround: + pos[i] = GST_AUDIO_CHANNEL_POSITION_REAR_RIGHT; + break; + case kAudioChannelLabel_RearSurroundLeft: + pos[i] = GST_AUDIO_CHANNEL_POSITION_SIDE_LEFT; + break; + case kAudioChannelLabel_RearSurroundRight: + pos[i] = GST_AUDIO_CHANNEL_POSITION_SIDE_RIGHT; + break; + case kAudioChannelLabel_CenterSurround: + pos[i] = GST_AUDIO_CHANNEL_POSITION_REAR_CENTER; + break; + default: + GST_WARNING_OBJECT (osxsink, "unrecognized channel: %d", + (int) layout->mChannelDescriptions[i].mChannelLabel); + use_positions = FALSE; + max_channels = 2; + break; + } + } + } + g_free (layout); + + /* Recover the template caps */ + element_class = GST_ELEMENT_GET_CLASS (osxsink); + pad_template = gst_element_class_get_pad_template (element_class, "sink"); + in_caps = gst_pad_template_get_caps (pad_template); + + /* Create the allowed subset */ + caps = gst_caps_new_empty (); + for (i = 0; i < gst_caps_get_size (in_caps); i++) { + GstStructure *in_s, *out_s; + + in_s = gst_caps_get_structure (in_caps, i); + + if (gst_structure_has_name (in_s, "audio/x-ac3") || + gst_structure_has_name (in_s, "audio/x-dts")) { + if (spdif_allowed) { + gst_caps_append_structure (caps, gst_structure_copy (in_s)); + } + } else { + if (max_channels > 2 && use_positions) { + out_s = gst_structure_copy (in_s); + gst_structure_remove_field (out_s, "channels"); + gst_structure_set (out_s, "channels", G_TYPE_INT, max_channels, NULL); + gst_audio_set_channel_positions (out_s, pos); + gst_caps_append_structure (caps, out_s); + } + out_s = gst_structure_copy (in_s); + gst_structure_remove_field (out_s, "channels"); + gst_structure_set (out_s, "channels", GST_TYPE_INT_RANGE, 1, 2, NULL); + gst_caps_append_structure (caps, out_s); + } + } + + if (osxsink->cached_caps) { + gst_caps_unref (osxsink->cached_caps); + } + + osxsink->cached_caps = caps; + + return TRUE; +} + static gboolean gst_osx_audio_sink_select_device (GstOsxAudioSink * osxsink) { @@ -626,9 +712,12 @@ gst_osx_audio_sink_select_device (GstOsxAudioSink * osxsink) 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); + done: g_free (devices); diff --git a/sys/osxaudio/gstosxaudiosink.h b/sys/osxaudio/gstosxaudiosink.h index cf94e474e0..e3aaec3f74 100644 --- a/sys/osxaudio/gstosxaudiosink.h +++ b/sys/osxaudio/gstosxaudiosink.h @@ -71,6 +71,7 @@ struct _GstOsxAudioSink GstBaseAudioSink sink; AudioDeviceID device_id; + AudioUnit audiounit; double volume; GstCaps *cached_caps; diff --git a/sys/osxaudio/gstosxcoreaudio.h b/sys/osxaudio/gstosxcoreaudio.h index f1d51239fe..8876115cf9 100644 --- a/sys/osxaudio/gstosxcoreaudio.h +++ b/sys/osxaudio/gstosxcoreaudio.h @@ -347,7 +347,7 @@ _audio_device_get_channel_layout (AudioDeviceID device_id) { OSStatus status = noErr; UInt32 propertySize = 0; - AudioChannelLayout *channel_layout = NULL; + AudioChannelLayout *layout = NULL; AudioObjectPropertyAddress channelLayoutAddress = { kAudioDevicePropertyPreferredChannelLayout, @@ -359,20 +359,50 @@ _audio_device_get_channel_layout (AudioDeviceID device_id) 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 */ - channel_layout = (AudioChannelLayout *) g_malloc (propertySize); + layout = (AudioChannelLayout *) g_malloc (propertySize); status = AudioObjectGetPropertyData (device_id, - &channelLayoutAddress, 0, NULL, &propertySize, channel_layout); + &channelLayoutAddress, 0, NULL, &propertySize, layout); if (status != noErr) { - g_free (channel_layout); - channel_layout = NULL; + 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 channel_layout; + return layout; + +failed: + g_free (layout); + return NULL; } static inline AudioStreamID * diff --git a/sys/osxaudio/gstosxringbuffer.c b/sys/osxaudio/gstosxringbuffer.c index afeb64498d..f43a055e5c 100644 --- a/sys/osxaudio/gstosxringbuffer.c +++ b/sys/osxaudio/gstosxringbuffer.c @@ -131,7 +131,6 @@ gst_osx_ring_buffer_init (GstOsxRingBuffer * ringbuffer, GstOsxRingBufferClass * g_class) { /* Nothing to do right now */ - ringbuffer->is_spdif_capable = FALSE; ringbuffer->is_passthrough = FALSE; ringbuffer->hog_pid = -1; ringbuffer->disabled_mixing = FALSE; @@ -257,13 +256,6 @@ gst_osx_ring_buffer_open_device (GstRingBuffer * buf) * thread to handle the notifications. */ _audio_system_set_runloop (NULL); - osxbuf->is_spdif_capable = _audio_device_is_spdif_avail (osxbuf->device_id); - - if (osxbuf->is_spdif_capable) { - GST_DEBUG_OBJECT (osxbuf, "device %u is SPDIF capable", - (unsigned) osxbuf->device_id); - } - osxbuf->audiounit = gst_osx_ring_buffer_create_audio_unit (osxbuf, osxbuf->is_src, osxbuf->device_id); diff --git a/sys/osxaudio/gstosxringbuffer.h b/sys/osxaudio/gstosxringbuffer.h index 6365511cf8..4394fa64ff 100644 --- a/sys/osxaudio/gstosxringbuffer.h +++ b/sys/osxaudio/gstosxringbuffer.h @@ -48,6 +48,8 @@ #include #include #include +#include + #include "gstosxaudioelement.h" G_BEGIN_DECLS @@ -74,7 +76,6 @@ struct _GstOsxRingBuffer { GstRingBuffer object; - gboolean is_spdif_capable; gboolean is_src; gboolean is_passthrough; gint stream_idx;