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

iOS端使用OpenTok与CallKit实现外呼时播放拨号音的方法

实现OpenTok + CallKit外呼时的拨号音功能

CallKit 本身确实没有提供外呼时播放拨号音的原生支持——你提到的 ringtoneSound 配置本来就是针对来电场景的,所以外呼时不会触发它。不过我们可以通过手动管理音频播放来实现这个需求,下面是具体的方案:

核心思路

在发起外呼请求并得到CallKit确认后,使用 AVAudioPlayer 循环播放拨号音文件;当通话连接成功(OpenTok会话建立)、用户取消呼叫,或者音频会话被中断时,停止播放。

步骤1:准备拨号音资源

确保你有符合iOS音频格式要求的拨号音文件(比如 .caf 或 .wav 格式,推荐PCM编码),并将其添加到项目的资源文件中。

步骤2:封装拨号音播放逻辑

创建一个音频播放器实例,处理播放、停止以及音频会话的兼容配置:

import AVFoundation

class DialtoneManager {
    private var player: AVAudioPlayer?
    
    func startPlaying() {
        guard let soundURL = Bundle.main.url(forResource: "Dialtone", withExtension: "caf") else {
            print("Dialtone file not found in bundle")
            return
        }
        
        do {
            // 配置音频会话,确保和CallKit的语音通话会话兼容
            try AVAudioSession.sharedInstance().setCategory(.playback, mode: .voiceChat, options: .mixWithOthers)
            try AVAudioSession.sharedInstance().setActive(true)
            
            player = try AVAudioPlayer(contentsOf: soundURL)
            player?.numberOfLoops = -1 // 无限循环播放
            player?.volume = AVAudioSession.sharedInstance().outputVolume // 匹配系统通话音量
            player?.play()
        } catch {
            print("Failed to initialize dialtone player: \(error.localizedDescription)")
        }
    }
    
    func stopPlaying() {
        player?.stop()
        player = nil
        
        // 释放音频会话(可选,根据场景调整)
        do {
            try AVAudioSession.sharedInstance().setActive(false, options: .notifyOthersOnDeactivation)
        } catch {
            print("Failed to deactivate audio session: \(error.localizedDescription)")
        }
    }
}

步骤3:把握播放/停止的时机

开始播放:

在你调用CallKit发起外呼请求,并且收到成功回调后,启动拨号音:

let dialtoneManager = DialtoneManager()

func initiateOutgoingCall(to phoneNumber: String) {
    let handle = CXHandle(type: .phoneNumber, value: phoneNumber)
    let startAction = CXStartCallAction(call: UUID(), handle: handle)
    let transaction = CXTransaction(action: startAction)
    
    callKitProvider.requestTransaction(transaction) { [weak self] error in
        guard let self = self else { return }
        if let error = error {
            print("Failed to start outgoing call: \(error.localizedDescription)")
            return
        }
        // CallKit确认发起呼叫后,开始播放拨号音
        self.dialtoneManager.startPlaying()
    }
}

停止播放:

在以下几种场景下需要停止拨号音:

  1. OpenTok会话成功建立:实现 OTSessionDelegate 的 sessionDidConnect(_:) 回调:
func sessionDidConnect(_ session: OTSession) {
    dialtoneManager.stopPlaying()
}
  1. 用户取消呼叫:在CallKit的 provider(_:perform:) 方法中处理 CXEndCallAction:
func provider(_ provider: CXProvider, perform action: CXEndCallAction) {
    dialtoneManager.stopPlaying()
    // 其他结束呼叫的逻辑...
    action.fulfill()
}
  1. 音频会话中断:监听系统音频中断通知,比如来电、切换到其他音频应用时停止播放:
// 在初始化时添加监听
NotificationCenter.default.addObserver(self, selector: #selector(handleAudioInterruption(_:)), name: AVAudioSession.interruptionNotification, object: nil)

@objc private func handleAudioInterruption(_ notification: Notification) {
    guard let info = notification.userInfo,
          let typeValue = info[AVAudioSessionInterruptionTypeKey] as? UInt,
          let type = AVAudioSession.InterruptionType(rawValue: typeValue) else {
        return
    }
    
    if type == .began {
        // 音频被中断,停止拨号音
        dialtoneManager.stopPlaying()
    }
}

注意事项

  • 音频文件格式:确保文件是iOS支持的无损格式,避免播放失败或卡顿。
  • 音频会话冲突:CallKit会接管语音通话的音频会话,所以配置 AVAudioSession 时要使用 .voiceChat 模式,确保和CallKit兼容。
  • 音量同步:通过 AVAudioSession.sharedInstance().outputVolume 获取系统通话音量,让拨号音音量和系统保持一致,提升用户体验。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.20 12:08:34