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