云服务器跑LiveTalking数字人连不上?两个 TCP 端口全搞定

📅 2026/8/24 4:46:15
云服务器跑LiveTalking数字人连不上?两个 TCP 端口全搞定
在云 GPU 服务器上跑 LiveTalking 数字人最崩溃的瞬间不是模型加载慢而是——画面推不出去。WebRTC 默认走 UDP而 AutoDL、企业防火墙、家庭对称型 NAT统统把 UDP 封得死死的。你兴冲冲部署好浏览器一打开黑屏、转圈、连不上。端口开了几十个还是不行其实你只需要开放两个 TCP 端口nginx 服务端口和 MediaMtx 的媒体端口。为什么 WebRTC 这么挑剔WebRTC 媒体流默认用 UDPSRTP传输还要做 ICE 连通性检查常常需要在 1~65535 整个 UDP 区间里试端口。在自有服务器上这没问题但在云环境里算力平台AutoDL 等只放行少量 TCP 端口UDP 基本全封企业网络防火墙策略严格UDP 入站直接拒绝家庭宽带对称型 NATUDP 穿透几乎必败。结果就是——服务端在客户端也在但媒体流就是过不去。MediaMtx 代理把 UDP 流拐进 TCPMediaMtx 是一个开源媒体服务器原生支持 WebRTC关键能力是可以通过 TCP 传输 WebRTC 媒体webrtcLocalTCPAddress。部署思路如下LiveTalking 把数字人画面作为 WebRTC 源推给同机的 MediaMtxMediaMtx按客户端请求主动从 LiveTalking 拉流然后 MediaMtx 以 WHEP 协议通过单个 TCP 媒体端口例如:8189对外服务客户端浏览器只跟 nginx 和这个 TCP 端口打交道。换句话说对外只暴露两个 TCP 端口nginx 服务端口 MediaMtx 媒体端口UDP 那一套全在服务器内网闭环根本不需要出网。三种方案对比看清差异LiveTalking 的网络部署有三套方案方案需开放端口适用环境A. WebRTC 全端口TCP 8010 UDP 1-65536自有服务器B. MediaMtx 代理2 个 TCP 端口nginx 服务端口 MediaMtx:8189云环境 / 防火墙首选C. SRS中转2 个 TCP 端口nginx 服务端口 srs:8000需要用rtcpush推流模式方案 B 的核心优势在 AutoDL 这类只给 TCP 的环境里它几乎是唯一能跑通 WebRTC 推流的方案。MediaMtx主动拉流 TCP 承载天然绕开 UDP 封锁和对称 NAT 穿透难题——UDP 被拒就换 TCP客户端连的是 TCP穿透难度天差地别。offer 接口 vs WHEP 接口差在哪LiveTalking 默认是直连方式前端把 WebRTC 的 SDP offer 直接 POST 到 LiveTalking 自己的/offer或类似自定义接口:8010LiveTalking 作为 WebRTC 对端直接回 answer随后把数字人画面用 UDP 推给浏览器。这条链路里浏览器和 LiveTalking 是直接点对点的连接所有 ICE、NAT 穿透、UDP 端口问题都得自己扛。WHEPWebRTC-HTTP Egress ProtocolRFC 8865是一套标准化的 WebRTC 信令协议客户端把 SDP offer 用一次 HTTP POST 发给服务端 WHEP 端点服务端回 answer并给一个资源 URL 用于断开。它把建连变成了一个简单的 HTTP 请求。引入 MediaMtx 后WHEP 端点从 LiveTalking 转移到了 MediaMtx 上客户端不再直连 LiveTalking而是把 offer 发给 MediaMtx 的/avatar/sessionid/whepMediaMtx 作为面向客户端的 WebRTC 对端再用WHEP 客户端身份反向去 LiveTalking 的whep://127.0.0.1:8010/whep拉流。区别一目了然offer 接口客户端 ↔ LiveTalking 直连LiveTalking 既是业务端又是 WebRTC 对端媒体走 UDP 直推WHEP 接口经 MediaMtx客户端 ↔ MediaMtxWebRTC 对端TCP:8189MediaMtx ↔ LiveTalking内网 WHEP 拉流:8010。媒体流量被 MediaMtx 兜了一层对外只走 TCP。一句话offer 接口是客户端直接怼 LiveTalkingWHEP 接口是客户端怼 MediaMtxMediaMtx 再去怼 LiveTalking——多了一跳但换来了只用 TCP 端口的部署自由。配置实战三处改动① MediaMtxmediamtx.yml— 关掉 UDPwebrtcLocalUDPAddress: 只留 TCP:8189path 用正则~^avatar/(.)$每个 sessionid 对应独立的 source 连接source 指向 LiveTalking 的 WHEP# mediamtx.yml webrtcLocalUDPAddress: webrtcLocalTCPAddress: :8189 # 配置 tcp 映射端口 webrtcAdditionalHosts: [] # 配置外网 ip 地址 paths: # 用正则匹配每个不同的 sessionid 创建独立的 source 连接 # 客户端访问: /avatar/sessionid/whep?avatarxxxttsyyy # $G1 sessionid, $MTX_QUERY avatarxxxttsyyy ~^avatar/(.)$: source: whep://127.0.0.1:8010/whep?sessionid$G1$MTX_QUERY sourceOnDemand: yes② nginx/etc/nginx/sites-enabled/default—/avatar全部转发到 MediaMtx 的 HTTP 端口:8889其余/转发到 LiveTalking:8010server { # /avatar 路由 → MediaMtx location ^~ /avatar { proxy_pass http://127.0.0.1:8889; } location / { rewrite ^/$ /index-whep.html break; proxy_pass http://127.0.0.1:8010; } }③ LiveTalking 前端— 把原来访问/offer的接口改成访问 MediaMtx 上的 WHEPsessionid 在客户端生成并拼进路径参数走 query stringMediaMtx 再代理到 LiveTalking// 客户端生成 sessionid放入路径中每个不同 sessionid 独立 source 连接 if (!sessionid) setSessionId(crypto.randomUUID()); // sessionid 在客户端生成 if (avatar) params.set(avatar, avatar); if (refAudio) params.set(refaudio, refAudio); if (refText) params.set(reftext, refText); const queryString params.toString(); // 参数配置在 query param // 访问接口由 offer 改成访问 mediamtx 上的 whep由 mediamtx 代理到 livetalking return fetch(/avatar/ sessionid /whep (queryString ? ? queryString : ), { method: POST, headers: { Content-Type: application/sdp }, body: offer.sdp });原本要开的几万个 UDP 端口被压缩成两个 TCP 端口nginx 服务端口 MediaMtx 媒体端口——这就是 MediaMtx 代理的价值。已在autodl上跑通整个流程镜像地址https://www.codewithgpu.com/i/lipku/livetalking/base下次在云上部署数字人推流卡住别再死磕 UDP 了。一个 nginx两个 TCP 端口问题就解决了。你踩过 WebRTC 端口的坑吗用的是哪种方案评论区聊聊 LiveTalking— 开源实时交互数字人引擎。GitHubhttps://github.com/lipku/LiveTalking文档https://doc.livetalking.ai