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

如何在iOS应用中通过TwilioVoiceSDK建立PSTN通话流程并传输音频?

解决方案:修复Twilio Voice PSTN通话流程与音频问题

一、解决流程混乱(先显示呼入再路由PSTN)的问题

1. 检查Access Token生成逻辑

日志出现呼入流程的核心原因是Access Token未正确配置呼出规则。生成Token时必须指定OutgoingApplicationSid(对应托管你TwiML端点的Twilio应用SID),这样Twilio才会在你调用connect()时触发呼出Webhook,而非误触发呼入流程。

示例Token生成代码(Python):

from twilio.jwt.access_token import AccessToken
from twilio.jwt.access_token.grants import VoiceGrant

account_sid = "你的Twilio Account SID"
api_key = "你的Twilio API Key"
api_secret = "你的Twilio API Secret"
outgoing_app_sid = "关联TwiML端点的Twilio应用SID"
identity = "用户唯一标识"

token = AccessToken(account_sid, api_key, api_secret, identity=identity)
voice_grant = VoiceGrant(
    outgoing_application_sid=outgoing_app_sid,
    incoming_allow=True  # 无需呼入可设为False
)
token.add_grant(voice_grant)
print(token.to_jwt())

2. 修正客户端状态跟踪

完善CallDelegate实现,区分不同阶段的连接状态,避免日志混淆:

extension YourCallManager: CallDelegate {
    func callDidConnect(_ call: Call) {
        print("客户端已连接至Twilio平台")
    }
    
    func callDidReceiveAudio(_ call: Call) {
        print("音频通道已建立(PSTN端已接通)")
    }
    
    func callDidFailToConnect(_ call: Call, error: Error) {
        print("通话连接失败:\(error.localizedDescription)")
    }
    
    func callDidDisconnect(_ call: Call, error: Error?) {
        print("通话断开:\(error?.localizedDescription ?? "无异常")")
    }
}

二、修复音频传输异常问题

1. 确保麦克风权限配置

在Info.plist中添加权限描述:

<key>NSMicrophoneUsageDescription</key>
<string>需要访问麦克风以进行语音通话</string>

调用connect()前主动请求权限:

import AVFoundation

func requestMicrophonePermission(completion: @escaping (Bool) -> Void) {
    AVAudioSession.sharedInstance().requestRecordPermission { granted in
        DispatchQueue.main.async {
            completion(granted)
        }
    }
}

2. 正确配置音频会话

Twilio Voice SDK依赖AVAudioSession的正确配置,建议在通话启动前设置:

func setupAudioSession() {
    let session = AVAudioSession.sharedInstance()
    do {
        try session.setCategory(.playAndRecord, mode: .voiceChat, options: .defaultToSpeaker)
        try session.setActive(true)
    } catch {
        print("音频会话配置失败:\(error)")
    }
}

通话结束后关闭音频会话:

func teardownAudioSession() {
    do {
        try AVAudioSession.sharedInstance().setActive(false)
    } catch {
        print("音频会话关闭失败:\(error)")
    }
}

3. 验证Caller ID有效性

确保Dial组件的caller_id是Twilio验证过的号码(不能用未验证号码或客户端Identity),否则PSTN端可能无法正常接收音频。

三、代码优化建议

iOS端优化

在performVoiceCall中先检查权限,再发起通话:

func performVoiceCall(uuid: UUID, phoneNumber: String, token: String, completionHandler: @escaping (Bool) -> Void) {
    requestMicrophonePermission { [weak self] granted in
        guard granted, let self = self else {
            completionHandler(false)
            return
        }
        self.setupAudioSession()
        
        let connectOptions = ConnectOptions(accessToken: token) { builder in
            builder.params = ["To": phoneNumber]
            builder.uuid = uuid
        }
        
        let call = TwilioVoiceSDK.connect(options: connectOptions, delegate: self)
        completionHandler(true)
    }
}

TwiML端点优化

增加参数校验和错误响应:

@https_fn.on_request()
def twiml_response(req: https_fn.Request) -> https_fn.Response:
    response = VoiceResponse()
    to_param = req.form.get("To")
    
    if not to_param:
        response.say("未指定目标号码,请检查输入。", voice="alice")
        return https_fn.Response(str(response), status=400, mimetype="application/xml")
    
    if to_param.startswith("+"):
        dial = Dial(caller_id="+162xxxxxxxx")
        dial.number(to_param)
        response.append(dial)
    else:
        response.say("无效的目标号码,请使用以+开头的有效PSTN号码。", voice="alice")
    
    return https_fn.Response(str(response), status=200, mimetype="application/xml")

四、正确的通话流程梳理

  1. iOS端请求麦克风权限并配置音频会话;
  2. 调用TwilioVoiceSDK.connect(),携带目标号码参数;
  3. Twilio根据Access Token中的OutgoingApplicationSid触发你的TwiML端点;
  4. TwiML返回Dial指令,Twilio发起对PSTN号码的呼叫;
  5. PSTN端接听后,Twilio建立客户端与PSTN的音频通道;
  6. 通话结束后,iOS端关闭音频会话。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.14 21:35:54