WebRTC 为何能取代 HLS/FLV¶
先给结论:在“实时直播 / 强互动”这个场景里,WebRTC 对 HLS 和 HTTP-FLV 是代际替代。 它的优势不是“把同一个分片切得更小”,而是从传输模型上取消了“先落盘/先成片、再通过 HTTP 拉取”的中间层。 但在点播、超大并发单向分发、老旧终端等场景,HLS/FLV 仍然有明确的位置。 本文讨论的是“实时直播”,不是“所有视频”。
1. 三种协议到底差在哪¶
| 维度 | HLS | HTTP-FLV | WebRTC / WHEP |
|---|---|---|---|
| 传输 | HTTP/TCP | HTTP/TCP | SRTP/UDP(ICE 建连) |
| 媒体组织 | TS/fMP4 分片 + m3u8 播放列表 | 连续 FLV 字节流 | RTP 包 / 音频视频轨道 |
| 播放端拿到什么 | 一个完整分片后再解码 | 连续字节流,边收边解封装 | 单个 RTP 包,边收边解码 |
| 典型时延 | 6–30 秒;LL-HLS 约 2–5 秒 | 1–3 秒 | 100 ms–1 秒,优化链路可低于 500 ms |
| 首帧 | 等分片、等关键帧 | 等关键帧/缓存窗口 | 建连后收到首个可解码帧即可 |
| 缓存模型 | CDN 可按分片缓存,天然适合大规模分发 | CDN 可流式转发,但仍是 TCP | 每个观众一条 SRTP 连接,边缘需要 SFU 转发 |
| 抗弱网 | TCP 重传 + 播放器缓冲,延迟随卡顿累积 | TCP 队头阻塞,延迟随缓冲累积 | UDP + NACK/FEC + 拥塞控制 + 抖动缓冲 |
| P2P | 不支持 | 不支持 | 支持,可绕开边缘带宽 |
| 原生 App | iOS/Android 播放器支持成熟 | 依赖 FLV 解封装/播放器 | 使用 libwebrtc;WHEP 只是 HTTP 信令 |
1.1 HLS 的时延是结构性的¶
HLS 的播放链路大致是:
编码器 → 切片器 → 生成分片 + m3u8 → CDN 缓存 → 播放器下载分片 → 解码播放
播放器必须先拿到一个分片,通常还要在缓冲区里预存几个分片,才能稳定播放。即使把分片切到 1 秒、引入 LL-HLS 的部分分片和阻塞重载,仍然绕不开:
- 分片边界和关键帧对齐;
- HTTP 请求、CDN 回源与缓存;
- 播放器自身的启动缓冲;
- TCP 的队头阻塞和重传。
所以 HLS 可以把“延迟”从 20 秒优化到 3 秒,但很难稳定进入“几百毫秒 + 可互动”的区间。
1.2 HTTP-FLV 只是“连续化的 HLS”¶
HTTP-FLV 去掉了 m3u8 刷新,媒体流连续传输,因此比 HLS 更容易做到 1–3 秒。但它仍然跑在 TCP 上:
- 一个丢包会阻塞后续字节,播放器只能靠缓存硬扛;
- 延迟会随着网络抖动和重连逐渐累积;
- 浏览器侧通常依赖 MSE/flv.js,iOS Safari 不是天然支持;
- 不支持 P2P,边缘带宽成本无法下沉到观众侧。
它是“HLS 的低延迟改良版”,不是另一代传输模型。
1.3 WebRTC 换掉了整条链路¶
WebRTC 的链路是:
编码器 → RTP/SRTP 包 → SFU/边缘转发 → RTP/SRTP 包 → 接收端解码渲染
没有“先落地成文件、再通过 HTTP 拉取”的步骤。接收端为每个音频/视频轨道维护抖动缓冲,丢包通过 NACK/FEC 处理,拥塞控制决定发送码率。WHEP 只负责把“我要看这条流”的 SDP Offer 通过 HTTP POST 发给边缘,并拿回 SDP Answer;真正的媒体走 ICE 建连后的 UDP/SRTP。
这就是 WebRTC 能做到“低时延”的根本原因:媒体包从产生到消费,不需要等待一个可播放的完整分片。
2. 时延:不是优化 30%,而是换了一个量级¶
| 方案 | 典型端到端时延 | 主要延迟来源 |
|---|---|---|
| HLS | 6–30 s | 分片时长、播放列表刷新、CDN 缓存、播放器缓冲 |
| LL-HLS | 2–5 s | 部分分片、阻塞重载、HTTP/TCP、播放器缓冲 |
| HTTP-FLV | 1–3 s | TCP 重传/队头阻塞、播放器缓冲、GOP |
| WebRTC/WHEP | 0.1–1 s | 采集/编码、网络 RTT、抖动缓冲、解码渲染 |
PPCDN 在公开的直播时延性能测试报告中给出的实测结果是:
- P2P 直连:约 70 ms
- WHIP 入库 + WHEP 播放:约 108 ms
- SRT 入库 + WHEP 播放:约 386 ms
这些数字来自特定测试条件,不代表对所有网络环境的承诺;但它们说明了一件事:WebRTC 的时延下限和 HLS/FLV 不在同一个数量级。
对强互动业务,这个差异会直接改变产品形态:
- 竞拍/抢购:出价和画面同步,不会“举牌后画面还停在上一秒”;
- 连麦/游戏:主持人和观众可以实时对话;
- 远程操作/云游戏:操作指令和画面反馈形成闭环;
- 在线教育:老师提问、学生回答不需要等“缓冲几秒”。
3. 播放性能:低时延只是第一层¶
3.1 首帧和切换更快¶
HLS 要等分片,FLV 要等关键帧和缓冲窗口;WebRTC 建连后收到首个可解码帧就能出画面。用户点击播放到看到画面的时间更短,切换清晰度也不必等待下一个分片边界。
3.2 卡顿恢复策略不同¶
TCP 系协议遇到丢包时,数据会排队重传,播放器只能靠增加缓冲吸收;一旦缓冲耗尽就卡顿。WebRTC 使用 UDP,通过 NACK/FEC 和拥塞控制处理丢包,延迟不会因为一次重传被永久推高。
这并不意味着 WebRTC 对弱网“免疫”:丢包严重时仍会花屏、降码率或卡顿。区别在于它可以把问题控制在毫秒到秒级,而不是让整条 TCP 流进入“越卡越慢”的恶性循环。
3.3 ABR/Simulcast 更细¶
HLS 的清晰度切换通常发生在分片边界,切晚了会浪费带宽,切早了可能卡顿。WebRTC 可以在 RTP 层按 simulcast 层或码率档位切换,切换粒度更细,配合接收端带宽估计更平滑。
PPCDN 还支持 H264/HEVC 多轨同播和 Simulcast:编码端一次输出多档,边缘只转发不转码;播放端按终端能力选择 H264 或 HEVC,不需要为不同终端准备多套流。
3.4 P2P 把带宽成本下沉¶
HLS/FLV 的每个观众都要从 CDN 拉流;WebRTC 可以让观众之间直连,边缘只服务一部分观众。PPCDN 支持 P2P 直连,并且能测量 P2P 的端到端时延,而不是只靠体感判断“有没有加速”。
3.5 但 WebRTC 不是没有代价¶
必须承认:
- WebRTC 的连接数更多,边缘需要 SFU 做转发,不能像 HLS 一样靠一个静态分片服务无限观众;
- 加密(DTLS-SRTP)和实时解码对 CPU/电量更敏感;
- 每个观众一条连接,CDN 的“缓存命中”优势用不上;
- 网络特别差、UDP 被限制的环境,仍需要 SRT/FLV/HLS 兜底。
所以准确说法是:在实时互动体验上 WebRTC 碾压 HLS/FLV;在大规模单向分发的单位成本上,HLS 仍然很强。
4. 终端兼容性:现代浏览器和原生 App 都已经就位¶
4.1 浏览器¶
| 终端/浏览器 | HLS | HTTP-FLV | WebRTC/WHEP |
|---|---|---|---|
| Chrome/Edge/Firefox 桌面 | 通常需 MSE + hls.js | 需 MSE + flv.js | 原生支持 |
| Safari 桌面 | 原生支持 | 不支持(无 MSE FLV) | 原生支持 |
| iOS Safari | 原生支持 | 不支持 | 原生支持(WebRTC) |
| Android Chrome | 通常需 MSE/hls.js | 需 MSE + flv.js | 原生支持 |
WebRTC 的优势不是“所有设备都能跑”,而是现代浏览器和移动端已经普遍内置 WebRTC。HLS 虽然“协议简单”,但在 Chrome/Firefox 桌面端反而需要额外播放库;FLV 在 iOS Safari 上则基本不可用。
4.2 原生 App¶
原生 App 不依赖浏览器的 <video>,而是直接集成 WebRTC SDK:
| 平台 | 推荐方案 |
|---|---|
| Android | org.webrtc / libwebrtc,SurfaceViewRenderer 渲染 |
| iOS | WebRTC.framework,RTCMTLVideoView 渲染 |
| Flutter | flutter_webrtc |
| React Native | react-native-webrtc |
| 桌面 | libwebrtc、GStreamer WHEP 客户端 |
这意味着:原生 App 完全可以拉 WHEP 流并播放。 WHEP 不是浏览器专有协议,它只是一个标准化的 HTTP 信令流程;媒体仍然是标准 WebRTC。
5. 原生 App 如何拉 WHEP¶
5.1 先拿播放决策,再建 WebRTC¶
以 PPCDN 为例,App 后端或 App 自身先向 ppcenter 请求播放决策:
POST /v1/play/requests
Authorization: Bearer <viewer-token>
Content-Type: application/json
{
"streamName": "B01-frontView",
"clientId": "android-xxxx",
"capabilities": ["whep"]
}
响应里的 edgeStreamUrl 是已经带签名的边缘流基址,例如:
https://edge-1.edge.pp-cdn.org/{appId}/{streamName}
App 根据设备能力追加 codec 段:
H264: {edgeStreamUrl}/h264/whep
HEVC: {edgeStreamUrl}/hevc/whep
不要把
edgeStreamUrl当成普通视频 URL 去下载。它是 WHEP 的 POST 目标,query 中的txTime/txSecret是边缘播放鉴权参数。
5.2 WHEP 信令流程¶
App Edge
| |
| 1. 创建 RTCPeerConnection |
| 2. addTransceiver(recvonly) |
| 3. createOffer |
| |
| 4. POST /h264/whep?tx... |
| Content-Type: application/sdp
| <SDP Offer> |
| -------------------------->|
| |
| 5. 200 OK |
| Content-Type: application/sdp
| Location: .../session/...
| <SDP Answer> |
| <--------------------------|
| |
| 6. setRemoteDescription |
| 7. ICE 连通,收到音视频轨道 |
| 8. 渲染 |
| |
| 9. PATCH trickle ICE(可选)|
| 10. DELETE Location(结束) |
关键点:
- 音频和视频都使用
recvonlytransceiver; - Offer 里只声明接收,不发送本地音视频;
- Answer 的
Content-Type必须是application/sdp; - 保存响应头
Location,退出播放时发送DELETE,避免边缘留下僵尸会话; - ICE candidate 可以随 Offer 一次性给全,也可以 trickle(
PATCH);移动端网络切换时 trickle 更常见; stunServers来自播放决策;如果没有 STUN/TURN,跨 NAT 的连通性会下降。
5.3 Android(libwebrtc)伪代码¶
val factory = PeerConnectionFactory.builder()
.setVideoDecoderFactory(DefaultVideoDecoderFactory(eglBase.eglBaseContext))
.createPeerConnectionFactory()
val pc = factory.createPeerConnection(
PeerConnection.RTCConfiguration(iceServers).apply {
sdpSemantics = PeerConnection.SdpSemantics.UNIFIED_PLAN
},
observer
)!!
pc.addTransceiver(
MediaStreamTrack.MediaType.MEDIA_TYPE_AUDIO,
RtpTransceiver.RtpTransceiverInit(RtpTransceiver.RtpTransceiverDirection.RECV_ONLY)
)
pc.addTransceiver(
MediaStreamTrack.MediaType.MEDIA_TYPE_VIDEO,
RtpTransceiver.RtpTransceiverInit(RtpTransceiver.RtpTransceiverDirection.RECV_ONLY)
)
val offer = pc.createOffer(SdpObserver...)
pc.setLocalDescription(offer, ...)
// POST application/sdp,body = offer.description
val response = httpPost("$edgeStreamUrl/h264/whep?txTime=...&txSecret=...", offer.description)
pc.setRemoteDescription(
SessionDescription(SessionDescription.Type.ANSWER, response.body),
...
)
// onTrack: 把 videoTrack 绑定到 SurfaceViewRenderer
// onIceCandidate: 通过 PATCH application/trickle-ice-sdpfrag 发送
5.4 iOS(WebRTC.framework)要点¶
RTCPeerConnectionFactory+RTCPeerConnection;RTCRtpTransceiverInit(direction: .recvOnly);RTCMTLVideoView绑定RTCVideoTrack;URLSession发 WHEP POST/PATCH/DELETE;- 注意
Location是相对/绝对 URL,按 HTTP 规范解析。
5.5 App 侧必须处理的异常¶
| 现象 | 处理 |
|---|---|
| 401/403 | 重新获取 viewer token / 重新签名 edgeStreamUrl |
| 404 | 流当前不在线,回到“等待开播”状态 |
| 503 | 暂时没有可用 Edge,指数退避重试 |
| ICE failed | 销毁旧连接,重新请求播放决策;不要复用失败 PC |
| 网络切换 | 监听网络变化,重建 PeerConnection(Wi-Fi/4G 切换) |
| 切后台 | 暂停/释放解码与渲染,回前台重新建连 |
txTime 过期 |
重新向 ppcenter 请求播放 URL;不要长期缓存带签名的 URL |
| P2P 关闭 | 只走 Edge WHEP 完全可用;P2P 是可选加速,不是播放前提 |
6. PPCDN 的落地方式¶
PPCDN 的实时链路是:
ppobs / 浏览器
├─ WHIP 推流(低时延)
└─ SRT 推流(抗弱网)
│
▼
Origin
│ 不解码、不转码,透传 Simulcast / 多轨
▼
Edge(SFU)
│
├─ WHEP/WebRTC ──> 浏览器 / 原生 App
└─ P2P 直连 ─────> 观众之间(可选)
具体能力:
- WHIP/SRT 双协议入库,按网络场景选择;
- H264/HEVC 多轨同播,播放端按终端能力选轨;
- Simulcast/ABR,边缘按丢包、RTT、带宽选择层;
- P2P 直连并测量端到端时延;
- WHEP 播放,原生 App 和浏览器使用同一套边缘;
- 录像通过独立 API 切分/上传,不阻塞直播链路。
相关文档:
- 推流/播放 API:仓库
docs/api/streaming.md - PPCDN 架构设计:仓库
docs/design/ppcdn-architecture-design.zh-CN.md - 直播时延性能测试报告
7. HLS/FLV 仍然适合什么¶
“淘汰”不是“协议消失”。下面这些场景,HLS/FLV 仍然合理:
- 点播/回看:文件已经完整,HLS 的分片缓存和 CDN 成本优势最大;
- 超大并发单向直播:百万级观众、只需观看、不需要互动,HLS 的单位带宽成本更低;
- 老旧终端/智能电视/机顶盒:原生 HLS 支持成熟,WebRTC 集成成本更高;
- 弱网/受限网络:UDP 被限制时,SRT/FLV 仍然有兜底价值;
- DRM/广告/字幕等传统媒体生态:HLS 的周边能力更完整。
所以正确的工程策略不是“全站删掉 HLS/FLV”,而是:
实时互动主链路用 WebRTC/WHEP;点播、回看、超大单向分发保留 HLS/FLV 作为分层能力。
8. 迁移检查清单¶
- [ ] 推流端支持 WHIP 或 SRT,至少一条低时延入库链路;
- [ ] 边缘具备 SFU/WHEP,而不是把 WebRTC 包转成 HLS 再播放;
- [ ] 播放端能按设备能力选择 H264/HEVC,并正确处理 401/404/503;
- [ ] viewer token 短时有效,
edgeStreamUrl不长期缓存; - [ ] App 处理 ICE failed、网络切换、前后台切换;
- [ ] 保留 HLS/FLV 兜底路径,按终端能力或网络质量降级;
- [ ] 监控首帧时间、卡顿率、P2P 成功率、端到端时延,而不是只看“有没有画面”。
9. 结论¶
HLS/FLV 解决的是“把视频可靠地分发出去”,WebRTC 解决的是“让视频实时地到达并可以互动”。前者优化的是缓存、分片和带宽;后者优化的是传输路径和反馈闭环。
在实时直播场景,WebRTC 的优势是结构性的:
- 时延:从秒级进入百毫秒级;
- 播放体验:首帧更快、卡顿恢复更平滑、ABR 更细;
- 终端:现代浏览器和原生 App 都能通过 WHEP 接入;
- 成本:P2P 可以把部分带宽从边缘下沉到观众侧。
原生 App 也完全可以拉 WHEP:集成 libwebrtc,按 WHEP 的 HTTP 信令交换 SDP,媒体走标准 WebRTC 即可。WHEP 不是浏览器专属,它只是让 WebRTC 的“建连”变得像普通 HTTP API 一样简单。