看到了很多次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 建立成功后,音视频流不经过信令服务器

相关推荐
best6664 小时前
Flex 与 Grid 的 order 参数:布局界的 "插队神器"
前端
猫七先生4 小时前
微信小程序一键登录可行性方案
前端·微信小程序
维他AD钙4 小时前
前端开发 8 个非常实用小技巧:高效解决日常开发痛点
前端
光影少年4 小时前
webpack和vite优化方案都有哪些
前端·webpack·node.js
给月亮点灯|4 小时前
Vue基础知识-脚手架开发-初始化目录解析
前端·javascript·vue.js
kk不中嘞4 小时前
Webpack 核心原理剖析
前端·webpack·node.js
Yvonne爱编码4 小时前
简述ajax、node.js、webpack、git
前端·git·ajax·webpack·node.js·visual studio
周小码4 小时前
CesiumJS详解:打造专业级Web 3D地球仪与地图的JavaScript库
前端·javascript·3d
哆啦A梦15884 小时前
Element-Plus
前端·vue.js·ts