Flutter iOS集成CallKit后音频(麦克风/扬声器)失效问题求助
解决Flutter iOS使用CallKit后音频失效的问题
核心原因
iOS的CallKit会系统级接管音频会话,WebRTC的音频配置必须和CallKit的音频生命周期同步,否则会出现音频抢占或权限请求被系统拦截的问题。
具体修复步骤
1. Swift侧:完善CallKit与音频会话的协同配置
修改AppDelegate.swift,在CallKit相关流程中正确管理AVAudioSession,确保CallKit和WebRTC的音频会话不冲突:
import AVFoundation // 在原有pushRegistry方法中,调用showCallkitIncoming后添加音频会话基础配置 func pushRegistry(_ registry: PKPushRegistry, didReceiveIncomingPushWith payload: PKPushPayload, for type: PKPushType, completion: @escaping () -> Void) { // ... 保留原有代码 // Display callkit SwiftFlutterCallkitIncomingPlugin.sharedInstance?.showCallkitIncoming(data, fromPushKit: true) // 配置音频会话为CallKit兼容模式 let audioSession = AVAudioSession.sharedInstance() do { try audioSession.setCategory(.playAndRecord, mode: .voiceChat, options: [.mixWithOthers, .allowBluetooth]) try audioSession.setActive(true, options: .notifyOthersOnDeactivation) } catch { NSLog("Failed to configure audio session: \(error)") } // ... 保留原有代码 } // 新增CallKit回调扩展,处理接听/结束时的音频会话状态 extension AppDelegate: CXProviderDelegate { func provider(_ provider: CXProvider, perform action: CXAnswerCallAction) { // 接听时激活音频会话 let audioSession = AVAudioSession.sharedInstance() do { try audioSession.setActive(true) } catch { NSLog("Failed to activate audio session on answer: \(error)") } action.fulfill() } func provider(_ provider: CXProvider, perform action: CXEndCallAction) { // 结束时停用音频会话 let audioSession = AVAudioSession.sharedInstance() do { try audioSession.setActive(false, options: .notifyOthersOnDeactivation) } catch { NSLog("Failed to deactivate audio session on end: \(error)") } action.fulfill() } }
2. Dart侧:调整权限请求时机与媒体流初始化
CallKit唤起应用时处于后台,此时权限请求会被系统拒绝,需将权限请求移至用户接听来电后(应用切换到前台时)执行:
acceptIncomingCall(SIP.Call incomingCall) async { try { bool remoteHasVideo = incomingCall!.remote_has_video; // 先请求权限,再获取媒体流(此时应用已切换到前台) await requestMediaAccess(); var mediaStream = await getUserMedia(); // ... 保留原有代码 incomingCall.answer(env.ua!.helper.buildCallOptions(!remoteHasVideo), mediaStream: mediaStream); } catch (err) { print('ERROR TRYING TO ACCEPT $err'); } } // 简化媒体流获取逻辑,去掉不必要的视频渲染 getUserMedia() async { final mediaConstraints = <String, dynamic>{ 'audio': true, 'video': false, }; print('Requesting media access...'); return await navigator.mediaDevices.getUserMedia(mediaConstraints); } requestMediaAccess() async { var microphoneStatus = await Permission.microphone.status; if (microphoneStatus.isDenied) { await Permission.microphone.request(); } else if (microphoneStatus.isPermanentlyDenied) { openAppSettings(); } // iOS中麦克风权限已覆盖音频访问,无需重复请求audio权限 }
3. 启用插件自带的WebRTC适配逻辑
确保你已经取消了flutter_callkit_incoming插件中WebRTC相关的模板代码注释,该代码会自动处理CallKit与WebRTC的音频会话同步,避免手动配置的冲突。
关键注意事项
- 必须确保用户在应用前台时已授予麦克风权限,后台状态下无法弹出权限请求弹窗。
- 不要同时手动配置AVAudioSession和依赖插件的自动配置,否则会导致音频会话冲突。
内容的提问来源于stack exchange,提问作者Mitchell
相关产品推荐
相关产品推荐

