3 * Copyright (C) 2012-2013 Fluendo S.A. <support@fluendo.com>
4 * Authors: Josep Torra Vallès <josep@fluendo.com>
5 * Andoni Morales Alastruey <amorales@fluendo.com>
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Library General Public
9 * License as published by the Free Software Foundation; either
10 * version 2 of the License, or (at your option) any later version.
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Library General Public License for more details.
17 * You should have received a copy of the GNU Library General Public
18 * License along with this library; if not, write to the
19 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
20 * Boston, MA 02111-1307, USA.
24 #include "gstosxcoreaudio.h"
25 #include "gstosxcoreaudiocommon.h"
27 GST_DEBUG_CATEGORY_STATIC (osx_audio_debug);
28 #define GST_CAT_DEFAULT osx_audio_debug
30 G_DEFINE_TYPE (GstCoreAudio, gst_core_audio, G_TYPE_OBJECT);
33 #include "gstosxcoreaudioremoteio.c"
35 #include "gstosxcoreaudiohal.c"
40 gst_core_audio_class_init (GstCoreAudioClass * klass)
45 gst_core_audio_init (GstCoreAudio * core_audio)
47 core_audio->is_passthrough = FALSE;
48 core_audio->device_id = kAudioDeviceUnknown;
49 core_audio->is_src = FALSE;
50 core_audio->audiounit = NULL;
51 core_audio->cached_caps = NULL;
52 core_audio->cached_caps_valid = FALSE;
54 core_audio->hog_pid = -1;
55 core_audio->disabled_mixing = FALSE;
60 _is_outer_scope (AudioUnitScope scope, AudioUnitElement element)
63 (scope == kAudioUnitScope_Input && element == 1) ||
64 (scope == kAudioUnitScope_Output && element == 0);
68 _audio_unit_property_listener (void *inRefCon, AudioUnit inUnit,
69 AudioUnitPropertyID inID, AudioUnitScope inScope,
70 AudioUnitElement inElement)
72 GstCoreAudio *core_audio;
74 core_audio = GST_CORE_AUDIO (inRefCon);
75 g_assert (inUnit == core_audio->audiounit);
78 case kAudioUnitProperty_AudioChannelLayout:
79 case kAudioUnitProperty_StreamFormat:
80 if (_is_outer_scope (inScope, inElement)) {
81 /* We don't push gst_event_new_caps here (for src),
82 * nor gst_event_new_reconfigure (for sink), since Core Audio continues
83 * to happily function with the old format, doing conversion/resampling
85 * This merely "refreshes" our PREFERRED caps. */
87 /* This function is called either from a Core Audio thread
88 * or as a result of a Core Audio API (e.g. AudioUnitInitialize)
89 * from our own thread. In the latter case, osxbuf can be
90 * already locked (GStreamer's mutex is not recursive).
91 * For this reason we use a boolean flag instead of nullifying
93 core_audio->cached_caps_valid = FALSE;
99 /**************************
101 *************************/
104 gst_core_audio_new (GstObject * osxbuf)
106 GstCoreAudio *core_audio;
108 core_audio = g_object_new (GST_TYPE_CORE_AUDIO, NULL);
109 core_audio->osxbuf = osxbuf;
110 core_audio->cached_caps = NULL;
115 gst_core_audio_close (GstCoreAudio * core_audio)
119 /* Uninitialize the AudioUnit */
120 status = AudioUnitUninitialize (core_audio->audiounit);
122 GST_ERROR_OBJECT (core_audio, "Failed to uninitialize AudioUnit: %d",
127 AudioUnitRemovePropertyListenerWithUserData (core_audio->audiounit,
128 kAudioUnitProperty_AudioChannelLayout, _audio_unit_property_listener,
130 AudioUnitRemovePropertyListenerWithUserData (core_audio->audiounit,
131 kAudioUnitProperty_StreamFormat, _audio_unit_property_listener,
134 /* core_audio->osxbuf is already locked at this point */
135 core_audio->cached_caps_valid = FALSE;
136 gst_caps_replace (&core_audio->cached_caps, NULL);
138 AudioComponentInstanceDispose (core_audio->audiounit);
139 core_audio->audiounit = NULL;
144 gst_core_audio_open (GstCoreAudio * core_audio)
148 /* core_audio->osxbuf is already locked at this point */
149 core_audio->cached_caps_valid = FALSE;
150 gst_caps_replace (&core_audio->cached_caps, NULL);
152 if (!gst_core_audio_open_impl (core_audio))
155 /* Add property listener */
156 status = AudioUnitAddPropertyListener (core_audio->audiounit,
157 kAudioUnitProperty_AudioChannelLayout, _audio_unit_property_listener,
159 if (status != noErr) {
160 GST_ERROR_OBJECT (core_audio, "Failed to add audio channel layout property "
161 "listener for AudioUnit: %d", (int) status);
163 status = AudioUnitAddPropertyListener (core_audio->audiounit,
164 kAudioUnitProperty_StreamFormat, _audio_unit_property_listener,
166 if (status != noErr) {
167 GST_ERROR_OBJECT (core_audio, "Failed to add stream format property "
168 "listener for AudioUnit: %d", (int) status);
171 /* Initialize the AudioUnit. We keep the audio unit initialized early so that
172 * we can probe the underlying device. */
173 status = AudioUnitInitialize (core_audio->audiounit);
175 GST_ERROR_OBJECT (core_audio, "Failed to initialize AudioUnit: %d",
184 gst_core_audio_start_processing (GstCoreAudio * core_audio)
186 return gst_core_audio_start_processing_impl (core_audio);
190 gst_core_audio_pause_processing (GstCoreAudio * core_audio)
192 return gst_core_audio_pause_processing_impl (core_audio);
196 gst_core_audio_stop_processing (GstCoreAudio * core_audio)
198 return gst_core_audio_stop_processing_impl (core_audio);
202 gst_core_audio_get_samples_and_latency (GstCoreAudio * core_audio,
203 gdouble rate, guint * samples, gdouble * latency)
205 return gst_core_audio_get_samples_and_latency_impl (core_audio, rate,
210 gst_core_audio_initialize (GstCoreAudio * core_audio,
211 AudioStreamBasicDescription format, GstCaps * caps, gboolean is_passthrough)
215 GST_DEBUG_OBJECT (core_audio,
216 "Initializing: passthrough:%d caps:%" GST_PTR_FORMAT, is_passthrough,
219 if (!gst_core_audio_initialize_impl (core_audio, format, caps,
220 is_passthrough, &frame_size)) {
224 if (core_audio->is_src) {
225 /* create AudioBufferList needed for recording */
226 core_audio->recBufferSize = frame_size * format.mBytesPerFrame;
227 core_audio->recBufferList =
228 buffer_list_alloc (format.mChannelsPerFrame, core_audio->recBufferSize,
229 /* Currently always TRUE (i.e. interleaved) */
230 !(format.mFormatFlags & kAudioFormatFlagIsNonInterleaved));
237 gst_core_audio_uninitialize (GstCoreAudio * core_audio)
239 buffer_list_free (core_audio->recBufferList);
240 core_audio->recBufferList = NULL;
244 gst_core_audio_set_volume (GstCoreAudio * core_audio, gfloat volume)
246 AudioUnitSetParameter (core_audio->audiounit, kHALOutputParam_Volume,
247 kAudioUnitScope_Global, 0, (float) volume, 0);
251 gst_core_audio_select_device (GstCoreAudio * core_audio)
253 return gst_core_audio_select_device_impl (core_audio);
257 gst_core_audio_init_debug (void)
259 GST_DEBUG_CATEGORY_INIT (osx_audio_debug, "osxaudio", 0,
260 "OSX Audio Elements");
264 gst_core_audio_audio_device_is_spdif_avail (AudioDeviceID device_id)
266 return gst_core_audio_audio_device_is_spdif_avail_impl (device_id);
269 /* Does the channel have at least one positioned channel?
270 * (GStreamer is more strict than Core Audio, in that it requires either
271 * all channels to be positioned, or all unpositioned.) */
273 _is_core_audio_layout_positioned (AudioChannelLayout * layout)
277 g_assert (layout->mChannelLayoutTag ==
278 kAudioChannelLayoutTag_UseChannelDescriptions);
280 for (i = 0; i < layout->mNumberChannelDescriptions; ++i) {
281 GstAudioChannelPosition p =
282 gst_core_audio_channel_label_to_gst
283 (layout->mChannelDescriptions[i].mChannelLabel, i, FALSE);
285 if (p >= 0) /* not special positition */
293 _core_audio_parse_channel_descriptions (AudioChannelLayout * layout,
294 guint * channels, guint64 * channel_mask, GstAudioChannelPosition * pos)
299 g_assert (layout->mChannelLayoutTag ==
300 kAudioChannelLayoutTag_UseChannelDescriptions);
302 positioned = _is_core_audio_layout_positioned (layout);
305 /* Go over all labels, either taking only positioned or only
306 * unpositioned channels, up to GST_OSX_AUDIO_MAX_CHANNEL channels.
308 * The resulting 'pos' array will contain either:
309 * - only regular (>= 0) positions
310 * - only GST_AUDIO_CHANNEL_POSITION_NONE positions
311 * in a compact form, skipping over all unsupported positions.
314 for (i = 0; i < layout->mNumberChannelDescriptions; ++i) {
315 GstAudioChannelPosition p =
316 gst_core_audio_channel_label_to_gst
317 (layout->mChannelDescriptions[i].mChannelLabel, i, TRUE);
319 /* In positioned layouts, skip all unpositioned channels.
320 * In unpositioned layouts, skip all invalid channels. */
321 if ((positioned && p >= 0) ||
322 (!positioned && p == GST_AUDIO_CHANNEL_POSITION_NONE)) {
326 *channel_mask |= G_GUINT64_CONSTANT (1) << p;
329 if (*channels == GST_OSX_AUDIO_MAX_CHANNEL)
330 break; /* not to overflow */
336 gst_core_audio_parse_channel_layout (AudioChannelLayout * layout,
337 guint * channels, guint64 * channel_mask, GstAudioChannelPosition * pos)
339 g_assert (channels != NULL);
340 g_assert (channel_mask != NULL);
341 g_assert (layout != NULL);
343 if (layout->mChannelLayoutTag !=
344 kAudioChannelLayoutTag_UseChannelDescriptions) {
346 ("Only kAudioChannelLayoutTag_UseChannelDescriptions is supported.");
352 switch (layout->mNumberChannelDescriptions) {
355 pos[0] = GST_AUDIO_CHANNEL_POSITION_NONE;
361 pos[0] = GST_AUDIO_CHANNEL_POSITION_MONO;
367 pos[0] = GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT;
368 pos[1] = GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT;
372 GST_AUDIO_CHANNEL_POSITION_MASK (FRONT_LEFT) |
373 GST_AUDIO_CHANNEL_POSITION_MASK (FRONT_RIGHT);
376 _core_audio_parse_channel_descriptions (layout, channels, channel_mask,
382 /* Converts an AudioStreamBasicDescription to preferred caps.
384 * These caps will indicate the AU element's canonical format, which won't
385 * make Core Audio resample nor convert.
387 * NOTE ON MULTI-CHANNEL AUDIO:
389 * If layout is not NULL, resulting caps will only include the subset
390 * of channels supported by GStreamer. If the Core Audio layout contained
391 * ANY positioned channels, then ONLY positioned channels will be included
392 * in the resulting caps. Otherwise, resulting caps will be unpositioned,
393 * and include only unpositioned channels.
394 * (Channels with unsupported AudioChannelLabel will be skipped either way.)
396 * Naturally, the number of channels indicated by 'channels' can be lower
397 * than the AU element's total number of channels.
400 gst_core_audio_asbd_to_caps (AudioStreamBasicDescription * asbd,
401 AudioChannelLayout * layout)
404 GstAudioFormat format = GST_AUDIO_FORMAT_UNKNOWN;
405 guint rate, channels, bps, endianness;
406 guint64 channel_mask;
407 gboolean sign, interleaved;
409 if (asbd->mFormatID != kAudioFormatLinearPCM) {
410 GST_WARNING ("Only linear PCM is supported");
414 if (!(asbd->mFormatFlags & kAudioFormatFlagIsPacked)) {
415 GST_WARNING ("Only packed formats supported");
419 if (asbd->mFormatFlags & kLinearPCMFormatFlagsSampleFractionMask) {
420 GST_WARNING ("Fixed point audio is unsupported");
424 rate = asbd->mSampleRate;
425 if (rate == kAudioStreamAnyRate) {
426 GST_WARNING ("No sample rate");
430 bps = asbd->mBitsPerChannel;
431 endianness = asbd->mFormatFlags & kAudioFormatFlagIsBigEndian ?
432 G_BIG_ENDIAN : G_LITTLE_ENDIAN;
433 sign = asbd->mFormatID & kAudioFormatFlagIsSignedInteger ? TRUE : FALSE;
434 interleaved = asbd->mFormatFlags & kAudioFormatFlagIsNonInterleaved ?
437 if (asbd->mFormatFlags & kAudioFormatFlagIsFloat) {
439 if (endianness == G_LITTLE_ENDIAN)
440 format = GST_AUDIO_FORMAT_F32LE;
442 format = GST_AUDIO_FORMAT_F32BE;
444 } else if (bps == 64) {
445 if (endianness == G_LITTLE_ENDIAN)
446 format = GST_AUDIO_FORMAT_F64LE;
448 format = GST_AUDIO_FORMAT_F64BE;
451 format = gst_audio_format_build_integer (sign, endianness, bps, bps);
454 if (format == GST_AUDIO_FORMAT_UNKNOWN) {
455 GST_WARNING ("Unsupported sample format");
460 GstAudioChannelPosition pos[GST_OSX_AUDIO_MAX_CHANNEL];
462 if (!gst_core_audio_parse_channel_layout (layout, &channels, &channel_mask,
464 GST_WARNING ("Failed to parse channel layout");
468 /* The AU can have arbitrary channel order, but we're using GstAudioInfo
469 * which supports only the GStreamer channel order.
470 * Also, we're eventually producing caps, which only have channel-mask
471 * (whose implied order is the GStreamer channel order). */
472 gst_audio_channel_positions_to_valid_order (pos, channels);
474 gst_audio_info_set_format (&info, format, rate, channels, pos);
476 channels = MIN (asbd->mChannelsPerFrame, GST_OSX_AUDIO_MAX_CHANNEL);
477 gst_audio_info_set_format (&info, format, rate, channels, NULL);
480 return gst_audio_info_to_caps (&info);
487 _core_audio_get_property (GstCoreAudio * core_audio, gboolean outer,
488 AudioUnitPropertyID inID, void *inData, UInt32 * inDataSize)
491 AudioUnitScope scope;
492 AudioUnitElement element;
495 CORE_AUDIO_OUTER_SCOPE (core_audio) : CORE_AUDIO_INNER_SCOPE (core_audio);
496 element = CORE_AUDIO_ELEMENT (core_audio);
499 AudioUnitGetProperty (core_audio->audiounit, inID, scope, element, inData,
502 return status == noErr;
506 _core_audio_get_stream_format (GstCoreAudio * core_audio,
507 AudioStreamBasicDescription * asbd, gboolean outer)
511 size = sizeof (AudioStreamBasicDescription);
512 return _core_audio_get_property (core_audio, outer,
513 kAudioUnitProperty_StreamFormat, asbd, &size);
517 gst_core_audio_get_channel_layout (GstCoreAudio * core_audio, gboolean outer)
520 AudioChannelLayout *layout;
522 if (core_audio->is_src) {
523 GST_WARNING_OBJECT (core_audio,
524 "gst_core_audio_get_channel_layout not supported on source.");
528 if (!_core_audio_get_property (core_audio, outer,
529 kAudioUnitProperty_AudioChannelLayout, NULL, &size)) {
530 GST_WARNING_OBJECT (core_audio, "unable to get channel layout");
534 layout = g_malloc (size);
535 if (!_core_audio_get_property (core_audio, outer,
536 kAudioUnitProperty_AudioChannelLayout, layout, &size)) {
537 GST_WARNING_OBJECT (core_audio, "unable to get channel layout");
545 #define STEREO_CHANNEL_MASK \
546 (GST_AUDIO_CHANNEL_POSITION_MASK (FRONT_LEFT) | \
547 GST_AUDIO_CHANNEL_POSITION_MASK (FRONT_RIGHT))
550 gst_core_audio_probe_caps (GstCoreAudio * core_audio, GstCaps * in_caps)
553 gboolean spdif_allowed;
554 AudioChannelLayout *layout;
555 AudioStreamBasicDescription outer_asbd;
556 gboolean got_outer_asbd;
557 GstCaps *caps = NULL;
558 guint64 channel_mask;
560 /* Get the ASBD of the outer scope (i.e. input scope of Input,
561 * output scope of Output).
562 * This ASBD indicates the hardware format. */
564 _core_audio_get_stream_format (core_audio, &outer_asbd, TRUE);
566 /* Collect info about the HW capabilites and preferences */
568 gst_core_audio_audio_device_is_spdif_avail (core_audio->device_id);
569 layout = gst_core_audio_get_channel_layout (core_audio, TRUE);
571 GST_DEBUG_OBJECT (core_audio, "Selected device ID: %u SPDIF allowed: %d",
572 (unsigned) core_audio->device_id, spdif_allowed);
575 if (!gst_core_audio_parse_channel_layout (layout, &channels, &channel_mask,
577 GST_WARNING_OBJECT (core_audio, "Failed to parse channel layout");
581 /* If available, start with the preferred caps. */
583 caps = gst_core_audio_asbd_to_caps (&outer_asbd, layout);
586 } else if (got_outer_asbd) {
587 channels = outer_asbd.mChannelsPerFrame;
589 /* If available, start with the preferred caps */
590 caps = gst_core_audio_asbd_to_caps (&outer_asbd, NULL);
592 GST_ERROR_OBJECT (core_audio,
593 "Unable to get any information about hardware");
597 /* Append the allowed subset based on the template caps */
599 caps = gst_caps_new_empty ();
600 for (i = 0; i < gst_caps_get_size (in_caps); i++) {
603 in_s = gst_caps_get_structure (in_caps, i);
605 if (gst_structure_has_name (in_s, "audio/x-ac3") ||
606 gst_structure_has_name (in_s, "audio/x-dts")) {
608 gst_caps_append_structure (caps, gst_structure_copy (in_s));
613 out_s = gst_structure_copy (in_s);
614 gst_structure_set (out_s, "channels", G_TYPE_INT, channels, NULL);
615 if (channel_mask != 0) {
616 /* positioned layout */
617 gst_structure_set (out_s,
618 "channel-mask", GST_TYPE_BITMASK, channel_mask, NULL);
620 /* unpositioned layout */
621 gst_structure_remove_field (out_s, "channel-mask");
624 /* Special cases for upmixing and downmixing.
625 * Other than that, the AUs don't upmix or downmix multi-channel audio,
626 * e.g. if you push 5.1-surround audio to a stereo configuration,
627 * the left and right channels will be played accordingly,
628 * and the rest will be dropped. */
630 if (channels == 1 || (channels == 2 &&
631 (channel_mask == 0 || channel_mask == STEREO_CHANNEL_MASK))) {
633 /* If have stereo channels, then also offer mono since CoreAudio
634 * upmixes it. If mono, then also offer stereo since CoreAudio
637 gst_structure_set (out_s, "channels", GST_TYPE_INT_RANGE, 1, 2, NULL);
640 gst_structure_set (out_s, "channel-mask", GST_TYPE_BITMASK,
641 STEREO_CHANNEL_MASK, NULL);
644 gst_caps_append_structure (caps, out_s);