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".
diff --git a/sys/osxaudio/Makefile.am b/sys/osxaudio/Makefile.am
index 6c168a9..138e5ae 100644
--- a/sys/osxaudio/Makefile.am
+++ b/sys/osxaudio/Makefile.am
@@ -14,7 +14,7 @@
$(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 aee25c9..99284d0 100644
--- a/sys/osxaudio/gstosxaudiosink.c
+++ b/sys/osxaudio/gstosxaudiosink.c
@@ -67,13 +67,14 @@
#endif
#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"
-#include <gst/audio/gstaudioiec61937.h>
-
GST_DEBUG_CATEGORY_STATIC (osx_audiosink_debug);
#define GST_CAT_DEFAULT osx_audiosink_debug
@@ -104,28 +105,28 @@
"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 @@
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 @@
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 @@
{
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 @@
{
gboolean parsed = FALSE;
- if (!osxbuf->is_spdif_capable)
- goto done;
-
st = gst_caps_get_structure (caps, 0);
gst_structure_get_boolean (st, "parsed", &parsed);
@@ -567,6 +531,128 @@
}
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)
{
AudioDeviceID *devices = NULL;
@@ -626,9 +712,12 @@
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 cf94e47..e3aaec3 100644
--- a/sys/osxaudio/gstosxaudiosink.h
+++ b/sys/osxaudio/gstosxaudiosink.h
@@ -71,6 +71,7 @@
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 f1d5123..8876115 100644
--- a/sys/osxaudio/gstosxcoreaudio.h
+++ b/sys/osxaudio/gstosxcoreaudio.h
@@ -347,7 +347,7 @@
{
OSStatus status = noErr;
UInt32 propertySize = 0;
- AudioChannelLayout *channel_layout = NULL;
+ AudioChannelLayout *layout = NULL;
AudioObjectPropertyAddress channelLayoutAddress = {
kAudioDevicePropertyPreferredChannelLayout,
@@ -359,20 +359,50 @@
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 afeb644..f43a055 100644
--- a/sys/osxaudio/gstosxringbuffer.c
+++ b/sys/osxaudio/gstosxringbuffer.c
@@ -131,7 +131,6 @@
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 @@
* 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 6365511..4394fa6 100644
--- a/sys/osxaudio/gstosxringbuffer.h
+++ b/sys/osxaudio/gstosxringbuffer.h
@@ -48,6 +48,8 @@
#include <gst/gst.h>
#include <gst/audio/gstringbuffer.h>
#include <CoreAudio/CoreAudio.h>
+#include <AudioToolbox/AudioToolbox.h>
+
#include "gstosxaudioelement.h"
G_BEGIN_DECLS
@@ -74,7 +76,6 @@
{
GstRingBuffer object;
- gboolean is_spdif_capable;
gboolean is_src;
gboolean is_passthrough;
gint stream_idx;