1
/* $Id: coreaudio_dev.c 4082 2012-04-24 13:09:14Z bennylp $ */
3
* Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
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.
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.
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
19
#include <pjmedia-audiodev/audiodev_imp.h>
20
#include <pj/assert.h>
24
#if PJMEDIA_AUDIO_DEV_HAS_COREAUDIO
26
#include "TargetConditionals.h"
28
#define COREAUDIO_MAC 0
30
#define COREAUDIO_MAC 1
33
#include <AudioUnit/AudioUnit.h>
34
#include <AudioToolbox/AudioConverter.h>
36
#include <AudioToolbox/AudioServices.h>
38
#define AudioDeviceID unsigned
41
* As in iOS SDK 4 or later, audio route change property listener is
42
* no longer necessary. Just make surethat your application can receive
43
* remote control events by adding the code:
44
* [[UIApplication sharedApplication]
45
* beginReceivingRemoteControlEvents];
46
* Otherwise audio route change (such as headset plug/unplug) will not be
47
* processed while your application is in the background mode.
49
#define USE_AUDIO_ROUTE_CHANGE_PROP_LISTENER 0
53
/* For Mac OS 10.5.x and earlier */
54
#if AUDIO_UNIT_VERSION < 1060
55
#define AudioComponent Component
56
#define AudioComponentDescription ComponentDescription
57
#define AudioComponentInstance ComponentInstance
58
#define AudioComponentFindNext FindNextComponent
59
#define AudioComponentInstanceNew OpenAComponent
60
#define AudioComponentInstanceDispose CloseComponent
64
#define THIS_FILE "coreaudio_dev.c"
66
/* coreaudio device info */
67
struct coreaudio_dev_info
69
pjmedia_aud_dev_info info;
73
/* linked list of streams */
76
PJ_DECL_LIST_MEMBER(struct stream_list);
77
struct coreaudio_stream *stream;
80
/* coreaudio factory */
81
struct coreaudio_factory
83
pjmedia_aud_dev_factory base;
90
struct coreaudio_dev_info *dev_info;
92
AudioComponent io_comp;
93
struct stream_list streams;
97
struct coreaudio_stream
99
pjmedia_aud_stream base; /**< Base stream */
100
pjmedia_aud_param param; /**< Settings */
101
pj_pool_t *pool; /**< Memory pool. */
102
struct coreaudio_factory *cf;
103
struct stream_list list_entry;
105
pjmedia_aud_rec_cb rec_cb; /**< Capture callback. */
106
pjmedia_aud_play_cb play_cb; /**< Playback callback. */
107
void *user_data; /**< Application data. */
109
pj_timestamp play_timestamp;
110
pj_timestamp rec_timestamp;
113
unsigned rec_buf_count;
114
pj_int16_t *play_buf;
115
unsigned play_buf_count;
117
pj_bool_t interrupted;
121
pj_bool_t rec_thread_initialized;
122
pj_thread_desc rec_thread_desc;
123
pj_thread_t *rec_thread;
125
pj_bool_t play_thread_initialized;
126
pj_thread_desc play_thread_desc;
127
pj_thread_t *play_thread;
129
AudioUnit io_units[2];
130
AudioStreamBasicDescription streamFormat;
131
AudioBufferList *audio_buf;
133
AudioConverterRef resample;
134
pj_int16_t *resample_buf;
135
void *resample_buf_ptr;
136
unsigned resample_buf_count;
137
unsigned resample_buf_size;
140
/* Static variable */
141
static struct coreaudio_factory *cf_instance = NULL;
144
static pj_status_t ca_factory_init(pjmedia_aud_dev_factory *f);
145
static pj_status_t ca_factory_destroy(pjmedia_aud_dev_factory *f);
146
static pj_status_t ca_factory_refresh(pjmedia_aud_dev_factory *f);
147
static unsigned ca_factory_get_dev_count(pjmedia_aud_dev_factory *f);
148
static pj_status_t ca_factory_get_dev_info(pjmedia_aud_dev_factory *f,
150
pjmedia_aud_dev_info *info);
151
static pj_status_t ca_factory_default_param(pjmedia_aud_dev_factory *f,
153
pjmedia_aud_param *param);
154
static pj_status_t ca_factory_create_stream(pjmedia_aud_dev_factory *f,
155
const pjmedia_aud_param *param,
156
pjmedia_aud_rec_cb rec_cb,
157
pjmedia_aud_play_cb play_cb,
159
pjmedia_aud_stream **p_aud_strm);
161
static pj_status_t ca_stream_get_param(pjmedia_aud_stream *strm,
162
pjmedia_aud_param *param);
163
static pj_status_t ca_stream_get_cap(pjmedia_aud_stream *strm,
164
pjmedia_aud_dev_cap cap,
166
static pj_status_t ca_stream_set_cap(pjmedia_aud_stream *strm,
167
pjmedia_aud_dev_cap cap,
169
static pj_status_t ca_stream_start(pjmedia_aud_stream *strm);
170
static pj_status_t ca_stream_stop(pjmedia_aud_stream *strm);
171
static pj_status_t ca_stream_destroy(pjmedia_aud_stream *strm);
172
static pj_status_t create_audio_unit(AudioComponent io_comp,
173
AudioDeviceID dev_id,
175
struct coreaudio_stream *strm,
178
static void interruptionListener(void *inClientData, UInt32 inInterruption);
179
static void propListener(void * inClientData,
180
AudioSessionPropertyID inID,
182
const void * inData);
186
static pjmedia_aud_dev_factory_op factory_op =
190
&ca_factory_get_dev_count,
191
&ca_factory_get_dev_info,
192
&ca_factory_default_param,
193
&ca_factory_create_stream,
197
static pjmedia_aud_stream_op stream_op =
199
&ca_stream_get_param,
208
/****************************************************************************
212
* Init coreaudio audio driver.
214
pjmedia_aud_dev_factory* pjmedia_coreaudio_factory(pj_pool_factory *pf)
216
struct coreaudio_factory *f;
219
pool = pj_pool_create(pf, "core audio base", 1000, 1000, NULL);
220
f = PJ_POOL_ZALLOC_T(pool, struct coreaudio_factory);
223
f->base.op = &factory_op;
229
/* API: init factory */
230
static pj_status_t ca_factory_init(pjmedia_aud_dev_factory *f)
232
struct coreaudio_factory *cf = (struct coreaudio_factory*)f;
233
AudioComponentDescription desc;
240
pj_list_init(&cf->streams);
241
status = pj_mutex_create_recursive(cf->base_pool,
244
if (status != PJ_SUCCESS)
247
desc.componentType = kAudioUnitType_Output;
249
desc.componentSubType = kAudioUnitSubType_HALOutput;
251
desc.componentSubType = kAudioUnitSubType_RemoteIO;
253
desc.componentManufacturer = kAudioUnitManufacturer_Apple;
254
desc.componentFlags = 0;
255
desc.componentFlagsMask = 0;
257
cf->io_comp = AudioComponentFindNext(NULL, &desc);
258
if (cf->io_comp == NULL)
259
return PJMEDIA_EAUD_INIT; // cannot find IO unit;
261
status = ca_factory_refresh(f);
262
if (status != PJ_SUCCESS)
266
cf->pool = pj_pool_create(cf->pf, "core audio", 1000, 1000, NULL);
268
cf->dev_info = (struct coreaudio_dev_info*)
269
pj_pool_calloc(cf->pool, cf->dev_count,
270
sizeof(struct coreaudio_dev_info));
271
for (i = 0; i < cf->dev_count; i++) {
272
struct coreaudio_dev_info *cdi;
274
cdi = &cf->dev_info[i];
275
pj_bzero(cdi, sizeof(*cdi));
277
strcpy(cdi->info.name, "iPhone IO device");
278
strcpy(cdi->info.driver, "apple");
279
cdi->info.input_count = 1;
280
cdi->info.output_count = 1;
281
cdi->info.default_samples_per_sec = 8000;
283
/* Set the device capabilities here */
284
cdi->info.caps = PJMEDIA_AUD_DEV_CAP_INPUT_LATENCY |
285
PJMEDIA_AUD_DEV_CAP_OUTPUT_LATENCY |
286
PJMEDIA_AUD_DEV_CAP_OUTPUT_VOLUME_SETTING |
287
PJMEDIA_AUD_DEV_CAP_INPUT_ROUTE |
288
PJMEDIA_AUD_DEV_CAP_OUTPUT_ROUTE |
289
PJMEDIA_AUD_DEV_CAP_EC;
290
cdi->info.routes = PJMEDIA_AUD_DEV_ROUTE_LOUDSPEAKER |
291
PJMEDIA_AUD_DEV_ROUTE_EARPIECE |
292
PJMEDIA_AUD_DEV_ROUTE_BLUETOOTH;
294
PJ_LOG(4, (THIS_FILE, " dev_id %d: %s (in=%d, out=%d) %dHz",
297
cdi->info.input_count,
298
cdi->info.output_count,
299
cdi->info.default_samples_per_sec));
302
/* Initialize the Audio Session */
303
ostatus = AudioSessionInitialize(NULL, NULL, interruptionListener, NULL);
304
if (ostatus != kAudioSessionNoError) {
305
PJ_LOG(4, (THIS_FILE,
306
"Warning: cannot initialize audio session services (%i)",
310
/* Listen for audio routing change notifications. */
311
#if USE_AUDIO_ROUTE_CHANGE_PROP_LISTENER != 0
312
ostatus = AudioSessionAddPropertyListener(
313
kAudioSessionProperty_AudioRouteChange,
315
if (ostatus != kAudioSessionNoError) {
316
PJ_LOG(4, (THIS_FILE,
317
"Warning: cannot listen for audio route change "
318
"notifications (%i)", ostatus));
325
PJ_LOG(4, (THIS_FILE, "core audio initialized"));
330
/* API: destroy factory */
331
static pj_status_t ca_factory_destroy(pjmedia_aud_dev_factory *f)
333
struct coreaudio_factory *cf = (struct coreaudio_factory*)f;
337
pj_assert(cf->base_pool);
338
pj_assert(pj_list_empty(&cf->streams));
341
#if USE_AUDIO_ROUTE_CHANGE_PROP_LISTENER != 0
342
AudioSessionRemovePropertyListenerWithUserData(
343
kAudioSessionProperty_AudioRouteChange, propListener, cf);
348
pj_pool_release(cf->pool);
353
pj_mutex_lock(cf->mutex);
355
pj_mutex_unlock(cf->mutex);
356
pj_mutex_destroy(cf->mutex);
360
pool = cf->base_pool;
361
cf->base_pool = NULL;
362
pj_pool_release(pool);
367
/* API: refresh the device list */
368
static pj_status_t ca_factory_refresh(pjmedia_aud_dev_factory *f)
371
/* iPhone doesn't support refreshing the device list */
375
struct coreaudio_factory *cf = (struct coreaudio_factory*)f;
378
AudioObjectPropertyAddress addr;
379
AudioDeviceID *dev_ids;
380
UInt32 buf_size, dev_size, size = sizeof(AudioDeviceID);
381
AudioBufferList *buf = NULL;
384
if (cf->pool != NULL) {
385
pj_pool_release(cf->pool);
390
cf->pool = pj_pool_create(cf->pf, "core audio", 1000, 1000, NULL);
392
/* Find out how many audio devices there are */
393
addr.mSelector = kAudioHardwarePropertyDevices;
394
addr.mScope = kAudioObjectPropertyScopeGlobal;
395
addr.mElement = kAudioObjectPropertyElementMaster;
396
ostatus = AudioObjectGetPropertyDataSize(kAudioObjectSystemObject, &addr,
398
if (ostatus != noErr) {
402
/* Calculate the number of audio devices available */
403
dev_count = dev_size / size;
405
PJ_LOG(4,(THIS_FILE, "core audio found no sound devices"));
406
/* Enabling this will cause pjsua-lib initialization to fail when
407
* there is no sound device installed in the system, even when pjsua
408
* has been run with --null-audio. Moreover, it might be better to
409
* think that the core audio backend initialization is successful,
410
* regardless there is no audio device installed, as later application
411
* can check it using get_dev_count().
412
return PJMEDIA_EAUD_NODEV;
416
PJ_LOG(4, (THIS_FILE, "core audio detected %d devices",
419
/* Get all the audio device IDs */
420
dev_ids = (AudioDeviceID *)pj_pool_calloc(cf->pool, dev_size, size);
423
pj_bzero(dev_ids, dev_count);
424
ostatus = AudioObjectGetPropertyData(kAudioObjectSystemObject, &addr,
426
&dev_size, (void *)dev_ids);
427
if (ostatus != noErr ) {
428
/* This should not happen since we have successfully retrieved
429
* the property data size before
431
return PJMEDIA_EAUD_INIT;
435
AudioDeviceID dev_id = kAudioObjectUnknown;
438
/* Find default audio input device */
439
addr.mSelector = kAudioHardwarePropertyDefaultInputDevice;
440
addr.mScope = kAudioObjectPropertyScopeGlobal;
441
addr.mElement = kAudioObjectPropertyElementMaster;
442
size = sizeof(dev_id);
444
ostatus = AudioObjectGetPropertyData(kAudioObjectSystemObject,
446
&size, (void *)&dev_id);
447
if (ostatus != noErr && dev_id != dev_ids[idx]) {
448
AudioDeviceID temp_id = dev_ids[idx];
450
for (i = idx + 1; i < dev_size; i++) {
451
if (dev_ids[i] == dev_id) {
452
dev_ids[idx++] = dev_id;
453
dev_ids[i] = temp_id;
459
/* Find default audio output device */
460
addr.mSelector = kAudioHardwarePropertyDefaultOutputDevice;
461
ostatus = AudioObjectGetPropertyData(kAudioObjectSystemObject,
463
&size, (void *)&dev_id);
464
if (ostatus != noErr && dev_id != dev_ids[idx]) {
465
AudioDeviceID temp_id = dev_ids[idx];
467
for (i = idx + 1; i < dev_size; i++) {
468
if (dev_ids[i] == dev_id) {
469
dev_ids[idx] = dev_id;
470
dev_ids[i] = temp_id;
477
/* Build the devices' info */
478
cf->dev_info = (struct coreaudio_dev_info*)
479
pj_pool_calloc(cf->pool, dev_count,
480
sizeof(struct coreaudio_dev_info));
482
for (i = 0; i < dev_count; i++) {
483
struct coreaudio_dev_info *cdi;
486
cdi = &cf->dev_info[i];
487
pj_bzero(cdi, sizeof(*cdi));
488
cdi->dev_id = dev_ids[i];
490
/* Get device name */
491
addr.mSelector = kAudioDevicePropertyDeviceName;
492
addr.mScope = kAudioObjectPropertyScopeGlobal;
493
addr.mElement = kAudioObjectPropertyElementMaster;
494
size = sizeof(cdi->info.name);
495
AudioObjectGetPropertyData(cdi->dev_id, &addr,
497
&size, (void *)cdi->info.name);
499
strcpy(cdi->info.driver, "core audio");
501
/* Get the number of input channels */
502
addr.mSelector = kAudioDevicePropertyStreamConfiguration;
503
addr.mScope = kAudioDevicePropertyScopeInput;
505
ostatus = AudioObjectGetPropertyDataSize(cdi->dev_id, &addr,
507
if (ostatus == noErr && size > 0) {
509
if (size > buf_size) {
510
buf = pj_pool_alloc(cf->pool, size);
516
/* Get the input stream configuration */
517
ostatus = AudioObjectGetPropertyData(cdi->dev_id, &addr,
520
if (ostatus == noErr) {
521
/* Count the total number of input channels in
524
for (idx = 0; idx < buf->mNumberBuffers; idx++) {
525
cdi->info.input_count +=
526
buf->mBuffers[idx].mNumberChannels;
532
/* Get the number of output channels */
533
addr.mScope = kAudioDevicePropertyScopeOutput;
535
ostatus = AudioObjectGetPropertyDataSize(cdi->dev_id, &addr,
537
if (ostatus == noErr && size > 0) {
539
if (size > buf_size) {
540
buf = pj_pool_alloc(cf->pool, size);
546
/* Get the output stream configuration */
547
ostatus = AudioObjectGetPropertyData(cdi->dev_id, &addr,
550
if (ostatus == noErr) {
551
/* Count the total number of output channels in
554
for (idx = 0; idx < buf->mNumberBuffers; idx++) {
555
cdi->info.output_count +=
556
buf->mBuffers[idx].mNumberChannels;
562
/* Get default sample rate */
563
addr.mSelector = kAudioDevicePropertyNominalSampleRate;
564
addr.mScope = kAudioObjectPropertyScopeGlobal;
565
size = sizeof(Float64);
566
ostatus = AudioObjectGetPropertyData (cdi->dev_id, &addr,
569
cdi->info.default_samples_per_sec = (ostatus == noErr ?
573
/* Set device capabilities here */
574
if (cdi->info.input_count > 0) {
575
cdi->info.caps |= PJMEDIA_AUD_DEV_CAP_INPUT_LATENCY;
577
if (cdi->info.output_count > 0) {
578
cdi->info.caps |= PJMEDIA_AUD_DEV_CAP_OUTPUT_LATENCY;
579
addr.mSelector = kAudioDevicePropertyVolumeScalar;
580
addr.mScope = kAudioDevicePropertyScopeOutput;
581
if (AudioObjectHasProperty(cdi->dev_id, &addr)) {
582
cdi->info.caps |= PJMEDIA_AUD_DEV_CAP_OUTPUT_VOLUME_SETTING;
588
PJ_LOG(4, (THIS_FILE, " dev_id %d: %s (in=%d, out=%d) %dHz",
591
cdi->info.input_count,
592
cdi->info.output_count,
593
cdi->info.default_samples_per_sec));
600
/* API: get number of devices */
601
static unsigned ca_factory_get_dev_count(pjmedia_aud_dev_factory *f)
603
struct coreaudio_factory *cf = (struct coreaudio_factory*)f;
604
return cf->dev_count;
607
/* API: get device info */
608
static pj_status_t ca_factory_get_dev_info(pjmedia_aud_dev_factory *f,
610
pjmedia_aud_dev_info *info)
612
struct coreaudio_factory *cf = (struct coreaudio_factory*)f;
614
PJ_ASSERT_RETURN(index < cf->dev_count, PJMEDIA_EAUD_INVDEV);
616
pj_memcpy(info, &cf->dev_info[index].info, sizeof(*info));
621
/* API: create default device parameter */
622
static pj_status_t ca_factory_default_param(pjmedia_aud_dev_factory *f,
624
pjmedia_aud_param *param)
626
struct coreaudio_factory *cf = (struct coreaudio_factory*)f;
627
struct coreaudio_dev_info *di = &cf->dev_info[index];
629
PJ_ASSERT_RETURN(index < cf->dev_count, PJMEDIA_EAUD_INVDEV);
631
pj_bzero(param, sizeof(*param));
632
if (di->info.input_count && di->info.output_count) {
633
param->dir = PJMEDIA_DIR_CAPTURE_PLAYBACK;
634
param->rec_id = index;
635
param->play_id = index;
636
} else if (di->info.input_count) {
637
param->dir = PJMEDIA_DIR_CAPTURE;
638
param->rec_id = index;
639
param->play_id = PJMEDIA_AUD_INVALID_DEV;
640
} else if (di->info.output_count) {
641
param->dir = PJMEDIA_DIR_PLAYBACK;
642
param->play_id = index;
643
param->rec_id = PJMEDIA_AUD_INVALID_DEV;
645
return PJMEDIA_EAUD_INVDEV;
648
/* Set the mandatory settings here */
649
param->clock_rate = di->info.default_samples_per_sec;
650
param->channel_count = 1;
651
param->samples_per_frame = di->info.default_samples_per_sec * 20 / 1000;
652
param->bits_per_sample = 16;
654
/* Set the param for device capabilities here */
655
param->flags = PJMEDIA_AUD_DEV_CAP_INPUT_LATENCY |
656
PJMEDIA_AUD_DEV_CAP_OUTPUT_LATENCY;
657
param->input_latency_ms = PJMEDIA_SND_DEFAULT_REC_LATENCY;
658
param->output_latency_ms = PJMEDIA_SND_DEFAULT_PLAY_LATENCY;
663
OSStatus resampleProc(AudioConverterRef inAudioConverter,
664
UInt32 *ioNumberDataPackets,
665
AudioBufferList *ioData,
666
AudioStreamPacketDescription **outDataPacketDescription,
669
struct coreaudio_stream *strm = (struct coreaudio_stream*)inUserData;
671
if (*ioNumberDataPackets > strm->resample_buf_size)
672
*ioNumberDataPackets = strm->resample_buf_size;
674
ioData->mNumberBuffers = 1;
675
ioData->mBuffers[0].mNumberChannels = strm->streamFormat.mChannelsPerFrame;
676
ioData->mBuffers[0].mData = strm->resample_buf_ptr;
677
ioData->mBuffers[0].mDataByteSize = *ioNumberDataPackets *
678
strm->streamFormat.mChannelsPerFrame *
679
strm->param.bits_per_sample >> 3;
684
static OSStatus resample_callback(void *inRefCon,
685
AudioUnitRenderActionFlags *ioActionFlags,
686
const AudioTimeStamp *inTimeStamp,
688
UInt32 inNumberFrames,
689
AudioBufferList *ioData)
691
struct coreaudio_stream *strm = (struct coreaudio_stream*)inRefCon;
693
pj_status_t status = 0;
695
AudioBufferList *buf = strm->audio_buf;
699
pj_assert(!strm->quit_flag);
701
/* Known cases of callback's thread:
702
* - The thread may be changed in the middle of a session
703
* it happens when plugging/unplugging headphone.
704
* - The same thread may be reused in consecutive sessions. The first
705
* session will leave TLS set, but release the TLS data address,
706
* so the second session must re-register the callback's thread.
708
if (strm->rec_thread_initialized == 0 || !pj_thread_is_registered())
710
pj_bzero(strm->rec_thread_desc, sizeof(pj_thread_desc));
711
status = pj_thread_register("ca_rec", strm->rec_thread_desc,
713
strm->rec_thread_initialized = 1;
714
PJ_LOG(5,(THIS_FILE, "Recorder thread started, (%i frames)",
718
buf->mBuffers[0].mData = NULL;
719
buf->mBuffers[0].mDataByteSize = inNumberFrames *
720
strm->streamFormat.mChannelsPerFrame;
721
/* Render the unit to get input data */
722
ostatus = AudioUnitRender(strm->io_units[0],
729
if (ostatus != noErr) {
730
PJ_LOG(5, (THIS_FILE, "Core audio unit render error %i", ostatus));
733
input = (pj_int16_t *)buf->mBuffers[0].mData;
735
resampleSize = strm->resample_buf_size;
736
nsamples = inNumberFrames * strm->param.channel_count +
737
strm->resample_buf_count;
739
if (nsamples >= resampleSize) {
741
UInt32 resampleOutput = strm->param.samples_per_frame /
742
strm->streamFormat.mChannelsPerFrame;
745
frame.type = PJMEDIA_FRAME_TYPE_AUDIO;
746
frame.buf = (void*) strm->rec_buf;
747
frame.size = strm->param.samples_per_frame *
748
strm->param.bits_per_sample >> 3;
751
ab.mNumberBuffers = 1;
752
ab.mBuffers[0].mNumberChannels = strm->streamFormat.mChannelsPerFrame;
753
ab.mBuffers[0].mData = strm->rec_buf;
754
ab.mBuffers[0].mDataByteSize = frame.size;
756
/* If buffer is not empty, combine the buffer with the just incoming
757
* samples, then call put_frame.
759
if (strm->resample_buf_count) {
760
unsigned chunk_count = resampleSize - strm->resample_buf_count;
761
pjmedia_copy_samples(strm->resample_buf + strm->resample_buf_count,
764
/* Do the resample */
766
strm->resample_buf_ptr = strm->resample_buf;
767
ostatus = AudioConverterFillComplexBuffer(strm->resample,
773
if (ostatus != noErr) {
776
frame.timestamp.u64 = strm->rec_timestamp.u64;
778
status = (*strm->rec_cb)(strm->user_data, &frame);
780
input = input + chunk_count;
781
nsamples -= resampleSize;
782
strm->resample_buf_count = 0;
783
strm->rec_timestamp.u64 += strm->param.samples_per_frame /
784
strm->param.channel_count;
788
/* Give all frames we have */
789
while (nsamples >= resampleSize && status == 0) {
790
frame.timestamp.u64 = strm->rec_timestamp.u64;
792
/* Do the resample */
793
strm->resample_buf_ptr = input;
794
ab.mBuffers[0].mDataByteSize = frame.size;
795
resampleOutput = strm->param.samples_per_frame /
796
strm->streamFormat.mChannelsPerFrame;
797
ostatus = AudioConverterFillComplexBuffer(strm->resample,
803
if (ostatus != noErr) {
807
status = (*strm->rec_cb)(strm->user_data, &frame);
809
input = (pj_int16_t*) input + resampleSize;
810
nsamples -= resampleSize;
811
strm->rec_timestamp.u64 += strm->param.samples_per_frame /
812
strm->param.channel_count;
815
/* Store the remaining samples into the buffer */
816
if (nsamples && status == 0) {
817
strm->resample_buf_count = nsamples;
818
pjmedia_copy_samples(strm->resample_buf, input,
823
/* Not enough samples, let's just store them in the buffer */
824
pjmedia_copy_samples(strm->resample_buf + strm->resample_buf_count,
826
inNumberFrames * strm->param.channel_count);
827
strm->resample_buf_count += inNumberFrames *
828
strm->param.channel_count;
837
static OSStatus input_callback(void *inRefCon,
838
AudioUnitRenderActionFlags *ioActionFlags,
839
const AudioTimeStamp *inTimeStamp,
841
UInt32 inNumberFrames,
842
AudioBufferList *ioData)
844
struct coreaudio_stream *strm = (struct coreaudio_stream*)inRefCon;
846
pj_status_t status = 0;
848
AudioBufferList *buf = strm->audio_buf;
851
pj_assert(!strm->quit_flag);
853
/* Known cases of callback's thread:
854
* - The thread may be changed in the middle of a session
855
* it happens when plugging/unplugging headphone.
856
* - The same thread may be reused in consecutive sessions. The first
857
* session will leave TLS set, but release the TLS data address,
858
* so the second session must re-register the callback's thread.
860
if (strm->rec_thread_initialized == 0 || !pj_thread_is_registered())
862
pj_bzero(strm->rec_thread_desc, sizeof(pj_thread_desc));
863
status = pj_thread_register("ca_rec", strm->rec_thread_desc,
865
strm->rec_thread_initialized = 1;
866
PJ_LOG(5,(THIS_FILE, "Recorder thread started, (%i frames)",
870
buf->mBuffers[0].mData = NULL;
871
buf->mBuffers[0].mDataByteSize = inNumberFrames *
872
strm->streamFormat.mChannelsPerFrame;
873
/* Render the unit to get input data */
874
ostatus = AudioUnitRender(strm->io_units[0],
881
if (ostatus != noErr) {
882
PJ_LOG(5, (THIS_FILE, "Core audio unit render error %i", ostatus));
885
input = (pj_int16_t *)buf->mBuffers[0].mData;
887
/* Calculate number of samples we've got */
888
nsamples = inNumberFrames * strm->param.channel_count +
890
if (nsamples >= strm->param.samples_per_frame) {
893
frame.type = PJMEDIA_FRAME_TYPE_AUDIO;
894
frame.size = strm->param.samples_per_frame *
895
strm->param.bits_per_sample >> 3;
898
/* If buffer is not empty, combine the buffer with the just incoming
899
* samples, then call put_frame.
901
if (strm->rec_buf_count) {
902
unsigned chunk_count = 0;
904
chunk_count = strm->param.samples_per_frame - strm->rec_buf_count;
905
pjmedia_copy_samples(strm->rec_buf + strm->rec_buf_count,
908
frame.buf = (void*) strm->rec_buf;
909
frame.timestamp.u64 = strm->rec_timestamp.u64;
911
status = (*strm->rec_cb)(strm->user_data, &frame);
913
input = input + chunk_count;
914
nsamples -= strm->param.samples_per_frame;
915
strm->rec_buf_count = 0;
916
strm->rec_timestamp.u64 += strm->param.samples_per_frame /
917
strm->param.channel_count;
920
/* Give all frames we have */
921
while (nsamples >= strm->param.samples_per_frame && status == 0) {
922
frame.buf = (void*) input;
923
frame.timestamp.u64 = strm->rec_timestamp.u64;
925
status = (*strm->rec_cb)(strm->user_data, &frame);
927
input = (pj_int16_t*) input + strm->param.samples_per_frame;
928
nsamples -= strm->param.samples_per_frame;
929
strm->rec_timestamp.u64 += strm->param.samples_per_frame /
930
strm->param.channel_count;
933
/* Store the remaining samples into the buffer */
934
if (nsamples && status == 0) {
935
strm->rec_buf_count = nsamples;
936
pjmedia_copy_samples(strm->rec_buf, input,
941
/* Not enough samples, let's just store them in the buffer */
942
pjmedia_copy_samples(strm->rec_buf + strm->rec_buf_count,
944
inNumberFrames * strm->param.channel_count);
945
strm->rec_buf_count += inNumberFrames * strm->param.channel_count;
954
static OSStatus output_renderer(void *inRefCon,
955
AudioUnitRenderActionFlags *ioActionFlags,
956
const AudioTimeStamp *inTimeStamp,
958
UInt32 inNumberFrames,
959
AudioBufferList *ioData)
961
struct coreaudio_stream *stream = (struct coreaudio_stream*)inRefCon;
962
pj_status_t status = 0;
963
unsigned nsamples_req = inNumberFrames * stream->param.channel_count;
964
pj_int16_t *output = ioData->mBuffers[0].mData;
966
pj_assert(!stream->quit_flag);
968
/* Known cases of callback's thread:
969
* - The thread may be changed in the middle of a session
970
* it happens when plugging/unplugging headphone.
971
* - The same thread may be reused in consecutive sessions. The first
972
* session will leave TLS set, but release the TLS data address,
973
* so the second session must re-register the callback's thread.
975
if (stream->play_thread_initialized == 0 || !pj_thread_is_registered())
977
pj_bzero(stream->play_thread_desc, sizeof(pj_thread_desc));
978
status = pj_thread_register("coreaudio", stream->play_thread_desc,
979
&stream->play_thread);
980
stream->play_thread_initialized = 1;
981
PJ_LOG(5,(THIS_FILE, "Player thread started, (%i frames)",
986
/* Check if any buffered samples */
987
if (stream->play_buf_count) {
988
/* samples buffered >= requested by sound device */
989
if (stream->play_buf_count >= nsamples_req) {
990
pjmedia_copy_samples((pj_int16_t*)output, stream->play_buf,
992
stream->play_buf_count -= nsamples_req;
993
pjmedia_move_samples(stream->play_buf,
994
stream->play_buf + nsamples_req,
995
stream->play_buf_count);
1001
/* samples buffered < requested by sound device */
1002
pjmedia_copy_samples((pj_int16_t*)output, stream->play_buf,
1003
stream->play_buf_count);
1004
nsamples_req -= stream->play_buf_count;
1005
output = (pj_int16_t*)output + stream->play_buf_count;
1006
stream->play_buf_count = 0;
1009
/* Fill output buffer as requested */
1010
while (nsamples_req && status == 0) {
1011
pjmedia_frame frame;
1013
frame.type = PJMEDIA_FRAME_TYPE_AUDIO;
1014
frame.size = stream->param.samples_per_frame *
1015
stream->param.bits_per_sample >> 3;
1016
frame.timestamp.u64 = stream->play_timestamp.u64;
1019
if (nsamples_req >= stream->param.samples_per_frame) {
1021
status = (*stream->play_cb)(stream->user_data, &frame);
1022
if (status != PJ_SUCCESS)
1025
if (frame.type != PJMEDIA_FRAME_TYPE_AUDIO)
1026
pj_bzero(frame.buf, frame.size);
1028
nsamples_req -= stream->param.samples_per_frame;
1029
output = (pj_int16_t*)output + stream->param.samples_per_frame;
1031
frame.buf = stream->play_buf;
1032
status = (*stream->play_cb)(stream->user_data, &frame);
1033
if (status != PJ_SUCCESS)
1036
if (frame.type != PJMEDIA_FRAME_TYPE_AUDIO)
1037
pj_bzero(frame.buf, frame.size);
1039
pjmedia_copy_samples((pj_int16_t*)output, stream->play_buf,
1041
stream->play_buf_count = stream->param.samples_per_frame -
1043
pjmedia_move_samples(stream->play_buf,
1044
stream->play_buf+nsamples_req,
1045
stream->play_buf_count);
1049
stream->play_timestamp.u64 += stream->param.samples_per_frame /
1050
stream->param.channel_count;
1060
static void propListener(void *inClientData,
1061
AudioSessionPropertyID inID,
1063
const void * inData)
1065
struct coreaudio_factory *cf = (struct coreaudio_factory*)inClientData;
1066
struct stream_list *it, *itBegin;
1067
CFDictionaryRef routeDictionary;
1072
if (inID != kAudioSessionProperty_AudioRouteChange)
1075
routeDictionary = (CFDictionaryRef)inData;
1076
reason = (CFNumberRef)
1077
CFDictionaryGetValue(
1079
CFSTR(kAudioSession_AudioRouteChangeKey_Reason));
1080
CFNumberGetValue(reason, kCFNumberSInt32Type, &reasonVal);
1082
if (reasonVal != kAudioSessionRouteChangeReason_OldDeviceUnavailable) {
1083
PJ_LOG(3, (THIS_FILE, "ignoring audio route change..."));
1087
PJ_LOG(3, (THIS_FILE, "audio route changed"));
1089
pj_mutex_lock(cf->mutex);
1090
itBegin = &cf->streams;
1091
for (it = itBegin->next; it != itBegin; it = it->next) {
1092
if (it->stream->interrupted)
1096
status = ca_stream_stop((pjmedia_aud_stream *)it->stream);
1097
status = ca_stream_start((pjmedia_aud_stream *)it->stream);
1098
if (status != PJ_SUCCESS) {
1099
PJ_LOG(3, (THIS_FILE,
1100
"Error: failed to restart the audio unit (%i)",
1104
PJ_LOG(3, (THIS_FILE, "core audio unit successfully restarted"));
1107
pj_mutex_unlock(cf->mutex);
1110
static void interruptionListener(void *inClientData, UInt32 inInterruption)
1112
struct stream_list *it, *itBegin;
1114
pj_thread_desc thread_desc;
1115
pj_thread_t *thread;
1117
/* Register the thread with PJLIB, this is must for any external threads
1118
* which need to use the PJLIB framework.
1120
if (!pj_thread_is_registered()) {
1121
pj_bzero(thread_desc, sizeof(pj_thread_desc));
1122
status = pj_thread_register("intListener", thread_desc, &thread);
1125
PJ_LOG(3, (THIS_FILE, "Session interrupted! --- %s ---",
1126
inInterruption == kAudioSessionBeginInterruption ?
1127
"Begin Interruption" : "End Interruption"));
1132
pj_mutex_lock(cf_instance->mutex);
1133
itBegin = &cf_instance->streams;
1134
for (it = itBegin->next; it != itBegin; it = it->next) {
1135
if (inInterruption == kAudioSessionEndInterruption &&
1136
it->stream->interrupted == PJ_TRUE)
1138
UInt32 audioCategory;
1141
/* Make sure that your application can receive remote control
1142
* events by adding the code:
1143
* [[UIApplication sharedApplication]
1144
* beginReceivingRemoteControlEvents];
1145
* Otherwise audio unit will fail to restart while your
1146
* application is in the background mode.
1148
/* Make sure we set the correct audio category before restarting */
1149
audioCategory = kAudioSessionCategory_PlayAndRecord;
1150
ostatus = AudioSessionSetProperty(kAudioSessionProperty_AudioCategory,
1151
sizeof(audioCategory),
1153
if (ostatus != kAudioSessionNoError) {
1154
PJ_LOG(4, (THIS_FILE,
1155
"Warning: cannot set the audio session category (%i)",
1159
/* Restart the stream */
1160
status = ca_stream_start((pjmedia_aud_stream*)it->stream);
1161
if (status != PJ_SUCCESS) {
1162
PJ_LOG(3, (THIS_FILE,
1163
"Error: failed to restart the audio unit (%i)",
1167
PJ_LOG(3, (THIS_FILE, "core audio unit successfully resumed"
1168
" after interruption"));
1169
} else if (inInterruption == kAudioSessionBeginInterruption &&
1170
it->stream->running == PJ_TRUE)
1172
status = ca_stream_stop((pjmedia_aud_stream*)it->stream);
1173
it->stream->interrupted = PJ_TRUE;
1176
pj_mutex_unlock(cf_instance->mutex);
1182
/* Internal: create audio converter for resampling the recorder device */
1183
static pj_status_t create_audio_resample(struct coreaudio_stream *strm,
1184
AudioStreamBasicDescription *desc)
1188
pj_assert(strm->streamFormat.mSampleRate != desc->mSampleRate);
1189
pj_assert(NULL == strm->resample);
1190
pj_assert(NULL == strm->resample_buf);
1192
/* Create the audio converter */
1193
ostatus = AudioConverterNew(desc, &strm->streamFormat, &strm->resample);
1194
if (ostatus != noErr) {
1195
return PJMEDIA_AUDIODEV_ERRNO_FROM_COREAUDIO(ostatus);
1199
* Allocate the buffer required to hold enough input data
1201
strm->resample_buf_size = (unsigned)(desc->mSampleRate *
1202
strm->param.samples_per_frame /
1203
strm->param.clock_rate);
1204
strm->resample_buf = (pj_int16_t*)
1205
pj_pool_alloc(strm->pool,
1206
strm->resample_buf_size *
1207
strm->param.bits_per_sample >> 3);
1208
if (!strm->resample_buf)
1210
strm->resample_buf_count = 0;
1216
/* Internal: create audio unit for recorder/playback device */
1217
static pj_status_t create_audio_unit(AudioComponent io_comp,
1218
AudioDeviceID dev_id,
1220
struct coreaudio_stream *strm,
1225
UInt32 audioCategory = kAudioSessionCategory_PlayAndRecord;
1226
/* We want to be able to open playback and recording streams */
1227
ostatus = AudioSessionSetProperty(kAudioSessionProperty_AudioCategory,
1228
sizeof(audioCategory),
1230
if (ostatus != kAudioSessionNoError) {
1231
PJ_LOG(4, (THIS_FILE,
1232
"Warning: cannot set the audio session category (%i)",
1237
/* Create an audio unit to interface with the device */
1238
ostatus = AudioComponentInstanceNew(io_comp, io_unit);
1239
if (ostatus != noErr) {
1240
return PJMEDIA_AUDIODEV_ERRNO_FROM_COREAUDIO(ostatus);
1243
/* Set audio unit's properties for capture device */
1244
if (dir & PJMEDIA_DIR_CAPTURE) {
1248
ostatus = AudioUnitSetProperty(*io_unit,
1249
kAudioOutputUnitProperty_EnableIO,
1250
kAudioUnitScope_Input,
1254
if (ostatus != noErr) {
1255
PJ_LOG(4, (THIS_FILE,
1256
"Warning: cannot enable IO of capture device %d",
1260
/* Disable output */
1261
if (!(dir & PJMEDIA_DIR_PLAYBACK)) {
1263
ostatus = AudioUnitSetProperty(*io_unit,
1264
kAudioOutputUnitProperty_EnableIO,
1265
kAudioUnitScope_Output,
1269
if (ostatus != noErr) {
1270
PJ_LOG(4, (THIS_FILE,
1271
"Warning: cannot disable IO of capture device %d",
1277
/* Set audio unit's properties for playback device */
1278
if (dir & PJMEDIA_DIR_PLAYBACK) {
1282
ostatus = AudioUnitSetProperty(*io_unit,
1283
kAudioOutputUnitProperty_EnableIO,
1284
kAudioUnitScope_Output,
1288
if (ostatus != noErr) {
1289
PJ_LOG(4, (THIS_FILE,
1290
"Warning: cannot enable IO of playback device %d",
1297
PJ_LOG(5, (THIS_FILE, "Opening device %d", dev_id));
1298
ostatus = AudioUnitSetProperty(*io_unit,
1299
kAudioOutputUnitProperty_CurrentDevice,
1300
kAudioUnitScope_Global,
1304
if (ostatus != noErr) {
1305
return PJMEDIA_AUDIODEV_ERRNO_FROM_COREAUDIO(ostatus);
1309
if (dir & PJMEDIA_DIR_CAPTURE) {
1311
AudioStreamBasicDescription deviceFormat;
1315
* Keep the sample rate from the device, otherwise we will confuse
1318
size = sizeof(AudioStreamBasicDescription);
1319
ostatus = AudioUnitGetProperty(*io_unit,
1320
kAudioUnitProperty_StreamFormat,
1321
kAudioUnitScope_Input,
1325
if (ostatus != noErr) {
1326
return PJMEDIA_AUDIODEV_ERRNO_FROM_COREAUDIO(ostatus);
1328
strm->streamFormat.mSampleRate = deviceFormat.mSampleRate;
1331
/* When setting the stream format, we have to make sure the sample
1332
* rate is supported. Setting an unsupported sample rate will cause
1333
* AudioUnitRender() to fail later.
1335
ostatus = AudioUnitSetProperty(*io_unit,
1336
kAudioUnitProperty_StreamFormat,
1337
kAudioUnitScope_Output,
1339
&strm->streamFormat,
1340
sizeof(strm->streamFormat));
1341
if (ostatus != noErr) {
1342
return PJMEDIA_AUDIODEV_ERRNO_FROM_COREAUDIO(ostatus);
1346
strm->streamFormat.mSampleRate = strm->param.clock_rate;
1347
size = sizeof(AudioStreamBasicDescription);
1348
ostatus = AudioUnitGetProperty (*io_unit,
1349
kAudioUnitProperty_StreamFormat,
1350
kAudioUnitScope_Output,
1354
if (ostatus == noErr) {
1355
if (strm->streamFormat.mSampleRate != deviceFormat.mSampleRate) {
1356
pj_status_t rc = create_audio_resample(strm, &deviceFormat);
1357
if (PJ_SUCCESS != rc)
1361
return PJMEDIA_AUDIODEV_ERRNO_FROM_COREAUDIO(ostatus);
1366
if (dir & PJMEDIA_DIR_PLAYBACK) {
1367
AURenderCallbackStruct output_cb;
1369
/* Set the stream format */
1370
ostatus = AudioUnitSetProperty(*io_unit,
1371
kAudioUnitProperty_StreamFormat,
1372
kAudioUnitScope_Input,
1374
&strm->streamFormat,
1375
sizeof(strm->streamFormat));
1376
if (ostatus != noErr) {
1377
PJ_LOG(4, (THIS_FILE,
1378
"Warning: cannot set playback stream format of dev %d",
1382
/* Set render callback */
1383
output_cb.inputProc = output_renderer;
1384
output_cb.inputProcRefCon = strm;
1385
ostatus = AudioUnitSetProperty(*io_unit,
1386
kAudioUnitProperty_SetRenderCallback,
1387
kAudioUnitScope_Input,
1391
if (ostatus != noErr) {
1392
return PJMEDIA_AUDIODEV_ERRNO_FROM_COREAUDIO(ostatus);
1395
/* Allocate playback buffer */
1396
strm->play_buf = (pj_int16_t*)pj_pool_alloc(strm->pool,
1397
strm->param.samples_per_frame *
1398
strm->param.bits_per_sample >> 3);
1399
if (!strm->play_buf)
1401
strm->play_buf_count = 0;
1404
if (dir & PJMEDIA_DIR_CAPTURE) {
1405
AURenderCallbackStruct input_cb;
1408
UInt32 size, buf_size;
1411
/* Set input callback */
1412
input_cb.inputProc = strm->resample ? resample_callback :
1414
input_cb.inputProcRefCon = strm;
1415
ostatus = AudioUnitSetProperty(
1417
kAudioOutputUnitProperty_SetInputCallback,
1418
kAudioUnitScope_Global,
1422
if (ostatus != noErr) {
1423
return PJMEDIA_AUDIODEV_ERRNO_FROM_COREAUDIO(ostatus);
1427
/* Get device's buffer frame size */
1428
size = sizeof(UInt32);
1429
ostatus = AudioUnitGetProperty(*io_unit,
1430
kAudioDevicePropertyBufferFrameSize,
1431
kAudioUnitScope_Global,
1435
if (ostatus != noErr)
1437
return PJMEDIA_AUDIODEV_ERRNO_FROM_COREAUDIO(ostatus);
1440
/* Allocate audio buffer */
1441
strm->audio_buf = (AudioBufferList*)pj_pool_alloc(strm->pool,
1442
sizeof(AudioBufferList) + sizeof(AudioBuffer));
1443
if (!strm->audio_buf)
1446
strm->audio_buf->mNumberBuffers = 1;
1447
ab = &strm->audio_buf->mBuffers[0];
1448
ab->mNumberChannels = strm->streamFormat.mChannelsPerFrame;
1449
ab->mDataByteSize = buf_size * ab->mNumberChannels *
1450
strm->param.bits_per_sample >> 3;
1451
ab->mData = pj_pool_alloc(strm->pool,
1457
/* We will let AudioUnitRender() to allocate the buffer
1460
strm->audio_buf = (AudioBufferList*)pj_pool_alloc(strm->pool,
1461
sizeof(AudioBufferList) + sizeof(AudioBuffer));
1462
if (!strm->audio_buf)
1465
strm->audio_buf->mNumberBuffers = 1;
1466
strm->audio_buf->mBuffers[0].mNumberChannels =
1467
strm->streamFormat.mChannelsPerFrame;
1471
/* Allocate recording buffer */
1472
strm->rec_buf = (pj_int16_t*)pj_pool_alloc(strm->pool,
1473
strm->param.samples_per_frame *
1474
strm->param.bits_per_sample >> 3);
1477
strm->rec_buf_count = 0;
1480
/* Initialize the audio unit */
1481
ostatus = AudioUnitInitialize(*io_unit);
1482
if (ostatus != noErr) {
1483
return PJMEDIA_AUDIODEV_ERRNO_FROM_COREAUDIO(ostatus);
1489
/* API: create stream */
1490
static pj_status_t ca_factory_create_stream(pjmedia_aud_dev_factory *f,
1491
const pjmedia_aud_param *param,
1492
pjmedia_aud_rec_cb rec_cb,
1493
pjmedia_aud_play_cb play_cb,
1495
pjmedia_aud_stream **p_aud_strm)
1497
struct coreaudio_factory *cf = (struct coreaudio_factory*)f;
1499
struct coreaudio_stream *strm;
1502
/* Create and Initialize stream descriptor */
1503
pool = pj_pool_create(cf->pf, "coreaudio-dev", 1000, 1000, NULL);
1504
PJ_ASSERT_RETURN(pool != NULL, PJ_ENOMEM);
1506
strm = PJ_POOL_ZALLOC_T(pool, struct coreaudio_stream);
1507
pj_list_init(&strm->list_entry);
1508
strm->list_entry.stream = strm;
1510
pj_memcpy(&strm->param, param, sizeof(*param));
1512
strm->rec_cb = rec_cb;
1513
strm->play_cb = play_cb;
1514
strm->user_data = user_data;
1516
/* Set the stream format */
1517
strm->streamFormat.mSampleRate = param->clock_rate;
1518
strm->streamFormat.mFormatID = kAudioFormatLinearPCM;
1519
strm->streamFormat.mFormatFlags = kLinearPCMFormatFlagIsSignedInteger
1520
| kLinearPCMFormatFlagIsPacked;
1521
strm->streamFormat.mBitsPerChannel = strm->param.bits_per_sample;
1522
strm->streamFormat.mChannelsPerFrame = param->channel_count;
1523
strm->streamFormat.mBytesPerFrame = strm->streamFormat.mChannelsPerFrame
1524
* strm->param.bits_per_sample >> 3;
1525
strm->streamFormat.mFramesPerPacket = 1;
1526
strm->streamFormat.mBytesPerPacket = strm->streamFormat.mBytesPerFrame *
1527
strm->streamFormat.mFramesPerPacket;
1529
/* Apply input/output routes settings before we create the audio units */
1530
if (param->flags & PJMEDIA_AUD_DEV_CAP_INPUT_ROUTE) {
1531
ca_stream_set_cap(&strm->base,
1532
PJMEDIA_AUD_DEV_CAP_INPUT_ROUTE,
1533
¶m->input_route);
1535
if (param->flags & PJMEDIA_AUD_DEV_CAP_OUTPUT_ROUTE) {
1536
ca_stream_set_cap(&strm->base,
1537
PJMEDIA_AUD_DEV_CAP_OUTPUT_ROUTE,
1538
¶m->output_route);
1540
if (param->flags & PJMEDIA_AUD_DEV_CAP_EC) {
1541
ca_stream_set_cap(&strm->base,
1542
PJMEDIA_AUD_DEV_CAP_EC,
1543
¶m->ec_enabled);
1545
pj_bool_t ec = PJ_FALSE;
1546
ca_stream_set_cap(&strm->base,
1547
PJMEDIA_AUD_DEV_CAP_EC, &ec);
1550
strm->io_units[0] = strm->io_units[1] = NULL;
1551
if (param->dir == PJMEDIA_DIR_CAPTURE_PLAYBACK &&
1552
param->rec_id == param->play_id)
1554
/* If both input and output are on the same device, only create
1555
* one audio unit to interface with the device.
1557
status = create_audio_unit(cf->io_comp,
1558
cf->dev_info[param->rec_id].dev_id,
1559
param->dir, strm, &strm->io_units[0]);
1560
if (status != PJ_SUCCESS)
1563
unsigned nunits = 0;
1565
if (param->dir & PJMEDIA_DIR_CAPTURE) {
1566
status = create_audio_unit(cf->io_comp,
1567
cf->dev_info[param->rec_id].dev_id,
1568
PJMEDIA_DIR_CAPTURE,
1569
strm, &strm->io_units[nunits++]);
1570
if (status != PJ_SUCCESS)
1573
if (param->dir & PJMEDIA_DIR_PLAYBACK) {
1575
status = create_audio_unit(cf->io_comp,
1576
cf->dev_info[param->play_id].dev_id,
1577
PJMEDIA_DIR_PLAYBACK,
1578
strm, &strm->io_units[nunits++]);
1579
if (status != PJ_SUCCESS)
1584
/* Apply the remaining settings */
1585
if (param->flags & PJMEDIA_AUD_DEV_CAP_INPUT_LATENCY) {
1586
ca_stream_get_cap(&strm->base,
1587
PJMEDIA_AUD_DEV_CAP_INPUT_LATENCY,
1588
&strm->param.input_latency_ms);
1590
if (param->flags & PJMEDIA_AUD_DEV_CAP_OUTPUT_LATENCY) {
1591
ca_stream_get_cap(&strm->base,
1592
PJMEDIA_AUD_DEV_CAP_OUTPUT_LATENCY,
1593
&strm->param.output_latency_ms);
1595
if (param->flags & PJMEDIA_AUD_DEV_CAP_OUTPUT_VOLUME_SETTING) {
1596
ca_stream_set_cap(&strm->base,
1597
PJMEDIA_AUD_DEV_CAP_OUTPUT_VOLUME_SETTING,
1598
¶m->output_vol);
1601
pj_mutex_lock(strm->cf->mutex);
1602
pj_assert(pj_list_empty(&strm->list_entry));
1603
pj_list_insert_after(&strm->cf->streams, &strm->list_entry);
1604
pj_mutex_unlock(strm->cf->mutex);
1607
strm->base.op = &stream_op;
1608
*p_aud_strm = &strm->base;
1613
ca_stream_destroy((pjmedia_aud_stream *)strm);
1617
/* API: Get stream info. */
1618
static pj_status_t ca_stream_get_param(pjmedia_aud_stream *s,
1619
pjmedia_aud_param *pi)
1621
struct coreaudio_stream *strm = (struct coreaudio_stream*)s;
1623
PJ_ASSERT_RETURN(strm && pi, PJ_EINVAL);
1625
pj_memcpy(pi, &strm->param, sizeof(*pi));
1627
/* Update the device capabilities' values */
1628
if (ca_stream_get_cap(s, PJMEDIA_AUD_DEV_CAP_INPUT_LATENCY,
1629
&pi->input_latency_ms) == PJ_SUCCESS)
1631
pi->flags |= PJMEDIA_AUD_DEV_CAP_INPUT_LATENCY;
1633
if (ca_stream_get_cap(s, PJMEDIA_AUD_DEV_CAP_OUTPUT_LATENCY,
1634
&pi->output_latency_ms) == PJ_SUCCESS)
1636
pi->flags |= PJMEDIA_AUD_DEV_CAP_OUTPUT_LATENCY;
1638
if (ca_stream_get_cap(s, PJMEDIA_AUD_DEV_CAP_OUTPUT_VOLUME_SETTING,
1639
&pi->output_vol) == PJ_SUCCESS)
1641
pi->flags |= PJMEDIA_AUD_DEV_CAP_OUTPUT_VOLUME_SETTING;
1643
if (ca_stream_get_cap(s, PJMEDIA_AUD_DEV_CAP_INPUT_ROUTE,
1644
&pi->input_route) == PJ_SUCCESS)
1646
pi->flags |= PJMEDIA_AUD_DEV_CAP_INPUT_ROUTE;
1648
if (ca_stream_get_cap(s, PJMEDIA_AUD_DEV_CAP_OUTPUT_ROUTE,
1649
&pi->output_route) == PJ_SUCCESS)
1651
pi->flags |= PJMEDIA_AUD_DEV_CAP_OUTPUT_ROUTE;
1653
if (ca_stream_get_cap(s, PJMEDIA_AUD_DEV_CAP_EC,
1654
&pi->ec_enabled) == PJ_SUCCESS)
1656
pi->flags |= PJMEDIA_AUD_DEV_CAP_EC;
1662
/* API: get capability */
1663
static pj_status_t ca_stream_get_cap(pjmedia_aud_stream *s,
1664
pjmedia_aud_dev_cap cap,
1667
struct coreaudio_stream *strm = (struct coreaudio_stream*)s;
1669
PJ_UNUSED_ARG(strm);
1671
PJ_ASSERT_RETURN(s && pval, PJ_EINVAL);
1673
if (cap==PJMEDIA_AUD_DEV_CAP_INPUT_LATENCY &&
1674
(strm->param.dir & PJMEDIA_DIR_CAPTURE))
1677
UInt32 latency, size = sizeof(UInt32);
1679
/* Recording latency */
1680
if (AudioUnitGetProperty (strm->io_units[0],
1681
kAudioDevicePropertyLatency,
1682
kAudioUnitScope_Input,
1688
if (AudioUnitGetProperty (strm->io_units[0],
1689
kAudioDevicePropertyBufferFrameSize,
1690
kAudioUnitScope_Input,
1695
strm->param.input_latency_ms = (latency + latency2) * 1000 /
1696
strm->param.clock_rate;
1697
strm->param.input_latency_ms++;
1701
Float32 latency, latency2;
1702
UInt32 size = sizeof(Float32);
1704
if ((AudioSessionGetProperty(
1705
kAudioSessionProperty_CurrentHardwareInputLatency,
1706
&size, &latency) == kAudioSessionNoError) &&
1707
(AudioSessionGetProperty(
1708
kAudioSessionProperty_CurrentHardwareIOBufferDuration,
1709
&size, &latency2) == kAudioSessionNoError))
1711
strm->param.input_latency_ms = (unsigned)
1712
((latency + latency2) * 1000);
1713
strm->param.input_latency_ms++;
1717
*(unsigned*)pval = strm->param.input_latency_ms;
1719
} else if (cap==PJMEDIA_AUD_DEV_CAP_OUTPUT_LATENCY &&
1720
(strm->param.dir & PJMEDIA_DIR_PLAYBACK))
1723
UInt32 latency, size = sizeof(UInt32);
1724
AudioUnit *io_unit = strm->io_units[1] ? &strm->io_units[1] :
1727
/* Playback latency */
1728
if (AudioUnitGetProperty (*io_unit,
1729
kAudioDevicePropertyLatency,
1730
kAudioUnitScope_Output,
1736
if (AudioUnitGetProperty (*io_unit,
1737
kAudioDevicePropertyBufferFrameSize,
1738
kAudioUnitScope_Output,
1743
strm->param.output_latency_ms = (latency + latency2) * 1000 /
1744
strm->param.clock_rate;
1745
strm->param.output_latency_ms++;
1749
Float32 latency, latency2;
1750
UInt32 size = sizeof(Float32);
1752
if ((AudioSessionGetProperty(
1753
kAudioSessionProperty_CurrentHardwareOutputLatency,
1754
&size, &latency) == kAudioSessionNoError) &&
1755
(AudioSessionGetProperty(
1756
kAudioSessionProperty_CurrentHardwareIOBufferDuration,
1757
&size, &latency2) == kAudioSessionNoError))
1759
strm->param.output_latency_ms = (unsigned)
1760
((latency + latency2) * 1000);
1761
strm->param.output_latency_ms++;
1764
*(unsigned*)pval = (++strm->param.output_latency_ms * 2);
1766
} else if (cap==PJMEDIA_AUD_DEV_CAP_OUTPUT_VOLUME_SETTING &&
1767
(strm->param.dir & PJMEDIA_DIR_PLAYBACK))
1771
UInt32 size = sizeof(Float32);
1773
/* Output volume setting */
1775
ostatus = AudioUnitGetProperty (strm->io_units[1] ? strm->io_units[1] :
1777
kAudioDevicePropertyVolumeScalar,
1778
kAudioUnitScope_Output,
1782
if (ostatus != noErr)
1783
return PJMEDIA_AUDIODEV_ERRNO_FROM_COREAUDIO(ostatus);
1785
ostatus = AudioSessionGetProperty(
1786
kAudioSessionProperty_CurrentHardwareOutputVolume,
1788
if (ostatus != kAudioSessionNoError) {
1789
return PJMEDIA_AUDIODEV_ERRNO_FROM_COREAUDIO(ostatus);
1793
*(unsigned*)pval = (unsigned)(volume * 100);
1796
} else if (cap==PJMEDIA_AUD_DEV_CAP_INPUT_ROUTE &&
1797
(strm->param.dir & PJMEDIA_DIR_CAPTURE))
1799
UInt32 btooth, size = sizeof(UInt32);
1802
ostatus = AudioSessionGetProperty (
1803
kAudioSessionProperty_OverrideCategoryEnableBluetoothInput,
1805
if (ostatus != kAudioSessionNoError) {
1806
return PJMEDIA_AUDIODEV_ERRNO_FROM_COREAUDIO(ostatus);
1809
*(pjmedia_aud_dev_route*)pval = btooth?
1810
PJMEDIA_AUD_DEV_ROUTE_BLUETOOTH:
1811
PJMEDIA_AUD_DEV_ROUTE_DEFAULT;
1813
} else if (cap==PJMEDIA_AUD_DEV_CAP_OUTPUT_ROUTE &&
1814
(strm->param.dir & PJMEDIA_DIR_PLAYBACK))
1817
UInt32 size = sizeof(CFStringRef);
1820
ostatus = AudioSessionGetProperty (kAudioSessionProperty_AudioRoute,
1822
if (ostatus != kAudioSessionNoError) {
1823
return PJMEDIA_AUDIODEV_ERRNO_FROM_COREAUDIO(ostatus);
1827
*(pjmedia_aud_dev_route*)pval = PJMEDIA_AUD_DEV_ROUTE_DEFAULT;
1828
} else if (CFStringHasPrefix(route, CFSTR("Headset"))) {
1829
*(pjmedia_aud_dev_route*)pval = PJMEDIA_AUD_DEV_ROUTE_EARPIECE;
1831
*(pjmedia_aud_dev_route*)pval = PJMEDIA_AUD_DEV_ROUTE_DEFAULT;
1837
} else if (cap==PJMEDIA_AUD_DEV_CAP_EC) {
1838
AudioComponentDescription desc;
1841
ostatus = AudioComponentGetDescription(strm->cf->io_comp, &desc);
1842
if (ostatus != noErr) {
1843
return PJMEDIA_AUDIODEV_ERRNO_FROM_COREAUDIO(ostatus);
1846
*(pj_bool_t*)pval = (desc.componentSubType ==
1847
kAudioUnitSubType_VoiceProcessingIO);
1851
return PJMEDIA_EAUD_INVCAP;
1855
/* API: set capability */
1856
static pj_status_t ca_stream_set_cap(pjmedia_aud_stream *s,
1857
pjmedia_aud_dev_cap cap,
1860
struct coreaudio_stream *strm = (struct coreaudio_stream*)s;
1862
PJ_ASSERT_RETURN(s && pval, PJ_EINVAL);
1865
if (cap==PJMEDIA_AUD_DEV_CAP_OUTPUT_VOLUME_SETTING &&
1866
(strm->param.dir & PJMEDIA_DIR_PLAYBACK))
1869
Float32 volume = *(unsigned*)pval;
1871
/* Output volume setting */
1873
ostatus = AudioUnitSetProperty (strm->io_units[1] ? strm->io_units[1] :
1875
kAudioDevicePropertyVolumeScalar,
1876
kAudioUnitScope_Output,
1880
if (ostatus != noErr) {
1881
return PJMEDIA_AUDIODEV_ERRNO_FROM_COREAUDIO(ostatus);
1883
strm->param.output_vol = *(unsigned*)pval;
1889
if ((cap==PJMEDIA_AUD_DEV_CAP_INPUT_LATENCY &&
1890
(strm->param.dir & PJMEDIA_DIR_CAPTURE)) ||
1891
(cap==PJMEDIA_AUD_DEV_CAP_OUTPUT_LATENCY &&
1892
(strm->param.dir & PJMEDIA_DIR_PLAYBACK)))
1894
Float32 bufferDuration = *(unsigned *)pval;
1898
/* For low-latency audio streaming, you can set this value to
1899
* as low as 5 ms (the default is 23ms). However, lowering the
1900
* latency may cause a decrease in audio quality.
1902
bufferDuration /= 1000;
1903
ostatus = AudioSessionSetProperty(
1904
kAudioSessionProperty_PreferredHardwareIOBufferDuration,
1905
sizeof(bufferDuration), &bufferDuration);
1906
if (ostatus != kAudioSessionNoError) {
1907
PJ_LOG(4, (THIS_FILE,
1908
"Error: cannot set the preferred buffer duration (%i)",
1910
return PJMEDIA_AUDIODEV_ERRNO_FROM_COREAUDIO(ostatus);
1913
ca_stream_get_cap(s, PJMEDIA_AUD_DEV_CAP_INPUT_LATENCY, &latency);
1914
ca_stream_get_cap(s, PJMEDIA_AUD_DEV_CAP_OUTPUT_LATENCY, &latency);
1917
} else if (cap==PJMEDIA_AUD_DEV_CAP_INPUT_ROUTE &&
1918
(strm->param.dir & PJMEDIA_DIR_CAPTURE))
1920
UInt32 btooth = *(pjmedia_aud_dev_route*)pval ==
1921
PJMEDIA_AUD_DEV_ROUTE_BLUETOOTH ? 1 : 0;
1924
ostatus = AudioSessionSetProperty (
1925
kAudioSessionProperty_OverrideCategoryEnableBluetoothInput,
1926
sizeof(btooth), &btooth);
1927
if (ostatus != kAudioSessionNoError) {
1928
return PJMEDIA_AUDIODEV_ERRNO_FROM_COREAUDIO(ostatus);
1930
strm->param.input_route = *(pjmedia_aud_dev_route*)pval;
1932
} else if (cap==PJMEDIA_AUD_DEV_CAP_OUTPUT_ROUTE &&
1933
(strm->param.dir & PJMEDIA_DIR_PLAYBACK))
1936
UInt32 route = *(pjmedia_aud_dev_route*)pval ==
1937
PJMEDIA_AUD_DEV_ROUTE_LOUDSPEAKER ?
1938
kAudioSessionOverrideAudioRoute_Speaker :
1939
kAudioSessionOverrideAudioRoute_None;
1941
ostatus = AudioSessionSetProperty (
1942
kAudioSessionProperty_OverrideAudioRoute,
1943
sizeof(route), &route);
1944
if (ostatus != kAudioSessionNoError) {
1945
return PJMEDIA_AUDIODEV_ERRNO_FROM_COREAUDIO(ostatus);
1947
strm->param.output_route = *(pjmedia_aud_dev_route*)pval;
1949
} else if (cap==PJMEDIA_AUD_DEV_CAP_EC) {
1950
AudioComponentDescription desc;
1951
AudioComponent io_comp;
1953
desc.componentType = kAudioUnitType_Output;
1954
desc.componentSubType = (*(pj_bool_t*)pval)?
1955
kAudioUnitSubType_VoiceProcessingIO :
1956
kAudioUnitSubType_RemoteIO;
1957
desc.componentManufacturer = kAudioUnitManufacturer_Apple;
1958
desc.componentFlags = 0;
1959
desc.componentFlagsMask = 0;
1961
io_comp = AudioComponentFindNext(NULL, &desc);
1962
if (io_comp == NULL)
1963
return PJMEDIA_AUDIODEV_ERRNO_FROM_COREAUDIO(-1);
1964
strm->cf->io_comp = io_comp;
1965
strm->param.ec_enabled = *(pj_bool_t*)pval;
1967
PJ_LOG(4, (THIS_FILE, "Using %s audio unit",
1968
(desc.componentSubType ==
1969
kAudioUnitSubType_RemoteIO? "RemoteIO":
1970
"VoiceProcessingIO")));
1976
return PJMEDIA_EAUD_INVCAP;
1979
/* API: Start stream. */
1980
static pj_status_t ca_stream_start(pjmedia_aud_stream *strm)
1982
struct coreaudio_stream *stream = (struct coreaudio_stream*)strm;
1986
if (stream->running)
1989
stream->quit_flag = 0;
1990
stream->interrupted = PJ_FALSE;
1991
stream->rec_buf_count = 0;
1992
stream->play_buf_count = 0;
1993
stream->resample_buf_count = 0;
1995
if (stream->resample) {
1996
ostatus = AudioConverterReset(stream->resample);
1997
if (ostatus != noErr)
1998
return PJMEDIA_AUDIODEV_ERRNO_FROM_COREAUDIO(ostatus);
2002
AudioSessionSetActive(true);
2005
for (i = 0; i < 2; i++) {
2006
if (stream->io_units[i] == NULL) break;
2007
ostatus = AudioOutputUnitStart(stream->io_units[i]);
2008
if (ostatus != noErr) {
2010
AudioOutputUnitStop(stream->io_units[0]);
2011
return PJMEDIA_AUDIODEV_ERRNO_FROM_COREAUDIO(ostatus);
2015
stream->running = PJ_TRUE;
2017
PJ_LOG(4, (THIS_FILE, "core audio stream started"));
2022
/* API: Stop stream. */
2023
static pj_status_t ca_stream_stop(pjmedia_aud_stream *strm)
2025
struct coreaudio_stream *stream = (struct coreaudio_stream*)strm;
2028
int should_deactivate;
2029
struct stream_list *it, *itBegin;
2031
if (!stream->running)
2034
for (i = 0; i < 2; i++) {
2035
if (stream->io_units[i] == NULL) break;
2036
ostatus = AudioOutputUnitStop(stream->io_units[i]);
2037
if (ostatus != noErr) {
2038
if (i == 0 && stream->io_units[1])
2039
AudioOutputUnitStop(stream->io_units[1]);
2040
return PJMEDIA_AUDIODEV_ERRNO_FROM_COREAUDIO(ostatus);
2044
/* Check whether we need to deactivate the audio session. */
2045
pj_mutex_lock(stream->cf->mutex);
2046
pj_assert(!pj_list_empty(&stream->cf->streams));
2047
pj_assert(!pj_list_empty(&stream->list_entry));
2048
stream->running = PJ_FALSE;
2049
should_deactivate = PJ_TRUE;
2050
itBegin = &stream->cf->streams;
2051
for (it = itBegin->next; it != itBegin; it = it->next) {
2052
if (it->stream->running) {
2053
should_deactivate = PJ_FALSE;
2057
pj_mutex_unlock(stream->cf->mutex);
2060
if (should_deactivate)
2061
AudioSessionSetActive(false);
2064
stream->quit_flag = 1;
2065
stream->play_thread_initialized = 0;
2066
stream->rec_thread_initialized = 0;
2067
pj_bzero(stream->rec_thread_desc, sizeof(pj_thread_desc));
2068
pj_bzero(stream->play_thread_desc, sizeof(pj_thread_desc));
2070
PJ_LOG(4, (THIS_FILE, "core audio stream stopped"));
2076
/* API: Destroy stream. */
2077
static pj_status_t ca_stream_destroy(pjmedia_aud_stream *strm)
2079
struct coreaudio_stream *stream = (struct coreaudio_stream*)strm;
2082
PJ_ASSERT_RETURN(stream != NULL, PJ_EINVAL);
2084
ca_stream_stop(strm);
2086
for (i = 0; i < 2; i++) {
2087
if (stream->io_units[i]) {
2088
AudioUnitUninitialize(stream->io_units[i]);
2089
AudioComponentInstanceDispose(stream->io_units[i]);
2090
stream->io_units[i] = NULL;
2094
if (stream->resample)
2095
AudioConverterDispose(stream->resample);
2097
pj_mutex_lock(stream->cf->mutex);
2098
if (!pj_list_empty(&stream->list_entry))
2099
pj_list_erase(&stream->list_entry);
2100
pj_mutex_unlock(stream->cf->mutex);
2102
pj_pool_release(stream->pool);
2107
#endif /* PJMEDIA_AUDIO_DEV_HAS_COREAUDIO */