- Timestamp:
- Dec 17, 2010 7:10:13 AM (14 years ago)
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
pjproject/trunk/pjmedia/src/pjmedia-audiodev/coreaudio_dev.c
r3386 r3398 32 32 33 33 #include <AudioUnit/AudioUnit.h> 34 #include <AudioToolbox/AudioConverter.h> 34 35 #if !COREAUDIO_MAC 35 36 #include <AudioToolbox/AudioServices.h> … … 65 66 }; 66 67 68 /* linked list of streams */ 69 struct stream_list 70 { 71 PJ_DECL_LIST_MEMBER(struct stream_list); 72 struct coreaudio_stream *stream; 73 }; 74 67 75 /* coreaudio factory */ 68 76 struct coreaudio_factory 69 77 { 70 78 pjmedia_aud_dev_factory base; 79 pj_pool_t *base_pool; 71 80 pj_pool_t *pool; 72 81 pj_pool_factory *pf; 82 pj_mutex_t *mutex; 73 83 74 84 unsigned dev_count; … … 76 86 77 87 AudioComponent io_comp; 78 struct coreaudio_stream *stream;88 struct stream_list streams; 79 89 }; 80 90 … … 82 92 struct coreaudio_stream 83 93 { 84 pjmedia_aud_stream base; /**< Base stream */85 pjmedia_aud_param param; /**< Settings */86 pj_pool_t 94 pjmedia_aud_stream base; /**< Base stream */ 95 pjmedia_aud_param param; /**< Settings */ 96 pj_pool_t *pool; /**< Memory pool. */ 87 97 struct coreaudio_factory *cf; 88 89 pjmedia_aud_rec_cb rec_cb; /**< Capture callback. */ 90 pjmedia_aud_play_cb play_cb; /**< Playback callback. */ 98 struct stream_list list_entry; 99 100 pjmedia_aud_rec_cb rec_cb; /**< Capture callback. */ 101 pjmedia_aud_play_cb play_cb; /**< Playback callback. */ 91 102 void *user_data; /**< Application data. */ 92 103 93 pj_timestamp play_timestamp;94 pj_timestamp rec_timestamp;104 pj_timestamp play_timestamp; 105 pj_timestamp rec_timestamp; 95 106 96 107 pj_int16_t *rec_buf; 97 unsigned rec_buf_count;108 unsigned rec_buf_count; 98 109 pj_int16_t *play_buf; 99 unsigned play_buf_count;100 101 pj_bool_t interrupted;102 pj_bool_t quit_flag;103 104 pj_bool_t rec_thread_exited; 105 pj_bool_t rec_thread_initialized;106 pj_thread_desc rec_thread_desc;110 unsigned play_buf_count; 111 112 pj_bool_t interrupted; 113 pj_bool_t quit_flag; 114 pj_bool_t running; 115 116 pj_bool_t rec_thread_initialized; 117 pj_thread_desc rec_thread_desc; 107 118 pj_thread_t *rec_thread; 108 119 109 pj_bool_t play_thread_exited; 110 pj_bool_t play_thread_initialized; 111 pj_thread_desc play_thread_desc; 120 pj_bool_t play_thread_initialized; 121 pj_thread_desc play_thread_desc; 112 122 pj_thread_t *play_thread; 113 123 114 AudioUnit io_units[2];115 AudioStreamBasicDescription streamFormat;124 AudioUnit io_units[2]; 125 AudioStreamBasicDescription streamFormat; 116 126 AudioBufferList *audio_buf; 127 128 AudioConverterRef resample; 129 pj_int16_t *resample_buf; 130 unsigned resample_buf_count; 131 unsigned resample_buf_size; 117 132 }; 118 133 … … 121 136 static pj_status_t ca_factory_init(pjmedia_aud_dev_factory *f); 122 137 static pj_status_t ca_factory_destroy(pjmedia_aud_dev_factory *f); 138 static pj_status_t ca_factory_refresh(pjmedia_aud_dev_factory *f); 123 139 static unsigned ca_factory_get_dev_count(pjmedia_aud_dev_factory *f); 124 140 static pj_status_t ca_factory_get_dev_info(pjmedia_aud_dev_factory *f, … … 153 169 #if !COREAUDIO_MAC 154 170 static void interruptionListener(void *inClientData, UInt32 inInterruption); 171 static void propListener(void * inClientData, 172 AudioSessionPropertyID inID, 173 UInt32 inDataSize, 174 const void * inData); 155 175 #endif 156 176 … … 188 208 pj_pool_t *pool; 189 209 190 pool = pj_pool_create(pf, "core audio ", 1000, 1000, NULL);210 pool = pj_pool_create(pf, "core audio base", 1000, 1000, NULL); 191 211 f = PJ_POOL_ZALLOC_T(pool, struct coreaudio_factory); 192 212 f->pf = pf; 193 f-> pool = pool;213 f->base_pool = pool; 194 214 f->base.op = &factory_op; 195 215 … … 202 222 { 203 223 struct coreaudio_factory *cf = (struct coreaudio_factory*)f; 224 AudioComponentDescription desc; 225 pj_status_t status; 226 #if !COREAUDIO_MAC 204 227 unsigned i; 205 AudioComponentDescription desc; 228 OSStatus ostatus; 229 UInt32 audioCategory; 230 #endif 231 232 pj_list_init(&cf->streams); 233 status = pj_mutex_create_recursive(cf->base_pool, 234 "coreaudio", 235 &cf->mutex); 236 if (status != PJ_SUCCESS) 237 return status; 238 239 desc.componentType = kAudioUnitType_Output; 206 240 #if COREAUDIO_MAC 241 desc.componentSubType = kAudioUnitSubType_HALOutput; 242 #else 243 desc.componentSubType = kAudioUnitSubType_VoiceProcessingIO; 244 #endif 245 desc.componentManufacturer = kAudioUnitManufacturer_Apple; 246 desc.componentFlags = 0; 247 desc.componentFlagsMask = 0; 248 249 cf->io_comp = AudioComponentFindNext(NULL, &desc); 250 if (cf->io_comp == NULL) 251 return PJMEDIA_EAUD_INIT; // cannot find IO unit; 252 253 status = ca_factory_refresh(f); 254 if (status != PJ_SUCCESS) 255 return status; 256 257 #if !COREAUDIO_MAC 258 cf->pool = pj_pool_create(cf->pf, "core audio", 1000, 1000, NULL); 259 cf->dev_count = 1; 260 cf->dev_info = (struct coreaudio_dev_info*) 261 pj_pool_calloc(cf->pool, cf->dev_count, 262 sizeof(struct coreaudio_dev_info)); 263 for (i = 0; i < cf->dev_count; i++) { 264 struct coreaudio_dev_info *cdi; 265 266 cdi = &cf->dev_info[i]; 267 pj_bzero(cdi, sizeof(*cdi)); 268 cdi->dev_id = 0; 269 strcpy(cdi->info.name, "iPhone IO device"); 270 strcpy(cdi->info.driver, "apple"); 271 cdi->info.input_count = 1; 272 cdi->info.output_count = 1; 273 cdi->info.default_samples_per_sec = 8000; 274 275 /* Set the device capabilities here */ 276 cdi->info.caps = PJMEDIA_AUD_DEV_CAP_INPUT_LATENCY | 277 PJMEDIA_AUD_DEV_CAP_OUTPUT_LATENCY | 278 PJMEDIA_AUD_DEV_CAP_OUTPUT_VOLUME_SETTING | 279 PJMEDIA_AUD_DEV_CAP_INPUT_ROUTE | 280 PJMEDIA_AUD_DEV_CAP_OUTPUT_ROUTE | 281 PJMEDIA_AUD_DEV_CAP_EC; 282 cdi->info.routes = PJMEDIA_AUD_DEV_ROUTE_LOUDSPEAKER | 283 PJMEDIA_AUD_DEV_ROUTE_EARPIECE | 284 PJMEDIA_AUD_DEV_ROUTE_BLUETOOTH; 285 286 PJ_LOG(4, (THIS_FILE, " dev_id %d: %s (in=%d, out=%d) %dHz", 287 i, 288 cdi->info.name, 289 cdi->info.input_count, 290 cdi->info.output_count, 291 cdi->info.default_samples_per_sec)); 292 } 293 294 /* Initialize the Audio Session */ 295 ostatus = AudioSessionInitialize(NULL, NULL, interruptionListener, cf); 296 if (ostatus != kAudioSessionNoError) { 297 PJ_LOG(4, (THIS_FILE, 298 "Error: cannot initialize audio session services (%i)", 299 ostatus)); 300 return PJMEDIA_AUDIODEV_ERRNO_FROM_COREAUDIO(ostatus); 301 } 302 303 /* We want to be able to open playback and recording streams */ 304 audioCategory = kAudioSessionCategory_PlayAndRecord; 305 ostatus = AudioSessionSetProperty(kAudioSessionProperty_AudioCategory, 306 sizeof(audioCategory), 307 &audioCategory); 308 if (ostatus != kAudioSessionNoError) { 309 PJ_LOG(4, (THIS_FILE, 310 "Error: cannot set the audio session category (%i)", 311 ostatus)); 312 return PJMEDIA_AUDIODEV_ERRNO_FROM_COREAUDIO(ostatus); 313 } 314 315 /* Listen for audio routing change notifications */ 316 ostatus = AudioSessionAddPropertyListener( 317 kAudioSessionProperty_AudioRouteChange, 318 propListener, cf); 319 if (ostatus != kAudioSessionNoError) { 320 PJ_LOG(4, (THIS_FILE, 321 "Error: cannot listen for audio route change " 322 "notifications (%i)", ostatus)); 323 return PJMEDIA_AUDIODEV_ERRNO_FROM_COREAUDIO(ostatus); 324 } 325 #endif 326 327 PJ_LOG(4, (THIS_FILE, "core audio initialized")); 328 329 return PJ_SUCCESS; 330 } 331 332 /* API: destroy factory */ 333 static pj_status_t ca_factory_destroy(pjmedia_aud_dev_factory *f) 334 { 335 struct coreaudio_factory *cf = (struct coreaudio_factory*)f; 336 pj_pool_t *pool; 337 338 pj_assert(cf); 339 pj_assert(cf->base_pool); 340 pj_assert(pj_list_empty(&cf->streams)); 341 342 #if !COREAUDIO_MAC 343 AudioSessionRemovePropertyListenerWithUserData( 344 kAudioSessionProperty_AudioRouteChange, propListener, cf); 345 #endif 346 347 if (cf->pool) { 348 pj_pool_release(cf->pool); 349 cf->pool = NULL; 350 } 351 352 if (cf->mutex) { 353 pj_mutex_destroy(cf->mutex); 354 cf->mutex = NULL; 355 } 356 357 pool = cf->base_pool; 358 cf->base_pool = NULL; 359 pj_pool_release(pool); 360 361 return PJ_SUCCESS; 362 } 363 364 /* API: refresh the device list */ 365 static pj_status_t ca_factory_refresh(pjmedia_aud_dev_factory *f) 366 { 367 #if !COREAUDIO_MAC 368 /* iPhone doesn't support refreshing the device list */ 369 PJ_UNUSED_ARG(f); 370 return PJ_SUCCESS; 371 #else 372 struct coreaudio_factory *cf = (struct coreaudio_factory*)f; 373 unsigned i; 207 374 unsigned dev_count; 208 375 AudioObjectPropertyAddress addr; … … 211 378 AudioBufferList *buf = NULL; 212 379 OSStatus ostatus; 213 #endif 214 215 desc.componentType = kAudioUnitType_Output; 216 #if COREAUDIO_MAC 217 desc.componentSubType = kAudioUnitSubType_HALOutput; 218 #else 219 desc.componentSubType = kAudioUnitSubType_VoiceProcessingIO; 220 #endif 221 desc.componentManufacturer = kAudioUnitManufacturer_Apple; 222 desc.componentFlags = 0; 223 desc.componentFlagsMask = 0; 224 225 cf->io_comp = AudioComponentFindNext(NULL, &desc); 226 if (cf->io_comp == NULL) 227 return PJMEDIA_EAUD_INIT; // cannot find IO unit; 228 229 cf->stream = NULL; 230 231 #if COREAUDIO_MAC 380 381 if (cf->pool != NULL) { 382 pj_pool_release(cf->pool); 383 cf->pool = NULL; 384 } 385 386 cf->dev_count = 0; 387 cf->pool = pj_pool_create(cf->pf, "core audio", 1000, 1000, NULL); 388 232 389 /* Find out how many audio devices there are */ 233 390 addr.mSelector = kAudioHardwarePropertyDevices; … … 254 411 return PJ_SUCCESS; 255 412 } 256 PJ_LOG(4, (THIS_FILE, "core audio initialized with%d devices",257 dev_count));413 PJ_LOG(4, (THIS_FILE, "core audio detected %d devices", 414 dev_count)); 258 415 259 416 /* Get all the audio device IDs */ … … 390 547 cdi->info.default_samples_per_sec)); 391 548 } 392 #else393 cf->dev_count = 1;394 cf->dev_info = (struct coreaudio_dev_info*)395 pj_pool_calloc(cf->pool, cf->dev_count,396 sizeof(struct coreaudio_dev_info));397 for (i = 0; i < cf->dev_count; i++) {398 struct coreaudio_dev_info *cdi;399 400 cdi = &cf->dev_info[i];401 pj_bzero(cdi, sizeof(*cdi));402 cdi->dev_id = 0;403 strcpy(cdi->info.name, "iPhone IO device");404 strcpy(cdi->info.driver, "apple");405 cdi->info.input_count = 1;406 cdi->info.output_count = 1;407 cdi->info.default_samples_per_sec = 8000;408 409 /* Set the device capabilities here */410 cdi->info.caps = PJMEDIA_AUD_DEV_CAP_INPUT_LATENCY |411 PJMEDIA_AUD_DEV_CAP_OUTPUT_LATENCY |412 PJMEDIA_AUD_DEV_CAP_OUTPUT_VOLUME_SETTING |413 PJMEDIA_AUD_DEV_CAP_INPUT_ROUTE |414 PJMEDIA_AUD_DEV_CAP_OUTPUT_ROUTE |415 PJMEDIA_AUD_DEV_CAP_EC;416 cdi->info.routes = PJMEDIA_AUD_DEV_ROUTE_LOUDSPEAKER |417 PJMEDIA_AUD_DEV_ROUTE_EARPIECE |418 PJMEDIA_AUD_DEV_ROUTE_BLUETOOTH;419 }420 421 if (AudioSessionInitialize(NULL, NULL, interruptionListener, cf) !=422 kAudioSessionNoError)423 {424 PJ_LOG(4, (THIS_FILE,425 "Warning: cannot initialize audio session services"));426 }427 428 PJ_LOG(4, (THIS_FILE, "core audio initialized"));429 430 #endif431 549 432 550 return PJ_SUCCESS; 433 } 434 435 /* API: destroy factory */ 436 static pj_status_t ca_factory_destroy(pjmedia_aud_dev_factory *f) 437 { 438 struct coreaudio_factory *cf = (struct coreaudio_factory*)f; 439 pj_pool_t *pool = cf->pool; 440 441 cf->pool = NULL; 442 pj_pool_release(pool); 443 444 return PJ_SUCCESS; 551 #endif 445 552 } 446 553 … … 508 615 } 509 616 510 static OSStatus input_callback(void *inRefCon, 511 AudioUnitRenderActionFlags *ioActionFlags, 512 const AudioTimeStamp *inTimeStamp, 513 UInt32 inBusNumber, 514 UInt32 inNumberFrames, 515 AudioBufferList *ioData) 617 OSStatus resampleProc(AudioConverterRef inAudioConverter, 618 UInt32 *ioNumberDataPackets, 619 AudioBufferList *ioData, 620 AudioStreamPacketDescription **outDataPacketDescription, 621 void *inUserData) 622 { 623 struct coreaudio_stream *strm = (struct coreaudio_stream*)inUserData; 624 625 pj_assert(*ioNumberDataPackets == strm->resample_buf_count); 626 627 ioData->mNumberBuffers = 1; 628 ioData->mBuffers[0].mNumberChannels = 1; 629 ioData->mBuffers[0].mData = strm->resample_buf; 630 ioData->mBuffers[0].mDataByteSize = strm->resample_buf_count * 631 strm->param.bits_per_sample >> 3; 632 return 0; 633 } 634 635 static OSStatus resample_callback(void *inRefCon, 636 AudioUnitRenderActionFlags *ioActionFlags, 637 const AudioTimeStamp *inTimeStamp, 638 UInt32 inBusNumber, 639 UInt32 inNumberFrames, 640 AudioBufferList *ioData) 516 641 { 517 642 struct coreaudio_stream *strm = (struct coreaudio_stream*)inRefCon; … … 521 646 AudioBufferList *buf = strm->audio_buf; 522 647 pj_int16_t *input; 523 524 if (strm->quit_flag) 525 goto on_break; 648 UInt32 resampleSize; 649 UInt32 size; 650 651 pj_assert(!strm->quit_flag); 526 652 527 653 /* Known cases of callback's thread: … … 537 663 &strm->rec_thread); 538 664 strm->rec_thread_initialized = 1; 539 PJ_LOG(5,(THIS_FILE, "Recorder thread started")); 665 PJ_LOG(5,(THIS_FILE, "Recorder thread started, (%i frames)", 666 inNumberFrames)); 540 667 } 541 668 … … 557 684 input = (pj_int16_t *)buf->mBuffers[0].mData; 558 685 686 /* Calculate how many frames we need to fill an entire packet */ 687 resampleSize = strm->param.samples_per_frame * 688 strm->param.bits_per_sample >> 3; 689 size = sizeof(resampleSize); 690 ostatus = AudioConverterGetProperty( 691 strm->resample, 692 kAudioConverterPropertyCalculateInputBufferSize, 693 &size, 694 &resampleSize); 695 if (ostatus != noErr) { 696 PJ_LOG(5, (THIS_FILE, "Core audio converter measure error %i", 697 ostatus)); 698 goto on_break; 699 } 700 resampleSize /= strm->param.bits_per_sample >> 3; 701 702 nsamples = inNumberFrames * strm->param.channel_count + 703 strm->resample_buf_count; 704 pj_assert(nsamples < strm->resample_buf_size); 705 706 if (nsamples >= resampleSize) { 707 UInt32 resampleOutput = strm->param.samples_per_frame; 708 unsigned chunk_count = 0; 709 pjmedia_frame frame; 710 AudioBufferList ab; 711 712 frame.type = PJMEDIA_FRAME_TYPE_AUDIO; 713 frame.size = strm->param.samples_per_frame * 714 strm->param.bits_per_sample >> 3; 715 frame.bit_info = 0; 716 717 /* If buffer is not empty, combine the buffer with the just incoming 718 * samples, then call put_frame. 719 */ 720 721 chunk_count = resampleSize - strm->resample_buf_count; 722 pjmedia_copy_samples(strm->resample_buf + strm->resample_buf_count, 723 input, chunk_count); 724 strm->resample_buf_count += chunk_count; 725 726 frame.buf = (void*) strm->rec_buf; 727 frame.timestamp.u64 = strm->rec_timestamp.u64; 728 729 ab.mNumberBuffers = 1; 730 ab.mBuffers[0].mNumberChannels = 1; 731 ab.mBuffers[0].mDataByteSize = frame.size; 732 ab.mBuffers[0].mData = frame.buf; 733 734 /* Do the resample */ 735 ostatus = AudioConverterFillComplexBuffer(strm->resample, 736 resampleProc, 737 strm, 738 &resampleOutput, 739 &ab, 740 NULL); 741 if (ostatus != noErr) { 742 goto on_break; 743 } 744 745 status = (*strm->rec_cb)(strm->user_data, &frame); 746 747 input = input + chunk_count; 748 nsamples -= resampleSize; 749 strm->resample_buf_count = 0; 750 strm->rec_timestamp.u64 += strm->param.samples_per_frame / 751 strm->param.channel_count; 752 753 pj_assert(nsamples < resampleSize); 754 755 /* Store the remaining samples into the buffer */ 756 if (nsamples && status == 0) { 757 strm->resample_buf_count = nsamples; 758 pjmedia_copy_samples(strm->resample_buf, input, 759 nsamples); 760 } 761 762 } else { 763 /* Not enough samples, let's just store them in the buffer */ 764 pjmedia_copy_samples(strm->resample_buf + strm->resample_buf_count, 765 input, 766 inNumberFrames * strm->param.channel_count); 767 strm->resample_buf_count += inNumberFrames * 768 strm->param.channel_count; 769 } 770 771 return noErr; 772 773 on_break: 774 return -1; 775 } 776 777 static OSStatus input_callback(void *inRefCon, 778 AudioUnitRenderActionFlags *ioActionFlags, 779 const AudioTimeStamp *inTimeStamp, 780 UInt32 inBusNumber, 781 UInt32 inNumberFrames, 782 AudioBufferList *ioData) 783 { 784 struct coreaudio_stream *strm = (struct coreaudio_stream*)inRefCon; 785 OSStatus ostatus; 786 pj_status_t status = 0; 787 unsigned nsamples; 788 AudioBufferList *buf = strm->audio_buf; 789 pj_int16_t *input; 790 791 pj_assert(!strm->quit_flag); 792 793 /* Known cases of callback's thread: 794 * - The thread may be changed in the middle of a session 795 * it happens when plugging/unplugging headphone. 796 * - The same thread may be reused in consecutive sessions. The first 797 * session will leave TLS set, but release the TLS data address, 798 * so the second session must re-register the callback's thread. 799 */ 800 if (strm->rec_thread_initialized == 0 || !pj_thread_is_registered()) 801 { 802 status = pj_thread_register("ca_rec", strm->rec_thread_desc, 803 &strm->rec_thread); 804 strm->rec_thread_initialized = 1; 805 PJ_LOG(5,(THIS_FILE, "Recorder thread started, (%i frames)", 806 inNumberFrames)); 807 } 808 809 buf->mBuffers[0].mData = NULL; 810 buf->mBuffers[0].mDataByteSize = inNumberFrames * 811 strm->streamFormat.mChannelsPerFrame; 812 /* Render the unit to get input data */ 813 ostatus = AudioUnitRender(strm->io_units[0], 814 ioActionFlags, 815 inTimeStamp, 816 inBusNumber, 817 inNumberFrames, 818 buf); 819 820 if (ostatus != noErr) { 821 PJ_LOG(5, (THIS_FILE, "Core audio unit render error %i", ostatus)); 822 goto on_break; 823 } 824 input = (pj_int16_t *)buf->mBuffers[0].mData; 825 559 826 /* Calculate number of samples we've got */ 560 nsamples = inNumberFrames * strm->param.channel_count + strm->rec_buf_count; 827 nsamples = inNumberFrames * strm->param.channel_count + 828 strm->rec_buf_count; 561 829 if (nsamples >= strm->param.samples_per_frame) 562 830 { … … 620 888 return noErr; 621 889 622 on_break: 623 strm->rec_thread_exited = 1; 624 return -1; 890 on_break: 891 return -1; 625 892 } 626 893 … … 637 904 pj_int16_t *output = ioData->mBuffers[0].mData; 638 905 639 if (stream->quit_flag) 640 goto on_break; 906 pj_assert(!stream->quit_flag); 641 907 642 908 /* Known cases of callback's thread: … … 652 918 &stream->play_thread); 653 919 stream->play_thread_initialized = 1; 654 PJ_LOG(5,(THIS_FILE, "Player thread started "));920 PJ_LOG(5,(THIS_FILE, "Player thread started, (%i frames)", inNumberFrames)); 655 921 } 656 922 … … 725 991 return noErr; 726 992 727 on_break: 728 stream->play_thread_exited = 1; 729 return -1; 993 on_break: 994 return -1; 730 995 } 731 996 … … 736 1001 const void * inData) 737 1002 { 738 struct coreaudio_stream *strm = (struct coreaudio_stream*)inClientData; 739 740 if (inID == kAudioSessionProperty_AudioRouteChange) { 741 742 PJ_LOG(3, (THIS_FILE, "audio route changed")); 743 if (strm->interrupted) 744 return; 745 746 ca_stream_stop((pjmedia_aud_stream *)strm); 747 AudioUnitUninitialize(strm->io_units[0]); 748 AudioComponentInstanceDispose(strm->io_units[0]); 749 750 if (create_audio_unit(strm->cf->io_comp, 0, 751 strm->param.dir, strm, 752 &strm->io_units[0]) != PJ_SUCCESS) 753 { 1003 struct coreaudio_factory *cf = (struct coreaudio_factory*)inClientData; 1004 struct stream_list *it, *itBegin; 1005 pj_status_t status; 1006 OSStatus ostatus; 1007 CFDictionaryRef routeDictionary; 1008 CFNumberRef reason; 1009 SInt32 reasonVal; 1010 pj_assert(cf); 1011 1012 if (inID != kAudioSessionProperty_AudioRouteChange) 1013 return; 1014 1015 routeDictionary = (CFDictionaryRef)inData; 1016 reason = (CFNumberRef) 1017 CFDictionaryGetValue( 1018 routeDictionary, 1019 CFSTR(kAudioSession_AudioRouteChangeKey_Reason)); 1020 CFNumberGetValue(reason, kCFNumberSInt32Type, &reasonVal); 1021 1022 if (reasonVal == kAudioSessionRouteChangeReason_CategoryChange) { 1023 PJ_LOG(3, (THIS_FILE, "audio route changed due to category change, " 1024 "ignoring...")); 1025 return; 1026 } 1027 if (reasonVal == kAudioSessionRouteChangeReason_Override) { 1028 PJ_LOG(3, (THIS_FILE, "audio route changed due to user override, " 1029 "ignoring...")); 1030 return; 1031 } 1032 1033 PJ_LOG(3, (THIS_FILE, "audio route changed")); 1034 1035 pj_mutex_lock(cf->mutex); 1036 itBegin = &cf->streams; 1037 for (it = itBegin->next; it != itBegin; it = it->next) { 1038 pj_bool_t running = it->stream->running; 1039 1040 if (it->stream->interrupted) 1041 continue; 1042 1043 status = ca_stream_stop((pjmedia_aud_stream *)it->stream); 1044 1045 ostatus = AudioUnitUninitialize(it->stream->io_units[0]); 1046 ostatus = AudioComponentInstanceDispose(it->stream->io_units[0]); 1047 1048 status = create_audio_unit(it->stream->cf->io_comp, 0, 1049 it->stream->param.dir, 1050 it->stream, &it->stream->io_units[0]); 1051 if (status != PJ_SUCCESS) { 754 1052 PJ_LOG(3, (THIS_FILE, 755 "Error: failed to create a new instance of audio unit")); 756 return; 757 } 758 if (ca_stream_start((pjmedia_aud_stream *)strm) != PJ_SUCCESS) { 1053 "Error: failed to create a replacement audio unit (%i)", 1054 status)); 1055 continue; 1056 } 1057 1058 if (running) { 1059 status = ca_stream_start((pjmedia_aud_stream *)it->stream); 1060 } 1061 if (status != PJ_SUCCESS) { 759 1062 PJ_LOG(3, (THIS_FILE, 760 "Error: failed to restart audio unit")); 1063 "Error: failed to restart the audio unit (%i)", 1064 status)); 1065 continue; 761 1066 } 762 1067 PJ_LOG(3, (THIS_FILE, "core audio unit successfully reinstantiated")); 763 1068 } 1069 pj_mutex_unlock(cf->mutex); 764 1070 } 765 1071 766 1072 static void interruptionListener(void *inClientData, UInt32 inInterruption) 767 1073 { 768 struct coreaudio_stream *strm = ((struct coreaudio_factory*)inClientData)-> 769 stream; 770 if (!strm) 771 return; 1074 struct coreaudio_factory *cf = (struct coreaudio_factory*)inClientData; 1075 struct stream_list *it, *itBegin; 1076 pj_status_t status; 1077 OSStatus ostatus; 1078 pj_assert(cf); 772 1079 773 1080 PJ_LOG(3, (THIS_FILE, "Session interrupted! --- %s ---", … … 775 1082 "Begin Interruption" : "End Interruption")); 776 1083 777 if (inInterruption == kAudioSessionEndInterruption) { 778 strm->interrupted = PJ_FALSE; 779 /* There may be an audio route change during the interruption 780 * (such as when the alarm rings), so we have to notify the 781 * listener as well. 782 */ 783 propListener(strm, kAudioSessionProperty_AudioRouteChange, 784 0, NULL); 785 } else if (inInterruption == kAudioSessionBeginInterruption) { 786 strm->interrupted = PJ_TRUE; 787 AudioOutputUnitStop(strm->io_units[0]); 788 } 789 } 790 1084 pj_mutex_lock(cf->mutex); 1085 itBegin = &cf->streams; 1086 for (it = itBegin->next; it != itBegin; it = it->next) { 1087 if (!it->stream->running) 1088 continue; 1089 1090 if (inInterruption == kAudioSessionEndInterruption && 1091 it->stream->interrupted == PJ_TRUE) 1092 { 1093 ostatus = AudioUnitUninitialize(it->stream->io_units[0]); 1094 ostatus = AudioComponentInstanceDispose(it->stream->io_units[0]); 1095 1096 status = create_audio_unit(it->stream->cf->io_comp, 0, 1097 it->stream->param.dir, 1098 it->stream, &it->stream->io_units[0]); 1099 if (status != PJ_SUCCESS) { 1100 PJ_LOG(3, (THIS_FILE, 1101 "Error: failed to create a replacement " 1102 "audio unit (%i)", ostatus)); 1103 continue; 1104 } 1105 1106 status = ca_stream_start((pjmedia_aud_stream*)it->stream); 1107 if (status != PJ_SUCCESS) { 1108 PJ_LOG(3, (THIS_FILE, 1109 "Error: failed to restart the audio unit (%i)", 1110 ostatus)); 1111 continue; 1112 } 1113 PJ_LOG(3, (THIS_FILE, "core audio unit successfully " 1114 "reinstantiated")); 1115 } else if (inInterruption == kAudioSessionBeginInterruption && 1116 it->stream->running == PJ_TRUE) 1117 { 1118 status = ca_stream_stop((pjmedia_aud_stream*)it->stream); 1119 it->stream->interrupted = PJ_TRUE; 1120 } 1121 } 1122 pj_mutex_unlock(cf->mutex); 1123 } 1124 1125 #endif 1126 1127 #if COREAUDIO_MAC 1128 /* Internal: create audio converter for resampling the recorder device */ 1129 static pj_status_t create_audio_resample(struct coreaudio_stream *strm, 1130 AudioStreamBasicDescription *desc) 1131 { 1132 OSStatus ostatus; 1133 1134 pj_assert(strm->streamFormat.mSampleRate != desc->mSampleRate); 1135 pj_assert(NULL == strm->resample); 1136 pj_assert(NULL == strm->resample_buf); 1137 1138 /* Create the audio converter */ 1139 ostatus = AudioConverterNew(desc, &strm->streamFormat, &strm->resample); 1140 if (ostatus != noErr) { 1141 return PJMEDIA_AUDIODEV_ERRNO_FROM_COREAUDIO(ostatus); 1142 } 1143 1144 /* 1145 * Allocate the buffer required to hold the enough input data 1146 * for two complete frames of audio. 1147 */ 1148 strm->resample_buf_size = (unsigned)(desc->mSampleRate * 1149 strm->param.samples_per_frame / 1150 strm->param.clock_rate * 2); 1151 strm->resample_buf = (pj_int16_t*) 1152 pj_pool_alloc(strm->pool, 1153 strm->resample_buf_size * 1154 strm->param.bits_per_sample >> 3); 1155 if (!strm->resample_buf) 1156 return PJ_ENOMEM; 1157 strm->resample_buf_count = 0; 1158 1159 return PJ_SUCCESS; 1160 } 791 1161 #endif 792 1162 … … 799 1169 { 800 1170 OSStatus ostatus; 801 #if !COREAUDIO_MAC802 UInt32 audioCategory = kAudioSessionCategory_PlayAndRecord;803 if (!(dir & PJMEDIA_DIR_CAPTURE)) {804 audioCategory = kAudioSessionCategory_MediaPlayback;805 } else if (!(dir & PJMEDIA_DIR_PLAYBACK)) {806 audioCategory = kAudioSessionCategory_RecordAudio;807 }808 AudioSessionSetProperty(kAudioSessionProperty_AudioCategory,809 sizeof(audioCategory), &audioCategory);810 #endif811 1171 812 1172 /* Create an audio unit to interface with the device */ … … 886 1246 AudioStreamBasicDescription deviceFormat; 887 1247 UInt32 size; 1248 1249 /* 1250 * Keep the sample rate from the device, otherwise we will confuse 1251 * AUHAL 1252 */ 1253 size = sizeof(AudioStreamBasicDescription); 1254 ostatus = AudioUnitGetProperty(*io_unit, 1255 kAudioUnitProperty_StreamFormat, 1256 kAudioUnitScope_Input, 1257 1, 1258 &deviceFormat, 1259 &size); 1260 if (ostatus != noErr) { 1261 return PJMEDIA_AUDIODEV_ERRNO_FROM_COREAUDIO(ostatus); 1262 } 1263 strm->streamFormat.mSampleRate = deviceFormat.mSampleRate; 888 1264 #endif 889 1265 … … 903 1279 904 1280 #if COREAUDIO_MAC 1281 strm->streamFormat.mSampleRate = strm->param.clock_rate; 905 1282 size = sizeof(AudioStreamBasicDescription); 906 1283 ostatus = AudioUnitGetProperty (*io_unit, 907 1284 kAudioUnitProperty_StreamFormat, 908 kAudioUnitScope_ Input,1285 kAudioUnitScope_Output, 909 1286 1, 910 1287 &deviceFormat, … … 912 1289 if (ostatus == noErr) { 913 1290 if (strm->streamFormat.mSampleRate != deviceFormat.mSampleRate) { 914 return PJMEDIA_AUDIODEV_ERRNO_FROM_COREAUDIO(ostatus); 1291 pj_status_t rc = create_audio_resample(strm, &deviceFormat); 1292 if (PJ_SUCCESS != rc) 1293 return rc; 915 1294 } 916 1295 } else { … … 966 1345 967 1346 /* Set input callback */ 968 input_cb.inputProc = input_callback; 1347 input_cb.inputProc = strm->resample ? resample_callback : 1348 input_callback; 969 1349 input_cb.inputProcRefCon = strm; 970 ostatus = AudioUnitSetProperty(*io_unit, 971 kAudioOutputUnitProperty_SetInputCallback, 972 kAudioUnitScope_Global, 973 0, 974 &input_cb, 975 sizeof(input_cb)); 1350 ostatus = AudioUnitSetProperty( 1351 *io_unit, 1352 kAudioOutputUnitProperty_SetInputCallback, 1353 kAudioUnitScope_Global, 1354 0, 1355 &input_cb, 1356 sizeof(input_cb)); 976 1357 if (ostatus != noErr) { 977 1358 return PJMEDIA_AUDIODEV_ERRNO_FROM_COREAUDIO(ostatus); … … 1058 1439 1059 1440 strm = PJ_POOL_ZALLOC_T(pool, struct coreaudio_stream); 1060 cf->stream = strm; 1441 pj_list_init(&strm->list_entry); 1442 strm->list_entry.stream = strm; 1061 1443 strm->cf = cf; 1062 1444 pj_memcpy(&strm->param, param, sizeof(*param)); … … 1146 1528 ¶m->output_vol); 1147 1529 } 1530 1531 pj_mutex_lock(strm->cf->mutex); 1532 pj_assert(pj_list_empty(&strm->list_entry)); 1533 pj_list_insert_after(&strm->cf->streams, &strm->list_entry); 1534 pj_mutex_unlock(strm->cf->mutex); 1148 1535 1149 1536 /* Done */ … … 1252 1639 &size, &latency2) == kAudioSessionNoError)) 1253 1640 { 1254 strm->param.input_latency_ms = (unsigned)((latency + latency2) * 1000); 1641 strm->param.input_latency_ms = (unsigned) 1642 ((latency + latency2) * 1000); 1255 1643 strm->param.input_latency_ms++; 1256 1644 } … … 1299 1687 &size, &latency2) == kAudioSessionNoError)) 1300 1688 { 1301 strm->param.output_latency_ms = (unsigned)((latency + latency2) * 1000); 1689 strm->param.output_latency_ms = (unsigned) 1690 ((latency + latency2) * 1000); 1302 1691 strm->param.output_latency_ms++; 1303 1692 } … … 1401 1790 struct coreaudio_stream *strm = (struct coreaudio_stream*)s; 1402 1791 1403 PJ_UNUSED_ARG(strm);1404 1405 1792 PJ_ASSERT_RETURN(s && pval, PJ_EINVAL); 1406 1793 … … 1427 1814 return PJ_SUCCESS; 1428 1815 } 1429 #endif 1430 1431 #if !COREAUDIO_MAC 1432 if (cap==PJMEDIA_AUD_DEV_CAP_INPUT_ROUTE && 1816 1817 #else 1818 1819 if ((cap==PJMEDIA_AUD_DEV_CAP_INPUT_LATENCY && 1820 (strm->param.dir & PJMEDIA_DIR_CAPTURE)) || 1821 (cap==PJMEDIA_AUD_DEV_CAP_OUTPUT_LATENCY && 1822 (strm->param.dir & PJMEDIA_DIR_PLAYBACK))) 1823 { 1824 Float32 bufferDuration = *(unsigned *)pval; 1825 OSStatus ostatus; 1826 unsigned latency; 1827 1828 /* For low-latency audio streaming, you can set this value to 1829 * as low as 5 ms (the default is 23ms). However, lowering the 1830 * latency may cause a decrease in audio quality. 1831 */ 1832 bufferDuration /= 1000; 1833 ostatus = AudioSessionSetProperty( 1834 kAudioSessionProperty_PreferredHardwareIOBufferDuration, 1835 sizeof(bufferDuration), &bufferDuration); 1836 if (ostatus != kAudioSessionNoError) { 1837 PJ_LOG(4, (THIS_FILE, 1838 "Error: cannot set the preferred buffer duration (%i)", 1839 ostatus)); 1840 return PJMEDIA_AUDIODEV_ERRNO_FROM_COREAUDIO(ostatus); 1841 } 1842 1843 ca_stream_get_cap(s, PJMEDIA_AUD_DEV_CAP_INPUT_LATENCY, &latency); 1844 ca_stream_get_cap(s, PJMEDIA_AUD_DEV_CAP_OUTPUT_LATENCY, &latency); 1845 1846 return PJ_SUCCESS; 1847 } else if (cap==PJMEDIA_AUD_DEV_CAP_INPUT_ROUTE && 1433 1848 (strm->param.dir & PJMEDIA_DIR_CAPTURE)) 1434 1849 { … … 1493 1908 OSStatus ostatus; 1494 1909 UInt32 i; 1910 pj_bool_t should_activate; 1911 struct stream_list *it, *itBegin; 1912 1913 if (stream->running) 1914 return PJ_SUCCESS; 1495 1915 1496 1916 stream->quit_flag = 0; 1497 stream->rec_thread_exited = 0;1498 stream->play_thread_exited = 0;1499 1917 stream->interrupted = PJ_FALSE; 1918 stream->rec_buf_count = 0; 1919 stream->play_buf_count = 0; 1920 stream->resample_buf_count = 0; 1921 1922 if (stream->resample) { 1923 ostatus = AudioConverterReset(stream->resample); 1924 if (ostatus != noErr) 1925 return PJMEDIA_AUDIODEV_ERRNO_FROM_COREAUDIO(ostatus); 1926 } 1500 1927 1501 1928 for (i = 0; i < 2; i++) { … … 1509 1936 } 1510 1937 1938 /* 1939 * Make sure this stream is not in the list of running streams. 1940 * If this is the 1st stream that is running we need to activate 1941 * the audio session. 1942 */ 1943 pj_mutex_lock(stream->cf->mutex); 1944 pj_assert(!pj_list_empty(&stream->cf->streams)); 1945 pj_assert(!pj_list_empty(&stream->list_entry)); 1946 should_activate = PJ_TRUE; 1947 itBegin = &stream->cf->streams; 1948 for (it = itBegin->next; it != itBegin; it = it->next) { 1949 if (it->stream->running) { 1950 should_activate = PJ_FALSE; 1951 break; 1952 } 1953 } 1954 stream->running = PJ_TRUE; 1955 pj_mutex_unlock(stream->cf->mutex); 1956 1511 1957 #if !COREAUDIO_MAC 1958 if (should_activate) 1512 1959 AudioSessionSetActive(true); 1513 1514 AudioSessionAddPropertyListener(kAudioSessionProperty_AudioRouteChange,1515 propListener, strm);1516 1960 #endif 1517 1961 … … 1527 1971 OSStatus ostatus; 1528 1972 unsigned i; 1529 1530 stream->quit_flag = 1; 1531 for (i=0; !stream->rec_thread_exited && i<100; ++i) 1532 pj_thread_sleep(10); 1533 for (i=0; !stream->play_thread_exited && i<100; ++i) 1534 pj_thread_sleep(10); 1535 1536 pj_thread_sleep(1); 1537 pj_bzero(stream->rec_thread_desc, sizeof(pj_thread_desc)); 1538 pj_bzero(stream->play_thread_desc, sizeof(pj_thread_desc)); 1539 1540 #if !COREAUDIO_MAC 1541 AudioSessionSetActive(false); 1542 #endif 1973 int should_deactivate; 1974 struct stream_list *it, *itBegin; 1975 1976 if (!stream->running) 1977 return PJ_SUCCESS; 1543 1978 1544 1979 for (i = 0; i < 2; i++) { … … 1551 1986 } 1552 1987 } 1988 1989 /* 1990 * Make sure this stream is not in the list of running streams. 1991 * If this is the 1st stream that is running we need to activate 1992 * the audio session. 1993 */ 1994 pj_mutex_lock(stream->cf->mutex); 1995 pj_assert(!pj_list_empty(&stream->cf->streams)); 1996 pj_assert(!pj_list_empty(&stream->list_entry)); 1997 stream->running = PJ_FALSE; 1998 should_deactivate = PJ_TRUE; 1999 itBegin = &stream->cf->streams; 2000 for (it = itBegin->next; it != itBegin; it = it->next) { 2001 if (it->stream->running) { 2002 should_deactivate = PJ_FALSE; 2003 break; 2004 } 2005 } 2006 pj_mutex_unlock(stream->cf->mutex); 2007 2008 #if !COREAUDIO_MAC 2009 if (should_deactivate) 2010 AudioSessionSetActive(false); 2011 #endif 2012 2013 stream->quit_flag = 1; 1553 2014 stream->play_thread_initialized = 0; 1554 2015 stream->rec_thread_initialized = 0; 2016 pj_bzero(stream->rec_thread_desc, sizeof(pj_thread_desc)); 2017 pj_bzero(stream->play_thread_desc, sizeof(pj_thread_desc)); 1555 2018 1556 2019 PJ_LOG(4, (THIS_FILE, "core audio stream stopped")); … … 1567 2030 1568 2031 PJ_ASSERT_RETURN(stream != NULL, PJ_EINVAL); 1569 1570 #if !COREAUDIO_MAC1571 AudioSessionRemovePropertyListenerWithUserData(1572 kAudioSessionProperty_AudioRouteChange, propListener, strm);1573 #endif1574 2032 1575 2033 ca_stream_stop(strm); … … 1583 2041 } 1584 2042 1585 stream->cf->stream = NULL; 2043 if (stream->resample) 2044 AudioConverterDispose(stream->resample); 2045 2046 pj_mutex_lock(stream->cf->mutex); 2047 if (!pj_list_empty(&stream->list_entry)) 2048 pj_list_erase(&stream->list_entry); 2049 pj_mutex_unlock(stream->cf->mutex); 2050 1586 2051 pj_pool_release(stream->pool); 1587 2052
Note: See TracChangeset
for help on using the changeset viewer.