diff --git a/sys/osxaudio/gstosxaudioringbuffer.c b/sys/osxaudio/gstosxaudioringbuffer.c index fff06728a2..fb8c3fa932 100644 --- a/sys/osxaudio/gstosxaudioringbuffer.c +++ b/sys/osxaudio/gstosxaudioringbuffer.c @@ -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); diff --git a/sys/osxaudio/gstosxaudiosink.c b/sys/osxaudio/gstosxaudiosink.c index 23f0982a0e..cfd791385c 100644 --- a/sys/osxaudio/gstosxaudiosink.c +++ b/sys/osxaudio/gstosxaudiosink.c @@ -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; -} diff --git a/sys/osxaudio/gstosxaudiosink.h b/sys/osxaudio/gstosxaudiosink.h index 01d99662fe..1428d763a0 100644 --- a/sys/osxaudio/gstosxaudiosink.h +++ b/sys/osxaudio/gstosxaudiosink.h @@ -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 diff --git a/sys/osxaudio/gstosxaudiosrc.c b/sys/osxaudio/gstosxaudiosrc.c index 8b98dc2707..692461b98f 100644 --- a/sys/osxaudio/gstosxaudiosrc.c +++ b/sys/osxaudio/gstosxaudiosrc.c @@ -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 * diff --git a/sys/osxaudio/gstosxaudiosrc.h b/sys/osxaudio/gstosxaudiosrc.h index 82e9361bd2..fb32aa2a4b 100644 --- a/sys/osxaudio/gstosxaudiosrc.h +++ b/sys/osxaudio/gstosxaudiosrc.h @@ -68,8 +68,6 @@ struct _GstOsxAudioSrc GstAudioBaseSrc src; AudioDeviceID device_id; - - GstCaps *cached_caps; }; struct _GstOsxAudioSrcClass diff --git a/sys/osxaudio/gstosxcoreaudio.c b/sys/osxaudio/gstosxcoreaudio.c index 2e537c26d0..77a5abecc6 100644 --- a/sys/osxaudio/gstosxcoreaudio.c +++ b/sys/osxaudio/gstosxcoreaudio.c @@ -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; +} diff --git a/sys/osxaudio/gstosxcoreaudio.h b/sys/osxaudio/gstosxcoreaudio.h index b319ced0e4..71be235938 100644 --- a/sys/osxaudio/gstosxcoreaudio.h +++ b/sys/osxaudio/gstosxcoreaudio.h @@ -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); diff --git a/sys/osxaudio/gstosxcoreaudiocommon.c b/sys/osxaudio/gstosxcoreaudiocommon.c index 24f778d116..b81af9cb19 100644 --- a/sys/osxaudio/gstosxcoreaudiocommon.c +++ b/sys/osxaudio/gstosxcoreaudiocommon.c @@ -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) { diff --git a/sys/osxaudio/gstosxcoreaudiocommon.h b/sys/osxaudio/gstosxcoreaudiocommon.h index 8e7936ac3b..c4602a6b30 100644 --- a/sys/osxaudio/gstosxcoreaudiocommon.h +++ b/sys/osxaudio/gstosxcoreaudiocommon.h @@ -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); diff --git a/sys/osxaudio/gstosxcoreaudiohal.c b/sys/osxaudio/gstosxcoreaudiohal.c index 776d765995..a05f384615 100644 --- a/sys/osxaudio/gstosxcoreaudiohal.c +++ b/sys/osxaudio/gstosxcoreaudiohal.c @@ -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) diff --git a/sys/osxaudio/gstosxcoreaudioremoteio.c b/sys/osxaudio/gstosxcoreaudioremoteio.c index 81a8aa2e3c..d2f2f0bde9 100644 --- a/sys/osxaudio/gstosxcoreaudioremoteio.c +++ b/sys/osxaudio/gstosxcoreaudioremoteio.c @@ -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) {