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() } }
停止播放:
在以下几种场景下需要停止拨号音:
- OpenTok会话成功建立:实现
OTSessionDelegate的sessionDidConnect(_:)回调:
func sessionDidConnect(_ session: OTSession) { dialtoneManager.stopPlaying() }
- 用户取消呼叫:在CallKit的
provider(_:perform:)方法中处理CXEndCallAction:
func provider(_ provider: CXProvider, perform action: CXEndCallAction) { dialtoneManager.stopPlaying() // 其他结束呼叫的逻辑... action.fulfill() }
- 音频会话中断:监听系统音频中断通知,比如来电、切换到其他音频应用时停止播放:
// 在初始化时添加监听 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
相关产品推荐
相关产品推荐

