WebRTC 完整调用流程(前端纯 JS 实现,最简可运行)

一、核心 API 简述

  1. navigator.mediaDevices.getUserMedia:获取本地摄像头 / 麦克风媒体流
  2. RTCPeerConnection:WebRTC 核心,建立 P2P 连接、传输音视频
  3. SDP:会话描述协议(信令交换:Offer / Answer)
  4. ICE:网络穿透,自动协商直连地址

WebRTC 本身不含信令服务,需自行实现信令(WebSocket/Socket.IO 交换 SDP、ICE 候选)。

二、最简可运行 Demo(本地模拟双人通话,无后端)

该示例单页面模拟两端,不用后端,直接浏览器打开即可测试。

html 复制代码
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <title>WebRTC 本地调用演示</title>
    <style>
        video { width: 400px; border: 1px solid #ccc; }
    </style>
</head>
<body>
    <h3>本地视频</h3>
    <video id="localVideo" autoplay muted playsinline></video>
    <h3>远端视频</h3>
    <video id="remoteVideo" autoplay playsinline></video>

    <script>
        // 1. 获取 DOM
        const localVideo = document.getElementById('localVideo');
        const remoteVideo = document.getElementById('remoteVideo');

        // 配置:音视频轨道 + ICE 服务器(STUN 用于内网穿透)
        const pcConfig = {
            iceServers: [
                { urls: 'stun:stun.l.google.com:19302' }
            ]
        };

        let localStream;
        let pc1, pc2; // 模拟两个 Peer

        // 启动
        start();

        async function start() {
            try {
                // 步骤1:采集本地音视频流
                localStream = await navigator.mediaDevices.getUserMedia({
                    video: true,
                    audio: true
                });
                localVideo.srcObject = localStream;

                // 步骤2:创建两个 PeerConnection(模拟A、B两端)
                pc1 = new RTCPeerConnection(pcConfig);
                pc2 = new RTCPeerConnection(pcConfig);

                // 将本地流添加到 Peer
                localStream.getTracks().forEach(track => {
                    pc1.addTrack(track, localStream);
                });

                // 远端收到轨道,渲染视频
                pc2.ontrack = e => {
                    remoteVideo.srcObject = e.streams[0];
                };

                // ICE 候选互相交换(模拟信令转发)
                pc1.onicecandidate = e => e.candidate && pc2.addIceCandidate(e.candidate);
                pc2.onicecandidate = e => e.candidate && pc1.addIceCandidate(e.candidate);

                // 步骤3:创建 Offer
                const offer = await pc1.createOffer();
                await pc1.setLocalDescription(offer);

                // 步骤4:B 端接收 Offer,创建 Answer
                await pc2.setRemoteDescription(offer);
                const answer = await pc2.createAnswer();
                await pc2.setLocalDescription(answer);

                // 步骤5:A 端接收 Answer
                await pc1.setRemoteDescription(answer);

            } catch (err) {
                console.error('WebRTC 调用失败:', err);
                alert('请允许摄像头/麦克风权限,且使用 HTTPS / localhost');
            }
        }
    </script>
</body>
</html>

运行要求

  1. 必须 localhost / HTTPS 环境(浏览器安全策略,HTTP 公网无法调用摄像头)
  2. 浏览器授权摄像头、麦克风权限
  3. 现代浏览器(Chrome / Edge / Firefox)

三、标准 P2P 通话完整流程(真实线上架构)

整体链路

客户端A ↔ 信令服务 (WebSocket/Socket.IO) ↔ 客户端B ↔ WebRTC P2P

3.1. 标准调用步骤(时序)

1,本地媒体采集

js

运行

javascript 复制代码
const stream = await navigator.mediaDevices.getUserMedia({video:true,audio:true})

2,创建 RTCPeerConnection

3,本地轨道 addTrack 进 Peer

  • A 发起 Offer
    • pc.createOffer() → setLocalDescription(offer)
    • 通过信令把 Offer 发给 B
  • B 收到 Offer
    • setRemoteDescription(offer)
    • createAnswer() → setLocalDescription(answer)
    • 通过信令把 Answer 发回 A
  • A 收到 Answer
    • setRemoteDescription(answer)
  • ICE 候选交换(网络穿透)
    • 两端 onicecandidate 拿到候选,通过信令互发
    • 对方 addIceCandidate
  • 连接成功 → ontrack 收到远端流,播放

3.2. 纯客户端核心代码(分离两端)

发起方(A)

javascript 复制代码
// 拿到本地流后
const pc = new RTCPeerConnection(pcConfig);
localStream.getTracks().forEach(t => pc.addTrack(t, localStream));

// 收到远端流
pc.ontrack = e => remoteVideo.srcObject = e.streams[0];

// ICE 候选发送给对方
pc.onicecandidate = e => {
  if (e.candidate) socket.emit('ice', e.candidate);
};

// 创建并发送 Offer
const offer = await pc.createOffer();
await pc.setLocalDescription(offer);
socket.emit('offer', offer);

// 接收对方 Answer
socket.on('answer', async answer => {
  await pc.setRemoteDescription(answer);
});

// 接收对方 ICE 候选
socket.on('ice', candidate => {
  pc.addIceCandidate(candidate);
});

接收方(B)

javascript 复制代码
const pc = new RTCPeerConnection(pcConfig);
localStream.getTracks().forEach(t => pc.addTrack(t, localStream));

pc.ontrack = e => remoteVideo.srcObject = e.streams[0];
pc.onicecandidate = e => e.candidate && socket.emit('ice', e.candidate);

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

// 接收 ICE
socket.on('ice', candidate => pc.addIceCandidate(candidate));

四、常用配置 & 功能扩展

1. 只开音频 / 只开视频

js

运行

javascript 复制代码
// 仅语音
navigator.mediaDevices.getUserMedia({ audio: true, video: false })
// 仅画面
navigator.mediaDevices.getUserMedia({ audio: false, video: true })

2. 关闭音视频轨道(静音 / 关摄像头)

js

运行

javascript 复制代码
// 关闭摄像头
localStream.getVideoTracks()[0].enabled = false;
// 静音
localStream.getAudioTracks()[0].enabled = false;

3. 挂断通话

js

运行

javascript 复制代码
// 停止媒体流
localStream.getTracks().forEach(track => track.stop());
// 关闭连接
pc.close();

4. 多路流 / 屏幕共享

屏幕共享 API:

js

运行

javascript 复制代码
const screenStream = await navigator.mediaDevices.getDisplayMedia({ video: true });

五、常见报错 & 排查

  1. NotAllowedError

    • 原因:未授权权限 / 非 localhost/HTTPS
    • 解决:本地用 localhost,线上部署 HTTPS
  2. RTCPeerConnection 不存在

    • 原因:浏览器过低 / 禁用 WebRTC
    • 解决:升级 Chrome/Edge
  3. 能发信令但看不到画面

    • 大概率 ICE/STUN 穿透失败,更换可用 STUN/TURN 服务器

六、后端信令选型(极简)

前端 WebRTC 只负责媒体连接,信令必须单独做:

  • 小型应用:Socket.IO(Node.js 最简)
  • 大型 / 分布式:原生 WebSocket、MQTT
  • 商用:直接用 Janus / SFU 服务(支持多人、转码、录制)
相关推荐
O。O蛋黄酥啊11 小时前
实时语音 Agent 开发完全指南:从 ASR/TTS 到 WebRTC/VAD 的链路拆解
大模型·webrtc·语音识别·tts·asr
海水冷却2 天前
WebRTC 视频通话:从裸写信令到 30 分钟上线
音视频·webrtc
PiaoKe___2 天前
云手机底层架构与工程实践:从 KVM/容器到 WebRTC 串流
智能手机·架构·webrtc
znnnk2 天前
【网络优化】减少B站直播卡顿的上行占用:AdGuard nowebrtc规则 / WebRTC Control 实测
服务器·网络·webrtc·直播·bilibili·adguard
平行云PVT2 天前
国产信创实时云渲染解决方案:如何实现数字孪生与三维应用云化部署?
unity·webrtc·unreal engine·实时云渲染·国产信创·摩尔线程·砺算科技
福大大架构师每日一题4 天前
mediamtx v1.21.2发布:UDP、JWT、RTSP、RTMP、HLS、WebRTC全面修复,稳定性与安全性再提升
网络协议·udp·webrtc
草根大哥6 天前
08-横向扩容与突发高并发:加机器到底买到了什么
运维·webrtc·srt·横向扩容·whip·自建cdn
草根大哥6 天前
07-P2P 直连与节点池调度
webrtc·p2p·srt·whip·whep·自建cdn
草根大哥6 天前
06-可观测性:端到端延迟、稳定性与画质
webrtc·srt·whip·自建cdn·ppcdn
大文说跨境6 天前
用 Python 自动化检测浏览器环境指纹:WebRTC 泄露、Canvas 哈希与时区一致性校验
python·自动化·webrtc