看到了很多次WebRTC,但是你真的需要它吗?

我最开始接触WebRTC应该是在24年的时候,那时候公司安防摄像头并不支持WebRTC,在这之前公司也没有web端查看摄像头画面的功能,因为公司新要一个新的平台,用于安防和考勤使用才开始转向WebRTC。

什么是WebRTC?

你可以理解为它是一个开源的实时通信技术,它允许网页、移动端或应用之间进行音视频通话、文件传输和数据共享,不用安装额外的插件或者第三方软件。主流浏览器都是支持WebRTC的,JS API直接调 navigator.mediaDevices.getUserMedia() 就能拿到摄像头,这个是很方便的。像我们公司的智能摄像头,就是把实时视频通过WebRTC推流到浏览器。

WebRTC优缺点

它的优点就是实时性好、延迟低对于安放摄像头来说这点还是很重要的,缺点就是P2P 建立连接受限NAT、防火墙这些,需要STUN/TURN 服务器辅助,我们在第一个项目的时候并没有搭建自己的STUN/TURN 服务器,而是使用的亚马逊的Amazon Kinesis Video Streams with WebRTC 服务,自己搭建运维成本高还需要考虑各种其他情况。

什么时候适合使用WebRTC呢?

安防摄像头的实时查看,现在我们用的很多的视频会议,甚至是远程操作无人机、机器人这些都可以使用WebRTC,因为它延迟低(<500ms)且浏览器直接支持。但是在大规模分发、存储场景下RTSP/RTMP/HLS仍然还是首选,因为WebRTC 在大规模转发、带宽利用上成本是很高的。现在海康威视、萤石、TP-Link、Home Assistant 插件都开始支持WebRTC,如果你刚好做的就是这些有关的工作,也可以先开始了解WebRTC了。

如何使用WebRTC?

其实WebRTC的使用核心就是信令交换,我这演示一个简单的方案。

获取本地媒体流(摄像头、麦克风)

js 复制代码
const localStream = await navigator.mediaDevices.getUserMedia({
  video: true,
  audio: true
});
document.getElementById("localVideo").srcObject = localStream;

建立 PeerConnection(点对点连接)

创建 RTCPeerConnection 对象,配置 ICE 服务器(STUN/TURN),但是有时候P2P是不成功的,这个时候就会走TURN 中继转发了。TURN 中继是需要TURN 服务器的,不过我们没有TURN服务器也不影响P2P使用的。如果感兴趣的也可以自建 STUN/TURN,在国内的云服务器(阿里云、腾讯云、华为云等)上自己部署 coturn

js 复制代码
const pc = new RTCPeerConnection({
  iceServers: [
    { urls: "stun:stun.l.google.com:19302" } // Google 免费 STUN
    /** 
    	无法访问谷歌的可以尝试下面的,但这些都是公共服务,稳定性和可控性不高,不适合生产。
    	stun:stun.xten.com
		stun:stun.voipbuster.com
		stun:stun.counterpath.net
		stun:stun.sipnet.net
    */
  ]
});

// 将本地流添加到连接中
localStream.getTracks().forEach(track => pc.addTrack(track, localStream));

// 远端视频接收
pc.ontrack = (event) => {
  document.getElementById("remoteVideo").srcObject = event.streams[0];
};

信令交换(核心!需要服务器中转,下面会给出服务端简单的案例)

示例:创建 offer

js 复制代码
const offer = await pc.createOffer();
await pc.setLocalDescription(offer);

// 连接你的信令服务器
const signalingServer = new WebSocket("ws://localhost:8080");

// 收到远端消息
signalingServer.onmessage = async (msg) => {
  const data = JSON.parse(msg.data);
  if (data.type === "answer") {
    await pc.setRemoteDescription(new RTCSessionDescription(data.sdp));
  } else if (data.type === "candidate") {
    await pc.addIceCandidate(new RTCIceCandidate(data.candidate));
  }
};


// 把 offer 发送给远端(通过 WebSocket/HTTP)
signalingServer.send(JSON.stringify({ type: "offer", sdp: offer }));

ICE 候选收集 & 发送

js 复制代码
pc.onicecandidate = (event) => {
  if (event.candidate) {
    signalingServer.send(JSON.stringify({ type: "candidate", candidate: event.candidate }));
  }
};

// 接收远端的 candidate
signalingServer.onmessage = async (msg) => {
  const data = JSON.parse(msg.data);
  if (data.type === "candidate") {
    await pc.addIceCandidate(new RTCIceCandidate(data.candidate));
  }
};

后端信令服务器示例(Node.js + WebSocket)

js 复制代码
// server.js
const WebSocket = require("ws");
const wss = new WebSocket.Server({ port: 8080 });

wss.on("connection", ws => {
  ws.on("message", msg => {
    // 简单广播给其他客户端
    wss.clients.forEach(client => {
      if (client !== ws && client.readyState === WebSocket.OPEN) {
        client.send(msg);
      }
    });
  });
});

它用来发送/接收 SDP、ICE Candidate,让两个浏览器知道对方怎么连,P2P 建立成功后,音视频流不经过信令服务器

相关推荐
hunterandroid7 分钟前
[Android 从零到一] Compose LazyColumn 性能优化:key、稳定性与重组治理
android·前端
lichenyang45316 分钟前
从一次团队邀请开始:用 React、NestJS 与 Socket.IO 做可靠的实时通知
前端
vtian17 分钟前
一篇文章吃透 Monorepo:pnpm + Turborepo + Changesets 全流程实战(含 8 个踩坑)
前端
默_笙22 分钟前
❗ 点击计数按钮,为什么"峨眉队"也跟着重新渲染?React.memo 说:我记住了
前端·javascript
31535669131 小时前
DeepSeek Harness 发布后,我没急着跑 Demo,先把 `.agents/` 翻了一遍
前端·后端·github
名字还没想好☜1 小时前
Next.js 用 Server Components 直连数据库:去掉 API 层的边界,和三条别踩的安全红线
前端·javascript·数据库·安全·react·next.js
学习zhao极致it1 小时前
2020全新React教程全家桶实战redux+antd+React Hooks前端js视频
前端
用户921080262861 小时前
Bubble 消息操作区改造:复制、重新生成和反馈
前端
LEE1 小时前
AI Agent 都在疯狂加功能,它说:我全砍了
前端·后端
张清悠1 小时前
用 TRAE Work 把 PRD + 设计稿注释,整理成 20 分钟可排期前端任务清单
前端