Issue Description
During an active call on iOS devices (even while running in the foreground), local audio capture fails to start, resulting in complete silence. The application remains running, but the local microphone feed is not transmitted to the channel.
Platform and SDK Context
Operating System: iOS (e.g., iOS 16)
SDK Component: Agora RTC SDK
Target Diagnostic Error/Trace:
Start: Failed to start audio unit, Error 561145187Start: Failed to start audio unit, Error 2003329396(kAudioHardwareIllegalOperationError)StartRecording: start audio unit failed
Root Cause Analysis
This issue occurs when local audio toggle APIs (such as enableLocalAudio) are called in rapid, high-frequency succession, or when the application's audio session setup conflicts with the SDK's internal driver lifecycle.
When enableLocalAudio(true/false) or muteLocalAudioStream is executed, the SDK communicates directly with the iOS AVAudioSession and AudioUnit APIs to allocate or release hardware capture resources. Rapidly toggling these APIs within milliseconds prevents the OS CoreAudio subsystem from finalizing hardware state transitions. As a result, the AudioUnit enters an invalid or locked state, throwing system OSStatus error codes (such as 561145187 or 2003329396) and suspending all subsequent recording initialization attempts.
Solution and Resolution Steps
Debounce and Restrict High-Frequency Audio API Calls
Implement rate-limiting or debouncing on UI toggles (such as Mute/Unmute buttons) to prevent calling
enableLocalAudioorenableAudioin rapid succession.Best Practice: Maintain an execution state flag or throttle UI inputs to ensure at least 300ms–500ms elapses between consecutive audio hardware state changes.
Standardize
AVAudioSessionLifecycle ManagementEnsure the application's native audio session configuration does not compete with the RTC SDK's internal audio driver:
Verify that the active category is set to
AVAudioSessionCategoryPlayAndRecordbefore starting capture.Avoid manually deactivating
AVAudioSession.sharedInstance()while the RTC engine is actively publishing or rendering audio.
Differentiate Between Audio Capture Control and Audio Publishing Control
To temporarily silence a user without destroying the underlying iOS
AudioUnitpipeline:Use
muteLocalAudioStream(true/false): This stops publishing audio packets to the network while keeping the hardwareAudioUnitactive and pre-warmed.Avoid
enableLocalAudio(false)for UI mutes: Only useenableLocalAudio(false)when you explicitly need to completely shut down the microphone hardware.
Implement Engine Recovery Sequence
If an
AudioUniterror is caught, trigger a controlled audio engine reset:Call
enableLocalAudio(false).Wait a brief delay (e.g., 300ms–500ms) for the iOS CoreAudio thread to release hardware locks.
Re-enable capture via
enableLocalAudio(true).
Prevention and Best Practice
To ensure reliable microphone capture across iOS updates:
Use Mute over Hardware Disable: Prefer stream muting (
muteLocalAudioStream) over full hardware pipeline tear-down (enableLocalAudio) for standard in-call user controls.Handle System Interruption Callbacks: Listen to
AVAudioSession.interruptionNotification(e.g., phone calls, Siri triggers) and ensure audio recovery logic executes only after the interruption state officially ends.