mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-12-28 19:20:35 +00:00
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:
parent
8a8884e150
commit
0e5d698c6f
11 changed files with 614 additions and 373 deletions
|
@ -220,10 +220,9 @@ gst_osx_audio_ring_buffer_acquire (GstAudioRingBuffer * buf,
|
|||
GST_DEBUG_OBJECT (osxbuf, "Format: " CORE_AUDIO_FORMAT,
|
||||
CORE_AUDIO_FORMAT_ARGS (format));
|
||||
|
||||
if (GST_IS_OSX_AUDIO_SINK (GST_OBJECT_PARENT (buf))) {
|
||||
gst_audio_ring_buffer_set_channel_positions (buf,
|
||||
GST_OSX_AUDIO_SINK (GST_OBJECT_PARENT (buf))->channel_positions);
|
||||
}
|
||||
/* gst_audio_ring_buffer_set_channel_positions is not called
|
||||
* since the AUs perform channel reordering themselves.
|
||||
* (see gst_core_audio_set_channel_layout) */
|
||||
|
||||
buf->size = spec->segtotal * spec->segsize;
|
||||
buf->memory = g_malloc0 (buf->size);
|
||||
|
|
|
@ -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_stop (GstBaseSink * base);
|
||||
static GstCaps *gst_osx_audio_sink_getcaps (GstBaseSink * base,
|
||||
GstCaps * filter);
|
||||
static gboolean gst_osx_audio_sink_acceptcaps (GstOsxAudioSink * sink,
|
||||
|
@ -128,7 +127,6 @@ static GstAudioRingBuffer
|
|||
* gst_osx_audio_sink_create_ringbuffer (GstAudioBaseSink * sink);
|
||||
static void gst_osx_audio_sink_osxelement_init (gpointer g_iface,
|
||||
gpointer iface_data);
|
||||
static void gst_osx_audio_sink_probe_caps (GstOsxAudioSink * sink);
|
||||
static void gst_osx_audio_sink_set_volume (GstOsxAudioSink * sink);
|
||||
|
||||
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));
|
||||
|
||||
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 =
|
||||
GST_DEBUG_FUNCPTR (gst_osx_audio_sink_create_ringbuffer);
|
||||
|
@ -210,19 +207,10 @@ gst_osx_audio_sink_class_init (GstOsxAudioSinkClass * klass)
|
|||
static void
|
||||
gst_osx_audio_sink_init (GstOsxAudioSink * sink)
|
||||
{
|
||||
gint i;
|
||||
|
||||
GST_DEBUG ("Initialising object");
|
||||
|
||||
sink->device_id = kAudioDeviceUnknown;
|
||||
sink->cached_caps = NULL;
|
||||
|
||||
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
|
||||
|
@ -330,28 +318,14 @@ gst_osx_audio_sink_query (GstBaseSink * base, GstQuery * query)
|
|||
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 *
|
||||
gst_osx_audio_sink_getcaps (GstBaseSink * sink, GstCaps * filter)
|
||||
{
|
||||
GstElementClass *gstelement_class;
|
||||
GstOsxAudioSink *osxsink;
|
||||
GstAudioRingBuffer *buf;
|
||||
GstCaps *ret = NULL;
|
||||
GstOsxAudioRingBuffer *osxbuf;
|
||||
GstCaps *caps, *filtered_caps;
|
||||
|
||||
gstelement_class = GST_ELEMENT_GET_CLASS (sink);
|
||||
osxsink = GST_OSX_AUDIO_SINK (sink);
|
||||
|
||||
GST_OBJECT_LOCK (osxsink);
|
||||
|
@ -360,33 +334,51 @@ gst_osx_audio_sink_getcaps (GstBaseSink * sink, GstCaps * filter)
|
|||
gst_object_ref (buf);
|
||||
GST_OBJECT_UNLOCK (osxsink);
|
||||
|
||||
if (buf) {
|
||||
GST_OBJECT_LOCK (buf);
|
||||
|
||||
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)
|
||||
ret = gst_caps_ref (osxsink->cached_caps);
|
||||
|
||||
GST_OBJECT_UNLOCK (buf);
|
||||
|
||||
gst_object_unref (buf);
|
||||
if (!buf) {
|
||||
GST_DEBUG_OBJECT (sink, "no ring buffer, returning NULL caps");
|
||||
return GST_BASE_SINK_CLASS (parent_class)->get_caps (sink, filter);
|
||||
}
|
||||
|
||||
if (!ret)
|
||||
ret = gst_pad_get_pad_template_caps (GST_AUDIO_BASE_SINK_PAD (osxsink));
|
||||
osxbuf = GST_OSX_AUDIO_RING_BUFFER (buf);
|
||||
|
||||
if (filter) {
|
||||
GstCaps *tmp;
|
||||
tmp = gst_caps_intersect_full (filter, ret, GST_CAPS_INTERSECT_FIRST);
|
||||
gst_caps_unref (ret);
|
||||
ret = tmp;
|
||||
/* 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;
|
||||
}
|
||||
|
||||
return ret;
|
||||
GST_OBJECT_UNLOCK (buf);
|
||||
|
||||
gst_object_unref (buf);
|
||||
|
||||
if (!caps)
|
||||
return NULL;
|
||||
|
||||
if (!filter)
|
||||
return caps;
|
||||
|
||||
/* 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
|
||||
|
@ -586,85 +578,3 @@ gst_osx_audio_sink_set_volume (GstOsxAudioSink * sink)
|
|||
|
||||
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;
|
||||
}
|
||||
|
|
|
@ -77,10 +77,8 @@ struct _GstOsxAudioSink
|
|||
|
||||
AudioUnit audiounit;
|
||||
double volume;
|
||||
GstCaps *cached_caps;
|
||||
|
||||
guint channels;
|
||||
GstAudioChannelPosition channel_positions[GST_OSX_AUDIO_MAX_CHANNEL];
|
||||
};
|
||||
|
||||
struct _GstOsxAudioSinkClass
|
||||
|
|
|
@ -97,7 +97,6 @@ static GstStateChangeReturn
|
|||
gst_osx_audio_src_change_state (GstElement * element,
|
||||
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 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);
|
||||
|
||||
src->device_id = kAudioDeviceUnknown;
|
||||
src->cached_caps = NULL;
|
||||
}
|
||||
|
||||
static void
|
||||
|
@ -241,72 +239,14 @@ out:
|
|||
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 *
|
||||
gst_osx_audio_src_get_caps (GstBaseSrc * src, GstCaps * filter)
|
||||
{
|
||||
GstElementClass *gstelement_class;
|
||||
GstOsxAudioSrc *osxsrc;
|
||||
GstAudioRingBuffer *buf;
|
||||
GstCaps *ret = NULL;
|
||||
GstOsxAudioRingBuffer *osxbuf;
|
||||
GstCaps *caps, *filtered_caps;
|
||||
|
||||
gstelement_class = GST_ELEMENT_GET_CLASS (src);
|
||||
osxsrc = GST_OSX_AUDIO_SRC (src);
|
||||
|
||||
GST_OBJECT_LOCK (osxsrc);
|
||||
|
@ -315,31 +255,51 @@ gst_osx_audio_src_get_caps (GstBaseSrc * src, GstCaps * filter)
|
|||
gst_object_ref (buf);
|
||||
GST_OBJECT_UNLOCK (osxsrc);
|
||||
|
||||
if (buf) {
|
||||
GST_OBJECT_LOCK (buf);
|
||||
|
||||
if (buf->open && !osxsrc->cached_caps) {
|
||||
/* Device is open, let's probe its caps */
|
||||
gst_osx_audio_src_probe_caps (osxsrc);
|
||||
}
|
||||
|
||||
GST_OBJECT_UNLOCK (buf);
|
||||
gst_object_unref (buf);
|
||||
if (!buf) {
|
||||
GST_DEBUG_OBJECT (src, "no ring buffer, using template caps");
|
||||
return GST_BASE_SRC_CLASS (parent_class)->get_caps (src, filter);
|
||||
}
|
||||
|
||||
if (osxsrc->cached_caps)
|
||||
ret = gst_caps_ref (osxsrc->cached_caps);
|
||||
else
|
||||
ret = gst_pad_get_pad_template_caps (GST_AUDIO_BASE_SRC_PAD (osxsrc));
|
||||
osxbuf = GST_OSX_AUDIO_RING_BUFFER (buf);
|
||||
|
||||
if (filter) {
|
||||
GstCaps *tmp;
|
||||
tmp = gst_caps_intersect_full (filter, ret, GST_CAPS_INTERSECT_FIRST);
|
||||
gst_caps_unref (ret);
|
||||
ret = tmp;
|
||||
/* protect against cached_caps going away */
|
||||
GST_OBJECT_LOCK (buf);
|
||||
|
||||
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 */
|
||||
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);
|
||||
}
|
||||
|
||||
return ret;
|
||||
GST_OBJECT_UNLOCK (buf);
|
||||
|
||||
gst_object_unref (buf);
|
||||
|
||||
if (!caps)
|
||||
return NULL;
|
||||
|
||||
if (!filter)
|
||||
return caps;
|
||||
|
||||
/* 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 *
|
||||
|
|
|
@ -68,8 +68,6 @@ struct _GstOsxAudioSrc
|
|||
GstAudioBaseSrc src;
|
||||
|
||||
AudioDeviceID device_id;
|
||||
|
||||
GstCaps *cached_caps;
|
||||
};
|
||||
|
||||
struct _GstOsxAudioSrcClass
|
||||
|
|
|
@ -48,6 +48,7 @@ gst_core_audio_init (GstCoreAudio * core_audio)
|
|||
core_audio->device_id = kAudioDeviceUnknown;
|
||||
core_audio->is_src = FALSE;
|
||||
core_audio->audiounit = NULL;
|
||||
core_audio->cached_caps = NULL;
|
||||
#ifndef HAVE_IOS
|
||||
core_audio->hog_pid = -1;
|
||||
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->osxbuf = osxbuf;
|
||||
core_audio->cached_caps = NULL;
|
||||
return core_audio;
|
||||
}
|
||||
|
||||
|
@ -81,6 +83,9 @@ gst_core_audio_close (GstCoreAudio * core_audio)
|
|||
return FALSE;
|
||||
}
|
||||
|
||||
/* core_audio->osxbuf is already locked at this point */
|
||||
gst_caps_replace (&core_audio->cached_caps, NULL);
|
||||
|
||||
AudioComponentInstanceDispose (core_audio->audiounit);
|
||||
core_audio->audiounit = NULL;
|
||||
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);
|
||||
}
|
||||
|
||||
gboolean
|
||||
gst_core_audio_parse_channel_layout (AudioChannelLayout * layout,
|
||||
gint channels, guint64 * channel_mask, GstAudioChannelPosition * pos)
|
||||
/* Does the channel have at least one positioned channel?
|
||||
* (GStreamer is more strict than Core Audio, in that it requires either
|
||||
* all channels to be positioned, or all unpositioned.) */
|
||||
static gboolean
|
||||
_is_core_audio_layout_positioned (AudioChannelLayout * layout)
|
||||
{
|
||||
gint i;
|
||||
gboolean ret = TRUE;
|
||||
guint i;
|
||||
|
||||
g_return_val_if_fail (channels <= GST_OSX_AUDIO_MAX_CHANNEL, FALSE);
|
||||
g_assert (layout->mChannelLayoutTag ==
|
||||
kAudioChannelLayoutTag_UseChannelDescriptions);
|
||||
|
||||
switch (channels) {
|
||||
case 0:
|
||||
pos[0] = GST_AUDIO_CHANNEL_POSITION_NONE;
|
||||
break;
|
||||
case 1:
|
||||
pos[0] = GST_AUDIO_CHANNEL_POSITION_MONO;
|
||||
break;
|
||||
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;
|
||||
}
|
||||
}
|
||||
for (i = 0; i < layout->mNumberChannelDescriptions; ++i) {
|
||||
GstAudioChannelPosition p =
|
||||
gst_core_audio_channel_label_to_gst
|
||||
(layout->mChannelDescriptions[i].mChannelLabel, i, FALSE);
|
||||
|
||||
if (p >= 0) /* not special positition */
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
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 *
|
||||
gst_core_audio_asbd_to_caps (AudioStreamBasicDescription * asbd,
|
||||
AudioChannelLayout * layout)
|
||||
{
|
||||
GstAudioInfo info;
|
||||
GstAudioFormat format = GST_AUDIO_FORMAT_UNKNOWN;
|
||||
GstAudioChannelPosition pos[64] = { GST_AUDIO_CHANNEL_POSITION_INVALID, };
|
||||
gint rate, channels, bps, endianness;
|
||||
guint rate, channels, bps, endianness;
|
||||
guint64 channel_mask;
|
||||
gboolean sign, interleaved;
|
||||
|
||||
|
@ -283,13 +352,9 @@ gst_core_audio_asbd_to_caps (AudioStreamBasicDescription * asbd,
|
|||
}
|
||||
|
||||
rate = asbd->mSampleRate;
|
||||
if (rate == kAudioStreamAnyRate)
|
||||
rate = GST_AUDIO_DEF_RATE;
|
||||
|
||||
channels = asbd->mChannelsPerFrame;
|
||||
if (channels == 0) {
|
||||
/* The documentation says this should not happen! */
|
||||
channels = 1;
|
||||
if (rate == kAudioStreamAnyRate) {
|
||||
GST_WARNING ("No sample rate");
|
||||
goto error;
|
||||
}
|
||||
|
||||
bps = asbd->mBitsPerChannel;
|
||||
|
@ -321,16 +386,206 @@ gst_core_audio_asbd_to_caps (AudioStreamBasicDescription * asbd,
|
|||
goto error;
|
||||
}
|
||||
|
||||
if (!gst_core_audio_parse_channel_layout (layout, channels, &channel_mask,
|
||||
pos)) {
|
||||
GST_WARNING ("Failed to parse channel layout");
|
||||
goto error;
|
||||
}
|
||||
if (layout) {
|
||||
GstAudioChannelPosition pos[GST_OSX_AUDIO_MAX_CHANNEL];
|
||||
|
||||
gst_audio_info_set_format (&info, format, rate, channels, pos);
|
||||
if (!gst_core_audio_parse_channel_layout (layout, &channels, &channel_mask,
|
||||
pos)) {
|
||||
GST_WARNING ("Failed to parse channel layout");
|
||||
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);
|
||||
} 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);
|
||||
|
||||
error:
|
||||
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;
|
||||
}
|
||||
|
|
|
@ -63,6 +63,7 @@ G_BEGIN_DECLS
|
|||
#define GST_IS_CORE_AUDIO_CLASS(klass) \
|
||||
(G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_CORE_AUDIO))
|
||||
|
||||
/* TODO: Consider raising to 64 */
|
||||
#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)
|
||||
|
@ -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_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 _GstCoreAudioClass GstCoreAudioClass;
|
||||
|
||||
|
@ -83,6 +88,7 @@ struct _GstCoreAudio
|
|||
gboolean is_src;
|
||||
gboolean is_passthrough;
|
||||
AudioDeviceID device_id;
|
||||
GstCaps *cached_caps;
|
||||
gint stream_idx;
|
||||
gboolean io_proc_active;
|
||||
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);
|
||||
|
||||
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,
|
||||
gint channels, guint64 * channel_mask, GstAudioChannelPosition * pos);
|
||||
guint * channels, guint64 * channel_mask, GstAudioChannelPosition * pos);
|
||||
GstCaps * gst_core_audio_asbd_to_caps (AudioStreamBasicDescription * asbd,
|
||||
AudioChannelLayout * layout);
|
||||
|
||||
|
|
|
@ -157,6 +157,7 @@ buffer_list_alloc (UInt32 channels, UInt32 size, gboolean interleaved)
|
|||
UInt32 num_buffers, n;
|
||||
|
||||
num_buffers = interleaved ? 1 : channels;
|
||||
/* AudioBufferList member mBuffers is variable-length array */
|
||||
list_size = G_STRUCT_OFFSET (AudioBufferList, mBuffers[num_buffers]);
|
||||
list = (AudioBufferList *) g_malloc (list_size);
|
||||
|
||||
|
@ -213,39 +214,66 @@ audiounit_error:
|
|||
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
|
||||
gst_core_audio_set_channel_layout (GstCoreAudio * core_audio,
|
||||
gint channels, GstCaps * caps)
|
||||
{
|
||||
/* Configure the output stream and allocate ringbuffer memory */
|
||||
AudioChannelLayout *layout = NULL;
|
||||
OSStatus status;
|
||||
int layoutSize, element, i;
|
||||
AudioUnitScope scope;
|
||||
gboolean ret;
|
||||
gsize layoutSize;
|
||||
gint i;
|
||||
GstStructure *structure;
|
||||
GstAudioChannelPosition *positions = NULL;
|
||||
GstAudioChannelPosition positions[GST_OSX_AUDIO_MAX_CHANNEL];
|
||||
guint64 channel_mask;
|
||||
|
||||
/* Describe channels */
|
||||
layoutSize = sizeof (AudioChannelLayout) +
|
||||
channels * sizeof (AudioChannelDescription);
|
||||
g_return_val_if_fail (channels <= GST_OSX_AUDIO_MAX_CHANNEL, FALSE);
|
||||
|
||||
/* 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);
|
||||
|
||||
structure = gst_caps_get_structure (caps, 0);
|
||||
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);
|
||||
}
|
||||
|
||||
/* Fill out the AudioChannelLayout */
|
||||
layout->mChannelLayoutTag = kAudioChannelLayoutTag_UseChannelDescriptions;
|
||||
layout->mChannelBitmap = 0; /* Not used */
|
||||
layout->mNumberChannelDescriptions = channels;
|
||||
for (i = 0; i < channels; i++) {
|
||||
if (positions) {
|
||||
if (channel_mask != 0) {
|
||||
layout->mChannelDescriptions[i].mChannelLabel =
|
||||
gst_audio_channel_position_to_coreaudio_channel_label (positions[i],
|
||||
i);
|
||||
gst_audio_channel_position_to_core_audio (positions[i], i);
|
||||
} else {
|
||||
/* Discrete channel numbers are ORed into this */
|
||||
layout->mChannelDescriptions[i].mChannelLabel =
|
||||
|
@ -253,61 +281,30 @@ gst_core_audio_set_channel_layout (GstCoreAudio * core_audio,
|
|||
}
|
||||
|
||||
/* Others unused */
|
||||
layout->mChannelDescriptions[i].mChannelFlags = 0;
|
||||
layout->mChannelDescriptions[i].mChannelFlags = kAudioChannelFlags_AllOff;
|
||||
layout->mChannelDescriptions[i].mCoordinates[0] = 0.f;
|
||||
layout->mChannelDescriptions[i].mCoordinates[1] = 0.f;
|
||||
layout->mChannelDescriptions[i].mCoordinates[2] = 0.f;
|
||||
}
|
||||
|
||||
if (positions) {
|
||||
g_free (positions);
|
||||
positions = NULL;
|
||||
}
|
||||
|
||||
scope = core_audio->is_src ? kAudioUnitScope_Output : kAudioUnitScope_Input;
|
||||
element = core_audio->is_src ? 1 : 0;
|
||||
|
||||
if (layoutSize) {
|
||||
status = AudioUnitSetProperty (core_audio->audiounit,
|
||||
kAudioUnitProperty_AudioChannelLayout,
|
||||
scope, element, layout, layoutSize);
|
||||
if (status) {
|
||||
GST_WARNING_OBJECT (core_audio->osxbuf,
|
||||
"Failed to set output channel layout: %d", (int) status);
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
/* 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);
|
||||
|
||||
g_free (layout);
|
||||
return TRUE;
|
||||
return ret;
|
||||
}
|
||||
|
||||
gboolean
|
||||
gst_core_audio_set_format (GstCoreAudio * core_audio,
|
||||
AudioStreamBasicDescription format)
|
||||
{
|
||||
/* Configure the output stream and allocate ringbuffer memory */
|
||||
OSStatus status;
|
||||
UInt32 propertySize;
|
||||
int element;
|
||||
AudioUnitScope scope;
|
||||
|
||||
GST_DEBUG_OBJECT (core_audio->osxbuf, "Setting format for AudioUnit");
|
||||
|
||||
scope = core_audio->is_src ? kAudioUnitScope_Output : kAudioUnitScope_Input;
|
||||
element = core_audio->is_src ? 1 : 0;
|
||||
|
||||
propertySize = sizeof (AudioStreamBasicDescription);
|
||||
status = AudioUnitSetProperty (core_audio->audiounit,
|
||||
kAudioUnitProperty_StreamFormat, scope, element, &format, propertySize);
|
||||
|
||||
if (status) {
|
||||
GST_WARNING_OBJECT (core_audio->osxbuf,
|
||||
"Failed to set audio description: %d", (int) status);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
return _core_audio_set_property (core_audio, kAudioUnitProperty_StreamFormat,
|
||||
&format, sizeof (AudioStreamBasicDescription));
|
||||
}
|
||||
|
||||
gboolean
|
||||
|
@ -375,14 +372,10 @@ gst_core_audio_open_device (GstCoreAudio * core_audio, OSType sub_type,
|
|||
}
|
||||
|
||||
AudioChannelLabel
|
||||
gst_audio_channel_position_to_coreaudio_channel_label (GstAudioChannelPosition
|
||||
gst_audio_channel_position_to_core_audio (GstAudioChannelPosition
|
||||
position, int channel)
|
||||
{
|
||||
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:
|
||||
return kAudioChannelLabel_Left;
|
||||
case GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT:
|
||||
|
@ -397,19 +390,141 @@ gst_audio_channel_position_to_coreaudio_channel_label (GstAudioChannelPosition
|
|||
return kAudioChannelLabel_LFEScreen;
|
||||
case GST_AUDIO_CHANNEL_POSITION_FRONT_CENTER:
|
||||
return kAudioChannelLabel_Center;
|
||||
case GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER:
|
||||
return kAudioChannelLabel_Center; // ???
|
||||
case GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER:
|
||||
return kAudioChannelLabel_Center; // ???
|
||||
case GST_AUDIO_CHANNEL_POSITION_SIDE_LEFT:
|
||||
return kAudioChannelLabel_LeftSurroundDirect;
|
||||
case GST_AUDIO_CHANNEL_POSITION_SIDE_RIGHT:
|
||||
return kAudioChannelLabel_RightSurroundDirect;
|
||||
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:
|
||||
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
|
||||
gst_core_audio_dump_channel_layout (AudioChannelLayout * channel_layout)
|
||||
{
|
||||
|
|
|
@ -61,5 +61,6 @@ OSStatus gst_core_audio_render_notify (GstCoreAudio * core_a
|
|||
unsigned int inNumberFrames,
|
||||
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);
|
||||
|
|
|
@ -319,7 +319,8 @@ _audio_device_has_output (AudioDeviceID device_id)
|
|||
return TRUE;
|
||||
}
|
||||
|
||||
AudioChannelLayout *
|
||||
#ifdef GST_CORE_AUDIO_DEBUG
|
||||
static AudioChannelLayout *
|
||||
gst_core_audio_audio_device_get_channel_layout (AudioDeviceID device_id,
|
||||
gboolean output)
|
||||
{
|
||||
|
@ -384,6 +385,7 @@ failed:
|
|||
g_free (layout);
|
||||
return NULL;
|
||||
}
|
||||
#endif
|
||||
|
||||
static inline AudioStreamID *
|
||||
_audio_device_get_streams (AudioDeviceID device_id, gint * nstreams)
|
||||
|
|
|
@ -99,13 +99,6 @@ gst_core_audio_initialize_impl (GstCoreAudio * core_audio,
|
|||
return TRUE;
|
||||
}
|
||||
|
||||
AudioChannelLayout *
|
||||
gst_core_audio_audio_device_get_channel_layout (AudioDeviceID device_id,
|
||||
gboolean output)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gst_core_audio_select_device_impl (GstCoreAudio * core_audio)
|
||||
{
|
||||
|
|
Loading…
Reference in a new issue