Add a render error callback from AudioDeviceIOS to AudioDeviceModuleIOS.
This change expands on https://webrtc-review.googlesource.com/c/src/+/374420 to cover the error produced when copying microphone samples.
Change-Id: I7aa58c9c9ac175d5f4cfdb60bbd3f16334c03c1b
Bug: webrtc:390314937
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/375540
Reviewed-by: Jakob Ivarsson‎ <jakobi@webrtc.org>
Reviewed-by: Henrik Andreassson <henrika@webrtc.org>
Commit-Queue: Peter Hanspers <peterhanspers@webrtc.org>
Cr-Commit-Position: refs/heads/main@{#43826}
diff --git a/modules/audio_device/audio_device_impl.cc b/modules/audio_device/audio_device_impl.cc
index b084ff2..4560529 100644
--- a/modules/audio_device/audio_device_impl.cc
+++ b/modules/audio_device/audio_device_impl.cc
@@ -243,7 +243,8 @@
if (audio_layer == kPlatformDefaultAudio) {
audio_device_.reset(new ios_adm::AudioDeviceIOS(
/*bypass_voice_processing=*/false,
- /*muted_speech_event_handler=*/nullptr));
+ /*muted_speech_event_handler=*/nullptr,
+ /*render_error_handler=*/nullptr));
RTC_LOG(LS_INFO) << "iPhone Audio APIs will be utilized.";
}
// END #if defined(WEBRTC_IOS)
diff --git a/sdk/objc/native/src/audio/audio_device_ios.h b/sdk/objc/native/src/audio/audio_device_ios.h
index caecf12..bbb4025 100644
--- a/sdk/objc/native/src/audio/audio_device_ios.h
+++ b/sdk/objc/native/src/audio/audio_device_ios.h
@@ -33,6 +33,11 @@
namespace ios_adm {
+// A callback handler for audio device rendering errors.
+// Note: Called on a realtime thread.
+// Note: Only applies to input rendering errors, not output.
+typedef void (^AudioDeviceIOSRenderErrorHandler)(OSStatus error);
+
// Implements full duplex 16-bit mono PCM audio support for iOS using a
// Voice-Processing (VP) I/O audio unit in Core Audio. The VP I/O audio unit
// supports audio echo cancellation. It also adds automatic gain control,
@@ -52,7 +57,8 @@
public:
explicit AudioDeviceIOS(
bool bypass_voice_processing,
- AudioDeviceModule::MutedSpeechEventHandler muted_speech_event_handler);
+ AudioDeviceModule::MutedSpeechEventHandler muted_speech_event_handler,
+ AudioDeviceIOSRenderErrorHandler render_error_handler);
~AudioDeviceIOS() override;
void AttachAudioBuffer(AudioDeviceBuffer* audioBuffer) override;
@@ -220,6 +226,13 @@
// Handle a user speaking during muted event
AudioDeviceModule::MutedSpeechEventHandler muted_speech_event_handler_;
+ // Handle microphone rendering errors.
+ AudioDeviceIOSRenderErrorHandler render_error_handler_;
+
+ // Copying microphone data (rendering to a buffer) may keep failing. This
+ // field makes sure subsequent errors are not reported.
+ bool disregard_next_render_error_;
+
// Native I/O audio thread checker.
SequenceChecker io_thread_checker_;
diff --git a/sdk/objc/native/src/audio/audio_device_ios.mm b/sdk/objc/native/src/audio/audio_device_ios.mm
index 49734a2..dd7dcc4 100644
--- a/sdk/objc/native/src/audio/audio_device_ios.mm
+++ b/sdk/objc/native/src/audio/audio_device_ios.mm
@@ -97,9 +97,12 @@
AudioDeviceIOS::AudioDeviceIOS(
bool bypass_voice_processing,
- AudioDeviceModule::MutedSpeechEventHandler muted_speech_event_handler)
+ AudioDeviceModule::MutedSpeechEventHandler muted_speech_event_handler,
+ AudioDeviceIOSRenderErrorHandler render_error_handler)
: bypass_voice_processing_(bypass_voice_processing),
muted_speech_event_handler_(muted_speech_event_handler),
+ render_error_handler_(render_error_handler),
+ disregard_next_render_error_(false),
audio_device_buffer_(nullptr),
audio_unit_(nullptr),
recording_(0),
@@ -431,13 +434,18 @@
// to the preallocated audio buffer list that the audio unit renders into.
// We can make the audio unit provide a buffer instead in io_data, but we
// currently just use our own.
- // TODO(henrika): should error handling be improved?
result = audio_unit_->Render(
flags, time_stamp, bus_number, num_frames, &audio_buffer_list);
if (result != noErr) {
RTCLogError(@"Failed to render audio.");
+ if (render_error_handler_ && !disregard_next_render_error_) {
+ disregard_next_render_error_ = true;
+ thread_->PostTask(
+ SafeTask(safety_, [this, result] { render_error_handler_(result); }));
+ }
return result;
}
+ disregard_next_render_error_ = false;
// Get a pointer to the recorded audio and send it to the WebRTC ADB.
// Use the FineAudioBuffer instance to convert between native buffer size
diff --git a/sdk/objc/native/src/audio/audio_device_module_ios.mm b/sdk/objc/native/src/audio/audio_device_module_ios.mm
index 3a7abb4..3b338f2 100644
--- a/sdk/objc/native/src/audio/audio_device_module_ios.mm
+++ b/sdk/objc/native/src/audio/audio_device_module_ios.mm
@@ -84,10 +84,13 @@
RTC_DLOG(LS_INFO) << __FUNCTION__;
if (initialized_) return 0;
+ AudioDeviceIOSRenderErrorHandler error_handler = ^(OSStatus error) {
+ ReportError(kRecordingDeviceFailed);
+ };
audio_device_buffer_.reset(
new webrtc::AudioDeviceBuffer(task_queue_factory_.get()));
- audio_device_.reset(new ios_adm::AudioDeviceIOS(bypass_voice_processing_,
- muted_speech_event_handler_));
+ audio_device_.reset(new ios_adm::AudioDeviceIOS(
+ bypass_voice_processing_, muted_speech_event_handler_, error_handler));
RTC_CHECK(audio_device_);
this->AttachAudioBuffer();
diff --git a/sdk/objc/unittests/RTCAudioDevice_xctest.mm b/sdk/objc/unittests/RTCAudioDevice_xctest.mm
index 299e5cb..1d48677 100644
--- a/sdk/objc/unittests/RTCAudioDevice_xctest.mm
+++ b/sdk/objc/unittests/RTCAudioDevice_xctest.mm
@@ -48,7 +48,8 @@
_audioDeviceModule = webrtc::CreateAudioDeviceModule();
_audio_device.reset(new webrtc::ios_adm::AudioDeviceIOS(
/*bypass_voice_processing=*/false,
- /*muted_speech_event_handler=*/nullptr));
+ /*muted_speech_event_handler=*/nullptr,
+ /*render_error_handler=*/nullptr));
self.audioSession = [RTC_OBJC_TYPE(RTCAudioSession) sharedInstance];
NSError *error = nil;
@@ -149,7 +150,8 @@
_audio_device.reset(new webrtc::ios_adm::AudioDeviceIOS(
/*bypass_voice_processing=*/false,
- /*muted_speech_event_handler=*/muted_speech_event_handler));
+ /*muted_speech_event_handler=*/muted_speech_event_handler,
+ /*render_error_handler=*/nullptr));
_audio_device->OnReceivedMutedSpeechActivity(
kAUVoiceIOSpeechActivityHasStarted);
@@ -168,7 +170,8 @@
_audio_device.reset(new webrtc::ios_adm::AudioDeviceIOS(
/*bypass_voice_processing=*/false,
- /*muted_speech_event_handler=*/muted_speech_event_handler));
+ /*muted_speech_event_handler=*/muted_speech_event_handler,
+ /*render_error_handler=*/nullptr));
_audio_device->OnReceivedMutedSpeechActivity(
kAUVoiceIOSpeechActivityHasEnded);
[self waitForExpectations:@[ handlerExpectation ] timeout:10.0];