osxaudio: Overhaul of probing caps

- Probing caps is unified between source and sink
- Hardware stream format is now reported as preferred capabilities
  (dynamically updated when hardware configuration changes)
- Get hardware channel layout from Remote IO just like from HAL
- More comprehensive mapping between AudioChannelLabel and
  GstAudioChannelPosition
- Support for unpositioned channel layouts
- Announce stereo-mono upmixing/downmixing in caps

https://bugzilla.gnome.org/show_bug.cgi?id=743758
This commit is contained in:
Ilya Konstantinov 2015-03-09 23:34:06 +02:00 committed by Arun Raghavan
parent 8a8884e150
commit 0e5d698c6f
11 changed files with 614 additions and 373 deletions

View file

@ -220,10 +220,9 @@ gst_osx_audio_ring_buffer_acquire (GstAudioRingBuffer * buf,
GST_DEBUG_OBJECT (osxbuf, "Format: " CORE_AUDIO_FORMAT, GST_DEBUG_OBJECT (osxbuf, "Format: " CORE_AUDIO_FORMAT,
CORE_AUDIO_FORMAT_ARGS (format)); CORE_AUDIO_FORMAT_ARGS (format));
if (GST_IS_OSX_AUDIO_SINK (GST_OBJECT_PARENT (buf))) { /* gst_audio_ring_buffer_set_channel_positions is not called
gst_audio_ring_buffer_set_channel_positions (buf, * since the AUs perform channel reordering themselves.
GST_OSX_AUDIO_SINK (GST_OBJECT_PARENT (buf))->channel_positions); * (see gst_core_audio_set_channel_layout) */
}
buf->size = spec->segtotal * spec->segsize; buf->size = spec->segtotal * spec->segsize;
buf->memory = g_malloc0 (buf->size); buf->memory = g_malloc0 (buf->size);

View file

@ -116,7 +116,6 @@ gst_osx_audio_sink_change_state (GstElement * element,
static gboolean gst_osx_audio_sink_query (GstBaseSink * base, GstQuery * query); static gboolean gst_osx_audio_sink_query (GstBaseSink * base, GstQuery * query);
static gboolean gst_osx_audio_sink_stop (GstBaseSink * base);
static GstCaps *gst_osx_audio_sink_getcaps (GstBaseSink * base, static GstCaps *gst_osx_audio_sink_getcaps (GstBaseSink * base,
GstCaps * filter); GstCaps * filter);
static gboolean gst_osx_audio_sink_acceptcaps (GstOsxAudioSink * sink, static gboolean gst_osx_audio_sink_acceptcaps (GstOsxAudioSink * sink,
@ -128,7 +127,6 @@ static GstAudioRingBuffer
* gst_osx_audio_sink_create_ringbuffer (GstAudioBaseSink * sink); * gst_osx_audio_sink_create_ringbuffer (GstAudioBaseSink * sink);
static void gst_osx_audio_sink_osxelement_init (gpointer g_iface, static void gst_osx_audio_sink_osxelement_init (gpointer g_iface,
gpointer iface_data); gpointer iface_data);
static void gst_osx_audio_sink_probe_caps (GstOsxAudioSink * sink);
static void gst_osx_audio_sink_set_volume (GstOsxAudioSink * sink); static void gst_osx_audio_sink_set_volume (GstOsxAudioSink * sink);
static OSStatus gst_osx_audio_sink_io_proc (GstOsxAudioRingBuffer * buf, static OSStatus gst_osx_audio_sink_io_proc (GstOsxAudioRingBuffer * buf,
@ -191,7 +189,6 @@ gst_osx_audio_sink_class_init (GstOsxAudioSinkClass * klass)
0, 1.0, 1.0, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); 0, 1.0, 1.0, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
gstbasesink_class->get_caps = GST_DEBUG_FUNCPTR (gst_osx_audio_sink_getcaps); gstbasesink_class->get_caps = GST_DEBUG_FUNCPTR (gst_osx_audio_sink_getcaps);
gstbasesink_class->stop = GST_DEBUG_FUNCPTR (gst_osx_audio_sink_stop);
gstaudiobasesink_class->create_ringbuffer = gstaudiobasesink_class->create_ringbuffer =
GST_DEBUG_FUNCPTR (gst_osx_audio_sink_create_ringbuffer); GST_DEBUG_FUNCPTR (gst_osx_audio_sink_create_ringbuffer);
@ -210,19 +207,10 @@ gst_osx_audio_sink_class_init (GstOsxAudioSinkClass * klass)
static void static void
gst_osx_audio_sink_init (GstOsxAudioSink * sink) gst_osx_audio_sink_init (GstOsxAudioSink * sink)
{ {
gint i;
GST_DEBUG ("Initialising object"); GST_DEBUG ("Initialising object");
sink->device_id = kAudioDeviceUnknown; sink->device_id = kAudioDeviceUnknown;
sink->cached_caps = NULL;
sink->volume = DEFAULT_VOLUME; sink->volume = DEFAULT_VOLUME;
sink->channels = 0;
for (i = 0; i < GST_OSX_AUDIO_MAX_CHANNEL; i++) {
sink->channel_positions[i] = GST_AUDIO_CHANNEL_POSITION_INVALID;
}
} }
static void static void
@ -330,28 +318,14 @@ gst_osx_audio_sink_query (GstBaseSink * base, GstQuery * query)
return ret; return ret;
} }
static gboolean
gst_osx_audio_sink_stop (GstBaseSink * base)
{
GstOsxAudioSink *sink = GST_OSX_AUDIO_SINK (base);
if (sink->cached_caps) {
gst_caps_unref (sink->cached_caps);
sink->cached_caps = NULL;
}
return GST_CALL_PARENT_WITH_DEFAULT (GST_BASE_SINK_CLASS, stop, (base), TRUE);
}
static GstCaps * static GstCaps *
gst_osx_audio_sink_getcaps (GstBaseSink * sink, GstCaps * filter) gst_osx_audio_sink_getcaps (GstBaseSink * sink, GstCaps * filter)
{ {
GstElementClass *gstelement_class;
GstOsxAudioSink *osxsink; GstOsxAudioSink *osxsink;
GstAudioRingBuffer *buf; GstAudioRingBuffer *buf;
GstCaps *ret = NULL; GstOsxAudioRingBuffer *osxbuf;
GstCaps *caps, *filtered_caps;
gstelement_class = GST_ELEMENT_GET_CLASS (sink);
osxsink = GST_OSX_AUDIO_SINK (sink); osxsink = GST_OSX_AUDIO_SINK (sink);
GST_OBJECT_LOCK (osxsink); GST_OBJECT_LOCK (osxsink);
@ -360,33 +334,51 @@ gst_osx_audio_sink_getcaps (GstBaseSink * sink, GstCaps * filter)
gst_object_ref (buf); gst_object_ref (buf);
GST_OBJECT_UNLOCK (osxsink); GST_OBJECT_UNLOCK (osxsink);
if (buf) { if (!buf) {
GST_OBJECT_LOCK (buf); GST_DEBUG_OBJECT (sink, "no ring buffer, returning NULL caps");
return GST_BASE_SINK_CLASS (parent_class)->get_caps (sink, filter);
if (buf->open && !osxsink->cached_caps) {
/* Device is open, let's probe its caps */
gst_osx_audio_sink_probe_caps (osxsink);
} }
if (osxsink->cached_caps) osxbuf = GST_OSX_AUDIO_RING_BUFFER (buf);
ret = gst_caps_ref (osxsink->cached_caps);
/* protect against cached_caps going away */
GST_OBJECT_LOCK (buf);
if (osxbuf->core_audio->cached_caps) {
GST_LOG_OBJECT (sink, "Returning cached caps");
caps = gst_caps_ref (osxbuf->core_audio->cached_caps);
} else if (buf->open) {
GstCaps *template_caps;
/* Get template caps */
template_caps =
gst_pad_get_pad_template_caps (GST_AUDIO_BASE_SINK_PAD (osxsink));
/* Device is open, let's probe its caps */
caps = gst_core_audio_probe_caps (osxbuf->core_audio, template_caps);
gst_caps_replace (&osxbuf->core_audio->cached_caps, caps);
gst_caps_unref (template_caps);
} else {
GST_DEBUG_OBJECT (sink, "ring buffer not open, returning NULL caps");
caps = NULL;
}
GST_OBJECT_UNLOCK (buf); GST_OBJECT_UNLOCK (buf);
gst_object_unref (buf); gst_object_unref (buf);
}
if (!ret) if (!caps)
ret = gst_pad_get_pad_template_caps (GST_AUDIO_BASE_SINK_PAD (osxsink)); return NULL;
if (filter) { if (!filter)
GstCaps *tmp; return caps;
tmp = gst_caps_intersect_full (filter, ret, GST_CAPS_INTERSECT_FIRST);
gst_caps_unref (ret);
ret = tmp;
}
return ret; /* Take care of filtered caps */
filtered_caps =
gst_caps_intersect_full (filter, caps, GST_CAPS_INTERSECT_FIRST);
gst_caps_unref (caps);
return filtered_caps;
} }
static gboolean static gboolean
@ -586,85 +578,3 @@ gst_osx_audio_sink_set_volume (GstOsxAudioSink * sink)
gst_core_audio_set_volume (osxbuf->core_audio, sink->volume); gst_core_audio_set_volume (osxbuf->core_audio, sink->volume);
} }
static void
gst_osx_audio_sink_probe_caps (GstOsxAudioSink * osxsink)
{
gint i, channels;
gboolean spdif_allowed;
AudioChannelLayout *layout;
GstElementClass *element_class;
GstPadTemplate *pad_template;
GstCaps *caps, *in_caps;
guint64 channel_mask = 0;
GstAudioChannelPosition *pos = osxsink->channel_positions;
/* First collect info about the HW capabilites and preferences */
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,
TRUE);
GST_DEBUG_OBJECT (osxsink, "Selected device ID: %u SPDIF allowed: %d",
(unsigned) osxsink->device_id, spdif_allowed);
if (layout) {
channels = MIN (layout->mNumberChannelDescriptions,
GST_OSX_AUDIO_MAX_CHANNEL);
} else {
GST_WARNING_OBJECT (osxsink, "This driver does not support "
"kAudioDevicePropertyPreferredChannelLayout.");
channels = 2;
}
if (!gst_core_audio_parse_channel_layout (layout, channels, &channel_mask,
pos)) {
GST_WARNING_OBJECT (osxsink, "Failed to parse channel layout");
}
g_free (layout);
if (!gst_audio_channel_positions_to_mask (pos, channels, TRUE, &channel_mask)) {
GST_WARNING_OBJECT (osxsink, "Probably unsupported channel order");
}
/* 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 {
out_s = gst_structure_copy (in_s);
gst_structure_remove_fields (out_s, "channels", NULL);
gst_structure_set (out_s, "channels", G_TYPE_INT, channels, NULL);
if (channel_mask) {
gst_structure_remove_fields (out_s, "channel-mask", NULL);
gst_structure_set (out_s, "channel-mask", GST_TYPE_BITMASK,
channel_mask, NULL);
}
gst_caps_append_structure (caps, out_s);
}
}
if (osxsink->cached_caps) {
gst_caps_unref (osxsink->cached_caps);
}
osxsink->cached_caps = caps;
osxsink->channels = channels;
}

View file

@ -77,10 +77,8 @@ struct _GstOsxAudioSink
AudioUnit audiounit; AudioUnit audiounit;
double volume; double volume;
GstCaps *cached_caps;
guint channels; guint channels;
GstAudioChannelPosition channel_positions[GST_OSX_AUDIO_MAX_CHANNEL];
}; };
struct _GstOsxAudioSinkClass struct _GstOsxAudioSinkClass

View file

@ -97,7 +97,6 @@ static GstStateChangeReturn
gst_osx_audio_src_change_state (GstElement * element, gst_osx_audio_src_change_state (GstElement * element,
GstStateChange transition); GstStateChange transition);
static void gst_osx_audio_src_probe_caps (GstOsxAudioSrc * src);
static GstCaps *gst_osx_audio_src_get_caps (GstBaseSrc * src, GstCaps * filter); static GstCaps *gst_osx_audio_src_get_caps (GstBaseSrc * src, GstCaps * filter);
static GstAudioRingBuffer *gst_osx_audio_src_create_ringbuffer (GstAudioBaseSrc static GstAudioRingBuffer *gst_osx_audio_src_create_ringbuffer (GstAudioBaseSrc
@ -171,7 +170,6 @@ gst_osx_audio_src_init (GstOsxAudioSrc * src)
gst_base_src_set_live (GST_BASE_SRC (src), TRUE); gst_base_src_set_live (GST_BASE_SRC (src), TRUE);
src->device_id = kAudioDeviceUnknown; src->device_id = kAudioDeviceUnknown;
src->cached_caps = NULL;
} }
static void static void
@ -241,72 +239,14 @@ out:
return ret; return ret;
} }
static void
gst_osx_audio_src_probe_caps (GstOsxAudioSrc * osxsrc)
{
GstOsxAudioRingBuffer *ringbuffer =
GST_OSX_AUDIO_RING_BUFFER (GST_AUDIO_BASE_SRC (osxsrc)->ringbuffer);
GstCoreAudio *core_audio = ringbuffer->core_audio;
GstCaps *caps;
gint channels;
AudioChannelLayout *layout;
AudioStreamBasicDescription asbd_in;
UInt32 propertySize;
OSStatus status;
propertySize = sizeof (asbd_in);
status = AudioUnitGetProperty (core_audio->audiounit,
kAudioUnitProperty_StreamFormat,
kAudioUnitScope_Input, 1, &asbd_in, &propertySize);
if (status)
goto fail;
layout = gst_core_audio_audio_device_get_channel_layout (osxsrc->device_id,
FALSE);
if (layout) {
channels = MIN (layout->mNumberChannelDescriptions,
GST_OSX_AUDIO_MAX_CHANNEL);
} else {
GST_WARNING_OBJECT (osxsrc, "This driver does not support "
"kAudioDevicePropertyPreferredChannelLayout.");
channels = 2;
}
caps = gst_core_audio_asbd_to_caps (&asbd_in, layout);
if (!caps) {
GST_WARNING_OBJECT (osxsrc, "Could not get caps from stream description");
g_free (layout);
goto fail;
} else {
GST_DEBUG_OBJECT (osxsrc, "Got caps on device: %p", caps);
}
g_free (layout);
if (osxsrc->cached_caps)
gst_caps_unref (osxsrc->cached_caps);
osxsrc->cached_caps = caps;
return;
fail:
AudioComponentInstanceDispose (core_audio->audiounit);
core_audio->audiounit = NULL;
GST_WARNING_OBJECT (osxsrc,
"Unable to obtain device properties: %d", (int) status);
}
static GstCaps * static GstCaps *
gst_osx_audio_src_get_caps (GstBaseSrc * src, GstCaps * filter) gst_osx_audio_src_get_caps (GstBaseSrc * src, GstCaps * filter)
{ {
GstElementClass *gstelement_class;
GstOsxAudioSrc *osxsrc; GstOsxAudioSrc *osxsrc;
GstAudioRingBuffer *buf; GstAudioRingBuffer *buf;
GstCaps *ret = NULL; GstOsxAudioRingBuffer *osxbuf;
GstCaps *caps, *filtered_caps;
gstelement_class = GST_ELEMENT_GET_CLASS (src);
osxsrc = GST_OSX_AUDIO_SRC (src); osxsrc = GST_OSX_AUDIO_SRC (src);
GST_OBJECT_LOCK (osxsrc); GST_OBJECT_LOCK (osxsrc);
@ -315,31 +255,51 @@ gst_osx_audio_src_get_caps (GstBaseSrc * src, GstCaps * filter)
gst_object_ref (buf); gst_object_ref (buf);
GST_OBJECT_UNLOCK (osxsrc); GST_OBJECT_UNLOCK (osxsrc);
if (buf) { if (!buf) {
GST_DEBUG_OBJECT (src, "no ring buffer, using template caps");
return GST_BASE_SRC_CLASS (parent_class)->get_caps (src, filter);
}
osxbuf = GST_OSX_AUDIO_RING_BUFFER (buf);
/* protect against cached_caps going away */
GST_OBJECT_LOCK (buf); GST_OBJECT_LOCK (buf);
if (buf->open && !osxsrc->cached_caps) { if (osxbuf->core_audio->cached_caps) {
GST_LOG_OBJECT (src, "Returning cached caps");
caps = gst_caps_ref (osxbuf->core_audio->cached_caps);
} else if (buf->open) {
GstCaps *template_caps;
/* Get template caps */
template_caps =
gst_pad_get_pad_template_caps (GST_AUDIO_BASE_SRC_PAD (osxsrc));
/* Device is open, let's probe its caps */ /* Device is open, let's probe its caps */
gst_osx_audio_src_probe_caps (osxsrc); caps = gst_core_audio_probe_caps (osxbuf->core_audio, template_caps);
gst_caps_replace (&osxbuf->core_audio->cached_caps, caps);
gst_caps_unref (template_caps);
} else {
GST_DEBUG_OBJECT (src, "ring buffer not open, using template caps");
caps = GST_BASE_SRC_CLASS (parent_class)->get_caps (src, NULL);
} }
GST_OBJECT_UNLOCK (buf); GST_OBJECT_UNLOCK (buf);
gst_object_unref (buf); gst_object_unref (buf);
}
if (osxsrc->cached_caps) if (!caps)
ret = gst_caps_ref (osxsrc->cached_caps); return NULL;
else
ret = gst_pad_get_pad_template_caps (GST_AUDIO_BASE_SRC_PAD (osxsrc));
if (filter) { if (!filter)
GstCaps *tmp; return caps;
tmp = gst_caps_intersect_full (filter, ret, GST_CAPS_INTERSECT_FIRST);
gst_caps_unref (ret);
ret = tmp;
}
return ret; /* Take care of filtered caps */
filtered_caps =
gst_caps_intersect_full (filter, caps, GST_CAPS_INTERSECT_FIRST);
gst_caps_unref (caps);
return filtered_caps;
} }
static GstAudioRingBuffer * static GstAudioRingBuffer *

View file

@ -68,8 +68,6 @@ struct _GstOsxAudioSrc
GstAudioBaseSrc src; GstAudioBaseSrc src;
AudioDeviceID device_id; AudioDeviceID device_id;
GstCaps *cached_caps;
}; };
struct _GstOsxAudioSrcClass struct _GstOsxAudioSrcClass

View file

@ -48,6 +48,7 @@ gst_core_audio_init (GstCoreAudio * core_audio)
core_audio->device_id = kAudioDeviceUnknown; core_audio->device_id = kAudioDeviceUnknown;
core_audio->is_src = FALSE; core_audio->is_src = FALSE;
core_audio->audiounit = NULL; core_audio->audiounit = NULL;
core_audio->cached_caps = NULL;
#ifndef HAVE_IOS #ifndef HAVE_IOS
core_audio->hog_pid = -1; core_audio->hog_pid = -1;
core_audio->disabled_mixing = FALSE; core_audio->disabled_mixing = FALSE;
@ -65,6 +66,7 @@ gst_core_audio_new (GstObject * osxbuf)
core_audio = g_object_new (GST_TYPE_CORE_AUDIO, NULL); core_audio = g_object_new (GST_TYPE_CORE_AUDIO, NULL);
core_audio->osxbuf = osxbuf; core_audio->osxbuf = osxbuf;
core_audio->cached_caps = NULL;
return core_audio; return core_audio;
} }
@ -81,6 +83,9 @@ gst_core_audio_close (GstCoreAudio * core_audio)
return FALSE; return FALSE;
} }
/* core_audio->osxbuf is already locked at this point */
gst_caps_replace (&core_audio->cached_caps, NULL);
AudioComponentInstanceDispose (core_audio->audiounit); AudioComponentInstanceDispose (core_audio->audiounit);
core_audio->audiounit = NULL; core_audio->audiounit = NULL;
return TRUE; return TRUE;
@ -191,79 +196,143 @@ gst_core_audio_audio_device_is_spdif_avail (AudioDeviceID device_id)
return gst_core_audio_audio_device_is_spdif_avail_impl (device_id); return gst_core_audio_audio_device_is_spdif_avail_impl (device_id);
} }
gboolean /* Does the channel have at least one positioned channel?
gst_core_audio_parse_channel_layout (AudioChannelLayout * layout, * (GStreamer is more strict than Core Audio, in that it requires either
gint channels, guint64 * channel_mask, GstAudioChannelPosition * pos) * all channels to be positioned, or all unpositioned.) */
static gboolean
_is_core_audio_layout_positioned (AudioChannelLayout * layout)
{ {
gint i; guint i;
gboolean ret = TRUE;
g_return_val_if_fail (channels <= GST_OSX_AUDIO_MAX_CHANNEL, FALSE); g_assert (layout->mChannelLayoutTag ==
kAudioChannelLayoutTag_UseChannelDescriptions);
switch (channels) { for (i = 0; i < layout->mNumberChannelDescriptions; ++i) {
case 0: GstAudioChannelPosition p =
pos[0] = GST_AUDIO_CHANNEL_POSITION_NONE; gst_core_audio_channel_label_to_gst
break; (layout->mChannelDescriptions[i].mChannelLabel, i, FALSE);
case 1:
pos[0] = GST_AUDIO_CHANNEL_POSITION_MONO; if (p >= 0) /* not special positition */
break; return TRUE;
case 2:
pos[0] = GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT;
pos[1] = GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT;
*channel_mask |= GST_AUDIO_CHANNEL_POSITION_MASK (FRONT_LEFT);
*channel_mask |= GST_AUDIO_CHANNEL_POSITION_MASK (FRONT_RIGHT);
break;
default:
for (i = 0; i < 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_LFE1;
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 ("unrecognized channel: %d",
(int) layout->mChannelDescriptions[i].mChannelLabel);
*channel_mask = 0;
ret = FALSE;
break;
}
}
} }
return ret; return FALSE;
} }
static void
_core_audio_parse_channel_descriptions (AudioChannelLayout * layout,
guint * channels, guint64 * channel_mask, GstAudioChannelPosition * pos)
{
gboolean positioned;
guint i;
g_assert (layout->mChannelLayoutTag ==
kAudioChannelLayoutTag_UseChannelDescriptions);
positioned = _is_core_audio_layout_positioned (layout);
*channel_mask = 0;
/* Go over all labels, either taking only positioned or only
* unpositioned channels, up to GST_OSX_AUDIO_MAX_CHANNEL channels.
*
* The resulting 'pos' array will contain either:
* - only regular (>= 0) positions
* - only GST_AUDIO_CHANNEL_POSITION_NONE positions
* in a compact form, skipping over all unsupported positions.
*/
*channels = 0;
for (i = 0; i < layout->mNumberChannelDescriptions; ++i) {
GstAudioChannelPosition p =
gst_core_audio_channel_label_to_gst
(layout->mChannelDescriptions[i].mChannelLabel, i, TRUE);
/* In positioned layouts, skip all unpositioned channels.
* In unpositioned layouts, skip all invalid channels. */
if ((positioned && p >= 0) ||
(!positioned && p == GST_AUDIO_CHANNEL_POSITION_NONE)) {
if (pos)
pos[*channels] = p;
*channel_mask |= G_GUINT64_CONSTANT (1) << p;
++(*channels);
if (*channels == GST_OSX_AUDIO_MAX_CHANNEL)
break; /* not to overflow */
}
}
}
gboolean
gst_core_audio_parse_channel_layout (AudioChannelLayout * layout,
guint * channels, guint64 * channel_mask, GstAudioChannelPosition * pos)
{
g_assert (channels != NULL);
g_assert (channel_mask != NULL);
g_assert (layout != NULL);
if (layout->mChannelLayoutTag !=
kAudioChannelLayoutTag_UseChannelDescriptions) {
GST_ERROR
("Only kAudioChannelLayoutTag_UseChannelDescriptions is supported.");
*channels = 0;
*channel_mask = 0;
return FALSE;
}
switch (layout->mNumberChannelDescriptions) {
case 0:
if (pos)
pos[0] = GST_AUDIO_CHANNEL_POSITION_NONE;
*channels = 0;
*channel_mask = 0;
return TRUE;
case 1:
if (pos)
pos[0] = GST_AUDIO_CHANNEL_POSITION_MONO;
*channels = 1;
*channel_mask = 0;
return TRUE;
case 2:
if (pos) {
pos[0] = GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT;
pos[1] = GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT;
}
*channels = 2;
*channel_mask =
GST_AUDIO_CHANNEL_POSITION_MASK (FRONT_LEFT) |
GST_AUDIO_CHANNEL_POSITION_MASK (FRONT_RIGHT);
return TRUE;
default:
_core_audio_parse_channel_descriptions (layout, channels, channel_mask,
pos);
return TRUE;
}
}
/* Converts an AudioStreamBasicDescription to preferred caps.
*
* These caps will indicate the AU element's canonical format, which won't
* make Core Audio resample nor convert.
*
* NOTE ON MULTI-CHANNEL AUDIO:
*
* If layout is not NULL, resulting caps will only include the subset
* of channels supported by GStreamer. If the Core Audio layout contained
* ANY positioned channels, then ONLY positioned channels will be included
* in the resulting caps. Otherwise, resulting caps will be unpositioned,
* and include only unpositioned channels.
* (Channels with unsupported AudioChannelLabel will be skipped either way.)
*
* Naturally, the number of channels indicated by 'channels' can be lower
* than the AU element's total number of channels.
*/
GstCaps * GstCaps *
gst_core_audio_asbd_to_caps (AudioStreamBasicDescription * asbd, gst_core_audio_asbd_to_caps (AudioStreamBasicDescription * asbd,
AudioChannelLayout * layout) AudioChannelLayout * layout)
{ {
GstAudioInfo info; GstAudioInfo info;
GstAudioFormat format = GST_AUDIO_FORMAT_UNKNOWN; GstAudioFormat format = GST_AUDIO_FORMAT_UNKNOWN;
GstAudioChannelPosition pos[64] = { GST_AUDIO_CHANNEL_POSITION_INVALID, }; guint rate, channels, bps, endianness;
gint rate, channels, bps, endianness;
guint64 channel_mask; guint64 channel_mask;
gboolean sign, interleaved; gboolean sign, interleaved;
@ -283,13 +352,9 @@ gst_core_audio_asbd_to_caps (AudioStreamBasicDescription * asbd,
} }
rate = asbd->mSampleRate; rate = asbd->mSampleRate;
if (rate == kAudioStreamAnyRate) if (rate == kAudioStreamAnyRate) {
rate = GST_AUDIO_DEF_RATE; GST_WARNING ("No sample rate");
goto error;
channels = asbd->mChannelsPerFrame;
if (channels == 0) {
/* The documentation says this should not happen! */
channels = 1;
} }
bps = asbd->mBitsPerChannel; bps = asbd->mBitsPerChannel;
@ -321,16 +386,206 @@ gst_core_audio_asbd_to_caps (AudioStreamBasicDescription * asbd,
goto error; goto error;
} }
if (!gst_core_audio_parse_channel_layout (layout, channels, &channel_mask, if (layout) {
GstAudioChannelPosition pos[GST_OSX_AUDIO_MAX_CHANNEL];
if (!gst_core_audio_parse_channel_layout (layout, &channels, &channel_mask,
pos)) { pos)) {
GST_WARNING ("Failed to parse channel layout"); GST_WARNING ("Failed to parse channel layout");
goto error; goto error;
} }
/* The AU can have arbitrary channel order, but we're using GstAudioInfo
* which supports only the GStreamer channel order.
* Also, we're eventually producing caps, which only have channel-mask
* (whose implied order is the GStreamer channel order). */
gst_audio_channel_positions_to_valid_order (pos, channels);
gst_audio_info_set_format (&info, format, rate, channels, pos); gst_audio_info_set_format (&info, format, rate, channels, pos);
} else {
channels = MIN (asbd->mChannelsPerFrame, GST_OSX_AUDIO_MAX_CHANNEL);
gst_audio_info_set_format (&info, format, rate, channels, NULL);
}
return gst_audio_info_to_caps (&info); return gst_audio_info_to_caps (&info);
error: error:
return NULL; return NULL;
} }
static gboolean
_core_audio_get_property (GstCoreAudio * core_audio, gboolean outer,
AudioUnitPropertyID inID, void *inData, UInt32 * inDataSize)
{
OSStatus status;
AudioUnitScope scope;
AudioUnitElement element;
scope = outer ?
CORE_AUDIO_OUTER_SCOPE (core_audio) : CORE_AUDIO_INNER_SCOPE (core_audio);
element = CORE_AUDIO_ELEMENT (core_audio);
status =
AudioUnitGetProperty (core_audio->audiounit, inID, scope, element, inData,
inDataSize);
return status == noErr;
}
static gboolean
_core_audio_get_stream_format (GstCoreAudio * core_audio,
AudioStreamBasicDescription * asbd, gboolean outer)
{
UInt32 size;
size = sizeof (AudioStreamBasicDescription);
return _core_audio_get_property (core_audio, outer,
kAudioUnitProperty_StreamFormat, asbd, &size);
}
AudioChannelLayout *
gst_core_audio_get_channel_layout (GstCoreAudio * core_audio, gboolean outer)
{
UInt32 size;
AudioChannelLayout *layout;
if (core_audio->is_src) {
GST_WARNING_OBJECT (core_audio,
"gst_core_audio_get_channel_layout not supported on source.");
return NULL;
}
if (!_core_audio_get_property (core_audio, outer,
kAudioUnitProperty_AudioChannelLayout, NULL, &size)) {
GST_WARNING_OBJECT (core_audio, "unable to get channel layout");
return NULL;
}
layout = g_malloc (size);
if (!_core_audio_get_property (core_audio, outer,
kAudioUnitProperty_AudioChannelLayout, layout, &size)) {
GST_WARNING_OBJECT (core_audio, "unable to get channel layout");
g_free (layout);
return NULL;
}
return layout;
}
GstCaps *
gst_core_audio_probe_caps (GstCoreAudio * core_audio, GstCaps * in_caps)
{
guint i, channels;
gboolean spdif_allowed;
AudioChannelLayout *layout;
AudioStreamBasicDescription outer_asbd;
gboolean got_outer_asbd;
GstCaps *caps = NULL;
guint64 channel_mask;
/* Get the ASBD of the outer scope (i.e. input scope of Input,
* output scope of Output).
* This ASBD indicates the hardware format. */
got_outer_asbd =
_core_audio_get_stream_format (core_audio, &outer_asbd, TRUE);
/* Collect info about the HW capabilites and preferences */
spdif_allowed =
gst_core_audio_audio_device_is_spdif_avail (core_audio->device_id);
layout = gst_core_audio_get_channel_layout (core_audio, TRUE);
GST_DEBUG_OBJECT (core_audio, "Selected device ID: %u SPDIF allowed: %d",
(unsigned) core_audio->device_id, spdif_allowed);
if (layout) {
if (!gst_core_audio_parse_channel_layout (layout, &channels, &channel_mask,
NULL)) {
GST_WARNING_OBJECT (core_audio, "Failed to parse channel layout");
channel_mask = 0;
}
/* If available, start with the preferred caps. */
if (got_outer_asbd)
caps = gst_core_audio_asbd_to_caps (&outer_asbd, layout);
g_free (layout);
} else if (got_outer_asbd) {
channels = outer_asbd.mChannelsPerFrame;
channel_mask = 0;
/* If available, start with the preferred caps */
caps = gst_core_audio_asbd_to_caps (&outer_asbd, NULL);
} else {
GST_ERROR_OBJECT (core_audio,
"Unable to get any information about hardware");
return NULL;
}
/* Append the allowed subset based on the template caps */
if (!caps)
caps = gst_caps_new_empty ();
for (i = 0; i < gst_caps_get_size (in_caps); i++) {
GstStructure *in_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 {
GstStructure *out_s;
out_s = gst_structure_copy (in_s);
gst_structure_set (out_s, "channels", G_TYPE_INT, channels, NULL);
if (channel_mask != 0) {
/* positioned layout */
gst_structure_set (out_s,
"channel-mask", GST_TYPE_BITMASK, channel_mask, NULL);
} else {
/* unpositioned layout */
gst_structure_remove_field (out_s, "channel-mask");
}
gst_caps_append_structure (caps, out_s);
/* Special cases for upmixing and downmixing.
* Other than that, the AUs don't upmix or downmix multi-channel audio,
* e.g. if you push 5.1-surround audio to a stereo configuration,
* the left and right channels will be played accordingly,
* and the rest will be dropped. */
if (channels >= 2 &&
(channel_mask & GST_AUDIO_CHANNEL_POSITION_MASK (FRONT_LEFT)) &&
(channel_mask & GST_AUDIO_CHANNEL_POSITION_MASK (FRONT_RIGHT))) {
/* If have stereo channels, then also offer mono since CoreAudio
* upmixes it */
out_s = gst_structure_copy (out_s);
gst_structure_set (out_s, "channels", G_TYPE_INT, 1, NULL);
/* Mono has no channel-mask */
gst_structure_remove_field (out_s, "channel-mask");
gst_caps_append_structure (caps, out_s);
} else if (channels == 1 && channel_mask == 0) {
/* If mono, then also offer stereo since CoreAudio downmixes to it */
out_s = gst_structure_copy (out_s);
gst_structure_set (out_s,
"channels", G_TYPE_INT, 1,
"channel-mask", GST_TYPE_BITMASK,
GST_AUDIO_CHANNEL_POSITION_MASK (FRONT_LEFT) |
GST_AUDIO_CHANNEL_POSITION_MASK (FRONT_RIGHT), NULL);
gst_caps_append_structure (caps, out_s);
}
}
}
return caps;
}

View file

@ -63,6 +63,7 @@ G_BEGIN_DECLS
#define GST_IS_CORE_AUDIO_CLASS(klass) \ #define GST_IS_CORE_AUDIO_CLASS(klass) \
(G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_CORE_AUDIO)) (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_CORE_AUDIO))
/* TODO: Consider raising to 64 */
#define GST_OSX_AUDIO_MAX_CHANNEL (9) #define GST_OSX_AUDIO_MAX_CHANNEL (9)
#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_IS_SPDIF(f) ((f).mFormat.mFormatID == 'IAC3' || (f).mFormat.mFormatID == 'iac3' || (f).mFormat.mFormatID == kAudioFormat60958AC3 || (f).mFormat.mFormatID == kAudioFormatAC3)
@ -70,6 +71,10 @@ G_BEGIN_DECLS
#define CORE_AUDIO_FORMAT "FormatID: %" GST_FOURCC_FORMAT " rate: %f flags: 0x%x BytesPerPacket: %u FramesPerPacket: %u BytesPerFrame: %u ChannelsPerFrame: %u BitsPerChannel: %u" #define CORE_AUDIO_FORMAT "FormatID: %" GST_FOURCC_FORMAT " rate: %f flags: 0x%x BytesPerPacket: %u FramesPerPacket: %u BytesPerFrame: %u ChannelsPerFrame: %u BitsPerChannel: %u"
#define CORE_AUDIO_FORMAT_ARGS(f) GST_FOURCC_ARGS((unsigned int)(f).mFormatID),(f).mSampleRate,(unsigned int)(f).mFormatFlags,(unsigned int)(f).mBytesPerPacket,(unsigned int)(f).mFramesPerPacket,(unsigned int)(f).mBytesPerFrame,(unsigned int)(f).mChannelsPerFrame,(unsigned int)(f).mBitsPerChannel #define CORE_AUDIO_FORMAT_ARGS(f) GST_FOURCC_ARGS((unsigned int)(f).mFormatID),(f).mSampleRate,(unsigned int)(f).mFormatFlags,(unsigned int)(f).mBytesPerPacket,(unsigned int)(f).mFramesPerPacket,(unsigned int)(f).mBytesPerFrame,(unsigned int)(f).mChannelsPerFrame,(unsigned int)(f).mBitsPerChannel
#define CORE_AUDIO_INNER_SCOPE(core_audio) ((core_audio)->is_src ? kAudioUnitScope_Output : kAudioUnitScope_Input)
#define CORE_AUDIO_OUTER_SCOPE(core_audio) ((core_audio)->is_src ? kAudioUnitScope_Input : kAudioUnitScope_Output)
#define CORE_AUDIO_ELEMENT(core_audio) ((core_audio)->is_src ? 1 : 0)
typedef struct _GstCoreAudio GstCoreAudio; typedef struct _GstCoreAudio GstCoreAudio;
typedef struct _GstCoreAudioClass GstCoreAudioClass; typedef struct _GstCoreAudioClass GstCoreAudioClass;
@ -83,6 +88,7 @@ struct _GstCoreAudio
gboolean is_src; gboolean is_src;
gboolean is_passthrough; gboolean is_passthrough;
AudioDeviceID device_id; AudioDeviceID device_id;
GstCaps *cached_caps;
gint stream_idx; gint stream_idx;
gboolean io_proc_active; gboolean io_proc_active;
gboolean io_proc_needs_deactivation; gboolean io_proc_needs_deactivation;
@ -144,10 +150,14 @@ gboolean gst_core_audio_audio_device_is_spdif_avail (AudioDeviceID devi
gboolean gst_core_audio_select_device (GstCoreAudio * core_audio); gboolean gst_core_audio_select_device (GstCoreAudio * core_audio);
AudioChannelLayout * gst_core_audio_audio_device_get_channel_layout (AudioDeviceID device_id, gboolean output); GstCaps *
gst_core_audio_probe_caps (GstCoreAudio * core_audio, GstCaps * in_caps);
AudioChannelLayout *
gst_core_audio_get_channel_layout (GstCoreAudio * core_audio, gboolean outer);
gboolean gst_core_audio_parse_channel_layout (AudioChannelLayout * layout, gboolean gst_core_audio_parse_channel_layout (AudioChannelLayout * layout,
gint channels, guint64 * channel_mask, GstAudioChannelPosition * pos); guint * channels, guint64 * channel_mask, GstAudioChannelPosition * pos);
GstCaps * gst_core_audio_asbd_to_caps (AudioStreamBasicDescription * asbd, GstCaps * gst_core_audio_asbd_to_caps (AudioStreamBasicDescription * asbd,
AudioChannelLayout * layout); AudioChannelLayout * layout);

View file

@ -157,6 +157,7 @@ buffer_list_alloc (UInt32 channels, UInt32 size, gboolean interleaved)
UInt32 num_buffers, n; UInt32 num_buffers, n;
num_buffers = interleaved ? 1 : channels; num_buffers = interleaved ? 1 : channels;
/* AudioBufferList member mBuffers is variable-length array */
list_size = G_STRUCT_OFFSET (AudioBufferList, mBuffers[num_buffers]); list_size = G_STRUCT_OFFSET (AudioBufferList, mBuffers[num_buffers]);
list = (AudioBufferList *) g_malloc (list_size); list = (AudioBufferList *) g_malloc (list_size);
@ -213,39 +214,66 @@ audiounit_error:
return FALSE; return FALSE;
} }
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;
}
gboolean gboolean
gst_core_audio_set_channel_layout (GstCoreAudio * core_audio, gst_core_audio_set_channel_layout (GstCoreAudio * core_audio,
gint channels, GstCaps * caps) gint channels, GstCaps * caps)
{ {
/* Configure the output stream and allocate ringbuffer memory */
AudioChannelLayout *layout = NULL; AudioChannelLayout *layout = NULL;
OSStatus status; gboolean ret;
int layoutSize, element, i; gsize layoutSize;
AudioUnitScope scope; gint i;
GstStructure *structure; GstStructure *structure;
GstAudioChannelPosition *positions = NULL; GstAudioChannelPosition positions[GST_OSX_AUDIO_MAX_CHANNEL];
guint64 channel_mask; guint64 channel_mask;
/* Describe channels */ g_return_val_if_fail (channels <= GST_OSX_AUDIO_MAX_CHANNEL, FALSE);
layoutSize = sizeof (AudioChannelLayout) +
channels * sizeof (AudioChannelDescription); /* Determine the channel positions */
structure = gst_caps_get_structure (caps, 0);
channel_mask = 0;
gst_structure_get (structure, "channel-mask", GST_TYPE_BITMASK, &channel_mask,
NULL);
if (channel_mask != 0)
gst_audio_channel_positions_from_mask (channels, channel_mask, positions);
/* AudioChannelLayout member mChannelDescriptions is variable-length array */
layoutSize =
G_STRUCT_OFFSET (AudioChannelLayout, mChannelDescriptions[channels]);
layout = g_malloc (layoutSize); layout = g_malloc (layoutSize);
structure = gst_caps_get_structure (caps, 0); /* Fill out the AudioChannelLayout */
if (gst_structure_get (structure, "channel-mask", GST_TYPE_BITMASK,
&channel_mask, NULL)) {
positions = g_new (GstAudioChannelPosition, channels);
gst_audio_channel_positions_from_mask (channels, channel_mask, positions);
}
layout->mChannelLayoutTag = kAudioChannelLayoutTag_UseChannelDescriptions; layout->mChannelLayoutTag = kAudioChannelLayoutTag_UseChannelDescriptions;
layout->mChannelBitmap = 0; /* Not used */ layout->mChannelBitmap = 0; /* Not used */
layout->mNumberChannelDescriptions = channels; layout->mNumberChannelDescriptions = channels;
for (i = 0; i < channels; i++) { for (i = 0; i < channels; i++) {
if (positions) { if (channel_mask != 0) {
layout->mChannelDescriptions[i].mChannelLabel = layout->mChannelDescriptions[i].mChannelLabel =
gst_audio_channel_position_to_coreaudio_channel_label (positions[i], gst_audio_channel_position_to_core_audio (positions[i], i);
i);
} else { } else {
/* Discrete channel numbers are ORed into this */ /* Discrete channel numbers are ORed into this */
layout->mChannelDescriptions[i].mChannelLabel = layout->mChannelDescriptions[i].mChannelLabel =
@ -253,61 +281,30 @@ gst_core_audio_set_channel_layout (GstCoreAudio * core_audio,
} }
/* Others unused */ /* Others unused */
layout->mChannelDescriptions[i].mChannelFlags = 0; layout->mChannelDescriptions[i].mChannelFlags = kAudioChannelFlags_AllOff;
layout->mChannelDescriptions[i].mCoordinates[0] = 0.f; layout->mChannelDescriptions[i].mCoordinates[0] = 0.f;
layout->mChannelDescriptions[i].mCoordinates[1] = 0.f; layout->mChannelDescriptions[i].mCoordinates[1] = 0.f;
layout->mChannelDescriptions[i].mCoordinates[2] = 0.f; layout->mChannelDescriptions[i].mCoordinates[2] = 0.f;
} }
if (positions) { /* Sets GStreamer-ordered channel layout on the inner scope.
g_free (positions); * Reordering between the inner scope and outer scope is handled
positions = NULL; * by the Audio Unit itself. */
} ret = _core_audio_set_property (core_audio,
kAudioUnitProperty_AudioChannelLayout, layout, layoutSize);
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: %d", (int) status);
return FALSE;
}
}
g_free (layout); g_free (layout);
return TRUE; return ret;
} }
gboolean gboolean
gst_core_audio_set_format (GstCoreAudio * core_audio, gst_core_audio_set_format (GstCoreAudio * core_audio,
AudioStreamBasicDescription format) 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"); GST_DEBUG_OBJECT (core_audio->osxbuf, "Setting format for AudioUnit");
scope = core_audio->is_src ? kAudioUnitScope_Output : kAudioUnitScope_Input; return _core_audio_set_property (core_audio, kAudioUnitProperty_StreamFormat,
element = core_audio->is_src ? 1 : 0; &format, sizeof (AudioStreamBasicDescription));
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: %d", (int) status);
return FALSE;
}
return TRUE;
} }
gboolean gboolean
@ -375,14 +372,10 @@ gst_core_audio_open_device (GstCoreAudio * core_audio, OSType sub_type,
} }
AudioChannelLabel AudioChannelLabel
gst_audio_channel_position_to_coreaudio_channel_label (GstAudioChannelPosition gst_audio_channel_position_to_core_audio (GstAudioChannelPosition
position, int channel) position, int channel)
{ {
switch (position) { switch (position) {
case GST_AUDIO_CHANNEL_POSITION_NONE:
return kAudioChannelLabel_Discrete_0 | channel;
case GST_AUDIO_CHANNEL_POSITION_MONO:
return kAudioChannelLabel_Mono;
case GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT: case GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT:
return kAudioChannelLabel_Left; return kAudioChannelLabel_Left;
case GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT: case GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT:
@ -397,19 +390,141 @@ gst_audio_channel_position_to_coreaudio_channel_label (GstAudioChannelPosition
return kAudioChannelLabel_LFEScreen; return kAudioChannelLabel_LFEScreen;
case GST_AUDIO_CHANNEL_POSITION_FRONT_CENTER: case GST_AUDIO_CHANNEL_POSITION_FRONT_CENTER:
return kAudioChannelLabel_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: case GST_AUDIO_CHANNEL_POSITION_SIDE_LEFT:
return kAudioChannelLabel_LeftSurroundDirect; return kAudioChannelLabel_LeftSurroundDirect;
case GST_AUDIO_CHANNEL_POSITION_SIDE_RIGHT: case GST_AUDIO_CHANNEL_POSITION_SIDE_RIGHT:
return kAudioChannelLabel_RightSurroundDirect; return kAudioChannelLabel_RightSurroundDirect;
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;
/* 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_CENTER:
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:
case GST_AUDIO_CHANNEL_POSITION_SURROUND_LEFT:
case GST_AUDIO_CHANNEL_POSITION_SURROUND_RIGHT:
default: default:
return kAudioChannelLabel_Unknown; return kAudioChannelLabel_Unknown;
} }
} }
/* 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:
return GST_AUDIO_CHANNEL_POSITION_REAR_LEFT;
case kAudioChannelLabel_RightSurround:
return GST_AUDIO_CHANNEL_POSITION_REAR_RIGHT;
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;
/* 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_RearSurroundLeft:
case kAudioChannelLabel_RearSurroundRight:
case kAudioChannelLabel_TopCenterSurround:
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;
}
}
}
void void
gst_core_audio_dump_channel_layout (AudioChannelLayout * channel_layout) gst_core_audio_dump_channel_layout (AudioChannelLayout * channel_layout)
{ {

View file

@ -61,5 +61,6 @@ OSStatus gst_core_audio_render_notify (GstCoreAudio * core_a
unsigned int inNumberFrames, unsigned int inNumberFrames,
AudioBufferList * ioData); AudioBufferList * ioData);
AudioChannelLabel gst_audio_channel_position_to_coreaudio_channel_label (GstAudioChannelPosition position, int channel); AudioChannelLabel gst_audio_channel_position_to_core_audio (GstAudioChannelPosition position, int channel);
GstAudioChannelPosition gst_core_audio_channel_label_to_gst (AudioChannelLabel label, int channel, gboolean warn);

View file

@ -319,7 +319,8 @@ _audio_device_has_output (AudioDeviceID device_id)
return TRUE; return TRUE;
} }
AudioChannelLayout * #ifdef GST_CORE_AUDIO_DEBUG
static AudioChannelLayout *
gst_core_audio_audio_device_get_channel_layout (AudioDeviceID device_id, gst_core_audio_audio_device_get_channel_layout (AudioDeviceID device_id,
gboolean output) gboolean output)
{ {
@ -384,6 +385,7 @@ failed:
g_free (layout); g_free (layout);
return NULL; return NULL;
} }
#endif
static inline AudioStreamID * static inline AudioStreamID *
_audio_device_get_streams (AudioDeviceID device_id, gint * nstreams) _audio_device_get_streams (AudioDeviceID device_id, gint * nstreams)

View file

@ -99,13 +99,6 @@ gst_core_audio_initialize_impl (GstCoreAudio * core_audio,
return TRUE; return TRUE;
} }
AudioChannelLayout *
gst_core_audio_audio_device_get_channel_layout (AudioDeviceID device_id,
gboolean output)
{
return NULL;
}
static gboolean static gboolean
gst_core_audio_select_device_impl (GstCoreAudio * core_audio) gst_core_audio_select_device_impl (GstCoreAudio * core_audio)
{ {