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. */
- (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

View file

@ -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

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

View file

@ -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;
}
}

View file

@ -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();

View file

@ -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() {