1/* 2 * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. 3 * 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 6 * tree. An additional intellectual property rights grant can be found 7 * in the file PATENTS. All contributing project authors may 8 * be found in the AUTHORS file in the root of the source tree. 9 */ 10 11#if !defined(__has_feature) || !__has_feature(objc_arc) 12#error "This file requires ARC support." 13#endif 14 15#import <AVFoundation/AVFoundation.h> 16#import <Foundation/Foundation.h> 17 18#include "webrtc/modules/audio_device/ios/audio_device_ios.h" 19 20#include "webrtc/base/atomicops.h" 21#include "webrtc/base/bind.h" 22#include "webrtc/base/checks.h" 23#include "webrtc/base/criticalsection.h" 24#include "webrtc/base/logging.h" 25#include "webrtc/base/thread.h" 26#include "webrtc/base/thread_annotations.h" 27#include "webrtc/modules/audio_device/fine_audio_buffer.h" 28#include "webrtc/sdk/objc/Framework/Classes/helpers.h" 29 30#import "WebRTC/RTCLogging.h" 31#import "webrtc/modules/audio_device/ios/objc/RTCAudioSession.h" 32#import "webrtc/modules/audio_device/ios/objc/RTCAudioSession+Private.h" 33#import "webrtc/modules/audio_device/ios/objc/RTCAudioSessionConfiguration.h" 34#import "webrtc/modules/audio_device/ios/objc/RTCAudioSessionDelegateAdapter.h" 35 36namespace webrtc { 37 38#define LOGI() LOG(LS_INFO) << "AudioDeviceIOS::" 39 40#define LOG_AND_RETURN_IF_ERROR(error, message) \ 41 do { \ 42 OSStatus err = error; \ 43 if (err) { \ 44 LOG(LS_ERROR) << message << ": " << err; \ 45 return false; \ 46 } \ 47 } while (0) 48 49#define LOG_IF_ERROR(error, message) \ 50 do { \ 51 OSStatus err = error; \ 52 if (err) { \ 53 LOG(LS_ERROR) << message << ": " << err; \ 54 } \ 55 } while (0) 56 57 58// Hardcoded delay estimates based on real measurements. 59// TODO(henrika): these value is not used in combination with built-in AEC. 60// Can most likely be removed. 61const UInt16 kFixedPlayoutDelayEstimate = 30; 62const UInt16 kFixedRecordDelayEstimate = 30; 63 64enum AudioDeviceMessageType : uint32_t { 65 kMessageTypeInterruptionBegin, 66 kMessageTypeInterruptionEnd, 67 kMessageTypeValidRouteChange, 68 kMessageTypeCanPlayOrRecordChange, 69}; 70 71using ios::CheckAndLogError; 72 73#if !defined(NDEBUG) 74// Helper method that logs essential device information strings. 75static void LogDeviceInfo() { 76 LOG(LS_INFO) << "LogDeviceInfo"; 77 @autoreleasepool { 78 LOG(LS_INFO) << " system name: " << ios::GetSystemName(); 79 LOG(LS_INFO) << " system version 1(2): " << ios::GetSystemVersionAsString(); 80 LOG(LS_INFO) << " system version 2(2): " << ios::GetSystemVersion(); 81 LOG(LS_INFO) << " device type: " << ios::GetDeviceType(); 82 LOG(LS_INFO) << " device name: " << ios::GetDeviceName(); 83 LOG(LS_INFO) << " process name: " << ios::GetProcessName(); 84 LOG(LS_INFO) << " process ID: " << ios::GetProcessID(); 85 LOG(LS_INFO) << " OS version: " << ios::GetOSVersionString(); 86 LOG(LS_INFO) << " processing cores: " << ios::GetProcessorCount(); 87#if defined(__IPHONE_9_0) && defined(__IPHONE_OS_VERSION_MAX_ALLOWED) \ 88 && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_9_0 89 LOG(LS_INFO) << " low power mode: " << ios::GetLowPowerModeEnabled(); 90#endif 91 } 92} 93#endif // !defined(NDEBUG) 94 95AudioDeviceIOS::AudioDeviceIOS() 96 : audio_device_buffer_(nullptr), 97 audio_unit_(nullptr), 98 recording_(0), 99 playing_(0), 100 initialized_(false), 101 audio_is_initialized_(false), 102 is_interrupted_(false), 103 has_configured_session_(false) { 104 LOGI() << "ctor" << ios::GetCurrentThreadDescription(); 105 thread_ = rtc::Thread::Current(); 106 audio_session_observer_ = 107 [[RTCAudioSessionDelegateAdapter alloc] initWithObserver:this]; 108} 109 110AudioDeviceIOS::~AudioDeviceIOS() { 111 LOGI() << "~dtor" << ios::GetCurrentThreadDescription(); 112 audio_session_observer_ = nil; 113 RTC_DCHECK(thread_checker_.CalledOnValidThread()); 114 Terminate(); 115} 116 117void AudioDeviceIOS::AttachAudioBuffer(AudioDeviceBuffer* audioBuffer) { 118 LOGI() << "AttachAudioBuffer"; 119 RTC_DCHECK(audioBuffer); 120 RTC_DCHECK(thread_checker_.CalledOnValidThread()); 121 audio_device_buffer_ = audioBuffer; 122} 123 124AudioDeviceGeneric::InitStatus AudioDeviceIOS::Init() { 125 LOGI() << "Init"; 126 RTC_DCHECK(thread_checker_.CalledOnValidThread()); 127 if (initialized_) { 128 return InitStatus::OK; 129 } 130#if !defined(NDEBUG) 131 LogDeviceInfo(); 132#endif 133 // Store the preferred sample rate and preferred number of channels already 134 // here. They have not been set and confirmed yet since configureForWebRTC 135 // is not called until audio is about to start. However, it makes sense to 136 // store the parameters now and then verify at a later stage. 137 RTCAudioSessionConfiguration* config = 138 [RTCAudioSessionConfiguration webRTCConfiguration]; 139 playout_parameters_.reset(config.sampleRate, 140 config.outputNumberOfChannels); 141 record_parameters_.reset(config.sampleRate, 142 config.inputNumberOfChannels); 143 // Ensure that the audio device buffer (ADB) knows about the internal audio 144 // parameters. Note that, even if we are unable to get a mono audio session, 145 // we will always tell the I/O audio unit to do a channel format conversion 146 // to guarantee mono on the "input side" of the audio unit. 147 UpdateAudioDeviceBuffer(); 148 initialized_ = true; 149 return InitStatus::OK; 150} 151 152int32_t AudioDeviceIOS::Terminate() { 153 LOGI() << "Terminate"; 154 RTC_DCHECK(thread_checker_.CalledOnValidThread()); 155 if (!initialized_) { 156 return 0; 157 } 158 StopPlayout(); 159 StopRecording(); 160 initialized_ = false; 161 return 0; 162} 163 164int32_t AudioDeviceIOS::InitPlayout() { 165 LOGI() << "InitPlayout"; 166 RTC_DCHECK(thread_checker_.CalledOnValidThread()); 167 RTC_DCHECK(initialized_); 168 RTC_DCHECK(!audio_is_initialized_); 169 RTC_DCHECK(!playing_); 170 if (!audio_is_initialized_) { 171 if (!InitPlayOrRecord()) { 172 LOG_F(LS_ERROR) << "InitPlayOrRecord failed for InitPlayout!"; 173 return -1; 174 } 175 } 176 audio_is_initialized_ = true; 177 return 0; 178} 179 180int32_t AudioDeviceIOS::InitRecording() { 181 LOGI() << "InitRecording"; 182 RTC_DCHECK(thread_checker_.CalledOnValidThread()); 183 RTC_DCHECK(initialized_); 184 RTC_DCHECK(!audio_is_initialized_); 185 RTC_DCHECK(!recording_); 186 if (!audio_is_initialized_) { 187 if (!InitPlayOrRecord()) { 188 LOG_F(LS_ERROR) << "InitPlayOrRecord failed for InitRecording!"; 189 return -1; 190 } 191 } 192 audio_is_initialized_ = true; 193 return 0; 194} 195 196int32_t AudioDeviceIOS::StartPlayout() { 197 LOGI() << "StartPlayout"; 198 RTC_DCHECK(thread_checker_.CalledOnValidThread()); 199 RTC_DCHECK(audio_is_initialized_); 200 RTC_DCHECK(!playing_); 201 RTC_DCHECK(audio_unit_); 202 if (fine_audio_buffer_) { 203 fine_audio_buffer_->ResetPlayout(); 204 } 205 if (!recording_ && 206 audio_unit_->GetState() == VoiceProcessingAudioUnit::kInitialized) { 207 if (!audio_unit_->Start()) { 208 RTCLogError(@"StartPlayout failed to start audio unit."); 209 return -1; 210 } 211 LOG(LS_INFO) << "Voice-Processing I/O audio unit is now started"; 212 } 213 rtc::AtomicOps::ReleaseStore(&playing_, 1); 214 return 0; 215} 216 217int32_t AudioDeviceIOS::StopPlayout() { 218 LOGI() << "StopPlayout"; 219 RTC_DCHECK(thread_checker_.CalledOnValidThread()); 220 if (!audio_is_initialized_ || !playing_) { 221 return 0; 222 } 223 if (!recording_) { 224 ShutdownPlayOrRecord(); 225 audio_is_initialized_ = false; 226 } 227 rtc::AtomicOps::ReleaseStore(&playing_, 0); 228 return 0; 229} 230 231int32_t AudioDeviceIOS::StartRecording() { 232 LOGI() << "StartRecording"; 233 RTC_DCHECK(thread_checker_.CalledOnValidThread()); 234 RTC_DCHECK(audio_is_initialized_); 235 RTC_DCHECK(!recording_); 236 RTC_DCHECK(audio_unit_); 237 if (fine_audio_buffer_) { 238 fine_audio_buffer_->ResetRecord(); 239 } 240 if (!playing_ && 241 audio_unit_->GetState() == VoiceProcessingAudioUnit::kInitialized) { 242 if (!audio_unit_->Start()) { 243 RTCLogError(@"StartRecording failed to start audio unit."); 244 return -1; 245 } 246 LOG(LS_INFO) << "Voice-Processing I/O audio unit is now started"; 247 } 248 rtc::AtomicOps::ReleaseStore(&recording_, 1); 249 return 0; 250} 251 252int32_t AudioDeviceIOS::StopRecording() { 253 LOGI() << "StopRecording"; 254 RTC_DCHECK(thread_checker_.CalledOnValidThread()); 255 if (!audio_is_initialized_ || !recording_) { 256 return 0; 257 } 258 if (!playing_) { 259 ShutdownPlayOrRecord(); 260 audio_is_initialized_ = false; 261 } 262 rtc::AtomicOps::ReleaseStore(&recording_, 0); 263 return 0; 264} 265 266// Change the default receiver playout route to speaker. 267int32_t AudioDeviceIOS::SetLoudspeakerStatus(bool enable) { 268 LOGI() << "SetLoudspeakerStatus(" << enable << ")"; 269 270 RTCAudioSession* session = [RTCAudioSession sharedInstance]; 271 [session lockForConfiguration]; 272 NSString* category = session.category; 273 AVAudioSessionCategoryOptions options = session.categoryOptions; 274 // Respect old category options if category is 275 // AVAudioSessionCategoryPlayAndRecord. Otherwise reset it since old options 276 // might not be valid for this category. 277 if ([category isEqualToString:AVAudioSessionCategoryPlayAndRecord]) { 278 if (enable) { 279 options |= AVAudioSessionCategoryOptionDefaultToSpeaker; 280 } else { 281 options &= ~AVAudioSessionCategoryOptionDefaultToSpeaker; 282 } 283 } else { 284 options = AVAudioSessionCategoryOptionDefaultToSpeaker; 285 } 286 NSError* error = nil; 287 BOOL success = [session setCategory:AVAudioSessionCategoryPlayAndRecord 288 withOptions:options 289 error:&error]; 290 ios::CheckAndLogError(success, error); 291 [session unlockForConfiguration]; 292 return (error == nil) ? 0 : -1; 293} 294 295int32_t AudioDeviceIOS::GetLoudspeakerStatus(bool& enabled) const { 296 LOGI() << "GetLoudspeakerStatus"; 297 RTCAudioSession* session = [RTCAudioSession sharedInstance]; 298 AVAudioSessionCategoryOptions options = session.categoryOptions; 299 enabled = options & AVAudioSessionCategoryOptionDefaultToSpeaker; 300 return 0; 301} 302 303int32_t AudioDeviceIOS::PlayoutDelay(uint16_t& delayMS) const { 304 delayMS = kFixedPlayoutDelayEstimate; 305 return 0; 306} 307 308int32_t AudioDeviceIOS::RecordingDelay(uint16_t& delayMS) const { 309 delayMS = kFixedRecordDelayEstimate; 310 return 0; 311} 312 313int AudioDeviceIOS::GetPlayoutAudioParameters(AudioParameters* params) const { 314 LOGI() << "GetPlayoutAudioParameters"; 315 RTC_DCHECK(playout_parameters_.is_valid()); 316 RTC_DCHECK(thread_checker_.CalledOnValidThread()); 317 *params = playout_parameters_; 318 return 0; 319} 320 321int AudioDeviceIOS::GetRecordAudioParameters(AudioParameters* params) const { 322 LOGI() << "GetRecordAudioParameters"; 323 RTC_DCHECK(record_parameters_.is_valid()); 324 RTC_DCHECK(thread_checker_.CalledOnValidThread()); 325 *params = record_parameters_; 326 return 0; 327} 328 329void AudioDeviceIOS::OnInterruptionBegin() { 330 RTC_DCHECK(thread_); 331 thread_->Post(RTC_FROM_HERE, this, kMessageTypeInterruptionBegin); 332} 333 334void AudioDeviceIOS::OnInterruptionEnd() { 335 RTC_DCHECK(thread_); 336 thread_->Post(RTC_FROM_HERE, this, kMessageTypeInterruptionEnd); 337} 338 339void AudioDeviceIOS::OnValidRouteChange() { 340 RTC_DCHECK(thread_); 341 thread_->Post(RTC_FROM_HERE, this, kMessageTypeValidRouteChange); 342} 343 344void AudioDeviceIOS::OnCanPlayOrRecordChange(bool can_play_or_record) { 345 RTC_DCHECK(thread_); 346 thread_->Post(RTC_FROM_HERE, this, kMessageTypeCanPlayOrRecordChange, 347 new rtc::TypedMessageData<bool>(can_play_or_record)); 348} 349 350OSStatus AudioDeviceIOS::OnDeliverRecordedData( 351 AudioUnitRenderActionFlags* flags, 352 const AudioTimeStamp* time_stamp, 353 UInt32 bus_number, 354 UInt32 num_frames, 355 AudioBufferList* /* io_data */) { 356 OSStatus result = noErr; 357 // Simply return if recording is not enabled. 358 if (!rtc::AtomicOps::AcquireLoad(&recording_)) 359 return result; 360 361 size_t frames_per_buffer = record_parameters_.frames_per_buffer(); 362 if (num_frames != frames_per_buffer) { 363 // We have seen short bursts (1-2 frames) where |in_number_frames| changes. 364 // Add a log to keep track of longer sequences if that should ever happen. 365 // Also return since calling AudioUnitRender in this state will only result 366 // in kAudio_ParamError (-50) anyhow. 367 RTCLogWarning(@"Expected %u frames but got %u", 368 static_cast<unsigned int>(frames_per_buffer), 369 static_cast<unsigned int>(num_frames)); 370 371 RTCAudioSession *session = [RTCAudioSession sharedInstance]; 372 RTCLogWarning(@"Session:\n %@", session); 373 return result; 374 } 375 376 // Obtain the recorded audio samples by initiating a rendering cycle. 377 // Since it happens on the input bus, the |io_data| parameter is a reference 378 // to the preallocated audio buffer list that the audio unit renders into. 379 // We can make the audio unit provide a buffer instead in io_data, but we 380 // currently just use our own. 381 // TODO(henrika): should error handling be improved? 382 AudioBufferList* io_data = &audio_record_buffer_list_; 383 result = 384 audio_unit_->Render(flags, time_stamp, bus_number, num_frames, io_data); 385 if (result != noErr) { 386 RTCLogError(@"Failed to render audio."); 387 return result; 388 } 389 390 // Get a pointer to the recorded audio and send it to the WebRTC ADB. 391 // Use the FineAudioBuffer instance to convert between native buffer size 392 // and the 10ms buffer size used by WebRTC. 393 AudioBuffer* audio_buffer = &io_data->mBuffers[0]; 394 const size_t size_in_bytes = audio_buffer->mDataByteSize; 395 RTC_CHECK_EQ(size_in_bytes / VoiceProcessingAudioUnit::kBytesPerSample, 396 num_frames); 397 int8_t* data = static_cast<int8_t*>(audio_buffer->mData); 398 fine_audio_buffer_->DeliverRecordedData(data, size_in_bytes, 399 kFixedPlayoutDelayEstimate, 400 kFixedRecordDelayEstimate); 401 return noErr; 402} 403 404OSStatus AudioDeviceIOS::OnGetPlayoutData(AudioUnitRenderActionFlags* flags, 405 const AudioTimeStamp* time_stamp, 406 UInt32 bus_number, 407 UInt32 num_frames, 408 AudioBufferList* io_data) { 409 // Verify 16-bit, noninterleaved mono PCM signal format. 410 RTC_DCHECK_EQ(1, io_data->mNumberBuffers); 411 AudioBuffer* audio_buffer = &io_data->mBuffers[0]; 412 RTC_DCHECK_EQ(1, audio_buffer->mNumberChannels); 413 // Get pointer to internal audio buffer to which new audio data shall be 414 // written. 415 const size_t size_in_bytes = audio_buffer->mDataByteSize; 416 RTC_CHECK_EQ(size_in_bytes / VoiceProcessingAudioUnit::kBytesPerSample, 417 num_frames); 418 int8_t* destination = reinterpret_cast<int8_t*>(audio_buffer->mData); 419 // Produce silence and give audio unit a hint about it if playout is not 420 // activated. 421 if (!rtc::AtomicOps::AcquireLoad(&playing_)) { 422 *flags |= kAudioUnitRenderAction_OutputIsSilence; 423 memset(destination, 0, size_in_bytes); 424 return noErr; 425 } 426 // Produce silence and log a warning message for the case when Core Audio is 427 // asking for an invalid number of audio frames. I don't expect this to happen 428 // but it is done as a safety measure to avoid bad audio if such as case would 429 // ever be triggered e.g. in combination with BT devices. 430 const size_t frames_per_buffer = playout_parameters_.frames_per_buffer(); 431 if (num_frames != frames_per_buffer) { 432 RTCLogWarning(@"Expected %u frames but got %u", 433 static_cast<unsigned int>(frames_per_buffer), 434 static_cast<unsigned int>(num_frames)); 435 *flags |= kAudioUnitRenderAction_OutputIsSilence; 436 memset(destination, 0, size_in_bytes); 437 return noErr; 438 } 439 440 // Read decoded 16-bit PCM samples from WebRTC (using a size that matches 441 // the native I/O audio unit) to a preallocated intermediate buffer and 442 // copy the result to the audio buffer in the |io_data| destination. 443 int8_t* source = playout_audio_buffer_.get(); 444 fine_audio_buffer_->GetPlayoutData(source); 445 memcpy(destination, source, size_in_bytes); 446 return noErr; 447} 448 449void AudioDeviceIOS::OnMessage(rtc::Message *msg) { 450 switch (msg->message_id) { 451 case kMessageTypeInterruptionBegin: 452 HandleInterruptionBegin(); 453 break; 454 case kMessageTypeInterruptionEnd: 455 HandleInterruptionEnd(); 456 break; 457 case kMessageTypeValidRouteChange: 458 HandleValidRouteChange(); 459 break; 460 case kMessageTypeCanPlayOrRecordChange: { 461 rtc::TypedMessageData<bool>* data = 462 static_cast<rtc::TypedMessageData<bool>*>(msg->pdata); 463 HandleCanPlayOrRecordChange(data->data()); 464 delete data; 465 break; 466 } 467 } 468} 469 470void AudioDeviceIOS::HandleInterruptionBegin() { 471 RTC_DCHECK(thread_checker_.CalledOnValidThread()); 472 473 if (audio_unit_ && 474 audio_unit_->GetState() == VoiceProcessingAudioUnit::kStarted) { 475 RTCLog(@"Stopping the audio unit due to interruption begin."); 476 if (!audio_unit_->Stop()) { 477 RTCLogError(@"Failed to stop the audio unit for interruption begin."); 478 } 479 } 480 is_interrupted_ = true; 481} 482 483void AudioDeviceIOS::HandleInterruptionEnd() { 484 RTC_DCHECK(thread_checker_.CalledOnValidThread()); 485 486 is_interrupted_ = false; 487 RTCLog(@"Interruption ended. Updating audio unit state."); 488 UpdateAudioUnit([RTCAudioSession sharedInstance].canPlayOrRecord); 489} 490 491void AudioDeviceIOS::HandleValidRouteChange() { 492 RTC_DCHECK(thread_checker_.CalledOnValidThread()); 493 494 RTCAudioSession* session = [RTCAudioSession sharedInstance]; 495 RTCLog(@"%@", session); 496 HandleSampleRateChange(session.sampleRate); 497} 498 499void AudioDeviceIOS::HandleCanPlayOrRecordChange(bool can_play_or_record) { 500 RTCLog(@"Handling CanPlayOrRecord change to: %d", can_play_or_record); 501 UpdateAudioUnit(can_play_or_record); 502} 503 504void AudioDeviceIOS::HandleSampleRateChange(float sample_rate) { 505 RTC_DCHECK(thread_checker_.CalledOnValidThread()); 506 RTCLog(@"Handling sample rate change to %f.", sample_rate); 507 508 // Don't do anything if we're interrupted. 509 if (is_interrupted_) { 510 RTCLog(@"Ignoring sample rate change to %f due to interruption.", 511 sample_rate); 512 return; 513 } 514 515 // If we don't have an audio unit yet, or the audio unit is uninitialized, 516 // there is no work to do. 517 if (!audio_unit_ || 518 audio_unit_->GetState() < VoiceProcessingAudioUnit::kInitialized) { 519 return; 520 } 521 522 // The audio unit is already initialized or started. 523 // Check to see if the sample rate or buffer size has changed. 524 RTCAudioSession* session = [RTCAudioSession sharedInstance]; 525 const double session_sample_rate = session.sampleRate; 526 const NSTimeInterval session_buffer_duration = session.IOBufferDuration; 527 const size_t session_frames_per_buffer = 528 static_cast<size_t>(session_sample_rate * session_buffer_duration + .5); 529 const double current_sample_rate = playout_parameters_.sample_rate(); 530 const size_t current_frames_per_buffer = 531 playout_parameters_.frames_per_buffer(); 532 RTCLog(@"Handling playout sample rate change to: %f\n" 533 " Session sample rate: %f frames_per_buffer: %lu\n" 534 " ADM sample rate: %f frames_per_buffer: %lu", 535 sample_rate, 536 session_sample_rate, (unsigned long)session_frames_per_buffer, 537 current_sample_rate, (unsigned long)current_frames_per_buffer);; 538 539 // Sample rate and buffer size are the same, no work to do. 540 if (std::abs(current_sample_rate - session_sample_rate) <= DBL_EPSILON && 541 current_frames_per_buffer == session_frames_per_buffer) { 542 return; 543 } 544 545 // We need to adjust our format and buffer sizes. 546 // The stream format is about to be changed and it requires that we first 547 // stop and uninitialize the audio unit to deallocate its resources. 548 RTCLog(@"Stopping and uninitializing audio unit to adjust buffers."); 549 bool restart_audio_unit = false; 550 if (audio_unit_->GetState() == VoiceProcessingAudioUnit::kStarted) { 551 audio_unit_->Stop(); 552 restart_audio_unit = true; 553 } 554 if (audio_unit_->GetState() == VoiceProcessingAudioUnit::kInitialized) { 555 audio_unit_->Uninitialize(); 556 } 557 558 // Allocate new buffers given the new stream format. 559 SetupAudioBuffersForActiveAudioSession(); 560 561 // Initialize the audio unit again with the new sample rate. 562 RTC_DCHECK_EQ(playout_parameters_.sample_rate(), session_sample_rate); 563 if (!audio_unit_->Initialize(session_sample_rate)) { 564 RTCLogError(@"Failed to initialize the audio unit with sample rate: %f", 565 session_sample_rate); 566 return; 567 } 568 569 // Restart the audio unit if it was already running. 570 if (restart_audio_unit && !audio_unit_->Start()) { 571 RTCLogError(@"Failed to start audio unit with sample rate: %f", 572 session_sample_rate); 573 return; 574 } 575 RTCLog(@"Successfully handled sample rate change."); 576} 577 578void AudioDeviceIOS::UpdateAudioDeviceBuffer() { 579 LOGI() << "UpdateAudioDevicebuffer"; 580 // AttachAudioBuffer() is called at construction by the main class but check 581 // just in case. 582 RTC_DCHECK(audio_device_buffer_) << "AttachAudioBuffer must be called first"; 583 // Inform the audio device buffer (ADB) about the new audio format. 584 audio_device_buffer_->SetPlayoutSampleRate(playout_parameters_.sample_rate()); 585 audio_device_buffer_->SetPlayoutChannels(playout_parameters_.channels()); 586 audio_device_buffer_->SetRecordingSampleRate( 587 record_parameters_.sample_rate()); 588 audio_device_buffer_->SetRecordingChannels(record_parameters_.channels()); 589} 590 591void AudioDeviceIOS::SetupAudioBuffersForActiveAudioSession() { 592 LOGI() << "SetupAudioBuffersForActiveAudioSession"; 593 // Verify the current values once the audio session has been activated. 594 RTCAudioSession* session = [RTCAudioSession sharedInstance]; 595 double sample_rate = session.sampleRate; 596 NSTimeInterval io_buffer_duration = session.IOBufferDuration; 597 RTCLog(@"%@", session); 598 599 // Log a warning message for the case when we are unable to set the preferred 600 // hardware sample rate but continue and use the non-ideal sample rate after 601 // reinitializing the audio parameters. Most BT headsets only support 8kHz or 602 // 16kHz. 603 RTCAudioSessionConfiguration* webRTCConfig = 604 [RTCAudioSessionConfiguration webRTCConfiguration]; 605 if (sample_rate != webRTCConfig.sampleRate) { 606 LOG(LS_WARNING) << "Unable to set the preferred sample rate"; 607 } 608 609 // At this stage, we also know the exact IO buffer duration and can add 610 // that info to the existing audio parameters where it is converted into 611 // number of audio frames. 612 // Example: IO buffer size = 0.008 seconds <=> 128 audio frames at 16kHz. 613 // Hence, 128 is the size we expect to see in upcoming render callbacks. 614 playout_parameters_.reset(sample_rate, playout_parameters_.channels(), 615 io_buffer_duration); 616 RTC_DCHECK(playout_parameters_.is_complete()); 617 record_parameters_.reset(sample_rate, record_parameters_.channels(), 618 io_buffer_duration); 619 RTC_DCHECK(record_parameters_.is_complete()); 620 LOG(LS_INFO) << " frames per I/O buffer: " 621 << playout_parameters_.frames_per_buffer(); 622 LOG(LS_INFO) << " bytes per I/O buffer: " 623 << playout_parameters_.GetBytesPerBuffer(); 624 RTC_DCHECK_EQ(playout_parameters_.GetBytesPerBuffer(), 625 record_parameters_.GetBytesPerBuffer()); 626 627 // Update the ADB parameters since the sample rate might have changed. 628 UpdateAudioDeviceBuffer(); 629 630 // Create a modified audio buffer class which allows us to ask for, 631 // or deliver, any number of samples (and not only multiple of 10ms) to match 632 // the native audio unit buffer size. 633 RTC_DCHECK(audio_device_buffer_); 634 fine_audio_buffer_.reset(new FineAudioBuffer( 635 audio_device_buffer_, playout_parameters_.GetBytesPerBuffer(), 636 playout_parameters_.sample_rate())); 637 638 // The extra/temporary playoutbuffer must be of this size to avoid 639 // unnecessary memcpy while caching data between successive callbacks. 640 const int required_playout_buffer_size = 641 fine_audio_buffer_->RequiredPlayoutBufferSizeBytes(); 642 LOG(LS_INFO) << " required playout buffer size: " 643 << required_playout_buffer_size; 644 playout_audio_buffer_.reset(new SInt8[required_playout_buffer_size]); 645 646 // Allocate AudioBuffers to be used as storage for the received audio. 647 // The AudioBufferList structure works as a placeholder for the 648 // AudioBuffer structure, which holds a pointer to the actual data buffer 649 // in |record_audio_buffer_|. Recorded audio will be rendered into this memory 650 // at each input callback when calling AudioUnitRender(). 651 const int data_byte_size = record_parameters_.GetBytesPerBuffer(); 652 record_audio_buffer_.reset(new SInt8[data_byte_size]); 653 memset(record_audio_buffer_.get(), 0, data_byte_size); 654 audio_record_buffer_list_.mNumberBuffers = 1; 655 AudioBuffer* audio_buffer = &audio_record_buffer_list_.mBuffers[0]; 656 audio_buffer->mNumberChannels = record_parameters_.channels(); 657 audio_buffer->mDataByteSize = data_byte_size; 658 audio_buffer->mData = record_audio_buffer_.get(); 659} 660 661bool AudioDeviceIOS::CreateAudioUnit() { 662 RTC_DCHECK(!audio_unit_); 663 664 audio_unit_.reset(new VoiceProcessingAudioUnit(this)); 665 if (!audio_unit_->Init()) { 666 audio_unit_.reset(); 667 return false; 668 } 669 670 return true; 671} 672 673void AudioDeviceIOS::UpdateAudioUnit(bool can_play_or_record) { 674 RTC_DCHECK(thread_checker_.CalledOnValidThread()); 675 RTCLog(@"Updating audio unit state. CanPlayOrRecord=%d IsInterrupted=%d", 676 can_play_or_record, is_interrupted_); 677 678 if (is_interrupted_) { 679 RTCLog(@"Ignoring audio unit update due to interruption."); 680 return; 681 } 682 683 // If we're not initialized we don't need to do anything. Audio unit will 684 // be initialized on initialization. 685 if (!audio_is_initialized_) 686 return; 687 688 // If we're initialized, we must have an audio unit. 689 RTC_DCHECK(audio_unit_); 690 691 bool should_initialize_audio_unit = false; 692 bool should_uninitialize_audio_unit = false; 693 bool should_start_audio_unit = false; 694 bool should_stop_audio_unit = false; 695 696 switch (audio_unit_->GetState()) { 697 case VoiceProcessingAudioUnit::kInitRequired: 698 RTCLog(@"VPAU state: InitRequired"); 699 RTC_NOTREACHED(); 700 break; 701 case VoiceProcessingAudioUnit::kUninitialized: 702 RTCLog(@"VPAU state: Uninitialized"); 703 should_initialize_audio_unit = can_play_or_record; 704 should_start_audio_unit = should_initialize_audio_unit && 705 (playing_ || recording_); 706 break; 707 case VoiceProcessingAudioUnit::kInitialized: 708 RTCLog(@"VPAU state: Initialized"); 709 should_start_audio_unit = 710 can_play_or_record && (playing_ || recording_); 711 should_uninitialize_audio_unit = !can_play_or_record; 712 break; 713 case VoiceProcessingAudioUnit::kStarted: 714 RTCLog(@"VPAU state: Started"); 715 RTC_DCHECK(playing_ || recording_); 716 should_stop_audio_unit = !can_play_or_record; 717 should_uninitialize_audio_unit = should_stop_audio_unit; 718 break; 719 } 720 721 if (should_initialize_audio_unit) { 722 RTCLog(@"Initializing audio unit for UpdateAudioUnit"); 723 ConfigureAudioSession(); 724 SetupAudioBuffersForActiveAudioSession(); 725 if (!audio_unit_->Initialize(playout_parameters_.sample_rate())) { 726 RTCLogError(@"Failed to initialize audio unit."); 727 return; 728 } 729 } 730 731 if (should_start_audio_unit) { 732 RTCLog(@"Starting audio unit for UpdateAudioUnit"); 733 // Log session settings before trying to start audio streaming. 734 RTCAudioSession* session = [RTCAudioSession sharedInstance]; 735 RTCLog(@"%@", session); 736 if (!audio_unit_->Start()) { 737 RTCLogError(@"Failed to start audio unit."); 738 return; 739 } 740 } 741 742 if (should_stop_audio_unit) { 743 RTCLog(@"Stopping audio unit for UpdateAudioUnit"); 744 if (!audio_unit_->Stop()) { 745 RTCLogError(@"Failed to stop audio unit."); 746 return; 747 } 748 } 749 750 if (should_uninitialize_audio_unit) { 751 RTCLog(@"Uninitializing audio unit for UpdateAudioUnit"); 752 audio_unit_->Uninitialize(); 753 UnconfigureAudioSession(); 754 } 755} 756 757void AudioDeviceIOS::ConfigureAudioSession() { 758 RTC_DCHECK(thread_checker_.CalledOnValidThread()); 759 RTCLog(@"Configuring audio session."); 760 if (has_configured_session_) { 761 RTCLogWarning(@"Audio session already configured."); 762 return; 763 } 764 RTCAudioSession* session = [RTCAudioSession sharedInstance]; 765 [session lockForConfiguration]; 766 [session configureWebRTCSession:nil]; 767 [session unlockForConfiguration]; 768 has_configured_session_ = true; 769 RTCLog(@"Configured audio session."); 770} 771 772void AudioDeviceIOS::UnconfigureAudioSession() { 773 RTC_DCHECK(thread_checker_.CalledOnValidThread()); 774 RTCLog(@"Unconfiguring audio session."); 775 if (!has_configured_session_) { 776 RTCLogWarning(@"Audio session already unconfigured."); 777 return; 778 } 779 RTCAudioSession* session = [RTCAudioSession sharedInstance]; 780 [session lockForConfiguration]; 781 [session unconfigureWebRTCSession:nil]; 782 [session unlockForConfiguration]; 783 has_configured_session_ = false; 784 RTCLog(@"Unconfigured audio session."); 785} 786 787bool AudioDeviceIOS::InitPlayOrRecord() { 788 LOGI() << "InitPlayOrRecord"; 789 790 // There should be no audio unit at this point. 791 if (!CreateAudioUnit()) { 792 return false; 793 } 794 795 RTCAudioSession* session = [RTCAudioSession sharedInstance]; 796 // Subscribe to audio session events. 797 [session pushDelegate:audio_session_observer_]; 798 799 // Lock the session to make configuration changes. 800 [session lockForConfiguration]; 801 NSError* error = nil; 802 if (![session beginWebRTCSession:&error]) { 803 [session unlockForConfiguration]; 804 RTCLogError(@"Failed to begin WebRTC session: %@", 805 error.localizedDescription); 806 return false; 807 } 808 809 // If we are ready to play or record, initialize the audio unit. 810 if (session.canPlayOrRecord) { 811 ConfigureAudioSession(); 812 SetupAudioBuffersForActiveAudioSession(); 813 audio_unit_->Initialize(playout_parameters_.sample_rate()); 814 } 815 816 // Release the lock. 817 [session unlockForConfiguration]; 818 819 return true; 820} 821 822void AudioDeviceIOS::ShutdownPlayOrRecord() { 823 LOGI() << "ShutdownPlayOrRecord"; 824 825 // Stop the audio unit to prevent any additional audio callbacks. 826 audio_unit_->Stop(); 827 828 // Close and delete the voice-processing I/O unit. 829 audio_unit_.reset(); 830 831 // Remove audio session notification observers. 832 RTCAudioSession* session = [RTCAudioSession sharedInstance]; 833 [session removeDelegate:audio_session_observer_]; 834 835 // All I/O should be stopped or paused prior to deactivating the audio 836 // session, hence we deactivate as last action. 837 [session lockForConfiguration]; 838 UnconfigureAudioSession(); 839 [session endWebRTCSession:nil]; 840 [session unlockForConfiguration]; 841} 842 843} // namespace webrtc 844