1/* $Id$ */
2/*
3 * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
18 */
19#include <pjmedia-audiodev/audiodev_imp.h>
20#include <pj/assert.h>
21#include <pj/log.h>
22#include <pj/os.h>
23
24#if PJMEDIA_AUDIO_DEV_HAS_COREAUDIO
25
26#include "TargetConditionals.h"
27#if TARGET_OS_IPHONE
28    #define COREAUDIO_MAC 0
29#else
30    #define COREAUDIO_MAC 1
31#endif
32
33#include <AudioUnit/AudioUnit.h>
34#include <AudioToolbox/AudioConverter.h>
35#if COREAUDIO_MAC
36    #include <CoreAudio/CoreAudio.h>
37#else
38    #include <AVFoundation/AVAudioSession.h>
39
40    #define AudioDeviceID unsigned
41
42    /**
43     * As in iOS SDK 4 or later, audio route change property listener is
44     * no longer necessary. Just make surethat your application can receive
45     * remote control events by adding the code:
46     *     [[UIApplication sharedApplication]
47     *      beginReceivingRemoteControlEvents];
48     * Otherwise audio route change (such as headset plug/unplug) will not be
49     * processed while your application is in the background mode.
50     */
51    #define USE_AUDIO_ROUTE_CHANGE_PROP_LISTENER 0
52
53    /* Starting iOS SDK 7, Audio Session API is deprecated. */
54    #define USE_AUDIO_SESSION_API 0
55
56    /* For better integration with CallKit features (available starting
57     * in iOS 10), let the application setup and manage its own
58     * audio session.
59     */
60    #define SETUP_AV_AUDIO_SESSION  0
61#endif
62
63/* For Mac OS 10.5.x and earlier */
64#if AUDIO_UNIT_VERSION < 1060
65    #define AudioComponent Component
66    #define AudioComponentDescription ComponentDescription
67    #define AudioComponentInstance ComponentInstance
68    #define AudioComponentFindNext FindNextComponent
69    #define AudioComponentInstanceNew OpenAComponent
70    #define AudioComponentInstanceDispose CloseComponent
71#endif
72
73
74#define THIS_FILE		"coreaudio_dev.c"
75
76/* coreaudio device info */
77struct coreaudio_dev_info
78{
79    pjmedia_aud_dev_info	 info;
80    AudioDeviceID		 dev_id;
81};
82
83/* linked list of streams */
84struct stream_list
85{
86    PJ_DECL_LIST_MEMBER(struct stream_list);
87    struct coreaudio_stream	*stream;
88};
89
90/* coreaudio factory */
91struct coreaudio_factory
92{
93    pjmedia_aud_dev_factory	 base;
94    pj_pool_t			*base_pool;
95    pj_pool_t			*pool;
96    pj_pool_factory		*pf;
97    pj_mutex_t			*mutex;
98
99    unsigned			 dev_count;
100    struct coreaudio_dev_info	*dev_info;
101
102    AudioComponent		 io_comp;
103    pj_bool_t			 has_vpio;
104    struct stream_list		 streams;
105};
106
107/* Sound stream. */
108struct coreaudio_stream
109{
110    pjmedia_aud_stream	 	 base;   	 /**< Base stream  	  */
111    pjmedia_aud_param	 	 param;		 /**< Settings	          */
112    pj_pool_t			*pool;           /**< Memory pool.        */
113    struct coreaudio_factory	*cf;
114    struct stream_list		 list_entry;
115
116    pjmedia_aud_rec_cb   	 rec_cb;         /**< Capture callback.   */
117    pjmedia_aud_play_cb  	 play_cb;        /**< Playback callback.  */
118    void                	*user_data;      /**< Application data.   */
119
120    pj_timestamp	 	 play_timestamp;
121    pj_timestamp	 	 rec_timestamp;
122
123    pj_int16_t			*rec_buf;
124    unsigned		 	 rec_buf_count;
125    pj_int16_t			*play_buf;
126    unsigned		 	 play_buf_count;
127
128    pj_bool_t			 interrupted;
129    pj_bool_t		 	 quit_flag;
130    pj_bool_t			 running;
131
132    pj_bool_t		 	 rec_thread_initialized;
133    pj_thread_desc	 	 rec_thread_desc;
134    pj_thread_t			*rec_thread;
135
136    pj_bool_t		 	 play_thread_initialized;
137    pj_thread_desc	 	 play_thread_desc;
138    pj_thread_t			*play_thread;
139
140    AudioUnit		 	 io_units[2];
141    AudioStreamBasicDescription  streamFormat;
142    AudioBufferList		*audio_buf;
143
144    AudioConverterRef            resample;
145    pj_int16_t			*resample_buf;
146    void			*resample_buf_ptr;
147    unsigned		 	 resample_buf_count;
148    unsigned		 	 resample_buf_size;
149
150#if !COREAUDIO_MAC
151    AVAudioSession              *sess;
152#endif
153};
154
155/* Static variable */
156static struct coreaudio_factory *cf_instance = NULL;
157
158/* Prototypes */
159static pj_status_t ca_factory_init(pjmedia_aud_dev_factory *f);
160static pj_status_t ca_factory_destroy(pjmedia_aud_dev_factory *f);
161static pj_status_t ca_factory_refresh(pjmedia_aud_dev_factory *f);
162static unsigned    ca_factory_get_dev_count(pjmedia_aud_dev_factory *f);
163static pj_status_t ca_factory_get_dev_info(pjmedia_aud_dev_factory *f,
164					   unsigned index,
165					   pjmedia_aud_dev_info *info);
166static pj_status_t ca_factory_default_param(pjmedia_aud_dev_factory *f,
167					    unsigned index,
168					    pjmedia_aud_param *param);
169static pj_status_t ca_factory_create_stream(pjmedia_aud_dev_factory *f,
170					    const pjmedia_aud_param *param,
171					    pjmedia_aud_rec_cb rec_cb,
172					    pjmedia_aud_play_cb play_cb,
173					    void *user_data,
174					    pjmedia_aud_stream **p_aud_strm);
175
176static pj_status_t ca_stream_get_param(pjmedia_aud_stream *strm,
177				       pjmedia_aud_param *param);
178static pj_status_t ca_stream_get_cap(pjmedia_aud_stream *strm,
179				     pjmedia_aud_dev_cap cap,
180				     void *value);
181static pj_status_t ca_stream_set_cap(pjmedia_aud_stream *strm,
182				     pjmedia_aud_dev_cap cap,
183				     const void *value);
184static pj_status_t ca_stream_start(pjmedia_aud_stream *strm);
185static pj_status_t ca_stream_stop(pjmedia_aud_stream *strm);
186static pj_status_t ca_stream_destroy(pjmedia_aud_stream *strm);
187static pj_status_t create_audio_unit(AudioComponent io_comp,
188				     AudioDeviceID dev_id,
189				     pjmedia_dir dir,
190				     struct coreaudio_stream *strm,
191				     AudioUnit *io_unit);
192#if !COREAUDIO_MAC && USE_AUDIO_SESSION_API != 0
193static void interruptionListener(void *inClientData, UInt32 inInterruption);
194static void propListener(void *                 inClientData,
195                         AudioSessionPropertyID inID,
196                         UInt32                 inDataSize,
197                         const void *           inData);
198#endif
199
200/* Operations */
201static pjmedia_aud_dev_factory_op factory_op =
202{
203    &ca_factory_init,
204    &ca_factory_destroy,
205    &ca_factory_get_dev_count,
206    &ca_factory_get_dev_info,
207    &ca_factory_default_param,
208    &ca_factory_create_stream,
209    &ca_factory_refresh
210};
211
212static pjmedia_aud_stream_op stream_op =
213{
214    &ca_stream_get_param,
215    &ca_stream_get_cap,
216    &ca_stream_set_cap,
217    &ca_stream_start,
218    &ca_stream_stop,
219    &ca_stream_destroy
220};
221
222
223/****************************************************************************
224 * Factory operations
225 */
226/*
227 * Init coreaudio audio driver.
228 */
229pjmedia_aud_dev_factory* pjmedia_coreaudio_factory(pj_pool_factory *pf)
230{
231    struct coreaudio_factory *f;
232    pj_pool_t *pool;
233
234    pool = pj_pool_create(pf, "core audio base", 1000, 1000, NULL);
235    f = PJ_POOL_ZALLOC_T(pool, struct coreaudio_factory);
236    f->pf = pf;
237    f->base_pool = pool;
238    f->base.op = &factory_op;
239
240    return &f->base;
241}
242
243
244/* API: init factory */
245static pj_status_t ca_factory_init(pjmedia_aud_dev_factory *f)
246{
247    struct coreaudio_factory *cf = (struct coreaudio_factory*)f;
248    AudioComponentDescription desc;
249    pj_status_t status;
250#if !COREAUDIO_MAC
251    unsigned i;
252#endif
253
254    pj_list_init(&cf->streams);
255    status = pj_mutex_create_recursive(cf->base_pool,
256				       "coreaudio",
257				       &cf->mutex);
258    if (status != PJ_SUCCESS)
259	return status;
260
261    desc.componentType = kAudioUnitType_Output;
262#if COREAUDIO_MAC
263    desc.componentSubType = kAudioUnitSubType_HALOutput;
264#else
265    desc.componentSubType = kAudioUnitSubType_RemoteIO;
266#endif
267    desc.componentManufacturer = kAudioUnitManufacturer_Apple;
268    desc.componentFlags = 0;
269    desc.componentFlagsMask = 0;
270
271    cf->io_comp = AudioComponentFindNext(NULL, &desc);
272    if (cf->io_comp == NULL)
273	return PJMEDIA_EAUD_INIT; // cannot find IO unit;
274
275    desc.componentSubType = kAudioUnitSubType_VoiceProcessingIO;
276    if (AudioComponentFindNext(NULL, &desc) != NULL)
277    	cf->has_vpio = PJ_TRUE;
278
279    status = ca_factory_refresh(f);
280    if (status != PJ_SUCCESS)
281	return status;
282
283#if !COREAUDIO_MAC
284    cf->pool = pj_pool_create(cf->pf, "core audio", 1000, 1000, NULL);
285    cf->dev_count = 1;
286    cf->dev_info = (struct coreaudio_dev_info*)
287   		   pj_pool_calloc(cf->pool, cf->dev_count,
288   		   sizeof(struct coreaudio_dev_info));
289    for (i = 0; i < cf->dev_count; i++) {
290 	struct coreaudio_dev_info *cdi;
291
292 	cdi = &cf->dev_info[i];
293 	pj_bzero(cdi, sizeof(*cdi));
294 	cdi->dev_id = 0;
295 	strcpy(cdi->info.name, "iPhone IO device");
296 	strcpy(cdi->info.driver, "apple");
297 	cdi->info.input_count = 1;
298 	cdi->info.output_count = 1;
299 	cdi->info.default_samples_per_sec = 8000;
300
301	/* Set the device capabilities here */
302 	cdi->info.caps = PJMEDIA_AUD_DEV_CAP_INPUT_LATENCY |
303			 PJMEDIA_AUD_DEV_CAP_OUTPUT_LATENCY |
304			 PJMEDIA_AUD_DEV_CAP_OUTPUT_VOLUME_SETTING |
305#if USE_AUDIO_SESSION_API != 0
306			 PJMEDIA_AUD_DEV_CAP_INPUT_ROUTE |
307			 PJMEDIA_AUD_DEV_CAP_OUTPUT_ROUTE |
308#endif
309			 PJMEDIA_AUD_DEV_CAP_EC;
310 	cdi->info.routes = PJMEDIA_AUD_DEV_ROUTE_LOUDSPEAKER |
311			   PJMEDIA_AUD_DEV_ROUTE_EARPIECE |
312			   PJMEDIA_AUD_DEV_ROUTE_BLUETOOTH;
313
314	PJ_LOG(4, (THIS_FILE, " dev_id %d: %s  (in=%d, out=%d) %dHz",
315		   i,
316		   cdi->info.name,
317		   cdi->info.input_count,
318		   cdi->info.output_count,
319		   cdi->info.default_samples_per_sec));
320    }
321
322#if USE_AUDIO_SESSION_API != 0
323    {
324        OSStatus ostatus;
325
326        /* Initialize the Audio Session */
327        ostatus = AudioSessionInitialize(NULL, NULL, interruptionListener,
328                                         NULL);
329        if (ostatus != kAudioSessionNoError) {
330            PJ_LOG(4, (THIS_FILE,
331                       "Warning: cannot initialize audio session services (%i)",
332                       ostatus));
333        }
334
335        /* Listen for audio routing change notifications. */
336#if USE_AUDIO_ROUTE_CHANGE_PROP_LISTENER != 0
337        ostatus = AudioSessionAddPropertyListener(
338                      kAudioSessionProperty_AudioRouteChange,
339		      propListener, cf);
340        if (ostatus != kAudioSessionNoError) {
341            PJ_LOG(4, (THIS_FILE,
342                       "Warning: cannot listen for audio route change "
343                       "notifications (%i)", ostatus));
344        }
345#endif
346    }
347#endif
348
349#if SETUP_AV_AUDIO_SESSION
350    /* Initialize audio session category and mode */
351    {
352	AVAudioSession *sess = [AVAudioSession sharedInstance];
353	pj_bool_t err;
354
355	if ([sess respondsToSelector:@selector(setCategory:withOptions:error:)])
356	{
357	    err = [sess setCategory:AVAudioSessionCategoryPlayAndRecord
358		        withOptions:AVAudioSessionCategoryOptionAllowBluetooth
359		        error:nil] != YES;
360        } else {
361    	    err = [sess setCategory:AVAudioSessionCategoryPlayAndRecord
362		        error:nil] != YES;
363        }
364	if (err) {
365            PJ_LOG(3, (THIS_FILE,
366   	               "Warning: failed settting audio session category"));
367	}
368
369	if ([sess respondsToSelector:@selector(setMode:error:)] &&
370	    [sess setMode:AVAudioSessionModeVoiceChat error:nil] != YES)
371	{
372	    PJ_LOG(3, (THIS_FILE, "Warning: failed settting audio mode"));
373	}
374    }
375#endif
376
377    cf_instance = cf;
378#endif
379
380    PJ_LOG(4, (THIS_FILE, "core audio initialized"));
381
382    return PJ_SUCCESS;
383}
384
385/* API: destroy factory */
386static pj_status_t ca_factory_destroy(pjmedia_aud_dev_factory *f)
387{
388    struct coreaudio_factory *cf = (struct coreaudio_factory*)f;
389    pj_pool_t *pool;
390
391    pj_assert(cf);
392    pj_assert(cf->base_pool);
393    pj_assert(pj_list_empty(&cf->streams));
394
395#if !COREAUDIO_MAC
396#if USE_AUDIO_SESSION_API != 0 && USE_AUDIO_ROUTE_CHANGE_PROP_LISTENER != 0
397    AudioSessionRemovePropertyListenerWithUserData(
398        kAudioSessionProperty_AudioRouteChange, propListener, cf);
399#endif
400#endif
401
402    if (cf->pool) {
403	pj_pool_release(cf->pool);
404	cf->pool = NULL;
405    }
406
407    if (cf->mutex) {
408	pj_mutex_lock(cf->mutex);
409	cf_instance = NULL;
410	pj_mutex_unlock(cf->mutex);
411	pj_mutex_destroy(cf->mutex);
412	cf->mutex = NULL;
413    }
414
415    pool = cf->base_pool;
416    cf->base_pool = NULL;
417    pj_pool_release(pool);
418
419    return PJ_SUCCESS;
420}
421
422/* API: refresh the device list */
423static pj_status_t ca_factory_refresh(pjmedia_aud_dev_factory *f)
424{
425#if !COREAUDIO_MAC
426    /* iPhone doesn't support refreshing the device list */
427    PJ_UNUSED_ARG(f);
428    return PJ_SUCCESS;
429#else
430    struct coreaudio_factory *cf = (struct coreaudio_factory*)f;
431    unsigned i;
432    unsigned dev_count;
433    AudioObjectPropertyAddress addr;
434    AudioDeviceID *dev_ids;
435    UInt32 buf_size, dev_size, size = sizeof(AudioDeviceID);
436    AudioBufferList *buf = NULL;
437    OSStatus ostatus;
438
439    if (cf->pool != NULL) {
440	pj_pool_release(cf->pool);
441	cf->pool = NULL;
442    }
443
444    cf->dev_count = 0;
445    cf->pool = pj_pool_create(cf->pf, "core audio", 1000, 1000, NULL);
446
447    /* Find out how many audio devices there are */
448    addr.mSelector = kAudioHardwarePropertyDevices;
449    addr.mScope = kAudioObjectPropertyScopeGlobal;
450    addr.mElement = kAudioObjectPropertyElementMaster;
451    ostatus = AudioObjectGetPropertyDataSize(kAudioObjectSystemObject, &addr,
452                                             0, NULL, &dev_size);
453    if (ostatus != noErr) {
454	dev_size = 0;
455    }
456
457    /* Calculate the number of audio devices available */
458    dev_count = dev_size / size;
459    if (dev_count==0) {
460  	PJ_LOG(4,(THIS_FILE, "core audio found no sound devices"));
461  	/* Enabling this will cause pjsua-lib initialization to fail when
462  	 * there is no sound device installed in the system, even when pjsua
463  	 * has been run with --null-audio. Moreover, it might be better to
464  	 * think that the core audio backend initialization is successful,
465  	 * regardless there is no audio device installed, as later application
466  	 * can check it using get_dev_count().
467  	return PJMEDIA_EAUD_NODEV;
468  	 */
469  	return PJ_SUCCESS;
470    }
471    PJ_LOG(4, (THIS_FILE, "core audio detected %d devices",
472	       dev_count));
473
474    /* Get all the audio device IDs */
475    dev_ids = (AudioDeviceID *)pj_pool_calloc(cf->pool, dev_count, size);
476    if (!dev_ids)
477	return PJ_ENOMEM;
478    ostatus = AudioObjectGetPropertyData(kAudioObjectSystemObject, &addr,
479					 0, NULL,
480				         &dev_size, (void *)dev_ids);
481    if (ostatus != noErr ) {
482	/* This should not happen since we have successfully retrieved
483	 * the property data size before
484	 */
485	return PJMEDIA_EAUD_INIT;
486    }
487
488    if (dev_size > 1) {
489	AudioDeviceID dev_id = kAudioObjectUnknown;
490	unsigned idx = 0;
491
492	/* Find default audio input device */
493	addr.mSelector = kAudioHardwarePropertyDefaultInputDevice;
494	addr.mScope = kAudioObjectPropertyScopeGlobal;
495	addr.mElement = kAudioObjectPropertyElementMaster;
496	size = sizeof(dev_id);
497
498	ostatus = AudioObjectGetPropertyData(kAudioObjectSystemObject,
499					     &addr, 0, NULL,
500					     &size, (void *)&dev_id);
501	if (ostatus == noErr && dev_id != dev_ids[idx]) {
502	    AudioDeviceID temp_id = dev_ids[idx];
503
504	    for (i = idx + 1; i < dev_count; i++) {
505		if (dev_ids[i] == dev_id) {
506		    dev_ids[idx++] = dev_id;
507		    dev_ids[i] = temp_id;
508		    break;
509		}
510	    }
511	}
512
513	/* Find default audio output device */
514	addr.mSelector = kAudioHardwarePropertyDefaultOutputDevice;
515	ostatus = AudioObjectGetPropertyData(kAudioObjectSystemObject,
516					     &addr, 0, NULL,
517					     &size, (void *)&dev_id);
518	if (ostatus == noErr && dev_id != dev_ids[idx]) {
519	    AudioDeviceID temp_id = dev_ids[idx];
520
521	    for (i = idx + 1; i < dev_count; i++) {
522		if (dev_ids[i] == dev_id) {
523		    dev_ids[idx] = dev_id;
524		    dev_ids[i] = temp_id;
525		    break;
526		}
527	    }
528	}
529    }
530
531    /* Build the devices' info */
532    cf->dev_info = (struct coreaudio_dev_info*)
533  		   pj_pool_calloc(cf->pool, dev_count,
534  		   sizeof(struct coreaudio_dev_info));
535    buf_size = 0;
536    for (i = 0; i < dev_count; i++) {
537	struct coreaudio_dev_info *cdi;
538	Float64 sampleRate;
539
540	cdi = &cf->dev_info[i];
541	pj_bzero(cdi, sizeof(*cdi));
542	cdi->dev_id = dev_ids[i];
543
544	/* Get device name */
545	addr.mSelector = kAudioDevicePropertyDeviceName;
546	addr.mScope = kAudioObjectPropertyScopeGlobal;
547	addr.mElement = kAudioObjectPropertyElementMaster;
548	size = sizeof(cdi->info.name);
549	AudioObjectGetPropertyData(cdi->dev_id, &addr,
550				   0, NULL,
551			           &size, (void *)cdi->info.name);
552
553	strcpy(cdi->info.driver, "core audio");
554
555        /* Get the number of input channels */
556	addr.mSelector = kAudioDevicePropertyStreamConfiguration;
557	addr.mScope = kAudioDevicePropertyScopeInput;
558	size = 0;
559	ostatus = AudioObjectGetPropertyDataSize(cdi->dev_id, &addr,
560	                                         0, NULL, &size);
561	if (ostatus == noErr && size > 0) {
562
563	    if (size > buf_size) {
564		buf = pj_pool_alloc(cf->pool, size);
565		buf_size = size;
566	    }
567	    if (buf) {
568		UInt32 idx;
569
570		/* Get the input stream configuration */
571		ostatus = AudioObjectGetPropertyData(cdi->dev_id, &addr,
572						     0, NULL,
573						     &size, buf);
574		if (ostatus == noErr) {
575		    /* Count the total number of input channels in
576		     * the stream
577		     */
578		    for (idx = 0; idx < buf->mNumberBuffers; idx++) {
579			cdi->info.input_count +=
580			    buf->mBuffers[idx].mNumberChannels;
581		    }
582		}
583	    }
584	}
585
586        /* Get the number of output channels */
587	addr.mScope = kAudioDevicePropertyScopeOutput;
588	size = 0;
589	ostatus = AudioObjectGetPropertyDataSize(cdi->dev_id, &addr,
590	                                         0, NULL, &size);
591	if (ostatus == noErr && size > 0) {
592
593	    if (size > buf_size) {
594		buf = pj_pool_alloc(cf->pool, size);
595		buf_size = size;
596	    }
597	    if (buf) {
598		UInt32 idx;
599
600		/* Get the output stream configuration */
601		ostatus = AudioObjectGetPropertyData(cdi->dev_id, &addr,
602						     0, NULL,
603						     &size, buf);
604		if (ostatus == noErr) {
605		    /* Count the total number of output channels in
606		     * the stream
607		     */
608		    for (idx = 0; idx < buf->mNumberBuffers; idx++) {
609			cdi->info.output_count +=
610			    buf->mBuffers[idx].mNumberChannels;
611		    }
612		}
613	    }
614	}
615
616	/* Get default sample rate */
617	addr.mSelector = kAudioDevicePropertyNominalSampleRate;
618	addr.mScope = kAudioObjectPropertyScopeGlobal;
619	size = sizeof(Float64);
620	ostatus = AudioObjectGetPropertyData (cdi->dev_id, &addr,
621		                              0, NULL,
622		                              &size, &sampleRate);
623	cdi->info.default_samples_per_sec = (ostatus == noErr ?
624					    sampleRate:
625					    16000);
626
627	/* Set device capabilities here */
628	if (cdi->info.input_count > 0) {
629	    cdi->info.caps |= PJMEDIA_AUD_DEV_CAP_INPUT_LATENCY;
630	}
631	if (cdi->info.output_count > 0) {
632	    cdi->info.caps |= PJMEDIA_AUD_DEV_CAP_OUTPUT_LATENCY;
633	    addr.mSelector = kAudioDevicePropertyVolumeScalar;
634	    addr.mScope = kAudioDevicePropertyScopeOutput;
635	    if (AudioObjectHasProperty(cdi->dev_id, &addr)) {
636		cdi->info.caps |= PJMEDIA_AUD_DEV_CAP_OUTPUT_VOLUME_SETTING;
637	    }
638	}
639	if (cf->has_vpio) {
640	    cdi->info.caps |= PJMEDIA_AUD_DEV_CAP_EC;
641	}
642
643	cf->dev_count++;
644
645	PJ_LOG(4, (THIS_FILE, " dev_id %d: %s  (in=%d, out=%d) %dHz",
646	       cdi->dev_id,
647	       cdi->info.name,
648	       cdi->info.input_count,
649	       cdi->info.output_count,
650	       cdi->info.default_samples_per_sec));
651    }
652
653    return PJ_SUCCESS;
654#endif
655}
656
657/* API: get number of devices */
658static unsigned ca_factory_get_dev_count(pjmedia_aud_dev_factory *f)
659{
660    struct coreaudio_factory *cf = (struct coreaudio_factory*)f;
661    return cf->dev_count;
662}
663
664/* API: get device info */
665static pj_status_t ca_factory_get_dev_info(pjmedia_aud_dev_factory *f,
666					   unsigned index,
667					   pjmedia_aud_dev_info *info)
668{
669    struct coreaudio_factory *cf = (struct coreaudio_factory*)f;
670
671    PJ_ASSERT_RETURN(index < cf->dev_count, PJMEDIA_EAUD_INVDEV);
672
673    pj_memcpy(info, &cf->dev_info[index].info, sizeof(*info));
674
675    return PJ_SUCCESS;
676}
677
678/* API: create default device parameter */
679static pj_status_t ca_factory_default_param(pjmedia_aud_dev_factory *f,
680					    unsigned index,
681					    pjmedia_aud_param *param)
682{
683    struct coreaudio_factory *cf = (struct coreaudio_factory*)f;
684    struct coreaudio_dev_info *di = &cf->dev_info[index];
685
686    PJ_ASSERT_RETURN(index < cf->dev_count, PJMEDIA_EAUD_INVDEV);
687
688    pj_bzero(param, sizeof(*param));
689    if (di->info.input_count && di->info.output_count) {
690	param->dir = PJMEDIA_DIR_CAPTURE_PLAYBACK;
691	param->rec_id = index;
692	param->play_id = index;
693    } else if (di->info.input_count) {
694	param->dir = PJMEDIA_DIR_CAPTURE;
695	param->rec_id = index;
696	param->play_id = PJMEDIA_AUD_INVALID_DEV;
697    } else if (di->info.output_count) {
698	param->dir = PJMEDIA_DIR_PLAYBACK;
699	param->play_id = index;
700	param->rec_id = PJMEDIA_AUD_INVALID_DEV;
701    } else {
702	return PJMEDIA_EAUD_INVDEV;
703    }
704
705    /* Set the mandatory settings here */
706    param->clock_rate = di->info.default_samples_per_sec;
707    param->channel_count = 1;
708    param->samples_per_frame = di->info.default_samples_per_sec * 20 / 1000;
709    param->bits_per_sample = 16;
710
711    /* Set the param for device capabilities here */
712    param->flags = PJMEDIA_AUD_DEV_CAP_INPUT_LATENCY |
713		   PJMEDIA_AUD_DEV_CAP_OUTPUT_LATENCY;
714    param->input_latency_ms = PJMEDIA_SND_DEFAULT_REC_LATENCY;
715    param->output_latency_ms = PJMEDIA_SND_DEFAULT_PLAY_LATENCY;
716
717    return PJ_SUCCESS;
718}
719
720OSStatus resampleProc(AudioConverterRef             inAudioConverter,
721		      UInt32                        *ioNumberDataPackets,
722		      AudioBufferList               *ioData,
723		      AudioStreamPacketDescription  **outDataPacketDescription,
724		      void                          *inUserData)
725{
726    struct coreaudio_stream *strm = (struct coreaudio_stream*)inUserData;
727
728    if (*ioNumberDataPackets > strm->resample_buf_size)
729	*ioNumberDataPackets = strm->resample_buf_size;
730
731    ioData->mNumberBuffers = 1;
732    ioData->mBuffers[0].mNumberChannels = strm->streamFormat.mChannelsPerFrame;
733    ioData->mBuffers[0].mData = strm->resample_buf_ptr;
734    ioData->mBuffers[0].mDataByteSize = *ioNumberDataPackets *
735					strm->streamFormat.mChannelsPerFrame *
736					strm->param.bits_per_sample >> 3;
737
738    return noErr;
739}
740
741static OSStatus resample_callback(void                       *inRefCon,
742				  AudioUnitRenderActionFlags *ioActionFlags,
743				  const AudioTimeStamp       *inTimeStamp,
744				  UInt32                      inBusNumber,
745				  UInt32                      inNumberFrames,
746				  AudioBufferList            *ioData)
747{
748    struct coreaudio_stream *strm = (struct coreaudio_stream*)inRefCon;
749    OSStatus ostatus;
750    pj_status_t status = 0;
751    unsigned nsamples;
752    AudioBufferList *buf = strm->audio_buf;
753    pj_int16_t *input;
754    UInt32 resampleSize;
755
756    pj_assert(!strm->quit_flag);
757
758    /* Known cases of callback's thread:
759     * - The thread may be changed in the middle of a session
760     *   it happens when plugging/unplugging headphone.
761     * - The same thread may be reused in consecutive sessions. The first
762     *   session will leave TLS set, but release the TLS data address,
763     *   so the second session must re-register the callback's thread.
764     */
765    if (strm->rec_thread_initialized == 0 || !pj_thread_is_registered())
766    {
767	pj_bzero(strm->rec_thread_desc, sizeof(pj_thread_desc));
768	status = pj_thread_register("ca_rec", strm->rec_thread_desc,
769				    &strm->rec_thread);
770	strm->rec_thread_initialized = 1;
771	PJ_LOG(5,(THIS_FILE, "Recorder thread started, (%i frames)",
772		  inNumberFrames));
773    }
774
775    buf->mBuffers[0].mData = NULL;
776    buf->mBuffers[0].mDataByteSize = inNumberFrames *
777				     strm->streamFormat.mChannelsPerFrame;
778    /* Render the unit to get input data */
779    ostatus = AudioUnitRender(strm->io_units[0],
780			      ioActionFlags,
781			      inTimeStamp,
782			      inBusNumber,
783			      inNumberFrames,
784			      buf);
785
786    if (ostatus != noErr) {
787	PJ_LOG(5, (THIS_FILE, "Core audio unit render error %i", ostatus));
788	goto on_break;
789    }
790    input = (pj_int16_t *)buf->mBuffers[0].mData;
791
792    resampleSize = strm->resample_buf_size;
793    nsamples = inNumberFrames * strm->param.channel_count +
794	       strm->resample_buf_count;
795
796    if (nsamples >= resampleSize) {
797	pjmedia_frame frame;
798	UInt32 resampleOutput = strm->param.samples_per_frame /
799				strm->streamFormat.mChannelsPerFrame;
800	AudioBufferList ab;
801
802	frame.type = PJMEDIA_FRAME_TYPE_AUDIO;
803	frame.buf = (void*) strm->rec_buf;
804	frame.size = strm->param.samples_per_frame *
805		     strm->param.bits_per_sample >> 3;
806	frame.bit_info = 0;
807
808	ab.mNumberBuffers = 1;
809	ab.mBuffers[0].mNumberChannels = strm->streamFormat.mChannelsPerFrame;
810	ab.mBuffers[0].mData = strm->rec_buf;
811	ab.mBuffers[0].mDataByteSize = frame.size;
812
813	/* If buffer is not empty, combine the buffer with the just incoming
814	 * samples, then call put_frame.
815	 */
816	if (strm->resample_buf_count) {
817	    unsigned chunk_count = resampleSize - strm->resample_buf_count;
818	    pjmedia_copy_samples(strm->resample_buf + strm->resample_buf_count,
819				 input, chunk_count);
820
821	    /* Do the resample */
822
823	    strm->resample_buf_ptr = strm->resample_buf;
824	    ostatus = AudioConverterFillComplexBuffer(strm->resample,
825						      resampleProc,
826						      strm,
827						      &resampleOutput,
828						      &ab,
829						      NULL);
830	    if (ostatus != noErr) {
831		goto on_break;
832	    }
833	    frame.timestamp.u64 = strm->rec_timestamp.u64;
834
835	    status = (*strm->rec_cb)(strm->user_data, &frame);
836
837	    input = input + chunk_count;
838	    nsamples -= resampleSize;
839	    strm->resample_buf_count = 0;
840	    strm->rec_timestamp.u64 += strm->param.samples_per_frame /
841				       strm->param.channel_count;
842	}
843
844
845 	/* Give all frames we have */
846 	while (nsamples >= resampleSize && status == 0) {
847 	    frame.timestamp.u64 = strm->rec_timestamp.u64;
848
849	    /* Do the resample */
850	    strm->resample_buf_ptr = input;
851	    ab.mBuffers[0].mDataByteSize = frame.size;
852	    resampleOutput = strm->param.samples_per_frame /
853			     strm->streamFormat.mChannelsPerFrame;
854	    ostatus = AudioConverterFillComplexBuffer(strm->resample,
855						      resampleProc,
856						      strm,
857						      &resampleOutput,
858						      &ab,
859						      NULL);
860	    if (ostatus != noErr) {
861		goto on_break;
862	    }
863
864 	    status = (*strm->rec_cb)(strm->user_data, &frame);
865
866 	    input = (pj_int16_t*) input + resampleSize;
867 	    nsamples -= resampleSize;
868 	    strm->rec_timestamp.u64 += strm->param.samples_per_frame /
869				       strm->param.channel_count;
870 	}
871
872	/* Store the remaining samples into the buffer */
873	if (nsamples && status == 0) {
874	    strm->resample_buf_count = nsamples;
875	    pjmedia_copy_samples(strm->resample_buf, input,
876				 nsamples);
877	}
878
879    } else {
880	/* Not enough samples, let's just store them in the buffer */
881	pjmedia_copy_samples(strm->resample_buf + strm->resample_buf_count,
882			     input,
883			     inNumberFrames * strm->param.channel_count);
884	strm->resample_buf_count += inNumberFrames *
885				    strm->param.channel_count;
886    }
887
888    return noErr;
889
890on_break:
891    return -1;
892}
893
894static OSStatus input_callback(void                       *inRefCon,
895                               AudioUnitRenderActionFlags *ioActionFlags,
896                               const AudioTimeStamp       *inTimeStamp,
897                               UInt32                      inBusNumber,
898                               UInt32                      inNumberFrames,
899                               AudioBufferList            *ioData)
900{
901    struct coreaudio_stream *strm = (struct coreaudio_stream*)inRefCon;
902    OSStatus ostatus;
903    pj_status_t status = 0;
904    unsigned nsamples;
905    AudioBufferList *buf = strm->audio_buf;
906    pj_int16_t *input;
907
908    pj_assert(!strm->quit_flag);
909
910    /* Known cases of callback's thread:
911     * - The thread may be changed in the middle of a session
912     *   it happens when plugging/unplugging headphone.
913     * - The same thread may be reused in consecutive sessions. The first
914     *   session will leave TLS set, but release the TLS data address,
915     *   so the second session must re-register the callback's thread.
916     */
917    if (strm->rec_thread_initialized == 0 || !pj_thread_is_registered())
918    {
919	pj_bzero(strm->rec_thread_desc, sizeof(pj_thread_desc));
920	status = pj_thread_register("ca_rec", strm->rec_thread_desc,
921				    &strm->rec_thread);
922	strm->rec_thread_initialized = 1;
923	PJ_LOG(5,(THIS_FILE, "Recorder thread started, (%i frames)",
924		  inNumberFrames));
925    }
926
927    buf->mBuffers[0].mData = NULL;
928    buf->mBuffers[0].mDataByteSize = inNumberFrames *
929				     strm->streamFormat.mChannelsPerFrame;
930    /* Render the unit to get input data */
931    ostatus = AudioUnitRender(strm->io_units[0],
932			      ioActionFlags,
933			      inTimeStamp,
934			      inBusNumber,
935			      inNumberFrames,
936			      buf);
937
938    if (ostatus != noErr) {
939	PJ_LOG(5, (THIS_FILE, "Core audio unit render error %i", ostatus));
940	goto on_break;
941    }
942    input = (pj_int16_t *)buf->mBuffers[0].mData;
943
944    /* Calculate number of samples we've got */
945    nsamples = inNumberFrames * strm->param.channel_count +
946	       strm->rec_buf_count;
947    if (nsamples >= strm->param.samples_per_frame) {
948	pjmedia_frame frame;
949
950	frame.type = PJMEDIA_FRAME_TYPE_AUDIO;
951	frame.size = strm->param.samples_per_frame *
952		     strm->param.bits_per_sample >> 3;
953	frame.bit_info = 0;
954
955 	/* If buffer is not empty, combine the buffer with the just incoming
956 	 * samples, then call put_frame.
957 	 */
958 	if (strm->rec_buf_count) {
959 	    unsigned chunk_count = 0;
960
961 	    chunk_count = strm->param.samples_per_frame - strm->rec_buf_count;
962 	    pjmedia_copy_samples(strm->rec_buf + strm->rec_buf_count,
963 				 input, chunk_count);
964
965 	    frame.buf = (void*) strm->rec_buf;
966 	    frame.timestamp.u64 = strm->rec_timestamp.u64;
967
968 	    status = (*strm->rec_cb)(strm->user_data, &frame);
969
970 	    input = input + chunk_count;
971 	    nsamples -= strm->param.samples_per_frame;
972 	    strm->rec_buf_count = 0;
973 	    strm->rec_timestamp.u64 += strm->param.samples_per_frame /
974				       strm->param.channel_count;
975 	}
976
977 	/* Give all frames we have */
978 	while (nsamples >= strm->param.samples_per_frame && status == 0) {
979 	    frame.buf = (void*) input;
980 	    frame.timestamp.u64 = strm->rec_timestamp.u64;
981
982 	    status = (*strm->rec_cb)(strm->user_data, &frame);
983
984 	    input = (pj_int16_t*) input + strm->param.samples_per_frame;
985 	    nsamples -= strm->param.samples_per_frame;
986 	    strm->rec_timestamp.u64 += strm->param.samples_per_frame /
987				       strm->param.channel_count;
988 	}
989
990 	/* Store the remaining samples into the buffer */
991 	if (nsamples && status == 0) {
992 	    strm->rec_buf_count = nsamples;
993 	    pjmedia_copy_samples(strm->rec_buf, input,
994 			         nsamples);
995 	}
996
997     } else {
998 	/* Not enough samples, let's just store them in the buffer */
999 	pjmedia_copy_samples(strm->rec_buf + strm->rec_buf_count,
1000 			     input,
1001 			     inNumberFrames * strm->param.channel_count);
1002 	strm->rec_buf_count += inNumberFrames * strm->param.channel_count;
1003     }
1004
1005    return noErr;
1006
1007on_break:
1008    return -1;
1009}
1010
1011/* Copy 16-bit signed int samples to a destination buffer, which can be
1012 * either 16-bit signed int, or float.
1013 */
1014static void copy_samples(void *dst, unsigned *dst_pos, unsigned dst_elmt_size,
1015			 pj_int16_t *src, unsigned nsamples)
1016{
1017   if (dst_elmt_size == sizeof(pj_int16_t)) {
1018   	/* Destination is also 16-bit signed int. */
1019	pjmedia_copy_samples((pj_int16_t*)dst + *dst_pos, src, nsamples);
1020   } else {
1021   	/* Convert it first to float. */
1022   	unsigned i;
1023   	float *fdst = (float *)dst;
1024
1025   	pj_assert(dst_elmt_size == sizeof(Float32));
1026
1027   	for (i = 0; i< nsamples; i++) {
1028   	    /* Value needs to be between -1.0 to 1.0 */
1029   	    fdst[*dst_pos + i] = (float)src[i] / 32768.0f;
1030   	}
1031   }
1032   *dst_pos += nsamples;
1033}
1034
1035static OSStatus output_renderer(void                       *inRefCon,
1036                                AudioUnitRenderActionFlags *ioActionFlags,
1037                                const AudioTimeStamp       *inTimeStamp,
1038                                UInt32                      inBusNumber,
1039                                UInt32                      inNumberFrames,
1040                                AudioBufferList            *ioData)
1041{
1042    struct coreaudio_stream *stream = (struct coreaudio_stream*)inRefCon;
1043    pj_status_t status = 0;
1044    unsigned nsamples_req = inNumberFrames * stream->param.channel_count;
1045    void *output = ioData->mBuffers[0].mData;
1046    unsigned elmt_size = ioData->mBuffers[0].mDataByteSize / inNumberFrames /
1047    			 stream->param.channel_count;
1048    unsigned output_pos = 0;
1049
1050    pj_assert(!stream->quit_flag);
1051
1052    /* Known cases of callback's thread:
1053     * - The thread may be changed in the middle of a session
1054     *   it happens when plugging/unplugging headphone.
1055     * - The same thread may be reused in consecutive sessions. The first
1056     *   session will leave TLS set, but release the TLS data address,
1057     *   so the second session must re-register the callback's thread.
1058     */
1059    if (stream->play_thread_initialized == 0 || !pj_thread_is_registered())
1060    {
1061	pj_bzero(stream->play_thread_desc, sizeof(pj_thread_desc));
1062	status = pj_thread_register("coreaudio", stream->play_thread_desc,
1063				    &stream->play_thread);
1064	stream->play_thread_initialized = 1;
1065	PJ_LOG(5,(THIS_FILE, "Player thread started, (%i frames)",
1066		  inNumberFrames));
1067    }
1068
1069
1070    /* Check if any buffered samples */
1071    if (stream->play_buf_count) {
1072	/* samples buffered >= requested by sound device */
1073	if (stream->play_buf_count >= nsamples_req) {
1074	    copy_samples(output, &output_pos, elmt_size,
1075	    		 stream->play_buf, nsamples_req);
1076	    stream->play_buf_count -= nsamples_req;
1077	    pjmedia_move_samples(stream->play_buf,
1078				 stream->play_buf + nsamples_req,
1079				 stream->play_buf_count);
1080	    nsamples_req = 0;
1081
1082	    return noErr;
1083	}
1084
1085	/* samples buffered < requested by sound device */
1086	copy_samples(output, &output_pos, elmt_size,
1087		     stream->play_buf, stream->play_buf_count);
1088	nsamples_req -= stream->play_buf_count;
1089	stream->play_buf_count = 0;
1090    }
1091
1092    /* Fill output buffer as requested */
1093    while (nsamples_req && status == 0) {
1094	pjmedia_frame frame;
1095
1096	frame.type = PJMEDIA_FRAME_TYPE_AUDIO;
1097	frame.size = stream->param.samples_per_frame *
1098		     stream->param.bits_per_sample >> 3;
1099	frame.timestamp.u64 = stream->play_timestamp.u64;
1100	frame.bit_info = 0;
1101
1102	if (nsamples_req >= stream->param.samples_per_frame) {
1103	    /* If the output buffer is 16-bit signed int, we can
1104	     * directly use the supplied buffer.
1105	     */
1106	    if (elmt_size == sizeof(pj_int16_t)) {
1107	    	frame.buf = (pj_int16_t *)output + output_pos;
1108	    } else {
1109	    	frame.buf = stream->play_buf;
1110	    }
1111	    status = (*stream->play_cb)(stream->user_data, &frame);
1112	    if (status != PJ_SUCCESS)
1113		goto on_break;
1114
1115	    if (frame.type != PJMEDIA_FRAME_TYPE_AUDIO)
1116		pj_bzero(frame.buf, frame.size);
1117
1118	    nsamples_req -= stream->param.samples_per_frame;
1119	    if (elmt_size == sizeof(pj_int16_t)) {
1120	    	output_pos += stream->param.samples_per_frame;
1121	    } else {
1122	    	copy_samples(output, &output_pos, elmt_size, stream->play_buf,
1123	    		     stream->param.samples_per_frame);
1124	    }
1125	} else {
1126	    frame.buf = stream->play_buf;
1127	    status = (*stream->play_cb)(stream->user_data, &frame);
1128	    if (status != PJ_SUCCESS)
1129		goto on_break;
1130
1131	    if (frame.type != PJMEDIA_FRAME_TYPE_AUDIO)
1132		pj_bzero(frame.buf, frame.size);
1133
1134	    copy_samples(output, &output_pos, elmt_size,
1135	    		 stream->play_buf, nsamples_req);
1136	    stream->play_buf_count = stream->param.samples_per_frame -
1137		                     nsamples_req;
1138	    pjmedia_move_samples(stream->play_buf,
1139				 stream->play_buf+nsamples_req,
1140				 stream->play_buf_count);
1141	    nsamples_req = 0;
1142	}
1143
1144	stream->play_timestamp.u64 += stream->param.samples_per_frame /
1145				      stream->param.channel_count;
1146    }
1147
1148    return noErr;
1149
1150on_break:
1151    return -1;
1152}
1153
1154#if !COREAUDIO_MAC && USE_AUDIO_SESSION_API != 0
1155static void propListener(void 			*inClientData,
1156			 AudioSessionPropertyID	inID,
1157			 UInt32                 inDataSize,
1158			 const void *           inData)
1159{
1160    struct coreaudio_factory *cf = (struct coreaudio_factory*)inClientData;
1161    struct stream_list *it, *itBegin;
1162    CFDictionaryRef routeDictionary;
1163    CFNumberRef reason;
1164    SInt32 reasonVal;
1165    pj_assert(cf);
1166
1167    if (inID != kAudioSessionProperty_AudioRouteChange)
1168	return;
1169
1170    routeDictionary = (CFDictionaryRef)inData;
1171    reason = (CFNumberRef)
1172	     CFDictionaryGetValue(
1173	         routeDictionary,
1174		 CFSTR(kAudioSession_AudioRouteChangeKey_Reason));
1175    CFNumberGetValue(reason, kCFNumberSInt32Type, &reasonVal);
1176
1177    if (reasonVal != kAudioSessionRouteChangeReason_OldDeviceUnavailable) {
1178	PJ_LOG(3, (THIS_FILE, "ignoring audio route change..."));
1179	return;
1180    }
1181
1182    PJ_LOG(3, (THIS_FILE, "audio route changed"));
1183
1184    pj_mutex_lock(cf->mutex);
1185    itBegin = &cf->streams;
1186    for (it = itBegin->next; it != itBegin; it = it->next) {
1187	if (it->stream->interrupted)
1188	    continue;
1189
1190	/*
1191	status = ca_stream_stop((pjmedia_aud_stream *)it->stream);
1192	status = ca_stream_start((pjmedia_aud_stream *)it->stream);
1193	if (status != PJ_SUCCESS) {
1194	    PJ_LOG(3, (THIS_FILE,
1195		       "Error: failed to restart the audio unit (%i)",
1196		       status));
1197	    continue;
1198	}
1199	PJ_LOG(3, (THIS_FILE, "core audio unit successfully restarted"));
1200	*/
1201    }
1202    pj_mutex_unlock(cf->mutex);
1203}
1204
1205static void interruptionListener(void *inClientData, UInt32 inInterruption)
1206{
1207    struct stream_list *it, *itBegin;
1208    pj_status_t status;
1209    static pj_thread_desc thread_desc;
1210    pj_thread_t *thread;
1211
1212    /* Register the thread with PJLIB, this is must for any external threads
1213     * which need to use the PJLIB framework.
1214     */
1215    if (!pj_thread_is_registered()) {
1216	pj_bzero(thread_desc, sizeof(pj_thread_desc));
1217	status = pj_thread_register("intListener", thread_desc, &thread);
1218    }
1219
1220    PJ_LOG(3, (THIS_FILE, "Session interrupted! --- %s ---",
1221	   inInterruption == kAudioSessionBeginInterruption ?
1222	   "Begin Interruption" : "End Interruption"));
1223
1224    if (!cf_instance)
1225	return;
1226
1227    pj_mutex_lock(cf_instance->mutex);
1228    itBegin = &cf_instance->streams;
1229    for (it = itBegin->next; it != itBegin; it = it->next) {
1230	if (inInterruption == kAudioSessionEndInterruption &&
1231	    it->stream->interrupted == PJ_TRUE)
1232	{
1233	    UInt32 audioCategory;
1234	    OSStatus ostatus;
1235
1236	    /* Make sure that your application can receive remote control
1237	     * events by adding the code:
1238	     *     [[UIApplication sharedApplication]
1239	     *      beginReceivingRemoteControlEvents];
1240	     * Otherwise audio unit will fail to restart while your
1241	     * application is in the background mode.
1242	     */
1243	    /* Make sure we set the correct audio category before restarting */
1244	    audioCategory = kAudioSessionCategory_PlayAndRecord;
1245	    ostatus = AudioSessionSetProperty(kAudioSessionProperty_AudioCategory,
1246					      sizeof(audioCategory),
1247					      &audioCategory);
1248	    if (ostatus != kAudioSessionNoError) {
1249		PJ_LOG(4, (THIS_FILE,
1250			   "Warning: cannot set the audio session category (%i)",
1251			   ostatus));
1252	    }
1253
1254	    /* Restart the stream */
1255	    status = ca_stream_start((pjmedia_aud_stream*)it->stream);
1256	    if (status != PJ_SUCCESS) {
1257		PJ_LOG(3, (THIS_FILE,
1258			   "Error: failed to restart the audio unit (%i)",
1259			   status));
1260		continue;
1261	    }
1262	    PJ_LOG(3, (THIS_FILE, "core audio unit successfully resumed"
1263		       " after interruption"));
1264	} else if (inInterruption == kAudioSessionBeginInterruption &&
1265		   it->stream->running == PJ_TRUE)
1266	{
1267	    status = ca_stream_stop((pjmedia_aud_stream*)it->stream);
1268	    it->stream->interrupted = PJ_TRUE;
1269	}
1270    }
1271    pj_mutex_unlock(cf_instance->mutex);
1272}
1273
1274#endif
1275
1276#if COREAUDIO_MAC
1277/* Internal: create audio converter for resampling the recorder device */
1278static pj_status_t create_audio_resample(struct coreaudio_stream     *strm,
1279					 AudioStreamBasicDescription *desc)
1280{
1281    OSStatus ostatus;
1282
1283    pj_assert(strm->streamFormat.mSampleRate != desc->mSampleRate);
1284    pj_assert(NULL == strm->resample);
1285    pj_assert(NULL == strm->resample_buf);
1286
1287    /* Create the audio converter */
1288    ostatus = AudioConverterNew(desc, &strm->streamFormat, &strm->resample);
1289    if (ostatus != noErr) {
1290	return PJMEDIA_AUDIODEV_ERRNO_FROM_COREAUDIO(ostatus);
1291    }
1292
1293    /*
1294     * Allocate the buffer required to hold enough input data
1295     */
1296    strm->resample_buf_size =  (unsigned)(desc->mSampleRate *
1297					  strm->param.samples_per_frame /
1298					  strm->param.clock_rate);
1299    strm->resample_buf = (pj_int16_t*)
1300			 pj_pool_alloc(strm->pool,
1301				       strm->resample_buf_size *
1302				       strm->param.bits_per_sample >> 3);
1303    if (!strm->resample_buf)
1304	return PJ_ENOMEM;
1305    strm->resample_buf_count = 0;
1306
1307    return PJ_SUCCESS;
1308}
1309#endif
1310
1311/* Internal: create audio unit for recorder/playback device */
1312static pj_status_t create_audio_unit(AudioComponent io_comp,
1313				     AudioDeviceID dev_id,
1314				     pjmedia_dir dir,
1315				     struct coreaudio_stream *strm,
1316				     AudioUnit *io_unit)
1317{
1318    OSStatus ostatus;
1319
1320#if !COREAUDIO_MAC
1321    strm->sess = [AVAudioSession sharedInstance];
1322#endif
1323
1324    /* Create an audio unit to interface with the device */
1325    ostatus = AudioComponentInstanceNew(io_comp, io_unit);
1326    if (ostatus != noErr) {
1327	return PJMEDIA_AUDIODEV_ERRNO_FROM_COREAUDIO(ostatus);
1328    }
1329
1330    /* Set audio unit's properties for capture device */
1331    if (dir & PJMEDIA_DIR_CAPTURE) {
1332	UInt32 enable = 1;
1333
1334	/* Enable input */
1335	ostatus = AudioUnitSetProperty(*io_unit,
1336	                               kAudioOutputUnitProperty_EnableIO,
1337	                               kAudioUnitScope_Input,
1338	                               1,
1339	                               &enable,
1340	                               sizeof(enable));
1341	if (ostatus != noErr && !strm->param.ec_enabled) {
1342	    PJ_LOG(4, (THIS_FILE,
1343		   "Warning: cannot enable IO of capture device %d",
1344		   dev_id));
1345	}
1346
1347	/* Disable output */
1348	if (!(dir & PJMEDIA_DIR_PLAYBACK)) {
1349	    enable = 0;
1350	    ostatus = AudioUnitSetProperty(*io_unit,
1351					   kAudioOutputUnitProperty_EnableIO,
1352					   kAudioUnitScope_Output,
1353					   0,
1354					   &enable,
1355					   sizeof(enable));
1356	    if (ostatus != noErr && !strm->param.ec_enabled) {
1357		PJ_LOG(4, (THIS_FILE,
1358		       "Warning: cannot disable IO of capture device %d",
1359		       dev_id));
1360	    }
1361	}
1362    }
1363
1364    /* Set audio unit's properties for playback device */
1365    if (dir & PJMEDIA_DIR_PLAYBACK) {
1366	UInt32 enable = 1;
1367
1368	/* Enable output */
1369	ostatus = AudioUnitSetProperty(*io_unit,
1370	                               kAudioOutputUnitProperty_EnableIO,
1371	                               kAudioUnitScope_Output,
1372	                               0,
1373	                               &enable,
1374	                               sizeof(enable));
1375	if (ostatus != noErr && !strm->param.ec_enabled)
1376	{
1377	    PJ_LOG(4, (THIS_FILE,
1378		   "Warning: cannot enable IO of playback device %d",
1379		   dev_id));
1380	}
1381
1382    }
1383
1384#if COREAUDIO_MAC
1385    if (!strm->param.ec_enabled) {
1386    	/* When using VPIO on Mac, we shouldn't set the current device.
1387    	 * Doing so will cause getting the buffer size later to fail
1388    	 * with kAudioUnitErr_InvalidProperty error (-10879).
1389    	 */
1390    	PJ_LOG(4, (THIS_FILE, "Setting current device %d", dev_id));
1391    	ostatus = AudioUnitSetProperty(*io_unit,
1392			               kAudioOutputUnitProperty_CurrentDevice,
1393			               kAudioUnitScope_Global,
1394			               0,
1395			               &dev_id,
1396			               sizeof(dev_id));
1397    	if (ostatus != noErr) {
1398	    PJ_LOG(3, (THIS_FILE, "Failed setting current device %d, "
1399	    	       "error: %d", dev_id, ostatus));
1400	    return PJMEDIA_AUDIODEV_ERRNO_FROM_COREAUDIO(ostatus);
1401	}
1402    }
1403#endif
1404
1405    if (dir & PJMEDIA_DIR_CAPTURE) {
1406#if COREAUDIO_MAC
1407	AudioStreamBasicDescription deviceFormat;
1408	UInt32 size;
1409
1410	if (!strm->param.ec_enabled) {
1411	    /* Keep the sample rate from the device, otherwise we will confuse
1412	     * AUHAL.
1413	     */
1414	    size = sizeof(AudioStreamBasicDescription);
1415	    ostatus = AudioUnitGetProperty(*io_unit,
1416				       	   kAudioUnitProperty_StreamFormat,
1417				       	   kAudioUnitScope_Input,
1418				       	   1,
1419				       	   &deviceFormat,
1420				       	   &size);
1421	    if (ostatus != noErr) {
1422	    	PJ_LOG(3, (THIS_FILE, "Failed getting stream format of device"
1423	    			      " %d, error: %d", dev_id, ostatus));
1424	    	return PJMEDIA_AUDIODEV_ERRNO_FROM_COREAUDIO(ostatus);
1425	    }
1426	    strm->streamFormat.mSampleRate = deviceFormat.mSampleRate;
1427	}
1428#endif
1429
1430	/* When setting the stream format, we have to make sure the sample
1431	 * rate is supported. Setting an unsupported sample rate will cause
1432	 * AudioUnitRender() to fail later.
1433	 */
1434	ostatus = AudioUnitSetProperty(*io_unit,
1435				       kAudioUnitProperty_StreamFormat,
1436				       kAudioUnitScope_Output,
1437				       1,
1438				       &strm->streamFormat,
1439				       sizeof(strm->streamFormat));
1440	if (ostatus != noErr) {
1441	    PJ_LOG(3, (THIS_FILE, "Failed setting stream format of device %d"
1442	    			  ", error: %d", dev_id, ostatus));
1443	    return PJMEDIA_AUDIODEV_ERRNO_FROM_COREAUDIO(ostatus);
1444	}
1445
1446#if COREAUDIO_MAC
1447	strm->streamFormat.mSampleRate = strm->param.clock_rate;
1448	size = sizeof(AudioStreamBasicDescription);
1449	ostatus = AudioUnitGetProperty (*io_unit,
1450					kAudioUnitProperty_StreamFormat,
1451					kAudioUnitScope_Output,
1452					1,
1453					&deviceFormat,
1454					&size);
1455	if (ostatus == noErr) {
1456	    if (strm->streamFormat.mSampleRate != deviceFormat.mSampleRate) {
1457	    	PJ_LOG(4, (THIS_FILE, "Creating audio resample from %d to %d",
1458	    		   (int)deviceFormat.mSampleRate,
1459	    		   (int)strm->streamFormat.mSampleRate));
1460		pj_status_t rc = create_audio_resample(strm, &deviceFormat);
1461		if (PJ_SUCCESS != rc) {
1462	    	    PJ_LOG(3, (THIS_FILE, "Failed creating resample %d",
1463	    		       rc));
1464		    return rc;
1465		}
1466	    }
1467	} else {
1468	    PJ_LOG(3, (THIS_FILE, "Failed getting stream format of device %d"
1469	    			  " (2), error: %d", dev_id, ostatus));
1470	    return PJMEDIA_AUDIODEV_ERRNO_FROM_COREAUDIO(ostatus);
1471	}
1472#endif
1473    }
1474
1475    if (dir & PJMEDIA_DIR_PLAYBACK) {
1476	AURenderCallbackStruct output_cb;
1477	AudioStreamBasicDescription streamFormat = strm->streamFormat;
1478	BOOL isMacCatalystApp = false;
1479
1480#ifdef __IPHONE_13_0
1481	if (@available(iOS 13.0, *)) {
1482            /* According to Apple's doc, the property isMacCatalystApp is true
1483	     * when the process is:
1484             * - A Mac app built with Mac Catalyst, or an iOS app running on Apple silicon.
1485             * - Running on a Mac.
1486             */
1487            isMacCatalystApp = [NSProcessInfo processInfo].isMacCatalystApp;
1488	}
1489#endif
1490
1491	/* Set the stream format */
1492   	if (strm->param.ec_enabled
1493#if !COREAUDIO_MAC
1494	    && isMacCatalystApp
1495#endif
1496   	) {
1497   	    /* When using VPIO on Mac, we need to use float data. Using
1498   	     * signed integer will generate no errors, but strangely,
1499   	     * no sound will be played.
1500   	     */
1501    	    streamFormat.mFormatFlags      = kLinearPCMFormatFlagIsFloat |
1502  					     kLinearPCMFormatFlagIsPacked;
1503    	    streamFormat.mBitsPerChannel   = sizeof(float) * 8;
1504   	    streamFormat.mBytesPerFrame    = streamFormat.mChannelsPerFrame *
1505	                                     streamFormat.mBitsPerChannel / 8;
1506    	    streamFormat.mBytesPerPacket   = streamFormat.mBytesPerFrame *
1507					     streamFormat.mFramesPerPacket;
1508	}
1509
1510	ostatus = AudioUnitSetProperty(*io_unit,
1511	                               kAudioUnitProperty_StreamFormat,
1512	                               kAudioUnitScope_Input,
1513	                               0,
1514	                               &streamFormat,
1515	                               sizeof(streamFormat));
1516	if (ostatus != noErr) {
1517	    PJ_LOG(4, (THIS_FILE,
1518		   "Warning: cannot set playback stream format of dev %d",
1519		   dev_id));
1520	}
1521
1522	/* Set render callback */
1523	output_cb.inputProc = output_renderer;
1524	output_cb.inputProcRefCon = strm;
1525	ostatus = AudioUnitSetProperty(*io_unit,
1526				       kAudioUnitProperty_SetRenderCallback,
1527				       kAudioUnitScope_Input,
1528				       0,
1529				       &output_cb,
1530				       sizeof(output_cb));
1531	if (ostatus != noErr) {
1532	    PJ_LOG(3, (THIS_FILE, "Failed setting render callback %d",
1533	    	       ostatus));
1534	    return PJMEDIA_AUDIODEV_ERRNO_FROM_COREAUDIO(ostatus);
1535	}
1536
1537	/* Allocate playback buffer */
1538	strm->play_buf = (pj_int16_t*)pj_pool_alloc(strm->pool,
1539			 strm->param.samples_per_frame *
1540			 (strm->param.ec_enabled? 2: 1) *
1541			 strm->param.bits_per_sample >> 3);
1542	if (!strm->play_buf)
1543	    return PJ_ENOMEM;
1544	strm->play_buf_count = 0;
1545    }
1546
1547    if (dir & PJMEDIA_DIR_CAPTURE) {
1548	AURenderCallbackStruct input_cb;
1549#if COREAUDIO_MAC
1550	AudioBuffer *ab;
1551	UInt32 size, buf_size;
1552#endif
1553
1554	/* Set input callback */
1555	input_cb.inputProc = strm->resample ? resample_callback :
1556			     input_callback;
1557	input_cb.inputProcRefCon = strm;
1558	ostatus = AudioUnitSetProperty(
1559		      *io_unit,
1560		      kAudioOutputUnitProperty_SetInputCallback,
1561		      kAudioUnitScope_Global,
1562		      1,
1563		      &input_cb,
1564		      sizeof(input_cb));
1565	if (ostatus != noErr) {
1566	    PJ_LOG(3, (THIS_FILE, "Failed setting input callback %d",
1567	    	       ostatus));
1568	    return PJMEDIA_AUDIODEV_ERRNO_FROM_COREAUDIO(ostatus);
1569	}
1570
1571#if COREAUDIO_MAC
1572	/* Set device's buffer frame size */
1573	size = sizeof(UInt32);
1574	buf_size = (20 * strm->streamFormat.mSampleRate) / 1000;
1575    	ostatus = AudioUnitSetProperty (*io_unit,
1576    					kAudioDevicePropertyBufferFrameSize,
1577    					kAudioUnitScope_Global,
1578    					0,
1579    					&buf_size,
1580    					size);
1581	if (ostatus != noErr) {
1582	    PJ_LOG(3, (THIS_FILE, "Failed setting buffer size %d",
1583	    	       ostatus));
1584	    return PJMEDIA_AUDIODEV_ERRNO_FROM_COREAUDIO(ostatus);
1585	}
1586
1587	/* Get device's buffer frame size */
1588	ostatus = AudioUnitGetProperty(*io_unit,
1589		                       kAudioDevicePropertyBufferFrameSize,
1590		                       kAudioUnitScope_Global,
1591		                       0,
1592		                       &buf_size,
1593		                       &size);
1594	if (ostatus != noErr) {
1595	    PJ_LOG(3, (THIS_FILE, "Failed getting buffer size %d",
1596	    	       ostatus));
1597	    return PJMEDIA_AUDIODEV_ERRNO_FROM_COREAUDIO(ostatus);
1598	}
1599
1600	/* Allocate audio buffer */
1601	strm->audio_buf = (AudioBufferList*)pj_pool_alloc(strm->pool,
1602		          sizeof(AudioBufferList) + sizeof(AudioBuffer));
1603	if (!strm->audio_buf)
1604	    return PJ_ENOMEM;
1605
1606	strm->audio_buf->mNumberBuffers = 1;
1607	ab = &strm->audio_buf->mBuffers[0];
1608	ab->mNumberChannels = strm->streamFormat.mChannelsPerFrame;
1609	ab->mDataByteSize = buf_size * ab->mNumberChannels *
1610			    strm->streamFormat.mBitsPerChannel >> 3;
1611	ab->mData = pj_pool_alloc(strm->pool,
1612				  ab->mDataByteSize);
1613	if (!ab->mData)
1614	    return PJ_ENOMEM;
1615
1616#else
1617	/* We will let AudioUnitRender() to allocate the buffer
1618	 * for us later
1619	 */
1620	strm->audio_buf = (AudioBufferList*)pj_pool_alloc(strm->pool,
1621		          sizeof(AudioBufferList) + sizeof(AudioBuffer));
1622	if (!strm->audio_buf)
1623	    return PJ_ENOMEM;
1624
1625	strm->audio_buf->mNumberBuffers = 1;
1626	strm->audio_buf->mBuffers[0].mNumberChannels =
1627		strm->streamFormat.mChannelsPerFrame;
1628
1629#endif
1630
1631	/* Allocate recording buffer */
1632	strm->rec_buf = (pj_int16_t*)pj_pool_alloc(strm->pool,
1633			strm->param.samples_per_frame *
1634			strm->param.bits_per_sample >> 3);
1635	if (!strm->rec_buf)
1636	    return PJ_ENOMEM;
1637	strm->rec_buf_count = 0;
1638    }
1639
1640    /* Initialize the audio unit */
1641    ostatus = AudioUnitInitialize(*io_unit);
1642    if (ostatus != noErr) {
1643	PJ_LOG(3, (THIS_FILE, "Failed initializing audio unit %d", ostatus));
1644 	return PJMEDIA_AUDIODEV_ERRNO_FROM_COREAUDIO(ostatus);
1645    }
1646
1647    return PJ_SUCCESS;
1648}
1649
1650/* API: create stream */
1651static pj_status_t ca_factory_create_stream(pjmedia_aud_dev_factory *f,
1652					    const pjmedia_aud_param *param,
1653					    pjmedia_aud_rec_cb rec_cb,
1654					    pjmedia_aud_play_cb play_cb,
1655					    void *user_data,
1656					    pjmedia_aud_stream **p_aud_strm)
1657{
1658    struct coreaudio_factory *cf = (struct coreaudio_factory*)f;
1659    pj_pool_t *pool;
1660    struct coreaudio_stream *strm;
1661    pj_status_t status;
1662
1663    /* Create and Initialize stream descriptor */
1664    pool = pj_pool_create(cf->pf, "coreaudio-dev", 1000, 1000, NULL);
1665    PJ_ASSERT_RETURN(pool != NULL, PJ_ENOMEM);
1666
1667    strm = PJ_POOL_ZALLOC_T(pool, struct coreaudio_stream);
1668    pj_list_init(&strm->list_entry);
1669    strm->list_entry.stream = strm;
1670    strm->cf = cf;
1671    pj_memcpy(&strm->param, param, sizeof(*param));
1672    strm->pool = pool;
1673    strm->rec_cb = rec_cb;
1674    strm->play_cb = play_cb;
1675    strm->user_data = user_data;
1676
1677    /* Set the stream format */
1678    strm->streamFormat.mSampleRate       = param->clock_rate;
1679    strm->streamFormat.mFormatID         = kAudioFormatLinearPCM;
1680    strm->streamFormat.mFormatFlags      = kLinearPCMFormatFlagIsSignedInteger
1681  					   | kLinearPCMFormatFlagIsPacked;
1682    strm->streamFormat.mBitsPerChannel   = strm->param.bits_per_sample;
1683    strm->streamFormat.mChannelsPerFrame = param->channel_count;
1684    strm->streamFormat.mBytesPerFrame    = strm->streamFormat.mChannelsPerFrame
1685	                                   * strm->streamFormat.mBitsPerChannel
1686	                                   / 8;
1687    strm->streamFormat.mFramesPerPacket  = 1;
1688    strm->streamFormat.mBytesPerPacket   = strm->streamFormat.mBytesPerFrame *
1689					   strm->streamFormat.mFramesPerPacket;
1690
1691    /* Apply input/output routes settings before we create the audio units */
1692    if (param->flags & PJMEDIA_AUD_DEV_CAP_INPUT_ROUTE) {
1693	ca_stream_set_cap(&strm->base,
1694		          PJMEDIA_AUD_DEV_CAP_INPUT_ROUTE,
1695		          &param->input_route);
1696    }
1697    if (param->flags & PJMEDIA_AUD_DEV_CAP_OUTPUT_ROUTE) {
1698	ca_stream_set_cap(&strm->base,
1699		          PJMEDIA_AUD_DEV_CAP_OUTPUT_ROUTE,
1700		          &param->output_route);
1701    }
1702    if (param->flags & PJMEDIA_AUD_DEV_CAP_EC) {
1703#if COREAUDIO_MAC
1704	/* Temporarily disable VPIO on Mac for stereo due to recording sound
1705	 * artefacts.
1706	 */
1707    	if (param->channel_count > 1) {
1708    	    strm->param.ec_enabled = PJ_FALSE;
1709    	}
1710#endif
1711	status = ca_stream_set_cap(&strm->base,
1712		          	   PJMEDIA_AUD_DEV_CAP_EC,
1713		          	   &strm->param.ec_enabled);
1714	if (status != PJ_SUCCESS)
1715	    strm->param.ec_enabled = PJ_FALSE;
1716    } else {
1717	pj_bool_t ec = PJ_FALSE;
1718	ca_stream_set_cap(&strm->base,
1719		          PJMEDIA_AUD_DEV_CAP_EC, &ec);
1720    }
1721
1722#if !TARGET_OS_IPHONE
1723    if (strm->param.ec_enabled &&
1724	param->rec_id != PJMEDIA_AUD_DEFAULT_CAPTURE_DEV &&
1725	param->play_id != PJMEDIA_AUD_DEFAULT_PLAYBACK_DEV)
1726    {
1727	PJ_LOG(4, (THIS_FILE, "Warning: audio device id settings are "
1728			      "ignored when using VPIO"));
1729    }
1730#endif
1731
1732    strm->io_units[0] = strm->io_units[1] = NULL;
1733    if ((param->dir == PJMEDIA_DIR_CAPTURE_PLAYBACK &&
1734	 param->rec_id == param->play_id) ||
1735	(param->flags & PJMEDIA_AUD_DEV_CAP_EC && strm->param.ec_enabled))
1736    {
1737	/* If both input and output are on the same device or if EC is enabled,
1738	 * only create one audio unit to interface with the device(s).
1739	 */
1740	status = create_audio_unit(cf->io_comp,
1741		                   cf->dev_info[param->rec_id].dev_id,
1742		                   param->dir, strm, &strm->io_units[0]);
1743	if (status != PJ_SUCCESS)
1744	    goto on_error;
1745    } else {
1746	unsigned nunits = 0;
1747
1748	if (param->dir & PJMEDIA_DIR_CAPTURE) {
1749	    status = create_audio_unit(cf->io_comp,
1750				       cf->dev_info[param->rec_id].dev_id,
1751				       PJMEDIA_DIR_CAPTURE,
1752				       strm, &strm->io_units[nunits++]);
1753	    if (status != PJ_SUCCESS)
1754		goto on_error;
1755	}
1756	if (param->dir & PJMEDIA_DIR_PLAYBACK) {
1757
1758	    status = create_audio_unit(cf->io_comp,
1759				       cf->dev_info[param->play_id].dev_id,
1760				       PJMEDIA_DIR_PLAYBACK,
1761				       strm, &strm->io_units[nunits++]);
1762	    if (status != PJ_SUCCESS)
1763		goto on_error;
1764	}
1765    }
1766
1767    /* Apply the remaining settings */
1768    if (param->flags & PJMEDIA_AUD_DEV_CAP_INPUT_LATENCY) {
1769	ca_stream_get_cap(&strm->base,
1770		          PJMEDIA_AUD_DEV_CAP_INPUT_LATENCY,
1771		          &strm->param.input_latency_ms);
1772    }
1773    if (param->flags & PJMEDIA_AUD_DEV_CAP_OUTPUT_LATENCY) {
1774	ca_stream_get_cap(&strm->base,
1775		          PJMEDIA_AUD_DEV_CAP_OUTPUT_LATENCY,
1776		          &strm->param.output_latency_ms);
1777    }
1778    if (param->flags & PJMEDIA_AUD_DEV_CAP_OUTPUT_VOLUME_SETTING) {
1779	ca_stream_set_cap(&strm->base,
1780		          PJMEDIA_AUD_DEV_CAP_OUTPUT_VOLUME_SETTING,
1781		          &param->output_vol);
1782    }
1783
1784    pj_mutex_lock(strm->cf->mutex);
1785    pj_assert(pj_list_empty(&strm->list_entry));
1786    pj_list_insert_after(&strm->cf->streams, &strm->list_entry);
1787    pj_mutex_unlock(strm->cf->mutex);
1788
1789    /* Done */
1790    strm->base.op = &stream_op;
1791    *p_aud_strm = &strm->base;
1792
1793    return PJ_SUCCESS;
1794
1795 on_error:
1796    ca_stream_destroy((pjmedia_aud_stream *)strm);
1797    return status;
1798}
1799
1800/* API: Get stream info. */
1801static pj_status_t ca_stream_get_param(pjmedia_aud_stream *s,
1802				       pjmedia_aud_param *pi)
1803{
1804    struct coreaudio_stream *strm = (struct coreaudio_stream*)s;
1805
1806    PJ_ASSERT_RETURN(strm && pi, PJ_EINVAL);
1807
1808    pj_memcpy(pi, &strm->param, sizeof(*pi));
1809
1810    /* Update the device capabilities' values */
1811    if (ca_stream_get_cap(s, PJMEDIA_AUD_DEV_CAP_INPUT_LATENCY,
1812		          &pi->input_latency_ms) == PJ_SUCCESS)
1813    {
1814    	pi->flags |= PJMEDIA_AUD_DEV_CAP_INPUT_LATENCY;
1815    }
1816    if (ca_stream_get_cap(s, PJMEDIA_AUD_DEV_CAP_OUTPUT_LATENCY,
1817			  &pi->output_latency_ms) == PJ_SUCCESS)
1818    {
1819	pi->flags |= PJMEDIA_AUD_DEV_CAP_OUTPUT_LATENCY;
1820    }
1821    if (ca_stream_get_cap(s, PJMEDIA_AUD_DEV_CAP_OUTPUT_VOLUME_SETTING,
1822			  &pi->output_vol) == PJ_SUCCESS)
1823    {
1824        pi->flags |= PJMEDIA_AUD_DEV_CAP_OUTPUT_VOLUME_SETTING;
1825    }
1826    if (ca_stream_get_cap(s, PJMEDIA_AUD_DEV_CAP_INPUT_ROUTE,
1827			  &pi->input_route) == PJ_SUCCESS)
1828    {
1829        pi->flags |= PJMEDIA_AUD_DEV_CAP_INPUT_ROUTE;
1830    }
1831    if (ca_stream_get_cap(s, PJMEDIA_AUD_DEV_CAP_OUTPUT_ROUTE,
1832			  &pi->output_route) == PJ_SUCCESS)
1833    {
1834        pi->flags |= PJMEDIA_AUD_DEV_CAP_OUTPUT_ROUTE;
1835    }
1836    if (ca_stream_get_cap(s, PJMEDIA_AUD_DEV_CAP_EC,
1837			  &pi->ec_enabled) == PJ_SUCCESS)
1838    {
1839        pi->flags |= PJMEDIA_AUD_DEV_CAP_EC;
1840    }
1841
1842    return PJ_SUCCESS;
1843}
1844
1845/* API: get capability */
1846static pj_status_t ca_stream_get_cap(pjmedia_aud_stream *s,
1847				     pjmedia_aud_dev_cap cap,
1848				     void *pval)
1849{
1850    struct coreaudio_stream *strm = (struct coreaudio_stream*)s;
1851
1852    PJ_UNUSED_ARG(strm);
1853
1854    PJ_ASSERT_RETURN(s && pval, PJ_EINVAL);
1855
1856    if (cap==PJMEDIA_AUD_DEV_CAP_INPUT_LATENCY &&
1857	(strm->param.dir & PJMEDIA_DIR_CAPTURE))
1858    {
1859#if COREAUDIO_MAC
1860	UInt32 latency, size = sizeof(UInt32);
1861
1862	/* Recording latency */
1863	if (AudioUnitGetProperty (strm->io_units[0],
1864				  kAudioDevicePropertyLatency,
1865				  kAudioUnitScope_Input,
1866				  1,
1867				  &latency,
1868				  &size) == noErr)
1869	{
1870	    UInt32 latency2;
1871	    if (AudioUnitGetProperty (strm->io_units[0],
1872				      kAudioDevicePropertyBufferFrameSize,
1873				      kAudioUnitScope_Input,
1874				      1,
1875				      &latency2,
1876				      &size) == noErr)
1877	    {
1878		strm->param.input_latency_ms = (latency + latency2) * 1000 /
1879					       strm->param.clock_rate;
1880		strm->param.input_latency_ms++;
1881	    }
1882	}
1883#else
1884        if ([strm->sess respondsToSelector:@selector(inputLatency)]) {
1885	    strm->param.input_latency_ms =
1886                (unsigned)(([strm->sess inputLatency] +
1887                            [strm->sess IOBufferDuration]) * 1000);
1888	    strm->param.input_latency_ms++;
1889	} else
1890            return PJMEDIA_EAUD_INVCAP;
1891#endif
1892
1893	*(unsigned*)pval = strm->param.input_latency_ms;
1894	return PJ_SUCCESS;
1895    } else if (cap==PJMEDIA_AUD_DEV_CAP_OUTPUT_LATENCY  &&
1896	       (strm->param.dir & PJMEDIA_DIR_PLAYBACK))
1897    {
1898#if COREAUDIO_MAC
1899	UInt32 latency, size = sizeof(UInt32);
1900	AudioUnit *io_unit = strm->io_units[1] ? &strm->io_units[1] :
1901			     &strm->io_units[0];
1902
1903	/* Playback latency */
1904	if (AudioUnitGetProperty (*io_unit,
1905				  kAudioDevicePropertyLatency,
1906				  kAudioUnitScope_Output,
1907				  0,
1908				  &latency,
1909				  &size) == noErr)
1910	{
1911	    UInt32 latency2;
1912	    if (AudioUnitGetProperty (*io_unit,
1913				      kAudioDevicePropertyBufferFrameSize,
1914				      kAudioUnitScope_Output,
1915				      0,
1916				      &latency2,
1917				      &size) == noErr)
1918	    {
1919		strm->param.output_latency_ms = (latency + latency2) * 1000 /
1920						strm->param.clock_rate;
1921		strm->param.output_latency_ms++;
1922	    }
1923	}
1924#else
1925        if ([strm->sess respondsToSelector:@selector(outputLatency)]) {
1926	    strm->param.output_latency_ms =
1927            (unsigned)(([strm->sess outputLatency] +
1928                        [strm->sess IOBufferDuration]) * 1000);
1929	    strm->param.output_latency_ms++;
1930	} else
1931            return PJMEDIA_EAUD_INVCAP;
1932#endif
1933	*(unsigned*)pval = (++strm->param.output_latency_ms * 2);
1934	return PJ_SUCCESS;
1935    } else if (cap==PJMEDIA_AUD_DEV_CAP_OUTPUT_VOLUME_SETTING &&
1936	       (strm->param.dir & PJMEDIA_DIR_PLAYBACK))
1937    {
1938#if COREAUDIO_MAC
1939	OSStatus ostatus;
1940	Float32 volume;
1941	UInt32 size = sizeof(Float32);
1942
1943	/* Output volume setting */
1944	ostatus = AudioUnitGetProperty (strm->io_units[1] ? strm->io_units[1] :
1945					strm->io_units[0],
1946					kAudioDevicePropertyVolumeScalar,
1947	                                kAudioUnitScope_Output,
1948	                                0,
1949	                                &volume,
1950	                                &size);
1951	if (ostatus != noErr)
1952	    return PJMEDIA_AUDIODEV_ERRNO_FROM_COREAUDIO(ostatus);
1953
1954	*(unsigned*)pval = (unsigned)(volume * 100);
1955	return PJ_SUCCESS;
1956#else
1957        if ([strm->sess respondsToSelector:@selector(outputVolume)]) {
1958            *(unsigned*)pval = (unsigned)([strm->sess outputVolume] * 100);
1959            return PJ_SUCCESS;
1960	} else
1961            return PJMEDIA_EAUD_INVCAP;
1962#endif
1963
1964#if !COREAUDIO_MAC
1965#if USE_AUDIO_SESSION_API != 0
1966    } else if (cap==PJMEDIA_AUD_DEV_CAP_INPUT_ROUTE &&
1967	       (strm->param.dir & PJMEDIA_DIR_CAPTURE))
1968    {
1969	UInt32 btooth, size = sizeof(UInt32);
1970	OSStatus ostatus;
1971
1972	ostatus = AudioSessionGetProperty (
1973	    kAudioSessionProperty_OverrideCategoryEnableBluetoothInput,
1974	    &size, &btooth);
1975	if (ostatus != kAudioSessionNoError) {
1976	    return PJMEDIA_AUDIODEV_ERRNO_FROM_COREAUDIO(ostatus);
1977	}
1978
1979	*(pjmedia_aud_dev_route*)pval = btooth?
1980		                        PJMEDIA_AUD_DEV_ROUTE_BLUETOOTH:
1981					PJMEDIA_AUD_DEV_ROUTE_DEFAULT;
1982	return PJ_SUCCESS;
1983    } else if (cap==PJMEDIA_AUD_DEV_CAP_OUTPUT_ROUTE &&
1984	       (strm->param.dir & PJMEDIA_DIR_PLAYBACK))
1985    {
1986	CFStringRef route;
1987	UInt32 size = sizeof(CFStringRef);
1988	OSStatus ostatus;
1989
1990	ostatus = AudioSessionGetProperty (kAudioSessionProperty_AudioRoute,
1991					   &size, &route);
1992	if (ostatus != kAudioSessionNoError) {
1993	    return PJMEDIA_AUDIODEV_ERRNO_FROM_COREAUDIO(ostatus);
1994	}
1995
1996	if (!route) {
1997	    *(pjmedia_aud_dev_route*)pval = PJMEDIA_AUD_DEV_ROUTE_DEFAULT;
1998	} else if (CFStringHasPrefix(route, CFSTR("Headset"))) {
1999	    *(pjmedia_aud_dev_route*)pval = PJMEDIA_AUD_DEV_ROUTE_EARPIECE;
2000	} else {
2001	    *(pjmedia_aud_dev_route*)pval = PJMEDIA_AUD_DEV_ROUTE_DEFAULT;
2002	}
2003
2004	CFRelease(route);
2005
2006	return PJ_SUCCESS;
2007#endif
2008    } else if (cap==PJMEDIA_AUD_DEV_CAP_EC) {
2009	AudioComponentDescription desc;
2010	OSStatus ostatus;
2011
2012	ostatus = AudioComponentGetDescription(strm->cf->io_comp, &desc);
2013	if (ostatus != noErr) {
2014	    return PJMEDIA_AUDIODEV_ERRNO_FROM_COREAUDIO(ostatus);
2015	}
2016
2017	*(pj_bool_t*)pval = (desc.componentSubType ==
2018		            kAudioUnitSubType_VoiceProcessingIO);
2019	return PJ_SUCCESS;
2020#endif
2021    } else {
2022	return PJMEDIA_EAUD_INVCAP;
2023    }
2024}
2025
2026/* API: set capability */
2027static pj_status_t ca_stream_set_cap(pjmedia_aud_stream *s,
2028				     pjmedia_aud_dev_cap cap,
2029				     const void *pval)
2030{
2031    struct coreaudio_stream *strm = (struct coreaudio_stream*)s;
2032
2033    PJ_ASSERT_RETURN(s && pval, PJ_EINVAL);
2034
2035    if (cap==PJMEDIA_AUD_DEV_CAP_EC) {
2036	AudioComponentDescription desc;
2037	AudioComponent io_comp;
2038
2039	desc.componentType = kAudioUnitType_Output;
2040	desc.componentSubType = (*(pj_bool_t*)pval)?
2041        			kAudioUnitSubType_VoiceProcessingIO :
2042#if COREAUDIO_MAC
2043        			kAudioUnitSubType_HALOutput;
2044#else
2045				kAudioUnitSubType_RemoteIO;
2046#endif
2047	desc.componentManufacturer = kAudioUnitManufacturer_Apple;
2048	desc.componentFlags = 0;
2049	desc.componentFlagsMask = 0;
2050
2051	io_comp = AudioComponentFindNext(NULL, &desc);
2052	if (io_comp == NULL)
2053	    return PJMEDIA_AUDIODEV_ERRNO_FROM_COREAUDIO(-1);
2054	strm->cf->io_comp = io_comp;
2055	strm->param.ec_enabled = *(pj_bool_t*)pval;
2056
2057        PJ_LOG(4, (THIS_FILE, "Using %s audio unit",
2058                   (desc.componentSubType ==
2059                    kAudioUnitSubType_VoiceProcessingIO?
2060                    "VoiceProcessingIO": "default")));
2061
2062	return PJ_SUCCESS;
2063    }
2064#if COREAUDIO_MAC
2065    else if (cap==PJMEDIA_AUD_DEV_CAP_OUTPUT_VOLUME_SETTING &&
2066	(strm->param.dir & PJMEDIA_DIR_PLAYBACK))
2067    {
2068	OSStatus ostatus;
2069	Float32 volume = *(unsigned*)pval;
2070
2071	/* Output volume setting */
2072	volume /= 100.0;
2073	ostatus = AudioUnitSetProperty (strm->io_units[1] ? strm->io_units[1] :
2074					strm->io_units[0],
2075					kAudioDevicePropertyVolumeScalar,
2076	                                kAudioUnitScope_Output,
2077	                                0,
2078	                                &volume,
2079	                                sizeof(Float32));
2080	if (ostatus != noErr) {
2081	    return PJMEDIA_AUDIODEV_ERRNO_FROM_COREAUDIO(ostatus);
2082	}
2083	strm->param.output_vol = *(unsigned*)pval;
2084	return PJ_SUCCESS;
2085    }
2086
2087#else
2088    else if ((cap==PJMEDIA_AUD_DEV_CAP_INPUT_LATENCY &&
2089	 (strm->param.dir & PJMEDIA_DIR_CAPTURE)) ||
2090	(cap==PJMEDIA_AUD_DEV_CAP_OUTPUT_LATENCY &&
2091	 (strm->param.dir & PJMEDIA_DIR_PLAYBACK)))
2092    {
2093	NSTimeInterval duration = *(unsigned *)pval;
2094	unsigned latency;
2095
2096	/* For low-latency audio streaming, you can set this value to
2097	 * as low as 5 ms (the default is 23ms). However, lowering the
2098	 * latency may cause a decrease in audio quality.
2099	 */
2100	duration /= 1000;
2101	if ([strm->sess setPreferredIOBufferDuration:duration error:nil]
2102            != YES)
2103        {
2104	    PJ_LOG(4, (THIS_FILE,
2105		       "Error: cannot set the preferred buffer duration"));
2106	    return PJMEDIA_EAUD_INVOP;
2107	}
2108
2109	ca_stream_get_cap(s, PJMEDIA_AUD_DEV_CAP_INPUT_LATENCY, &latency);
2110	ca_stream_get_cap(s, PJMEDIA_AUD_DEV_CAP_OUTPUT_LATENCY, &latency);
2111
2112	return PJ_SUCCESS;
2113    }
2114
2115#if USE_AUDIO_SESSION_API != 0
2116
2117    else if (cap==PJMEDIA_AUD_DEV_CAP_INPUT_ROUTE &&
2118	       (strm->param.dir & PJMEDIA_DIR_CAPTURE))
2119    {
2120	UInt32 btooth = *(pjmedia_aud_dev_route*)pval ==
2121		        PJMEDIA_AUD_DEV_ROUTE_BLUETOOTH ? 1 : 0;
2122	OSStatus ostatus;
2123
2124	ostatus = AudioSessionSetProperty (
2125	    kAudioSessionProperty_OverrideCategoryEnableBluetoothInput,
2126	    sizeof(btooth), &btooth);
2127	if (ostatus != kAudioSessionNoError) {
2128	    return PJMEDIA_AUDIODEV_ERRNO_FROM_COREAUDIO(ostatus);
2129	}
2130	strm->param.input_route = *(pjmedia_aud_dev_route*)pval;
2131	return PJ_SUCCESS;
2132    } else if (cap==PJMEDIA_AUD_DEV_CAP_OUTPUT_ROUTE &&
2133	       (strm->param.dir & PJMEDIA_DIR_PLAYBACK))
2134    {
2135	OSStatus ostatus;
2136	UInt32 route = *(pjmedia_aud_dev_route*)pval ==
2137		       PJMEDIA_AUD_DEV_ROUTE_LOUDSPEAKER ?
2138		       kAudioSessionOverrideAudioRoute_Speaker :
2139		       kAudioSessionOverrideAudioRoute_None;
2140
2141	ostatus = AudioSessionSetProperty (
2142	    kAudioSessionProperty_OverrideAudioRoute,
2143	    sizeof(route), &route);
2144	if (ostatus != kAudioSessionNoError) {
2145	    return PJMEDIA_AUDIODEV_ERRNO_FROM_COREAUDIO(ostatus);
2146	}
2147	strm->param.output_route = *(pjmedia_aud_dev_route*)pval;
2148	return PJ_SUCCESS;
2149    }
2150#endif
2151#endif
2152
2153    return PJMEDIA_EAUD_INVCAP;
2154}
2155
2156/* API: Start stream. */
2157static pj_status_t ca_stream_start(pjmedia_aud_stream *strm)
2158{
2159    struct coreaudio_stream *stream = (struct coreaudio_stream*)strm;
2160    OSStatus ostatus;
2161    UInt32 i;
2162
2163    if (stream->running)
2164	return PJ_SUCCESS;
2165
2166    stream->quit_flag = 0;
2167    stream->interrupted = PJ_FALSE;
2168    stream->rec_buf_count = 0;
2169    stream->play_buf_count = 0;
2170    stream->resample_buf_count = 0;
2171
2172    if (stream->resample) {
2173	ostatus = AudioConverterReset(stream->resample);
2174	if (ostatus != noErr)
2175	    return PJMEDIA_AUDIODEV_ERRNO_FROM_COREAUDIO(ostatus);
2176    }
2177
2178#if !COREAUDIO_MAC
2179    if ([stream->sess setActive:true error:nil] != YES) {
2180	PJ_LOG(4, (THIS_FILE, "Warning: cannot activate audio session"));
2181    }
2182#endif
2183
2184    for (i = 0; i < 2; i++) {
2185	if (stream->io_units[i] == NULL) break;
2186	ostatus = AudioOutputUnitStart(stream->io_units[i]);
2187	if (ostatus != noErr) {
2188	    if (i == 1)
2189		AudioOutputUnitStop(stream->io_units[0]);
2190	    return PJMEDIA_AUDIODEV_ERRNO_FROM_COREAUDIO(ostatus);
2191	}
2192    }
2193
2194    stream->running = PJ_TRUE;
2195
2196    PJ_LOG(4, (THIS_FILE, "core audio stream started"));
2197
2198    return PJ_SUCCESS;
2199}
2200
2201/* API: Stop stream. */
2202static pj_status_t ca_stream_stop(pjmedia_aud_stream *strm)
2203{
2204    struct coreaudio_stream *stream = (struct coreaudio_stream*)strm;
2205    OSStatus ostatus;
2206    unsigned i;
2207    int should_deactivate;
2208    struct stream_list *it, *itBegin;
2209
2210    if (!stream->running)
2211	return PJ_SUCCESS;
2212
2213    for (i = 0; i < 2; i++) {
2214	if (stream->io_units[i] == NULL) break;
2215	ostatus = AudioOutputUnitStop(stream->io_units[i]);
2216	if (ostatus != noErr) {
2217	    if (i == 0 && stream->io_units[1])
2218		AudioOutputUnitStop(stream->io_units[1]);
2219	    return PJMEDIA_AUDIODEV_ERRNO_FROM_COREAUDIO(ostatus);
2220	}
2221    }
2222
2223    /* Check whether we need to deactivate the audio session. */
2224    pj_mutex_lock(stream->cf->mutex);
2225    pj_assert(!pj_list_empty(&stream->cf->streams));
2226    pj_assert(!pj_list_empty(&stream->list_entry));
2227    stream->running = PJ_FALSE;
2228    should_deactivate = PJ_TRUE;
2229    itBegin = &stream->cf->streams;
2230    for (it = itBegin->next; it != itBegin; it = it->next) {
2231	if (it->stream->running) {
2232	    should_deactivate = PJ_FALSE;
2233	    break;
2234	}
2235    }
2236    pj_mutex_unlock(stream->cf->mutex);
2237
2238#if !COREAUDIO_MAC && SETUP_AV_AUDIO_SESSION
2239    if (should_deactivate) {
2240        if ([stream->sess
2241             respondsToSelector:@selector(setActive:withOptions:error:)])
2242        {
2243  	    [stream->sess setActive:NO
2244  	    withOptions:AVAudioSessionSetActiveOptionNotifyOthersOnDeactivation
2245  	    error:nil];
2246	} else {
2247	    if ([stream->sess setActive:NO error:nil] != YES) {
2248            	PJ_LOG(4, (THIS_FILE, "Warning: cannot deactivate "
2249            			      "audio session"));
2250            }
2251        }
2252    }
2253#endif
2254
2255    stream->quit_flag = 1;
2256    stream->play_thread_initialized = 0;
2257    stream->rec_thread_initialized = 0;
2258    pj_bzero(stream->rec_thread_desc, sizeof(pj_thread_desc));
2259    pj_bzero(stream->play_thread_desc, sizeof(pj_thread_desc));
2260
2261    PJ_LOG(4, (THIS_FILE, "core audio stream stopped"));
2262
2263    return PJ_SUCCESS;
2264}
2265
2266
2267/* API: Destroy stream. */
2268static pj_status_t ca_stream_destroy(pjmedia_aud_stream *strm)
2269{
2270    struct coreaudio_stream *stream = (struct coreaudio_stream*)strm;
2271    unsigned i;
2272
2273    PJ_ASSERT_RETURN(stream != NULL, PJ_EINVAL);
2274
2275    ca_stream_stop(strm);
2276
2277    for (i = 0; i < 2; i++) {
2278	if (stream->io_units[i]) {
2279	    AudioUnitUninitialize(stream->io_units[i]);
2280	    AudioComponentInstanceDispose(stream->io_units[i]);
2281	    stream->io_units[i] = NULL;
2282	}
2283    }
2284
2285    if (stream->resample)
2286	AudioConverterDispose(stream->resample);
2287
2288    pj_mutex_lock(stream->cf->mutex);
2289    if (!pj_list_empty(&stream->list_entry))
2290	pj_list_erase(&stream->list_entry);
2291    pj_mutex_unlock(stream->cf->mutex);
2292
2293    pj_pool_release(stream->pool);
2294
2295    return PJ_SUCCESS;
2296}
2297
2298#endif	/* PJMEDIA_AUDIO_DEV_HAS_COREAUDIO */
2299