Flutter音频流重连:iOS锁屏下AVFAudio错误561145187问题求助
iOS锁屏下音频流重连失败(错误码561145187)解决方案
问题概况
- 触发场景:音频流因第二设备关闭重启中断后执行重连逻辑,iPhone锁屏状态下启动重连必现错误
- 异常表现:前几次重连全部失败,热重启应用后重试可正常恢复通话
- 错误码对应:
AVAudioSession.ErrorCode.cannotStartRecording
错误日志
[aurioc] AURemoteIO.cpp:1668 AUIOClient_StartIO failed (561145187)
[avae] AVAEInternal.h:109 [AVAudioEngineGraph.mm:1545:Start: (err = PerformCommand(*ioNode, kAUStartIO, NULL, 0)): error 561145187
Flutter : PlatformException(record, The operation couldn’t be completed. (com.apple.coreaudio.avfaudio error 561145187.), null, null)
已尝试无效的配置
重连前已调用AudioSession配置,但问题依旧:
Future<AudioSession> configureAudioSession() async { final session = await AudioSession.instance; await session.configure(AudioSessionConfiguration( avAudioSessionCategory: AVAudioSessionCategory.playAndRecord, avAudioSessionCategoryOptions: AVAudioSessionCategoryOptions.defaultToSpeaker | AVAudioSessionCategoryOptions.mixWithOthers | AVAudioSessionCategoryOptions.allowBluetooth | AVAudioSessionCategoryOptions.interruptSpokenAudioAndMixWithOthers, avAudioSessionMode: AVAudioSessionMode.voiceChat, avAudioSessionRouteSharingPolicy: AVAudioSessionRouteSharingPolicy.defaultPolicy, avAudioSessionSetActiveOptions: AVAudioSessionSetActiveOptions.notifyOthersOnDeactivation, androidAudioAttributes: const AndroidAudioAttributes( contentType: AndroidAudioContentType.speech, flags: AndroidAudioFlags.none, usage: AndroidAudioUsage.voiceCommunication, ), androidAudioFocusGainType: AndroidAudioFocusGainType.gain, androidWillPauseWhenDucked: true, )); return session; }
启动音频流代码:
final stream = await audioRecorder.startStream(const RecordConfig( encoder: AudioEncoder.pcm16bits, bitRate: audioRecorderChannelCount * BabyMonitorCallService.sampleRate * 16, sampleRate: BabyMonitorCallService.sampleRate, numChannels: audioRecorderChannelCount, autoGain: true, noiseSuppress: true)); final streamSub = await _subscribeToPcm16Stream(stream, onFrame);
可行解决方案建议
1. 彻底重置AudioSession状态
锁屏下错误大概率是旧会话状态残留导致,重连前先强制停用再重新激活:
Future<AudioSession> reconfigureAudioSession() async { final session = await AudioSession.instance; // 先尝试停用旧会话,给系统释放资源的时间 try { await session.setActive(false, options: AVAudioSessionSetActiveOptions.notifyOthersOnDeactivation); await Future.delayed(const Duration(milliseconds: 100)); } catch (e) { // 忽略停用失败的情况,继续下一步 } // 重新配置,移除锁屏下可能干扰权限的选项 await session.configure(AudioSessionConfiguration( avAudioSessionCategory: AVAudioSessionCategory.playAndRecord, // 只保留必要选项:默认扬声器+允许蓝牙,mixWithOthers会降低锁屏录音优先级 avAudioSessionCategoryOptions: AVAudioSessionCategoryOptions.defaultToSpeaker | AVAudioSessionCategoryOptions.allowBluetooth, avAudioSessionMode: AVAudioSessionMode.voiceChat, avAudioSessionSetActiveOptions: AVAudioSessionSetActiveOptions.none, // Android配置保持不变 androidAudioAttributes: const AndroidAudioAttributes( contentType: AndroidAudioContentType.speech, flags: AndroidAudioFlags.none, usage: AndroidAudioUsage.voiceCommunication, ), androidAudioFocusGainType: AndroidAudioFocusGainType.gain, androidWillPauseWhenDucked: true, )); // 主动激活会话 await session.setActive(true); return session; }
2. 增加重连重试机制
针对561145187错误做指数退避重试,每次重试前重置会话:
Future<void> attemptReconnect(int retryCount) async { if (retryCount >= 3) return; // 限制重试次数,避免无限循环 try { await reconfigureAudioSession(); final stream = await audioRecorder.startStream(const RecordConfig( encoder: AudioEncoder.pcm16bits, bitRate: audioRecorderChannelCount * BabyMonitorCallService.sampleRate * 16, sampleRate: BabyMonitorCallService.sampleRate, numChannels: audioRecorderChannelCount, autoGain: true, noiseSuppress: true )); final streamSub = await _subscribeToPcm16Stream(stream, onFrame); } on PlatformException catch (e) { if (e.code == 'record' && e.message?.contains('561145187') == true) { // 指数退避延迟:200ms、400ms、600ms await Future.delayed(Duration(milliseconds: 200 * (retryCount + 1))); await attemptReconnect(retryCount + 1); } } }
3. 检查后台模式配置
确保Xcode中已开启Audio, AirPlay, and Picture in Picture后台权限,锁屏下应用需要该权限才能正常操作音频资源。
4. 监听路由变化并主动切换
重连前确认音频路由可用,避免设备断开后的路由残留问题:
// 提前监听路由变化 final session = await AudioSession.instance; session.onRouteChanged.listen((event) { if (event.reason == AVAudioSessionRouteChangeReason.oldDeviceUnavailable) { // 标记需要重新检查路由 _needRouteReset = true; } }); // 重连前主动切换到内置设备 if (_needRouteReset) { await session.setPreferredInputType(AVAudioSessionPortBuiltInMic); await session.setPreferredOutputType(AVAudioSessionPortBuiltInSpeaker); _needRouteReset = false; }
核心原因分析
锁屏下iOS对录音权限的限制更严格,mixWithOthers等混音选项会降低系统授予录音权限的优先级;同时旧AudioSession状态未彻底释放,导致新会话无法获取资源。热重启能解决问题,也验证了是会话状态残留的问题。
内容的提问来源于stack exchange,提问作者William
相关产品推荐
相关产品推荐

