Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 /* | 1 /* |
| 2 * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. | 2 * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. |
| 3 * | 3 * |
| 4 * Use of this source code is governed by a BSD-style license | 4 * Use of this source code is governed by a BSD-style license |
| 5 * that can be found in the LICENSE file in the root of the source | 5 * that can be found in the LICENSE file in the root of the source |
| 6 * tree. An additional intellectual property rights grant can be found | 6 * tree. An additional intellectual property rights grant can be found |
| 7 * in the file PATENTS. All contributing project authors may | 7 * in the file PATENTS. All contributing project authors may |
| 8 * be found in the AUTHORS file in the root of the source tree. | 8 * be found in the AUTHORS file in the root of the source tree. |
| 9 */ | 9 */ |
| 10 | 10 |
| (...skipping 43 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 54 } \ | 54 } \ |
| 55 } while (0) | 55 } while (0) |
| 56 | 56 |
| 57 | 57 |
| 58 // Hardcoded delay estimates based on real measurements. | 58 // Hardcoded delay estimates based on real measurements. |
| 59 // TODO(henrika): these value is not used in combination with built-in AEC. | 59 // TODO(henrika): these value is not used in combination with built-in AEC. |
| 60 // Can most likely be removed. | 60 // Can most likely be removed. |
| 61 const UInt16 kFixedPlayoutDelayEstimate = 30; | 61 const UInt16 kFixedPlayoutDelayEstimate = 30; |
| 62 const UInt16 kFixedRecordDelayEstimate = 30; | 62 const UInt16 kFixedRecordDelayEstimate = 30; |
| 63 | 63 |
| 64 enum AudioDeviceMessageType : uint32_t { | |
| 65 kMessageTypeInterruptionBegin, | |
| 66 kMessageTypeInterruptionEnd, | |
| 67 kMessageTypeValidRouteChange, | |
| 68 kMessageTypeCanPlayOrRecordChange, | |
| 69 kMessageTypeSampleRateChange, | |
| 70 }; | |
| 71 | |
| 64 using ios::CheckAndLogError; | 72 using ios::CheckAndLogError; |
| 65 | 73 |
| 66 #if !defined(NDEBUG) | 74 #if !defined(NDEBUG) |
| 67 // Helper method that logs essential device information strings. | 75 // Helper method that logs essential device information strings. |
| 68 static void LogDeviceInfo() { | 76 static void LogDeviceInfo() { |
| 69 LOG(LS_INFO) << "LogDeviceInfo"; | 77 LOG(LS_INFO) << "LogDeviceInfo"; |
| 70 @autoreleasepool { | 78 @autoreleasepool { |
| 71 LOG(LS_INFO) << " system name: " << ios::GetSystemName(); | 79 LOG(LS_INFO) << " system name: " << ios::GetSystemName(); |
| 72 LOG(LS_INFO) << " system version 1(2): " << ios::GetSystemVersionAsString(); | 80 LOG(LS_INFO) << " system version 1(2): " << ios::GetSystemVersionAsString(); |
| 73 LOG(LS_INFO) << " system version 2(2): " << ios::GetSystemVersion(); | 81 LOG(LS_INFO) << " system version 2(2): " << ios::GetSystemVersion(); |
| 74 LOG(LS_INFO) << " device type: " << ios::GetDeviceType(); | 82 LOG(LS_INFO) << " device type: " << ios::GetDeviceType(); |
| 75 LOG(LS_INFO) << " device name: " << ios::GetDeviceName(); | 83 LOG(LS_INFO) << " device name: " << ios::GetDeviceName(); |
| 76 LOG(LS_INFO) << " process name: " << ios::GetProcessName(); | 84 LOG(LS_INFO) << " process name: " << ios::GetProcessName(); |
| 77 LOG(LS_INFO) << " process ID: " << ios::GetProcessID(); | 85 LOG(LS_INFO) << " process ID: " << ios::GetProcessID(); |
| 78 LOG(LS_INFO) << " OS version: " << ios::GetOSVersionString(); | 86 LOG(LS_INFO) << " OS version: " << ios::GetOSVersionString(); |
| 79 LOG(LS_INFO) << " processing cores: " << ios::GetProcessorCount(); | 87 LOG(LS_INFO) << " processing cores: " << ios::GetProcessorCount(); |
| 80 #if defined(__IPHONE_9_0) && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_9_0 | 88 #if defined(__IPHONE_9_0) && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_9_0 |
| 81 LOG(LS_INFO) << " low power mode: " << ios::GetLowPowerModeEnabled(); | 89 LOG(LS_INFO) << " low power mode: " << ios::GetLowPowerModeEnabled(); |
| 82 #endif | 90 #endif |
| 83 } | 91 } |
| 84 } | 92 } |
| 85 #endif // !defined(NDEBUG) | 93 #endif // !defined(NDEBUG) |
| 86 | 94 |
| 87 AudioDeviceIOS::AudioDeviceIOS() | 95 AudioDeviceIOS::AudioDeviceIOS() |
| 88 : async_invoker_(new rtc::AsyncInvoker()), | 96 : audio_device_buffer_(nullptr), |
| 89 audio_device_buffer_(nullptr), | |
| 90 audio_unit_(nullptr), | 97 audio_unit_(nullptr), |
| 91 recording_(0), | 98 recording_(0), |
| 92 playing_(0), | 99 playing_(0), |
| 93 initialized_(false), | 100 initialized_(false), |
| 94 rec_is_initialized_(false), | 101 rec_is_initialized_(false), |
| 95 play_is_initialized_(false), | 102 play_is_initialized_(false), |
| 96 is_interrupted_(false) { | 103 is_interrupted_(false), |
| 104 has_configured_session_(false) { | |
| 97 LOGI() << "ctor" << ios::GetCurrentThreadDescription(); | 105 LOGI() << "ctor" << ios::GetCurrentThreadDescription(); |
| 98 thread_ = rtc::Thread::Current(); | 106 thread_ = rtc::Thread::Current(); |
| 99 audio_session_observer_ = | 107 audio_session_observer_ = |
| 100 [[RTCAudioSessionDelegateAdapter alloc] initWithObserver:this]; | 108 [[RTCAudioSessionDelegateAdapter alloc] initWithObserver:this]; |
| 101 } | 109 } |
| 102 | 110 |
| 103 AudioDeviceIOS::~AudioDeviceIOS() { | 111 AudioDeviceIOS::~AudioDeviceIOS() { |
| 104 LOGI() << "~dtor" << ios::GetCurrentThreadDescription(); | 112 LOGI() << "~dtor" << ios::GetCurrentThreadDescription(); |
| 105 audio_session_observer_ = nil; | 113 audio_session_observer_ = nil; |
| 106 RTC_DCHECK(thread_checker_.CalledOnValidThread()); | 114 RTC_DCHECK(thread_checker_.CalledOnValidThread()); |
| (...skipping 204 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 311 | 319 |
| 312 int AudioDeviceIOS::GetRecordAudioParameters(AudioParameters* params) const { | 320 int AudioDeviceIOS::GetRecordAudioParameters(AudioParameters* params) const { |
| 313 LOGI() << "GetRecordAudioParameters"; | 321 LOGI() << "GetRecordAudioParameters"; |
| 314 RTC_DCHECK(record_parameters_.is_valid()); | 322 RTC_DCHECK(record_parameters_.is_valid()); |
| 315 RTC_DCHECK(thread_checker_.CalledOnValidThread()); | 323 RTC_DCHECK(thread_checker_.CalledOnValidThread()); |
| 316 *params = record_parameters_; | 324 *params = record_parameters_; |
| 317 return 0; | 325 return 0; |
| 318 } | 326 } |
| 319 | 327 |
| 320 void AudioDeviceIOS::OnInterruptionBegin() { | 328 void AudioDeviceIOS::OnInterruptionBegin() { |
| 321 RTC_DCHECK(async_invoker_); | |
| 322 RTC_DCHECK(thread_); | 329 RTC_DCHECK(thread_); |
| 323 if (thread_->IsCurrent()) { | 330 thread_->Post(this, kMessageTypeInterruptionBegin); |
| 324 HandleInterruptionBegin(); | |
| 325 return; | |
| 326 } | |
| 327 async_invoker_->AsyncInvoke<void>( | |
| 328 thread_, | |
| 329 rtc::Bind(&webrtc::AudioDeviceIOS::HandleInterruptionBegin, this)); | |
| 330 } | 331 } |
| 331 | 332 |
| 332 void AudioDeviceIOS::OnInterruptionEnd() { | 333 void AudioDeviceIOS::OnInterruptionEnd() { |
| 333 RTC_DCHECK(async_invoker_); | |
| 334 RTC_DCHECK(thread_); | 334 RTC_DCHECK(thread_); |
| 335 if (thread_->IsCurrent()) { | 335 thread_->Post(this, kMessageTypeInterruptionEnd); |
| 336 HandleInterruptionEnd(); | |
| 337 return; | |
| 338 } | |
| 339 async_invoker_->AsyncInvoke<void>( | |
| 340 thread_, | |
| 341 rtc::Bind(&webrtc::AudioDeviceIOS::HandleInterruptionEnd, this)); | |
| 342 } | 336 } |
| 343 | 337 |
| 344 void AudioDeviceIOS::OnValidRouteChange() { | 338 void AudioDeviceIOS::OnValidRouteChange() { |
| 345 RTC_DCHECK(async_invoker_); | |
| 346 RTC_DCHECK(thread_); | 339 RTC_DCHECK(thread_); |
| 347 if (thread_->IsCurrent()) { | 340 thread_->Post(this, kMessageTypeValidRouteChange); |
| 348 HandleValidRouteChange(); | |
| 349 return; | |
| 350 } | |
| 351 async_invoker_->AsyncInvoke<void>( | |
| 352 thread_, | |
| 353 rtc::Bind(&webrtc::AudioDeviceIOS::HandleValidRouteChange, this)); | |
| 354 } | 341 } |
| 355 | 342 |
| 356 void AudioDeviceIOS::OnConfiguredForWebRTC() { | 343 void AudioDeviceIOS::OnCanPlayOrRecordChange(bool can_play_or_record) { |
| 357 RTC_DCHECK(async_invoker_); | |
| 358 RTC_DCHECK(thread_); | 344 RTC_DCHECK(thread_); |
| 359 if (thread_->IsCurrent()) { | 345 thread_->Post(this, kMessageTypeCanPlayOrRecordChange, |
| 360 HandleValidRouteChange(); | 346 new rtc::TypedMessageData<bool>(can_play_or_record)); |
| 361 return; | |
| 362 } | |
| 363 async_invoker_->AsyncInvoke<void>( | |
| 364 thread_, | |
| 365 rtc::Bind(&webrtc::AudioDeviceIOS::HandleConfiguredForWebRTC, this)); | |
| 366 } | 347 } |
| 367 | 348 |
| 368 OSStatus AudioDeviceIOS::OnDeliverRecordedData( | 349 OSStatus AudioDeviceIOS::OnDeliverRecordedData( |
| 369 AudioUnitRenderActionFlags* flags, | 350 AudioUnitRenderActionFlags* flags, |
| 370 const AudioTimeStamp* time_stamp, | 351 const AudioTimeStamp* time_stamp, |
| 371 UInt32 bus_number, | 352 UInt32 bus_number, |
| 372 UInt32 num_frames, | 353 UInt32 num_frames, |
| 373 AudioBufferList* /* io_data */) { | 354 AudioBufferList* /* io_data */) { |
| 374 OSStatus result = noErr; | 355 OSStatus result = noErr; |
| 375 // Simply return if recording is not enabled. | 356 // Simply return if recording is not enabled. |
| 376 if (!rtc::AtomicOps::AcquireLoad(&recording_)) | 357 if (!rtc::AtomicOps::AcquireLoad(&recording_)) |
| 377 return result; | 358 return result; |
| 378 | 359 |
| 379 size_t frames_per_buffer = record_parameters_.frames_per_buffer(); | 360 size_t frames_per_buffer = record_parameters_.frames_per_buffer(); |
| 380 if (num_frames != frames_per_buffer) { | 361 if (num_frames != frames_per_buffer) { |
| 381 // We have seen short bursts (1-2 frames) where |in_number_frames| changes. | 362 // We have seen short bursts (1-2 frames) where |in_number_frames| changes. |
| 382 // Add a log to keep track of longer sequences if that should ever happen. | 363 // Add a log to keep track of longer sequences if that should ever happen. |
| 383 // Also return since calling AudioUnitRender in this state will only result | 364 // Also return since calling AudioUnitRender in this state will only result |
| 384 // in kAudio_ParamError (-50) anyhow. | 365 // in kAudio_ParamError (-50) anyhow. |
| 385 RTCLogWarning(@"Expected %u frames but got %u", | 366 RTCLogWarning(@"Expected %u frames but got %u", |
| 386 static_cast<unsigned int>(frames_per_buffer), | 367 static_cast<unsigned int>(frames_per_buffer), |
| 387 static_cast<unsigned int>(num_frames)); | 368 static_cast<unsigned int>(num_frames)); |
| 369 | |
| 370 RTCAudioSession *session = [RTCAudioSession sharedInstance]; | |
| 371 RTCLogWarning(@"Session:\n %@", session); | |
| 388 return result; | 372 return result; |
| 389 } | 373 } |
| 390 | 374 |
| 391 // Obtain the recorded audio samples by initiating a rendering cycle. | 375 // Obtain the recorded audio samples by initiating a rendering cycle. |
| 392 // Since it happens on the input bus, the |io_data| parameter is a reference | 376 // Since it happens on the input bus, the |io_data| parameter is a reference |
| 393 // to the preallocated audio buffer list that the audio unit renders into. | 377 // to the preallocated audio buffer list that the audio unit renders into. |
| 394 // We can make the audio unit provide a buffer instead in io_data, but we | 378 // We can make the audio unit provide a buffer instead in io_data, but we |
| 395 // currently just use our own. | 379 // currently just use our own. |
| 396 // TODO(henrika): should error handling be improved? | 380 // TODO(henrika): should error handling be improved? |
| 397 AudioBufferList* io_data = &audio_record_buffer_list_; | 381 AudioBufferList* io_data = &audio_record_buffer_list_; |
| (...skipping 42 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 440 } | 424 } |
| 441 // Read decoded 16-bit PCM samples from WebRTC (using a size that matches | 425 // Read decoded 16-bit PCM samples from WebRTC (using a size that matches |
| 442 // the native I/O audio unit) to a preallocated intermediate buffer and | 426 // the native I/O audio unit) to a preallocated intermediate buffer and |
| 443 // copy the result to the audio buffer in the |io_data| destination. | 427 // copy the result to the audio buffer in the |io_data| destination. |
| 444 int8_t* source = playout_audio_buffer_.get(); | 428 int8_t* source = playout_audio_buffer_.get(); |
| 445 fine_audio_buffer_->GetPlayoutData(source); | 429 fine_audio_buffer_->GetPlayoutData(source); |
| 446 memcpy(destination, source, size_in_bytes); | 430 memcpy(destination, source, size_in_bytes); |
| 447 return noErr; | 431 return noErr; |
| 448 } | 432 } |
| 449 | 433 |
| 434 void AudioDeviceIOS::OnSampleRateChange(float sample_rate) { | |
| 435 RTC_DCHECK(thread_); | |
| 436 thread_->Post(this, kMessageTypeSampleRateChange, | |
| 437 new rtc::TypedMessageData<float>(sample_rate)); | |
| 438 } | |
| 439 | |
| 440 void AudioDeviceIOS::OnMessage(rtc::Message *msg) { | |
|
henrika_webrtc
2016/05/04 12:33:07
nice!
tkchin_webrtc
2016/05/05 23:23:27
Acknowledged.
| |
| 441 switch (msg->message_id) { | |
| 442 case kMessageTypeInterruptionBegin: | |
| 443 HandleInterruptionBegin(); | |
| 444 break; | |
| 445 case kMessageTypeInterruptionEnd: | |
| 446 HandleInterruptionEnd(); | |
| 447 break; | |
| 448 case kMessageTypeValidRouteChange: | |
| 449 HandleValidRouteChange(); | |
| 450 break; | |
| 451 case kMessageTypeCanPlayOrRecordChange: { | |
| 452 rtc::TypedMessageData<bool>* data = | |
| 453 static_cast<rtc::TypedMessageData<bool>*>(msg->pdata); | |
| 454 HandleCanPlayOrRecordChange(data->data()); | |
| 455 delete data; | |
| 456 break; | |
| 457 } | |
| 458 case kMessageTypeSampleRateChange: { | |
| 459 rtc::TypedMessageData<float>* data = | |
| 460 static_cast<rtc::TypedMessageData<float>*>(msg->pdata); | |
| 461 HandleSampleRateChange(data->data()); | |
| 462 delete data; | |
| 463 break; | |
| 464 } | |
| 465 } | |
| 466 } | |
| 467 | |
| 450 void AudioDeviceIOS::HandleInterruptionBegin() { | 468 void AudioDeviceIOS::HandleInterruptionBegin() { |
| 451 RTC_DCHECK(thread_checker_.CalledOnValidThread()); | 469 RTC_DCHECK(thread_checker_.CalledOnValidThread()); |
| 452 | 470 |
| 453 RTCLog(@"Stopping the audio unit due to interruption begin."); | 471 RTCLog(@"Stopping the audio unit due to interruption begin."); |
| 454 if (!audio_unit_->Stop()) { | 472 if (!audio_unit_->Stop()) { |
| 455 RTCLogError(@"Failed to stop the audio unit."); | 473 RTCLogError(@"Failed to stop the audio unit."); |
| 456 } | 474 } |
| 457 is_interrupted_ = true; | 475 is_interrupted_ = true; |
| 458 } | 476 } |
| 459 | 477 |
| 460 void AudioDeviceIOS::HandleInterruptionEnd() { | 478 void AudioDeviceIOS::HandleInterruptionEnd() { |
| 461 RTC_DCHECK(thread_checker_.CalledOnValidThread()); | 479 RTC_DCHECK(thread_checker_.CalledOnValidThread()); |
| 462 | 480 |
| 463 RTCLog(@"Starting the audio unit due to interruption end."); | 481 RTCLog(@"Starting the audio unit due to interruption end."); |
| 464 if (!audio_unit_->Start()) { | 482 if (!audio_unit_->Start()) { |
| 465 RTCLogError(@"Failed to start the audio unit."); | 483 RTCLogError(@"Failed to start the audio unit."); |
| 466 } | 484 } |
| 467 is_interrupted_ = false; | 485 is_interrupted_ = false; |
| 468 } | 486 } |
| 469 | 487 |
| 470 void AudioDeviceIOS::HandleValidRouteChange() { | 488 void AudioDeviceIOS::HandleValidRouteChange() { |
| 471 RTC_DCHECK(thread_checker_.CalledOnValidThread()); | 489 RTC_DCHECK(thread_checker_.CalledOnValidThread()); |
| 472 | 490 |
| 473 // Don't do anything if we're interrupted. | |
| 474 if (is_interrupted_) { | |
| 475 return; | |
| 476 } | |
| 477 | |
| 478 // Only restart audio for a valid route change if the session sample rate | |
| 479 // has changed. | |
| 480 RTCAudioSession* session = [RTCAudioSession sharedInstance]; | 491 RTCAudioSession* session = [RTCAudioSession sharedInstance]; |
| 481 const double current_sample_rate = playout_parameters_.sample_rate(); | 492 HandleSampleRateChange(session.sampleRate); |
| 482 const double session_sample_rate = session.sampleRate; | |
| 483 if (current_sample_rate != session_sample_rate) { | |
| 484 RTCLog(@"Route changed caused sample rate to change from %f to %f. " | |
| 485 "Restarting audio unit.", current_sample_rate, session_sample_rate); | |
| 486 if (!RestartAudioUnit(session_sample_rate)) { | |
| 487 RTCLogError(@"Audio restart failed."); | |
| 488 } | |
| 489 } | |
| 490 } | 493 } |
| 491 | 494 |
| 492 void AudioDeviceIOS::HandleConfiguredForWebRTC() { | 495 void AudioDeviceIOS::HandleCanPlayOrRecordChange(bool can_play_or_record) { |
| 493 RTC_DCHECK(thread_checker_.CalledOnValidThread()); | 496 RTC_DCHECK(thread_checker_.CalledOnValidThread()); |
| 497 RTCLog(@"Handling CanPlayOrRecordChange to %d", can_play_or_record); | |
| 494 | 498 |
| 495 // If we're not initialized we don't need to do anything. Audio unit will | 499 // If we're not initialized we don't need to do anything. Audio unit will |
| 496 // be initialized on initialization. | 500 // be initialized on initialization. |
| 497 if (!rec_is_initialized_ && !play_is_initialized_) | 501 if (!rec_is_initialized_ && !play_is_initialized_) |
| 498 return; | 502 return; |
| 499 | 503 |
| 500 // If we're initialized, we must have an audio unit. | 504 // If we're initialized, we must have an audio unit. |
| 501 RTC_DCHECK(audio_unit_); | 505 RTC_DCHECK(audio_unit_); |
| 502 | 506 |
| 503 // Use configured audio session's settings to set up audio device buffer. | 507 bool should_initialize_audio_unit = false; |
| 504 // TODO(tkchin): Use RTCAudioSessionConfiguration to pick up settings and | 508 bool should_uninitialize_audio_unit = false; |
| 505 // pass it along. | 509 bool should_start_audio_unit = false; |
| 506 SetupAudioBuffersForActiveAudioSession(); | 510 bool should_stop_audio_unit = false; |
| 507 | 511 |
| 508 // Initialize the audio unit. This will affect any existing audio playback. | 512 switch (audio_unit_->GetState()) { |
| 509 if (!audio_unit_->Initialize(playout_parameters_.sample_rate())) { | 513 case VoiceProcessingAudioUnit::kInitRequired: |
| 510 RTCLogError(@"Failed to initialize audio unit after configuration."); | 514 RTC_NOTREACHED(); |
| 515 break; | |
| 516 case VoiceProcessingAudioUnit::kUninitialized: | |
| 517 should_initialize_audio_unit = can_play_or_record; | |
| 518 should_start_audio_unit = should_initialize_audio_unit && | |
| 519 (playing_ || recording_); | |
| 520 break; | |
| 521 case VoiceProcessingAudioUnit::kInitialized: | |
| 522 should_start_audio_unit = can_play_or_record && (playing_ || recording_); | |
| 523 should_uninitialize_audio_unit = !can_play_or_record; | |
| 524 break; | |
| 525 case VoiceProcessingAudioUnit::kStarted: | |
| 526 RTC_DCHECK(playing_ || recording_); | |
| 527 should_stop_audio_unit = !can_play_or_record; | |
| 528 should_uninitialize_audio_unit = should_stop_audio_unit; | |
| 529 break; | |
| 530 } | |
| 531 | |
| 532 if (should_initialize_audio_unit) { | |
| 533 RTCLog(@"Initializing audio unit for CanPlayOrRecordChange"); | |
| 534 // Mad hack: Sometimes the audio session is already active because the app | |
|
henrika_webrtc
2016/05/04 12:33:07
Wow. Do you have a case where you can reliably tes
tkchin_webrtc
2016/05/05 23:23:27
Yup. With my BT headset. I'm backing out the chang
henrika_webrtc
2016/05/06 11:22:16
Acknowledged.
| |
| 535 // activated it. In this condition, the audio session reports an inaccurate | |
| 536 // IOBufferDuration until the audio unit is started, but this causes audio | |
| 537 // artifacts. To work around it, deactivate it forcibly and reactivate it. | |
| 538 // This only works if the app has only activated the session once. | |
| 539 RTCAudioSession *session = [RTCAudioSession sharedInstance]; | |
| 540 BOOL refreshAudioSession = session.isActive; | |
| 541 if (refreshAudioSession) { | |
| 542 [session lockForConfiguration]; | |
| 543 [session setActive:NO error:nil]; | |
| 544 [session unlockForConfiguration]; | |
| 545 } | |
| 546 ConfigureAudioSession(); | |
| 547 if (refreshAudioSession) { | |
| 548 [session lockForConfiguration]; | |
| 549 [session setActive:YES error:nil]; | |
| 550 [session unlockForConfiguration]; | |
| 551 } | |
| 552 SetupAudioBuffersForActiveAudioSession(); | |
| 553 if (!audio_unit_->Initialize(playout_parameters_.sample_rate())) { | |
| 554 RTCLogError(@"Failed to initialize audio unit."); | |
| 555 return; | |
| 556 } | |
| 557 } | |
| 558 | |
| 559 if (should_start_audio_unit) { | |
| 560 RTCLog(@"Starting audio unit for CanPlayOrRecordChange"); | |
| 561 if (!audio_unit_->Start()) { | |
| 562 RTCLogError(@"Failed to start audio unit."); | |
| 563 return; | |
| 564 } | |
| 565 } | |
| 566 | |
| 567 if (should_stop_audio_unit) { | |
| 568 RTCLog(@"Stopping audio unit for CanPlayOrRecordChange"); | |
| 569 if (!audio_unit_->Stop()) { | |
| 570 RTCLogError(@"Failed to stop audio unit."); | |
| 571 return; | |
| 572 } | |
| 573 } | |
| 574 | |
| 575 if (should_uninitialize_audio_unit) { | |
| 576 RTCLog(@"Uninitializing audio unit for CanPlayOrRecordChange"); | |
| 577 audio_unit_->Uninitialize(); | |
| 578 UnconfigureAudioSession(); | |
| 579 } | |
| 580 } | |
| 581 | |
| 582 void AudioDeviceIOS::HandleSampleRateChange(float sample_rate) { | |
| 583 RTC_DCHECK(thread_checker_.CalledOnValidThread()); | |
| 584 | |
| 585 // Don't do anything if we're interrupted. | |
| 586 if (is_interrupted_) { | |
| 511 return; | 587 return; |
| 512 } | 588 } |
| 513 | 589 |
| 514 // If we haven't started playing or recording there's nothing more to do. | 590 RTCAudioSession* session = [RTCAudioSession sharedInstance]; |
| 515 if (!playing_ && !recording_) | 591 const double session_sample_rate = session.sampleRate; |
| 516 return; | 592 const NSTimeInterval session_buffer_duration = session.IOBufferDuration; |
| 593 const size_t session_frames_per_buffer = | |
| 594 static_cast<size_t>(session_sample_rate * session_buffer_duration + .5); | |
| 595 const double current_sample_rate = playout_parameters_.sample_rate(); | |
| 596 const size_t current_frames_per_buffer = | |
| 597 playout_parameters_.frames_per_buffer(); | |
| 598 RTCLog(@"Handling playout sample rate change to: %f\n" | |
| 599 " Session sample rate: %f frames_per_buffer: %lu\n" | |
| 600 " ADM sample rate: %f frames_per_buffer: %lu", | |
| 601 sample_rate, | |
| 602 session_sample_rate, (unsigned long)session_frames_per_buffer, | |
| 603 current_sample_rate, (unsigned long)current_frames_per_buffer);; | |
| 517 | 604 |
| 518 // We are in a play or record state, start the audio unit. | 605 if (current_sample_rate != session_sample_rate || |
| 519 if (!audio_unit_->Start()) { | 606 current_frames_per_buffer != session_frames_per_buffer) { |
| 520 RTCLogError(@"Failed to start audio unit after configuration."); | 607 RTCLog(@"Restarting audio unit due to frames per buffer change."); |
| 521 return; | 608 if (!RestartAudioUnit(sample_rate)) { |
| 609 RTCLogError(@"Audio restart failed."); | |
| 610 } | |
| 522 } | 611 } |
| 523 } | 612 } |
| 524 | 613 |
| 525 void AudioDeviceIOS::UpdateAudioDeviceBuffer() { | 614 void AudioDeviceIOS::UpdateAudioDeviceBuffer() { |
| 526 LOGI() << "UpdateAudioDevicebuffer"; | 615 LOGI() << "UpdateAudioDevicebuffer"; |
| 527 // AttachAudioBuffer() is called at construction by the main class but check | 616 // AttachAudioBuffer() is called at construction by the main class but check |
| 528 // just in case. | 617 // just in case. |
| 529 RTC_DCHECK(audio_device_buffer_) << "AttachAudioBuffer must be called first"; | 618 RTC_DCHECK(audio_device_buffer_) << "AttachAudioBuffer must be called first"; |
| 530 // Inform the audio device buffer (ADB) about the new audio format. | 619 // Inform the audio device buffer (ADB) about the new audio format. |
| 531 audio_device_buffer_->SetPlayoutSampleRate(playout_parameters_.sample_rate()); | 620 audio_device_buffer_->SetPlayoutSampleRate(playout_parameters_.sample_rate()); |
| (...skipping 58 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 590 << required_playout_buffer_size; | 679 << required_playout_buffer_size; |
| 591 playout_audio_buffer_.reset(new SInt8[required_playout_buffer_size]); | 680 playout_audio_buffer_.reset(new SInt8[required_playout_buffer_size]); |
| 592 | 681 |
| 593 // Allocate AudioBuffers to be used as storage for the received audio. | 682 // Allocate AudioBuffers to be used as storage for the received audio. |
| 594 // The AudioBufferList structure works as a placeholder for the | 683 // The AudioBufferList structure works as a placeholder for the |
| 595 // AudioBuffer structure, which holds a pointer to the actual data buffer | 684 // AudioBuffer structure, which holds a pointer to the actual data buffer |
| 596 // in |record_audio_buffer_|. Recorded audio will be rendered into this memory | 685 // in |record_audio_buffer_|. Recorded audio will be rendered into this memory |
| 597 // at each input callback when calling AudioUnitRender(). | 686 // at each input callback when calling AudioUnitRender(). |
| 598 const int data_byte_size = record_parameters_.GetBytesPerBuffer(); | 687 const int data_byte_size = record_parameters_.GetBytesPerBuffer(); |
| 599 record_audio_buffer_.reset(new SInt8[data_byte_size]); | 688 record_audio_buffer_.reset(new SInt8[data_byte_size]); |
| 689 memset(record_audio_buffer_.get(), 0, data_byte_size); | |
| 600 audio_record_buffer_list_.mNumberBuffers = 1; | 690 audio_record_buffer_list_.mNumberBuffers = 1; |
| 601 AudioBuffer* audio_buffer = &audio_record_buffer_list_.mBuffers[0]; | 691 AudioBuffer* audio_buffer = &audio_record_buffer_list_.mBuffers[0]; |
| 602 audio_buffer->mNumberChannels = record_parameters_.channels(); | 692 audio_buffer->mNumberChannels = record_parameters_.channels(); |
| 603 audio_buffer->mDataByteSize = data_byte_size; | 693 audio_buffer->mDataByteSize = data_byte_size; |
| 604 audio_buffer->mData = record_audio_buffer_.get(); | 694 audio_buffer->mData = record_audio_buffer_.get(); |
| 605 } | 695 } |
| 606 | 696 |
| 607 bool AudioDeviceIOS::CreateAudioUnit() { | 697 bool AudioDeviceIOS::CreateAudioUnit() { |
| 608 RTC_DCHECK(!audio_unit_); | 698 RTC_DCHECK(!audio_unit_); |
| 609 | 699 |
| (...skipping 36 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 646 // Restart the audio unit. | 736 // Restart the audio unit. |
| 647 if (!audio_unit_->Start()) { | 737 if (!audio_unit_->Start()) { |
| 648 RTCLogError(@"Failed to start audio unit."); | 738 RTCLogError(@"Failed to start audio unit."); |
| 649 return false; | 739 return false; |
| 650 } | 740 } |
| 651 RTCLog(@"Successfully restarted audio unit."); | 741 RTCLog(@"Successfully restarted audio unit."); |
| 652 | 742 |
| 653 return true; | 743 return true; |
| 654 } | 744 } |
| 655 | 745 |
| 746 void AudioDeviceIOS::ConfigureAudioSession() { | |
| 747 RTC_DCHECK(thread_checker_.CalledOnValidThread()); | |
| 748 RTCLog(@"Configuring audio session."); | |
| 749 if (has_configured_session_) { | |
| 750 RTCLogWarning(@"Audio session already configured."); | |
| 751 return; | |
| 752 } | |
| 753 RTCAudioSession* session = [RTCAudioSession sharedInstance]; | |
| 754 [session lockForConfiguration]; | |
| 755 [session configureWebRTCSession:nil]; | |
| 756 [session unlockForConfiguration]; | |
| 757 has_configured_session_ = true; | |
| 758 RTCLog(@"Configured audio session."); | |
| 759 } | |
| 760 | |
| 761 void AudioDeviceIOS::UnconfigureAudioSession() { | |
| 762 RTC_DCHECK(thread_checker_.CalledOnValidThread()); | |
| 763 RTCLog(@"Unconfiguring audio session."); | |
| 764 if (!has_configured_session_) { | |
| 765 RTCLogWarning(@"Audio session already unconfigured."); | |
| 766 return; | |
| 767 } | |
| 768 RTCAudioSession* session = [RTCAudioSession sharedInstance]; | |
| 769 [session lockForConfiguration]; | |
| 770 [session unconfigureWebRTCSession:nil]; | |
| 771 [session unlockForConfiguration]; | |
| 772 has_configured_session_ = false; | |
| 773 RTCLog(@"Unconfigured audio session."); | |
| 774 } | |
| 775 | |
| 656 bool AudioDeviceIOS::InitPlayOrRecord() { | 776 bool AudioDeviceIOS::InitPlayOrRecord() { |
| 657 LOGI() << "InitPlayOrRecord"; | 777 LOGI() << "InitPlayOrRecord"; |
| 658 | 778 |
| 779 // There should be no audio unit at this point. | |
| 659 if (!CreateAudioUnit()) { | 780 if (!CreateAudioUnit()) { |
| 660 return false; | 781 return false; |
| 661 } | 782 } |
| 662 | 783 |
| 663 RTCAudioSession* session = [RTCAudioSession sharedInstance]; | 784 RTCAudioSession* session = [RTCAudioSession sharedInstance]; |
| 664 // Subscribe to audio session events. | 785 // Subscribe to audio session events. |
| 665 [session pushDelegate:audio_session_observer_]; | 786 [session pushDelegate:audio_session_observer_]; |
| 666 | 787 |
| 667 // Lock the session to make configuration changes. | 788 // Lock the session to make configuration changes. |
| 668 [session lockForConfiguration]; | 789 [session lockForConfiguration]; |
| 669 NSError* error = nil; | 790 NSError* error = nil; |
| 670 if (![session beginWebRTCSession:&error]) { | 791 if (![session beginWebRTCSession:&error]) { |
| 671 [session unlockForConfiguration]; | 792 [session unlockForConfiguration]; |
| 672 RTCLogError(@"Failed to begin WebRTC session: %@", | 793 RTCLogError(@"Failed to begin WebRTC session: %@", |
| 673 error.localizedDescription); | 794 error.localizedDescription); |
| 674 return false; | 795 return false; |
| 675 } | 796 } |
| 676 | 797 |
| 677 // If we are already configured properly, we can initialize the audio unit. | 798 // If we are ready to play or record, initialize the audio unit. |
| 678 if (session.isConfiguredForWebRTC) { | 799 if (session.canPlayOrRecord) { |
| 679 [session unlockForConfiguration]; | 800 ConfigureAudioSession(); |
| 680 SetupAudioBuffersForActiveAudioSession(); | 801 SetupAudioBuffersForActiveAudioSession(); |
| 681 // Audio session has been marked ready for WebRTC so we can initialize the | |
| 682 // audio unit now. | |
| 683 audio_unit_->Initialize(playout_parameters_.sample_rate()); | 802 audio_unit_->Initialize(playout_parameters_.sample_rate()); |
| 684 return true; | |
| 685 } | 803 } |
| 686 | 804 |
| 687 // Release the lock. | 805 // Release the lock. |
| 688 [session unlockForConfiguration]; | 806 [session unlockForConfiguration]; |
| 689 | 807 |
| 690 return true; | 808 return true; |
| 691 } | 809 } |
| 692 | 810 |
| 693 void AudioDeviceIOS::ShutdownPlayOrRecord() { | 811 void AudioDeviceIOS::ShutdownPlayOrRecord() { |
| 694 LOGI() << "ShutdownPlayOrRecord"; | 812 LOGI() << "ShutdownPlayOrRecord"; |
| 695 | 813 |
| 696 // Close and delete the voice-processing I/O unit. | 814 // Close and delete the voice-processing I/O unit. |
| 697 if (audio_unit_) { | 815 audio_unit_.reset(); |
| 698 audio_unit_.reset(); | |
| 699 } | |
| 700 | 816 |
| 817 RTCAudioSession* session = [RTCAudioSession sharedInstance]; | |
| 701 // Remove audio session notification observers. | 818 // Remove audio session notification observers. |
| 702 RTCAudioSession* session = [RTCAudioSession sharedInstance]; | |
| 703 [session removeDelegate:audio_session_observer_]; | 819 [session removeDelegate:audio_session_observer_]; |
| 704 | 820 |
| 705 // All I/O should be stopped or paused prior to deactivating the audio | 821 // All I/O should be stopped or paused prior to deactivating the audio |
| 706 // session, hence we deactivate as last action. | 822 // session, hence we deactivate as last action. |
| 707 [session lockForConfiguration]; | 823 [session lockForConfiguration]; |
| 824 UnconfigureAudioSession(); | |
| 708 [session endWebRTCSession:nil]; | 825 [session endWebRTCSession:nil]; |
| 709 [session unlockForConfiguration]; | 826 [session unlockForConfiguration]; |
| 710 } | 827 } |
| 711 | 828 |
| 712 } // namespace webrtc | 829 } // namespace webrtc |
| OLD | NEW |