From e5b4e941a0bd040cf0ff761891bd75c7a0136fb7 Mon Sep 17 00:00:00 2001 From: Peter Hanspers Date: Thu, 26 Aug 2021 17:56:44 +0200 Subject: [PATCH] Surface audio unit errors. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit With this change, we catch audio unit start errors and pipe them to the audio session. The audio session notifies its delegate, which can then take appropriate action based on the error code. The signal follows the same path as the playout glitch detection. Bug: webrtc:13119 Change-Id: I8c9f9d2a1e3457447d0ce61ad197f7e1c6392837 Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/230240 Commit-Queue: Peter Hanspers Reviewed-by: Henrik Andreassson Reviewed-by: Xavier Lepaul‎ Cr-Commit-Position: refs/heads/main@{#34862} --- .../audio/RTCAudioSession+Private.h | 7 +++- sdk/objc/components/audio/RTCAudioSession.h | 3 ++ sdk/objc/components/audio/RTCAudioSession.mm | 12 +++++++ sdk/objc/native/src/audio/audio_device_ios.mm | 33 ++++++++++++++----- .../src/audio/voice_processing_audio_unit.h | 2 +- .../src/audio/voice_processing_audio_unit.mm | 6 ++-- 6 files changed, 49 insertions(+), 14 deletions(-) diff --git a/sdk/objc/components/audio/RTCAudioSession+Private.h b/sdk/objc/components/audio/RTCAudioSession+Private.h index 8ee4fddd7f..4f5107f7e9 100644 --- a/sdk/objc/components/audio/RTCAudioSession+Private.h +++ b/sdk/objc/components/audio/RTCAudioSession+Private.h @@ -73,6 +73,12 @@ NS_ASSUME_NONNULL_BEGIN /** Returns a configuration error with the given description. */ - (NSError *)configurationErrorWithDescription:(NSString *)description; +/** Notifies the reciever that a playout glitch was detected. */ +- (void)notifyDidDetectPlayoutGlitch:(int64_t)totalNumberOfGlitches; + +/** Notifies the reciever that there was an error when starting an audio unit. */ +- (void)notifyAudioUnitStartFailedWithError:(OSStatus)error; + // Properties and methods for tests. - (void)notifyDidBeginInterruption; - (void)notifyDidEndInterruptionWithShouldResumeSession:(BOOL)shouldResumeSession; @@ -83,7 +89,6 @@ NS_ASSUME_NONNULL_BEGIN - (void)notifyDidChangeCanPlayOrRecord:(BOOL)canPlayOrRecord; - (void)notifyDidStartPlayOrRecord; - (void)notifyDidStopPlayOrRecord; -- (void)notifyDidDetectPlayoutGlitch:(int64_t)totalNumberOfGlitches; @end diff --git a/sdk/objc/components/audio/RTCAudioSession.h b/sdk/objc/components/audio/RTCAudioSession.h index 58811553b3..3b83b27ba5 100644 --- a/sdk/objc/components/audio/RTCAudioSession.h +++ b/sdk/objc/components/audio/RTCAudioSession.h @@ -99,6 +99,9 @@ RTC_OBJC_EXPORT failedToSetActive:(BOOL)active error:(NSError *)error; +- (void)audioSession:(RTC_OBJC_TYPE(RTCAudioSession) *)audioSession + audioUnitStartFailedWithError:(NSError *)error; + @end /** This is a protocol used to inform RTCAudioSession when the audio session diff --git a/sdk/objc/components/audio/RTCAudioSession.mm b/sdk/objc/components/audio/RTCAudioSession.mm index 057f62cf27..5031e1542e 100644 --- a/sdk/objc/components/audio/RTCAudioSession.mm +++ b/sdk/objc/components/audio/RTCAudioSession.mm @@ -794,6 +794,18 @@ NSString * const kRTCAudioSessionOutputVolumeSelector = @"outputVolume"; } } +- (void)notifyAudioUnitStartFailedWithError:(OSStatus)error { + for (auto delegate : self.delegates) { + SEL sel = @selector(audioSession:audioUnitStartFailedWithError:); + if ([delegate respondsToSelector:sel]) { + [delegate audioSession:self + audioUnitStartFailedWithError:[NSError errorWithDomain:kRTCAudioSessionErrorDomain + code:error + userInfo:nil]]; + } + } +} + - (void)notifyDidBeginInterruption { for (auto delegate : self.delegates) { SEL sel = @selector(audioSessionDidBeginInterruption:); diff --git a/sdk/objc/native/src/audio/audio_device_ios.mm b/sdk/objc/native/src/audio/audio_device_ios.mm index e3020ec1df..3ec7d0b75a 100644 --- a/sdk/objc/native/src/audio/audio_device_ios.mm +++ b/sdk/objc/native/src/audio/audio_device_ios.mm @@ -235,8 +235,11 @@ int32_t AudioDeviceIOS::StartPlayout() { fine_audio_buffer_->ResetPlayout(); } if (!recording_ && audio_unit_->GetState() == VoiceProcessingAudioUnit::kInitialized) { - if (!audio_unit_->Start()) { - RTCLogError(@"StartPlayout failed to start audio unit."); + OSStatus result = audio_unit_->Start(); + if (result != noErr) { + RTC_OBJC_TYPE(RTCAudioSession)* session = [RTC_OBJC_TYPE(RTCAudioSession) sharedInstance]; + [session notifyAudioUnitStartFailedWithError:result]; + RTCLogError(@"StartPlayout failed to start audio unit, reason %d", result); return -1; } RTC_LOG(LS_INFO) << "Voice-Processing I/O audio unit is now started"; @@ -288,8 +291,11 @@ int32_t AudioDeviceIOS::StartRecording() { fine_audio_buffer_->ResetRecord(); } if (!playing_ && audio_unit_->GetState() == VoiceProcessingAudioUnit::kInitialized) { - if (!audio_unit_->Start()) { - RTCLogError(@"StartRecording failed to start audio unit."); + OSStatus result = audio_unit_->Start(); + if (result != noErr) { + RTC_OBJC_TYPE(RTCAudioSession)* session = [RTC_OBJC_TYPE(RTCAudioSession) sharedInstance]; + [session notifyAudioUnitStartFailedWithError:result]; + RTCLogError(@"StartRecording failed to start audio unit, reason %d", result); return -1; } RTC_LOG(LS_INFO) << "Voice-Processing I/O audio unit is now started"; @@ -620,9 +626,16 @@ void AudioDeviceIOS::HandleSampleRateChange(float sample_rate) { } // Restart the audio unit if it was already running. - if (restart_audio_unit && !audio_unit_->Start()) { - RTCLogError(@"Failed to start audio unit with sample rate: %f", session_sample_rate); - return; + if (restart_audio_unit) { + OSStatus result = audio_unit_->Start(); + if (result != noErr) { + RTC_OBJC_TYPE(RTCAudioSession)* session = [RTC_OBJC_TYPE(RTCAudioSession) sharedInstance]; + [session notifyAudioUnitStartFailedWithError:result]; + RTCLogError(@"Failed to start audio unit with sample rate: %f, reason %d", + session_sample_rate, + result); + return; + } } RTCLog(@"Successfully handled sample rate change."); } @@ -801,8 +814,10 @@ void AudioDeviceIOS::UpdateAudioUnit(bool can_play_or_record) { // Log session settings before trying to start audio streaming. RTC_OBJC_TYPE(RTCAudioSession)* session = [RTC_OBJC_TYPE(RTCAudioSession) sharedInstance]; RTCLog(@"%@", session); - if (!audio_unit_->Start()) { - RTCLogError(@"Failed to start audio unit."); + OSStatus result = audio_unit_->Start(); + if (result != noErr) { + [session notifyAudioUnitStartFailedWithError:result]; + RTCLogError(@"Failed to start audio unit, reason %d", result); return; } } diff --git a/sdk/objc/native/src/audio/voice_processing_audio_unit.h b/sdk/objc/native/src/audio/voice_processing_audio_unit.h index 72e29c0d67..ed9dd98568 100644 --- a/sdk/objc/native/src/audio/voice_processing_audio_unit.h +++ b/sdk/objc/native/src/audio/voice_processing_audio_unit.h @@ -78,7 +78,7 @@ class VoiceProcessingAudioUnit { bool Initialize(Float64 sample_rate); // Starts the underlying audio unit. - bool Start(); + OSStatus Start(); // Stops the underlying audio unit. bool Stop(); diff --git a/sdk/objc/native/src/audio/voice_processing_audio_unit.mm b/sdk/objc/native/src/audio/voice_processing_audio_unit.mm index 2325b2ed2e..661022f523 100644 --- a/sdk/objc/native/src/audio/voice_processing_audio_unit.mm +++ b/sdk/objc/native/src/audio/voice_processing_audio_unit.mm @@ -332,19 +332,19 @@ bool VoiceProcessingAudioUnit::Initialize(Float64 sample_rate) { return true; } -bool VoiceProcessingAudioUnit::Start() { +OSStatus VoiceProcessingAudioUnit::Start() { RTC_DCHECK_GE(state_, kUninitialized); RTCLog(@"Starting audio unit."); OSStatus result = AudioOutputUnitStart(vpio_unit_); if (result != noErr) { RTCLogError(@"Failed to start audio unit. Error=%ld", (long)result); - return false; + return result; } else { RTCLog(@"Started audio unit"); } state_ = kStarted; - return true; + return noErr; } bool VoiceProcessingAudioUnit::Stop() {