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 服务(支持多人、转码、录制)
相关推荐
换个昵称都难17 天前
webrtc peerconnection_server 模块介绍
运维·服务器·webrtc
EasyGBS17 天前
延迟直降90%!国标GB28181视频平台EasyGBS支持WebRTC WHIP推流设备接入,让万物互联更简单
音视频·webrtc
换个昵称都难18 天前
webrtc RtpRtcp模块化测试-MockRtpRtcp
webrtc
如意IT18 天前
指纹浏览器检测之BrowserScan的webrtc指纹检测和反检测
自动化·webrtc·chromium·浏览器开发
换个昵称都难18 天前
webrtc TURN 主要源码介绍
webrtc
换个昵称都难18 天前
webrtc RTC_P2P源码解析
asp.net·webrtc·p2p
换个昵称都难18 天前
webrtc StunServer源码介绍
webrtc
数据知道19 天前
指纹浏览器:DNS 泄漏防范与 WebRTC 本地 IP 屏蔽的底层实现
爬虫·网络协议·tcp/ip·安全·webrtc·数据采集·指纹浏览器
换个昵称都难20 天前
webrtc源码解析概要介绍
webrtc