27、WebRTC与直播结合:WebRTC推流到RTMP服务器、WebRTC拉取HLS流、低延迟直播方案(WHIP/WHEP)
聊到WebRTC和直播的结合,我得先坦白一件事——WebRTC天生就不是为直播设计的。它是个点对点的实时通信协议,讲究的是低延迟、双向通信。而直播呢?传统直播用的是RTMP推流、HLS拉流,延迟动不动就几秒甚至十几秒。
但现实需求摆在那:用户既想要WebRTC的超低延迟,又想要直播的大规模分发能力。怎么办?把WebRTC塞进直播链路里。今天我就把这块的实战经验掰开揉碎讲给你听。
WebRTC推流到RTMP服务器
为什么要把WebRTC推到RTMP?说白了,RTMP是目前直播生态里最成熟的协议。几乎所有直播平台、CDN、编码器都支持RTMP。但WebRTC不直接支持RTMP,中间需要一层转换。
我在项目中常用的方案有两种:
- 服务器端转码:WebRTC推流到SFU,SFU内部转成RTMP再推到Nginx-RTMP或SRS。
- 客户端直接推RTMP:用WebSocket把WebRTC的媒体数据取出,封装成FLV再通过RTMP推出去。
我个人更推荐第一种。为什么?客户端推RTMP太折腾了。你要处理FLV封装、RTMP握手、AMF编码……而且浏览器的WebRTC拿到的原始数据是RTP包,你得自己解包再封装。我在一个项目里试过第二种,结果光调试RTMP握手就花了两天。
来看一个用Janus推RTMP的配置片段:
// Janus RTMP推流插件配置
{
"request": "create",
"type": "rtmp",
"id": 1,
"description": "WebRTC to RTMP",
"rtmp": {
"server": "rtmp://your-nginx-server/live",
"streamkey": "stream123",
"videocodec": "h264",
"audiocodec": "aac"
}
}
这里要注意:视频编码必须是H.264。WebRTC默认支持VP8和H.264,但RTMP只认H.264。音频方面,AAC是RTMP的标配,Opus虽然WebRTC用得多,但RTMP不直接支持,需要转码。
WebRTC拉取HLS流
HLS是苹果搞的协议,延迟高但兼容性好。几乎所有浏览器、手机都支持HLS播放。但WebRTC拉HLS?听起来有点奇怪,因为WebRTC本身不需要HLS。
实际场景是这样的:用户想在一个WebRTC应用里同时展示直播流和实时通信流。比如在线教育里,老师用WebRTC上课,同时播放一段HLS录播视频。或者直播监控场景,主画面用WebRTC低延迟,副画面用HLS做备份。
实现方式很简单:
- 前端用hls.js库解析HLS流,拿到视频数据
- 把视频数据塞进HTML的video元素
- 通过WebRTC的
addTrack把video的媒体轨道传给远端
代码示例:
// 拉取HLS流并注入WebRTC
const video = document.createElement('video');
const hls = new Hls();
hls.loadSource('https://your-cdn/live/stream.m3u8');
hls.attachMedia(video);
video.play();
// 等HLS加载完成后,把视频轨道加入WebRTC
video.oncanplay = () => {
const stream = video.captureStream();
const videoTrack = stream.getVideoTracks()[0];
peerConnection.addTrack(videoTrack, stream);
};
嗯,这里有个坑——HLS的延迟问题。HLS默认延迟在6-30秒,如果你把HLS流通过WebRTC转发,远端用户看到的是半分钟前的画面。所以这个方案只适合非实时场景。
低延迟直播方案:WHIP和WHEP
WHIP和WHEP是最近两年火起来的协议。说白了,它们就是WebRTC的标准化推拉流协议。
| 协议 | 全称 | 作用 | 延迟 |
|---|---|---|---|
| WHIP | WebRTC-HTTP Ingestion Protocol | WebRTC推流到服务器 | <1秒 |
| WHEP | WebRTC-HTTP Egress Protocol | 从服务器拉取WebRTC流 | <1秒 |
WHIP/WHEP的好处是什么?标准化。以前各家WebRTC推拉流都是私有协议,对接起来很痛苦。WHIP/WHEP统一了信令交互方式,用HTTP/HTTPS做信令,媒体走SRTP/SCTP。
我记得第一次用WHIP时,最大的感受是——简单。不需要自己写信令服务器,不需要处理WebSocket。客户端发一个HTTP POST,服务器返回SDP,连接就建好了。
WHIP推流示例:
// WHIP推流 - 浏览器端
async function whipPublish(stream, whipUrl) {
// 创建PeerConnection
const pc = new RTCPeerConnection();
// 添加音视频轨道
stream.getTracks().forEach(track => pc.addTrack(track, stream));
// 创建Offer
const offer = await pc.createOffer();
await pc.setLocalDescription(offer);
// 通过HTTP POST发送Offer
const response = await fetch(whipUrl, {
method: 'POST',
headers: { 'Content-Type': 'application/sdp' },
body: offer.sdp
});
// 获取Answer
const answerSdp = await response.text();
await pc.setRemoteDescription({
type: 'answer',
sdp: answerSdp
});
return pc;
}
WHEP拉流更简单,几乎一样,只是角色互换:
// WHEP拉流 - 浏览器端
async function whepPlay(whepUrl) {
const pc = new RTCPeerConnection();
// 接收远端音视频
pc.ontrack = (event) => {
videoElement.srcObject = event.streams[0];
};
// 创建Offer
const offer = await pc.createOffer();
await pc.setLocalDescription(offer);
// 发送到WHEP端点
const response = await fetch(whepUrl, {
method: 'POST',
headers: { 'Content-Type': 'application/sdp' },
body: offer.sdp
});
const answerSdp = await response.text();
await pc.setRemoteDescription({
type: 'answer',
sdp: answerSdp
});
return pc;
}
三种方案的对比与选择
讲了这么多,到底该用哪个?我整理了一张对比表:
| 方案 | 延迟 | 兼容性 | 部署复杂度 | 适用场景 |
|---|---|---|---|---|
| WebRTC→RTMP | 2-5秒 | 高(RTMP生态成熟) | 中(需要转码服务器) | 直播平台、CDN分发 |
| WebRTC→HLS | 6-30秒 | 极高(所有浏览器支持) | 低(CDN直接支持) | 录播、非实时场景 |
| WHIP/WHEP | <1秒 | 中(需要支持WHIP的服务器) | 低(信令简单) | 实时互动直播、在线教育 |
我个人在实际项目中的选择策略:
- 要做大规模CDN分发 → 用WebRTC推RTMP,CDN拉RTMP转HLS
- 用户量不大但要求低延迟 → 直接用WHIP/WHEP,省去转码环节
- 需要兼容老旧设备 → 保留HLS作为降级方案
最后说一句:没有银弹。直播场景千变万化,RTMP、HLS、WHIP、WHEP各有各的生态位。我的经验是——先搞清楚你的用户在哪、延迟要求多高、服务器预算多少,然后再选方案。别为了追求新技术而用WHIP/WHEP,也别因为保守而死守RTMP。
公众号:蓝海资料掘金营,微信deep3321