跳转到内容

能力中的 API

WebRTC API

发布于 更新于

自 2017-09 起广泛可用W3C 草案

WebRTC 在两个浏览器之间(或浏览器与媒体服务器之间)通过加密 UDP 直接传输音频、视频和任意数据:RTCPeerConnection 经 SDP 的 offer/answer 交换协商编解码器与传输,ICE 寻找网络路径,addTrack() 挂上 getUserMedia() 采集的 MediaStreamTrack,RTCDataChannel 经 SCTP 传递应用消息。offer、answer 与 ICE candidate 的交换(信令)留给应用自己完成,页面已有的任何通道都行,通常是 WebSocket。

无前缀的 RTCPeerConnection 构造函数自 Chrome 56、Edge 15、Firefox 44、Safari 11、Samsung Internet 6 和 Android WebView 56 起可用,因此 Baseline 把它列为 2017-09 起广泛可用。getUserMedia() 带 [SecureContext],采集需要 https:// 或 localhost;对等连接本身不要求,但采集不了的页面通常也没什么可发送。

new RTCPeerConnection()
new RTCPeerConnection(configuration)
pc.createOffer()
pc.createOffer(options)
pc.createAnswer()
pc.setLocalDescription()
pc.setLocalDescription(description)
pc.setRemoteDescription(description)
pc.addIceCandidate(candidate)
pc.addTrack(track)
pc.addTrack(track, ...streams)
pc.createDataChannel(label)
pc.createDataChannel(label, options)
pc.close()
navigator.mediaDevices.getUserMedia(constraints)

createOffer() 与 createAnswer() 以 RTCSessionDescriptionInit({ type, sdp })兑现;两个设置描述的方法和 addIceCandidate() 以 undefined 兑现,并在连接的操作链上按调用顺序执行。不带参数调用 setLocalDescription() 会按当前 signalingState 创建所需的 offer 或 answer。addTrack() 同步返回 RTCRtpSender,createDataChannel() 同步返回 RTCDataChannel,getUserMedia() 以 MediaStream 兑现。

构造函数的 RTCConfiguration、createDataChannel() 的 RTCDataChannelInit 和 getUserMedia() 的 MediaStreamConstraints 是一次典型调用会碰到的字典(见 RTCConfiguration 成员(w3.org))。

成员 类型 必填 说明
configuration.iceServers sequence<RTCIceServer> 否 默认 []。每项含 urls(一个或多个 stun:/turn:/turns: URL)以及 username 和 credential,turn: 与 turns: 条目必须携带后两者。没有 TURN 服务器时,对称 NAT 后的对等方无法连通。
configuration.iceTransportPolicy RTCIceTransportPolicy 否 "all"(默认)或 "relay";"relay" 只使用 TURN candidate,从而隐藏用户 IP。
configuration.bundlePolicy RTCBundlePolicy 否 "balanced"(默认)、"max-compat" 或 "max-bundle";控制多个媒体段协商多少条传输。
configuration.rtcpMuxPolicy RTCRtcpMuxPolicy 否 规范中只剩 "require";不复用 RTCP 的远端描述会被拒绝。
configuration.certificates sequence<RTCCertificate> 否 默认 []。来自 RTCPeerConnection.generateCertificate() 的证书,使 DTLS 指纹跨会话稳定。
configuration.iceCandidatePoolSize octet 否 默认 0。在 setLocalDescription() 之前预先收集的 candidate 数。
options.ordered(数据通道) boolean 否 默认 true。false 时消息按到达顺序交付。
options.maxPacketLifeTime / options.maxRetransmits unsigned short 否 互斥;任一项都把通道变为部分可靠。
options.negotiated / options.id boolean / unsigned short 否 negotiated: true 配合显式 id(0 到 65534)跳过带内通道通告;两端都必须创建该通道。
constraints.audio / constraints.video boolean 或 MediaTrackConstraints 否 至少一项存在且为真值。对象形式接受 width、height、frameRate、facingMode、deviceId,每项可写裸值(偏好)或 { exact } / { ideal }。

下列名称来自 webrtc-pc 与 mediacapture-main;RTCPeerConnection 的方法以拒绝 Promise 的方式报错,标注「抛出」的除外(带 errorDetail 的 SDP 与传输失败见 RTCError(w3.org))。

异常 条件
InvalidStateError signalingState 为 "closed" 后调用 createOffer()、createAnswer()、两个设置描述的方法、addIceCandidate()、addTrack()(抛出)和 createDataChannel()(抛出);remoteDescription 为 null 时调用 addIceCandidate();状态不是 "have-remote-offer" 或 "have-local-pranswer" 时调用 createAnswer()。
InvalidModificationError 传给 setLocalDescription() 的 offer 或 answer,其 sdp 与 createOffer()/createAnswer() 最近生成的不一致。
InvalidAccessError 构造函数收到过期或跨源证书,或没有 username 和 credential 的 turn: 服务器(抛出);描述未通过 RFC 9429 语义校验、在 rtcpMuxPolicy: "require" 下不复用 RTCP,或重新协商 RID;对同一个 track 调用两次 addTrack()(抛出)。
SyntaxError iceServers 的某个 URL 无法解析、带路径,或 query 不是 transport=udp 或 transport=tcp(构造函数与 setConfiguration() 抛出)。
NotSupportedError iceServers 的某个 URL 使用浏览器未实现的协议(抛出)。
TypeError addIceCandidate() 收到非空 candidate 但 sdpMid 与 sdpMLineIndex 都为 null;createDataChannel() 同时设置 maxPacketLifeTime 和 maxRetransmits、用 negotiated: true 却不给 id,或使用 id: 65535(抛出)。
OperationError addIceCandidate() 的 sdpMid 匹配不到任何媒体段、sdpMLineIndex 越界,或 usernameFragment 与已应用的远端描述不匹配;createDataChannel() 发现 id 已被占用或耗尽(抛出);应用描述因上述以外的原因失败;createOffer() 无法检查系统的编解码器与资源。
RTCError DOMException 的子类,errorDetail 指明失败类型:设置描述的方法给出 "sdp-syntax-error"(附 sdpLineNumber),传输层给出 "dtls-failure"、"fingerprint-failure"、"sctp-failure" 或 "data-channel-failure",经 RTCDataChannel 的 error 事件送达。
NotAllowedError(getUserMedia) 用户拒绝授权、页面被 camera/microphone Permissions Policy 禁止,或调用缺少浏览器要求的激活。
NotFoundError(getUserMedia) 不存在所请求类型的设备。
NotReadableError(getUserMedia) 操作系统或其他应用占用着设备。
OverconstrainedError(getUserMedia) 某个 exact 约束无法满足;error.constraint 给出其名称。
TypeError / SecurityError(getUserMedia) constraints 既没有 audio 也没有 video;文档不是 fully active 或上下文不安全。

三个示例构成一次完整通话:采集、协商、交换数据,每个都给出缺少支持或权限时所走的分支。

采集本地媒体并在约束不满足时回退

Section titled “采集本地媒体并在约束不满足时回退”

把 RTCPeerConnection 和 navigator.mediaDevices 一起检测;http:// 页面的 navigator.mediaDevices 为 undefined。先请求理想分辨率,摄像头满足不了 exact 约束时改用最简单的 video: true 重试。

async function capture() {
if (!("RTCPeerConnection" in window) || !navigator.mediaDevices?.getUserMedia) {
return null; // 改为显示上传表单,而不是实时通话
}
try {
return await navigator.mediaDevices.getUserMedia({
audio: true,
video: { width: { ideal: 1280 }, height: { ideal: 720 }, facingMode: { exact: "user" } },
});
} catch (err) {
if (err.name === "OverconstrainedError") {
console.warn(`约束 ${err.constraint} 无法满足,去掉后重试`);
return navigator.mediaDevices.getUserMedia({ audio: true, video: true });
}
if (err.name === "NotAllowedError") return null; // 权限被拒:走无音视频的回退
throw err;
}
}

桌面端最常触发它的是 facingMode: { exact: "user" },因为网络摄像头根本不报告朝向。

由浏览器事件驱动 offer、answer 与 ICE

Section titled “由浏览器事件驱动 offer、answer 与 ICE”

让 negotiationneeded 来创建 offer,这样之后加入的 track 会自动重新协商;把每个 icecandidate 经信令通道转发;只在 setRemoteDescription() 执行之后才把对端的 candidate 交给 addIceCandidate()。

function connect(stream, signaling, iceServers) {
const pc = new RTCPeerConnection({ iceServers });
const controller = new AbortController();
const { signal } = controller;
for (const track of stream.getTracks()) pc.addTrack(track, stream);
pc.addEventListener("negotiationneeded", async () => {
await pc.setLocalDescription(); // 按当前状态创建 offer
signaling.send({ description: pc.localDescription });
}, { signal });
pc.addEventListener("icecandidate", ({ candidate }) => {
if (candidate) signaling.send({ candidate });
}, { signal });
pc.addEventListener("track", ({ streams }) => {
document.querySelector("#remote").srcObject = streams[0];
}, { signal });
signaling.onmessage = async ({ description, candidate }) => {
if (description) {
await pc.setRemoteDescription(description);
if (description.type === "offer") {
await pc.setLocalDescription();
signaling.send({ description: pc.localDescription });
}
} else if (candidate && pc.remoteDescription) {
await pc.addIceCandidate(candidate);
}
};
return () => {
controller.abort();
for (const track of stream.getTracks()) track.stop();
pc.close();
};
}

返回的函数就是挂断:中止 signal 一次性移除全部监听器,停止 track 会熄灭摄像头指示灯,close() 结束 ICE。addEventListener() 的 signal 选项需要 Chrome 90、Firefox 86 或 Safari 15;低于这些版本时保留具名处理函数并调用 removeEventListener()。

通过数据通道收发消息并回退到 WebSocket

Section titled “通过数据通道收发消息并回退到 WebSocket”

数据通道不需要任何媒体。在第一个 offer 之前创建通道,使其在带内被通告;每次 send() 前读取 readyState,通道只在 "open" 时可用。

function openChat(pc, signalingSocket, onMessage) {
if (!pc || typeof pc.createDataChannel !== "function") {
signalingSocket.addEventListener("message", (e) => onMessage(e.data));
return (text) => signalingSocket.send(text); // 经服务器中转
}
const channel = pc.createDataChannel("chat", { ordered: false, maxRetransmits: 0 });
channel.binaryType = "arraybuffer";
channel.addEventListener("message", (e) => onMessage(e.data));
channel.addEventListener("error", (e) => console.error(e.error.errorDetail, e.error.message));
pc.addEventListener("datachannel", ({ channel: incoming }) => {
incoming.addEventListener("message", (e) => onMessage(e.data));
});
return (text) => {
if (channel.readyState === "open") channel.send(text);
else signalingSocket.send(text);
};
}

ordered: false 配合 maxRetransmits: 0 给在线状态或光标位置更新提供类似 UDP 的交付;必须完整、有序到达的聊天文本则两个选项都不要设。

规范

规范状态
WebRTC(RTCPeerConnection)W3C 草案
WebRTC: RTCPeerConnection constructorW3C 草案
WebRTC: setLocalDescription()W3C 草案
WebRTC: addIceCandidate()W3C 草案
WebRTC: createDataChannel()W3C 草案
Media Capture and Streams: getUserMedia()W3C 草案
  • 图例
  • 支持
  • 部分支持
  • 需开启标志
  • 不支持
  • 未知
浏览器 / 平台支持版本置信度来源备注
Chrome (Desktop)支持56高来源—
Chrome (Android)支持56高来源1
Edge (Desktop)支持15高来源—
Firefox (Desktop)支持44高来源—
Firefox (Android)支持44高来源—
Safari (macOS)支持11高来源—
Safari (iOS)支持11高来源2
Samsung Internet支持6.0高来源3
WebView (Android)支持56高来源4
  1. 由 browser-compat-data 镜像自 Chrome 的数据推导。
  2. 由 browser-compat-data 镜像自 Safari 的数据推导。
  3. 由 browser-compat-data 镜像自 Chrome Android 的数据推导。
  4. 由 browser-compat-data 镜像自 Chrome Android 的数据推导。

源数据: /compatibility/web-rtc.json · 全球使用占比: 93 % (StatCounter 2026-05)

来源: 规范 · MDN · 最近核验 2026-10-03 · 置信度: 高 (由来源计算)