/// ICE 服务器配置。 /// /// 与可正常连接的 `webrtc_controller_ios`(原生 Swift 项目)保持一致: /// 使用 `www.ttstd.com:3478` 作为 STUN/TURN 服务器。 /// /// 注意:旧配置曾误用 `175.178.213.60:3478`(错误凭据)及内网 /// `192.168.5.224:3478`(模拟器不可达),导致 iOS 模拟器下 /// `ICE Checking -> Failed`。已修正为与 iOS 原生项目相同的服务器。 /// ICE 服务器配置。 /// /// 与「被控端 Android」同一台 TURN 服务器(175.178.213.60)、同一凭据 /// (fanhuitong / Fan19961907..)。 /// /// **iOS 模拟器关键修复**: /// 明文 `turn:175.178.213.60:3478` 在 iOS 模拟器(libwebrtc)下不会发起 /// RELAY 分配请求,导致控制端本地候选缺失 `typ relay`,两端无公共中继可 /// 配对 → candidate-pair=0 → ICE Failed(Android 真机/网页则正常)。 /// 改用 `turns:`(TLS over TCP 5349)后,模拟器能稳定拿到 relay 候选。 /// 同时保留 udp / tcp 明文项作为降级,以及 STUN 用于 srflx。 /// /// **Linux / Windows 桌面端崩溃修复(free(): invalid pointer)**: /// flutter_webrtc 桌面端的原生 `RTCConfiguration` 使用**定长数组**存放 /// ICE 服务器:`IceServer ice_servers[kMaxIceServerSize]`,其中 /// `kMaxIceServerSize = 8`(见 third_party/libwebrtc/include/rtc_types.h)。 /// 而 `FlutterWebRTCBase::CreateIceServers()` 直接按传入列表长度写入该数组, /// **没有做任何越界检查**。一旦条目数 > 8,就会越界写入相邻堆内存, /// 并向非法地址赋值 `std::string`,最终在释放时触发 /// `free(): invalid pointer` 并导致进程闪退。 /// /// 因此下表条目数**必须 ≤ [kMaxIceServerCount](8)**。 /// 这里按「同一组凭据合并为一条」的标准写法(`urls` 传数组), /// 在不丢失任何服务器地址的前提下把条目压缩到 6 条。 /// /// **桌面端 `urls` 数组的额外注意点**: /// 同一函数里解析 `urls` 列表时是 `ice_server.uri = ...` 逐个**覆盖**赋值, /// 只有**最后一个** URL 会真正生效(Android / iOS / Web 则会全部生效)。 /// 因此每个数组都把「最通用、最可能连通」的地址放在**最后一位**: /// TURN 组以明文 `turn:...:3478`(UDP)收尾,内网组以 `turn:` 收尾。 const int kMaxIceServerCount = 8; const List> kIceServers = [ // 公共 STUN。 {'urls': 'stun:stun.l.google.com:19302'}, {'urls': 'stun:175.178.213.60:3478'}, { // 同一组凭据的多个 TURN 地址合并为一条。 // 顺序说明:移动端/Web 全部生效;桌面端只取最后一个, // 故把兼容性最好的明文 UDP turn 放在末尾。 'urls': [ 'turns:175.178.213.60:5349', 'turn:175.178.213.60:3478?transport=tcp', 'turn:175.178.213.60:3478', ], 'username': 'fanhuitong', 'credential': 'Fan19961907..', }, {'urls': 'stun:47.242.112.133:3478'}, { 'urls': 'turn:47.242.112.133:3478', 'username': 'ttstd', 'credential': 'fanhuitong', }, { // 内网 STUN/TURN(同机房/局域网时可直连)。 'urls': [ 'stun:192.168.5.224:3478', 'turn:192.168.5.224:3478', ], 'username': 'tt', 'credential': 'fht', }, ]; /// 信令 WebSocket 默认地址。 const String kDefaultSignalServer = 'wss://www.ttstd.com/signal'; /// DataChannel 标签,控制端与被控端需一致。 const String kDataChannelLabel = 'control_channel';