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}
This commit is contained in:
Peter Hanspers 2025-01-30 10:38:57 +01:00 committed by WebRTC LUCI CQ
parent 4b39cb3eb6
commit d643be9fdc
5 changed files with 37 additions and 9 deletions

View File

@ -243,7 +243,8 @@ int32_t AudioDeviceModuleImpl::CreatePlatformSpecificObjects() {
if (audio_layer == kPlatformDefaultAudio) { if (audio_layer == kPlatformDefaultAudio) {
audio_device_.reset(new ios_adm::AudioDeviceIOS( audio_device_.reset(new ios_adm::AudioDeviceIOS(
/*bypass_voice_processing=*/false, /*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."; RTC_LOG(LS_INFO) << "iPhone Audio APIs will be utilized.";
} }
// END #if defined(WEBRTC_IOS) // END #if defined(WEBRTC_IOS)

View File

@ -33,6 +33,11 @@ class FineAudioBuffer;
namespace ios_adm { 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 // 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 // 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, // supports audio echo cancellation. It also adds automatic gain control,
@ -52,7 +57,8 @@ class AudioDeviceIOS : public AudioDeviceGeneric,
public: public:
explicit AudioDeviceIOS( explicit AudioDeviceIOS(
bool bypass_voice_processing, bool bypass_voice_processing,
AudioDeviceModule::MutedSpeechEventHandler muted_speech_event_handler); AudioDeviceModule::MutedSpeechEventHandler muted_speech_event_handler,
AudioDeviceIOSRenderErrorHandler render_error_handler);
~AudioDeviceIOS() override; ~AudioDeviceIOS() override;
void AttachAudioBuffer(AudioDeviceBuffer* audioBuffer) override; void AttachAudioBuffer(AudioDeviceBuffer* audioBuffer) override;
@ -220,6 +226,13 @@ class AudioDeviceIOS : public AudioDeviceGeneric,
// Handle a user speaking during muted event // Handle a user speaking during muted event
AudioDeviceModule::MutedSpeechEventHandler muted_speech_event_handler_; 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. // Native I/O audio thread checker.
SequenceChecker io_thread_checker_; SequenceChecker io_thread_checker_;

View File

@ -97,9 +97,12 @@ static void LogDeviceInfo() {
AudioDeviceIOS::AudioDeviceIOS( AudioDeviceIOS::AudioDeviceIOS(
bool bypass_voice_processing, 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), : bypass_voice_processing_(bypass_voice_processing),
muted_speech_event_handler_(muted_speech_event_handler), muted_speech_event_handler_(muted_speech_event_handler),
render_error_handler_(render_error_handler),
disregard_next_render_error_(false),
audio_device_buffer_(nullptr), audio_device_buffer_(nullptr),
audio_unit_(nullptr), audio_unit_(nullptr),
recording_(0), recording_(0),
@ -431,13 +434,18 @@ OSStatus AudioDeviceIOS::OnDeliverRecordedData(
// to the preallocated audio buffer list that the audio unit renders into. // 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 // We can make the audio unit provide a buffer instead in io_data, but we
// currently just use our own. // currently just use our own.
// TODO(henrika): should error handling be improved?
result = audio_unit_->Render( result = audio_unit_->Render(
flags, time_stamp, bus_number, num_frames, &audio_buffer_list); flags, time_stamp, bus_number, num_frames, &audio_buffer_list);
if (result != noErr) { if (result != noErr) {
RTCLogError(@"Failed to render audio."); 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; return result;
} }
disregard_next_render_error_ = false;
// Get a pointer to the recorded audio and send it to the WebRTC ADB. // Get a pointer to the recorded audio and send it to the WebRTC ADB.
// Use the FineAudioBuffer instance to convert between native buffer size // Use the FineAudioBuffer instance to convert between native buffer size

View File

@ -84,10 +84,13 @@ int32_t AudioDeviceModuleIOS::Init() {
RTC_DLOG(LS_INFO) << __FUNCTION__; RTC_DLOG(LS_INFO) << __FUNCTION__;
if (initialized_) return 0; if (initialized_) return 0;
AudioDeviceIOSRenderErrorHandler error_handler = ^(OSStatus error) {
ReportError(kRecordingDeviceFailed);
};
audio_device_buffer_.reset( audio_device_buffer_.reset(
new webrtc::AudioDeviceBuffer(task_queue_factory_.get())); new webrtc::AudioDeviceBuffer(task_queue_factory_.get()));
audio_device_.reset(new ios_adm::AudioDeviceIOS(bypass_voice_processing_, audio_device_.reset(new ios_adm::AudioDeviceIOS(
muted_speech_event_handler_)); bypass_voice_processing_, muted_speech_event_handler_, error_handler));
RTC_CHECK(audio_device_); RTC_CHECK(audio_device_);
this->AttachAudioBuffer(); this->AttachAudioBuffer();

View File

@ -48,7 +48,8 @@
_audioDeviceModule = webrtc::CreateAudioDeviceModule(); _audioDeviceModule = webrtc::CreateAudioDeviceModule();
_audio_device.reset(new webrtc::ios_adm::AudioDeviceIOS( _audio_device.reset(new webrtc::ios_adm::AudioDeviceIOS(
/*bypass_voice_processing=*/false, /*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]; self.audioSession = [RTC_OBJC_TYPE(RTCAudioSession) sharedInstance];
NSError *error = nil; NSError *error = nil;
@ -149,7 +150,8 @@
_audio_device.reset(new webrtc::ios_adm::AudioDeviceIOS( _audio_device.reset(new webrtc::ios_adm::AudioDeviceIOS(
/*bypass_voice_processing=*/false, /*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( _audio_device->OnReceivedMutedSpeechActivity(
kAUVoiceIOSpeechActivityHasStarted); kAUVoiceIOSpeechActivityHasStarted);
@ -168,7 +170,8 @@
_audio_device.reset(new webrtc::ios_adm::AudioDeviceIOS( _audio_device.reset(new webrtc::ios_adm::AudioDeviceIOS(
/*bypass_voice_processing=*/false, /*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( _audio_device->OnReceivedMutedSpeechActivity(
kAUVoiceIOSpeechActivityHasEnded); kAUVoiceIOSpeechActivityHasEnded);
[self waitForExpectations:@[ handlerExpectation ] timeout:10.0]; [self waitForExpectations:@[ handlerExpectation ] timeout:10.0];