腾讯云快直播浏览器推流深度解析:从 WebRTC 原理到 480p 落地方案
腾讯云快直播浏览器推流深度解析:从 WebRTC 原理到 480p 落地方案
最近在做机器人远程遥控,需要把摄像头画面从浏览器推到云上,延迟必须压到 1 秒以内。选型兜了一圈,最后落在腾讯云快直播(LEB)上。本文记录从零搭建到稳定推流的全过程,涵盖 WHIP 协议解析、SDK 踩坑、编码调优,以及过程中遇到的各种"文档和代码对不上"的真实问题。
一、背景与选型
1.1 我要解决什么问题
事情很简单:一台跑着 Ubuntu 的机器人,上面有摄像头,我需要把这个摄像头的画面实时传回到远程操控端的浏览器里。听起来是个常见需求,但细想有几个硬指标:
- 延迟要低:操控机器人和打游戏一样,你推摇杆,画面半秒后才动,那体验基本没法用。目标 < 1s
- 不能装软件:机器人端除了浏览器什么都没有,OBS 就别想了。推流必须纯 Web 搞定
- 带宽要省:机器人走的是 4G/5G 网络,流量不是无限的。画质够用就行,480p 绰绰有余
- 播放也要简单:操控端同样浏览器打开就能看,不用装任何东西
这几个条件一筛,可选方案其实没几个。
1.2 方案对比
| 方案 | 协议 | 延迟 | 浏览器推流 | 浏览器拉流 |
|---|---|---|---|---|
| 标准直播 (CSS) | RTMP → HLS/FLV | 3~20s | ❌ 需插件 | ✅ |
| TRTC (实时音视频) | WebRTC (私有) | < 300ms | ✅ | ✅ (需 SDK) |
| 快直播 (LEB) | WebRTC (WHIP/CDN) | 300~800ms | ✅ TXLivePusher | ✅ 标准 WebRTC |
| 自建 WebRTC | WebRTC (开源) | < 300ms | ✅ | ✅ (需信令服务器) |
标准直播延迟太高直接 pass。TRTC 很好但按房间时长计费,机器人 24 小时在线成本吃不消。自建 WebRTC 要自己搭信令服务器、维护 STUN/TURN,太重。
快直播卡在中间位置刚好:走 CDN 按流量计费适合长挂场景,推流有浏览器 SDK 封装好了 WHIP,播放端直接走腾讯云 CDN 边缘的 WebRTC 分发。延迟比标准直播低一个数量级,成本比 TRTC 低。就它了。
二、技术架构深度剖析
2.1 整体数据流
┌──────────────────────────────────────────────────────────────────────┐ │ 推流端 (浏览器) │ │ │ │ ┌──────────┐ ┌──────────┐ ┌───────────────┐ │ │ │ 摄像头 │───→│ H.264 编码│───→│ RTCPeerConn │ │ │ │getUserMedia│ │ setVideoQuality│ .createOffer()│ │ │ └──────────┘ └──────────┘ └───────┬───────┘ │ │ │ SDP Offer │ └──────────────────────────────────────────┼───────────────────────────┘ │ HTTP POST (WHIP) ▼ ┌──────────────────────────────────────────────────────────────────────┐ │ WHIP 信令服务器 (腾讯云) │ │ https://webrtcpush.tlivewebrtcpush.com/webrtc/v2/whip │ │ │ │ ┌──────────────────────────────────────────────────────────────┐ │ │ │ 1. 解析 Bearer Token → 提取推流域名/App/Stream/txSecret/txTime│ │ │ │ 2. 鉴权验证 (txSecret + txTime) │ │ │ │ 3. 路由到对应 CDN 边缘节点 │ │ │ │ 4. 返回 SDP Answer (包含 ICE Candidate / DTLS 指纹 / 编解码) │ │ │ └──────────────────────────────────────────────────────────────┘ │ └──────────────────────────────────────┬───────────────────────────────┘ │ ICE 连通性检测 (STUN) │ DTLS 握手 (证书交换) │ SRTP 媒体流 (加密 UDP) ▼ ┌──────────────────────────────────────────────────────────────────────┐ │ CDN 边缘节点 (快直播改造) │ │ tdns.l3.sched.dcloudlive.com │ │ │ │ ┌──────────────────────────────────────────────────────────────┐ │ │ │ • WebRTC 协议栈 (ICE/DTLS/SRTP) │ │ │ │ • 转封装: WebRTC → RTMP → HLS/FLV (降级链路) │ │ │ │ • 分布式分发 (同标准直播 CDN 架构) │ │ │ └──────────────────────────────────────────────────────────────┘ │ └──────────────────────────────────────┬───────────────────────────────┘ │ WebSocket 信令 + SRTP ▼ ┌──────────────────────────────────────────────────────────────────────┐ │ 播放端 (浏览器) │ │ TCPlayerLite / TCPlayer / 标准 WebRTC (需自行实现 WebSocket 信令) │ └──────────────────────────────────────────────────────────────────────┘2.2 WHIP 协议:推流侧的 SDP 交换
WHIP (WebRTC-HTTP Ingestion Protocol)是 IETF 标准化的 WebRTC 推流协议(RFC draft-ietf-wish-whip)。核心思想:用 HTTP POST 承载 SDP Offer/Answer 交换,省去 WebSocket 信令服务器的复杂度。
一次完整的 WHIP 推流信令交互:
推流端 WHIP 服务器 │ │ │ 1. RTCPeerConnection.createOffer() │ │ SDP 内容: │ │ - v=0 (协议版本) │ │ - m=video 9 UDP/TLS/RTP/SAVPF 96 97 ... │ │ - a=rtpmap:96 H264/90000 │ │ - a=fmtp:96 profile-level-id=42e01f │ │ - a=ice-ufrag:xxxx │ │ - a=ice-pwd:xxxxxxxx │ │ - a=fingerprint:sha-256 xx:xx:... │ │ - a=setup:actpass │ │ │ │ 2. setLocalDescription(offer) │ │ │ │ 3. POST /webrtc/v2/whip HTTP/1.1 │ │ Host: webrtcpush.tlivewebrtcpush.com │ │ Content-Type: application/sdp │ │ Authorization: Bearer webrtc://domain/live/stream │ │ ?txSecret=xxx&txTime=xxx │ │ ──────────────────────────────────────────→ │ │ │ │ 4. 鉴权验证 │ │ - 解析 txTime 十六进制时间戳 │ - MD5(txSecret + txTime) 比对 │ - 路由到目标 CDN 边缘 │ │ │ 5. 生成 SDP Answer │ │ - 匹配编解码 │ │ - 分配 ICE Candidate │ - DTLS 指纹 │ │ │ │ 6. ←───── HTTP 201 Created ───────── │ │ Content-Type: application/sdp │ │ SDP Answer: │ │ - m=video 9 UDP/TLS/RTP/SAVPF 96 │ │ - a=rtpmap:96 H264/90000 │ │ - a=ice-ufrag:yyyy │ │ - a=ice-pwd:yyyyyyyy │ │ - a=fingerprint:sha-256 yy:yy:... │ │ - a=candidate:1 udp 2130706431 1.2.3.4 8080 ... │ │ │ │ 7. setRemoteDescription(answer) │ │ │ │ 8. ICE Candidate 交换 + 连通性检测 │ │ STUN Binding Request/Response │ │ ──────────────────────────────────────────→ │ │ ←────────────────────────────────────────── │ │ │ │ 9. DTLS 握手 │ │ ClientHello → ←ServerHello → 证书验证 → 密钥协商 │ │ │ │ 10. SRTP 媒体流推送 (UDP) │ │ ──────────────────────────────────────────→ │关键步骤详解:
第 1 步 — SDP Offer 生成:浏览器RTCPeerConnection.createOffer()生成的 SDP 包含:
- 媒体描述(m= line):声明发送 video/audio 轨道的端口、协议栈(UDP/TLS/RTP/SAVPF)和负载类型编号
- 编解码协商(a=rtpmap / a=fmtp):列出支持的编码格式,按优先级排列。TXLivePusher 固定使用 H.264 (profile-level-id=42e01f = Baseline Profile Level 3.1)
- ICE 参数(a=ice-ufrag / a=ice-pwd):用于 STUN 认证的用户名和密码片段
- DTLS 指纹(a=fingerprint:sha-256):自签名证书的哈希,用于 DTLS 握手验证
- 连接角色(a=setup:actpass):声明推流端可以作为 DTLS 客户端或服务端
第 3 步 — HTTP POST 承载信令:
POST /webrtc/v2/whip HTTP/1.1 Host: webrtcpush.tlivewebrtcpush.com Content-Type: application/sdp Authorization: Bearer webrtc://xxx.push.tlivecloud.com/live/stream?txSecret=***&txTime=XXXXXXXX [Raw SDP Offer Body]WHIP 协议规定:
- Content-Type 必须为
application/sdp - 鉴权使用 HTTP Bearer Token,Token 值即为推流 URL
- 成功响应码为 HTTP 201(Created)
第 8 步 — ICE 穿透:使用标准 STUN 协议(RFC 5389)进行 NAT 穿透:
| ICE Candidate 类型 | 说明 | 优先级 | 典型场景 |
|---|---|---|---|
| host | 本地网卡 IP:Port | 最高 (126) | 同局域网 |
| srflx (Server Reflexive) | 经 STUN 获取的公网映射地址 | 中 (100) | 家用 NAT |
| relay | TURN |
