Description
Environment
Plugin: record 6.2.1 / record_ios 1.2.1
iOS: 26.5 (physical device)
Flutter: 3.x
Problem
Calling startStream() for the first time on iOS takes 3+ seconds. During this time, the Flutter UI freezes completely. Subsequent calls are faster but still around 700ms. Additionally, if an existing AVAudioEngine or NativeAudioIOS playback is active (e.g. a live video stream), startStream() breaks the existing audio — causing AU render err: -1 spam in the console.
Our use case is a live video player with an intercom (talk) feature: the user watches a live video stream with audio, then presses a button to start sending audio to the device. startStream() is called to capture microphone input.
Root Cause Analysis
I've identified three specific issues in the iOS implementation:
- AVAudioEngine created fresh on every startStream() call
File: RecorderStreamDelegate.swift:27
swift
应用
let audioEngine = AVAudioEngine()
A new AVAudioEngine is allocated every time start() is called. After stop() (line 86-88), it's destroyed:
swift
应用
m_audioEngine?.inputNode.removeTap(onBus: m_bus)
m_audioEngine?.stop()
m_audioEngine = nil // engine destroyed, next start() must re-create
The first AVAudioEngine.start() on iOS involves heavy initialization: allocating AudioUnits, configuring the audio processing pipeline, setting up input routing. This is the primary cause of the 3-second delay.
Suggested fix: Create the AVAudioEngine once and keep it alive, using pause()/resume() instead of stop()/destroy.
- setActive(true) conflicts with existing audio playback
File: RecorderSessionExtension.swift:25-31
if manage {
try audioSession.setCategory(.playAndRecord, options: ...)
try audioSession.setActive(true, options: .notifyOthersOnDeactivation)
}
Even when the AVAudioSession is already in .playAndRecord mode (set by the app), the plugin unconditionally calls setCategory + setActive(true). This deactivates and reactivates the audio session, interrupting any existing playback. This causes AU render err: -1 and breaks the video audio during talk initiation.
Suggested fix: Check if the session is already in the desired category/state before re-activating. Or expose a config option to skip internal session management when the app handles it externally.
- audioEngine.start() blocks the main thread
File: RecorderStreamDelegate.swift:70-71
audioEngine.prepare()
try audioEngine.start()
Both prepare() and start() run synchronously on the main thread (called from the Flutter MethodChannel handler). On first use, AVAudioEngine.start() takes 2-3 seconds because iOS initializes the audio input hardware, configures voice processing I/O, and sets up routing — all synchronously. This blocks the Flutter platform thread and freezes the UI.
Suggested fix: Move prepare() to happen eagerly (e.g. on engine creation), and consider offloading start() to a background queue if possible, or at minimum document that the first call will be slow.
Additional observation
setVoiceProcessingEnabled(true) in line 30 also adds to the initialization cost. Voice processing I/O requires additional setup on the AudioUnit.
Reproduction Steps
Start an AVAudioEngine or other audio playback (simulating video audio)
Call AudioRecorder.startStream() with:
encoder: pcm16bits
sampleRate: 8000
numChannels: 1
echoCancel: true
autoGain: true
Observe 3+ second freeze on first call
Console shows AU render err: -1 spam
Expected Behavior
First startStream() should complete in < 500ms
Should not block Flutter UI rendering
Should not break existing audio playback
Description
Environment
Plugin: record 6.2.1 / record_ios 1.2.1
iOS: 26.5 (physical device)
Flutter: 3.x
Problem
Calling startStream() for the first time on iOS takes 3+ seconds. During this time, the Flutter UI freezes completely. Subsequent calls are faster but still around 700ms. Additionally, if an existing AVAudioEngine or NativeAudioIOS playback is active (e.g. a live video stream), startStream() breaks the existing audio — causing AU render err: -1 spam in the console.
Our use case is a live video player with an intercom (talk) feature: the user watches a live video stream with audio, then presses a button to start sending audio to the device. startStream() is called to capture microphone input.
Root Cause Analysis
I've identified three specific issues in the iOS implementation:
File: RecorderStreamDelegate.swift:27
swift
应用
let audioEngine = AVAudioEngine()
A new AVAudioEngine is allocated every time start() is called. After stop() (line 86-88), it's destroyed:
swift
应用
m_audioEngine?.inputNode.removeTap(onBus: m_bus)
m_audioEngine?.stop()
m_audioEngine = nil // engine destroyed, next start() must re-create
The first AVAudioEngine.start() on iOS involves heavy initialization: allocating AudioUnits, configuring the audio processing pipeline, setting up input routing. This is the primary cause of the 3-second delay.
Suggested fix: Create the AVAudioEngine once and keep it alive, using pause()/resume() instead of stop()/destroy.
File: RecorderSessionExtension.swift:25-31
if manage {
try audioSession.setCategory(.playAndRecord, options: ...)
try audioSession.setActive(true, options: .notifyOthersOnDeactivation)
}
Even when the AVAudioSession is already in .playAndRecord mode (set by the app), the plugin unconditionally calls setCategory + setActive(true). This deactivates and reactivates the audio session, interrupting any existing playback. This causes AU render err: -1 and breaks the video audio during talk initiation.
Suggested fix: Check if the session is already in the desired category/state before re-activating. Or expose a config option to skip internal session management when the app handles it externally.
File: RecorderStreamDelegate.swift:70-71
audioEngine.prepare()
try audioEngine.start()
Both prepare() and start() run synchronously on the main thread (called from the Flutter MethodChannel handler). On first use, AVAudioEngine.start() takes 2-3 seconds because iOS initializes the audio input hardware, configures voice processing I/O, and sets up routing — all synchronously. This blocks the Flutter platform thread and freezes the UI.
Suggested fix: Move prepare() to happen eagerly (e.g. on engine creation), and consider offloading start() to a background queue if possible, or at minimum document that the first call will be slow.
Additional observation
setVoiceProcessingEnabled(true) in line 30 also adds to the initialization cost. Voice processing I/O requires additional setup on the AudioUnit.
Reproduction Steps
Start an AVAudioEngine or other audio playback (simulating video audio)
Call AudioRecorder.startStream() with:
encoder: pcm16bits
sampleRate: 8000
numChannels: 1
echoCancel: true
autoGain: true
Observe 3+ second freeze on first call
Console shows AU render err: -1 spam
Expected Behavior
First startStream() should complete in < 500ms
Should not block Flutter UI rendering
Should not break existing audio playback