1
/* $Id: coreaudio_dev.m 4751 2014-02-19 04:24:08Z 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 <CoreAudio/CoreAudio.h>
38
#include <AVFoundation/AVAudioSession.h>
40
#define AudioDeviceID unsigned
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.
51
#define USE_AUDIO_ROUTE_CHANGE_PROP_LISTENER 0
53
/* Starting iOS SDK 7, Audio Session API is deprecated. */
54
#define USE_AUDIO_SESSION_API 0
57
/* For Mac OS 10.5.x and earlier */
58
#if AUDIO_UNIT_VERSION < 1060
59
#define AudioComponent Component
60
#define AudioComponentDescription ComponentDescription
61
#define AudioComponentInstance ComponentInstance
62
#define AudioComponentFindNext FindNextComponent
63
#define AudioComponentInstanceNew OpenAComponent
64
#define AudioComponentInstanceDispose CloseComponent
68
#define THIS_FILE "coreaudio_dev.c"
70
/* coreaudio device info */
71
struct coreaudio_dev_info
73
pjmedia_aud_dev_info info;
77
/* linked list of streams */
80
PJ_DECL_LIST_MEMBER(struct stream_list);
81
struct coreaudio_stream *stream;
84
/* coreaudio factory */
85
struct coreaudio_factory
87
pjmedia_aud_dev_factory base;
94
struct coreaudio_dev_info *dev_info;
96
AudioComponent io_comp;
97
struct stream_list streams;
101
struct coreaudio_stream
103
pjmedia_aud_stream base; /**< Base stream */
104
pjmedia_aud_param param; /**< Settings */
105
pj_pool_t *pool; /**< Memory pool. */
106
struct coreaudio_factory *cf;
107
struct stream_list list_entry;
109
pjmedia_aud_rec_cb rec_cb; /**< Capture callback. */
110
pjmedia_aud_play_cb play_cb; /**< Playback callback. */
111
void *user_data; /**< Application data. */
113
pj_timestamp play_timestamp;
114
pj_timestamp rec_timestamp;
117
unsigned rec_buf_count;
118
pj_int16_t *play_buf;
119
unsigned play_buf_count;
121
pj_bool_t interrupted;
125
pj_bool_t rec_thread_initialized;
126
pj_thread_desc rec_thread_desc;
127
pj_thread_t *rec_thread;
129
pj_bool_t play_thread_initialized;
130
pj_thread_desc play_thread_desc;
131
pj_thread_t *play_thread;
133
AudioUnit io_units[2];
134
AudioStreamBasicDescription streamFormat;
135
AudioBufferList *audio_buf;
137
AudioConverterRef resample;
138
pj_int16_t *resample_buf;
139
void *resample_buf_ptr;
140
unsigned resample_buf_count;
141
unsigned resample_buf_size;
144
AVAudioSession *sess;
148
/* Static variable */
149
static struct coreaudio_factory *cf_instance = NULL;
152
static pj_status_t ca_factory_init(pjmedia_aud_dev_factory *f);
153
static pj_status_t ca_factory_destroy(pjmedia_aud_dev_factory *f);
154
static pj_status_t ca_factory_refresh(pjmedia_aud_dev_factory *f);
155
static unsigned ca_factory_get_dev_count(pjmedia_aud_dev_factory *f);
156
static pj_status_t ca_factory_get_dev_info(pjmedia_aud_dev_factory *f,
158
pjmedia_aud_dev_info *info);
159
static pj_status_t ca_factory_default_param(pjmedia_aud_dev_factory *f,
161
pjmedia_aud_param *param);
162
static pj_status_t ca_factory_create_stream(pjmedia_aud_dev_factory *f,
163
const pjmedia_aud_param *param,
164
pjmedia_aud_rec_cb rec_cb,
165
pjmedia_aud_play_cb play_cb,
167
pjmedia_aud_stream **p_aud_strm);
169
static pj_status_t ca_stream_get_param(pjmedia_aud_stream *strm,
170
pjmedia_aud_param *param);
171
static pj_status_t ca_stream_get_cap(pjmedia_aud_stream *strm,
172
pjmedia_aud_dev_cap cap,
174
static pj_status_t ca_stream_set_cap(pjmedia_aud_stream *strm,
175
pjmedia_aud_dev_cap cap,
177
static pj_status_t ca_stream_start(pjmedia_aud_stream *strm);
178
static pj_status_t ca_stream_stop(pjmedia_aud_stream *strm);
179
static pj_status_t ca_stream_destroy(pjmedia_aud_stream *strm);
180
static pj_status_t create_audio_unit(AudioComponent io_comp,
181
AudioDeviceID dev_id,
183
struct coreaudio_stream *strm,
185
#if !COREAUDIO_MAC && USE_AUDIO_SESSION_API != 0
186
static void interruptionListener(void *inClientData, UInt32 inInterruption);
187
static void propListener(void * inClientData,
188
AudioSessionPropertyID inID,
190
const void * inData);
194
static pjmedia_aud_dev_factory_op factory_op =
198
&ca_factory_get_dev_count,
199
&ca_factory_get_dev_info,
200
&ca_factory_default_param,
201
&ca_factory_create_stream,
205
static pjmedia_aud_stream_op stream_op =
207
&ca_stream_get_param,
216
/****************************************************************************
220
* Init coreaudio audio driver.
222
pjmedia_aud_dev_factory* pjmedia_coreaudio_factory(pj_pool_factory *pf)
224
struct coreaudio_factory *f;
227
pool = pj_pool_create(pf, "core audio base", 1000, 1000, NULL);
228
f = PJ_POOL_ZALLOC_T(pool, struct coreaudio_factory);
231
f->base.op = &factory_op;
237
/* API: init factory */
238
static pj_status_t ca_factory_init(pjmedia_aud_dev_factory *f)
240
struct coreaudio_factory *cf = (struct coreaudio_factory*)f;
241
AudioComponentDescription desc;
247
pj_list_init(&cf->streams);
248
status = pj_mutex_create_recursive(cf->base_pool,
251
if (status != PJ_SUCCESS)
254
desc.componentType = kAudioUnitType_Output;
256
desc.componentSubType = kAudioUnitSubType_HALOutput;
258
desc.componentSubType = kAudioUnitSubType_RemoteIO;
260
desc.componentManufacturer = kAudioUnitManufacturer_Apple;
261
desc.componentFlags = 0;
262
desc.componentFlagsMask = 0;
264
cf->io_comp = AudioComponentFindNext(NULL, &desc);
265
if (cf->io_comp == NULL)
266
return PJMEDIA_EAUD_INIT; // cannot find IO unit;
268
status = ca_factory_refresh(f);
269
if (status != PJ_SUCCESS)
273
cf->pool = pj_pool_create(cf->pf, "core audio", 1000, 1000, NULL);
275
cf->dev_info = (struct coreaudio_dev_info*)
276
pj_pool_calloc(cf->pool, cf->dev_count,
277
sizeof(struct coreaudio_dev_info));
278
for (i = 0; i < cf->dev_count; i++) {
279
struct coreaudio_dev_info *cdi;
281
cdi = &cf->dev_info[i];
282
pj_bzero(cdi, sizeof(*cdi));
284
strcpy(cdi->info.name, "iPhone IO device");
285
strcpy(cdi->info.driver, "apple");
286
cdi->info.input_count = 1;
287
cdi->info.output_count = 1;
288
cdi->info.default_samples_per_sec = 8000;
290
/* Set the device capabilities here */
291
cdi->info.caps = PJMEDIA_AUD_DEV_CAP_INPUT_LATENCY |
292
PJMEDIA_AUD_DEV_CAP_OUTPUT_LATENCY |
293
PJMEDIA_AUD_DEV_CAP_OUTPUT_VOLUME_SETTING |
294
#if USE_AUDIO_SESSION_API != 0
295
PJMEDIA_AUD_DEV_CAP_INPUT_ROUTE |
296
PJMEDIA_AUD_DEV_CAP_OUTPUT_ROUTE |
298
PJMEDIA_AUD_DEV_CAP_EC;
299
cdi->info.routes = PJMEDIA_AUD_DEV_ROUTE_LOUDSPEAKER |
300
PJMEDIA_AUD_DEV_ROUTE_EARPIECE |
301
PJMEDIA_AUD_DEV_ROUTE_BLUETOOTH;
303
PJ_LOG(4, (THIS_FILE, " dev_id %d: %s (in=%d, out=%d) %dHz",
306
cdi->info.input_count,
307
cdi->info.output_count,
308
cdi->info.default_samples_per_sec));
311
#if USE_AUDIO_SESSION_API != 0
315
/* Initialize the Audio Session */
316
ostatus = AudioSessionInitialize(NULL, NULL, interruptionListener,
318
if (ostatus != kAudioSessionNoError) {
319
PJ_LOG(4, (THIS_FILE,
320
"Warning: cannot initialize audio session services (%i)",
324
/* Listen for audio routing change notifications. */
325
#if USE_AUDIO_ROUTE_CHANGE_PROP_LISTENER != 0
326
ostatus = AudioSessionAddPropertyListener(
327
kAudioSessionProperty_AudioRouteChange,
329
if (ostatus != kAudioSessionNoError) {
330
PJ_LOG(4, (THIS_FILE,
331
"Warning: cannot listen for audio route change "
332
"notifications (%i)", ostatus));
341
PJ_LOG(4, (THIS_FILE, "core audio initialized"));
346
/* API: destroy factory */
347
static pj_status_t ca_factory_destroy(pjmedia_aud_dev_factory *f)
349
struct coreaudio_factory *cf = (struct coreaudio_factory*)f;
353
pj_assert(cf->base_pool);
354
pj_assert(pj_list_empty(&cf->streams));
357
#if USE_AUDIO_SESSION_API != 0 && USE_AUDIO_ROUTE_CHANGE_PROP_LISTENER != 0
358
AudioSessionRemovePropertyListenerWithUserData(
359
kAudioSessionProperty_AudioRouteChange, propListener, cf);
364
pj_pool_release(cf->pool);
369
pj_mutex_lock(cf->mutex);
371
pj_mutex_unlock(cf->mutex);
372
pj_mutex_destroy(cf->mutex);
376
pool = cf->base_pool;
377
cf->base_pool = NULL;
378
pj_pool_release(pool);
383
/* API: refresh the device list */
384
static pj_status_t ca_factory_refresh(pjmedia_aud_dev_factory *f)
387
/* iPhone doesn't support refreshing the device list */
391
struct coreaudio_factory *cf = (struct coreaudio_factory*)f;
394
AudioObjectPropertyAddress addr;
395
AudioDeviceID *dev_ids;
396
UInt32 buf_size, dev_size, size = sizeof(AudioDeviceID);
397
AudioBufferList *buf = NULL;
400
if (cf->pool != NULL) {
401
pj_pool_release(cf->pool);
406
cf->pool = pj_pool_create(cf->pf, "core audio", 1000, 1000, NULL);
408
/* Find out how many audio devices there are */
409
addr.mSelector = kAudioHardwarePropertyDevices;
410
addr.mScope = kAudioObjectPropertyScopeGlobal;
411
addr.mElement = kAudioObjectPropertyElementMaster;
412
ostatus = AudioObjectGetPropertyDataSize(kAudioObjectSystemObject, &addr,
414
if (ostatus != noErr) {
418
/* Calculate the number of audio devices available */
419
dev_count = dev_size / size;
421
PJ_LOG(4,(THIS_FILE, "core audio found no sound devices"));
422
/* Enabling this will cause pjsua-lib initialization to fail when
423
* there is no sound device installed in the system, even when pjsua
424
* has been run with --null-audio. Moreover, it might be better to
425
* think that the core audio backend initialization is successful,
426
* regardless there is no audio device installed, as later application
427
* can check it using get_dev_count().
428
return PJMEDIA_EAUD_NODEV;
432
PJ_LOG(4, (THIS_FILE, "core audio detected %d devices",
435
/* Get all the audio device IDs */
436
dev_ids = (AudioDeviceID *)pj_pool_calloc(cf->pool, dev_size, size);
439
pj_bzero(dev_ids, dev_count);
440
ostatus = AudioObjectGetPropertyData(kAudioObjectSystemObject, &addr,
442
&dev_size, (void *)dev_ids);
443
if (ostatus != noErr ) {
444
/* This should not happen since we have successfully retrieved
445
* the property data size before
447
return PJMEDIA_EAUD_INIT;
451
AudioDeviceID dev_id = kAudioObjectUnknown;
454
/* Find default audio input device */
455
addr.mSelector = kAudioHardwarePropertyDefaultInputDevice;
456
addr.mScope = kAudioObjectPropertyScopeGlobal;
457
addr.mElement = kAudioObjectPropertyElementMaster;
458
size = sizeof(dev_id);
460
ostatus = AudioObjectGetPropertyData(kAudioObjectSystemObject,
462
&size, (void *)&dev_id);
463
if (ostatus == noErr && dev_id != dev_ids[idx]) {
464
AudioDeviceID temp_id = dev_ids[idx];
466
for (i = idx + 1; i < dev_size; i++) {
467
if (dev_ids[i] == dev_id) {
468
dev_ids[idx++] = dev_id;
469
dev_ids[i] = temp_id;
475
/* Find default audio output device */
476
addr.mSelector = kAudioHardwarePropertyDefaultOutputDevice;
477
ostatus = AudioObjectGetPropertyData(kAudioObjectSystemObject,
479
&size, (void *)&dev_id);
480
if (ostatus == noErr && dev_id != dev_ids[idx]) {
481
AudioDeviceID temp_id = dev_ids[idx];
483
for (i = idx + 1; i < dev_size; i++) {
484
if (dev_ids[i] == dev_id) {
485
dev_ids[idx] = dev_id;
486
dev_ids[i] = temp_id;
493
/* Build the devices' info */
494
cf->dev_info = (struct coreaudio_dev_info*)
495
pj_pool_calloc(cf->pool, dev_count,
496
sizeof(struct coreaudio_dev_info));
498
for (i = 0; i < dev_count; i++) {
499
struct coreaudio_dev_info *cdi;
502
cdi = &cf->dev_info[i];
503
pj_bzero(cdi, sizeof(*cdi));
504
cdi->dev_id = dev_ids[i];
506
/* Get device name */
507
addr.mSelector = kAudioDevicePropertyDeviceName;
508
addr.mScope = kAudioObjectPropertyScopeGlobal;
509
addr.mElement = kAudioObjectPropertyElementMaster;
510
size = sizeof(cdi->info.name);
511
AudioObjectGetPropertyData(cdi->dev_id, &addr,
513
&size, (void *)cdi->info.name);
515
strcpy(cdi->info.driver, "core audio");
517
/* Get the number of input channels */
518
addr.mSelector = kAudioDevicePropertyStreamConfiguration;
519
addr.mScope = kAudioDevicePropertyScopeInput;
521
ostatus = AudioObjectGetPropertyDataSize(cdi->dev_id, &addr,
523
if (ostatus == noErr && size > 0) {
525
if (size > buf_size) {
526
buf = pj_pool_alloc(cf->pool, size);
532
/* Get the input stream configuration */
533
ostatus = AudioObjectGetPropertyData(cdi->dev_id, &addr,
536
if (ostatus == noErr) {
537
/* Count the total number of input channels in
540
for (idx = 0; idx < buf->mNumberBuffers; idx++) {
541
cdi->info.input_count +=
542
buf->mBuffers[idx].mNumberChannels;
548
/* Get the number of output channels */
549
addr.mScope = kAudioDevicePropertyScopeOutput;
551
ostatus = AudioObjectGetPropertyDataSize(cdi->dev_id, &addr,
553
if (ostatus == noErr && size > 0) {
555
if (size > buf_size) {
556
buf = pj_pool_alloc(cf->pool, size);
562
/* Get the output stream configuration */
563
ostatus = AudioObjectGetPropertyData(cdi->dev_id, &addr,
566
if (ostatus == noErr) {
567
/* Count the total number of output channels in
570
for (idx = 0; idx < buf->mNumberBuffers; idx++) {
571
cdi->info.output_count +=
572
buf->mBuffers[idx].mNumberChannels;
578
/* Get default sample rate */
579
addr.mSelector = kAudioDevicePropertyNominalSampleRate;
580
addr.mScope = kAudioObjectPropertyScopeGlobal;
581
size = sizeof(Float64);
582
ostatus = AudioObjectGetPropertyData (cdi->dev_id, &addr,
585
cdi->info.default_samples_per_sec = (ostatus == noErr ?
589
/* Set device capabilities here */
590
if (cdi->info.input_count > 0) {
591
cdi->info.caps |= PJMEDIA_AUD_DEV_CAP_INPUT_LATENCY;
593
if (cdi->info.output_count > 0) {
594
cdi->info.caps |= PJMEDIA_AUD_DEV_CAP_OUTPUT_LATENCY;
595
addr.mSelector = kAudioDevicePropertyVolumeScalar;
596
addr.mScope = kAudioDevicePropertyScopeOutput;
597
if (AudioObjectHasProperty(cdi->dev_id, &addr)) {
598
cdi->info.caps |= PJMEDIA_AUD_DEV_CAP_OUTPUT_VOLUME_SETTING;
604
PJ_LOG(4, (THIS_FILE, " dev_id %d: %s (in=%d, out=%d) %dHz",
607
cdi->info.input_count,
608
cdi->info.output_count,
609
cdi->info.default_samples_per_sec));
616
/* API: get number of devices */
617
static unsigned ca_factory_get_dev_count(pjmedia_aud_dev_factory *f)
619
struct coreaudio_factory *cf = (struct coreaudio_factory*)f;
620
return cf->dev_count;
623
/* API: get device info */
624
static pj_status_t ca_factory_get_dev_info(pjmedia_aud_dev_factory *f,
626
pjmedia_aud_dev_info *info)
628
struct coreaudio_factory *cf = (struct coreaudio_factory*)f;
630
PJ_ASSERT_RETURN(index < cf->dev_count, PJMEDIA_EAUD_INVDEV);
632
pj_memcpy(info, &cf->dev_info[index].info, sizeof(*info));
637
/* API: create default device parameter */
638
static pj_status_t ca_factory_default_param(pjmedia_aud_dev_factory *f,
640
pjmedia_aud_param *param)
642
struct coreaudio_factory *cf = (struct coreaudio_factory*)f;
643
struct coreaudio_dev_info *di = &cf->dev_info[index];
645
PJ_ASSERT_RETURN(index < cf->dev_count, PJMEDIA_EAUD_INVDEV);
647
pj_bzero(param, sizeof(*param));
648
if (di->info.input_count && di->info.output_count) {
649
param->dir = PJMEDIA_DIR_CAPTURE_PLAYBACK;
650
param->rec_id = index;
651
param->play_id = index;
652
} else if (di->info.input_count) {
653
param->dir = PJMEDIA_DIR_CAPTURE;
654
param->rec_id = index;
655
param->play_id = PJMEDIA_AUD_INVALID_DEV;
656
} else if (di->info.output_count) {
657
param->dir = PJMEDIA_DIR_PLAYBACK;
658
param->play_id = index;
659
param->rec_id = PJMEDIA_AUD_INVALID_DEV;
661
return PJMEDIA_EAUD_INVDEV;
664
/* Set the mandatory settings here */
665
param->clock_rate = di->info.default_samples_per_sec;
666
param->channel_count = 1;
667
param->samples_per_frame = di->info.default_samples_per_sec * 20 / 1000;
668
param->bits_per_sample = 16;
670
/* Set the param for device capabilities here */
671
param->flags = PJMEDIA_AUD_DEV_CAP_INPUT_LATENCY |
672
PJMEDIA_AUD_DEV_CAP_OUTPUT_LATENCY;
673
param->input_latency_ms = PJMEDIA_SND_DEFAULT_REC_LATENCY;
674
param->output_latency_ms = PJMEDIA_SND_DEFAULT_PLAY_LATENCY;
679
OSStatus resampleProc(AudioConverterRef inAudioConverter,
680
UInt32 *ioNumberDataPackets,
681
AudioBufferList *ioData,
682
AudioStreamPacketDescription **outDataPacketDescription,
685
struct coreaudio_stream *strm = (struct coreaudio_stream*)inUserData;
687
if (*ioNumberDataPackets > strm->resample_buf_size)
688
*ioNumberDataPackets = strm->resample_buf_size;
690
ioData->mNumberBuffers = 1;
691
ioData->mBuffers[0].mNumberChannels = strm->streamFormat.mChannelsPerFrame;
692
ioData->mBuffers[0].mData = strm->resample_buf_ptr;
693
ioData->mBuffers[0].mDataByteSize = *ioNumberDataPackets *
694
strm->streamFormat.mChannelsPerFrame *
695
strm->param.bits_per_sample >> 3;
700
static OSStatus resample_callback(void *inRefCon,
701
AudioUnitRenderActionFlags *ioActionFlags,
702
const AudioTimeStamp *inTimeStamp,
704
UInt32 inNumberFrames,
705
AudioBufferList *ioData)
707
struct coreaudio_stream *strm = (struct coreaudio_stream*)inRefCon;
709
pj_status_t status = 0;
711
AudioBufferList *buf = strm->audio_buf;
715
pj_assert(!strm->quit_flag);
717
/* Known cases of callback's thread:
718
* - The thread may be changed in the middle of a session
719
* it happens when plugging/unplugging headphone.
720
* - The same thread may be reused in consecutive sessions. The first
721
* session will leave TLS set, but release the TLS data address,
722
* so the second session must re-register the callback's thread.
724
if (strm->rec_thread_initialized == 0 || !pj_thread_is_registered())
726
pj_bzero(strm->rec_thread_desc, sizeof(pj_thread_desc));
727
status = pj_thread_register("ca_rec", strm->rec_thread_desc,
729
strm->rec_thread_initialized = 1;
730
PJ_LOG(5,(THIS_FILE, "Recorder thread started, (%i frames)",
734
buf->mBuffers[0].mData = NULL;
735
buf->mBuffers[0].mDataByteSize = inNumberFrames *
736
strm->streamFormat.mChannelsPerFrame;
737
/* Render the unit to get input data */
738
ostatus = AudioUnitRender(strm->io_units[0],
745
if (ostatus != noErr) {
746
PJ_LOG(5, (THIS_FILE, "Core audio unit render error %i", ostatus));
749
input = (pj_int16_t *)buf->mBuffers[0].mData;
751
resampleSize = strm->resample_buf_size;
752
nsamples = inNumberFrames * strm->param.channel_count +
753
strm->resample_buf_count;
755
if (nsamples >= resampleSize) {
757
UInt32 resampleOutput = strm->param.samples_per_frame /
758
strm->streamFormat.mChannelsPerFrame;
761
frame.type = PJMEDIA_FRAME_TYPE_AUDIO;
762
frame.buf = (void*) strm->rec_buf;
763
frame.size = strm->param.samples_per_frame *
764
strm->param.bits_per_sample >> 3;
767
ab.mNumberBuffers = 1;
768
ab.mBuffers[0].mNumberChannels = strm->streamFormat.mChannelsPerFrame;
769
ab.mBuffers[0].mData = strm->rec_buf;
770
ab.mBuffers[0].mDataByteSize = frame.size;
772
/* If buffer is not empty, combine the buffer with the just incoming
773
* samples, then call put_frame.
775
if (strm->resample_buf_count) {
776
unsigned chunk_count = resampleSize - strm->resample_buf_count;
777
pjmedia_copy_samples(strm->resample_buf + strm->resample_buf_count,
780
/* Do the resample */
782
strm->resample_buf_ptr = strm->resample_buf;
783
ostatus = AudioConverterFillComplexBuffer(strm->resample,
789
if (ostatus != noErr) {
792
frame.timestamp.u64 = strm->rec_timestamp.u64;
794
status = (*strm->rec_cb)(strm->user_data, &frame);
796
input = input + chunk_count;
797
nsamples -= resampleSize;
798
strm->resample_buf_count = 0;
799
strm->rec_timestamp.u64 += strm->param.samples_per_frame /
800
strm->param.channel_count;
804
/* Give all frames we have */
805
while (nsamples >= resampleSize && status == 0) {
806
frame.timestamp.u64 = strm->rec_timestamp.u64;
808
/* Do the resample */
809
strm->resample_buf_ptr = input;
810
ab.mBuffers[0].mDataByteSize = frame.size;
811
resampleOutput = strm->param.samples_per_frame /
812
strm->streamFormat.mChannelsPerFrame;
813
ostatus = AudioConverterFillComplexBuffer(strm->resample,
819
if (ostatus != noErr) {
823
status = (*strm->rec_cb)(strm->user_data, &frame);
825
input = (pj_int16_t*) input + resampleSize;
826
nsamples -= resampleSize;
827
strm->rec_timestamp.u64 += strm->param.samples_per_frame /
828
strm->param.channel_count;
831
/* Store the remaining samples into the buffer */
832
if (nsamples && status == 0) {
833
strm->resample_buf_count = nsamples;
834
pjmedia_copy_samples(strm->resample_buf, input,
839
/* Not enough samples, let's just store them in the buffer */
840
pjmedia_copy_samples(strm->resample_buf + strm->resample_buf_count,
842
inNumberFrames * strm->param.channel_count);
843
strm->resample_buf_count += inNumberFrames *
844
strm->param.channel_count;
853
static OSStatus input_callback(void *inRefCon,
854
AudioUnitRenderActionFlags *ioActionFlags,
855
const AudioTimeStamp *inTimeStamp,
857
UInt32 inNumberFrames,
858
AudioBufferList *ioData)
860
struct coreaudio_stream *strm = (struct coreaudio_stream*)inRefCon;
862
pj_status_t status = 0;
864
AudioBufferList *buf = strm->audio_buf;
867
pj_assert(!strm->quit_flag);
869
/* Known cases of callback's thread:
870
* - The thread may be changed in the middle of a session
871
* it happens when plugging/unplugging headphone.
872
* - The same thread may be reused in consecutive sessions. The first
873
* session will leave TLS set, but release the TLS data address,
874
* so the second session must re-register the callback's thread.
876
if (strm->rec_thread_initialized == 0 || !pj_thread_is_registered())
878
pj_bzero(strm->rec_thread_desc, sizeof(pj_thread_desc));
879
status = pj_thread_register("ca_rec", strm->rec_thread_desc,
881
strm->rec_thread_initialized = 1;
882
PJ_LOG(5,(THIS_FILE, "Recorder thread started, (%i frames)",
886
buf->mBuffers[0].mData = NULL;
887
buf->mBuffers[0].mDataByteSize = inNumberFrames *
888
strm->streamFormat.mChannelsPerFrame;
889
/* Render the unit to get input data */
890
ostatus = AudioUnitRender(strm->io_units[0],
897
if (ostatus != noErr) {
898
PJ_LOG(5, (THIS_FILE, "Core audio unit render error %i", ostatus));
901
input = (pj_int16_t *)buf->mBuffers[0].mData;
903
/* Calculate number of samples we've got */
904
nsamples = inNumberFrames * strm->param.channel_count +
906
if (nsamples >= strm->param.samples_per_frame) {
909
frame.type = PJMEDIA_FRAME_TYPE_AUDIO;
910
frame.size = strm->param.samples_per_frame *
911
strm->param.bits_per_sample >> 3;
914
/* If buffer is not empty, combine the buffer with the just incoming
915
* samples, then call put_frame.
917
if (strm->rec_buf_count) {
918
unsigned chunk_count = 0;
920
chunk_count = strm->param.samples_per_frame - strm->rec_buf_count;
921
pjmedia_copy_samples(strm->rec_buf + strm->rec_buf_count,
924
frame.buf = (void*) strm->rec_buf;
925
frame.timestamp.u64 = strm->rec_timestamp.u64;
927
status = (*strm->rec_cb)(strm->user_data, &frame);
929
input = input + chunk_count;
930
nsamples -= strm->param.samples_per_frame;
931
strm->rec_buf_count = 0;
932
strm->rec_timestamp.u64 += strm->param.samples_per_frame /
933
strm->param.channel_count;
936
/* Give all frames we have */
937
while (nsamples >= strm->param.samples_per_frame && status == 0) {
938
frame.buf = (void*) input;
939
frame.timestamp.u64 = strm->rec_timestamp.u64;
941
status = (*strm->rec_cb)(strm->user_data, &frame);
943
input = (pj_int16_t*) input + strm->param.samples_per_frame;
944
nsamples -= strm->param.samples_per_frame;
945
strm->rec_timestamp.u64 += strm->param.samples_per_frame /
946
strm->param.channel_count;
949
/* Store the remaining samples into the buffer */
950
if (nsamples && status == 0) {
951
strm->rec_buf_count = nsamples;
952
pjmedia_copy_samples(strm->rec_buf, input,
957
/* Not enough samples, let's just store them in the buffer */
958
pjmedia_copy_samples(strm->rec_buf + strm->rec_buf_count,
960
inNumberFrames * strm->param.channel_count);
961
strm->rec_buf_count += inNumberFrames * strm->param.channel_count;
970
static OSStatus output_renderer(void *inRefCon,
971
AudioUnitRenderActionFlags *ioActionFlags,
972
const AudioTimeStamp *inTimeStamp,
974
UInt32 inNumberFrames,
975
AudioBufferList *ioData)
977
struct coreaudio_stream *stream = (struct coreaudio_stream*)inRefCon;
978
pj_status_t status = 0;
979
unsigned nsamples_req = inNumberFrames * stream->param.channel_count;
980
pj_int16_t *output = ioData->mBuffers[0].mData;
982
pj_assert(!stream->quit_flag);
984
/* Known cases of callback's thread:
985
* - The thread may be changed in the middle of a session
986
* it happens when plugging/unplugging headphone.
987
* - The same thread may be reused in consecutive sessions. The first
988
* session will leave TLS set, but release the TLS data address,
989
* so the second session must re-register the callback's thread.
991
if (stream->play_thread_initialized == 0 || !pj_thread_is_registered())
993
pj_bzero(stream->play_thread_desc, sizeof(pj_thread_desc));
994
status = pj_thread_register("coreaudio", stream->play_thread_desc,
995
&stream->play_thread);
996
stream->play_thread_initialized = 1;
997
PJ_LOG(5,(THIS_FILE, "Player thread started, (%i frames)",
1002
/* Check if any buffered samples */
1003
if (stream->play_buf_count) {
1004
/* samples buffered >= requested by sound device */
1005
if (stream->play_buf_count >= nsamples_req) {
1006
pjmedia_copy_samples((pj_int16_t*)output, stream->play_buf,
1008
stream->play_buf_count -= nsamples_req;
1009
pjmedia_move_samples(stream->play_buf,
1010
stream->play_buf + nsamples_req,
1011
stream->play_buf_count);
1017
/* samples buffered < requested by sound device */
1018
pjmedia_copy_samples((pj_int16_t*)output, stream->play_buf,
1019
stream->play_buf_count);
1020
nsamples_req -= stream->play_buf_count;
1021
output = (pj_int16_t*)output + stream->play_buf_count;
1022
stream->play_buf_count = 0;
1025
/* Fill output buffer as requested */
1026
while (nsamples_req && status == 0) {
1027
pjmedia_frame frame;
1029
frame.type = PJMEDIA_FRAME_TYPE_AUDIO;
1030
frame.size = stream->param.samples_per_frame *
1031
stream->param.bits_per_sample >> 3;
1032
frame.timestamp.u64 = stream->play_timestamp.u64;
1035
if (nsamples_req >= stream->param.samples_per_frame) {
1037
status = (*stream->play_cb)(stream->user_data, &frame);
1038
if (status != PJ_SUCCESS)
1041
if (frame.type != PJMEDIA_FRAME_TYPE_AUDIO)
1042
pj_bzero(frame.buf, frame.size);
1044
nsamples_req -= stream->param.samples_per_frame;
1045
output = (pj_int16_t*)output + stream->param.samples_per_frame;
1047
frame.buf = stream->play_buf;
1048
status = (*stream->play_cb)(stream->user_data, &frame);
1049
if (status != PJ_SUCCESS)
1052
if (frame.type != PJMEDIA_FRAME_TYPE_AUDIO)
1053
pj_bzero(frame.buf, frame.size);
1055
pjmedia_copy_samples((pj_int16_t*)output, stream->play_buf,
1057
stream->play_buf_count = stream->param.samples_per_frame -
1059
pjmedia_move_samples(stream->play_buf,
1060
stream->play_buf+nsamples_req,
1061
stream->play_buf_count);
1065
stream->play_timestamp.u64 += stream->param.samples_per_frame /
1066
stream->param.channel_count;
1075
#if !COREAUDIO_MAC && USE_AUDIO_SESSION_API != 0
1076
static void propListener(void *inClientData,
1077
AudioSessionPropertyID inID,
1079
const void * inData)
1081
struct coreaudio_factory *cf = (struct coreaudio_factory*)inClientData;
1082
struct stream_list *it, *itBegin;
1083
CFDictionaryRef routeDictionary;
1088
if (inID != kAudioSessionProperty_AudioRouteChange)
1091
routeDictionary = (CFDictionaryRef)inData;
1092
reason = (CFNumberRef)
1093
CFDictionaryGetValue(
1095
CFSTR(kAudioSession_AudioRouteChangeKey_Reason));
1096
CFNumberGetValue(reason, kCFNumberSInt32Type, &reasonVal);
1098
if (reasonVal != kAudioSessionRouteChangeReason_OldDeviceUnavailable) {
1099
PJ_LOG(3, (THIS_FILE, "ignoring audio route change..."));
1103
PJ_LOG(3, (THIS_FILE, "audio route changed"));
1105
pj_mutex_lock(cf->mutex);
1106
itBegin = &cf->streams;
1107
for (it = itBegin->next; it != itBegin; it = it->next) {
1108
if (it->stream->interrupted)
1112
status = ca_stream_stop((pjmedia_aud_stream *)it->stream);
1113
status = ca_stream_start((pjmedia_aud_stream *)it->stream);
1114
if (status != PJ_SUCCESS) {
1115
PJ_LOG(3, (THIS_FILE,
1116
"Error: failed to restart the audio unit (%i)",
1120
PJ_LOG(3, (THIS_FILE, "core audio unit successfully restarted"));
1123
pj_mutex_unlock(cf->mutex);
1126
static void interruptionListener(void *inClientData, UInt32 inInterruption)
1128
struct stream_list *it, *itBegin;
1130
static pj_thread_desc thread_desc;
1131
pj_thread_t *thread;
1133
/* Register the thread with PJLIB, this is must for any external threads
1134
* which need to use the PJLIB framework.
1136
if (!pj_thread_is_registered()) {
1137
pj_bzero(thread_desc, sizeof(pj_thread_desc));
1138
status = pj_thread_register("intListener", thread_desc, &thread);
1141
PJ_LOG(3, (THIS_FILE, "Session interrupted! --- %s ---",
1142
inInterruption == kAudioSessionBeginInterruption ?
1143
"Begin Interruption" : "End Interruption"));
1148
pj_mutex_lock(cf_instance->mutex);
1149
itBegin = &cf_instance->streams;
1150
for (it = itBegin->next; it != itBegin; it = it->next) {
1151
if (inInterruption == kAudioSessionEndInterruption &&
1152
it->stream->interrupted == PJ_TRUE)
1154
UInt32 audioCategory;
1157
/* Make sure that your application can receive remote control
1158
* events by adding the code:
1159
* [[UIApplication sharedApplication]
1160
* beginReceivingRemoteControlEvents];
1161
* Otherwise audio unit will fail to restart while your
1162
* application is in the background mode.
1164
/* Make sure we set the correct audio category before restarting */
1165
audioCategory = kAudioSessionCategory_PlayAndRecord;
1166
ostatus = AudioSessionSetProperty(kAudioSessionProperty_AudioCategory,
1167
sizeof(audioCategory),
1169
if (ostatus != kAudioSessionNoError) {
1170
PJ_LOG(4, (THIS_FILE,
1171
"Warning: cannot set the audio session category (%i)",
1175
/* Restart the stream */
1176
status = ca_stream_start((pjmedia_aud_stream*)it->stream);
1177
if (status != PJ_SUCCESS) {
1178
PJ_LOG(3, (THIS_FILE,
1179
"Error: failed to restart the audio unit (%i)",
1183
PJ_LOG(3, (THIS_FILE, "core audio unit successfully resumed"
1184
" after interruption"));
1185
} else if (inInterruption == kAudioSessionBeginInterruption &&
1186
it->stream->running == PJ_TRUE)
1188
status = ca_stream_stop((pjmedia_aud_stream*)it->stream);
1189
it->stream->interrupted = PJ_TRUE;
1192
pj_mutex_unlock(cf_instance->mutex);
1198
/* Internal: create audio converter for resampling the recorder device */
1199
static pj_status_t create_audio_resample(struct coreaudio_stream *strm,
1200
AudioStreamBasicDescription *desc)
1204
pj_assert(strm->streamFormat.mSampleRate != desc->mSampleRate);
1205
pj_assert(NULL == strm->resample);
1206
pj_assert(NULL == strm->resample_buf);
1208
/* Create the audio converter */
1209
ostatus = AudioConverterNew(desc, &strm->streamFormat, &strm->resample);
1210
if (ostatus != noErr) {
1211
return PJMEDIA_AUDIODEV_ERRNO_FROM_COREAUDIO(ostatus);
1215
* Allocate the buffer required to hold enough input data
1217
strm->resample_buf_size = (unsigned)(desc->mSampleRate *
1218
strm->param.samples_per_frame /
1219
strm->param.clock_rate);
1220
strm->resample_buf = (pj_int16_t*)
1221
pj_pool_alloc(strm->pool,
1222
strm->resample_buf_size *
1223
strm->param.bits_per_sample >> 3);
1224
if (!strm->resample_buf)
1226
strm->resample_buf_count = 0;
1232
/* Internal: create audio unit for recorder/playback device */
1233
static pj_status_t create_audio_unit(AudioComponent io_comp,
1234
AudioDeviceID dev_id,
1236
struct coreaudio_stream *strm,
1241
/* We want to be able to open playback and recording streams */
1242
strm->sess = [AVAudioSession sharedInstance];
1243
if ([strm->sess setCategory:AVAudioSessionCategoryPlayAndRecord
1246
PJ_LOG(4, (THIS_FILE,
1247
"Warning: cannot set the audio session category"));
1251
/* Create an audio unit to interface with the device */
1252
ostatus = AudioComponentInstanceNew(io_comp, io_unit);
1253
if (ostatus != noErr) {
1254
return PJMEDIA_AUDIODEV_ERRNO_FROM_COREAUDIO(ostatus);
1257
/* Set audio unit's properties for capture device */
1258
if (dir & PJMEDIA_DIR_CAPTURE) {
1262
ostatus = AudioUnitSetProperty(*io_unit,
1263
kAudioOutputUnitProperty_EnableIO,
1264
kAudioUnitScope_Input,
1268
if (ostatus != noErr) {
1269
PJ_LOG(4, (THIS_FILE,
1270
"Warning: cannot enable IO of capture device %d",
1274
/* Disable output */
1275
if (!(dir & PJMEDIA_DIR_PLAYBACK)) {
1277
ostatus = AudioUnitSetProperty(*io_unit,
1278
kAudioOutputUnitProperty_EnableIO,
1279
kAudioUnitScope_Output,
1283
if (ostatus != noErr) {
1284
PJ_LOG(4, (THIS_FILE,
1285
"Warning: cannot disable IO of capture device %d",
1291
/* Set audio unit's properties for playback device */
1292
if (dir & PJMEDIA_DIR_PLAYBACK) {
1296
ostatus = AudioUnitSetProperty(*io_unit,
1297
kAudioOutputUnitProperty_EnableIO,
1298
kAudioUnitScope_Output,
1302
if (ostatus != noErr) {
1303
PJ_LOG(4, (THIS_FILE,
1304
"Warning: cannot enable IO of playback device %d",
1311
PJ_LOG(5, (THIS_FILE, "Opening device %d", dev_id));
1312
ostatus = AudioUnitSetProperty(*io_unit,
1313
kAudioOutputUnitProperty_CurrentDevice,
1314
kAudioUnitScope_Global,
1318
if (ostatus != noErr) {
1319
return PJMEDIA_AUDIODEV_ERRNO_FROM_COREAUDIO(ostatus);
1323
if (dir & PJMEDIA_DIR_CAPTURE) {
1325
AudioStreamBasicDescription deviceFormat;
1329
* Keep the sample rate from the device, otherwise we will confuse
1332
size = sizeof(AudioStreamBasicDescription);
1333
ostatus = AudioUnitGetProperty(*io_unit,
1334
kAudioUnitProperty_StreamFormat,
1335
kAudioUnitScope_Input,
1339
if (ostatus != noErr) {
1340
return PJMEDIA_AUDIODEV_ERRNO_FROM_COREAUDIO(ostatus);
1342
strm->streamFormat.mSampleRate = deviceFormat.mSampleRate;
1345
/* When setting the stream format, we have to make sure the sample
1346
* rate is supported. Setting an unsupported sample rate will cause
1347
* AudioUnitRender() to fail later.
1349
ostatus = AudioUnitSetProperty(*io_unit,
1350
kAudioUnitProperty_StreamFormat,
1351
kAudioUnitScope_Output,
1353
&strm->streamFormat,
1354
sizeof(strm->streamFormat));
1355
if (ostatus != noErr) {
1356
return PJMEDIA_AUDIODEV_ERRNO_FROM_COREAUDIO(ostatus);
1360
strm->streamFormat.mSampleRate = strm->param.clock_rate;
1361
size = sizeof(AudioStreamBasicDescription);
1362
ostatus = AudioUnitGetProperty (*io_unit,
1363
kAudioUnitProperty_StreamFormat,
1364
kAudioUnitScope_Output,
1368
if (ostatus == noErr) {
1369
if (strm->streamFormat.mSampleRate != deviceFormat.mSampleRate) {
1370
pj_status_t rc = create_audio_resample(strm, &deviceFormat);
1371
if (PJ_SUCCESS != rc)
1375
return PJMEDIA_AUDIODEV_ERRNO_FROM_COREAUDIO(ostatus);
1380
if (dir & PJMEDIA_DIR_PLAYBACK) {
1381
AURenderCallbackStruct output_cb;
1383
/* Set the stream format */
1384
ostatus = AudioUnitSetProperty(*io_unit,
1385
kAudioUnitProperty_StreamFormat,
1386
kAudioUnitScope_Input,
1388
&strm->streamFormat,
1389
sizeof(strm->streamFormat));
1390
if (ostatus != noErr) {
1391
PJ_LOG(4, (THIS_FILE,
1392
"Warning: cannot set playback stream format of dev %d",
1396
/* Set render callback */
1397
output_cb.inputProc = output_renderer;
1398
output_cb.inputProcRefCon = strm;
1399
ostatus = AudioUnitSetProperty(*io_unit,
1400
kAudioUnitProperty_SetRenderCallback,
1401
kAudioUnitScope_Input,
1405
if (ostatus != noErr) {
1406
return PJMEDIA_AUDIODEV_ERRNO_FROM_COREAUDIO(ostatus);
1409
/* Allocate playback buffer */
1410
strm->play_buf = (pj_int16_t*)pj_pool_alloc(strm->pool,
1411
strm->param.samples_per_frame *
1412
strm->param.bits_per_sample >> 3);
1413
if (!strm->play_buf)
1415
strm->play_buf_count = 0;
1418
if (dir & PJMEDIA_DIR_CAPTURE) {
1419
AURenderCallbackStruct input_cb;
1422
UInt32 size, buf_size;
1425
/* Set input callback */
1426
input_cb.inputProc = strm->resample ? resample_callback :
1428
input_cb.inputProcRefCon = strm;
1429
ostatus = AudioUnitSetProperty(
1431
kAudioOutputUnitProperty_SetInputCallback,
1432
kAudioUnitScope_Global,
1436
if (ostatus != noErr) {
1437
return PJMEDIA_AUDIODEV_ERRNO_FROM_COREAUDIO(ostatus);
1441
/* Get device's buffer frame size */
1442
size = sizeof(UInt32);
1443
ostatus = AudioUnitGetProperty(*io_unit,
1444
kAudioDevicePropertyBufferFrameSize,
1445
kAudioUnitScope_Global,
1449
if (ostatus != noErr)
1451
return PJMEDIA_AUDIODEV_ERRNO_FROM_COREAUDIO(ostatus);
1454
/* Allocate audio buffer */
1455
strm->audio_buf = (AudioBufferList*)pj_pool_alloc(strm->pool,
1456
sizeof(AudioBufferList) + sizeof(AudioBuffer));
1457
if (!strm->audio_buf)
1460
strm->audio_buf->mNumberBuffers = 1;
1461
ab = &strm->audio_buf->mBuffers[0];
1462
ab->mNumberChannels = strm->streamFormat.mChannelsPerFrame;
1463
ab->mDataByteSize = buf_size * ab->mNumberChannels *
1464
strm->param.bits_per_sample >> 3;
1465
ab->mData = pj_pool_alloc(strm->pool,
1471
/* We will let AudioUnitRender() to allocate the buffer
1474
strm->audio_buf = (AudioBufferList*)pj_pool_alloc(strm->pool,
1475
sizeof(AudioBufferList) + sizeof(AudioBuffer));
1476
if (!strm->audio_buf)
1479
strm->audio_buf->mNumberBuffers = 1;
1480
strm->audio_buf->mBuffers[0].mNumberChannels =
1481
strm->streamFormat.mChannelsPerFrame;
1485
/* Allocate recording buffer */
1486
strm->rec_buf = (pj_int16_t*)pj_pool_alloc(strm->pool,
1487
strm->param.samples_per_frame *
1488
strm->param.bits_per_sample >> 3);
1491
strm->rec_buf_count = 0;
1494
/* Initialize the audio unit */
1495
ostatus = AudioUnitInitialize(*io_unit);
1496
if (ostatus != noErr) {
1497
return PJMEDIA_AUDIODEV_ERRNO_FROM_COREAUDIO(ostatus);
1503
/* API: create stream */
1504
static pj_status_t ca_factory_create_stream(pjmedia_aud_dev_factory *f,
1505
const pjmedia_aud_param *param,
1506
pjmedia_aud_rec_cb rec_cb,
1507
pjmedia_aud_play_cb play_cb,
1509
pjmedia_aud_stream **p_aud_strm)
1511
struct coreaudio_factory *cf = (struct coreaudio_factory*)f;
1513
struct coreaudio_stream *strm;
1516
/* Create and Initialize stream descriptor */
1517
pool = pj_pool_create(cf->pf, "coreaudio-dev", 1000, 1000, NULL);
1518
PJ_ASSERT_RETURN(pool != NULL, PJ_ENOMEM);
1520
strm = PJ_POOL_ZALLOC_T(pool, struct coreaudio_stream);
1521
pj_list_init(&strm->list_entry);
1522
strm->list_entry.stream = strm;
1524
pj_memcpy(&strm->param, param, sizeof(*param));
1526
strm->rec_cb = rec_cb;
1527
strm->play_cb = play_cb;
1528
strm->user_data = user_data;
1530
/* Set the stream format */
1531
strm->streamFormat.mSampleRate = param->clock_rate;
1532
strm->streamFormat.mFormatID = kAudioFormatLinearPCM;
1533
strm->streamFormat.mFormatFlags = kLinearPCMFormatFlagIsSignedInteger
1534
| kLinearPCMFormatFlagIsPacked;
1535
strm->streamFormat.mBitsPerChannel = strm->param.bits_per_sample;
1536
strm->streamFormat.mChannelsPerFrame = param->channel_count;
1537
strm->streamFormat.mBytesPerFrame = strm->streamFormat.mChannelsPerFrame
1538
* strm->param.bits_per_sample >> 3;
1539
strm->streamFormat.mFramesPerPacket = 1;
1540
strm->streamFormat.mBytesPerPacket = strm->streamFormat.mBytesPerFrame *
1541
strm->streamFormat.mFramesPerPacket;
1543
/* Apply input/output routes settings before we create the audio units */
1544
if (param->flags & PJMEDIA_AUD_DEV_CAP_INPUT_ROUTE) {
1545
ca_stream_set_cap(&strm->base,
1546
PJMEDIA_AUD_DEV_CAP_INPUT_ROUTE,
1547
¶m->input_route);
1549
if (param->flags & PJMEDIA_AUD_DEV_CAP_OUTPUT_ROUTE) {
1550
ca_stream_set_cap(&strm->base,
1551
PJMEDIA_AUD_DEV_CAP_OUTPUT_ROUTE,
1552
¶m->output_route);
1554
if (param->flags & PJMEDIA_AUD_DEV_CAP_EC) {
1555
ca_stream_set_cap(&strm->base,
1556
PJMEDIA_AUD_DEV_CAP_EC,
1557
¶m->ec_enabled);
1559
pj_bool_t ec = PJ_FALSE;
1560
ca_stream_set_cap(&strm->base,
1561
PJMEDIA_AUD_DEV_CAP_EC, &ec);
1564
strm->io_units[0] = strm->io_units[1] = NULL;
1565
if (param->dir == PJMEDIA_DIR_CAPTURE_PLAYBACK &&
1566
param->rec_id == param->play_id)
1568
/* If both input and output are on the same device, only create
1569
* one audio unit to interface with the device.
1571
status = create_audio_unit(cf->io_comp,
1572
cf->dev_info[param->rec_id].dev_id,
1573
param->dir, strm, &strm->io_units[0]);
1574
if (status != PJ_SUCCESS)
1577
unsigned nunits = 0;
1579
if (param->dir & PJMEDIA_DIR_CAPTURE) {
1580
status = create_audio_unit(cf->io_comp,
1581
cf->dev_info[param->rec_id].dev_id,
1582
PJMEDIA_DIR_CAPTURE,
1583
strm, &strm->io_units[nunits++]);
1584
if (status != PJ_SUCCESS)
1587
if (param->dir & PJMEDIA_DIR_PLAYBACK) {
1589
status = create_audio_unit(cf->io_comp,
1590
cf->dev_info[param->play_id].dev_id,
1591
PJMEDIA_DIR_PLAYBACK,
1592
strm, &strm->io_units[nunits++]);
1593
if (status != PJ_SUCCESS)
1598
/* Apply the remaining settings */
1599
if (param->flags & PJMEDIA_AUD_DEV_CAP_INPUT_LATENCY) {
1600
ca_stream_get_cap(&strm->base,
1601
PJMEDIA_AUD_DEV_CAP_INPUT_LATENCY,
1602
&strm->param.input_latency_ms);
1604
if (param->flags & PJMEDIA_AUD_DEV_CAP_OUTPUT_LATENCY) {
1605
ca_stream_get_cap(&strm->base,
1606
PJMEDIA_AUD_DEV_CAP_OUTPUT_LATENCY,
1607
&strm->param.output_latency_ms);
1609
if (param->flags & PJMEDIA_AUD_DEV_CAP_OUTPUT_VOLUME_SETTING) {
1610
ca_stream_set_cap(&strm->base,
1611
PJMEDIA_AUD_DEV_CAP_OUTPUT_VOLUME_SETTING,
1612
¶m->output_vol);
1615
pj_mutex_lock(strm->cf->mutex);
1616
pj_assert(pj_list_empty(&strm->list_entry));
1617
pj_list_insert_after(&strm->cf->streams, &strm->list_entry);
1618
pj_mutex_unlock(strm->cf->mutex);
1621
strm->base.op = &stream_op;
1622
*p_aud_strm = &strm->base;
1627
ca_stream_destroy((pjmedia_aud_stream *)strm);
1631
/* API: Get stream info. */
1632
static pj_status_t ca_stream_get_param(pjmedia_aud_stream *s,
1633
pjmedia_aud_param *pi)
1635
struct coreaudio_stream *strm = (struct coreaudio_stream*)s;
1637
PJ_ASSERT_RETURN(strm && pi, PJ_EINVAL);
1639
pj_memcpy(pi, &strm->param, sizeof(*pi));
1641
/* Update the device capabilities' values */
1642
if (ca_stream_get_cap(s, PJMEDIA_AUD_DEV_CAP_INPUT_LATENCY,
1643
&pi->input_latency_ms) == PJ_SUCCESS)
1645
pi->flags |= PJMEDIA_AUD_DEV_CAP_INPUT_LATENCY;
1647
if (ca_stream_get_cap(s, PJMEDIA_AUD_DEV_CAP_OUTPUT_LATENCY,
1648
&pi->output_latency_ms) == PJ_SUCCESS)
1650
pi->flags |= PJMEDIA_AUD_DEV_CAP_OUTPUT_LATENCY;
1652
if (ca_stream_get_cap(s, PJMEDIA_AUD_DEV_CAP_OUTPUT_VOLUME_SETTING,
1653
&pi->output_vol) == PJ_SUCCESS)
1655
pi->flags |= PJMEDIA_AUD_DEV_CAP_OUTPUT_VOLUME_SETTING;
1657
if (ca_stream_get_cap(s, PJMEDIA_AUD_DEV_CAP_INPUT_ROUTE,
1658
&pi->input_route) == PJ_SUCCESS)
1660
pi->flags |= PJMEDIA_AUD_DEV_CAP_INPUT_ROUTE;
1662
if (ca_stream_get_cap(s, PJMEDIA_AUD_DEV_CAP_OUTPUT_ROUTE,
1663
&pi->output_route) == PJ_SUCCESS)
1665
pi->flags |= PJMEDIA_AUD_DEV_CAP_OUTPUT_ROUTE;
1667
if (ca_stream_get_cap(s, PJMEDIA_AUD_DEV_CAP_EC,
1668
&pi->ec_enabled) == PJ_SUCCESS)
1670
pi->flags |= PJMEDIA_AUD_DEV_CAP_EC;
1676
/* API: get capability */
1677
static pj_status_t ca_stream_get_cap(pjmedia_aud_stream *s,
1678
pjmedia_aud_dev_cap cap,
1681
struct coreaudio_stream *strm = (struct coreaudio_stream*)s;
1683
PJ_UNUSED_ARG(strm);
1685
PJ_ASSERT_RETURN(s && pval, PJ_EINVAL);
1687
if (cap==PJMEDIA_AUD_DEV_CAP_INPUT_LATENCY &&
1688
(strm->param.dir & PJMEDIA_DIR_CAPTURE))
1691
UInt32 latency, size = sizeof(UInt32);
1693
/* Recording latency */
1694
if (AudioUnitGetProperty (strm->io_units[0],
1695
kAudioDevicePropertyLatency,
1696
kAudioUnitScope_Input,
1702
if (AudioUnitGetProperty (strm->io_units[0],
1703
kAudioDevicePropertyBufferFrameSize,
1704
kAudioUnitScope_Input,
1709
strm->param.input_latency_ms = (latency + latency2) * 1000 /
1710
strm->param.clock_rate;
1711
strm->param.input_latency_ms++;
1715
if ([strm->sess respondsToSelector:@selector(inputLatency)]) {
1716
strm->param.input_latency_ms =
1717
(unsigned)(([strm->sess inputLatency] +
1718
[strm->sess IOBufferDuration]) * 1000);
1719
strm->param.input_latency_ms++;
1721
return PJMEDIA_EAUD_INVCAP;
1724
*(unsigned*)pval = strm->param.input_latency_ms;
1726
} else if (cap==PJMEDIA_AUD_DEV_CAP_OUTPUT_LATENCY &&
1727
(strm->param.dir & PJMEDIA_DIR_PLAYBACK))
1730
UInt32 latency, size = sizeof(UInt32);
1731
AudioUnit *io_unit = strm->io_units[1] ? &strm->io_units[1] :
1734
/* Playback latency */
1735
if (AudioUnitGetProperty (*io_unit,
1736
kAudioDevicePropertyLatency,
1737
kAudioUnitScope_Output,
1743
if (AudioUnitGetProperty (*io_unit,
1744
kAudioDevicePropertyBufferFrameSize,
1745
kAudioUnitScope_Output,
1750
strm->param.output_latency_ms = (latency + latency2) * 1000 /
1751
strm->param.clock_rate;
1752
strm->param.output_latency_ms++;
1756
if ([strm->sess respondsToSelector:@selector(outputLatency)]) {
1757
strm->param.output_latency_ms =
1758
(unsigned)(([strm->sess outputLatency] +
1759
[strm->sess IOBufferDuration]) * 1000);
1760
strm->param.output_latency_ms++;
1762
return PJMEDIA_EAUD_INVCAP;
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))
1772
UInt32 size = sizeof(Float32);
1774
/* 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
*(unsigned*)pval = (unsigned)(volume * 100);
1788
if ([strm->sess respondsToSelector:@selector(outputVolume)]) {
1789
*(unsigned*)pval = (unsigned)([strm->sess outputVolume] * 100);
1792
return PJMEDIA_EAUD_INVCAP;
1796
#if USE_AUDIO_SESSION_API != 0
1797
} else if (cap==PJMEDIA_AUD_DEV_CAP_INPUT_ROUTE &&
1798
(strm->param.dir & PJMEDIA_DIR_CAPTURE))
1800
UInt32 btooth, size = sizeof(UInt32);
1803
ostatus = AudioSessionGetProperty (
1804
kAudioSessionProperty_OverrideCategoryEnableBluetoothInput,
1806
if (ostatus != kAudioSessionNoError) {
1807
return PJMEDIA_AUDIODEV_ERRNO_FROM_COREAUDIO(ostatus);
1810
*(pjmedia_aud_dev_route*)pval = btooth?
1811
PJMEDIA_AUD_DEV_ROUTE_BLUETOOTH:
1812
PJMEDIA_AUD_DEV_ROUTE_DEFAULT;
1814
} else if (cap==PJMEDIA_AUD_DEV_CAP_OUTPUT_ROUTE &&
1815
(strm->param.dir & PJMEDIA_DIR_PLAYBACK))
1818
UInt32 size = sizeof(CFStringRef);
1821
ostatus = AudioSessionGetProperty (kAudioSessionProperty_AudioRoute,
1823
if (ostatus != kAudioSessionNoError) {
1824
return PJMEDIA_AUDIODEV_ERRNO_FROM_COREAUDIO(ostatus);
1828
*(pjmedia_aud_dev_route*)pval = PJMEDIA_AUD_DEV_ROUTE_DEFAULT;
1829
} else if (CFStringHasPrefix(route, CFSTR("Headset"))) {
1830
*(pjmedia_aud_dev_route*)pval = PJMEDIA_AUD_DEV_ROUTE_EARPIECE;
1832
*(pjmedia_aud_dev_route*)pval = PJMEDIA_AUD_DEV_ROUTE_DEFAULT;
1839
} else if (cap==PJMEDIA_AUD_DEV_CAP_EC) {
1840
AudioComponentDescription desc;
1843
ostatus = AudioComponentGetDescription(strm->cf->io_comp, &desc);
1844
if (ostatus != noErr) {
1845
return PJMEDIA_AUDIODEV_ERRNO_FROM_COREAUDIO(ostatus);
1848
*(pj_bool_t*)pval = (desc.componentSubType ==
1849
kAudioUnitSubType_VoiceProcessingIO);
1853
return PJMEDIA_EAUD_INVCAP;
1857
/* API: set capability */
1858
static pj_status_t ca_stream_set_cap(pjmedia_aud_stream *s,
1859
pjmedia_aud_dev_cap cap,
1862
struct coreaudio_stream *strm = (struct coreaudio_stream*)s;
1864
PJ_ASSERT_RETURN(s && pval, PJ_EINVAL);
1867
if (cap==PJMEDIA_AUD_DEV_CAP_OUTPUT_VOLUME_SETTING &&
1868
(strm->param.dir & PJMEDIA_DIR_PLAYBACK))
1871
Float32 volume = *(unsigned*)pval;
1873
/* Output volume setting */
1875
ostatus = AudioUnitSetProperty (strm->io_units[1] ? strm->io_units[1] :
1877
kAudioDevicePropertyVolumeScalar,
1878
kAudioUnitScope_Output,
1882
if (ostatus != noErr) {
1883
return PJMEDIA_AUDIODEV_ERRNO_FROM_COREAUDIO(ostatus);
1885
strm->param.output_vol = *(unsigned*)pval;
1890
if (cap==PJMEDIA_AUD_DEV_CAP_EC) {
1891
AudioComponentDescription desc;
1892
AudioComponent io_comp;
1894
desc.componentType = kAudioUnitType_Output;
1895
desc.componentSubType = (*(pj_bool_t*)pval)?
1896
kAudioUnitSubType_VoiceProcessingIO :
1897
kAudioUnitSubType_RemoteIO;
1898
desc.componentManufacturer = kAudioUnitManufacturer_Apple;
1899
desc.componentFlags = 0;
1900
desc.componentFlagsMask = 0;
1902
io_comp = AudioComponentFindNext(NULL, &desc);
1903
if (io_comp == NULL)
1904
return PJMEDIA_AUDIODEV_ERRNO_FROM_COREAUDIO(-1);
1905
strm->cf->io_comp = io_comp;
1906
strm->param.ec_enabled = *(pj_bool_t*)pval;
1908
PJ_LOG(4, (THIS_FILE, "Using %s audio unit",
1909
(desc.componentSubType ==
1910
kAudioUnitSubType_RemoteIO? "RemoteIO":
1911
"VoiceProcessingIO")));
1914
} else if ((cap==PJMEDIA_AUD_DEV_CAP_INPUT_LATENCY &&
1915
(strm->param.dir & PJMEDIA_DIR_CAPTURE)) ||
1916
(cap==PJMEDIA_AUD_DEV_CAP_OUTPUT_LATENCY &&
1917
(strm->param.dir & PJMEDIA_DIR_PLAYBACK)))
1919
NSTimeInterval duration = *(unsigned *)pval;
1922
/* For low-latency audio streaming, you can set this value to
1923
* as low as 5 ms (the default is 23ms). However, lowering the
1924
* latency may cause a decrease in audio quality.
1927
if ([strm->sess setPreferredIOBufferDuration:duration error:nil]
1930
PJ_LOG(4, (THIS_FILE,
1931
"Error: cannot set the preferred buffer duration"));
1932
return PJMEDIA_EAUD_INVOP;
1935
ca_stream_get_cap(s, PJMEDIA_AUD_DEV_CAP_INPUT_LATENCY, &latency);
1936
ca_stream_get_cap(s, PJMEDIA_AUD_DEV_CAP_OUTPUT_LATENCY, &latency);
1941
#if USE_AUDIO_SESSION_API != 0
1943
else if (cap==PJMEDIA_AUD_DEV_CAP_INPUT_ROUTE &&
1944
(strm->param.dir & PJMEDIA_DIR_CAPTURE))
1946
UInt32 btooth = *(pjmedia_aud_dev_route*)pval ==
1947
PJMEDIA_AUD_DEV_ROUTE_BLUETOOTH ? 1 : 0;
1950
ostatus = AudioSessionSetProperty (
1951
kAudioSessionProperty_OverrideCategoryEnableBluetoothInput,
1952
sizeof(btooth), &btooth);
1953
if (ostatus != kAudioSessionNoError) {
1954
return PJMEDIA_AUDIODEV_ERRNO_FROM_COREAUDIO(ostatus);
1956
strm->param.input_route = *(pjmedia_aud_dev_route*)pval;
1958
} else if (cap==PJMEDIA_AUD_DEV_CAP_OUTPUT_ROUTE &&
1959
(strm->param.dir & PJMEDIA_DIR_PLAYBACK))
1962
UInt32 route = *(pjmedia_aud_dev_route*)pval ==
1963
PJMEDIA_AUD_DEV_ROUTE_LOUDSPEAKER ?
1964
kAudioSessionOverrideAudioRoute_Speaker :
1965
kAudioSessionOverrideAudioRoute_None;
1967
ostatus = AudioSessionSetProperty (
1968
kAudioSessionProperty_OverrideAudioRoute,
1969
sizeof(route), &route);
1970
if (ostatus != kAudioSessionNoError) {
1971
return PJMEDIA_AUDIODEV_ERRNO_FROM_COREAUDIO(ostatus);
1973
strm->param.output_route = *(pjmedia_aud_dev_route*)pval;
1979
return PJMEDIA_EAUD_INVCAP;
1982
/* API: Start stream. */
1983
static pj_status_t ca_stream_start(pjmedia_aud_stream *strm)
1985
struct coreaudio_stream *stream = (struct coreaudio_stream*)strm;
1989
if (stream->running)
1992
stream->quit_flag = 0;
1993
stream->interrupted = PJ_FALSE;
1994
stream->rec_buf_count = 0;
1995
stream->play_buf_count = 0;
1996
stream->resample_buf_count = 0;
1998
if (stream->resample) {
1999
ostatus = AudioConverterReset(stream->resample);
2000
if (ostatus != noErr)
2001
return PJMEDIA_AUDIODEV_ERRNO_FROM_COREAUDIO(ostatus);
2005
if ([stream->sess setActive:true error:nil] != YES) {
2006
PJ_LOG(4, (THIS_FILE, "Warning: cannot activate audio session"));
2010
for (i = 0; i < 2; i++) {
2011
if (stream->io_units[i] == NULL) break;
2012
ostatus = AudioOutputUnitStart(stream->io_units[i]);
2013
if (ostatus != noErr) {
2015
AudioOutputUnitStop(stream->io_units[0]);
2016
return PJMEDIA_AUDIODEV_ERRNO_FROM_COREAUDIO(ostatus);
2020
stream->running = PJ_TRUE;
2022
PJ_LOG(4, (THIS_FILE, "core audio stream started"));
2027
/* API: Stop stream. */
2028
static pj_status_t ca_stream_stop(pjmedia_aud_stream *strm)
2030
struct coreaudio_stream *stream = (struct coreaudio_stream*)strm;
2033
int should_deactivate;
2034
struct stream_list *it, *itBegin;
2036
if (!stream->running)
2039
for (i = 0; i < 2; i++) {
2040
if (stream->io_units[i] == NULL) break;
2041
ostatus = AudioOutputUnitStop(stream->io_units[i]);
2042
if (ostatus != noErr) {
2043
if (i == 0 && stream->io_units[1])
2044
AudioOutputUnitStop(stream->io_units[1]);
2045
return PJMEDIA_AUDIODEV_ERRNO_FROM_COREAUDIO(ostatus);
2049
/* Check whether we need to deactivate the audio session. */
2050
pj_mutex_lock(stream->cf->mutex);
2051
pj_assert(!pj_list_empty(&stream->cf->streams));
2052
pj_assert(!pj_list_empty(&stream->list_entry));
2053
stream->running = PJ_FALSE;
2054
should_deactivate = PJ_TRUE;
2055
itBegin = &stream->cf->streams;
2056
for (it = itBegin->next; it != itBegin; it = it->next) {
2057
if (it->stream->running) {
2058
should_deactivate = PJ_FALSE;
2062
pj_mutex_unlock(stream->cf->mutex);
2065
if (should_deactivate) {
2066
if ([stream->sess setActive:false error:nil] != YES) {
2067
PJ_LOG(4, (THIS_FILE, "Warning: cannot deactivate audio session"));
2072
stream->quit_flag = 1;
2073
stream->play_thread_initialized = 0;
2074
stream->rec_thread_initialized = 0;
2075
pj_bzero(stream->rec_thread_desc, sizeof(pj_thread_desc));
2076
pj_bzero(stream->play_thread_desc, sizeof(pj_thread_desc));
2078
PJ_LOG(4, (THIS_FILE, "core audio stream stopped"));
2084
/* API: Destroy stream. */
2085
static pj_status_t ca_stream_destroy(pjmedia_aud_stream *strm)
2087
struct coreaudio_stream *stream = (struct coreaudio_stream*)strm;
2090
PJ_ASSERT_RETURN(stream != NULL, PJ_EINVAL);
2092
ca_stream_stop(strm);
2094
for (i = 0; i < 2; i++) {
2095
if (stream->io_units[i]) {
2096
AudioUnitUninitialize(stream->io_units[i]);
2097
AudioComponentInstanceDispose(stream->io_units[i]);
2098
stream->io_units[i] = NULL;
2102
if (stream->resample)
2103
AudioConverterDispose(stream->resample);
2105
pj_mutex_lock(stream->cf->mutex);
2106
if (!pj_list_empty(&stream->list_entry))
2107
pj_list_erase(&stream->list_entry);
2108
pj_mutex_unlock(stream->cf->mutex);
2110
pj_pool_release(stream->pool);
2115
#endif /* PJMEDIA_AUDIO_DEV_HAS_COREAUDIO */