osxaudiosink: fix segfault when we can't get the channels layout
[platform/upstream/gst-plugins-good.git] / sys / osxaudio / gstosxcoreaudiohal.c
1 /*
2  * GStreamer
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>
6  *
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.
11  *
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.
16  *
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.
21  *
22  */
23
24 #include <unistd.h>             /* for getpid */
25 #include "gstosxaudiosink.h"
26
27 static inline gboolean
28 _audio_system_set_runloop (CFRunLoopRef runLoop)
29 {
30   OSStatus status = noErr;
31
32   gboolean res = FALSE;
33
34   AudioObjectPropertyAddress runloopAddress = {
35     kAudioHardwarePropertyRunLoop,
36     kAudioObjectPropertyScopeGlobal,
37     kAudioObjectPropertyElementMaster
38   };
39
40   status = AudioObjectSetPropertyData (kAudioObjectSystemObject,
41       &runloopAddress, 0, NULL, sizeof (CFRunLoopRef), &runLoop);
42   if (status == noErr) {
43     res = TRUE;
44   } else {
45     GST_ERROR ("failed to set runloop to %p: %" GST_FOURCC_FORMAT,
46         runLoop, GST_FOURCC_ARGS (status));
47   }
48
49   return res;
50 }
51
52 static inline AudioDeviceID
53 _audio_system_get_default_output (void)
54 {
55   OSStatus status = noErr;
56   UInt32 propertySize = sizeof (AudioDeviceID);
57   AudioDeviceID device_id = kAudioDeviceUnknown;
58
59   AudioObjectPropertyAddress defaultDeviceAddress = {
60     kAudioHardwarePropertyDefaultOutputDevice,
61     kAudioDevicePropertyScopeOutput,
62     kAudioObjectPropertyElementMaster
63   };
64
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));
70   }
71
72   return device_id;
73 }
74
75 static inline AudioDeviceID *
76 _audio_system_get_devices (gint * ndevices)
77 {
78   OSStatus status = noErr;
79   UInt32 propertySize = 0;
80   AudioDeviceID *devices = NULL;
81
82   AudioObjectPropertyAddress audioDevicesAddress = {
83     kAudioHardwarePropertyDevices,
84     kAudioDevicePropertyScopeOutput,
85     kAudioObjectPropertyElementMaster
86   };
87
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));
93     return NULL;
94   }
95
96   *ndevices = propertySize / sizeof (AudioDeviceID);
97
98   devices = (AudioDeviceID *) g_malloc (propertySize);
99   if (devices) {
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));
105       g_free (devices);
106       *ndevices = 0;
107       return NULL;
108     }
109   }
110   return devices;
111 }
112
113 static inline gboolean
114 _audio_device_is_alive (AudioDeviceID device_id)
115 {
116   OSStatus status = noErr;
117   int alive = FALSE;
118   UInt32 propertySize = sizeof (alive);
119
120   AudioObjectPropertyAddress audioDeviceAliveAddress = {
121     kAudioDevicePropertyDeviceIsAlive,
122     kAudioDevicePropertyScopeOutput,
123     kAudioObjectPropertyElementMaster
124   };
125
126   status = AudioObjectGetPropertyData (device_id,
127       &audioDeviceAliveAddress, 0, NULL, &propertySize, &alive);
128   if (status != noErr) {
129     alive = FALSE;
130   }
131
132   return alive;
133 }
134
135 static inline guint
136 _audio_device_get_latency (AudioDeviceID device_id)
137 {
138   OSStatus status = noErr;
139   UInt32 latency = 0;
140   UInt32 propertySize = sizeof (latency);
141
142   AudioObjectPropertyAddress audioDeviceLatencyAddress = {
143     kAudioDevicePropertyLatency,
144     kAudioDevicePropertyScopeOutput,
145     kAudioObjectPropertyElementMaster
146   };
147
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));
153     latency = -1;
154   }
155
156   return latency;
157 }
158
159 static inline pid_t
160 _audio_device_get_hog (AudioDeviceID device_id)
161 {
162   OSStatus status = noErr;
163   pid_t hog_pid;
164   UInt32 propertySize = sizeof (hog_pid);
165
166   AudioObjectPropertyAddress audioDeviceHogModeAddress = {
167     kAudioDevicePropertyHogMode,
168     kAudioDevicePropertyScopeOutput,
169     kAudioObjectPropertyElementMaster
170   };
171
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));
177     hog_pid = -1;
178   }
179
180   return hog_pid;
181 }
182
183 static inline gboolean
184 _audio_device_set_hog (AudioDeviceID device_id, pid_t hog_pid)
185 {
186   OSStatus status = noErr;
187   UInt32 propertySize = sizeof (hog_pid);
188   gboolean res = FALSE;
189
190   AudioObjectPropertyAddress audioDeviceHogModeAddress = {
191     kAudioDevicePropertyHogMode,
192     kAudioDevicePropertyScopeOutput,
193     kAudioObjectPropertyElementMaster
194   };
195
196   status = AudioObjectSetPropertyData (device_id,
197       &audioDeviceHogModeAddress, 0, NULL, propertySize, &hog_pid);
198
199   if (status == noErr) {
200     res = TRUE;
201   } else {
202     GST_ERROR ("failed to set hog: %" GST_FOURCC_FORMAT,
203         GST_FOURCC_ARGS (status));
204   }
205
206   return res;
207 }
208
209 static inline gboolean
210 _audio_device_set_mixing (AudioDeviceID device_id, gboolean enable_mix)
211 {
212   OSStatus status = noErr;
213   UInt32 propertySize = 0, can_mix = enable_mix;
214   Boolean writable = FALSE;
215   gboolean res = FALSE;
216
217   AudioObjectPropertyAddress audioDeviceSupportsMixingAddress = {
218     kAudioDevicePropertySupportsMixing,
219     kAudioObjectPropertyScopeGlobal,
220     kAudioObjectPropertyElementMaster
221   };
222
223   if (AudioObjectHasProperty (device_id, &audioDeviceSupportsMixingAddress)) {
224     /* Set mixable to false if we are allowed to */
225     status = AudioObjectIsPropertySettable (device_id,
226         &audioDeviceSupportsMixingAddress, &writable);
227     if (status) {
228       GST_DEBUG ("AudioObjectIsPropertySettable: %" GST_FOURCC_FORMAT,
229           GST_FOURCC_ARGS (status));
230     }
231     status = AudioObjectGetPropertyDataSize (device_id,
232         &audioDeviceSupportsMixingAddress, 0, NULL, &propertySize);
233     if (status) {
234       GST_DEBUG ("AudioObjectGetPropertyDataSize: %" GST_FOURCC_FORMAT,
235           GST_FOURCC_ARGS (status));
236     }
237     status = AudioObjectGetPropertyData (device_id,
238         &audioDeviceSupportsMixingAddress, 0, NULL, &propertySize, &can_mix);
239     if (status) {
240       GST_DEBUG ("AudioObjectGetPropertyData: %" GST_FOURCC_FORMAT,
241           GST_FOURCC_ARGS (status));
242     }
243
244     if (status == noErr && writable) {
245       can_mix = enable_mix;
246       status = AudioObjectSetPropertyData (device_id,
247           &audioDeviceSupportsMixingAddress, 0, NULL, propertySize, &can_mix);
248       res = TRUE;
249     }
250
251     if (status != noErr) {
252       GST_ERROR ("failed to set mixmode: %" GST_FOURCC_FORMAT,
253           GST_FOURCC_ARGS (status));
254     }
255   } else {
256     GST_DEBUG ("property not found, mixing coudln't be changed");
257   }
258
259   return res;
260 }
261
262 static inline gchar *
263 _audio_device_get_name (AudioDeviceID device_id)
264 {
265   OSStatus status = noErr;
266   UInt32 propertySize = 0;
267   gchar *device_name = NULL;
268
269   AudioObjectPropertyAddress deviceNameAddress = {
270     kAudioDevicePropertyDeviceName,
271     kAudioDevicePropertyScopeOutput,
272     kAudioObjectPropertyElementMaster
273   };
274
275   /* Get the length of the device name */
276   status = AudioObjectGetPropertyDataSize (device_id,
277       &deviceNameAddress, 0, NULL, &propertySize);
278   if (status != noErr) {
279     goto beach;
280   }
281
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);
288     device_name = NULL;
289   }
290
291 beach:
292   return device_name;
293 }
294
295 static inline gboolean
296 _audio_device_has_output (AudioDeviceID device_id)
297 {
298   OSStatus status = noErr;
299   UInt32 propertySize;
300
301   AudioObjectPropertyAddress streamsAddress = {
302     kAudioDevicePropertyStreams,
303     kAudioDevicePropertyScopeOutput,
304     kAudioObjectPropertyElementMaster
305   };
306
307   status = AudioObjectGetPropertyDataSize (device_id,
308       &streamsAddress, 0, NULL, &propertySize);
309   if (status != noErr) {
310     return FALSE;
311   }
312   if (propertySize == 0) {
313     return FALSE;
314   }
315
316   return TRUE;
317 }
318
319 AudioChannelLayout *
320 gst_core_audio_audio_device_get_channel_layout (AudioDeviceID device_id)
321 {
322   OSStatus status = noErr;
323   UInt32 propertySize = 0;
324   AudioChannelLayout *layout = NULL;
325
326   AudioObjectPropertyAddress channelLayoutAddress = {
327     kAudioDevicePropertyPreferredChannelLayout,
328     kAudioDevicePropertyScopeOutput,
329     kAudioObjectPropertyElementMaster
330   };
331
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));
338     goto beach;
339   }
340
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));
348     goto failed;
349   }
350
351   if (layout->mChannelLayoutTag == kAudioChannelLayoutTag_UseChannelBitmap) {
352     /* bitmap defined channellayout */
353     status =
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));
359       goto failed;
360     }
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));
370       goto failed;
371     }
372   }
373
374   gst_core_audio_dump_channel_layout (layout);
375
376 beach:
377   return layout;
378
379 failed:
380   g_free (layout);
381   return NULL;
382 }
383
384 static inline AudioStreamID *
385 _audio_device_get_streams (AudioDeviceID device_id, gint * nstreams)
386 {
387   OSStatus status = noErr;
388   UInt32 propertySize = 0;
389   AudioStreamID *streams = NULL;
390
391   AudioObjectPropertyAddress streamsAddress = {
392     kAudioDevicePropertyStreams,
393     kAudioDevicePropertyScopeOutput,
394     kAudioObjectPropertyElementMaster
395   };
396
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));
402     return NULL;
403   }
404
405   *nstreams = propertySize / sizeof (AudioStreamID);
406   streams = (AudioStreamID *) g_malloc (propertySize);
407
408   if (streams) {
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));
414       g_free (streams);
415       *nstreams = 0;
416       return NULL;
417     }
418   }
419
420   return streams;
421 }
422
423 static inline guint
424 _audio_stream_get_latency (AudioStreamID stream_id)
425 {
426   OSStatus status = noErr;
427   UInt32 latency;
428   UInt32 propertySize = sizeof (latency);
429
430   AudioObjectPropertyAddress latencyAddress = {
431     kAudioStreamPropertyLatency,
432     kAudioObjectPropertyScopeGlobal,
433     kAudioObjectPropertyElementMaster
434   };
435
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));
441     latency = -1;
442   }
443
444   return latency;
445 }
446
447 static inline gboolean
448 _audio_stream_get_current_format (AudioStreamID stream_id,
449     AudioStreamBasicDescription * format)
450 {
451   OSStatus status = noErr;
452   UInt32 propertySize = sizeof (AudioStreamBasicDescription);
453
454   AudioObjectPropertyAddress formatAddress = {
455     kAudioStreamPropertyPhysicalFormat,
456     kAudioObjectPropertyScopeGlobal,
457     kAudioObjectPropertyElementMaster
458   };
459
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));
465     return FALSE;
466   }
467
468   return TRUE;
469 }
470
471 static inline gboolean
472 _audio_stream_set_current_format (AudioStreamID stream_id,
473     AudioStreamBasicDescription format)
474 {
475   OSStatus status = noErr;
476   UInt32 propertySize = sizeof (AudioStreamBasicDescription);
477
478   AudioObjectPropertyAddress formatAddress = {
479     kAudioStreamPropertyPhysicalFormat,
480     kAudioObjectPropertyScopeGlobal,
481     kAudioObjectPropertyElementMaster
482   };
483
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));
489     return FALSE;
490   }
491
492   return TRUE;
493 }
494
495 static inline AudioStreamRangedDescription *
496 _audio_stream_get_formats (AudioStreamID stream_id, gint * nformats)
497 {
498   OSStatus status = noErr;
499   UInt32 propertySize = 0;
500   AudioStreamRangedDescription *formats = NULL;
501
502   AudioObjectPropertyAddress formatsAddress = {
503     kAudioStreamPropertyAvailablePhysicalFormats,
504     kAudioObjectPropertyScopeGlobal,
505     kAudioObjectPropertyElementMaster
506   };
507
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));
513     return NULL;
514   }
515
516   *nformats = propertySize / sizeof (AudioStreamRangedDescription);
517
518   formats = (AudioStreamRangedDescription *) g_malloc (propertySize);
519   if (formats) {
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));
525       g_free (formats);
526       *nformats = 0;
527       return NULL;
528     }
529   }
530   return formats;
531 }
532
533 static inline gboolean
534 _audio_stream_is_spdif_avail (AudioStreamID stream_id)
535 {
536   AudioStreamRangedDescription *formats;
537   gint i, nformats = 0;
538   gboolean res = FALSE;
539
540   formats = _audio_stream_get_formats (stream_id, &nformats);
541   GST_DEBUG ("found %d stream formats", nformats);
542
543   if (formats) {
544     GST_DEBUG ("formats supported on stream ID: %u", (unsigned) stream_id);
545
546     for (i = 0; i < nformats; i++) {
547       GST_DEBUG ("  " CORE_AUDIO_FORMAT,
548           CORE_AUDIO_FORMAT_ARGS (formats[i].mFormat));
549
550       if (CORE_AUDIO_FORMAT_IS_SPDIF (formats[i])) {
551         res = TRUE;
552       }
553     }
554     g_free (formats);
555   }
556
557   return res;
558 }
559
560 static OSStatus
561 _audio_stream_format_listener (AudioObjectID inObjectID,
562     UInt32 inNumberAddresses,
563     const AudioObjectPropertyAddress inAddresses[], void *inClientData)
564 {
565   OSStatus status = noErr;
566   guint i;
567   PropertyMutex *prop_mutex = inClientData;
568
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);
574       break;
575     }
576   }
577   return (status);
578 }
579
580 static gboolean
581 _audio_stream_change_format (AudioStreamID stream_id,
582     AudioStreamBasicDescription format)
583 {
584   OSStatus status = noErr;
585   gint i;
586   gboolean ret = FALSE;
587   AudioStreamBasicDescription cformat;
588   PropertyMutex prop_mutex;
589
590   AudioObjectPropertyAddress formatAddress = {
591     kAudioStreamPropertyPhysicalFormat,
592     kAudioObjectPropertyScopeGlobal,
593     kAudioObjectPropertyElementMaster
594   };
595
596   GST_DEBUG ("setting stream format: " CORE_AUDIO_FORMAT,
597       CORE_AUDIO_FORMAT_ARGS (format));
598
599   /* Condition because SetProperty is asynchronous */
600   g_mutex_init (&prop_mutex.lock);
601   g_cond_init (&prop_mutex.cond);
602
603   g_mutex_lock (&prop_mutex.lock);
604
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));
611     goto done;
612   }
613
614   /* Change the format */
615   if (!_audio_stream_set_current_format (stream_id, format)) {
616     goto done;
617   }
618
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++) {
623     GTimeVal timeout;
624
625     g_get_current_time (&timeout);
626     g_time_val_add (&timeout, 250000);
627
628     if (!g_cond_wait_until (&prop_mutex.cond, &prop_mutex.lock, timeout.tv_sec)) {
629       GST_LOG ("timeout...");
630     }
631
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));
635
636       if (cformat.mSampleRate == format.mSampleRate &&
637           cformat.mFormatID == format.mFormatID &&
638           cformat.mFramesPerPacket == format.mFramesPerPacket) {
639         /* The right format is now active */
640         break;
641       }
642     }
643   }
644
645   if (cformat.mSampleRate != format.mSampleRate ||
646       cformat.mFormatID != format.mFormatID ||
647       cformat.mFramesPerPacket != format.mFramesPerPacket) {
648     goto done;
649   }
650
651   ret = TRUE;
652
653 done:
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));
660   }
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);
665
666   return ret;
667 }
668
669 static OSStatus
670 _audio_stream_hardware_changed_listener (AudioObjectID inObjectID,
671     UInt32 inNumberAddresses,
672     const AudioObjectPropertyAddress inAddresses[], void *inClientData)
673 {
674   OSStatus status = noErr;
675   guint i;
676   GstCoreAudio *core_audio = inClientData;
677
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"));
686       }
687       break;
688     }
689   }
690   return (status);
691 }
692
693 static inline gboolean
694 _monitorize_spdif (GstCoreAudio * core_audio)
695 {
696   OSStatus status = noErr;
697   gboolean ret = TRUE;
698
699   AudioObjectPropertyAddress propAddress = {
700     kAudioDevicePropertyDeviceHasChanged,
701     kAudioObjectPropertyScopeGlobal,
702     kAudioObjectPropertyElementMaster
703   };
704
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));
713     ret = FALSE;
714   }
715
716   return ret;
717 }
718
719 static inline gboolean
720 _unmonitorize_spdif (GstCoreAudio * core_audio)
721 {
722   OSStatus status = noErr;
723   gboolean ret = TRUE;
724
725   AudioObjectPropertyAddress propAddress = {
726     kAudioDevicePropertyDeviceHasChanged,
727     kAudioObjectPropertyScopeGlobal,
728     kAudioObjectPropertyElementMaster
729   };
730
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));
739     ret = FALSE;
740   }
741
742   return ret;
743 }
744
745 static inline gboolean
746 _open_spdif (GstCoreAudio * core_audio)
747 {
748   gboolean res = FALSE;
749   pid_t hog_pid, own_pid = getpid ();
750
751   /* We need the device in exclusive and disable the mixing */
752   hog_pid = _audio_device_get_hog (core_audio->device_id);
753
754   if (hog_pid != -1 && hog_pid != own_pid) {
755     GST_DEBUG_OBJECT (core_audio,
756         "device is currently in use by another application");
757     goto done;
758   }
759
760   if (_audio_device_set_hog (core_audio->device_id, own_pid)) {
761     core_audio->hog_pid = own_pid;
762   }
763
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;
767   }
768
769   res = TRUE;
770 done:
771   return res;
772 }
773
774 static inline gboolean
775 _close_spdif (GstCoreAudio * core_audio)
776 {
777   pid_t hog_pid;
778
779   _unmonitorize_spdif (core_audio);
780
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");
785     }
786     core_audio->revert_format = FALSE;
787   }
788
789   if (core_audio->disabled_mixing) {
790     _audio_device_set_mixing (core_audio->device_id, TRUE);
791     core_audio->disabled_mixing = FALSE;
792   }
793
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;
799       }
800     }
801   }
802
803   return TRUE;
804 }
805
806 static OSStatus
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)
813 {
814   OSStatus status;
815
816   status = core_audio->element->io_proc (core_audio->osxbuf, NULL, inTimestamp,
817       0, 0, bufferList);
818
819   return status;
820 }
821
822 static inline gboolean
823 _acquire_spdif (GstCoreAudio * core_audio, AudioStreamBasicDescription format)
824 {
825   AudioStreamID *streams = NULL;
826   gint i, j, nstreams = 0;
827   gboolean ret = FALSE;
828
829   if (!_open_spdif (core_audio))
830     goto done;
831
832   streams = _audio_device_get_streams (core_audio->device_id, &nstreams);
833
834   for (i = 0; i < nstreams; i++) {
835     AudioStreamRangedDescription *formats = NULL;
836     gint nformats = 0;
837
838     formats = _audio_stream_get_formats (streams[i], &nformats);
839
840     if (formats) {
841       gboolean is_spdif = FALSE;
842
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])) {
846           is_spdif = TRUE;
847           break;
848         }
849       }
850
851       if (is_spdif) {
852         /* if this stream supports a digital (cac3) format,
853          * then go set it. */
854         gint requested_rate_format = -1;
855         gint current_rate_format = -1;
856         gint backup_rate_format = -1;
857
858         core_audio->stream_id = streams[i];
859         core_audio->stream_idx = i;
860
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");
866             g_free (formats);
867             continue;
868           }
869           core_audio->revert_format = TRUE;
870         }
871
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));
877
878             if (formats[j].mFormat.mSampleRate == format.mSampleRate) {
879               requested_rate_format = j;
880               break;
881             } else if (formats[j].mFormat.mSampleRate ==
882                 core_audio->original_format.mSampleRate) {
883               current_rate_format = j;
884             } else {
885               if (backup_rate_format < 0 ||
886                   formats[j].mFormat.mSampleRate >
887                   formats[backup_rate_format].mFormat.mSampleRate) {
888                 backup_rate_format = j;
889               }
890             }
891           }
892         }
893
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;
900         } else {
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;
904         }
905       }
906       g_free (formats);
907     }
908   }
909   g_free (streams);
910
911   GST_DEBUG_OBJECT (core_audio,
912       "original stream format: " CORE_AUDIO_FORMAT,
913       CORE_AUDIO_FORMAT_ARGS (core_audio->original_format));
914
915   if (!_audio_stream_change_format (core_audio->stream_id,
916           core_audio->stream_format))
917     goto done;
918
919   ret = TRUE;
920
921 done:
922   return ret;
923 }
924
925 static inline void
926 _remove_render_spdif_callback (GstCoreAudio * core_audio)
927 {
928   OSStatus status;
929
930   /* Deactivate the render callback by calling
931    * AudioDeviceDestroyIOProcID */
932   status =
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));
938   }
939
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);
943
944   /* We're deactivated.. */
945   core_audio->procID = 0;
946   core_audio->io_proc_needs_deactivation = FALSE;
947   core_audio->io_proc_active = FALSE;
948 }
949
950 static inline gboolean
951 _io_proc_spdif_start (GstCoreAudio * core_audio)
952 {
953   OSErr status;
954
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);
958
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));
968       return FALSE;
969     }
970     core_audio->io_proc_active = TRUE;
971   }
972
973   core_audio->io_proc_needs_deactivation = FALSE;
974
975   /* Start device */
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));
981     return FALSE;
982   }
983   return TRUE;
984 }
985
986 static inline gboolean
987 _io_proc_spdif_stop (GstCoreAudio * core_audio)
988 {
989   OSErr status;
990
991   /* Stop device */
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));
997   }
998
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);
1002
1003   if (core_audio->io_proc_active) {
1004     _remove_render_spdif_callback (core_audio);
1005   }
1006
1007   _close_spdif (core_audio);
1008
1009   return TRUE;
1010 }
1011
1012
1013 /***********************
1014  *   Implementation    *
1015  **********************/
1016
1017 static gboolean
1018 gst_core_audio_open_impl (GstCoreAudio * core_audio)
1019 {
1020   /* The following is needed to instruct HAL to create their own
1021    * thread to handle the notifications. */
1022   _audio_system_set_runloop (NULL);
1023
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)
1029    *
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
1033    */
1034   return gst_core_audio_open_device (core_audio, kAudioUnitSubType_HALOutput,
1035       "HALOutput");
1036 }
1037
1038 static gboolean
1039 gst_core_audio_start_processing_impl (GstCoreAudio * core_audio)
1040 {
1041   if (core_audio->is_passthrough) {
1042     return _io_proc_spdif_start (core_audio);
1043   } else {
1044     return gst_core_audio_io_proc_start (core_audio);
1045   }
1046 }
1047
1048 static gboolean
1049 gst_core_audio_pause_processing_impl (GstCoreAudio * core_audio)
1050 {
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);
1055
1056     if (core_audio->io_proc_active) {
1057       _remove_render_spdif_callback (core_audio);
1058     }
1059   } else {
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
1067        */
1068       core_audio->io_proc_needs_deactivation = TRUE;
1069     }
1070   }
1071   return TRUE;
1072 }
1073
1074 static gboolean
1075 gst_core_audio_stop_processing_impl (GstCoreAudio * core_audio)
1076 {
1077   if (core_audio->is_passthrough) {
1078     _io_proc_spdif_stop (core_audio);
1079   } else {
1080     gst_core_audio_io_proc_stop (core_audio);
1081   }
1082
1083   return TRUE;
1084 }
1085
1086 static gboolean
1087 gst_core_audio_get_samples_and_latency_impl (GstCoreAudio * core_audio,
1088     gdouble rate, guint * samples, gdouble * latency)
1089 {
1090   OSStatus status;
1091   UInt32 size = sizeof (double);
1092
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;
1097   } else {
1098     status = AudioUnitGetProperty (core_audio->audiounit, kAudioUnitProperty_Latency, kAudioUnitScope_Global, 0,        /* N/A for global */
1099         latency, &size);
1100
1101     if (status) {
1102       GST_WARNING_OBJECT (core_audio->osxbuf, "Failed to get latency: %"
1103           GST_FOURCC_FORMAT, GST_FOURCC_ARGS (status));
1104       *samples = 0;
1105       return FALSE;
1106     }
1107
1108     *samples = *latency * rate;
1109   }
1110   return TRUE;
1111 }
1112
1113 static gboolean
1114 gst_core_audio_initialize_impl (GstCoreAudio * core_audio,
1115     AudioStreamBasicDescription format, GstCaps * caps,
1116     gboolean is_passthrough, guint32 * frame_size)
1117 {
1118   gboolean ret = FALSE;
1119
1120   core_audio->is_passthrough = is_passthrough;
1121   if (is_passthrough) {
1122     if (!_acquire_spdif (core_audio, format))
1123       goto done;
1124     _monitorize_spdif (core_audio);
1125   } else {
1126     OSStatus status;
1127     UInt32 propertySize;
1128
1129     core_audio->stream_idx = 0;
1130     if (!gst_core_audio_set_format (core_audio, format))
1131       goto done;
1132
1133     if (!gst_core_audio_set_channels_layout (core_audio,
1134             format.mChannelsPerFrame, caps))
1135       goto done;
1136
1137     if (!gst_core_audio_bind_device (core_audio))
1138       goto done;
1139
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);
1144
1145       if (status) {
1146         GST_WARNING_OBJECT (core_audio->osxbuf, "Failed to get frame size: %"
1147             GST_FOURCC_FORMAT, GST_FOURCC_ARGS (status));
1148         goto done;
1149       }
1150     }
1151   }
1152
1153   ret = TRUE;
1154
1155 done:
1156   if (ret) {
1157     GST_DEBUG_OBJECT (core_audio, "osxbuf ring buffer acquired");
1158   }
1159   return ret;
1160 }
1161
1162 static gboolean
1163 gst_core_audio_select_device_impl (AudioDeviceID * device_id)
1164 {
1165   AudioDeviceID *devices = NULL;
1166   AudioDeviceID default_device_id = 0;
1167   AudioChannelLayout *channel_layout;
1168   gint i, ndevices = 0;
1169   gboolean res = FALSE;
1170
1171   devices = _audio_system_get_devices (&ndevices);
1172
1173   if (ndevices < 1) {
1174     GST_ERROR ("no audio output devices found");
1175     goto done;
1176   }
1177
1178   GST_DEBUG ("found %d audio device(s)", ndevices);
1179
1180   for (i = 0; i < ndevices; i++) {
1181     gchar *device_name;
1182
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);
1187       } else {
1188         GST_DEBUG ("Output Device ID: %u Name: %s",
1189             (unsigned) devices[i], device_name);
1190
1191         channel_layout =
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);
1196         }
1197       }
1198
1199       g_free (device_name);
1200     }
1201   }
1202
1203   /* Find the ID of the default output device */
1204   default_device_id = _audio_system_get_default_output ();
1205
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;
1211       res = TRUE;
1212     }
1213   } else {
1214     for (i = 0; i < ndevices; i++) {
1215       if (*device_id == devices[i]) {
1216         res = TRUE;
1217       }
1218     }
1219
1220     if (res && !_audio_device_is_alive (*device_id)) {
1221       GST_ERROR ("Requested device not usable");
1222       res = FALSE;
1223       goto done;
1224     }
1225   }
1226
1227 done:
1228   g_free (devices);
1229   return res;
1230 }
1231
1232 static gboolean
1233 gst_core_audio_audio_device_is_spdif_avail_impl (AudioDeviceID device_id)
1234 {
1235   AudioStreamID *streams = NULL;
1236   gint i, nstreams = 0;
1237   gboolean res = FALSE;
1238
1239   streams = _audio_device_get_streams (device_id, &nstreams);
1240   GST_DEBUG ("found %d streams", nstreams);
1241   if (streams) {
1242     for (i = 0; i < nstreams; i++) {
1243       if (_audio_stream_is_spdif_avail (streams[i])) {
1244         res = TRUE;
1245       }
1246     }
1247
1248     g_free (streams);
1249   }
1250
1251   return res;
1252 }
1253
1254 static gboolean
1255 gst_core_audio_select_source_device_impl (AudioDeviceID * device_id)
1256 {
1257   OSStatus status;
1258   UInt32 propertySize;
1259
1260   if (*device_id == kAudioDeviceUnknown) {
1261     /* If no specific device has been selected by the user, then pick the
1262      * default device */
1263     GST_DEBUG ("Selecting device for OSXAudioSrc");
1264     propertySize = sizeof (*device_id);
1265     status = AudioHardwareGetProperty (kAudioHardwarePropertyDefaultInputDevice,
1266         &propertySize, device_id);
1267
1268     if (status) {
1269       GST_WARNING ("AudioHardwareGetProperty returned %d", (int) status);
1270     } else {
1271       GST_DEBUG ("AudioHardwareGetProperty returned 0");
1272     }
1273
1274     if (*device_id == kAudioDeviceUnknown) {
1275       GST_WARNING ("AudioHardwareGetProperty: device_id is "
1276           "kAudioDeviceUnknown");
1277     }
1278
1279     GST_DEBUG ("AudioHardwareGetProperty: device_id is %lu", (long) *device_id);
1280   }
1281
1282   return TRUE;
1283 }