Android 14 WebRTC 通话断连排查:coturn、TURN 与 ICE 状态机

这是一篇脱敏后的工程复盘。文中只保留可公开讨论的技术判断。

背景

我们原本已经在受限网络环境中落地了一套 WebRTC 音视频通话方案。早期场景主要发生在内部网络,很多设备之间可以直接建立媒体链路;后续为了支持更复杂的网络边界和公网访问,需要引入 TURN relay,于是加入了 coturn 作为中继服务。

这次排查发生在一次公网中继兼容性验证中。系统包含 Android 端 WebRTC 客户端、服务端信令逻辑和 coturn 中继服务。目标是验证不同 Android 版本、不同网络条件下的通话稳定性。

现象

在一组对比测试中,我们观察到一个很稳定但又很容易误判的问题:

  • Android 14 设备与旧版 Android 设备通话时,Android 14 端高概率在通话 30-60 秒内自动退出。
  • 旧版 Android 设备表现正常。
  • 少数情况下双方可以持续正常通话。
  • 问题在强制走 TURN relay 后更容易稳定复现。

从用户视角看,这像是“通话一会儿自动挂断”。从工程视角看,真正要回答的是:媒体链路断了、信令状态错了、客户端主动退出了,还是网络中继配置触发了兼容性问题。

一个误导性线索

排查初期,日志和抓包里出现过一类本地地址相关的中继通道协商失败,并伴随 timeout。这个报错出现频率很高,而且经常出现在通话断开前后,所以一开始很容易把它当成根因。

后续验证发现,这个 timeout 只是强相关现象,不是直接触发断连的根因。真正导致客户端退出的关键事件是 WebRTC 连接状态变化:

IceConnectionChange -> DISCONNECTED / FAILED
StandardizedIceConnectionChange -> DISCONNECTED / FAILED

这一步很关键。高频日志不一定等于根因,WebRTC 的 ICE 状态机才是判断链路是否真正断开的核心证据。

排查阶段

1. 统一链路,避免直连掩盖问题

早期测试里,设备在内部网络下可能不需要走中继即可直连。这样会带来一个问题:同一套代码在不同网络条件下走的不是同一条链路,导致复现结果不稳定。

为了隔离变量,我们把服务部署到公网测试环境,并让设备无论处于哪类网络,都尽量经过 coturn 中继。

这一步的价值是把问题从“复杂网络偶发异常”收敛成“TURN relay 链路下的稳定复现问题”。

2. 排除 WebRTC 库版本问题

第二个怀疑方向是 WebRTC 库版本或 Android 端 SDK 兼容问题。我们替换过更新版本的 WebRTC 依赖,并重复测试同样的通话链路。

结果是问题仍然存在,因此基本排除“单纯由 WebRTC 库版本导致”的方向。

3. 对比 Android 版本和固件差异

继续扩大设备矩阵后,问题范围逐渐收敛到 Android 14 设备侧:

  • 某批 Android 14 固件设备触发概率明显更高。
  • 多个旧版 Android 设备基本不触发。
  • 问题和是否使用 TURN relay 强相关。

这时排查方向从“WebRTC 代码 bug”转向“Android 14 网络安全策略、TURN 配置、证书和端口兼容性”。

4. 回到 coturn 配置和 ICE 状态机

最终我们围绕 coturn 配置做了调整,包括证书、安全参数和必要端口开放。调整后,通话不再稳定出现 30-60 秒自动断开。

从排查结果看,问题更像是 Android 14 在中继链路上的安全策略和 TURN 服务配置之间存在兼容性要求。当配置不满足时,客户端侧最终体现为 ICE 状态进入 DISCONNECTEDFAILED,再触发应用层通话退出逻辑。

这里需要谨慎表达:不要简单地说“Android 14 有 bug”,更准确的说法是:

在我们的测试矩阵里,Android 14 设备对 TURN relay 链路中的证书、安全参数和端口配置更敏感;完善 coturn 配置后,断连问题消失。

最终修复方向

公开版本只保留方向,不暴露具体部署细节:

  • coturn 配置符合客户端要求的证书链。
  • 检查 TURN/TLS/TCP/UDP 相关端口是否按预期开放。
  • 确认客户端实际选择的 candidate pair 是否符合预期。
  • 以 ICE 状态变化作为断链判断依据,而不是只看某条 timeout 日志。
  • 对不同 Android 版本建立兼容性测试矩阵。

复盘

这次问题最有价值的地方不是某个配置项,而是排查方法。

第一,不要被高频日志带偏。某个报错如果总在故障附近出现,只能说明它值得关注,不能直接证明它是根因。

第二,WebRTC 排查要回到状态机。ICE connecteddisconnectedfailed,candidate pair 选择,TURN relay 是否生效,这些比单条应用日志更可靠。

第三,要先统一链路。直连和中继混在一起测试,会让问题看起来像随机网络抖动。强制走 TURN relay 后,问题才变成可重复分析的工程问题。

第四,Android 版本差异不能只看 API。系统网络安全策略、证书信任、固件实现、硬件差异,都可能影响 WebRTC 链路表现。

可复用的排查清单

  • 记录双方 Android 版本、WebRTC 库版本和网络类型。
  • 明确本次通话走的是 host、srflx 还是 relay candidate。
  • 记录 IceConnectionChangeStandardizedIceConnectionChange 的完整时间线。
  • 在必要时强制走 TURN relay,避免直连路径掩盖问题。
  • 检查 coturn 证书、安全参数和端口开放。
  • 对修复前后做同样设备、同样网络、同样通话时长的对比。

结论

这类问题不是简单的“网络不好”或者“WebRTC 不稳定”。在真实设备和复杂网络里,WebRTC 稳定性往往取决于链路选择、TURN 配置、系统版本、安全策略和应用层状态处理的组合。

这次排查最终让我们把注意力从误导性 timeout 转向 ICE 状态机和 TURN relay 配置。这个过程也再次说明:WebRTC 生产问题排查,必须先把链路跑清楚,再谈修复。

Open to Go/WebRTC/RTC Gateway roles. 欢迎交流 Android WebRTC、coturn、TURN relay 和 RTC Gateway 稳定性问题。