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