iOS Swift端WebRTC与Janus Gateway集成时insertDtmf方法无法发送DTMF至对端的问题求助
你遇到的这个问题挺典型的——语音通话正常但DTMF发不出去,Android端同逻辑却没问题,大概率是iOS WebRTC的细节处理和Android端有差异。结合你贴的代码,我整理了几个关键排查点和修复方案:
1. 精准获取音频轨道的DTMF Sender
你现在用pc.senders.first?.dtmfSender取DTMF Sender,这里有个隐患:PeerConnection的senders可能包含视频或其他轨道,first不一定指向音频轨道。Android端可能刚好第一个是音频sender,但iOS端可能不是,导致拿到的DTMF Sender无效。
修复方法:遍历senders,明确筛选音频轨道对应的sender:
public func insertDTMF(_ tone:String){ guard let peerConnection = pc else { print("Peer connection未初始化") return } // 精准定位音频轨道的sender let audioSender = peerConnection.senders.first { sender in sender.track?.kind == RTCRtpMediaType.audio.rawValue } guard let dtmfSender = audioSender?.dtmfSender else { print("未找到音频轨道对应的有效DTMF Sender") return } // 避免重复发送(如果当前DTMF正在播放) if !dtmfSender.isActive { let success = dtmfSender.insertDtmf(tone, duration: 200, interToneGap: 70) if !success { print("DTMF音发送失败: \(tone)") } } else { print("DTMF Sender正忙,请等待当前音播放完成") } }
2. 切换到Unified Plan SDP语义
你的代码里用了rtcConfig.sdpSemantics = .planB,而WebRTC现在更推荐用Unified Plan,部分网关(包括Janus)对Plan B的DTMF协商支持可能有兼容性问题——这很可能是iOS端失效的核心原因(Android端可能默认用了Unified Plan)。
修改SDP语义,并调整轨道添加方式(Unified Plan不支持直接添加MediaStream,需要单独添加Track):
// 在streamsDone方法中修改RTCConfiguration let rtcConfig = RTCConfiguration.init() rtcConfig.iceServers = server.iceServers rtcConfig.bundlePolicy = RTCBundlePolicy.maxBundle rtcConfig.rtcpMuxPolicy = RTCRtcpMuxPolicy.require rtcConfig.continualGatheringPolicy = RTCContinualGatheringPolicy.gatherContinually rtcConfig.sdpSemantics = .unifiedPlan // 替换为Unified Plan // 替换pc.add(myStream)为逐个添加轨道 if let audioTrack = audioTrack { do { try pc.addTrack(audioTrack, mediaStreamIds: [LOCAL_MEDIA_ID]) } catch { print("添加音频轨道失败: \(error.localizedDescription)") } }
3. 验证SDP中的DTMF协商信息
DTMF发送需要两端SDP中包含telephone-event的RTP映射。可以分别抓取iOS和Android端的SDP,对比是否iOS端的SDP缺少类似以下的行:
a=rtpmap:101 telephone-event/8000 a=fmtp:101 0-16
如果缺失,说明WebRTC没有启用DTMF协商,可以在音频约束中强制开启:
// 初始化audioConstraints时添加DTMF相关约束 let audioConstraints = RTCMediaConstraints(mandatoryConstraints: [ RTCPair(key: "googDtmf", value: "true") ], optionalConstraints: nil)
4. 补全错误处理,排查隐藏问题
你的代码中setRemoteDescription的completion handler是空的,可能存在隐藏的SDP设置错误,导致DTMF无法正常工作。添加错误日志排查:
pc.setRemoteDescription(sessionDescription) { error in if let error = error { print("设置远端SDP失败: \(error.localizedDescription)") } else { print("远端SDP设置成功") } }
5. 调整DTMF参数(可选)
虽然你当前的duration(200ms)和interToneGap(70ms)符合规范,但部分网关对参数有更严格的要求,可以尝试调整为更通用的值:
dtmfSender.insertDtmf(tone, duration: 160, interToneGap: 100)
建议先从第1点和第2点入手排查,这两个是最可能导致iOS端DTMF失效的原因。如果还是不行,可以检查Janus Gateway的日志,看是否收到了iOS端的DTMF包,进一步定位问题。
内容的提问来源于stack exchange,提问作者Bilal Şimşek

