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: %d", runLoop, (int) status);
51 static inline AudioDeviceID
52 _audio_system_get_default_device (gboolean output)
54 OSStatus status = noErr;
55 UInt32 propertySize = sizeof (AudioDeviceID);
56 AudioDeviceID device_id = kAudioDeviceUnknown;
57 AudioObjectPropertySelector prop_selector;
59 prop_selector = output ? kAudioHardwarePropertyDefaultOutputDevice :
60 kAudioHardwarePropertyDefaultInputDevice;
62 AudioObjectPropertyAddress defaultDeviceAddress = {
64 kAudioObjectPropertyScopeGlobal,
65 kAudioObjectPropertyElementMaster
68 status = AudioObjectGetPropertyData (kAudioObjectSystemObject,
69 &defaultDeviceAddress, 0, NULL, &propertySize, &device_id);
70 if (status != noErr) {
71 GST_ERROR ("failed getting default output device: %d", (int) status);
74 GST_DEBUG ("Default device id: %u", (unsigned) device_id);
79 static inline AudioDeviceID *
80 _audio_system_get_devices (gint * ndevices)
82 OSStatus status = noErr;
83 UInt32 propertySize = 0;
84 AudioDeviceID *devices = NULL;
86 AudioObjectPropertyAddress audioDevicesAddress = {
87 kAudioHardwarePropertyDevices,
88 kAudioObjectPropertyScopeGlobal,
89 kAudioObjectPropertyElementMaster
92 status = AudioObjectGetPropertyDataSize (kAudioObjectSystemObject,
93 &audioDevicesAddress, 0, NULL, &propertySize);
94 if (status != noErr) {
95 GST_WARNING ("failed getting number of devices: %d", (int) status);
99 *ndevices = propertySize / sizeof (AudioDeviceID);
101 devices = (AudioDeviceID *) g_malloc (propertySize);
103 status = AudioObjectGetPropertyData (kAudioObjectSystemObject,
104 &audioDevicesAddress, 0, NULL, &propertySize, devices);
105 if (status != noErr) {
106 GST_WARNING ("failed getting the list of devices: %d", (int) status);
115 static inline gboolean
116 _audio_device_is_alive (AudioDeviceID device_id, gboolean output)
118 OSStatus status = noErr;
120 UInt32 propertySize = sizeof (alive);
121 AudioObjectPropertyScope prop_scope;
123 prop_scope = output ? kAudioDevicePropertyScopeOutput :
124 kAudioDevicePropertyScopeInput;
126 AudioObjectPropertyAddress audioDeviceAliveAddress = {
127 kAudioDevicePropertyDeviceIsAlive,
129 kAudioObjectPropertyElementMaster
132 status = AudioObjectGetPropertyData (device_id,
133 &audioDeviceAliveAddress, 0, NULL, &propertySize, &alive);
134 if (status != noErr) {
142 _audio_device_get_latency (AudioDeviceID device_id)
144 OSStatus status = noErr;
146 UInt32 propertySize = sizeof (latency);
148 AudioObjectPropertyAddress audioDeviceLatencyAddress = {
149 kAudioDevicePropertyLatency,
150 kAudioDevicePropertyScopeOutput,
151 kAudioObjectPropertyElementMaster
154 status = AudioObjectGetPropertyData (device_id,
155 &audioDeviceLatencyAddress, 0, NULL, &propertySize, &latency);
156 if (status != noErr) {
157 GST_ERROR ("failed to get latency: %d", (int) status);
165 _audio_device_get_hog (AudioDeviceID device_id)
167 OSStatus status = noErr;
169 UInt32 propertySize = sizeof (hog_pid);
171 AudioObjectPropertyAddress audioDeviceHogModeAddress = {
172 kAudioDevicePropertyHogMode,
173 kAudioDevicePropertyScopeOutput,
174 kAudioObjectPropertyElementMaster
177 status = AudioObjectGetPropertyData (device_id,
178 &audioDeviceHogModeAddress, 0, NULL, &propertySize, &hog_pid);
179 if (status != noErr) {
180 GST_ERROR ("failed to get hog: %d", (int) status);
187 static inline gboolean
188 _audio_device_set_hog (AudioDeviceID device_id, pid_t hog_pid)
190 OSStatus status = noErr;
191 UInt32 propertySize = sizeof (hog_pid);
192 gboolean res = FALSE;
194 AudioObjectPropertyAddress audioDeviceHogModeAddress = {
195 kAudioDevicePropertyHogMode,
196 kAudioDevicePropertyScopeOutput,
197 kAudioObjectPropertyElementMaster
200 status = AudioObjectSetPropertyData (device_id,
201 &audioDeviceHogModeAddress, 0, NULL, propertySize, &hog_pid);
203 if (status == noErr) {
206 GST_ERROR ("failed to set hog: %d", (int) status);
212 static inline gboolean
213 _audio_device_set_mixing (AudioDeviceID device_id, gboolean enable_mix)
215 OSStatus status = noErr;
216 UInt32 propertySize = 0, can_mix = enable_mix;
217 Boolean writable = FALSE;
218 gboolean res = FALSE;
220 AudioObjectPropertyAddress audioDeviceSupportsMixingAddress = {
221 kAudioDevicePropertySupportsMixing,
222 kAudioObjectPropertyScopeGlobal,
223 kAudioObjectPropertyElementMaster
226 if (AudioObjectHasProperty (device_id, &audioDeviceSupportsMixingAddress)) {
227 /* Set mixable to false if we are allowed to */
228 status = AudioObjectIsPropertySettable (device_id,
229 &audioDeviceSupportsMixingAddress, &writable);
231 GST_DEBUG ("AudioObjectIsPropertySettable: %d", (int) status);
233 status = AudioObjectGetPropertyDataSize (device_id,
234 &audioDeviceSupportsMixingAddress, 0, NULL, &propertySize);
236 GST_DEBUG ("AudioObjectGetPropertyDataSize: %d", (int) status);
238 status = AudioObjectGetPropertyData (device_id,
239 &audioDeviceSupportsMixingAddress, 0, NULL, &propertySize, &can_mix);
241 GST_DEBUG ("AudioObjectGetPropertyData: %d", (int) 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: %d", (int) status);
255 GST_DEBUG ("property not found, mixing coudln't be changed");
261 static inline gchar *
262 _audio_device_get_name (AudioDeviceID device_id, gboolean output)
264 OSStatus status = noErr;
265 UInt32 propertySize = 0;
266 gchar *device_name = NULL;
267 AudioObjectPropertyScope prop_scope;
269 prop_scope = output ? kAudioDevicePropertyScopeOutput :
270 kAudioDevicePropertyScopeInput;
272 AudioObjectPropertyAddress deviceNameAddress = {
273 kAudioDevicePropertyDeviceName,
275 kAudioObjectPropertyElementMaster
278 /* Get the length of the device name */
279 status = AudioObjectGetPropertyDataSize (device_id,
280 &deviceNameAddress, 0, NULL, &propertySize);
281 if (status != noErr) {
285 /* Get the name of the device */
286 device_name = (gchar *) g_malloc (propertySize);
287 status = AudioObjectGetPropertyData (device_id,
288 &deviceNameAddress, 0, NULL, &propertySize, device_name);
289 if (status != noErr) {
290 g_free (device_name);
298 static inline gboolean
299 _audio_device_has_output (AudioDeviceID device_id)
301 OSStatus status = noErr;
304 AudioObjectPropertyAddress streamsAddress = {
305 kAudioDevicePropertyStreams,
306 kAudioDevicePropertyScopeOutput,
307 kAudioObjectPropertyElementMaster
310 status = AudioObjectGetPropertyDataSize (device_id,
311 &streamsAddress, 0, NULL, &propertySize);
312 if (status != noErr) {
315 if (propertySize == 0) {
322 #ifdef GST_CORE_AUDIO_DEBUG
323 static AudioChannelLayout *
324 gst_core_audio_audio_device_get_channel_layout (AudioDeviceID device_id,
327 OSStatus status = noErr;
328 UInt32 propertySize = 0;
329 AudioChannelLayout *layout = NULL;
330 AudioObjectPropertyScope prop_scope;
332 prop_scope = output ? kAudioDevicePropertyScopeOutput :
333 kAudioDevicePropertyScopeInput;
335 AudioObjectPropertyAddress channelLayoutAddress = {
336 kAudioDevicePropertyPreferredChannelLayout,
338 kAudioObjectPropertyElementMaster
341 /* Get the length of the default channel layout structure */
342 status = AudioObjectGetPropertyDataSize (device_id,
343 &channelLayoutAddress, 0, NULL, &propertySize);
344 if (status != noErr) {
345 GST_ERROR ("failed to get preferred layout: %d", (int) status);
349 /* Get the default channel layout of the device */
350 layout = (AudioChannelLayout *) g_malloc (propertySize);
351 status = AudioObjectGetPropertyData (device_id,
352 &channelLayoutAddress, 0, NULL, &propertySize, layout);
353 if (status != noErr) {
354 GST_ERROR ("failed to get preferred layout: %d", (int) status);
358 if (layout->mChannelLayoutTag == kAudioChannelLayoutTag_UseChannelBitmap) {
359 /* bitmap defined channellayout */
361 AudioFormatGetProperty (kAudioFormatProperty_ChannelLayoutForBitmap,
362 sizeof (UInt32), &layout->mChannelBitmap, &propertySize, layout);
363 if (status != noErr) {
364 GST_ERROR ("failed to get layout for bitmap: %d", (int) status);
367 } else if (layout->mChannelLayoutTag !=
368 kAudioChannelLayoutTag_UseChannelDescriptions) {
369 /* layouttags defined channellayout */
370 status = AudioFormatGetProperty (kAudioFormatProperty_ChannelLayoutForTag,
371 sizeof (AudioChannelLayoutTag), &layout->mChannelLayoutTag,
372 &propertySize, layout);
373 if (status != noErr) {
374 GST_ERROR ("failed to get layout for tag: %d", (int) status);
379 gst_core_audio_dump_channel_layout (layout);
390 static inline AudioStreamID *
391 _audio_device_get_streams (AudioDeviceID device_id, gint * nstreams)
393 OSStatus status = noErr;
394 UInt32 propertySize = 0;
395 AudioStreamID *streams = NULL;
397 AudioObjectPropertyAddress streamsAddress = {
398 kAudioDevicePropertyStreams,
399 kAudioDevicePropertyScopeOutput,
400 kAudioObjectPropertyElementMaster
403 status = AudioObjectGetPropertyDataSize (device_id,
404 &streamsAddress, 0, NULL, &propertySize);
405 if (status != noErr) {
406 GST_WARNING ("failed getting number of streams: %d", (int) status);
410 *nstreams = propertySize / sizeof (AudioStreamID);
411 streams = (AudioStreamID *) g_malloc (propertySize);
414 status = AudioObjectGetPropertyData (device_id,
415 &streamsAddress, 0, NULL, &propertySize, streams);
416 if (status != noErr) {
417 GST_WARNING ("failed getting the list of streams: %d", (int) status);
428 _audio_stream_get_latency (AudioStreamID stream_id)
430 OSStatus status = noErr;
432 UInt32 propertySize = sizeof (latency);
434 AudioObjectPropertyAddress latencyAddress = {
435 kAudioStreamPropertyLatency,
436 kAudioObjectPropertyScopeGlobal,
437 kAudioObjectPropertyElementMaster
440 status = AudioObjectGetPropertyData (stream_id,
441 &latencyAddress, 0, NULL, &propertySize, &latency);
442 if (status != noErr) {
443 GST_ERROR ("failed to get latency: %d", (int) status);
450 static inline gboolean
451 _audio_stream_get_current_format (AudioStreamID stream_id,
452 AudioStreamBasicDescription * format)
454 OSStatus status = noErr;
455 UInt32 propertySize = sizeof (AudioStreamBasicDescription);
457 AudioObjectPropertyAddress formatAddress = {
458 kAudioStreamPropertyPhysicalFormat,
459 kAudioObjectPropertyScopeGlobal,
460 kAudioObjectPropertyElementMaster
463 status = AudioObjectGetPropertyData (stream_id,
464 &formatAddress, 0, NULL, &propertySize, format);
465 if (status != noErr) {
466 GST_ERROR ("failed to get current format: %d", (int) status);
473 static inline gboolean
474 _audio_stream_set_current_format (AudioStreamID stream_id,
475 AudioStreamBasicDescription format)
477 OSStatus status = noErr;
478 UInt32 propertySize = sizeof (AudioStreamBasicDescription);
480 AudioObjectPropertyAddress formatAddress = {
481 kAudioStreamPropertyPhysicalFormat,
482 kAudioObjectPropertyScopeGlobal,
483 kAudioObjectPropertyElementMaster
486 status = AudioObjectSetPropertyData (stream_id,
487 &formatAddress, 0, NULL, propertySize, &format);
488 if (status != noErr) {
489 GST_ERROR ("failed to set current format: %d", (int) status);
496 static inline AudioStreamRangedDescription *
497 _audio_stream_get_formats (AudioStreamID stream_id, gint * nformats)
499 OSStatus status = noErr;
500 UInt32 propertySize = 0;
501 AudioStreamRangedDescription *formats = NULL;
503 AudioObjectPropertyAddress formatsAddress = {
504 kAudioStreamPropertyAvailablePhysicalFormats,
505 kAudioObjectPropertyScopeGlobal,
506 kAudioObjectPropertyElementMaster
509 status = AudioObjectGetPropertyDataSize (stream_id,
510 &formatsAddress, 0, NULL, &propertySize);
511 if (status != noErr) {
512 GST_WARNING ("failed getting number of stream formats: %d", (int) 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: %d",
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: %d", (int) status);
613 /* Change the format */
614 if (!_audio_stream_set_current_format (stream_id, format)) {
618 /* The AudioObjectSetProperty is not only asynchronous
619 * it is also not atomic in its behaviour.
620 * Therefore we check 4 times before we really give up. */
621 for (i = 0; i < 4; i++) {
624 g_get_current_time (&timeout);
625 g_time_val_add (&timeout, 250000);
627 if (!g_cond_wait_until (&prop_mutex.cond, &prop_mutex.lock, timeout.tv_sec)) {
628 GST_LOG ("timeout...");
631 if (_audio_stream_get_current_format (stream_id, &cformat)) {
632 GST_DEBUG ("current stream format: " CORE_AUDIO_FORMAT,
633 CORE_AUDIO_FORMAT_ARGS (cformat));
635 if (cformat.mSampleRate == format.mSampleRate &&
636 cformat.mFormatID == format.mFormatID &&
637 cformat.mFramesPerPacket == format.mFramesPerPacket) {
638 /* The right format is now active */
644 if (cformat.mSampleRate != format.mSampleRate ||
645 cformat.mFormatID != format.mFormatID ||
646 cformat.mFramesPerPacket != format.mFramesPerPacket) {
653 /* Removing the property listener */
654 status = AudioObjectRemovePropertyListener (stream_id,
655 &formatAddress, _audio_stream_format_listener, (void *) &prop_mutex);
656 if (status != noErr) {
657 GST_ERROR ("AudioObjectRemovePropertyListener failed: %d", (int) status);
659 /* Destroy the lock and condition */
660 g_mutex_unlock (&prop_mutex.lock);
661 g_mutex_clear (&prop_mutex.lock);
662 g_cond_clear (&prop_mutex.cond);
668 _audio_stream_hardware_changed_listener (AudioObjectID inObjectID,
669 UInt32 inNumberAddresses,
670 const AudioObjectPropertyAddress inAddresses[], void *inClientData)
672 OSStatus status = noErr;
674 GstCoreAudio *core_audio = inClientData;
676 for (i = 0; i < inNumberAddresses; i++) {
677 if (inAddresses[i].mSelector == kAudioDevicePropertyDeviceHasChanged) {
678 if (!gst_core_audio_audio_device_is_spdif_avail (core_audio->device_id)) {
679 GstOsxAudioSink *sink =
680 GST_OSX_AUDIO_SINK (GST_OBJECT_PARENT (core_audio->osxbuf));
681 GST_ELEMENT_ERROR (sink, RESOURCE, FAILED,
682 ("SPDIF output no longer available"),
683 ("Audio device is reporting that SPDIF output isn't available"));
691 static inline gboolean
692 _monitorize_spdif (GstCoreAudio * core_audio)
694 OSStatus status = noErr;
697 AudioObjectPropertyAddress propAddress = {
698 kAudioDevicePropertyDeviceHasChanged,
699 kAudioObjectPropertyScopeGlobal,
700 kAudioObjectPropertyElementMaster
703 /* Install the property listener */
704 status = AudioObjectAddPropertyListener (core_audio->device_id,
705 &propAddress, _audio_stream_hardware_changed_listener,
706 (void *) core_audio);
707 if (status != noErr) {
708 GST_ERROR_OBJECT (core_audio->osxbuf,
709 "AudioObjectAddPropertyListener failed: %d", (int) status);
716 static inline gboolean
717 _unmonitorize_spdif (GstCoreAudio * core_audio)
719 OSStatus status = noErr;
722 AudioObjectPropertyAddress propAddress = {
723 kAudioDevicePropertyDeviceHasChanged,
724 kAudioObjectPropertyScopeGlobal,
725 kAudioObjectPropertyElementMaster
728 /* Remove the property listener */
729 status = AudioObjectRemovePropertyListener (core_audio->device_id,
730 &propAddress, _audio_stream_hardware_changed_listener,
731 (void *) core_audio);
732 if (status != noErr) {
733 GST_ERROR_OBJECT (core_audio->osxbuf,
734 "AudioObjectRemovePropertyListener failed: %d", (int) status);
741 static inline gboolean
742 _open_spdif (GstCoreAudio * core_audio)
744 gboolean res = FALSE;
745 pid_t hog_pid, own_pid = getpid ();
747 /* We need the device in exclusive and disable the mixing */
748 hog_pid = _audio_device_get_hog (core_audio->device_id);
750 if (hog_pid != -1 && hog_pid != own_pid) {
751 GST_DEBUG_OBJECT (core_audio,
752 "device is currently in use by another application");
756 if (_audio_device_set_hog (core_audio->device_id, own_pid)) {
757 core_audio->hog_pid = own_pid;
760 if (_audio_device_set_mixing (core_audio->device_id, FALSE)) {
761 GST_DEBUG_OBJECT (core_audio, "disabled mixing on the device");
762 core_audio->disabled_mixing = TRUE;
770 static inline gboolean
771 _close_spdif (GstCoreAudio * core_audio)
775 _unmonitorize_spdif (core_audio);
777 if (core_audio->revert_format) {
778 if (!_audio_stream_change_format (core_audio->stream_id,
779 core_audio->original_format)) {
780 GST_WARNING_OBJECT (core_audio->osxbuf, "Format revert failed");
782 core_audio->revert_format = FALSE;
785 if (core_audio->disabled_mixing) {
786 _audio_device_set_mixing (core_audio->device_id, TRUE);
787 core_audio->disabled_mixing = FALSE;
790 if (core_audio->hog_pid != -1) {
791 hog_pid = _audio_device_get_hog (core_audio->device_id);
792 if (hog_pid == getpid ()) {
793 if (_audio_device_set_hog (core_audio->device_id, -1)) {
794 core_audio->hog_pid = -1;
803 _io_proc_spdif (AudioDeviceID inDevice,
804 const AudioTimeStamp * inNow,
805 const void *inInputData,
806 const AudioTimeStamp * inTimestamp,
807 AudioBufferList * bufferList,
808 const AudioTimeStamp * inOutputTime, GstCoreAudio * core_audio)
812 status = core_audio->element->io_proc (core_audio->osxbuf, NULL, inTimestamp,
818 static inline gboolean
819 _acquire_spdif (GstCoreAudio * core_audio, AudioStreamBasicDescription format)
821 AudioStreamID *streams = NULL;
822 gint i, j, nstreams = 0;
823 gboolean ret = FALSE;
825 if (!_open_spdif (core_audio))
828 streams = _audio_device_get_streams (core_audio->device_id, &nstreams);
830 for (i = 0; i < nstreams; i++) {
831 AudioStreamRangedDescription *formats = NULL;
834 formats = _audio_stream_get_formats (streams[i], &nformats);
837 gboolean is_spdif = FALSE;
839 /* Check if one of the supported formats is a digital format */
840 for (j = 0; j < nformats; j++) {
841 if (CORE_AUDIO_FORMAT_IS_SPDIF (formats[j])) {
848 /* if this stream supports a digital (cac3) format,
850 gint requested_rate_format = -1;
851 gint current_rate_format = -1;
852 gint backup_rate_format = -1;
854 core_audio->stream_id = streams[i];
855 core_audio->stream_idx = i;
857 if (!core_audio->revert_format) {
858 if (!_audio_stream_get_current_format (core_audio->stream_id,
859 &core_audio->original_format)) {
860 GST_WARNING_OBJECT (core_audio->osxbuf,
861 "format could not be saved");
865 core_audio->revert_format = TRUE;
868 for (j = 0; j < nformats; j++) {
869 if (CORE_AUDIO_FORMAT_IS_SPDIF (formats[j])) {
870 GST_LOG_OBJECT (core_audio->osxbuf,
871 "found stream format: " CORE_AUDIO_FORMAT,
872 CORE_AUDIO_FORMAT_ARGS (formats[j].mFormat));
874 if (formats[j].mFormat.mSampleRate == format.mSampleRate) {
875 requested_rate_format = j;
877 } else if (formats[j].mFormat.mSampleRate ==
878 core_audio->original_format.mSampleRate) {
879 current_rate_format = j;
881 if (backup_rate_format < 0 ||
882 formats[j].mFormat.mSampleRate >
883 formats[backup_rate_format].mFormat.mSampleRate) {
884 backup_rate_format = j;
890 if (requested_rate_format >= 0) {
891 /* We prefer to output at the rate of the original audio */
892 core_audio->stream_format = formats[requested_rate_format].mFormat;
893 } else if (current_rate_format >= 0) {
894 /* If not possible, we will try to use the current rate */
895 core_audio->stream_format = formats[current_rate_format].mFormat;
897 /* And if we have to, any digital format will be just
898 * fine (highest rate possible) */
899 core_audio->stream_format = formats[backup_rate_format].mFormat;
907 GST_DEBUG_OBJECT (core_audio,
908 "original stream format: " CORE_AUDIO_FORMAT,
909 CORE_AUDIO_FORMAT_ARGS (core_audio->original_format));
911 if (!_audio_stream_change_format (core_audio->stream_id,
912 core_audio->stream_format))
922 _remove_render_spdif_callback (GstCoreAudio * core_audio)
926 /* Deactivate the render callback by calling
927 * AudioDeviceDestroyIOProcID */
929 AudioDeviceDestroyIOProcID (core_audio->device_id, core_audio->procID);
930 if (status != noErr) {
931 GST_ERROR_OBJECT (core_audio->osxbuf,
932 "AudioDeviceDestroyIOProcID failed: %d", (int) status);
935 GST_DEBUG_OBJECT (core_audio,
936 "osx ring buffer removed ioproc ID: %p device_id %lu",
937 core_audio->procID, (gulong) core_audio->device_id);
939 /* We're deactivated.. */
940 core_audio->procID = 0;
941 core_audio->io_proc_needs_deactivation = FALSE;
942 core_audio->io_proc_active = FALSE;
945 static inline gboolean
946 _io_proc_spdif_start (GstCoreAudio * core_audio)
950 GST_DEBUG_OBJECT (core_audio,
951 "osx ring buffer start ioproc ID: %p device_id %lu",
952 core_audio->procID, (gulong) core_audio->device_id);
954 if (!core_audio->io_proc_active) {
955 /* Add IOProc callback */
956 status = AudioDeviceCreateIOProcID (core_audio->device_id,
957 (AudioDeviceIOProc) _io_proc_spdif,
958 (void *) core_audio, &core_audio->procID);
959 if (status != noErr) {
960 GST_ERROR_OBJECT (core_audio->osxbuf,
961 ":AudioDeviceCreateIOProcID failed: %d", (int) status);
964 core_audio->io_proc_active = TRUE;
967 core_audio->io_proc_needs_deactivation = FALSE;
970 status = AudioDeviceStart (core_audio->device_id, core_audio->procID);
971 if (status != noErr) {
972 GST_ERROR_OBJECT (core_audio->osxbuf,
973 "AudioDeviceStart failed: %d", (int) status);
979 static inline gboolean
980 _io_proc_spdif_stop (GstCoreAudio * core_audio)
985 status = AudioDeviceStop (core_audio->device_id, core_audio->procID);
986 if (status != noErr) {
987 GST_ERROR_OBJECT (core_audio->osxbuf,
988 "AudioDeviceStop failed: %d", (int) status);
991 GST_DEBUG_OBJECT (core_audio,
992 "osx ring buffer stop ioproc ID: %p device_id %lu",
993 core_audio->procID, (gulong) core_audio->device_id);
995 if (core_audio->io_proc_active) {
996 _remove_render_spdif_callback (core_audio);
999 _close_spdif (core_audio);
1005 /***********************
1007 **********************/
1010 gst_core_audio_open_impl (GstCoreAudio * core_audio)
1014 /* The following is needed to instruct HAL to create their own
1015 * thread to handle the notifications. */
1016 _audio_system_set_runloop (NULL);
1018 /* Create a HALOutput AudioUnit.
1019 * This is the lowest-level output API that is actually sensibly
1020 * usable (the lower level ones require that you do
1021 * channel-remapping yourself, and the CoreAudio channel mapping
1022 * is sufficiently complex that doing so would be very difficult)
1024 * Note that for input we request an output unit even though
1025 * we will do input with it.
1026 * http://developer.apple.com/technotes/tn2002/tn2091.html
1028 ret = gst_core_audio_open_device (core_audio, kAudioUnitSubType_HALOutput,
1031 GST_DEBUG ("Could not open device");
1035 ret = gst_core_audio_bind_device (core_audio);
1037 GST_DEBUG ("Could not bind device");
1046 gst_core_audio_start_processing_impl (GstCoreAudio * core_audio)
1048 if (core_audio->is_passthrough) {
1049 return _io_proc_spdif_start (core_audio);
1051 return gst_core_audio_io_proc_start (core_audio);
1056 gst_core_audio_pause_processing_impl (GstCoreAudio * core_audio)
1058 if (core_audio->is_passthrough) {
1059 GST_DEBUG_OBJECT (core_audio,
1060 "osx ring buffer pause ioproc ID: %p device_id %lu",
1061 core_audio->procID, (gulong) core_audio->device_id);
1063 if (core_audio->io_proc_active) {
1064 _remove_render_spdif_callback (core_audio);
1067 GST_DEBUG_OBJECT (core_audio,
1068 "osx ring buffer pause ioproc: %p device_id %lu",
1069 core_audio->element->io_proc, (gulong) core_audio->device_id);
1070 if (core_audio->io_proc_active) {
1071 /* CoreAudio isn't threadsafe enough to do this here;
1072 * we must deactivate the render callback elsewhere. See:
1073 * http://lists.apple.com/archives/Coreaudio-api/2006/Mar/msg00010.html
1075 core_audio->io_proc_needs_deactivation = TRUE;
1082 gst_core_audio_stop_processing_impl (GstCoreAudio * core_audio)
1084 if (core_audio->is_passthrough) {
1085 _io_proc_spdif_stop (core_audio);
1087 gst_core_audio_io_proc_stop (core_audio);
1094 gst_core_audio_get_samples_and_latency_impl (GstCoreAudio * core_audio,
1095 gdouble rate, guint * samples, gdouble * latency)
1098 UInt32 size = sizeof (double);
1100 if (core_audio->is_passthrough) {
1101 *samples = _audio_device_get_latency (core_audio->device_id);
1102 *samples += _audio_stream_get_latency (core_audio->stream_id);
1103 *latency = (double) *samples / rate;
1105 status = AudioUnitGetProperty (core_audio->audiounit, kAudioUnitProperty_Latency, kAudioUnitScope_Global, 0, /* N/A for global */
1109 GST_WARNING_OBJECT (core_audio->osxbuf, "Failed to get latency: %d",
1115 *samples = *latency * rate;
1121 gst_core_audio_initialize_impl (GstCoreAudio * core_audio,
1122 AudioStreamBasicDescription format, GstCaps * caps,
1123 gboolean is_passthrough, guint32 * frame_size)
1125 gboolean ret = FALSE;
1128 /* Uninitialize the AudioUnit before changing formats */
1129 status = AudioUnitUninitialize (core_audio->audiounit);
1131 GST_ERROR_OBJECT (core_audio, "Failed to uninitialize AudioUnit: %d",
1136 core_audio->is_passthrough = is_passthrough;
1137 if (is_passthrough) {
1138 if (!_acquire_spdif (core_audio, format))
1140 _monitorize_spdif (core_audio);
1143 UInt32 propertySize;
1145 core_audio->stream_idx = 0;
1146 if (!gst_core_audio_set_format (core_audio, format))
1149 if (!gst_core_audio_set_channel_layout (core_audio,
1150 format.mChannelsPerFrame, caps))
1153 if (core_audio->is_src) {
1154 propertySize = sizeof (*frame_size);
1155 status = AudioUnitGetProperty (core_audio->audiounit, kAudioDevicePropertyBufferFrameSize, kAudioUnitScope_Global, 0, /* N/A for global */
1156 frame_size, &propertySize);
1159 GST_WARNING_OBJECT (core_audio->osxbuf, "Failed to get frame size: %d",
1169 /* Format changed, initialise the AudioUnit again */
1170 status = AudioUnitInitialize (core_audio->audiounit);
1172 GST_ERROR_OBJECT (core_audio, "Failed to initialize AudioUnit: %d",
1178 GST_DEBUG_OBJECT (core_audio, "osxbuf ring buffer acquired");
1185 gst_core_audio_select_device_impl (GstCoreAudio * core_audio)
1187 AudioDeviceID *devices = NULL;
1188 AudioDeviceID device_id = core_audio->device_id;
1189 AudioDeviceID default_device_id = 0;
1190 gint i, ndevices = 0;
1191 gboolean output = !core_audio->is_src;
1192 gboolean res = FALSE;
1193 #ifdef GST_CORE_AUDIO_DEBUG
1194 AudioChannelLayout *channel_layout;
1197 devices = _audio_system_get_devices (&ndevices);
1200 GST_ERROR ("no audio output devices found");
1204 GST_DEBUG ("found %d audio device(s)", ndevices);
1206 #ifdef GST_CORE_AUDIO_DEBUG
1207 for (i = 0; i < ndevices; i++) {
1210 if ((device_name = _audio_device_get_name (devices[i], output))) {
1211 if (!_audio_device_has_output (devices[i])) {
1212 GST_DEBUG ("Input Device ID: %u Name: %s",
1213 (unsigned) devices[i], device_name);
1215 GST_DEBUG ("Output Device ID: %u Name: %s",
1216 (unsigned) devices[i], device_name);
1219 gst_core_audio_audio_device_get_channel_layout (devices[i], output);
1220 if (channel_layout) {
1221 gst_core_audio_dump_channel_layout (channel_layout);
1222 g_free (channel_layout);
1226 g_free (device_name);
1231 /* Find the ID of the default output device */
1232 default_device_id = _audio_system_get_default_device (output);
1234 /* Here we decide if selected device is valid or autoselect
1235 * the default one when required */
1236 if (device_id == kAudioDeviceUnknown) {
1237 if (default_device_id != kAudioDeviceUnknown) {
1238 device_id = default_device_id;
1241 GST_ERROR ("No device of required type available");
1245 for (i = 0; i < ndevices; i++) {
1246 if (device_id == devices[i]) {
1251 if (res && !_audio_device_is_alive (device_id, output)) {
1252 GST_ERROR ("Requested device not usable");
1259 core_audio->device_id = device_id;
1267 gst_core_audio_audio_device_is_spdif_avail_impl (AudioDeviceID device_id)
1269 AudioStreamID *streams = NULL;
1270 gint i, nstreams = 0;
1271 gboolean res = FALSE;
1273 streams = _audio_device_get_streams (device_id, &nstreams);
1274 GST_DEBUG ("found %d streams", nstreams);
1276 for (i = 0; i < nstreams; i++) {
1277 if (_audio_stream_is_spdif_avail (streams[i])) {