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 |