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