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 173 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
184 } | 184 } |
185 rec_is_initialized_ = true; | 185 rec_is_initialized_ = true; |
186 return 0; | 186 return 0; |
187 } | 187 } |
188 | 188 |
189 int32_t AudioDeviceIOS::StartPlayout() { | 189 int32_t AudioDeviceIOS::StartPlayout() { |
190 LOGI() << "StartPlayout"; | 190 LOGI() << "StartPlayout"; |
191 RTC_DCHECK(thread_checker_.CalledOnValidThread()); | 191 RTC_DCHECK(thread_checker_.CalledOnValidThread()); |
192 RTC_DCHECK(play_is_initialized_); | 192 RTC_DCHECK(play_is_initialized_); |
193 RTC_DCHECK(!playing_); | 193 RTC_DCHECK(!playing_); |
194 fine_audio_buffer_->ResetPlayout(); | 194 if (fine_audio_buffer_) { |
195 if (!recording_) { | 195 fine_audio_buffer_->ResetPlayout(); |
| 196 } |
| 197 if (!recording_ && |
| 198 audio_unit_->GetState() == VoiceProcessingAudioUnit::kInitialized) { |
196 if (!audio_unit_->Start()) { | 199 if (!audio_unit_->Start()) { |
197 RTCLogError(@"StartPlayout failed to start audio unit."); | 200 RTCLogError(@"StartPlayout failed to start audio unit."); |
198 return -1; | 201 return -1; |
199 } | 202 } |
200 LOG(LS_INFO) << "Voice-Processing I/O audio unit is now started"; | 203 LOG(LS_INFO) << "Voice-Processing I/O audio unit is now started"; |
201 } | 204 } |
202 rtc::AtomicOps::ReleaseStore(&playing_, 1); | 205 rtc::AtomicOps::ReleaseStore(&playing_, 1); |
203 return 0; | 206 return 0; |
204 } | 207 } |
205 | 208 |
206 int32_t AudioDeviceIOS::StopPlayout() { | 209 int32_t AudioDeviceIOS::StopPlayout() { |
207 LOGI() << "StopPlayout"; | 210 LOGI() << "StopPlayout"; |
208 RTC_DCHECK(thread_checker_.CalledOnValidThread()); | 211 RTC_DCHECK(thread_checker_.CalledOnValidThread()); |
209 if (!play_is_initialized_ || !playing_) { | 212 if (!play_is_initialized_ || !playing_) { |
210 return 0; | 213 return 0; |
211 } | 214 } |
212 if (!recording_) { | 215 if (!recording_) { |
213 ShutdownPlayOrRecord(); | 216 ShutdownPlayOrRecord(); |
214 } | 217 } |
215 play_is_initialized_ = false; | 218 play_is_initialized_ = false; |
216 rtc::AtomicOps::ReleaseStore(&playing_, 0); | 219 rtc::AtomicOps::ReleaseStore(&playing_, 0); |
217 return 0; | 220 return 0; |
218 } | 221 } |
219 | 222 |
220 int32_t AudioDeviceIOS::StartRecording() { | 223 int32_t AudioDeviceIOS::StartRecording() { |
221 LOGI() << "StartRecording"; | 224 LOGI() << "StartRecording"; |
222 RTC_DCHECK(thread_checker_.CalledOnValidThread()); | 225 RTC_DCHECK(thread_checker_.CalledOnValidThread()); |
223 RTC_DCHECK(rec_is_initialized_); | 226 RTC_DCHECK(rec_is_initialized_); |
224 RTC_DCHECK(!recording_); | 227 RTC_DCHECK(!recording_); |
225 fine_audio_buffer_->ResetRecord(); | 228 if (fine_audio_buffer_) { |
226 if (!playing_) { | 229 fine_audio_buffer_->ResetRecord(); |
| 230 } |
| 231 if (!playing_ && |
| 232 audio_unit_->GetState() == VoiceProcessingAudioUnit::kInitialized) { |
227 if (!audio_unit_->Start()) { | 233 if (!audio_unit_->Start()) { |
228 RTCLogError(@"StartRecording failed to start audio unit."); | 234 RTCLogError(@"StartRecording failed to start audio unit."); |
229 return -1; | 235 return -1; |
230 } | 236 } |
231 LOG(LS_INFO) << "Voice-Processing I/O audio unit is now started"; | 237 LOG(LS_INFO) << "Voice-Processing I/O audio unit is now started"; |
232 } | 238 } |
233 rtc::AtomicOps::ReleaseStore(&recording_, 1); | 239 rtc::AtomicOps::ReleaseStore(&recording_, 1); |
234 return 0; | 240 return 0; |
235 } | 241 } |
236 | 242 |
(...skipping 103 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
340 RTC_DCHECK(thread_); | 346 RTC_DCHECK(thread_); |
341 if (thread_->IsCurrent()) { | 347 if (thread_->IsCurrent()) { |
342 HandleValidRouteChange(); | 348 HandleValidRouteChange(); |
343 return; | 349 return; |
344 } | 350 } |
345 async_invoker_->AsyncInvoke<void>( | 351 async_invoker_->AsyncInvoke<void>( |
346 thread_, | 352 thread_, |
347 rtc::Bind(&webrtc::AudioDeviceIOS::HandleValidRouteChange, this)); | 353 rtc::Bind(&webrtc::AudioDeviceIOS::HandleValidRouteChange, this)); |
348 } | 354 } |
349 | 355 |
| 356 void AudioDeviceIOS::OnConfiguredForWebRTC() { |
| 357 RTC_DCHECK(async_invoker_); |
| 358 RTC_DCHECK(thread_); |
| 359 if (thread_->IsCurrent()) { |
| 360 HandleValidRouteChange(); |
| 361 return; |
| 362 } |
| 363 async_invoker_->AsyncInvoke<void>( |
| 364 thread_, |
| 365 rtc::Bind(&webrtc::AudioDeviceIOS::HandleConfiguredForWebRTC, this)); |
| 366 } |
| 367 |
350 OSStatus AudioDeviceIOS::OnDeliverRecordedData( | 368 OSStatus AudioDeviceIOS::OnDeliverRecordedData( |
351 AudioUnitRenderActionFlags* flags, | 369 AudioUnitRenderActionFlags* flags, |
352 const AudioTimeStamp* time_stamp, | 370 const AudioTimeStamp* time_stamp, |
353 UInt32 bus_number, | 371 UInt32 bus_number, |
354 UInt32 num_frames, | 372 UInt32 num_frames, |
355 AudioBufferList* /* io_data */) { | 373 AudioBufferList* /* io_data */) { |
356 OSStatus result = noErr; | 374 OSStatus result = noErr; |
357 // Simply return if recording is not enabled. | 375 // Simply return if recording is not enabled. |
358 if (!rtc::AtomicOps::AcquireLoad(&recording_)) | 376 if (!rtc::AtomicOps::AcquireLoad(&recording_)) |
359 return result; | 377 return result; |
(...skipping 64 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
424 // the native I/O audio unit) to a preallocated intermediate buffer and | 442 // the native I/O audio unit) to a preallocated intermediate buffer and |
425 // copy the result to the audio buffer in the |io_data| destination. | 443 // copy the result to the audio buffer in the |io_data| destination. |
426 int8_t* source = playout_audio_buffer_.get(); | 444 int8_t* source = playout_audio_buffer_.get(); |
427 fine_audio_buffer_->GetPlayoutData(source); | 445 fine_audio_buffer_->GetPlayoutData(source); |
428 memcpy(destination, source, size_in_bytes); | 446 memcpy(destination, source, size_in_bytes); |
429 return noErr; | 447 return noErr; |
430 } | 448 } |
431 | 449 |
432 void AudioDeviceIOS::HandleInterruptionBegin() { | 450 void AudioDeviceIOS::HandleInterruptionBegin() { |
433 RTC_DCHECK(thread_checker_.CalledOnValidThread()); | 451 RTC_DCHECK(thread_checker_.CalledOnValidThread()); |
| 452 |
434 RTCLog(@"Stopping the audio unit due to interruption begin."); | 453 RTCLog(@"Stopping the audio unit due to interruption begin."); |
435 if (!audio_unit_->Stop()) { | 454 if (!audio_unit_->Stop()) { |
436 RTCLogError(@"Failed to stop the audio unit."); | 455 RTCLogError(@"Failed to stop the audio unit."); |
437 } | 456 } |
438 is_interrupted_ = true; | 457 is_interrupted_ = true; |
439 } | 458 } |
440 | 459 |
441 void AudioDeviceIOS::HandleInterruptionEnd() { | 460 void AudioDeviceIOS::HandleInterruptionEnd() { |
442 RTC_DCHECK(thread_checker_.CalledOnValidThread()); | 461 RTC_DCHECK(thread_checker_.CalledOnValidThread()); |
| 462 |
443 RTCLog(@"Starting the audio unit due to interruption end."); | 463 RTCLog(@"Starting the audio unit due to interruption end."); |
444 if (!audio_unit_->Start()) { | 464 if (!audio_unit_->Start()) { |
445 RTCLogError(@"Failed to start the audio unit."); | 465 RTCLogError(@"Failed to start the audio unit."); |
446 } | 466 } |
447 is_interrupted_ = false; | 467 is_interrupted_ = false; |
448 } | 468 } |
449 | 469 |
450 void AudioDeviceIOS::HandleValidRouteChange() { | 470 void AudioDeviceIOS::HandleValidRouteChange() { |
451 RTC_DCHECK(thread_checker_.CalledOnValidThread()); | 471 RTC_DCHECK(thread_checker_.CalledOnValidThread()); |
452 | 472 |
453 // Don't do anything if we're interrupted. | 473 // Don't do anything if we're interrupted. |
454 if (is_interrupted_) { | 474 if (is_interrupted_) { |
455 return; | 475 return; |
456 } | 476 } |
457 | 477 |
458 // Only restart audio for a valid route change if the session sample rate | 478 // Only restart audio for a valid route change if the session sample rate |
459 // has changed. | 479 // has changed. |
460 RTCAudioSession* session = [RTCAudioSession sharedInstance]; | 480 RTCAudioSession* session = [RTCAudioSession sharedInstance]; |
461 const double current_sample_rate = playout_parameters_.sample_rate(); | 481 const double current_sample_rate = playout_parameters_.sample_rate(); |
462 const double session_sample_rate = session.sampleRate; | 482 const double session_sample_rate = session.sampleRate; |
463 if (current_sample_rate != session_sample_rate) { | 483 if (current_sample_rate != session_sample_rate) { |
464 RTCLog(@"Route changed caused sample rate to change from %f to %f. " | 484 RTCLog(@"Route changed caused sample rate to change from %f to %f. " |
465 "Restarting audio unit.", current_sample_rate, session_sample_rate); | 485 "Restarting audio unit.", current_sample_rate, session_sample_rate); |
466 if (!RestartAudioUnit(session_sample_rate)) { | 486 if (!RestartAudioUnit(session_sample_rate)) { |
467 RTCLogError(@"Audio restart failed."); | 487 RTCLogError(@"Audio restart failed."); |
468 } | 488 } |
469 } | 489 } |
470 } | 490 } |
471 | 491 |
| 492 void AudioDeviceIOS::HandleConfiguredForWebRTC() { |
| 493 RTC_DCHECK(thread_checker_.CalledOnValidThread()); |
| 494 |
| 495 // If we're not initialized we don't need to do anything. Audio unit will |
| 496 // be initialized on initialization. |
| 497 if (!rec_is_initialized_ && !play_is_initialized_) |
| 498 return; |
| 499 |
| 500 // If we're initialized, we must have an audio unit. |
| 501 RTC_DCHECK(audio_unit_); |
| 502 |
| 503 // Use configured audio session's settings to set up audio device buffer. |
| 504 // TODO(tkchin): Use RTCAudioSessionConfiguration to pick up settings and |
| 505 // pass it along. |
| 506 SetupAudioBuffersForActiveAudioSession(); |
| 507 |
| 508 // Initialize the audio unit. This will affect any existing audio playback. |
| 509 if (!audio_unit_->Initialize(playout_parameters_.sample_rate())) { |
| 510 RTCLogError(@"Failed to initialize audio unit after configuration."); |
| 511 return; |
| 512 } |
| 513 |
| 514 // If we haven't started playing or recording there's nothing more to do. |
| 515 if (!playing_ && !recording_) |
| 516 return; |
| 517 |
| 518 // We are in a play or record state, start the audio unit. |
| 519 if (!audio_unit_->Start()) { |
| 520 RTCLogError(@"Failed to start audio unit after configuration."); |
| 521 return; |
| 522 } |
| 523 } |
| 524 |
472 void AudioDeviceIOS::UpdateAudioDeviceBuffer() { | 525 void AudioDeviceIOS::UpdateAudioDeviceBuffer() { |
473 LOGI() << "UpdateAudioDevicebuffer"; | 526 LOGI() << "UpdateAudioDevicebuffer"; |
474 // AttachAudioBuffer() is called at construction by the main class but check | 527 // AttachAudioBuffer() is called at construction by the main class but check |
475 // just in case. | 528 // just in case. |
476 RTC_DCHECK(audio_device_buffer_) << "AttachAudioBuffer must be called first"; | 529 RTC_DCHECK(audio_device_buffer_) << "AttachAudioBuffer must be called first"; |
477 // Inform the audio device buffer (ADB) about the new audio format. | 530 // Inform the audio device buffer (ADB) about the new audio format. |
478 audio_device_buffer_->SetPlayoutSampleRate(playout_parameters_.sample_rate()); | 531 audio_device_buffer_->SetPlayoutSampleRate(playout_parameters_.sample_rate()); |
479 audio_device_buffer_->SetPlayoutChannels(playout_parameters_.channels()); | 532 audio_device_buffer_->SetPlayoutChannels(playout_parameters_.channels()); |
480 audio_device_buffer_->SetRecordingSampleRate( | 533 audio_device_buffer_->SetRecordingSampleRate( |
481 record_parameters_.sample_rate()); | 534 record_parameters_.sample_rate()); |
(...skipping 114 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
596 return false; | 649 return false; |
597 } | 650 } |
598 RTCLog(@"Successfully restarted audio unit."); | 651 RTCLog(@"Successfully restarted audio unit."); |
599 | 652 |
600 return true; | 653 return true; |
601 } | 654 } |
602 | 655 |
603 bool AudioDeviceIOS::InitPlayOrRecord() { | 656 bool AudioDeviceIOS::InitPlayOrRecord() { |
604 LOGI() << "InitPlayOrRecord"; | 657 LOGI() << "InitPlayOrRecord"; |
605 | 658 |
606 // Use the correct audio session configuration for WebRTC. | 659 if (!CreateAudioUnit()) { |
607 // This will attempt to activate the audio session. | |
608 RTCAudioSession* session = [RTCAudioSession sharedInstance]; | |
609 [session lockForConfiguration]; | |
610 NSError* error = nil; | |
611 if (![session configureWebRTCSession:&error]) { | |
612 RTCLogError(@"Failed to configure WebRTC session: %@", | |
613 error.localizedDescription); | |
614 [session unlockForConfiguration]; | |
615 return false; | 660 return false; |
616 } | 661 } |
617 | 662 |
618 // Start observing audio session interruptions and route changes. | 663 RTCAudioSession* session = [RTCAudioSession sharedInstance]; |
| 664 // Subscribe to audio session events. |
619 [session pushDelegate:audio_session_observer_]; | 665 [session pushDelegate:audio_session_observer_]; |
620 | 666 |
621 // Ensure that we got what what we asked for in our active audio session. | 667 // Lock the session to make configuration changes. |
622 SetupAudioBuffersForActiveAudioSession(); | 668 [session lockForConfiguration]; |
623 | 669 NSError* error = nil; |
624 // Create, setup and initialize a new Voice-Processing I/O unit. | 670 if (![session beginWebRTCSession:&error]) { |
625 // TODO(tkchin): Delay the initialization when needed. | |
626 if (!CreateAudioUnit() || | |
627 !audio_unit_->Initialize(playout_parameters_.sample_rate())) { | |
628 [session setActive:NO error:nil]; | |
629 [session unlockForConfiguration]; | 671 [session unlockForConfiguration]; |
| 672 RTCLogError(@"Failed to begin WebRTC session: %@", |
| 673 error.localizedDescription); |
630 return false; | 674 return false; |
631 } | 675 } |
| 676 |
| 677 // If we are already configured properly, we can initialize the audio unit. |
| 678 if (session.isConfiguredForWebRTC) { |
| 679 [session unlockForConfiguration]; |
| 680 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()); |
| 684 return true; |
| 685 } |
| 686 |
| 687 // Release the lock. |
632 [session unlockForConfiguration]; | 688 [session unlockForConfiguration]; |
633 | 689 |
634 return true; | 690 return true; |
635 } | 691 } |
636 | 692 |
637 void AudioDeviceIOS::ShutdownPlayOrRecord() { | 693 void AudioDeviceIOS::ShutdownPlayOrRecord() { |
638 LOGI() << "ShutdownPlayOrRecord"; | 694 LOGI() << "ShutdownPlayOrRecord"; |
639 | 695 |
640 // Close and delete the voice-processing I/O unit. | 696 // Close and delete the voice-processing I/O unit. |
641 if (audio_unit_) { | 697 if (audio_unit_) { |
642 audio_unit_->Stop(); | |
643 audio_unit_->Uninitialize(); | |
644 audio_unit_.reset(); | 698 audio_unit_.reset(); |
645 } | 699 } |
646 | 700 |
647 // Remove audio session notification observers. | 701 // Remove audio session notification observers. |
648 RTCAudioSession* session = [RTCAudioSession sharedInstance]; | 702 RTCAudioSession* session = [RTCAudioSession sharedInstance]; |
649 [session removeDelegate:audio_session_observer_]; | 703 [session removeDelegate:audio_session_observer_]; |
650 | 704 |
651 // All I/O should be stopped or paused prior to deactivating the audio | 705 // All I/O should be stopped or paused prior to deactivating the audio |
652 // session, hence we deactivate as last action. | 706 // session, hence we deactivate as last action. |
653 [session lockForConfiguration]; | 707 [session lockForConfiguration]; |
654 [session setActive:NO error:nil]; | 708 [session endWebRTCSession:nil]; |
655 [session unlockForConfiguration]; | 709 [session unlockForConfiguration]; |
656 } | 710 } |
657 | 711 |
658 } // namespace webrtc | 712 } // namespace webrtc |
OLD | NEW |