Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(442)

Side by Side Diff: webrtc/modules/audio_device/ios/audio_device_ios.mm

Issue 1809343002: Refactor AudioUnit code into its own class. (Closed) Base URL: https://chromium.googlesource.com/external/webrtc.git@master
Patch Set: Created 4 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
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 37 matching lines...) Expand 10 before | Expand all | Expand 10 after
48 48
49 #define LOG_IF_ERROR(error, message) \ 49 #define LOG_IF_ERROR(error, message) \
50 do { \ 50 do { \
51 OSStatus err = error; \ 51 OSStatus err = error; \
52 if (err) { \ 52 if (err) { \
53 LOG(LS_ERROR) << message << ": " << err; \ 53 LOG(LS_ERROR) << message << ": " << err; \
54 } \ 54 } \
55 } while (0) 55 } while (0)
56 56
57 57
58 // Number of bytes per audio sample for 16-bit signed integer representation.
59 const UInt32 kBytesPerSample = 2;
60 // Hardcoded delay estimates based on real measurements. 58 // Hardcoded delay estimates based on real measurements.
61 // 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.
62 // Can most likely be removed. 60 // Can most likely be removed.
63 const UInt16 kFixedPlayoutDelayEstimate = 30; 61 const UInt16 kFixedPlayoutDelayEstimate = 30;
64 const UInt16 kFixedRecordDelayEstimate = 30; 62 const UInt16 kFixedRecordDelayEstimate = 30;
65 // Calls to AudioUnitInitialize() can fail if called back-to-back on different
66 // ADM instances. A fall-back solution is to allow multiple sequential calls
67 // with as small delay between each. This factor sets the max number of allowed
68 // initialization attempts.
69 const int kMaxNumberOfAudioUnitInitializeAttempts = 5;
70 63
71 using ios::CheckAndLogError; 64 using ios::CheckAndLogError;
72 65
73 #if !defined(NDEBUG) 66 #if !defined(NDEBUG)
74 // Helper method for printing out an AudioStreamBasicDescription structure.
75 static void LogABSD(AudioStreamBasicDescription absd) {
76 char formatIDString[5];
77 UInt32 formatID = CFSwapInt32HostToBig(absd.mFormatID);
78 bcopy(&formatID, formatIDString, 4);
79 formatIDString[4] = '\0';
80 LOG(LS_INFO) << "LogABSD";
81 LOG(LS_INFO) << " sample rate: " << absd.mSampleRate;
82 LOG(LS_INFO) << " format ID: " << formatIDString;
83 LOG(LS_INFO) << " format flags: " << std::hex << absd.mFormatFlags;
84 LOG(LS_INFO) << " bytes per packet: " << absd.mBytesPerPacket;
85 LOG(LS_INFO) << " frames per packet: " << absd.mFramesPerPacket;
86 LOG(LS_INFO) << " bytes per frame: " << absd.mBytesPerFrame;
87 LOG(LS_INFO) << " channels per packet: " << absd.mChannelsPerFrame;
88 LOG(LS_INFO) << " bits per channel: " << absd.mBitsPerChannel;
89 LOG(LS_INFO) << " reserved: " << absd.mReserved;
90 }
91
92 // Helper method that logs essential device information strings. 67 // Helper method that logs essential device information strings.
93 static void LogDeviceInfo() { 68 static void LogDeviceInfo() {
94 LOG(LS_INFO) << "LogDeviceInfo"; 69 LOG(LS_INFO) << "LogDeviceInfo";
95 @autoreleasepool { 70 @autoreleasepool {
96 LOG(LS_INFO) << " system name: " << ios::GetSystemName(); 71 LOG(LS_INFO) << " system name: " << ios::GetSystemName();
97 LOG(LS_INFO) << " system version 1(2): " << ios::GetSystemVersionAsString(); 72 LOG(LS_INFO) << " system version 1(2): " << ios::GetSystemVersionAsString();
98 LOG(LS_INFO) << " system version 2(2): " << ios::GetSystemVersion(); 73 LOG(LS_INFO) << " system version 2(2): " << ios::GetSystemVersion();
99 LOG(LS_INFO) << " device type: " << ios::GetDeviceType(); 74 LOG(LS_INFO) << " device type: " << ios::GetDeviceType();
100 LOG(LS_INFO) << " device name: " << ios::GetDeviceName(); 75 LOG(LS_INFO) << " device name: " << ios::GetDeviceName();
101 LOG(LS_INFO) << " process name: " << ios::GetProcessName(); 76 LOG(LS_INFO) << " process name: " << ios::GetProcessName();
102 LOG(LS_INFO) << " process ID: " << ios::GetProcessID(); 77 LOG(LS_INFO) << " process ID: " << ios::GetProcessID();
103 LOG(LS_INFO) << " OS version: " << ios::GetOSVersionString(); 78 LOG(LS_INFO) << " OS version: " << ios::GetOSVersionString();
104 LOG(LS_INFO) << " processing cores: " << ios::GetProcessorCount(); 79 LOG(LS_INFO) << " processing cores: " << ios::GetProcessorCount();
105 #if defined(__IPHONE_9_0) && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_9_0 80 #if defined(__IPHONE_9_0) && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_9_0
106 LOG(LS_INFO) << " low power mode: " << ios::GetLowPowerModeEnabled(); 81 LOG(LS_INFO) << " low power mode: " << ios::GetLowPowerModeEnabled();
107 #endif 82 #endif
108 } 83 }
109 } 84 }
110 #endif // !defined(NDEBUG) 85 #endif // !defined(NDEBUG)
111 86
112 AudioDeviceIOS::AudioDeviceIOS() 87 AudioDeviceIOS::AudioDeviceIOS()
113 : async_invoker_(new rtc::AsyncInvoker()), 88 : async_invoker_(new rtc::AsyncInvoker()),
114 audio_device_buffer_(nullptr), 89 audio_device_buffer_(nullptr),
115 vpio_unit_(nullptr), 90 audio_unit_(nullptr),
116 recording_(0), 91 recording_(0),
117 playing_(0), 92 playing_(0),
118 initialized_(false), 93 initialized_(false),
119 rec_is_initialized_(false), 94 rec_is_initialized_(false),
120 play_is_initialized_(false), 95 play_is_initialized_(false),
121 is_interrupted_(false) { 96 is_interrupted_(false) {
122 LOGI() << "ctor" << ios::GetCurrentThreadDescription(); 97 LOGI() << "ctor" << ios::GetCurrentThreadDescription();
123 thread_ = rtc::Thread::Current(); 98 thread_ = rtc::Thread::Current();
124 audio_session_observer_ = 99 audio_session_observer_ =
125 [[RTCAudioSessionDelegateAdapter alloc] initWithObserver:this]; 100 [[RTCAudioSessionDelegateAdapter alloc] initWithObserver:this];
126 } 101 }
127 102
128 AudioDeviceIOS::~AudioDeviceIOS() { 103 AudioDeviceIOS::~AudioDeviceIOS() {
129 LOGI() << "~dtor" << ios::GetCurrentThreadDescription(); 104 LOGI() << "~dtor" << ios::GetCurrentThreadDescription();
130 audio_session_observer_ = nil; 105 audio_session_observer_ = nil;
131 RTC_DCHECK(thread_checker_.CalledOnValidThread()); 106 RTC_DCHECK(thread_checker_.CalledOnValidThread());
(...skipping 79 matching lines...) Expand 10 before | Expand all | Expand 10 after
211 return 0; 186 return 0;
212 } 187 }
213 188
214 int32_t AudioDeviceIOS::StartPlayout() { 189 int32_t AudioDeviceIOS::StartPlayout() {
215 LOGI() << "StartPlayout"; 190 LOGI() << "StartPlayout";
216 RTC_DCHECK(thread_checker_.CalledOnValidThread()); 191 RTC_DCHECK(thread_checker_.CalledOnValidThread());
217 RTC_DCHECK(play_is_initialized_); 192 RTC_DCHECK(play_is_initialized_);
218 RTC_DCHECK(!playing_); 193 RTC_DCHECK(!playing_);
219 fine_audio_buffer_->ResetPlayout(); 194 fine_audio_buffer_->ResetPlayout();
220 if (!recording_) { 195 if (!recording_) {
221 OSStatus result = AudioOutputUnitStart(vpio_unit_); 196 if (!audio_unit_->Start()) {
222 if (result != noErr) { 197 LOG_F(LS_ERROR) << "AudioOutputUnitStart failed for StartPlayout.";
henrika_webrtc 2016/03/18 11:54:02 Perhaps the comment can be changed given that the
tkchin_webrtc 2016/03/18 17:34:35 Done.
223 LOG_F(LS_ERROR) << "AudioOutputUnitStart failed for StartPlayout: "
224 << result;
225 return -1; 198 return -1;
226 } 199 }
227 LOG(LS_INFO) << "Voice-Processing I/O audio unit is now started"; 200 LOG(LS_INFO) << "Voice-Processing I/O audio unit is now started";
228 } 201 }
229 rtc::AtomicOps::ReleaseStore(&playing_, 1); 202 rtc::AtomicOps::ReleaseStore(&playing_, 1);
230 return 0; 203 return 0;
231 } 204 }
232 205
233 int32_t AudioDeviceIOS::StopPlayout() { 206 int32_t AudioDeviceIOS::StopPlayout() {
234 LOGI() << "StopPlayout"; 207 LOGI() << "StopPlayout";
235 RTC_DCHECK(thread_checker_.CalledOnValidThread()); 208 RTC_DCHECK(thread_checker_.CalledOnValidThread());
236 if (!play_is_initialized_ || !playing_) { 209 if (!play_is_initialized_ || !playing_) {
237 return 0; 210 return 0;
238 } 211 }
239 if (!recording_) { 212 if (!recording_) {
240 ShutdownPlayOrRecord(); 213 ShutdownPlayOrRecord();
241 } 214 }
242 play_is_initialized_ = false; 215 play_is_initialized_ = false;
243 rtc::AtomicOps::ReleaseStore(&playing_, 0); 216 rtc::AtomicOps::ReleaseStore(&playing_, 0);
244 return 0; 217 return 0;
245 } 218 }
246 219
247 int32_t AudioDeviceIOS::StartRecording() { 220 int32_t AudioDeviceIOS::StartRecording() {
248 LOGI() << "StartRecording"; 221 LOGI() << "StartRecording";
249 RTC_DCHECK(thread_checker_.CalledOnValidThread()); 222 RTC_DCHECK(thread_checker_.CalledOnValidThread());
250 RTC_DCHECK(rec_is_initialized_); 223 RTC_DCHECK(rec_is_initialized_);
251 RTC_DCHECK(!recording_); 224 RTC_DCHECK(!recording_);
252 fine_audio_buffer_->ResetRecord(); 225 fine_audio_buffer_->ResetRecord();
253 if (!playing_) { 226 if (!playing_) {
254 OSStatus result = AudioOutputUnitStart(vpio_unit_); 227 if (!audio_unit_->Start()) {
255 if (result != noErr) { 228 LOG_F(LS_ERROR) << "AudioOutputUnitStart failed for StartRecording.";
henrika_webrtc 2016/03/18 11:54:02 ditto
tkchin_webrtc 2016/03/18 17:34:35 Done.
256 LOG_F(LS_ERROR) << "AudioOutputUnitStart failed for StartRecording: "
257 << result;
258 return -1; 229 return -1;
259 } 230 }
260 LOG(LS_INFO) << "Voice-Processing I/O audio unit is now started"; 231 LOG(LS_INFO) << "Voice-Processing I/O audio unit is now started";
261 } 232 }
262 rtc::AtomicOps::ReleaseStore(&recording_, 1); 233 rtc::AtomicOps::ReleaseStore(&recording_, 1);
263 return 0; 234 return 0;
264 } 235 }
265 236
266 int32_t AudioDeviceIOS::StopRecording() { 237 int32_t AudioDeviceIOS::StopRecording() {
267 LOGI() << "StopRecording"; 238 LOGI() << "StopRecording";
(...skipping 101 matching lines...) Expand 10 before | Expand all | Expand 10 after
369 RTC_DCHECK(thread_); 340 RTC_DCHECK(thread_);
370 if (thread_->IsCurrent()) { 341 if (thread_->IsCurrent()) {
371 HandleValidRouteChange(); 342 HandleValidRouteChange();
372 return; 343 return;
373 } 344 }
374 async_invoker_->AsyncInvoke<void>( 345 async_invoker_->AsyncInvoke<void>(
375 thread_, 346 thread_,
376 rtc::Bind(&webrtc::AudioDeviceIOS::HandleValidRouteChange, this)); 347 rtc::Bind(&webrtc::AudioDeviceIOS::HandleValidRouteChange, this));
377 } 348 }
378 349
350 OSStatus AudioDeviceIOS::OnDeliverRecordedData(
henrika_webrtc 2016/03/18 11:54:02 I am assuming that this code is almost identical t
tkchin_webrtc 2016/03/18 17:34:35 Yes, should be identical except for some variable
351 AudioUnitRenderActionFlags* flags,
352 const AudioTimeStamp* time_stamp,
353 UInt32 bus_number,
354 UInt32 num_frames,
355 AudioBufferList* io_data_unused) {
356 OSStatus result = noErr;
357 // Simply return if recording is not enabled.
358 if (!rtc::AtomicOps::AcquireLoad(&recording_))
Chuck 2016/03/18 14:44:14 Is this allowed in the WebRTC coding style? Referr
tkchin_webrtc 2016/03/18 17:34:35 It's the preferred style now apparently for single
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 return result;
371 }
372
373 // Obtain the recorded audio samples by initiating a rendering cycle.
374 // Since it happens on the input bus, the |io_data| parameter is a reference
375 // to the preallocated audio buffer list that the audio unit renders into.
376 // TODO(henrika): should error handling be improved?
377 AudioBufferList* io_data = &audio_record_buffer_list_;
378 result =
379 audio_unit_->Render(flags, time_stamp, bus_number, num_frames, io_data);
380 if (result != noErr) {
381 RTCLogError(@"Failed to render audio.");
382 return result;
383 }
384
385 // Get a pointer to the recorded audio and send it to the WebRTC ADB.
386 // Use the FineAudioBuffer instance to convert between native buffer size
387 // and the 10ms buffer size used by WebRTC.
388 AudioBuffer* audio_buffer = &io_data->mBuffers[0];
389 const size_t size_in_bytes = audio_buffer->mDataByteSize;
390 RTC_CHECK_EQ(size_in_bytes / VoiceProcessingAudioUnit::kBytesPerSample,
391 num_frames);
392 int8_t* data = static_cast<int8_t*>(audio_buffer->mData);
393 fine_audio_buffer_->DeliverRecordedData(data, size_in_bytes,
394 kFixedPlayoutDelayEstimate,
395 kFixedRecordDelayEstimate);
396 return noErr;
397 }
398
399 OSStatus AudioDeviceIOS::OnGetPlayoutData(AudioUnitRenderActionFlags* flags,
400 const AudioTimeStamp* time_stamp,
401 UInt32 bus_number,
402 UInt32 num_frames,
403 AudioBufferList* io_data) {
404 // Verify 16-bit, noninterleaved mono PCM signal format.
405 RTC_DCHECK_EQ(1u, io_data->mNumberBuffers);
406 AudioBuffer* audio_buffer = &io_data->mBuffers[0];
407 RTC_DCHECK_EQ(1u, audio_buffer->mNumberChannels);
408 // Get pointer to internal audio buffer to which new audio data shall be
409 // written.
410 const size_t size_in_bytes = audio_buffer->mDataByteSize;
411 RTC_CHECK_EQ(size_in_bytes / VoiceProcessingAudioUnit::kBytesPerSample,
412 num_frames);
413 int8_t* destination = reinterpret_cast<int8_t*>(audio_buffer->mData);
414 // Produce silence and give audio unit a hint about it if playout is not
415 // activated.
416 if (!rtc::AtomicOps::AcquireLoad(&playing_)) {
417 *flags |= kAudioUnitRenderAction_OutputIsSilence;
418 memset(destination, 0, size_in_bytes);
419 return noErr;
420 }
421 // Read decoded 16-bit PCM samples from WebRTC (using a size that matches
422 // the native I/O audio unit) to a preallocated intermediate buffer and
423 // copy the result to the audio buffer in the |io_data| destination.
424 int8_t* source = playout_audio_buffer_.get();
425 fine_audio_buffer_->GetPlayoutData(source);
426 memcpy(destination, source, size_in_bytes);
427 return noErr;
428 }
429
379 void AudioDeviceIOS::HandleInterruptionBegin() { 430 void AudioDeviceIOS::HandleInterruptionBegin() {
380 RTC_DCHECK(thread_checker_.CalledOnValidThread()); 431 RTC_DCHECK(thread_checker_.CalledOnValidThread());
381 RTCLog(@"Stopping the audio unit due to interruption begin."); 432 RTCLog(@"Stopping the audio unit due to interruption begin.");
382 LOG_IF_ERROR(AudioOutputUnitStop(vpio_unit_), 433 if (!audio_unit_->Stop()) {
383 "Failed to stop the the Voice-Processing I/O unit"); 434 RTCLogError(@"Failed to stop the audio unit.");
henrika_webrtc 2016/03/18 11:54:02 Guess you can use the same type of error logs in t
tkchin_webrtc 2016/03/18 17:34:35 Acknowledged.
435 }
384 is_interrupted_ = true; 436 is_interrupted_ = true;
385 } 437 }
386 438
387 void AudioDeviceIOS::HandleInterruptionEnd() { 439 void AudioDeviceIOS::HandleInterruptionEnd() {
388 RTC_DCHECK(thread_checker_.CalledOnValidThread()); 440 RTC_DCHECK(thread_checker_.CalledOnValidThread());
389 RTCLog(@"Starting the audio unit due to interruption end."); 441 RTCLog(@"Starting the audio unit due to interruption end.");
390 LOG_IF_ERROR(AudioOutputUnitStart(vpio_unit_), 442 if (!audio_unit_->Start()) {
391 "Failed to start the the Voice-Processing I/O unit"); 443 RTCLogError(@"Failed to start the audio unit.");
444 }
392 is_interrupted_ = false; 445 is_interrupted_ = false;
393 } 446 }
394 447
395 void AudioDeviceIOS::HandleValidRouteChange() { 448 void AudioDeviceIOS::HandleValidRouteChange() {
396 RTC_DCHECK(thread_checker_.CalledOnValidThread()); 449 RTC_DCHECK(thread_checker_.CalledOnValidThread());
397 450
398 // Don't do anything if we're interrupted. 451 // Don't do anything if we're interrupted.
399 if (is_interrupted_) { 452 if (is_interrupted_) {
400 return; 453 return;
401 } 454 }
402 455
403 // Only restart audio for a valid route change if the session sample rate 456 // Only restart audio for a valid route change if the session sample rate
404 // has changed. 457 // has changed.
405 RTCAudioSession* session = [RTCAudioSession sharedInstance]; 458 RTCAudioSession* session = [RTCAudioSession sharedInstance];
406 const double current_sample_rate = playout_parameters_.sample_rate(); 459 const double current_sample_rate = playout_parameters_.sample_rate();
407 const double session_sample_rate = session.sampleRate; 460 const double session_sample_rate = session.sampleRate;
408 if (current_sample_rate != session_sample_rate) { 461 if (current_sample_rate != session_sample_rate) {
409 RTCLog(@"Route changed caused sample rate to change from %f to %f. " 462 RTCLog(@"Route changed caused sample rate to change from %f to %f. "
410 "Restarting audio unit.", current_sample_rate, session_sample_rate); 463 "Restarting audio unit.", current_sample_rate, session_sample_rate);
411 if (!RestartAudioUnitWithNewFormat(session_sample_rate)) { 464 if (!RestartAudioUnit(session_sample_rate)) {
412 RTCLogError(@"Audio restart failed."); 465 RTCLogError(@"Audio restart failed.");
413 } 466 }
414 } 467 }
415 } 468 }
416 469
417 void AudioDeviceIOS::UpdateAudioDeviceBuffer() { 470 void AudioDeviceIOS::UpdateAudioDeviceBuffer() {
418 LOGI() << "UpdateAudioDevicebuffer"; 471 LOGI() << "UpdateAudioDevicebuffer";
419 // AttachAudioBuffer() is called at construction by the main class but check 472 // AttachAudioBuffer() is called at construction by the main class but check
420 // just in case. 473 // just in case.
421 RTC_DCHECK(audio_device_buffer_) << "AttachAudioBuffer must be called first"; 474 RTC_DCHECK(audio_device_buffer_) << "AttachAudioBuffer must be called first";
422 // Inform the audio device buffer (ADB) about the new audio format. 475 // Inform the audio device buffer (ADB) about the new audio format.
423 audio_device_buffer_->SetPlayoutSampleRate(playout_parameters_.sample_rate()); 476 audio_device_buffer_->SetPlayoutSampleRate(playout_parameters_.sample_rate());
424 audio_device_buffer_->SetPlayoutChannels(playout_parameters_.channels()); 477 audio_device_buffer_->SetPlayoutChannels(playout_parameters_.channels());
425 audio_device_buffer_->SetRecordingSampleRate( 478 audio_device_buffer_->SetRecordingSampleRate(
426 record_parameters_.sample_rate()); 479 record_parameters_.sample_rate());
427 audio_device_buffer_->SetRecordingChannels(record_parameters_.channels()); 480 audio_device_buffer_->SetRecordingChannels(record_parameters_.channels());
428 } 481 }
429 482
430 void AudioDeviceIOS::SetupAudioBuffersForActiveAudioSession() { 483 void AudioDeviceIOS::SetupAudioBuffersForActiveAudioSession() {
431 LOGI() << "SetupAudioBuffersForActiveAudioSession"; 484 LOGI() << "SetupAudioBuffersForActiveAudioSession";
432 // Verify the current values once the audio session has been activated. 485 // Verify the current values once the audio session has been activated.
433 RTCAudioSession* session = [RTCAudioSession sharedInstance]; 486 RTCAudioSession* session = [RTCAudioSession sharedInstance];
434 double sample_rate = session.sampleRate; 487 double sample_rate = session.sampleRate;
435 NSTimeInterval io_buffer_duration = session.IOBufferDuration; 488 NSTimeInterval io_buffer_duration = session.IOBufferDuration;
436 LOG(LS_INFO) << " sample rate: " << sample_rate; 489 RTCLog(@"%@", session);
henrika_webrtc 2016/03/18 11:54:02 Nice ;-)
tkchin_webrtc 2016/03/18 17:34:35 Acknowledged.
437 LOG(LS_INFO) << " IO buffer duration: " << io_buffer_duration;
438 LOG(LS_INFO) << " output channels: " << session.outputNumberOfChannels;
439 LOG(LS_INFO) << " input channels: " << session.inputNumberOfChannels;
440 LOG(LS_INFO) << " output latency: " << session.outputLatency;
441 LOG(LS_INFO) << " input latency: " << session.inputLatency;
442 490
443 // Log a warning message for the case when we are unable to set the preferred 491 // Log a warning message for the case when we are unable to set the preferred
444 // hardware sample rate but continue and use the non-ideal sample rate after 492 // hardware sample rate but continue and use the non-ideal sample rate after
445 // reinitializing the audio parameters. Most BT headsets only support 8kHz or 493 // reinitializing the audio parameters. Most BT headsets only support 8kHz or
446 // 16kHz. 494 // 16kHz.
447 RTCAudioSessionConfiguration* webRTCConfig = 495 RTCAudioSessionConfiguration* webRTCConfig =
448 [RTCAudioSessionConfiguration webRTCConfiguration]; 496 [RTCAudioSessionConfiguration webRTCConfiguration];
449 if (sample_rate != webRTCConfig.sampleRate) { 497 if (sample_rate != webRTCConfig.sampleRate) {
450 LOG(LS_WARNING) << "Unable to set the preferred sample rate"; 498 LOG(LS_WARNING) << "Unable to set the preferred sample rate";
451 } 499 }
(...skipping 42 matching lines...) Expand 10 before | Expand all | Expand 10 after
494 // at each input callback when calling AudioUnitRender(). 542 // at each input callback when calling AudioUnitRender().
495 const int data_byte_size = record_parameters_.GetBytesPerBuffer(); 543 const int data_byte_size = record_parameters_.GetBytesPerBuffer();
496 record_audio_buffer_.reset(new SInt8[data_byte_size]); 544 record_audio_buffer_.reset(new SInt8[data_byte_size]);
497 audio_record_buffer_list_.mNumberBuffers = 1; 545 audio_record_buffer_list_.mNumberBuffers = 1;
498 AudioBuffer* audio_buffer = &audio_record_buffer_list_.mBuffers[0]; 546 AudioBuffer* audio_buffer = &audio_record_buffer_list_.mBuffers[0];
499 audio_buffer->mNumberChannels = record_parameters_.channels(); 547 audio_buffer->mNumberChannels = record_parameters_.channels();
500 audio_buffer->mDataByteSize = data_byte_size; 548 audio_buffer->mDataByteSize = data_byte_size;
501 audio_buffer->mData = record_audio_buffer_.get(); 549 audio_buffer->mData = record_audio_buffer_.get();
502 } 550 }
503 551
504 bool AudioDeviceIOS::SetupAndInitializeVoiceProcessingAudioUnit() { 552 bool AudioDeviceIOS::CreateAudioUnit() {
505 LOGI() << "SetupAndInitializeVoiceProcessingAudioUnit"; 553 RTC_DCHECK(!audio_unit_);
506 RTC_DCHECK(!vpio_unit_) << "VoiceProcessingIO audio unit already exists";
507 // Create an audio component description to identify the Voice-Processing
508 // I/O audio unit.
509 AudioComponentDescription vpio_unit_description;
510 vpio_unit_description.componentType = kAudioUnitType_Output;
511 vpio_unit_description.componentSubType = kAudioUnitSubType_VoiceProcessingIO;
512 vpio_unit_description.componentManufacturer = kAudioUnitManufacturer_Apple;
513 vpio_unit_description.componentFlags = 0;
514 vpio_unit_description.componentFlagsMask = 0;
515 554
516 // Obtain an audio unit instance given the description. 555 audio_unit_.reset(new VoiceProcessingAudioUnit(this));
517 AudioComponent found_vpio_unit_ref = 556 if (!audio_unit_->Init()) {
518 AudioComponentFindNext(nullptr, &vpio_unit_description); 557 audio_unit_.reset();
519
520 // Create a Voice-Processing IO audio unit.
521 OSStatus result = noErr;
522 result = AudioComponentInstanceNew(found_vpio_unit_ref, &vpio_unit_);
523 if (result != noErr) {
524 vpio_unit_ = nullptr;
525 LOG(LS_ERROR) << "AudioComponentInstanceNew failed: " << result;
526 return false; 558 return false;
527 } 559 }
528 560
529 // A VP I/O unit's bus 1 connects to input hardware (microphone). Enable 561 return true;
530 // input on the input scope of the input element. 562 }
531 AudioUnitElement input_bus = 1; 563
532 UInt32 enable_input = 1; 564 bool AudioDeviceIOS::RestartAudioUnit(float sample_rate) {
533 result = AudioUnitSetProperty(vpio_unit_, kAudioOutputUnitProperty_EnableIO, 565 RTCLog(@"Restarting audio unit with new sample rate: %f", sample_rate);
534 kAudioUnitScope_Input, input_bus, &enable_input, 566
535 sizeof(enable_input)); 567 // Stop the active audio unit.
536 if (result != noErr) { 568 if (!audio_unit_->Stop()) {
537 DisposeAudioUnit(); 569 RTCLogError(@"Failed to stop the audio unit.");
538 LOG(LS_ERROR) << "Failed to enable input on input scope of input element: "
539 << result;
540 return false; 570 return false;
541 } 571 }
542 572
543 // A VP I/O unit's bus 0 connects to output hardware (speaker). Enable 573 // The stream format is about to be changed and it requires that we first
544 // output on the output scope of the output element. 574 // uninitialize it to deallocate its resources.
545 AudioUnitElement output_bus = 0; 575 if (!audio_unit_->Uninitialize()) {
546 UInt32 enable_output = 1; 576 RTCLogError(@"Failed to uninitialize the audio unit.");
547 result = AudioUnitSetProperty(vpio_unit_, kAudioOutputUnitProperty_EnableIO,
548 kAudioUnitScope_Output, output_bus,
549 &enable_output, sizeof(enable_output));
550 if (result != noErr) {
551 DisposeAudioUnit();
552 LOG(LS_ERROR)
553 << "Failed to enable output on output scope of output element: "
554 << result;
555 return false; 577 return false;
556 } 578 }
557 579
558 // Set the application formats for input and output:
559 // - use same format in both directions
560 // - avoid resampling in the I/O unit by using the hardware sample rate
561 // - linear PCM => noncompressed audio data format with one frame per packet
562 // - no need to specify interleaving since only mono is supported
563 AudioStreamBasicDescription application_format = {0};
564 UInt32 size = sizeof(application_format);
565 RTC_DCHECK_EQ(playout_parameters_.sample_rate(),
566 record_parameters_.sample_rate());
567 RTC_DCHECK_EQ(1, kRTCAudioSessionPreferredNumberOfChannels);
568 application_format.mSampleRate = playout_parameters_.sample_rate();
569 application_format.mFormatID = kAudioFormatLinearPCM;
570 application_format.mFormatFlags =
571 kLinearPCMFormatFlagIsSignedInteger | kLinearPCMFormatFlagIsPacked;
572 application_format.mBytesPerPacket = kBytesPerSample;
573 application_format.mFramesPerPacket = 1; // uncompressed
574 application_format.mBytesPerFrame = kBytesPerSample;
575 application_format.mChannelsPerFrame =
576 kRTCAudioSessionPreferredNumberOfChannels;
577 application_format.mBitsPerChannel = 8 * kBytesPerSample;
578 // Store the new format.
579 application_format_ = application_format;
580 #if !defined(NDEBUG)
581 LogABSD(application_format_);
582 #endif
583
584 // Set the application format on the output scope of the input element/bus.
585 result = AudioUnitSetProperty(vpio_unit_, kAudioUnitProperty_StreamFormat,
586 kAudioUnitScope_Output, input_bus,
587 &application_format, size);
588 if (result != noErr) {
589 DisposeAudioUnit();
590 LOG(LS_ERROR)
591 << "Failed to set application format on output scope of input bus: "
592 << result;
593 return false;
594 }
595
596 // Set the application format on the input scope of the output element/bus.
597 result = AudioUnitSetProperty(vpio_unit_, kAudioUnitProperty_StreamFormat,
598 kAudioUnitScope_Input, output_bus,
599 &application_format, size);
600 if (result != noErr) {
601 DisposeAudioUnit();
602 LOG(LS_ERROR)
603 << "Failed to set application format on input scope of output bus: "
604 << result;
605 return false;
606 }
607
608 // Specify the callback function that provides audio samples to the audio
609 // unit.
610 AURenderCallbackStruct render_callback;
611 render_callback.inputProc = GetPlayoutData;
612 render_callback.inputProcRefCon = this;
613 result = AudioUnitSetProperty(
614 vpio_unit_, kAudioUnitProperty_SetRenderCallback, kAudioUnitScope_Input,
615 output_bus, &render_callback, sizeof(render_callback));
616 if (result != noErr) {
617 DisposeAudioUnit();
618 LOG(LS_ERROR) << "Failed to specify the render callback on the output bus: "
619 << result;
620 return false;
621 }
622
623 // Disable AU buffer allocation for the recorder, we allocate our own.
624 // TODO(henrika): not sure that it actually saves resource to make this call.
625 UInt32 flag = 0;
626 result = AudioUnitSetProperty(
627 vpio_unit_, kAudioUnitProperty_ShouldAllocateBuffer,
628 kAudioUnitScope_Output, input_bus, &flag, sizeof(flag));
629 if (result != noErr) {
630 DisposeAudioUnit();
631 LOG(LS_ERROR) << "Failed to disable buffer allocation on the input bus: "
632 << result;
633 }
634
635 // Specify the callback to be called by the I/O thread to us when input audio
636 // is available. The recorded samples can then be obtained by calling the
637 // AudioUnitRender() method.
638 AURenderCallbackStruct input_callback;
639 input_callback.inputProc = RecordedDataIsAvailable;
640 input_callback.inputProcRefCon = this;
641 result = AudioUnitSetProperty(vpio_unit_,
642 kAudioOutputUnitProperty_SetInputCallback,
643 kAudioUnitScope_Global, input_bus,
644 &input_callback, sizeof(input_callback));
645 if (result != noErr) {
646 DisposeAudioUnit();
647 LOG(LS_ERROR) << "Failed to specify the input callback on the input bus: "
648 << result;
649 }
650
651 // Initialize the Voice-Processing I/O unit instance.
652 // Calls to AudioUnitInitialize() can fail if called back-to-back on
653 // different ADM instances. The error message in this case is -66635 which is
654 // undocumented. Tests have shown that calling AudioUnitInitialize a second
655 // time, after a short sleep, avoids this issue.
656 // See webrtc:5166 for details.
657 int failed_initalize_attempts = 0;
658 result = AudioUnitInitialize(vpio_unit_);
659 while (result != noErr) {
660 LOG(LS_ERROR) << "Failed to initialize the Voice-Processing I/O unit: "
661 << result;
662 ++failed_initalize_attempts;
663 if (failed_initalize_attempts == kMaxNumberOfAudioUnitInitializeAttempts) {
664 // Max number of initialization attempts exceeded, hence abort.
665 LOG(LS_WARNING) << "Too many initialization attempts";
666 DisposeAudioUnit();
667 return false;
668 }
669 LOG(LS_INFO) << "pause 100ms and try audio unit initialization again...";
670 [NSThread sleepForTimeInterval:0.1f];
671 result = AudioUnitInitialize(vpio_unit_);
672 }
673 LOG(LS_INFO) << "Voice-Processing I/O unit is now initialized";
674 return true;
675 }
676
677 bool AudioDeviceIOS::RestartAudioUnitWithNewFormat(float sample_rate) {
678 LOGI() << "RestartAudioUnitWithNewFormat(sample_rate=" << sample_rate << ")";
679 // Stop the active audio unit.
680 LOG_AND_RETURN_IF_ERROR(AudioOutputUnitStop(vpio_unit_),
681 "Failed to stop the the Voice-Processing I/O unit");
682
683 // The stream format is about to be changed and it requires that we first
684 // uninitialize it to deallocate its resources.
685 LOG_AND_RETURN_IF_ERROR(
686 AudioUnitUninitialize(vpio_unit_),
687 "Failed to uninitialize the the Voice-Processing I/O unit");
688
689 // Allocate new buffers given the new stream format. 580 // Allocate new buffers given the new stream format.
690 SetupAudioBuffersForActiveAudioSession(); 581 SetupAudioBuffersForActiveAudioSession();
691 582
692 // Update the existing application format using the new sample rate. 583 // Initialize the audio unit again with the new sample rate.
693 application_format_.mSampleRate = playout_parameters_.sample_rate(); 584 RTC_DCHECK_EQ(playout_parameters_.sample_rate(), sample_rate);
694 UInt32 size = sizeof(application_format_); 585 if (!audio_unit_->Initialize(sample_rate)) {
695 AudioUnitSetProperty(vpio_unit_, kAudioUnitProperty_StreamFormat, 586 RTCLogError(@"Failed to initialize the audio unit with sample rate: %f",
696 kAudioUnitScope_Output, 1, &application_format_, size); 587 sample_rate);
697 AudioUnitSetProperty(vpio_unit_, kAudioUnitProperty_StreamFormat, 588 return false;
698 kAudioUnitScope_Input, 0, &application_format_, size); 589 }
699 590
700 // Prepare the audio unit to render audio again. 591 // Restart the audio unit.
701 LOG_AND_RETURN_IF_ERROR(AudioUnitInitialize(vpio_unit_), 592 if (!audio_unit_->Start()) {
702 "Failed to initialize the Voice-Processing I/O unit"); 593 RTCLogError(@"Failed to start audio unit.");
703 LOG(LS_INFO) << "Voice-Processing I/O unit is now reinitialized"; 594 return false;
595 }
596 RTCLog(@"Successfully restarted audio unit.");
704 597
705 // Start rendering audio using the new format.
706 LOG_AND_RETURN_IF_ERROR(AudioOutputUnitStart(vpio_unit_),
707 "Failed to start the Voice-Processing I/O unit");
708 LOG(LS_INFO) << "Voice-Processing I/O unit is now restarted";
709 return true; 598 return true;
710 } 599 }
711 600
712 bool AudioDeviceIOS::InitPlayOrRecord() { 601 bool AudioDeviceIOS::InitPlayOrRecord() {
713 LOGI() << "InitPlayOrRecord"; 602 LOGI() << "InitPlayOrRecord";
714 603
715 // Use the correct audio session configuration for WebRTC. 604 // Use the correct audio session configuration for WebRTC.
716 // This will attempt to activate the audio session. 605 // This will attempt to activate the audio session.
717 RTCAudioSession* session = [RTCAudioSession sharedInstance]; 606 RTCAudioSession* session = [RTCAudioSession sharedInstance];
718 [session lockForConfiguration]; 607 [session lockForConfiguration];
719 NSError* error = nil; 608 NSError* error = nil;
720 if (![session configureWebRTCSession:&error]) { 609 if (![session configureWebRTCSession:&error]) {
721 RTCLogError(@"Failed to configure WebRTC session: %@", 610 RTCLogError(@"Failed to configure WebRTC session: %@",
722 error.localizedDescription); 611 error.localizedDescription);
723 [session unlockForConfiguration]; 612 [session unlockForConfiguration];
724 return false; 613 return false;
725 } 614 }
726 615
727 // Start observing audio session interruptions and route changes. 616 // Start observing audio session interruptions and route changes.
728 [session pushDelegate:audio_session_observer_]; 617 [session pushDelegate:audio_session_observer_];
729 618
730 // Ensure that we got what what we asked for in our active audio session. 619 // Ensure that we got what what we asked for in our active audio session.
731 SetupAudioBuffersForActiveAudioSession(); 620 SetupAudioBuffersForActiveAudioSession();
732 621
733 // Create, setup and initialize a new Voice-Processing I/O unit. 622 // Create, setup and initialize a new Voice-Processing I/O unit.
734 if (!SetupAndInitializeVoiceProcessingAudioUnit()) { 623 // TODO(tkchin): Delay the initialization when needed.
624 if (!CreateAudioUnit() ||
625 !audio_unit_->Initialize(playout_parameters_.sample_rate())) {
735 [session setActive:NO error:nil]; 626 [session setActive:NO error:nil];
736 [session unlockForConfiguration]; 627 [session unlockForConfiguration];
737 return false; 628 return false;
738 } 629 }
739 [session unlockForConfiguration]; 630 [session unlockForConfiguration];
631
740 return true; 632 return true;
741 } 633 }
742 634
743 void AudioDeviceIOS::ShutdownPlayOrRecord() { 635 void AudioDeviceIOS::ShutdownPlayOrRecord() {
744 LOGI() << "ShutdownPlayOrRecord"; 636 LOGI() << "ShutdownPlayOrRecord";
637
745 // Close and delete the voice-processing I/O unit. 638 // Close and delete the voice-processing I/O unit.
746 OSStatus result = -1; 639 if (audio_unit_) {
747 if (nullptr != vpio_unit_) { 640 audio_unit_->Stop();
748 result = AudioOutputUnitStop(vpio_unit_); 641 audio_unit_->Uninitialize();
749 if (result != noErr) { 642 audio_unit_.reset();
750 LOG_F(LS_ERROR) << "AudioOutputUnitStop failed: " << result;
751 }
752 result = AudioUnitUninitialize(vpio_unit_);
753 if (result != noErr) {
754 LOG_F(LS_ERROR) << "AudioUnitUninitialize failed: " << result;
755 }
756 DisposeAudioUnit();
757 } 643 }
758 644
759 // Remove audio session notification observers. 645 // Remove audio session notification observers.
760 RTCAudioSession* session = [RTCAudioSession sharedInstance]; 646 RTCAudioSession* session = [RTCAudioSession sharedInstance];
761 [session removeDelegate:audio_session_observer_]; 647 [session removeDelegate:audio_session_observer_];
762 648
763 // All I/O should be stopped or paused prior to deactivating the audio 649 // All I/O should be stopped or paused prior to deactivating the audio
764 // session, hence we deactivate as last action. 650 // session, hence we deactivate as last action.
765 [session lockForConfiguration]; 651 [session lockForConfiguration];
766 [session setActive:NO error:nil]; 652 [session setActive:NO error:nil];
767 [session unlockForConfiguration]; 653 [session unlockForConfiguration];
768 } 654 }
769 655
770 void AudioDeviceIOS::DisposeAudioUnit() {
771 if (nullptr == vpio_unit_)
772 return;
773 OSStatus result = AudioComponentInstanceDispose(vpio_unit_);
774 if (result != noErr) {
775 LOG(LS_ERROR) << "AudioComponentInstanceDispose failed:" << result;
776 }
777 vpio_unit_ = nullptr;
778 }
779
780 OSStatus AudioDeviceIOS::RecordedDataIsAvailable(
781 void* in_ref_con,
782 AudioUnitRenderActionFlags* io_action_flags,
783 const AudioTimeStamp* in_time_stamp,
784 UInt32 in_bus_number,
785 UInt32 in_number_frames,
786 AudioBufferList* io_data) {
787 RTC_DCHECK_EQ(1u, in_bus_number);
788 RTC_DCHECK(
789 !io_data); // no buffer should be allocated for input at this stage
790 AudioDeviceIOS* audio_device_ios = static_cast<AudioDeviceIOS*>(in_ref_con);
791 return audio_device_ios->OnRecordedDataIsAvailable(
792 io_action_flags, in_time_stamp, in_bus_number, in_number_frames);
793 }
794
795 OSStatus AudioDeviceIOS::OnRecordedDataIsAvailable(
796 AudioUnitRenderActionFlags* io_action_flags,
797 const AudioTimeStamp* in_time_stamp,
798 UInt32 in_bus_number,
799 UInt32 in_number_frames) {
800 OSStatus result = noErr;
801 // Simply return if recording is not enabled.
802 if (!rtc::AtomicOps::AcquireLoad(&recording_))
803 return result;
804 if (in_number_frames != record_parameters_.frames_per_buffer()) {
805 // We have seen short bursts (1-2 frames) where |in_number_frames| changes.
806 // Add a log to keep track of longer sequences if that should ever happen.
807 // Also return since calling AudioUnitRender in this state will only result
808 // in kAudio_ParamError (-50) anyhow.
809 LOG(LS_WARNING) << "in_number_frames (" << in_number_frames
810 << ") != " << record_parameters_.frames_per_buffer();
811 return noErr;
812 }
813 // Obtain the recorded audio samples by initiating a rendering cycle.
814 // Since it happens on the input bus, the |io_data| parameter is a reference
815 // to the preallocated audio buffer list that the audio unit renders into.
816 // TODO(henrika): should error handling be improved?
817 AudioBufferList* io_data = &audio_record_buffer_list_;
818 result = AudioUnitRender(vpio_unit_, io_action_flags, in_time_stamp,
819 in_bus_number, in_number_frames, io_data);
820 if (result != noErr) {
821 LOG_F(LS_ERROR) << "AudioUnitRender failed: " << result;
822 return result;
823 }
824 // Get a pointer to the recorded audio and send it to the WebRTC ADB.
825 // Use the FineAudioBuffer instance to convert between native buffer size
826 // and the 10ms buffer size used by WebRTC.
827 const UInt32 data_size_in_bytes = io_data->mBuffers[0].mDataByteSize;
828 RTC_CHECK_EQ(data_size_in_bytes / kBytesPerSample, in_number_frames);
829 SInt8* data = static_cast<SInt8*>(io_data->mBuffers[0].mData);
830 fine_audio_buffer_->DeliverRecordedData(data, data_size_in_bytes,
831 kFixedPlayoutDelayEstimate,
832 kFixedRecordDelayEstimate);
833 return noErr;
834 }
835
836 OSStatus AudioDeviceIOS::GetPlayoutData(
837 void* in_ref_con,
838 AudioUnitRenderActionFlags* io_action_flags,
839 const AudioTimeStamp* in_time_stamp,
840 UInt32 in_bus_number,
841 UInt32 in_number_frames,
842 AudioBufferList* io_data) {
843 RTC_DCHECK_EQ(0u, in_bus_number);
844 RTC_DCHECK(io_data);
845 AudioDeviceIOS* audio_device_ios = static_cast<AudioDeviceIOS*>(in_ref_con);
846 return audio_device_ios->OnGetPlayoutData(io_action_flags, in_number_frames,
847 io_data);
848 }
849
850 OSStatus AudioDeviceIOS::OnGetPlayoutData(
851 AudioUnitRenderActionFlags* io_action_flags,
852 UInt32 in_number_frames,
853 AudioBufferList* io_data) {
854 // Verify 16-bit, noninterleaved mono PCM signal format.
855 RTC_DCHECK_EQ(1u, io_data->mNumberBuffers);
856 RTC_DCHECK_EQ(1u, io_data->mBuffers[0].mNumberChannels);
857 // Get pointer to internal audio buffer to which new audio data shall be
858 // written.
859 const UInt32 dataSizeInBytes = io_data->mBuffers[0].mDataByteSize;
860 RTC_CHECK_EQ(dataSizeInBytes / kBytesPerSample, in_number_frames);
861 SInt8* destination = static_cast<SInt8*>(io_data->mBuffers[0].mData);
862 // Produce silence and give audio unit a hint about it if playout is not
863 // activated.
864 if (!rtc::AtomicOps::AcquireLoad(&playing_)) {
865 *io_action_flags |= kAudioUnitRenderAction_OutputIsSilence;
866 memset(destination, 0, dataSizeInBytes);
867 return noErr;
868 }
869 // Read decoded 16-bit PCM samples from WebRTC (using a size that matches
870 // the native I/O audio unit) to a preallocated intermediate buffer and
871 // copy the result to the audio buffer in the |io_data| destination.
872 SInt8* source = playout_audio_buffer_.get();
873 fine_audio_buffer_->GetPlayoutData(source);
874 memcpy(destination, source, dataSizeInBytes);
875 return noErr;
876 }
877
878 } // namespace webrtc 656 } // namespace webrtc
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698