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

GSM通话被对方结束后,VoIP通话音频无法恢复的问题求助

解决VoIP通话被GSM中断后对方挂断GSM音频无法恢复的问题

问题场景回顾

当VoIP通话被GSM通话中断,选择“保持/接听”GSM通话后,VoIP通话被保持、音频会话停用均正常;但对方主动结束GSM通话时,iOS不会自动激活VoIP的音频会话,手动激活还会出现“会话激活失败”错误。

可行解决思路

  • 通过CXCallObserver监听GSM通话结束事件
    利用CallKit的CXCallObserver监听系统内所有通话的状态变化,精准捕捉GSM通话结束的时机,触发VoIP音频恢复逻辑。

    class CallStatusObserver: NSObject, CXCallObserverDelegate {
        private let callObserver = CXCallObserver()
        private var VoIPCallUUID: UUID! // 替换为你的VoIP通话UUID
    
        override init() {
            super.init()
            callObserver.setDelegate(self, queue: DispatchQueue.main)
        }
    
        func callObserver(_ callObserver: CXCallObserver, callChanged call: CXCall) {
            // 过滤非VoIP通话,且确认通话已结束
            if call.hasEnded && !call.isVoIP {
                restoreVoIPAudioSession()
            }
        }
    
        private func restoreVoIPAudioSession() {
            // 先更新CallKit中VoIP通话的保持状态为激活
            let setHeldAction = CXSetHeldCallAction(callUUID: VoIPCallUUID, onHold: false)
            let transaction = CXTransaction(action: setHeldAction)
            CXCallController().request(transaction) { [weak self] error in
                guard error == nil else {
                    print("更新VoIP通话状态失败: \(error!.localizedDescription)")
                    return
                }
                self?.activateAudioSession()
            }
        }
    
        private func activateAudioSession() {
            let session = AVAudioSession.sharedInstance()
            do {
                try session.setCategory(.playAndRecord, mode: .voiceChat, options: [.allowBluetooth, .allowBluetoothA2DP])
                try session.setActive(true, options: .notifyOthersOnDeactivation)
                // 恢复音视频流的播放与采集
            } catch {
                print("激活音频会话失败: \(error.localizedDescription)")
                // 打印错误详情定位问题,比如错误码、域
                if let nsError = error as NSError? {
                    print("错误码: \(nsError.code), 错误域: \(nsError.domain)")
                }
            }
        }
    }
    
  • 监听音频会话中断通知,处理恢复逻辑
    注册AVAudioSession.interruptionNotification,在中断结束时判断是否需要手动恢复,尤其注意系统未自动触发shouldResume的场景:

    // 在初始化方法中注册通知
    NotificationCenter.default.addObserver(self, selector: #selector(handleAudioInterruption(_:)), name: AVAudioSession.interruptionNotification, object: nil)
    
    @objc private func handleAudioInterruption(_ notification: Notification) {
        guard let info = notification.userInfo else { return }
        let typeValue = info[AVAudioSessionInterruptionTypeKey] as! UInt
        let interruptionType = AVAudioSession.InterruptionType(rawValue: typeValue)!
    
        if interruptionType == .ended {
            let optionsValue = info[AVAudioSessionInterruptionOptionKey] as! UInt
            let options = AVAudioSession.InterruptionOptions(rawValue: optionsValue)
            
            if options.contains(.shouldResume) {
                activateAudioSession()
            } else {
                // 系统未建议自动恢复,手动触发CallKit状态更新+音频激活
                restoreVoIPAudioSession()
            }
        }
    }
    
  • 排查手动激活失败的核心原因
    出现“会话激活失败”时,重点排查:

    • 音频会话的category/mode配置是否正确,确保和VoIP通话场景匹配(必须是playAndRecord+voiceChat)
    • 是否有其他音频会话未释放,可通过AVAudioSession.sharedInstance().isOtherAudioPlaying检查
    • 激活时的options参数是否合理,比如使用.notifyOthersOnDeactivation而非.mixWithOthers

内容的提问来源于stack exchange,提问作者rauldhasmana

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.11 10:17:42