WebRTC技术解析

WebRTC技术解析

一个无需插件、开箱即用的 P2P 音视频传输方案

什么是 WebRTC

WebRTC(Web Real-Time Communication)是 Google 开源的一项实时通信技术,直接内置于浏览器,无需安装任何插件或第三方软件,就能实现浏览器之间的音视频通话、数据传输和屏幕共享。

核心指标 :端到端延迟可控制在 200ms 以内 ,相比传统基于服务器的轮询方案延迟降低 90% ,且服务器带宽成本可减少 70% 以上(因为媒体流不经过服务器中转)。

解决了什么问题

传统网页实现视频通话需要:

  • 浏览器插件(Flash、Silverlight)→ 用户需手动安装,已淘汰

  • RTMP + Flash 流媒体服务器 → 延迟高、维护复杂

  • Socket.io + 服务端转发 → 服务器压力大,无法承载大规模并发

WebRTC 的核心突破:

  1. NAT 穿透 :通过 ICE(交互式连接建立)协议自动尝试 STUN/TURN 服务器,成功率可达 95% 以上

  2. 端到端加密:基于 DTLS 和 SRTP,媒体流传输过程全程加密

  3. 自适应码率:根据网络状况动态调整视频分辨率、帧率和码率,弱网下仍能保持通话

  4. 直接 P2P 传输 :媒体流不经过服务器,降低服务端带宽消耗 80% 以上

最简单的使用流程

WebRTC 连接建立需要信令服务器 (可用 Socket.io 实现)来交换连接信息,但实际媒体流是 P2P 的。

1. 获取本地媒体流

javascript 复制代码
// 获取摄像头和麦克风
const localStream = await navigator.mediaDevices.getUserMedia({
  video: true,
  audio: true
});
// 将流绑定到 video 元素
document.getElementById('localVideo').srcObject = localStream;

2. 创建 RTCPeerConnection 并添加流

javascript 复制代码
// 配置 STUN 服务器(用于 NAT 穿透)
const configuration = {
  iceServers: [{ urls: 'stun:stun.l.google.com:19302' }]
};
const peerConnection = new RTCPeerConnection(configuration);
// 将本地流的所有 track 添加到连接中
localStream.getTracks().forEach(track => {
  peerConnection.addTrack(track, localStream);
});
// 监听远程流并播放
peerConnection.ontrack = (event) => {
  document.getElementById('remoteVideo').srcObject = event.streams[0];
};

3. 创建 Offer 并交换 SDP

发起方(Caller):

javascript 复制代码
// 创建 offer(包含本地媒体能力描述)
const offer = await peerConnection.createOffer();
await peerConnection.setLocalDescription(offer);
// 通过信令服务器发送 offer 给对端
signalingSocket.emit('offer', { offer });

接收方(Callee):

javascript 复制代码
// 收到 offer 后设置远程描述,并创建 answer
await peerConnection.setRemoteDescription(offer);
const answer = await peerConnection.createAnswer();
await peerConnection.setLocalDescription(answer);
// 发送 answer 回对端
signalingSocket.emit('answer', { answer });

发起方收到 answer:

javascript 复制代码
await peerConnection.setRemoteDescription(answer);
// 此时连接建立完成,ontrack 会触发

4. ICE 候选交换

连接过程中,浏览器会自动收集 ICE 候选(即可能的网络路径),双方需互相交换:

javascript

javascript 复制代码
// 监听本地 ICE 候选
peerConnection.onicecandidate = (event) => {
  if (event.candidate) {
    signalingSocket.emit('ice-candidate', { candidate: event.candidate });
  }
};
// 收到对端候选时添加
signalingSocket.on('ice-candidate', async ({ candidate }) => {
  await peerConnection.addIceCandidate(candidate);
});

完整的最小 Demo(配合 Socket.io 信令)

服务端(server.js):

javascript 复制代码
const io = require('socket.io')(3000);
io.on('connection', (socket) => {
  socket.on('offer', (data) => socket.broadcast.emit('offer', data));
  socket.on('answer', (data) => socket.broadcast.emit('answer', data));
  socket.on('ice-candidate', (data) => socket.broadcast.emit('ice-candidate', data));
});

客户端核心代码(省略 HTML 元素绑定):

javascript 复制代码
// 统一封装信令处理
const socket = io('http://localhost:3000');
let pc = null;
async function startCall(isInitiator) {
 const stream = await navigator.mediaDevices.getUserMedia({ video: true, audio: true });
 document.getElementById('localVideo').srcObject = stream;

const config = { iceServers: [{ urls: 'stun:stun.l.google.com:19302' }] };
 pc = new RTCPeerConnection(config);

stream.getTracks().forEach(t => pc.addTrack(t, stream));
 pc.ontrack = e => document.getElementById('remoteVideo').srcObject = e.streams[0];
 pc.onicecandidate = e => e.candidate && socket.emit('ice-candidate', e.candidate);

if (isInitiator) {
 const offer = await pc.createOffer();
 await pc.setLocalDescription(offer);
 socket.emit('offer', { offer });
 }

socket.on('offer', async ({ offer }) => {
 await pc.setRemoteDescription(offer);
 const answer = await pc.createAnswer();
 await pc.setLocalDescription(answer);
 socket.emit('answer', { answer });
 });

socket.on('answer', async ({ answer }) => {
 await pc.setRemoteDescription(answer);
 });

socket.on('ice-candidate', async ({ candidate }) => {
 await pc.addIceCandidate(candidate);
 });
}
// 页面加载后决定是否作为发起方(例如通过 URL 参数)
startCall(window.location.search.includes('initiator=true'));
相关推荐
OpenLoong 开源社区16 小时前
打破 NAT 限制,零侵入跨网络通信:基于 WebRTC 的透明 TCP 隧道跨网络通信方案
网络协议·tcp/ip·开源·webrtc·openloong开源社区
探鸽小钛3 天前
4G 终端 ES8311 - 挂断一次,下次说话就没声了之解决办法
物联网·音视频·嵌入式·esp32·webrtc
草根大哥4 天前
AI 编程实践之自建视频CDN
音视频·webrtc·低延迟直播·视频cdn·低延迟cdn
EasyCVR5 天前
应急指挥怕延迟?聊聊视频汇聚/视频融合/视频分析EasyCVR平台WebRTC低延迟方案
音视频·webrtc·easycvr
TrueDei6 天前
WebRTC ICE 候选变成 mDNS 地址导致连接失败:原因分析与解决方案
webrtc
中屹指纹浏览器svip8 天前
WebRTC泄露是什么,怎么关闭?
webrtc
海边捡石子9 天前
从零设计一个通用智能电话平台:FreeSWITCH + ESL + AI 实战架构
人工智能·架构·webrtc
音视频牛哥10 天前
不换摄像机,也能接入 WebRTC?SmartMediaKit RTSP/RTMP 转 WHIP 技术实践
音视频·webrtc·smartmediakit·rtsp转whip·rtmp转whip·whip推流·rtsp转webrtc播放
直播技术开发交流12 天前
Android 端 WHIP 协议推流技术实践:相机/录屏/多路转发WHIP实现测试
webrtc·android whip·rtsp转whip·毫秒级实时媒体流·whip低延时推送·whip推送实现·rtsp转webrtc
程序员阿明13 天前
docker搭建视频流服务go2rtc,webRTC
docker·容器·webrtc