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
_audio_system_set_runloop(CFRunLoopRef runLoop)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: %d", runLoop, (int) status);
46   }
47 
48   return res;
49 }
50 
51 static inline AudioDeviceID
_audio_system_get_default_device(gboolean output)52 _audio_system_get_default_device (gboolean output)
53 {
54   OSStatus status = noErr;
55   UInt32 propertySize = sizeof (AudioDeviceID);
56   AudioDeviceID device_id = kAudioDeviceUnknown;
57   AudioObjectPropertySelector prop_selector;
58 
59   prop_selector = output ? kAudioHardwarePropertyDefaultOutputDevice :
60       kAudioHardwarePropertyDefaultInputDevice;
61 
62   AudioObjectPropertyAddress defaultDeviceAddress = {
63     prop_selector,
64     kAudioObjectPropertyScopeGlobal,
65     kAudioObjectPropertyElementMaster
66   };
67 
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);
72   }
73 
74   GST_DEBUG ("Default device id: %u", (unsigned) device_id);
75 
76   return device_id;
77 }
78 
79 static inline AudioDeviceID *
_audio_system_get_devices(gint * ndevices)80 _audio_system_get_devices (gint * ndevices)
81 {
82   OSStatus status = noErr;
83   UInt32 propertySize = 0;
84   AudioDeviceID *devices = NULL;
85 
86   AudioObjectPropertyAddress audioDevicesAddress = {
87     kAudioHardwarePropertyDevices,
88     kAudioObjectPropertyScopeGlobal,
89     kAudioObjectPropertyElementMaster
90   };
91 
92   status = AudioObjectGetPropertyDataSize (kAudioObjectSystemObject,
93       &audioDevicesAddress, 0, NULL, &propertySize);
94   if (status != noErr) {
95     GST_WARNING ("failed getting number of devices: %d", (int) status);
96     return NULL;
97   }
98 
99   *ndevices = propertySize / sizeof (AudioDeviceID);
100 
101   devices = (AudioDeviceID *) g_malloc (propertySize);
102   if (devices) {
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);
107       g_free (devices);
108       *ndevices = 0;
109       return NULL;
110     }
111   }
112   return devices;
113 }
114 
115 static inline gboolean
_audio_device_is_alive(AudioDeviceID device_id,gboolean output)116 _audio_device_is_alive (AudioDeviceID device_id, gboolean output)
117 {
118   OSStatus status = noErr;
119   int alive = FALSE;
120   UInt32 propertySize = sizeof (alive);
121   AudioObjectPropertyScope prop_scope;
122 
123   prop_scope = output ? kAudioDevicePropertyScopeOutput :
124       kAudioDevicePropertyScopeInput;
125 
126   AudioObjectPropertyAddress audioDeviceAliveAddress = {
127     kAudioDevicePropertyDeviceIsAlive,
128     prop_scope,
129     kAudioObjectPropertyElementMaster
130   };
131 
132   status = AudioObjectGetPropertyData (device_id,
133       &audioDeviceAliveAddress, 0, NULL, &propertySize, &alive);
134   if (status != noErr) {
135     alive = FALSE;
136   }
137 
138   return alive;
139 }
140 
141 static inline guint
_audio_device_get_latency(AudioDeviceID device_id)142 _audio_device_get_latency (AudioDeviceID device_id)
143 {
144   OSStatus status = noErr;
145   UInt32 latency = 0;
146   UInt32 propertySize = sizeof (latency);
147 
148   AudioObjectPropertyAddress audioDeviceLatencyAddress = {
149     kAudioDevicePropertyLatency,
150     kAudioDevicePropertyScopeOutput,
151     kAudioObjectPropertyElementMaster
152   };
153 
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);
158     latency = -1;
159   }
160 
161   return latency;
162 }
163 
164 static inline pid_t
_audio_device_get_hog(AudioDeviceID device_id)165 _audio_device_get_hog (AudioDeviceID device_id)
166 {
167   OSStatus status = noErr;
168   pid_t hog_pid;
169   UInt32 propertySize = sizeof (hog_pid);
170 
171   AudioObjectPropertyAddress audioDeviceHogModeAddress = {
172     kAudioDevicePropertyHogMode,
173     kAudioDevicePropertyScopeOutput,
174     kAudioObjectPropertyElementMaster
175   };
176 
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);
181     hog_pid = -1;
182   }
183 
184   return hog_pid;
185 }
186 
187 static inline gboolean
_audio_device_set_hog(AudioDeviceID device_id,pid_t hog_pid)188 _audio_device_set_hog (AudioDeviceID device_id, pid_t hog_pid)
189 {
190   OSStatus status = noErr;
191   UInt32 propertySize = sizeof (hog_pid);
192   gboolean res = FALSE;
193 
194   AudioObjectPropertyAddress audioDeviceHogModeAddress = {
195     kAudioDevicePropertyHogMode,
196     kAudioDevicePropertyScopeOutput,
197     kAudioObjectPropertyElementMaster
198   };
199 
200   status = AudioObjectSetPropertyData (device_id,
201       &audioDeviceHogModeAddress, 0, NULL, propertySize, &hog_pid);
202 
203   if (status == noErr) {
204     res = TRUE;
205   } else {
206     GST_ERROR ("failed to set hog: %d", (int) status);
207   }
208 
209   return res;
210 }
211 
212 static inline gboolean
_audio_device_set_mixing(AudioDeviceID device_id,gboolean enable_mix)213 _audio_device_set_mixing (AudioDeviceID device_id, gboolean enable_mix)
214 {
215   OSStatus status = noErr;
216   UInt32 propertySize = 0, can_mix = enable_mix;
217   Boolean writable = FALSE;
218   gboolean res = FALSE;
219 
220   AudioObjectPropertyAddress audioDeviceSupportsMixingAddress = {
221     kAudioDevicePropertySupportsMixing,
222     kAudioObjectPropertyScopeGlobal,
223     kAudioObjectPropertyElementMaster
224   };
225 
226   if (AudioObjectHasProperty (device_id, &audioDeviceSupportsMixingAddress)) {
227     /* Set mixable to false if we are allowed to */
228     status = AudioObjectIsPropertySettable (device_id,
229         &audioDeviceSupportsMixingAddress, &writable);
230     if (status) {
231       GST_DEBUG ("AudioObjectIsPropertySettable: %d", (int) status);
232     }
233     status = AudioObjectGetPropertyDataSize (device_id,
234         &audioDeviceSupportsMixingAddress, 0, NULL, &propertySize);
235     if (status) {
236       GST_DEBUG ("AudioObjectGetPropertyDataSize: %d", (int) status);
237     }
238     status = AudioObjectGetPropertyData (device_id,
239         &audioDeviceSupportsMixingAddress, 0, NULL, &propertySize, &can_mix);
240     if (status) {
241       GST_DEBUG ("AudioObjectGetPropertyData: %d", (int) 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: %d", (int) status);
253     }
254   } else {
255     GST_DEBUG ("property not found, mixing coudln't be changed");
256   }
257 
258   return res;
259 }
260 
261 static inline gchar *
_audio_device_get_name(AudioDeviceID device_id,gboolean output)262 _audio_device_get_name (AudioDeviceID device_id, gboolean output)
263 {
264   OSStatus status = noErr;
265   UInt32 propertySize = 0;
266   gchar *device_name = NULL;
267   AudioObjectPropertyScope prop_scope;
268 
269   prop_scope = output ? kAudioDevicePropertyScopeOutput :
270       kAudioDevicePropertyScopeInput;
271 
272   AudioObjectPropertyAddress deviceNameAddress = {
273     kAudioDevicePropertyDeviceName,
274     prop_scope,
275     kAudioObjectPropertyElementMaster
276   };
277 
278   /* Get the length of the device name */
279   status = AudioObjectGetPropertyDataSize (device_id,
280       &deviceNameAddress, 0, NULL, &propertySize);
281   if (status != noErr) {
282     goto beach;
283   }
284 
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);
291     device_name = NULL;
292   }
293 
294 beach:
295   return device_name;
296 }
297 
298 static inline gboolean
_audio_device_has_output(AudioDeviceID device_id)299 _audio_device_has_output (AudioDeviceID device_id)
300 {
301   OSStatus status = noErr;
302   UInt32 propertySize;
303 
304   AudioObjectPropertyAddress streamsAddress = {
305     kAudioDevicePropertyStreams,
306     kAudioDevicePropertyScopeOutput,
307     kAudioObjectPropertyElementMaster
308   };
309 
310   status = AudioObjectGetPropertyDataSize (device_id,
311       &streamsAddress, 0, NULL, &propertySize);
312   if (status != noErr) {
313     return FALSE;
314   }
315   if (propertySize == 0) {
316     return FALSE;
317   }
318 
319   return TRUE;
320 }
321 
322 #ifdef GST_CORE_AUDIO_DEBUG
323 static AudioChannelLayout *
gst_core_audio_audio_device_get_channel_layout(AudioDeviceID device_id,gboolean output)324 gst_core_audio_audio_device_get_channel_layout (AudioDeviceID device_id,
325     gboolean output)
326 {
327   OSStatus status = noErr;
328   UInt32 propertySize = 0;
329   AudioChannelLayout *layout = NULL;
330   AudioObjectPropertyScope prop_scope;
331 
332   prop_scope = output ? kAudioDevicePropertyScopeOutput :
333       kAudioDevicePropertyScopeInput;
334 
335   AudioObjectPropertyAddress channelLayoutAddress = {
336     kAudioDevicePropertyPreferredChannelLayout,
337     prop_scope,
338     kAudioObjectPropertyElementMaster
339   };
340 
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);
346     goto beach;
347   }
348 
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);
355     goto failed;
356   }
357 
358   if (layout->mChannelLayoutTag == kAudioChannelLayoutTag_UseChannelBitmap) {
359     /* bitmap defined channellayout */
360     status =
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);
365       goto failed;
366     }
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);
375       goto failed;
376     }
377   }
378 
379   gst_core_audio_dump_channel_layout (layout);
380 
381 beach:
382   return layout;
383 
384 failed:
385   g_free (layout);
386   return NULL;
387 }
388 #endif
389 
390 static inline AudioStreamID *
_audio_device_get_streams(AudioDeviceID device_id,gint * nstreams)391 _audio_device_get_streams (AudioDeviceID device_id, gint * nstreams)
392 {
393   OSStatus status = noErr;
394   UInt32 propertySize = 0;
395   AudioStreamID *streams = NULL;
396 
397   AudioObjectPropertyAddress streamsAddress = {
398     kAudioDevicePropertyStreams,
399     kAudioDevicePropertyScopeOutput,
400     kAudioObjectPropertyElementMaster
401   };
402 
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);
407     return NULL;
408   }
409 
410   *nstreams = propertySize / sizeof (AudioStreamID);
411   streams = (AudioStreamID *) g_malloc (propertySize);
412 
413   if (streams) {
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);
418       g_free (streams);
419       *nstreams = 0;
420       return NULL;
421     }
422   }
423 
424   return streams;
425 }
426 
427 static inline guint
_audio_stream_get_latency(AudioStreamID stream_id)428 _audio_stream_get_latency (AudioStreamID stream_id)
429 {
430   OSStatus status = noErr;
431   UInt32 latency;
432   UInt32 propertySize = sizeof (latency);
433 
434   AudioObjectPropertyAddress latencyAddress = {
435     kAudioStreamPropertyLatency,
436     kAudioObjectPropertyScopeGlobal,
437     kAudioObjectPropertyElementMaster
438   };
439 
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);
444     latency = -1;
445   }
446 
447   return latency;
448 }
449 
450 static inline gboolean
_audio_stream_get_current_format(AudioStreamID stream_id,AudioStreamBasicDescription * format)451 _audio_stream_get_current_format (AudioStreamID stream_id,
452     AudioStreamBasicDescription * format)
453 {
454   OSStatus status = noErr;
455   UInt32 propertySize = sizeof (AudioStreamBasicDescription);
456 
457   AudioObjectPropertyAddress formatAddress = {
458     kAudioStreamPropertyPhysicalFormat,
459     kAudioObjectPropertyScopeGlobal,
460     kAudioObjectPropertyElementMaster
461   };
462 
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);
467     return FALSE;
468   }
469 
470   return TRUE;
471 }
472 
473 static inline gboolean
_audio_stream_set_current_format(AudioStreamID stream_id,AudioStreamBasicDescription format)474 _audio_stream_set_current_format (AudioStreamID stream_id,
475     AudioStreamBasicDescription format)
476 {
477   OSStatus status = noErr;
478   UInt32 propertySize = sizeof (AudioStreamBasicDescription);
479 
480   AudioObjectPropertyAddress formatAddress = {
481     kAudioStreamPropertyPhysicalFormat,
482     kAudioObjectPropertyScopeGlobal,
483     kAudioObjectPropertyElementMaster
484   };
485 
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);
490     return FALSE;
491   }
492 
493   return TRUE;
494 }
495 
496 static inline AudioStreamRangedDescription *
_audio_stream_get_formats(AudioStreamID stream_id,gint * nformats)497 _audio_stream_get_formats (AudioStreamID stream_id, gint * nformats)
498 {
499   OSStatus status = noErr;
500   UInt32 propertySize = 0;
501   AudioStreamRangedDescription *formats = NULL;
502 
503   AudioObjectPropertyAddress formatsAddress = {
504     kAudioStreamPropertyAvailablePhysicalFormats,
505     kAudioObjectPropertyScopeGlobal,
506     kAudioObjectPropertyElementMaster
507   };
508 
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);
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: %d",
524           (int) status);
525       g_free (formats);
526       *nformats = 0;
527       return NULL;
528     }
529   }
530   return formats;
531 }
532 
533 static inline gboolean
_audio_stream_is_spdif_avail(AudioStreamID stream_id)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
_audio_stream_format_listener(AudioObjectID inObjectID,UInt32 inNumberAddresses,const AudioObjectPropertyAddress inAddresses[],void * inClientData)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
_audio_stream_change_format(AudioStreamID stream_id,AudioStreamBasicDescription format)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: %d", (int) status);
610     goto done;
611   }
612 
613   /* Change the format */
614   if (!_audio_stream_set_current_format (stream_id, format)) {
615     goto done;
616   }
617 
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++) {
622     GTimeVal timeout;
623 
624     g_get_current_time (&timeout);
625     g_time_val_add (&timeout, 250000);
626 
627     if (!g_cond_wait_until (&prop_mutex.cond, &prop_mutex.lock, timeout.tv_sec)) {
628       GST_LOG ("timeout...");
629     }
630 
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));
634 
635       if (cformat.mSampleRate == format.mSampleRate &&
636           cformat.mFormatID == format.mFormatID &&
637           cformat.mFramesPerPacket == format.mFramesPerPacket) {
638         /* The right format is now active */
639         break;
640       }
641     }
642   }
643 
644   if (cformat.mSampleRate != format.mSampleRate ||
645       cformat.mFormatID != format.mFormatID ||
646       cformat.mFramesPerPacket != format.mFramesPerPacket) {
647     goto done;
648   }
649 
650   ret = TRUE;
651 
652 done:
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);
658   }
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);
663 
664   return ret;
665 }
666 
667 static OSStatus
_audio_stream_hardware_changed_listener(AudioObjectID inObjectID,UInt32 inNumberAddresses,const AudioObjectPropertyAddress inAddresses[],void * inClientData)668 _audio_stream_hardware_changed_listener (AudioObjectID inObjectID,
669     UInt32 inNumberAddresses,
670     const AudioObjectPropertyAddress inAddresses[], void *inClientData)
671 {
672   OSStatus status = noErr;
673   guint i;
674   GstCoreAudio *core_audio = inClientData;
675 
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"));
684       }
685       break;
686     }
687   }
688   return (status);
689 }
690 
691 static inline gboolean
_monitorize_spdif(GstCoreAudio * core_audio)692 _monitorize_spdif (GstCoreAudio * core_audio)
693 {
694   OSStatus status = noErr;
695   gboolean ret = TRUE;
696 
697   AudioObjectPropertyAddress propAddress = {
698     kAudioDevicePropertyDeviceHasChanged,
699     kAudioObjectPropertyScopeGlobal,
700     kAudioObjectPropertyElementMaster
701   };
702 
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);
710     ret = FALSE;
711   }
712 
713   return ret;
714 }
715 
716 static inline gboolean
_unmonitorize_spdif(GstCoreAudio * core_audio)717 _unmonitorize_spdif (GstCoreAudio * core_audio)
718 {
719   OSStatus status = noErr;
720   gboolean ret = TRUE;
721 
722   AudioObjectPropertyAddress propAddress = {
723     kAudioDevicePropertyDeviceHasChanged,
724     kAudioObjectPropertyScopeGlobal,
725     kAudioObjectPropertyElementMaster
726   };
727 
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);
735     ret = FALSE;
736   }
737 
738   return ret;
739 }
740 
741 static inline gboolean
_open_spdif(GstCoreAudio * core_audio)742 _open_spdif (GstCoreAudio * core_audio)
743 {
744   gboolean res = FALSE;
745   pid_t hog_pid, own_pid = getpid ();
746 
747   /* We need the device in exclusive and disable the mixing */
748   hog_pid = _audio_device_get_hog (core_audio->device_id);
749 
750   if (hog_pid != -1 && hog_pid != own_pid) {
751     GST_DEBUG_OBJECT (core_audio,
752         "device is currently in use by another application");
753     goto done;
754   }
755 
756   if (_audio_device_set_hog (core_audio->device_id, own_pid)) {
757     core_audio->hog_pid = own_pid;
758   }
759 
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;
763   }
764 
765   res = TRUE;
766 done:
767   return res;
768 }
769 
770 static inline gboolean
_close_spdif(GstCoreAudio * core_audio)771 _close_spdif (GstCoreAudio * core_audio)
772 {
773   pid_t hog_pid;
774 
775   _unmonitorize_spdif (core_audio);
776 
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");
781     }
782     core_audio->revert_format = FALSE;
783   }
784 
785   if (core_audio->disabled_mixing) {
786     _audio_device_set_mixing (core_audio->device_id, TRUE);
787     core_audio->disabled_mixing = FALSE;
788   }
789 
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;
795       }
796     }
797   }
798 
799   return TRUE;
800 }
801 
802 static OSStatus
_io_proc_spdif(AudioDeviceID inDevice,const AudioTimeStamp * inNow,const void * inInputData,const AudioTimeStamp * inTimestamp,AudioBufferList * bufferList,const AudioTimeStamp * inOutputTime,GstCoreAudio * core_audio)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)
809 {
810   OSStatus status;
811 
812   status = core_audio->element->io_proc (core_audio->osxbuf, NULL, inTimestamp,
813       0, 0, bufferList);
814 
815   return status;
816 }
817 
818 static inline gboolean
_acquire_spdif(GstCoreAudio * core_audio,AudioStreamBasicDescription format)819 _acquire_spdif (GstCoreAudio * core_audio, AudioStreamBasicDescription format)
820 {
821   AudioStreamID *streams = NULL;
822   gint i, j, nstreams = 0;
823   gboolean ret = FALSE;
824 
825   if (!_open_spdif (core_audio))
826     goto done;
827 
828   streams = _audio_device_get_streams (core_audio->device_id, &nstreams);
829 
830   for (i = 0; i < nstreams; i++) {
831     AudioStreamRangedDescription *formats = NULL;
832     gint nformats = 0;
833 
834     formats = _audio_stream_get_formats (streams[i], &nformats);
835 
836     if (formats) {
837       gboolean is_spdif = FALSE;
838 
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])) {
842           is_spdif = TRUE;
843           break;
844         }
845       }
846 
847       if (is_spdif) {
848         /* if this stream supports a digital (cac3) format,
849          * then go set it. */
850         gint requested_rate_format = -1;
851         gint current_rate_format = -1;
852         gint backup_rate_format = -1;
853 
854         core_audio->stream_id = streams[i];
855         core_audio->stream_idx = i;
856 
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");
862             g_free (formats);
863             continue;
864           }
865           core_audio->revert_format = TRUE;
866         }
867 
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));
873 
874             if (formats[j].mFormat.mSampleRate == format.mSampleRate) {
875               requested_rate_format = j;
876               break;
877             } else if (formats[j].mFormat.mSampleRate ==
878                 core_audio->original_format.mSampleRate) {
879               current_rate_format = j;
880             } else {
881               if (backup_rate_format < 0 ||
882                   formats[j].mFormat.mSampleRate >
883                   formats[backup_rate_format].mFormat.mSampleRate) {
884                 backup_rate_format = j;
885               }
886             }
887           }
888         }
889 
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;
896         } else {
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;
900         }
901       }
902       g_free (formats);
903     }
904   }
905   g_free (streams);
906 
907   GST_DEBUG_OBJECT (core_audio,
908       "original stream format: " CORE_AUDIO_FORMAT,
909       CORE_AUDIO_FORMAT_ARGS (core_audio->original_format));
910 
911   if (!_audio_stream_change_format (core_audio->stream_id,
912           core_audio->stream_format))
913     goto done;
914 
915   ret = TRUE;
916 
917 done:
918   return ret;
919 }
920 
921 static inline void
_remove_render_spdif_callback(GstCoreAudio * core_audio)922 _remove_render_spdif_callback (GstCoreAudio * core_audio)
923 {
924   OSStatus status;
925 
926   /* Deactivate the render callback by calling
927    * AudioDeviceDestroyIOProcID */
928   status =
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);
933   }
934 
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);
938 
939   /* We're deactivated.. */
940   core_audio->procID = 0;
941   core_audio->io_proc_needs_deactivation = FALSE;
942   core_audio->io_proc_active = FALSE;
943 }
944 
945 static inline gboolean
_io_proc_spdif_start(GstCoreAudio * core_audio)946 _io_proc_spdif_start (GstCoreAudio * core_audio)
947 {
948   OSErr status;
949 
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);
953 
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);
962       return FALSE;
963     }
964     core_audio->io_proc_active = TRUE;
965   }
966 
967   core_audio->io_proc_needs_deactivation = FALSE;
968 
969   /* Start device */
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);
974     return FALSE;
975   }
976   return TRUE;
977 }
978 
979 static inline gboolean
_io_proc_spdif_stop(GstCoreAudio * core_audio)980 _io_proc_spdif_stop (GstCoreAudio * core_audio)
981 {
982   OSErr status;
983 
984   /* Stop device */
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);
989   }
990 
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);
994 
995   if (core_audio->io_proc_active) {
996     _remove_render_spdif_callback (core_audio);
997   }
998 
999   _close_spdif (core_audio);
1000 
1001   return TRUE;
1002 }
1003 
1004 
1005 /***********************
1006  *   Implementation    *
1007  **********************/
1008 
1009 static gboolean
gst_core_audio_open_impl(GstCoreAudio * core_audio)1010 gst_core_audio_open_impl (GstCoreAudio * core_audio)
1011 {
1012   gboolean ret;
1013 
1014   /* The following is needed to instruct HAL to create their own
1015    * thread to handle the notifications. */
1016   _audio_system_set_runloop (NULL);
1017 
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)
1023    *
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
1027    */
1028   ret = gst_core_audio_open_device (core_audio, kAudioUnitSubType_HALOutput,
1029       "HALOutput");
1030   if (!ret) {
1031     GST_DEBUG ("Could not open device");
1032     goto done;
1033   }
1034 
1035   ret = gst_core_audio_bind_device (core_audio);
1036   if (!ret) {
1037     GST_DEBUG ("Could not bind device");
1038     goto done;
1039   }
1040 
1041 done:
1042   return ret;
1043 }
1044 
1045 static gboolean
gst_core_audio_start_processing_impl(GstCoreAudio * core_audio)1046 gst_core_audio_start_processing_impl (GstCoreAudio * core_audio)
1047 {
1048   if (core_audio->is_passthrough) {
1049     return _io_proc_spdif_start (core_audio);
1050   } else {
1051     return gst_core_audio_io_proc_start (core_audio);
1052   }
1053 }
1054 
1055 static gboolean
gst_core_audio_pause_processing_impl(GstCoreAudio * core_audio)1056 gst_core_audio_pause_processing_impl (GstCoreAudio * core_audio)
1057 {
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);
1062 
1063     if (core_audio->io_proc_active) {
1064       _remove_render_spdif_callback (core_audio);
1065     }
1066   } else {
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
1074        */
1075       core_audio->io_proc_needs_deactivation = TRUE;
1076     }
1077   }
1078   return TRUE;
1079 }
1080 
1081 static gboolean
gst_core_audio_stop_processing_impl(GstCoreAudio * core_audio)1082 gst_core_audio_stop_processing_impl (GstCoreAudio * core_audio)
1083 {
1084   if (core_audio->is_passthrough) {
1085     _io_proc_spdif_stop (core_audio);
1086   } else {
1087     gst_core_audio_io_proc_stop (core_audio);
1088   }
1089 
1090   return TRUE;
1091 }
1092 
1093 static gboolean
gst_core_audio_get_samples_and_latency_impl(GstCoreAudio * core_audio,gdouble rate,guint * samples,gdouble * latency)1094 gst_core_audio_get_samples_and_latency_impl (GstCoreAudio * core_audio,
1095     gdouble rate, guint * samples, gdouble * latency)
1096 {
1097   OSStatus status;
1098   UInt32 size = sizeof (double);
1099 
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;
1104   } else {
1105     status = AudioUnitGetProperty (core_audio->audiounit, kAudioUnitProperty_Latency, kAudioUnitScope_Global, 0,        /* N/A for global */
1106         latency, &size);
1107 
1108     if (status) {
1109       GST_WARNING_OBJECT (core_audio->osxbuf, "Failed to get latency: %d",
1110           (int) status);
1111       *samples = 0;
1112       return FALSE;
1113     }
1114 
1115     *samples = *latency * rate;
1116   }
1117   return TRUE;
1118 }
1119 
1120 static gboolean
gst_core_audio_initialize_impl(GstCoreAudio * core_audio,AudioStreamBasicDescription format,GstCaps * caps,gboolean is_passthrough,guint32 * frame_size)1121 gst_core_audio_initialize_impl (GstCoreAudio * core_audio,
1122     AudioStreamBasicDescription format, GstCaps * caps,
1123     gboolean is_passthrough, guint32 * frame_size)
1124 {
1125   gboolean ret = FALSE;
1126   OSStatus status;
1127 
1128   /* Uninitialize the AudioUnit before changing formats */
1129   status = AudioUnitUninitialize (core_audio->audiounit);
1130   if (status) {
1131     GST_ERROR_OBJECT (core_audio, "Failed to uninitialize AudioUnit: %d",
1132         (int) status);
1133     return FALSE;
1134   }
1135 
1136   core_audio->is_passthrough = is_passthrough;
1137   if (is_passthrough) {
1138     if (!_acquire_spdif (core_audio, format))
1139       goto done;
1140     _monitorize_spdif (core_audio);
1141   } else {
1142     OSStatus status;
1143     UInt32 propertySize;
1144 
1145     core_audio->stream_idx = 0;
1146     if (!gst_core_audio_set_format (core_audio, format))
1147       goto done;
1148 
1149     if (!gst_core_audio_set_channel_layout (core_audio,
1150             format.mChannelsPerFrame, caps))
1151       goto done;
1152 
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);
1157 
1158       if (status) {
1159         GST_WARNING_OBJECT (core_audio->osxbuf, "Failed to get frame size: %d",
1160             (int) status);
1161         goto done;
1162       }
1163     }
1164   }
1165 
1166   ret = TRUE;
1167 
1168 done:
1169   /* Format changed, initialise the AudioUnit again */
1170   status = AudioUnitInitialize (core_audio->audiounit);
1171   if (status) {
1172     GST_ERROR_OBJECT (core_audio, "Failed to initialize AudioUnit: %d",
1173         (int) status);
1174     ret = FALSE;
1175   }
1176 
1177   if (ret) {
1178     GST_DEBUG_OBJECT (core_audio, "osxbuf ring buffer acquired");
1179   }
1180 
1181   return ret;
1182 }
1183 
1184 static gboolean
gst_core_audio_select_device_impl(GstCoreAudio * core_audio)1185 gst_core_audio_select_device_impl (GstCoreAudio * core_audio)
1186 {
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;
1195 #endif
1196 
1197   devices = _audio_system_get_devices (&ndevices);
1198 
1199   if (ndevices < 1) {
1200     GST_ERROR ("no audio output devices found");
1201     goto done;
1202   }
1203 
1204   GST_DEBUG ("found %d audio device(s)", ndevices);
1205 
1206 #ifdef GST_CORE_AUDIO_DEBUG
1207   for (i = 0; i < ndevices; i++) {
1208     gchar *device_name;
1209 
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);
1214       } else {
1215         GST_DEBUG ("Output Device ID: %u Name: %s",
1216             (unsigned) devices[i], device_name);
1217 
1218         channel_layout =
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);
1223         }
1224       }
1225 
1226       g_free (device_name);
1227     }
1228   }
1229 #endif
1230 
1231   /* Find the ID of the default output device */
1232   default_device_id = _audio_system_get_default_device (output);
1233 
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;
1239       res = TRUE;
1240     } else {
1241       GST_ERROR ("No device of required type available");
1242       res = FALSE;
1243     }
1244   } else {
1245     for (i = 0; i < ndevices; i++) {
1246       if (device_id == devices[i]) {
1247         res = TRUE;
1248         break;
1249       }
1250     }
1251 
1252     if (res && !_audio_device_is_alive (device_id, output)) {
1253       GST_ERROR ("Requested device not usable");
1254       res = FALSE;
1255       goto done;
1256     }
1257   }
1258 
1259   if (res)
1260     core_audio->device_id = device_id;
1261 
1262 done:
1263   g_free (devices);
1264   return res;
1265 }
1266 
1267 static gboolean
gst_core_audio_audio_device_is_spdif_avail_impl(AudioDeviceID device_id)1268 gst_core_audio_audio_device_is_spdif_avail_impl (AudioDeviceID device_id)
1269 {
1270   AudioStreamID *streams = NULL;
1271   gint i, nstreams = 0;
1272   gboolean res = FALSE;
1273 
1274   streams = _audio_device_get_streams (device_id, &nstreams);
1275   GST_DEBUG ("found %d streams", nstreams);
1276   if (streams) {
1277     for (i = 0; i < nstreams; i++) {
1278       if (_audio_stream_is_spdif_avail (streams[i])) {
1279         res = TRUE;
1280       }
1281     }
1282 
1283     g_free (streams);
1284   }
1285 
1286   return res;
1287 }
1288