Surface audio unit errors.

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 <peterhanspers@webrtc.org>
Reviewed-by: Henrik Andreassson <henrika@webrtc.org>
Reviewed-by: Xavier Lepaul‎ <xalep@webrtc.org>
Cr-Commit-Position: refs/heads/main@{#34862}
This commit is contained in:
Peter Hanspers
2021-08-26 17:56:44 +02:00
committed by WebRTC LUCI CQ
parent fb0dca6c05
commit e5b4e941a0
6 changed files with 49 additions and 14 deletions

View File

@ -73,6 +73,12 @@ NS_ASSUME_NONNULL_BEGIN
/** Returns a configuration error with the given description. */ /** Returns a configuration error with the given description. */
- (NSError *)configurationErrorWithDescription:(NSString *)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. // Properties and methods for tests.
- (void)notifyDidBeginInterruption; - (void)notifyDidBeginInterruption;
- (void)notifyDidEndInterruptionWithShouldResumeSession:(BOOL)shouldResumeSession; - (void)notifyDidEndInterruptionWithShouldResumeSession:(BOOL)shouldResumeSession;
@ -83,7 +89,6 @@ NS_ASSUME_NONNULL_BEGIN
- (void)notifyDidChangeCanPlayOrRecord:(BOOL)canPlayOrRecord; - (void)notifyDidChangeCanPlayOrRecord:(BOOL)canPlayOrRecord;
- (void)notifyDidStartPlayOrRecord; - (void)notifyDidStartPlayOrRecord;
- (void)notifyDidStopPlayOrRecord; - (void)notifyDidStopPlayOrRecord;
- (void)notifyDidDetectPlayoutGlitch:(int64_t)totalNumberOfGlitches;
@end @end

View File

@ -99,6 +99,9 @@ RTC_OBJC_EXPORT
failedToSetActive:(BOOL)active failedToSetActive:(BOOL)active
error:(NSError *)error; error:(NSError *)error;
- (void)audioSession:(RTC_OBJC_TYPE(RTCAudioSession) *)audioSession
audioUnitStartFailedWithError:(NSError *)error;
@end @end
/** This is a protocol used to inform RTCAudioSession when the audio session /** This is a protocol used to inform RTCAudioSession when the audio session

View File

@ -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 { - (void)notifyDidBeginInterruption {
for (auto delegate : self.delegates) { for (auto delegate : self.delegates) {
SEL sel = @selector(audioSessionDidBeginInterruption:); SEL sel = @selector(audioSessionDidBeginInterruption:);

View File

@ -235,8 +235,11 @@ int32_t AudioDeviceIOS::StartPlayout() {
fine_audio_buffer_->ResetPlayout(); fine_audio_buffer_->ResetPlayout();
} }
if (!recording_ && audio_unit_->GetState() == VoiceProcessingAudioUnit::kInitialized) { if (!recording_ && audio_unit_->GetState() == VoiceProcessingAudioUnit::kInitialized) {
if (!audio_unit_->Start()) { OSStatus result = audio_unit_->Start();
RTCLogError(@"StartPlayout failed to start audio unit."); 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; return -1;
} }
RTC_LOG(LS_INFO) << "Voice-Processing I/O audio unit is now started"; 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(); fine_audio_buffer_->ResetRecord();
} }
if (!playing_ && audio_unit_->GetState() == VoiceProcessingAudioUnit::kInitialized) { if (!playing_ && audio_unit_->GetState() == VoiceProcessingAudioUnit::kInitialized) {
if (!audio_unit_->Start()) { OSStatus result = audio_unit_->Start();
RTCLogError(@"StartRecording failed to start audio unit."); 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; return -1;
} }
RTC_LOG(LS_INFO) << "Voice-Processing I/O audio unit is now started"; 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. // Restart the audio unit if it was already running.
if (restart_audio_unit && !audio_unit_->Start()) { if (restart_audio_unit) {
RTCLogError(@"Failed to start audio unit with sample rate: %f", session_sample_rate); OSStatus result = audio_unit_->Start();
return; 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."); 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. // Log session settings before trying to start audio streaming.
RTC_OBJC_TYPE(RTCAudioSession)* session = [RTC_OBJC_TYPE(RTCAudioSession) sharedInstance]; RTC_OBJC_TYPE(RTCAudioSession)* session = [RTC_OBJC_TYPE(RTCAudioSession) sharedInstance];
RTCLog(@"%@", session); RTCLog(@"%@", session);
if (!audio_unit_->Start()) { OSStatus result = audio_unit_->Start();
RTCLogError(@"Failed to start audio unit."); if (result != noErr) {
[session notifyAudioUnitStartFailedWithError:result];
RTCLogError(@"Failed to start audio unit, reason %d", result);
return; return;
} }
} }

View File

@ -78,7 +78,7 @@ class VoiceProcessingAudioUnit {
bool Initialize(Float64 sample_rate); bool Initialize(Float64 sample_rate);
// Starts the underlying audio unit. // Starts the underlying audio unit.
bool Start(); OSStatus Start();
// Stops the underlying audio unit. // Stops the underlying audio unit.
bool Stop(); bool Stop();

View File

@ -332,19 +332,19 @@ bool VoiceProcessingAudioUnit::Initialize(Float64 sample_rate) {
return true; return true;
} }
bool VoiceProcessingAudioUnit::Start() { OSStatus VoiceProcessingAudioUnit::Start() {
RTC_DCHECK_GE(state_, kUninitialized); RTC_DCHECK_GE(state_, kUninitialized);
RTCLog(@"Starting audio unit."); RTCLog(@"Starting audio unit.");
OSStatus result = AudioOutputUnitStart(vpio_unit_); OSStatus result = AudioOutputUnitStart(vpio_unit_);
if (result != noErr) { if (result != noErr) {
RTCLogError(@"Failed to start audio unit. Error=%ld", (long)result); RTCLogError(@"Failed to start audio unit. Error=%ld", (long)result);
return false; return result;
} else { } else {
RTCLog(@"Started audio unit"); RTCLog(@"Started audio unit");
} }
state_ = kStarted; state_ = kStarted;
return true; return noErr;
} }
bool VoiceProcessingAudioUnit::Stop() { bool VoiceProcessingAudioUnit::Stop() {