• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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     gint64 timeout;
623 
624     timeout = g_get_monotonic_time () + 250000;
625 
626     if (!g_cond_wait_until (&prop_mutex.cond, &prop_mutex.lock, timeout)) {
627       GST_LOG ("timeout...");
628     }
629 
630     if (_audio_stream_get_current_format (stream_id, &cformat)) {
631       GST_DEBUG ("current stream format: " CORE_AUDIO_FORMAT,
632           CORE_AUDIO_FORMAT_ARGS (cformat));
633 
634       if (cformat.mSampleRate == format.mSampleRate &&
635           cformat.mFormatID == format.mFormatID &&
636           cformat.mFramesPerPacket == format.mFramesPerPacket) {
637         /* The right format is now active */
638         break;
639       }
640     }
641   }
642 
643   if (cformat.mSampleRate != format.mSampleRate ||
644       cformat.mFormatID != format.mFormatID ||
645       cformat.mFramesPerPacket != format.mFramesPerPacket) {
646     goto done;
647   }
648 
649   ret = TRUE;
650 
651 done:
652   /* Removing the property listener */
653   status = AudioObjectRemovePropertyListener (stream_id,
654       &formatAddress, _audio_stream_format_listener, (void *) &prop_mutex);
655   if (status != noErr) {
656     GST_ERROR ("AudioObjectRemovePropertyListener failed: %d", (int) status);
657   }
658   /* Destroy the lock and condition */
659   g_mutex_unlock (&prop_mutex.lock);
660   g_mutex_clear (&prop_mutex.lock);
661   g_cond_clear (&prop_mutex.cond);
662 
663   return ret;
664 }
665 
666 static OSStatus
_audio_stream_hardware_changed_listener(AudioObjectID inObjectID,UInt32 inNumberAddresses,const AudioObjectPropertyAddress inAddresses[],void * inClientData)667 _audio_stream_hardware_changed_listener (AudioObjectID inObjectID,
668     UInt32 inNumberAddresses,
669     const AudioObjectPropertyAddress inAddresses[], void *inClientData)
670 {
671   OSStatus status = noErr;
672   guint i;
673   GstCoreAudio *core_audio = inClientData;
674 
675   for (i = 0; i < inNumberAddresses; i++) {
676     if (inAddresses[i].mSelector == kAudioDevicePropertyDeviceHasChanged) {
677       if (!gst_core_audio_audio_device_is_spdif_avail (core_audio->device_id)) {
678         GstOsxAudioSink *sink =
679             GST_OSX_AUDIO_SINK (GST_OBJECT_PARENT (core_audio->osxbuf));
680         GST_ELEMENT_ERROR (sink, RESOURCE, FAILED,
681             ("SPDIF output no longer available"),
682             ("Audio device is reporting that SPDIF output isn't available"));
683       }
684       break;
685     }
686   }
687   return (status);
688 }
689 
690 static inline gboolean
_monitorize_spdif(GstCoreAudio * core_audio)691 _monitorize_spdif (GstCoreAudio * core_audio)
692 {
693   OSStatus status = noErr;
694   gboolean ret = TRUE;
695 
696   AudioObjectPropertyAddress propAddress = {
697     kAudioDevicePropertyDeviceHasChanged,
698     kAudioObjectPropertyScopeGlobal,
699     kAudioObjectPropertyElementMaster
700   };
701 
702   /* Install the property listener */
703   status = AudioObjectAddPropertyListener (core_audio->device_id,
704       &propAddress, _audio_stream_hardware_changed_listener,
705       (void *) core_audio);
706   if (status != noErr) {
707     GST_ERROR_OBJECT (core_audio->osxbuf,
708         "AudioObjectAddPropertyListener failed: %d", (int) status);
709     ret = FALSE;
710   }
711 
712   return ret;
713 }
714 
715 static inline gboolean
_unmonitorize_spdif(GstCoreAudio * core_audio)716 _unmonitorize_spdif (GstCoreAudio * core_audio)
717 {
718   OSStatus status = noErr;
719   gboolean ret = TRUE;
720 
721   AudioObjectPropertyAddress propAddress = {
722     kAudioDevicePropertyDeviceHasChanged,
723     kAudioObjectPropertyScopeGlobal,
724     kAudioObjectPropertyElementMaster
725   };
726 
727   /* Remove the property listener */
728   status = AudioObjectRemovePropertyListener (core_audio->device_id,
729       &propAddress, _audio_stream_hardware_changed_listener,
730       (void *) core_audio);
731   if (status != noErr) {
732     GST_ERROR_OBJECT (core_audio->osxbuf,
733         "AudioObjectRemovePropertyListener failed: %d", (int) status);
734     ret = FALSE;
735   }
736 
737   return ret;
738 }
739 
740 static inline gboolean
_open_spdif(GstCoreAudio * core_audio)741 _open_spdif (GstCoreAudio * core_audio)
742 {
743   gboolean res = FALSE;
744   pid_t hog_pid, own_pid = getpid ();
745 
746   /* We need the device in exclusive and disable the mixing */
747   hog_pid = _audio_device_get_hog (core_audio->device_id);
748 
749   if (hog_pid != -1 && hog_pid != own_pid) {
750     GST_DEBUG_OBJECT (core_audio,
751         "device is currently in use by another application");
752     goto done;
753   }
754 
755   if (_audio_device_set_hog (core_audio->device_id, own_pid)) {
756     core_audio->hog_pid = own_pid;
757   }
758 
759   if (_audio_device_set_mixing (core_audio->device_id, FALSE)) {
760     GST_DEBUG_OBJECT (core_audio, "disabled mixing on the device");
761     core_audio->disabled_mixing = TRUE;
762   }
763 
764   res = TRUE;
765 done:
766   return res;
767 }
768 
769 static inline gboolean
_close_spdif(GstCoreAudio * core_audio)770 _close_spdif (GstCoreAudio * core_audio)
771 {
772   pid_t hog_pid;
773 
774   _unmonitorize_spdif (core_audio);
775 
776   if (core_audio->revert_format) {
777     if (!_audio_stream_change_format (core_audio->stream_id,
778             core_audio->original_format)) {
779       GST_WARNING_OBJECT (core_audio->osxbuf, "Format revert failed");
780     }
781     core_audio->revert_format = FALSE;
782   }
783 
784   if (core_audio->disabled_mixing) {
785     _audio_device_set_mixing (core_audio->device_id, TRUE);
786     core_audio->disabled_mixing = FALSE;
787   }
788 
789   if (core_audio->hog_pid != -1) {
790     hog_pid = _audio_device_get_hog (core_audio->device_id);
791     if (hog_pid == getpid ()) {
792       if (_audio_device_set_hog (core_audio->device_id, -1)) {
793         core_audio->hog_pid = -1;
794       }
795     }
796   }
797 
798   return TRUE;
799 }
800 
801 static OSStatus
_io_proc_spdif(AudioDeviceID inDevice,const AudioTimeStamp * inNow,const void * inInputData,const AudioTimeStamp * inTimestamp,AudioBufferList * bufferList,const AudioTimeStamp * inOutputTime,GstCoreAudio * core_audio)802 _io_proc_spdif (AudioDeviceID inDevice,
803     const AudioTimeStamp * inNow,
804     const void *inInputData,
805     const AudioTimeStamp * inTimestamp,
806     AudioBufferList * bufferList,
807     const AudioTimeStamp * inOutputTime, GstCoreAudio * core_audio)
808 {
809   OSStatus status;
810 
811   status = core_audio->element->io_proc (core_audio->osxbuf, NULL, inTimestamp,
812       0, 0, bufferList);
813 
814   return status;
815 }
816 
817 static inline gboolean
_acquire_spdif(GstCoreAudio * core_audio,AudioStreamBasicDescription format)818 _acquire_spdif (GstCoreAudio * core_audio, AudioStreamBasicDescription format)
819 {
820   AudioStreamID *streams = NULL;
821   gint i, j, nstreams = 0;
822   gboolean ret = FALSE;
823 
824   if (!_open_spdif (core_audio))
825     goto done;
826 
827   streams = _audio_device_get_streams (core_audio->device_id, &nstreams);
828 
829   for (i = 0; i < nstreams; i++) {
830     AudioStreamRangedDescription *formats = NULL;
831     gint nformats = 0;
832 
833     formats = _audio_stream_get_formats (streams[i], &nformats);
834 
835     if (formats) {
836       gboolean is_spdif = FALSE;
837 
838       /* Check if one of the supported formats is a digital format */
839       for (j = 0; j < nformats; j++) {
840         if (CORE_AUDIO_FORMAT_IS_SPDIF (formats[j])) {
841           is_spdif = TRUE;
842           break;
843         }
844       }
845 
846       if (is_spdif) {
847         /* if this stream supports a digital (cac3) format,
848          * then go set it. */
849         gint requested_rate_format = -1;
850         gint current_rate_format = -1;
851         gint backup_rate_format = -1;
852 
853         core_audio->stream_id = streams[i];
854         core_audio->stream_idx = i;
855 
856         if (!core_audio->revert_format) {
857           if (!_audio_stream_get_current_format (core_audio->stream_id,
858                   &core_audio->original_format)) {
859             GST_WARNING_OBJECT (core_audio->osxbuf,
860                 "format could not be saved");
861             g_free (formats);
862             continue;
863           }
864           core_audio->revert_format = TRUE;
865         }
866 
867         for (j = 0; j < nformats; j++) {
868           if (CORE_AUDIO_FORMAT_IS_SPDIF (formats[j])) {
869             GST_LOG_OBJECT (core_audio->osxbuf,
870                 "found stream format: " CORE_AUDIO_FORMAT,
871                 CORE_AUDIO_FORMAT_ARGS (formats[j].mFormat));
872 
873             if (formats[j].mFormat.mSampleRate == format.mSampleRate) {
874               requested_rate_format = j;
875               break;
876             } else if (formats[j].mFormat.mSampleRate ==
877                 core_audio->original_format.mSampleRate) {
878               current_rate_format = j;
879             } else {
880               if (backup_rate_format < 0 ||
881                   formats[j].mFormat.mSampleRate >
882                   formats[backup_rate_format].mFormat.mSampleRate) {
883                 backup_rate_format = j;
884               }
885             }
886           }
887         }
888 
889         if (requested_rate_format >= 0) {
890           /* We prefer to output at the rate of the original audio */
891           core_audio->stream_format = formats[requested_rate_format].mFormat;
892         } else if (current_rate_format >= 0) {
893           /* If not possible, we will try to use the current rate */
894           core_audio->stream_format = formats[current_rate_format].mFormat;
895         } else {
896           /* And if we have to, any digital format will be just
897            * fine (highest rate possible) */
898           core_audio->stream_format = formats[backup_rate_format].mFormat;
899         }
900       }
901       g_free (formats);
902     }
903   }
904   g_free (streams);
905 
906   GST_DEBUG_OBJECT (core_audio,
907       "original stream format: " CORE_AUDIO_FORMAT,
908       CORE_AUDIO_FORMAT_ARGS (core_audio->original_format));
909 
910   if (!_audio_stream_change_format (core_audio->stream_id,
911           core_audio->stream_format))
912     goto done;
913 
914   ret = TRUE;
915 
916 done:
917   return ret;
918 }
919 
920 static inline void
_remove_render_spdif_callback(GstCoreAudio * core_audio)921 _remove_render_spdif_callback (GstCoreAudio * core_audio)
922 {
923   OSStatus status;
924 
925   /* Deactivate the render callback by calling
926    * AudioDeviceDestroyIOProcID */
927   status =
928       AudioDeviceDestroyIOProcID (core_audio->device_id, core_audio->procID);
929   if (status != noErr) {
930     GST_ERROR_OBJECT (core_audio->osxbuf,
931         "AudioDeviceDestroyIOProcID failed: %d", (int) status);
932   }
933 
934   GST_DEBUG_OBJECT (core_audio,
935       "osx ring buffer removed ioproc ID: %p device_id %lu",
936       core_audio->procID, (gulong) core_audio->device_id);
937 
938   /* We're deactivated.. */
939   core_audio->procID = 0;
940   core_audio->io_proc_needs_deactivation = FALSE;
941   core_audio->io_proc_active = FALSE;
942 }
943 
944 static inline gboolean
_io_proc_spdif_start(GstCoreAudio * core_audio)945 _io_proc_spdif_start (GstCoreAudio * core_audio)
946 {
947   OSErr status;
948 
949   GST_DEBUG_OBJECT (core_audio,
950       "osx ring buffer start ioproc ID: %p device_id %lu",
951       core_audio->procID, (gulong) core_audio->device_id);
952 
953   if (!core_audio->io_proc_active) {
954     /* Add IOProc callback */
955     status = AudioDeviceCreateIOProcID (core_audio->device_id,
956         (AudioDeviceIOProc) _io_proc_spdif,
957         (void *) core_audio, &core_audio->procID);
958     if (status != noErr) {
959       GST_ERROR_OBJECT (core_audio->osxbuf,
960           ":AudioDeviceCreateIOProcID failed: %d", (int) status);
961       return FALSE;
962     }
963     core_audio->io_proc_active = TRUE;
964   }
965 
966   core_audio->io_proc_needs_deactivation = FALSE;
967 
968   /* Start device */
969   status = AudioDeviceStart (core_audio->device_id, core_audio->procID);
970   if (status != noErr) {
971     GST_ERROR_OBJECT (core_audio->osxbuf,
972         "AudioDeviceStart failed: %d", (int) status);
973     return FALSE;
974   }
975   return TRUE;
976 }
977 
978 static inline gboolean
_io_proc_spdif_stop(GstCoreAudio * core_audio)979 _io_proc_spdif_stop (GstCoreAudio * core_audio)
980 {
981   OSErr status;
982 
983   /* Stop device */
984   status = AudioDeviceStop (core_audio->device_id, core_audio->procID);
985   if (status != noErr) {
986     GST_ERROR_OBJECT (core_audio->osxbuf,
987         "AudioDeviceStop failed: %d", (int) status);
988   }
989 
990   GST_DEBUG_OBJECT (core_audio,
991       "osx ring buffer stop ioproc ID: %p device_id %lu",
992       core_audio->procID, (gulong) core_audio->device_id);
993 
994   if (core_audio->io_proc_active) {
995     _remove_render_spdif_callback (core_audio);
996   }
997 
998   _close_spdif (core_audio);
999 
1000   return TRUE;
1001 }
1002 
1003 
1004 /***********************
1005  *   Implementation    *
1006  **********************/
1007 
1008 static gboolean
gst_core_audio_open_impl(GstCoreAudio * core_audio)1009 gst_core_audio_open_impl (GstCoreAudio * core_audio)
1010 {
1011   gboolean ret;
1012 
1013   /* The following is needed to instruct HAL to create their own
1014    * thread to handle the notifications. */
1015   _audio_system_set_runloop (NULL);
1016 
1017   /* Create a HALOutput AudioUnit.
1018    * This is the lowest-level output API that is actually sensibly
1019    * usable (the lower level ones require that you do
1020    * channel-remapping yourself, and the CoreAudio channel mapping
1021    * is sufficiently complex that doing so would be very difficult)
1022    *
1023    * Note that for input we request an output unit even though
1024    * we will do input with it.
1025    * http://developer.apple.com/technotes/tn2002/tn2091.html
1026    */
1027   ret = gst_core_audio_open_device (core_audio, kAudioUnitSubType_HALOutput,
1028       "HALOutput");
1029   if (!ret) {
1030     GST_DEBUG ("Could not open device");
1031     goto done;
1032   }
1033 
1034   ret = gst_core_audio_bind_device (core_audio);
1035   if (!ret) {
1036     GST_DEBUG ("Could not bind device");
1037     goto done;
1038   }
1039 
1040 done:
1041   return ret;
1042 }
1043 
1044 static gboolean
gst_core_audio_start_processing_impl(GstCoreAudio * core_audio)1045 gst_core_audio_start_processing_impl (GstCoreAudio * core_audio)
1046 {
1047   if (core_audio->is_passthrough) {
1048     return _io_proc_spdif_start (core_audio);
1049   } else {
1050     return gst_core_audio_io_proc_start (core_audio);
1051   }
1052 }
1053 
1054 static gboolean
gst_core_audio_pause_processing_impl(GstCoreAudio * core_audio)1055 gst_core_audio_pause_processing_impl (GstCoreAudio * core_audio)
1056 {
1057   if (core_audio->is_passthrough) {
1058     GST_DEBUG_OBJECT (core_audio,
1059         "osx ring buffer pause ioproc ID: %p device_id %lu",
1060         core_audio->procID, (gulong) core_audio->device_id);
1061 
1062     if (core_audio->io_proc_active) {
1063       _remove_render_spdif_callback (core_audio);
1064     }
1065   } else {
1066     GST_DEBUG_OBJECT (core_audio,
1067         "osx ring buffer pause ioproc: %p device_id %lu",
1068         core_audio->element->io_proc, (gulong) core_audio->device_id);
1069     if (core_audio->io_proc_active) {
1070       /* CoreAudio isn't threadsafe enough to do this here;
1071        * we must deactivate the render callback elsewhere. See:
1072        * http://lists.apple.com/archives/Coreaudio-api/2006/Mar/msg00010.html
1073        */
1074       core_audio->io_proc_needs_deactivation = TRUE;
1075     }
1076   }
1077   return TRUE;
1078 }
1079 
1080 static gboolean
gst_core_audio_stop_processing_impl(GstCoreAudio * core_audio)1081 gst_core_audio_stop_processing_impl (GstCoreAudio * core_audio)
1082 {
1083   if (core_audio->is_passthrough) {
1084     _io_proc_spdif_stop (core_audio);
1085   } else {
1086     gst_core_audio_io_proc_stop (core_audio);
1087   }
1088 
1089   return TRUE;
1090 }
1091 
1092 static gboolean
gst_core_audio_get_samples_and_latency_impl(GstCoreAudio * core_audio,gdouble rate,guint * samples,gdouble * latency)1093 gst_core_audio_get_samples_and_latency_impl (GstCoreAudio * core_audio,
1094     gdouble rate, guint * samples, gdouble * latency)
1095 {
1096   OSStatus status;
1097   UInt32 size = sizeof (double);
1098 
1099   if (core_audio->is_passthrough) {
1100     *samples = _audio_device_get_latency (core_audio->device_id);
1101     *samples += _audio_stream_get_latency (core_audio->stream_id);
1102     *latency = (double) *samples / rate;
1103   } else {
1104     status = AudioUnitGetProperty (core_audio->audiounit, kAudioUnitProperty_Latency, kAudioUnitScope_Global, 0,        /* N/A for global */
1105         latency, &size);
1106 
1107     if (status) {
1108       GST_WARNING_OBJECT (core_audio->osxbuf, "Failed to get latency: %d",
1109           (int) status);
1110       *samples = 0;
1111       return FALSE;
1112     }
1113 
1114     *samples = *latency * rate;
1115   }
1116   return TRUE;
1117 }
1118 
1119 static gboolean
gst_core_audio_initialize_impl(GstCoreAudio * core_audio,AudioStreamBasicDescription format,GstCaps * caps,gboolean is_passthrough,guint32 * frame_size)1120 gst_core_audio_initialize_impl (GstCoreAudio * core_audio,
1121     AudioStreamBasicDescription format, GstCaps * caps,
1122     gboolean is_passthrough, guint32 * frame_size)
1123 {
1124   gboolean ret = FALSE;
1125   OSStatus status;
1126 
1127   /* Uninitialize the AudioUnit before changing formats */
1128   status = AudioUnitUninitialize (core_audio->audiounit);
1129   if (status) {
1130     GST_ERROR_OBJECT (core_audio, "Failed to uninitialize AudioUnit: %d",
1131         (int) status);
1132     return FALSE;
1133   }
1134 
1135   core_audio->is_passthrough = is_passthrough;
1136   if (is_passthrough) {
1137     if (!_acquire_spdif (core_audio, format))
1138       goto done;
1139     _monitorize_spdif (core_audio);
1140   } else {
1141     OSStatus status;
1142     UInt32 propertySize;
1143 
1144     core_audio->stream_idx = 0;
1145     if (!gst_core_audio_set_format (core_audio, format))
1146       goto done;
1147 
1148     if (!gst_core_audio_set_channel_layout (core_audio,
1149             format.mChannelsPerFrame, caps))
1150       goto done;
1151 
1152     if (core_audio->is_src) {
1153       propertySize = sizeof (*frame_size);
1154       status = AudioUnitGetProperty (core_audio->audiounit, kAudioDevicePropertyBufferFrameSize, kAudioUnitScope_Global, 0,     /* N/A for global */
1155           frame_size, &propertySize);
1156 
1157       if (status) {
1158         GST_WARNING_OBJECT (core_audio->osxbuf, "Failed to get frame size: %d",
1159             (int) status);
1160         goto done;
1161       }
1162     }
1163   }
1164 
1165   ret = TRUE;
1166 
1167 done:
1168   /* Format changed, initialise the AudioUnit again */
1169   status = AudioUnitInitialize (core_audio->audiounit);
1170   if (status) {
1171     GST_ERROR_OBJECT (core_audio, "Failed to initialize AudioUnit: %d",
1172         (int) status);
1173     ret = FALSE;
1174   }
1175 
1176   if (ret) {
1177     GST_DEBUG_OBJECT (core_audio, "osxbuf ring buffer acquired");
1178   }
1179 
1180   return ret;
1181 }
1182 
1183 static gboolean
gst_core_audio_select_device_impl(GstCoreAudio * core_audio)1184 gst_core_audio_select_device_impl (GstCoreAudio * core_audio)
1185 {
1186   AudioDeviceID *devices = NULL;
1187   AudioDeviceID device_id = core_audio->device_id;
1188   AudioDeviceID default_device_id = 0;
1189   gint i, ndevices = 0;
1190   gboolean output = !core_audio->is_src;
1191   gboolean res = FALSE;
1192 #ifdef GST_CORE_AUDIO_DEBUG
1193   AudioChannelLayout *channel_layout;
1194 #endif
1195 
1196   devices = _audio_system_get_devices (&ndevices);
1197 
1198   if (ndevices < 1) {
1199     GST_ERROR ("no audio output devices found");
1200     goto done;
1201   }
1202 
1203   GST_DEBUG ("found %d audio device(s)", ndevices);
1204 
1205 #ifdef GST_CORE_AUDIO_DEBUG
1206   for (i = 0; i < ndevices; i++) {
1207     gchar *device_name;
1208 
1209     if ((device_name = _audio_device_get_name (devices[i], output))) {
1210       if (!_audio_device_has_output (devices[i])) {
1211         GST_DEBUG ("Input Device ID: %u Name: %s",
1212             (unsigned) devices[i], device_name);
1213       } else {
1214         GST_DEBUG ("Output Device ID: %u Name: %s",
1215             (unsigned) devices[i], device_name);
1216 
1217         channel_layout =
1218             gst_core_audio_audio_device_get_channel_layout (devices[i], output);
1219         if (channel_layout) {
1220           gst_core_audio_dump_channel_layout (channel_layout);
1221           g_free (channel_layout);
1222         }
1223       }
1224 
1225       g_free (device_name);
1226     }
1227   }
1228 #endif
1229 
1230   /* Find the ID of the default output device */
1231   default_device_id = _audio_system_get_default_device (output);
1232 
1233   /* Here we decide if selected device is valid or autoselect
1234    * the default one when required */
1235   if (device_id == kAudioDeviceUnknown) {
1236     if (default_device_id != kAudioDeviceUnknown) {
1237       device_id = default_device_id;
1238       res = TRUE;
1239     } else {
1240       GST_ERROR ("No device of required type available");
1241       res = FALSE;
1242     }
1243   } else {
1244     for (i = 0; i < ndevices; i++) {
1245       if (device_id == devices[i]) {
1246         res = TRUE;
1247         break;
1248       }
1249     }
1250 
1251     if (res && !_audio_device_is_alive (device_id, output)) {
1252       GST_ERROR ("Requested device not usable");
1253       res = FALSE;
1254       goto done;
1255     }
1256   }
1257 
1258   if (res)
1259     core_audio->device_id = device_id;
1260 
1261 done:
1262   g_free (devices);
1263   return res;
1264 }
1265 
1266 static gboolean
gst_core_audio_audio_device_is_spdif_avail_impl(AudioDeviceID device_id)1267 gst_core_audio_audio_device_is_spdif_avail_impl (AudioDeviceID device_id)
1268 {
1269   AudioStreamID *streams = NULL;
1270   gint i, nstreams = 0;
1271   gboolean res = FALSE;
1272 
1273   streams = _audio_device_get_streams (device_id, &nstreams);
1274   GST_DEBUG ("found %d streams", nstreams);
1275   if (streams) {
1276     for (i = 0; i < nstreams; i++) {
1277       if (_audio_stream_is_spdif_avail (streams[i])) {
1278         res = TRUE;
1279       }
1280     }
1281 
1282     g_free (streams);
1283   }
1284 
1285   return res;
1286 }
1287