我最开始接触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 建立成功后,音视频流不经过信令服务器