You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用Livekit SDK开发语音App:后台/终止状态激活AudioSession失败

问题解答

这并非LiveKit的限制,核心问题在于iOS对后台/终止状态下激活AudioSession的严格规则,以及你当前使用的推送类型和流程不符合系统要求。以下是具体的解决方案:

1. 必须使用VoIP推送而非普通远程推送

普通远程推送的后台执行时间有限(约30秒),且系统不允许在这种场景下直接激活AudioSession用于实时语音通讯。你需要改用VoIP推送(通过PushKit框架),这是iOS专为语音/视频通话类App设计的推送类型,允许App在终止状态下被唤醒,并合法激活AudioSession。

配置要点:

  • 在Xcode的Capability中添加Push Notifications和Voice over IP权限
  • 使用PKPushRegistry注册VoIP推送,而非普通的UNUserNotificationCenter
  • 处理VoIP推送的回调是PKPushRegistryDelegate的didReceiveIncomingPushWithPayload方法,而非didReceiveRemoteNotification

2. 必须集成CallKit

iOS要求VoIP类App在收到来电推送时,必须通过CallKit向系统报告来电请求。只有当用户接听(或通过代码模拟接听)后,系统才会允许激活AudioSession。跳过这一步会直接导致AudioSession激活失败。

关键流程:

  1. 收到VoIP推送后,初始化CXCallUpdate对象,设置来电相关信息
  2. 通过CXCallController请求系统展示来电UI(即使是自动接听场景,也需要先发起CallKit请求)
  3. 在CallKit的provider(_:perform:)回调中,再执行LiveKit房间连接和AudioSession激活操作

3. 正确配置AudioSession的时机与参数

激活AudioSession时需满足以下要求:

  • 必须在连接LiveKit房间之前激活AudioSession
  • 使用适配语音通话的Category和Mode组合:
    let session = AVAudioSession.sharedInstance()
    do {
        try session.setCategory(.playAndRecord, mode: .voiceChat, options: [.allowBluetooth, .defaultToSpeaker])
        try session.setActive(true, options: .notifyOthersOnDeactivation)
    } catch {
        print("AudioSession激活失败: \(error.localizedDescription)")
    }
    
  • 禁止在didReceiveRemoteNotification中直接操作AudioSession,该方法的上下文不支持音频激活操作

4. 调整LiveKit连接流程

将LiveKit房间连接代码从didReceiveRemoteNotification移至CallKit的接听回调中,示例代码框架如下:

// 处理VoIP推送回调
func pushRegistry(_ registry: PKPushRegistry, didReceiveIncomingPushWithPayload payload: PKPushPayload, for type: PKPushType, completion: @escaping () -> Void) {
    // 1. 初始化CallKit来电请求
    let update = CXCallUpdate()
    update.remoteHandle = CXHandle(type: .generic, value: "来电用户ID")
    update.hasVideo = false
    
    // 2. 请求系统展示来电UI
    let callController = CXCallController()
    let transaction = CXTransaction(action: CXStartCallAction(callUUID: UUID(), handle: update.remoteHandle!))
    callController.request(transaction) { error in
        if let error = error {
            print("CallKit请求失败: \(error)")
            completion()
            return
        }
        // 若需自动接听,在此触发CallKit接听操作
        self.answerCall()
        completion()
    }
}

// CallKit接听回调
func provider(_ provider: CXProvider, perform action: CXAnswerCallAction) {
    // 先激活AudioSession
    let session = AVAudioSession.sharedInstance()
    do {
        try session.setCategory(.playAndRecord, mode: .voiceChat, options: [.allowBluetooth, .defaultToSpeaker])
        try session.setActive(true)
    } catch {
        print("AudioSession激活失败: \(error)")
        action.fail()
        return
    }
    
    // 再连接LiveKit房间
    Task {
        let room = Room(delegate: self)
        let url = URL(string: "你的LiveKit服务器地址")!
        let token = "你的房间Token"
        do {
            try await room.connect(url, token: token)
            action.fulfill()
        } catch {
            print("LiveKit连接失败: \(error)")
            action.fail()
        }
    }
}

// 模拟自动接听
private func answerCall() {
    let callController = CXCallController()
    let transaction = CXTransaction(action: CXAnswerCallAction(callUUID: UUID()))
    callController.request(transaction) { _ in }
}

额外排查点

  • 确认LiveKit Token包含正确的权限(允许加入房间、接收音频)
  • 检查Xcode后台模式是否同时开启了Audio, AirPlay, and Picture in Picture和Voice over IP
  • 在iOS系统设置中确认App已获得麦克风权限和后台App刷新权限

内容的提问来源于stack exchange,提问作者enrico-g

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.26 07:03:23