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 <unistd.h> /* for getpid */
25 #include "gstosxaudiosink.h"
27 static inline gboolean
28 _audio_system_set_runloop (CFRunLoopRef runLoop)
30 OSStatus status = noErr;
34 AudioObjectPropertyAddress runloopAddress = {
35 kAudioHardwarePropertyRunLoop,
36 kAudioObjectPropertyScopeGlobal,
37 kAudioObjectPropertyElementMaster
40 status = AudioObjectSetPropertyData (kAudioObjectSystemObject,
41 &runloopAddress, 0, NULL, sizeof (CFRunLoopRef), &runLoop);
42 if (status == noErr) {
45 GST_ERROR ("failed to set runloop to %p: %" GST_FOURCC_FORMAT,
46 runLoop, GST_FOURCC_ARGS (status));
52 static inline AudioDeviceID
53 _audio_system_get_default_output (void)
55 OSStatus status = noErr;
56 UInt32 propertySize = sizeof (AudioDeviceID);
57 AudioDeviceID device_id = kAudioDeviceUnknown;
59 AudioObjectPropertyAddress defaultDeviceAddress = {
60 kAudioHardwarePropertyDefaultOutputDevice,
61 kAudioDevicePropertyScopeOutput,
62 kAudioObjectPropertyElementMaster
65 status = AudioObjectGetPropertyData (kAudioObjectSystemObject,
66 &defaultDeviceAddress, 0, NULL, &propertySize, &device_id);
67 if (status != noErr) {
68 GST_ERROR ("failed getting default output device: %"
69 GST_FOURCC_FORMAT, GST_FOURCC_ARGS (status));
75 static inline AudioDeviceID *
76 _audio_system_get_devices (gint * ndevices)
78 OSStatus status = noErr;
79 UInt32 propertySize = 0;
80 AudioDeviceID *devices = NULL;
82 AudioObjectPropertyAddress audioDevicesAddress = {
83 kAudioHardwarePropertyDevices,
84 kAudioDevicePropertyScopeOutput,
85 kAudioObjectPropertyElementMaster
88 status = AudioObjectGetPropertyDataSize (kAudioObjectSystemObject,
89 &audioDevicesAddress, 0, NULL, &propertySize);
90 if (status != noErr) {
91 GST_WARNING ("failed getting number of devices: %"
92 GST_FOURCC_FORMAT, GST_FOURCC_ARGS (status));
96 *ndevices = propertySize / sizeof (AudioDeviceID);
98 devices = (AudioDeviceID *) g_malloc (propertySize);
100 status = AudioObjectGetPropertyData (kAudioObjectSystemObject,
101 &audioDevicesAddress, 0, NULL, &propertySize, devices);
102 if (status != noErr) {
103 GST_WARNING ("failed getting the list of devices: %"
104 GST_FOURCC_FORMAT, GST_FOURCC_ARGS (status));
113 static inline gboolean
114 _audio_device_is_alive (AudioDeviceID device_id)
116 OSStatus status = noErr;
118 UInt32 propertySize = sizeof (alive);
120 AudioObjectPropertyAddress audioDeviceAliveAddress = {
121 kAudioDevicePropertyDeviceIsAlive,
122 kAudioDevicePropertyScopeOutput,
123 kAudioObjectPropertyElementMaster
126 status = AudioObjectGetPropertyData (device_id,
127 &audioDeviceAliveAddress, 0, NULL, &propertySize, &alive);
128 if (status != noErr) {
136 _audio_device_get_latency (AudioDeviceID device_id)
138 OSStatus status = noErr;
140 UInt32 propertySize = sizeof (latency);
142 AudioObjectPropertyAddress audioDeviceLatencyAddress = {
143 kAudioDevicePropertyLatency,
144 kAudioDevicePropertyScopeOutput,
145 kAudioObjectPropertyElementMaster
148 status = AudioObjectGetPropertyData (device_id,
149 &audioDeviceLatencyAddress, 0, NULL, &propertySize, &latency);
150 if (status != noErr) {
151 GST_ERROR ("failed to get latency: %" GST_FOURCC_FORMAT,
152 GST_FOURCC_ARGS (status));
160 _audio_device_get_hog (AudioDeviceID device_id)
162 OSStatus status = noErr;
164 UInt32 propertySize = sizeof (hog_pid);
166 AudioObjectPropertyAddress audioDeviceHogModeAddress = {
167 kAudioDevicePropertyHogMode,
168 kAudioDevicePropertyScopeOutput,
169 kAudioObjectPropertyElementMaster
172 status = AudioObjectGetPropertyData (device_id,
173 &audioDeviceHogModeAddress, 0, NULL, &propertySize, &hog_pid);
174 if (status != noErr) {
175 GST_ERROR ("failed to get hog: %" GST_FOURCC_FORMAT,
176 GST_FOURCC_ARGS (status));
183 static inline gboolean
184 _audio_device_set_hog (AudioDeviceID device_id, pid_t hog_pid)
186 OSStatus status = noErr;
187 UInt32 propertySize = sizeof (hog_pid);
188 gboolean res = FALSE;
190 AudioObjectPropertyAddress audioDeviceHogModeAddress = {
191 kAudioDevicePropertyHogMode,
192 kAudioDevicePropertyScopeOutput,
193 kAudioObjectPropertyElementMaster
196 status = AudioObjectSetPropertyData (device_id,
197 &audioDeviceHogModeAddress, 0, NULL, propertySize, &hog_pid);
199 if (status == noErr) {
202 GST_ERROR ("failed to set hog: %" GST_FOURCC_FORMAT,
203 GST_FOURCC_ARGS (status));
209 static inline gboolean
210 _audio_device_set_mixing (AudioDeviceID device_id, gboolean enable_mix)
212 OSStatus status = noErr;
213 UInt32 propertySize = 0, can_mix = enable_mix;
214 Boolean writable = FALSE;
215 gboolean res = FALSE;
217 AudioObjectPropertyAddress audioDeviceSupportsMixingAddress = {
218 kAudioDevicePropertySupportsMixing,
219 kAudioObjectPropertyScopeGlobal,
220 kAudioObjectPropertyElementMaster
223 if (AudioObjectHasProperty (device_id, &audioDeviceSupportsMixingAddress)) {
224 /* Set mixable to false if we are allowed to */
225 status = AudioObjectIsPropertySettable (device_id,
226 &audioDeviceSupportsMixingAddress, &writable);
228 GST_DEBUG ("AudioObjectIsPropertySettable: %" GST_FOURCC_FORMAT,
229 GST_FOURCC_ARGS (status));
231 status = AudioObjectGetPropertyDataSize (device_id,
232 &audioDeviceSupportsMixingAddress, 0, NULL, &propertySize);
234 GST_DEBUG ("AudioObjectGetPropertyDataSize: %" GST_FOURCC_FORMAT,
235 GST_FOURCC_ARGS (status));
237 status = AudioObjectGetPropertyData (device_id,
238 &audioDeviceSupportsMixingAddress, 0, NULL, &propertySize, &can_mix);
240 GST_DEBUG ("AudioObjectGetPropertyData: %" GST_FOURCC_FORMAT,
241 GST_FOURCC_ARGS (status));
244 if (status == noErr && writable) {
245 can_mix = enable_mix;
246 status = AudioObjectSetPropertyData (device_id,
247 &audioDeviceSupportsMixingAddress, 0, NULL, propertySize, &can_mix);
251 if (status != noErr) {
252 GST_ERROR ("failed to set mixmode: %" GST_FOURCC_FORMAT,
253 GST_FOURCC_ARGS (status));
256 GST_DEBUG ("property not found, mixing coudln't be changed");
262 static inline gchar *
263 _audio_device_get_name (AudioDeviceID device_id)
265 OSStatus status = noErr;
266 UInt32 propertySize = 0;
267 gchar *device_name = NULL;
269 AudioObjectPropertyAddress deviceNameAddress = {
270 kAudioDevicePropertyDeviceName,
271 kAudioDevicePropertyScopeOutput,
272 kAudioObjectPropertyElementMaster
275 /* Get the length of the device name */
276 status = AudioObjectGetPropertyDataSize (device_id,
277 &deviceNameAddress, 0, NULL, &propertySize);
278 if (status != noErr) {
282 /* Get the name of the device */
283 device_name = (gchar *) g_malloc (propertySize);
284 status = AudioObjectGetPropertyData (device_id,
285 &deviceNameAddress, 0, NULL, &propertySize, device_name);
286 if (status != noErr) {
287 g_free (device_name);
295 static inline gboolean
296 _audio_device_has_output (AudioDeviceID device_id)
298 OSStatus status = noErr;
301 AudioObjectPropertyAddress streamsAddress = {
302 kAudioDevicePropertyStreams,
303 kAudioDevicePropertyScopeOutput,
304 kAudioObjectPropertyElementMaster
307 status = AudioObjectGetPropertyDataSize (device_id,
308 &streamsAddress, 0, NULL, &propertySize);
309 if (status != noErr) {
312 if (propertySize == 0) {
320 gst_core_audio_audio_device_get_channel_layout (AudioDeviceID device_id)
322 OSStatus status = noErr;
323 UInt32 propertySize = 0;
324 AudioChannelLayout *layout = NULL;
326 AudioObjectPropertyAddress channelLayoutAddress = {
327 kAudioDevicePropertyPreferredChannelLayout,
328 kAudioDevicePropertyScopeOutput,
329 kAudioObjectPropertyElementMaster
332 /* Get the length of the default channel layout structure */
333 status = AudioObjectGetPropertyDataSize (device_id,
334 &channelLayoutAddress, 0, NULL, &propertySize);
335 if (status != noErr) {
336 GST_ERROR ("failed to get prefered layout: %" GST_FOURCC_FORMAT,
337 GST_FOURCC_ARGS (status));
341 /* Get the default channel layout of the device */
342 layout = (AudioChannelLayout *) g_malloc (propertySize);
343 status = AudioObjectGetPropertyData (device_id,
344 &channelLayoutAddress, 0, NULL, &propertySize, layout);
345 if (status != noErr) {
346 GST_ERROR ("failed to get prefered layout: %" GST_FOURCC_FORMAT,
347 GST_FOURCC_ARGS (status));
351 if (layout->mChannelLayoutTag == kAudioChannelLayoutTag_UseChannelBitmap) {
352 /* bitmap defined channellayout */
354 AudioFormatGetProperty (kAudioFormatProperty_ChannelLayoutForBitmap,
355 sizeof (UInt32), &layout->mChannelBitmap, &propertySize, layout);
356 if (status != noErr) {
357 GST_ERROR ("failed to get layout for bitmap: %" GST_FOURCC_FORMAT,
358 GST_FOURCC_ARGS (status));
361 } else if (layout->mChannelLayoutTag !=
362 kAudioChannelLayoutTag_UseChannelDescriptions) {
363 /* layouttags defined channellayout */
364 status = AudioFormatGetProperty (kAudioFormatProperty_ChannelLayoutForTag,
365 sizeof (AudioChannelLayoutTag), &layout->mChannelLayoutTag,
366 &propertySize, layout);
367 if (status != noErr) {
368 GST_ERROR ("failed to get layout for tag: %" GST_FOURCC_FORMAT,
369 GST_FOURCC_ARGS (status));
374 gst_core_audio_dump_channel_layout (layout);
384 static inline AudioStreamID *
385 _audio_device_get_streams (AudioDeviceID device_id, gint * nstreams)
387 OSStatus status = noErr;
388 UInt32 propertySize = 0;
389 AudioStreamID *streams = NULL;
391 AudioObjectPropertyAddress streamsAddress = {
392 kAudioDevicePropertyStreams,
393 kAudioDevicePropertyScopeOutput,
394 kAudioObjectPropertyElementMaster
397 status = AudioObjectGetPropertyDataSize (device_id,
398 &streamsAddress, 0, NULL, &propertySize);
399 if (status != noErr) {
400 GST_WARNING ("failed getting number of streams: %"
401 GST_FOURCC_FORMAT, GST_FOURCC_ARGS (status));
405 *nstreams = propertySize / sizeof (AudioStreamID);
406 streams = (AudioStreamID *) g_malloc (propertySize);
409 status = AudioObjectGetPropertyData (device_id,
410 &streamsAddress, 0, NULL, &propertySize, streams);
411 if (status != noErr) {
412 GST_WARNING ("failed getting the list of streams: %"
413 GST_FOURCC_FORMAT, GST_FOURCC_ARGS (status));
424 _audio_stream_get_latency (AudioStreamID stream_id)
426 OSStatus status = noErr;
428 UInt32 propertySize = sizeof (latency);
430 AudioObjectPropertyAddress latencyAddress = {
431 kAudioStreamPropertyLatency,
432 kAudioObjectPropertyScopeGlobal,
433 kAudioObjectPropertyElementMaster
436 status = AudioObjectGetPropertyData (stream_id,
437 &latencyAddress, 0, NULL, &propertySize, &latency);
438 if (status != noErr) {
439 GST_ERROR ("failed to get latency: %" GST_FOURCC_FORMAT,
440 GST_FOURCC_ARGS (status));
447 static inline gboolean
448 _audio_stream_get_current_format (AudioStreamID stream_id,
449 AudioStreamBasicDescription * format)
451 OSStatus status = noErr;
452 UInt32 propertySize = sizeof (AudioStreamBasicDescription);
454 AudioObjectPropertyAddress formatAddress = {
455 kAudioStreamPropertyPhysicalFormat,
456 kAudioObjectPropertyScopeGlobal,
457 kAudioObjectPropertyElementMaster
460 status = AudioObjectGetPropertyData (stream_id,
461 &formatAddress, 0, NULL, &propertySize, format);
462 if (status != noErr) {
463 GST_ERROR ("failed to get current format: %" GST_FOURCC_FORMAT,
464 GST_FOURCC_ARGS (status));
471 static inline gboolean
472 _audio_stream_set_current_format (AudioStreamID stream_id,
473 AudioStreamBasicDescription format)
475 OSStatus status = noErr;
476 UInt32 propertySize = sizeof (AudioStreamBasicDescription);
478 AudioObjectPropertyAddress formatAddress = {
479 kAudioStreamPropertyPhysicalFormat,
480 kAudioObjectPropertyScopeGlobal,
481 kAudioObjectPropertyElementMaster
484 status = AudioObjectSetPropertyData (stream_id,
485 &formatAddress, 0, NULL, propertySize, &format);
486 if (status != noErr) {
487 GST_ERROR ("failed to set current format: %" GST_FOURCC_FORMAT,
488 GST_FOURCC_ARGS (status));
495 static inline AudioStreamRangedDescription *
496 _audio_stream_get_formats (AudioStreamID stream_id, gint * nformats)
498 OSStatus status = noErr;
499 UInt32 propertySize = 0;
500 AudioStreamRangedDescription *formats = NULL;
502 AudioObjectPropertyAddress formatsAddress = {
503 kAudioStreamPropertyAvailablePhysicalFormats,
504 kAudioObjectPropertyScopeGlobal,
505 kAudioObjectPropertyElementMaster
508 status = AudioObjectGetPropertyDataSize (stream_id,
509 &formatsAddress, 0, NULL, &propertySize);
510 if (status != noErr) {
511 GST_WARNING ("failed getting number of stream formats: %"
512 GST_FOURCC_FORMAT, GST_FOURCC_ARGS (status));
516 *nformats = propertySize / sizeof (AudioStreamRangedDescription);
518 formats = (AudioStreamRangedDescription *) g_malloc (propertySize);
520 status = AudioObjectGetPropertyData (stream_id,
521 &formatsAddress, 0, NULL, &propertySize, formats);
522 if (status != noErr) {
523 GST_WARNING ("failed getting the list of stream formats: %"
524 GST_FOURCC_FORMAT, GST_FOURCC_ARGS (status));
533 static inline gboolean
534 _audio_stream_is_spdif_avail (AudioStreamID stream_id)
536 AudioStreamRangedDescription *formats;
537 gint i, nformats = 0;
538 gboolean res = FALSE;
540 formats = _audio_stream_get_formats (stream_id, &nformats);
541 GST_DEBUG ("found %d stream formats", nformats);
544 GST_DEBUG ("formats supported on stream ID: %u", (unsigned) stream_id);
546 for (i = 0; i < nformats; i++) {
547 GST_DEBUG (" " CORE_AUDIO_FORMAT,
548 CORE_AUDIO_FORMAT_ARGS (formats[i].mFormat));
550 if (CORE_AUDIO_FORMAT_IS_SPDIF (formats[i])) {
561 _audio_stream_format_listener (AudioObjectID inObjectID,
562 UInt32 inNumberAddresses,
563 const AudioObjectPropertyAddress inAddresses[], void *inClientData)
565 OSStatus status = noErr;
567 PropertyMutex *prop_mutex = inClientData;
569 for (i = 0; i < inNumberAddresses; i++) {
570 if (inAddresses[i].mSelector == kAudioStreamPropertyPhysicalFormat) {
571 g_mutex_lock (&prop_mutex->lock);
572 g_cond_signal (&prop_mutex->cond);
573 g_mutex_unlock (&prop_mutex->lock);
581 _audio_stream_change_format (AudioStreamID stream_id,
582 AudioStreamBasicDescription format)
584 OSStatus status = noErr;
586 gboolean ret = FALSE;
587 AudioStreamBasicDescription cformat;
588 PropertyMutex prop_mutex;
590 AudioObjectPropertyAddress formatAddress = {
591 kAudioStreamPropertyPhysicalFormat,
592 kAudioObjectPropertyScopeGlobal,
593 kAudioObjectPropertyElementMaster
596 GST_DEBUG ("setting stream format: " CORE_AUDIO_FORMAT,
597 CORE_AUDIO_FORMAT_ARGS (format));
599 /* Condition because SetProperty is asynchronous */
600 g_mutex_init (&prop_mutex.lock);
601 g_cond_init (&prop_mutex.cond);
603 g_mutex_lock (&prop_mutex.lock);
605 /* Install the property listener to serialize the operations */
606 status = AudioObjectAddPropertyListener (stream_id, &formatAddress,
607 _audio_stream_format_listener, (void *) &prop_mutex);
608 if (status != noErr) {
609 GST_ERROR ("AudioObjectAddPropertyListener failed: %"
610 GST_FOURCC_FORMAT, GST_FOURCC_ARGS (status));
614 /* Change the format */
615 if (!_audio_stream_set_current_format (stream_id, format)) {
619 /* The AudioObjectSetProperty is not only asynchronous
620 * it is also not atomic in its behaviour.
621 * Therefore we check 4 times before we really give up. */
622 for (i = 0; i < 4; i++) {
625 g_get_current_time (&timeout);
626 g_time_val_add (&timeout, 250000);
628 if (!g_cond_wait_until (&prop_mutex.cond, &prop_mutex.lock, timeout.tv_sec)) {
629 GST_LOG ("timeout...");
632 if (_audio_stream_get_current_format (stream_id, &cformat)) {
633 GST_DEBUG ("current stream format: " CORE_AUDIO_FORMAT,
634 CORE_AUDIO_FORMAT_ARGS (cformat));
636 if (cformat.mSampleRate == format.mSampleRate &&
637 cformat.mFormatID == format.mFormatID &&
638 cformat.mFramesPerPacket == format.mFramesPerPacket) {
639 /* The right format is now active */
645 if (cformat.mSampleRate != format.mSampleRate ||
646 cformat.mFormatID != format.mFormatID ||
647 cformat.mFramesPerPacket != format.mFramesPerPacket) {
654 /* Removing the property listener */
655 status = AudioObjectRemovePropertyListener (stream_id,
656 &formatAddress, _audio_stream_format_listener, (void *) &prop_mutex);
657 if (status != noErr) {
658 GST_ERROR ("AudioObjectRemovePropertyListener failed: %"
659 GST_FOURCC_FORMAT, GST_FOURCC_ARGS (status));
661 /* Destroy the lock and condition */
662 g_mutex_unlock (&prop_mutex.lock);
663 g_mutex_clear (&prop_mutex.lock);
664 g_cond_clear (&prop_mutex.cond);
670 _audio_stream_hardware_changed_listener (AudioObjectID inObjectID,
671 UInt32 inNumberAddresses,
672 const AudioObjectPropertyAddress inAddresses[], void *inClientData)
674 OSStatus status = noErr;
676 GstCoreAudio *core_audio = inClientData;
678 for (i = 0; i < inNumberAddresses; i++) {
679 if (inAddresses[i].mSelector == kAudioDevicePropertyDeviceHasChanged) {
680 if (!gst_core_audio_audio_device_is_spdif_avail (core_audio->device_id)) {
681 GstOsxAudioSink *sink =
682 GST_OSX_AUDIO_SINK (GST_OBJECT_PARENT (core_audio->osxbuf));
683 GST_ELEMENT_ERROR (sink, RESOURCE, FAILED,
684 ("SPDIF output no longer available"),
685 ("Audio device is reporting that SPDIF output isn't available"));
693 static inline gboolean
694 _monitorize_spdif (GstCoreAudio * core_audio)
696 OSStatus status = noErr;
699 AudioObjectPropertyAddress propAddress = {
700 kAudioDevicePropertyDeviceHasChanged,
701 kAudioObjectPropertyScopeGlobal,
702 kAudioObjectPropertyElementMaster
705 /* Install the property listener */
706 status = AudioObjectAddPropertyListener (core_audio->device_id,
707 &propAddress, _audio_stream_hardware_changed_listener,
708 (void *) core_audio);
709 if (status != noErr) {
710 GST_ERROR_OBJECT (core_audio->osxbuf,
711 "AudioObjectAddPropertyListener failed: %"
712 GST_FOURCC_FORMAT, GST_FOURCC_ARGS (status));
719 static inline gboolean
720 _unmonitorize_spdif (GstCoreAudio * core_audio)
722 OSStatus status = noErr;
725 AudioObjectPropertyAddress propAddress = {
726 kAudioDevicePropertyDeviceHasChanged,
727 kAudioObjectPropertyScopeGlobal,
728 kAudioObjectPropertyElementMaster
731 /* Remove the property listener */
732 status = AudioObjectRemovePropertyListener (core_audio->device_id,
733 &propAddress, _audio_stream_hardware_changed_listener,
734 (void *) core_audio);
735 if (status != noErr) {
736 GST_ERROR_OBJECT (core_audio->osxbuf,
737 "AudioObjectRemovePropertyListener failed: %"
738 GST_FOURCC_FORMAT, GST_FOURCC_ARGS (status));
745 static inline gboolean
746 _open_spdif (GstCoreAudio * core_audio)
748 gboolean res = FALSE;
749 pid_t hog_pid, own_pid = getpid ();
751 /* We need the device in exclusive and disable the mixing */
752 hog_pid = _audio_device_get_hog (core_audio->device_id);
754 if (hog_pid != -1 && hog_pid != own_pid) {
755 GST_DEBUG_OBJECT (core_audio,
756 "device is currently in use by another application");
760 if (_audio_device_set_hog (core_audio->device_id, own_pid)) {
761 core_audio->hog_pid = own_pid;
764 if (_audio_device_set_mixing (core_audio->device_id, FALSE)) {
765 GST_DEBUG_OBJECT (core_audio, "disabled mixing on the device");
766 core_audio->disabled_mixing = TRUE;
774 static inline gboolean
775 _close_spdif (GstCoreAudio * core_audio)
779 _unmonitorize_spdif (core_audio);
781 if (core_audio->revert_format) {
782 if (!_audio_stream_change_format (core_audio->stream_id,
783 core_audio->original_format)) {
784 GST_WARNING_OBJECT (core_audio->osxbuf, "Format revert failed");
786 core_audio->revert_format = FALSE;
789 if (core_audio->disabled_mixing) {
790 _audio_device_set_mixing (core_audio->device_id, TRUE);
791 core_audio->disabled_mixing = FALSE;
794 if (core_audio->hog_pid != -1) {
795 hog_pid = _audio_device_get_hog (core_audio->device_id);
796 if (hog_pid == getpid ()) {
797 if (_audio_device_set_hog (core_audio->device_id, -1)) {
798 core_audio->hog_pid = -1;
807 _io_proc_spdif (AudioDeviceID inDevice,
808 const AudioTimeStamp * inNow,
809 const void *inInputData,
810 const AudioTimeStamp * inTimestamp,
811 AudioBufferList * bufferList,
812 const AudioTimeStamp * inOutputTime, GstCoreAudio * core_audio)
816 status = core_audio->element->io_proc (core_audio->osxbuf, NULL, inTimestamp,
822 static inline gboolean
823 _acquire_spdif (GstCoreAudio * core_audio, AudioStreamBasicDescription format)
825 AudioStreamID *streams = NULL;
826 gint i, j, nstreams = 0;
827 gboolean ret = FALSE;
829 if (!_open_spdif (core_audio))
832 streams = _audio_device_get_streams (core_audio->device_id, &nstreams);
834 for (i = 0; i < nstreams; i++) {
835 AudioStreamRangedDescription *formats = NULL;
838 formats = _audio_stream_get_formats (streams[i], &nformats);
841 gboolean is_spdif = FALSE;
843 /* Check if one of the supported formats is a digital format */
844 for (j = 0; j < nformats; j++) {
845 if (CORE_AUDIO_FORMAT_IS_SPDIF (formats[j])) {
852 /* if this stream supports a digital (cac3) format,
854 gint requested_rate_format = -1;
855 gint current_rate_format = -1;
856 gint backup_rate_format = -1;
858 core_audio->stream_id = streams[i];
859 core_audio->stream_idx = i;
861 if (!core_audio->revert_format) {
862 if (!_audio_stream_get_current_format (core_audio->stream_id,
863 &core_audio->original_format)) {
864 GST_WARNING_OBJECT (core_audio->osxbuf,
865 "format could not be saved");
869 core_audio->revert_format = TRUE;
872 for (j = 0; j < nformats; j++) {
873 if (CORE_AUDIO_FORMAT_IS_SPDIF (formats[j])) {
874 GST_LOG_OBJECT (core_audio->osxbuf,
875 "found stream format: " CORE_AUDIO_FORMAT,
876 CORE_AUDIO_FORMAT_ARGS (formats[j].mFormat));
878 if (formats[j].mFormat.mSampleRate == format.mSampleRate) {
879 requested_rate_format = j;
881 } else if (formats[j].mFormat.mSampleRate ==
882 core_audio->original_format.mSampleRate) {
883 current_rate_format = j;
885 if (backup_rate_format < 0 ||
886 formats[j].mFormat.mSampleRate >
887 formats[backup_rate_format].mFormat.mSampleRate) {
888 backup_rate_format = j;
894 if (requested_rate_format >= 0) {
895 /* We prefer to output at the rate of the original audio */
896 core_audio->stream_format = formats[requested_rate_format].mFormat;
897 } else if (current_rate_format >= 0) {
898 /* If not possible, we will try to use the current rate */
899 core_audio->stream_format = formats[current_rate_format].mFormat;
901 /* And if we have to, any digital format will be just
902 * fine (highest rate possible) */
903 core_audio->stream_format = formats[backup_rate_format].mFormat;
911 GST_DEBUG_OBJECT (core_audio,
912 "original stream format: " CORE_AUDIO_FORMAT,
913 CORE_AUDIO_FORMAT_ARGS (core_audio->original_format));
915 if (!_audio_stream_change_format (core_audio->stream_id,
916 core_audio->stream_format))
926 _remove_render_spdif_callback (GstCoreAudio * core_audio)
930 /* Deactivate the render callback by calling
931 * AudioDeviceDestroyIOProcID */
933 AudioDeviceDestroyIOProcID (core_audio->device_id, core_audio->procID);
934 if (status != noErr) {
935 GST_ERROR_OBJECT (core_audio->osxbuf,
936 "AudioDeviceDestroyIOProcID failed: %"
937 GST_FOURCC_FORMAT, GST_FOURCC_ARGS (status));
940 GST_DEBUG_OBJECT (core_audio,
941 "osx ring buffer removed ioproc ID: %p device_id %lu",
942 core_audio->procID, (gulong) core_audio->device_id);
944 /* We're deactivated.. */
945 core_audio->procID = 0;
946 core_audio->io_proc_needs_deactivation = FALSE;
947 core_audio->io_proc_active = FALSE;
950 static inline gboolean
951 _io_proc_spdif_start (GstCoreAudio * core_audio)
955 GST_DEBUG_OBJECT (core_audio,
956 "osx ring buffer start ioproc ID: %p device_id %lu",
957 core_audio->procID, (gulong) core_audio->device_id);
959 if (!core_audio->io_proc_active) {
960 /* Add IOProc callback */
961 status = AudioDeviceCreateIOProcID (core_audio->device_id,
962 (AudioDeviceIOProc) _io_proc_spdif,
963 (void *) core_audio, &core_audio->procID);
964 if (status != noErr) {
965 GST_ERROR_OBJECT (core_audio->osxbuf,
966 ":AudioDeviceCreateIOProcID failed: %"
967 GST_FOURCC_FORMAT, GST_FOURCC_ARGS (status));
970 core_audio->io_proc_active = TRUE;
973 core_audio->io_proc_needs_deactivation = FALSE;
976 status = AudioDeviceStart (core_audio->device_id, core_audio->procID);
977 if (status != noErr) {
978 GST_ERROR_OBJECT (core_audio->osxbuf,
979 "AudioDeviceStart failed: %" GST_FOURCC_FORMAT,
980 GST_FOURCC_ARGS (status));
986 static inline gboolean
987 _io_proc_spdif_stop (GstCoreAudio * core_audio)
992 status = AudioDeviceStop (core_audio->device_id, core_audio->procID);
993 if (status != noErr) {
994 GST_ERROR_OBJECT (core_audio->osxbuf,
995 "AudioDeviceStop failed: %" GST_FOURCC_FORMAT,
996 GST_FOURCC_ARGS (status));
999 GST_DEBUG_OBJECT (core_audio,
1000 "osx ring buffer stop ioproc ID: %p device_id %lu",
1001 core_audio->procID, (gulong) core_audio->device_id);
1003 if (core_audio->io_proc_active) {
1004 _remove_render_spdif_callback (core_audio);
1007 _close_spdif (core_audio);
1013 /***********************
1015 **********************/
1018 gst_core_audio_open_impl (GstCoreAudio * core_audio)
1020 /* The following is needed to instruct HAL to create their own
1021 * thread to handle the notifications. */
1022 _audio_system_set_runloop (NULL);
1024 /* Create a HALOutput AudioUnit.
1025 * This is the lowest-level output API that is actually sensibly
1026 * usable (the lower level ones require that you do
1027 * channel-remapping yourself, and the CoreAudio channel mapping
1028 * is sufficiently complex that doing so would be very difficult)
1030 * Note that for input we request an output unit even though
1031 * we will do input with it.
1032 * http://developer.apple.com/technotes/tn2002/tn2091.html
1034 return gst_core_audio_open_device (core_audio, kAudioUnitSubType_HALOutput,
1039 gst_core_audio_start_processing_impl (GstCoreAudio * core_audio)
1041 if (core_audio->is_passthrough) {
1042 return _io_proc_spdif_start (core_audio);
1044 return gst_core_audio_io_proc_start (core_audio);
1049 gst_core_audio_pause_processing_impl (GstCoreAudio * core_audio)
1051 if (core_audio->is_passthrough) {
1052 GST_DEBUG_OBJECT (core_audio,
1053 "osx ring buffer pause ioproc ID: %p device_id %lu",
1054 core_audio->procID, (gulong) core_audio->device_id);
1056 if (core_audio->io_proc_active) {
1057 _remove_render_spdif_callback (core_audio);
1060 GST_DEBUG_OBJECT (core_audio,
1061 "osx ring buffer pause ioproc: %p device_id %lu",
1062 core_audio->element->io_proc, (gulong) core_audio->device_id);
1063 if (core_audio->io_proc_active) {
1064 /* CoreAudio isn't threadsafe enough to do this here;
1065 * we must deactivate the render callback elsewhere. See:
1066 * http://lists.apple.com/archives/Coreaudio-api/2006/Mar/msg00010.html
1068 core_audio->io_proc_needs_deactivation = TRUE;
1075 gst_core_audio_stop_processing_impl (GstCoreAudio * core_audio)
1077 if (core_audio->is_passthrough) {
1078 _io_proc_spdif_stop (core_audio);
1080 gst_core_audio_io_proc_stop (core_audio);
1087 gst_core_audio_get_samples_and_latency_impl (GstCoreAudio * core_audio,
1088 gdouble rate, guint * samples, gdouble * latency)
1091 UInt32 size = sizeof (double);
1093 if (core_audio->is_passthrough) {
1094 *samples = _audio_device_get_latency (core_audio->device_id);
1095 *samples += _audio_stream_get_latency (core_audio->stream_id);
1096 *latency = (double) *samples / rate;
1098 status = AudioUnitGetProperty (core_audio->audiounit, kAudioUnitProperty_Latency, kAudioUnitScope_Global, 0, /* N/A for global */
1102 GST_WARNING_OBJECT (core_audio->osxbuf, "Failed to get latency: %"
1103 GST_FOURCC_FORMAT, GST_FOURCC_ARGS (status));
1108 *samples = *latency * rate;
1114 gst_core_audio_initialize_impl (GstCoreAudio * core_audio,
1115 AudioStreamBasicDescription format, GstCaps * caps,
1116 gboolean is_passthrough, guint32 * frame_size)
1118 gboolean ret = FALSE;
1120 core_audio->is_passthrough = is_passthrough;
1121 if (is_passthrough) {
1122 if (!_acquire_spdif (core_audio, format))
1124 _monitorize_spdif (core_audio);
1127 UInt32 propertySize;
1129 core_audio->stream_idx = 0;
1130 if (!gst_core_audio_set_format (core_audio, format))
1133 if (!gst_core_audio_set_channels_layout (core_audio,
1134 format.mChannelsPerFrame, caps))
1137 if (!gst_core_audio_bind_device (core_audio))
1140 if (core_audio->is_src) {
1141 propertySize = sizeof (*frame_size);
1142 status = AudioUnitGetProperty (core_audio->audiounit, kAudioDevicePropertyBufferFrameSize, kAudioUnitScope_Global, 0, /* N/A for global */
1143 frame_size, &propertySize);
1146 GST_WARNING_OBJECT (core_audio->osxbuf, "Failed to get frame size: %"
1147 GST_FOURCC_FORMAT, GST_FOURCC_ARGS (status));
1157 GST_DEBUG_OBJECT (core_audio, "osxbuf ring buffer acquired");
1163 gst_core_audio_select_device_impl (AudioDeviceID * device_id)
1165 AudioDeviceID *devices = NULL;
1166 AudioDeviceID default_device_id = 0;
1167 AudioChannelLayout *channel_layout;
1168 gint i, ndevices = 0;
1169 gboolean res = FALSE;
1171 devices = _audio_system_get_devices (&ndevices);
1174 GST_ERROR ("no audio output devices found");
1178 GST_DEBUG ("found %d audio device(s)", ndevices);
1180 for (i = 0; i < ndevices; i++) {
1183 if ((device_name = _audio_device_get_name (devices[i]))) {
1184 if (!_audio_device_has_output (devices[i])) {
1185 GST_DEBUG ("Input Device ID: %u Name: %s",
1186 (unsigned) devices[i], device_name);
1188 GST_DEBUG ("Output Device ID: %u Name: %s",
1189 (unsigned) devices[i], device_name);
1192 gst_core_audio_audio_device_get_channel_layout (devices[i]);
1193 if (channel_layout) {
1194 gst_core_audio_dump_channel_layout (channel_layout);
1195 g_free (channel_layout);
1199 g_free (device_name);
1203 /* Find the ID of the default output device */
1204 default_device_id = _audio_system_get_default_output ();
1206 /* Here we decide if selected device is valid or autoselect
1207 * the default one when required */
1208 if (*device_id == kAudioDeviceUnknown) {
1209 if (default_device_id != kAudioDeviceUnknown) {
1210 *device_id = default_device_id;
1214 for (i = 0; i < ndevices; i++) {
1215 if (*device_id == devices[i]) {
1220 if (res && !_audio_device_is_alive (*device_id)) {
1221 GST_ERROR ("Requested device not usable");
1233 gst_core_audio_audio_device_is_spdif_avail_impl (AudioDeviceID device_id)
1235 AudioStreamID *streams = NULL;
1236 gint i, nstreams = 0;
1237 gboolean res = FALSE;
1239 streams = _audio_device_get_streams (device_id, &nstreams);
1240 GST_DEBUG ("found %d streams", nstreams);
1242 for (i = 0; i < nstreams; i++) {
1243 if (_audio_stream_is_spdif_avail (streams[i])) {
1255 gst_core_audio_select_source_device_impl (AudioDeviceID * device_id)
1258 UInt32 propertySize;
1260 if (*device_id == kAudioDeviceUnknown) {
1261 /* If no specific device has been selected by the user, then pick the
1263 GST_DEBUG ("Selecting device for OSXAudioSrc");
1264 propertySize = sizeof (*device_id);
1265 status = AudioHardwareGetProperty (kAudioHardwarePropertyDefaultInputDevice,
1266 &propertySize, device_id);
1269 GST_WARNING ("AudioHardwareGetProperty returned %d", (int) status);
1271 GST_DEBUG ("AudioHardwareGetProperty returned 0");
1274 if (*device_id == kAudioDeviceUnknown) {
1275 GST_WARNING ("AudioHardwareGetProperty: device_id is "
1276 "kAudioDeviceUnknown");
1279 GST_DEBUG ("AudioHardwareGetProperty: device_id is %lu", (long) *device_id);