27、WebRTC与直播结合:WebRTC推流到RTMP服务器、WebRTC拉取HLS流、低延迟直播方案(WHIP/WHEP)

聊到WebRTC和直播的结合,我得先坦白一件事——WebRTC天生就不是为直播设计的。它是个点对点的实时通信协议,讲究的是低延迟、双向通信。而直播呢?传统直播用的是RTMP推流、HLS拉流,延迟动不动就几秒甚至十几秒。

但现实需求摆在那:用户既想要WebRTC的超低延迟,又想要直播的大规模分发能力。怎么办?把WebRTC塞进直播链路里。今天我就把这块的实战经验掰开揉碎讲给你听。

核心思路:WebRTC负责采集和传输,直播服务器负责转码和分发。WebRTC推流到RTMP服务器,用户通过HLS或WebRTC拉流观看。
WebRTC + 直播混合架构 WebRTC 推流端 浏览器/App采集 WHIP协议 SFU/媒体服务器 转码·录制·混流 Janus/Medooze/LiveKit RTMP推流 RTMP服务器 Nginx-RTMP/SRS 转HLS HLS分发 CDN·m3u8切片 WebRTC拉流端 WHEP协议 WHEP拉流 内部转发 HLS拉流端 hls.js/原生播放器 HTTP拉取m3u8/ts WHIP推流 WHEP拉流 HLS传统拉流

WebRTC推流到RTMP服务器

为什么要把WebRTC推到RTMP?说白了,RTMP是目前直播生态里最成熟的协议。几乎所有直播平台、CDN、编码器都支持RTMP。但WebRTC不直接支持RTMP,中间需要一层转换。

我在项目中常用的方案有两种:

  1. 服务器端转码:WebRTC推流到SFU,SFU内部转成RTMP再推到Nginx-RTMP或SRS。
  2. 客户端直接推RTMP:用WebSocket把WebRTC的媒体数据取出,封装成FLV再通过RTMP推出去。

我个人更推荐第一种。为什么?客户端推RTMP太折腾了。你要处理FLV封装、RTMP握手、AMF编码……而且浏览器的WebRTC拿到的原始数据是RTP包,你得自己解包再封装。我在一个项目里试过第二种,结果光调试RTMP握手就花了两天。

实战推荐:用Janus或Medooze做SFU,它们内置了RTMP推流插件。配置好RTMP地址,WebRTC流自动转成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不直接支持,需要转码。

踩坑提醒:我曾经遇到一个诡异的问题——推流到RTMP后画面卡顿。排查了半天,发现是WebRTC的码率波动太大,RTMP服务器扛不住。解决方案是在SFU端做码率平滑,或者限制WebRTC的最大码率。

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转发,远端用户看到的是半分钟前的画面。所以这个方案只适合非实时场景。

我的建议:如果对延迟有要求,别用HLS。直接用WebRTC拉流,或者用下面要讲的WHEP。

低延迟直播方案: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;
}
WHIP/WHEP的服务器支持:目前LiveKit、Medooze、Janus都支持。SRS也在最新版本里加入了WHIP支持。如果你自己搭建,推荐用LiveKit,它对WHIP/WHEP的支持最成熟。

三种方案的对比与选择

讲了这么多,到底该用哪个?我整理了一张对比表:

方案 延迟 兼容性 部署复杂度 适用场景
WebRTC→RTMP 2-5秒 高(RTMP生态成熟) 中(需要转码服务器) 直播平台、CDN分发
WebRTC→HLS 6-30秒 极高(所有浏览器支持) 低(CDN直接支持) 录播、非实时场景
WHIP/WHEP <1秒 中(需要支持WHIP的服务器) 低(信令简单) 实时互动直播、在线教育

我个人在实际项目中的选择策略:

  • 要做大规模CDN分发 → 用WebRTC推RTMP,CDN拉RTMP转HLS
  • 用户量不大但要求低延迟 → 直接用WHIP/WHEP,省去转码环节
  • 需要兼容老旧设备 → 保留HLS作为降级方案
重要提醒:WHIP/WHEP虽然延迟低,但它的可扩展性不如RTMP+HLS。WHIP/WHEP本质上是WebRTC,每个用户都需要一个PeerConnection,服务器压力大。如果并发上万,建议用RTMP+HLS做分发层,WHIP/WHEP只做推流和少量拉流。

最后说一句:没有银弹。直播场景千变万化,RTMP、HLS、WHIP、WHEP各有各的生态位。我的经验是——先搞清楚你的用户在哪、延迟要求多高、服务器预算多少,然后再选方案。别为了追求新技术而用WHIP/WHEP,也别因为保守而死守RTMP。

一句话总结:WebRTC推RTMP解决生态兼容问题,WHIP/WHEP解决低延迟问题,HLS解决广泛分发问题。三者配合使用,才是直播架构的最佳实践。

公众号:蓝海资料掘金营,微信deep3321