WebRTC(七):媒体能力协商

目的

在 WebRTC 中,每个浏览器或终端支持的音视频编解码器、分辨率、码率、帧率等可能不同。媒体能力协商的目的就是:

  • 确保双方能"听得懂"对方发的媒体流;
  • 明确谁发送、谁接收、怎么发送;
  • 保障连接的互操作性 和兼容性。

P2P的基本流程

参与角色

角色 说明
peerA 发起连接的端(通常是主叫)
peerB 接收连接的端(通常是被叫)
signal 信令服务器,用于中转 SDP 和 ICE 信息,但不参与媒体传输
stun/turn STUN/TURN 服务器,用于穿透 NAT 或作为中继,辅助建立 P2P 或 Relay 通信

流程详解

连接信令服务器

  • peerA 和 peerB 分别通过 WebSocket 或其他方式连接信令服务器,用于后续中转 SDP 和 ICE 数据。

PeerConnection 创建 + 添加媒体流(媒体准备)

peerA:

  • 创建 RTCPeerConnection 实例。
  • 通过 getUserMedia() 获取本地音视频流。
  • addTrack() 或 addStream() 将媒体加入连接对象。

创建 Offer SDP(发起协商)

  • peerA 调用 createOffer():
    • 生成包含自身媒体能力(支持的音视频编解码器、方向、SSRC、码率等)的 SDP。
  • peerA 调用 setLocalDescription(offer):
    • 表示将该 SDP 用作自己的本地描述。
    • 浏览器会开始进行 ICE 候选收集(即寻找可用的 IP/端口路径)。

发送 Offer SDP(信令交换)

  • peerA 将 Offer SDP 通过 signal 发送给 peerB。
  • 此时可以看到 peerA → signal → peerB 的 "Send SDP Offer"。

接收方处理 Offer

peerB:

  • 创建 RTCPeerConnection 实例。
  • setRemoteDescription(offer) 设置远端描述(即 peerA 的 SDP)。
  • 浏览器据此了解 peerA 的媒体能力,并开始准备回答。

创建 Answer SDP(应答协商)

  • peerB 调用 createAnswer(),根据双方交集生成 Answer SDP。
  • 调用 setLocalDescription(answer) 设置为自己的本地描述。

发送 Answer SDP(信令交换)

  • peerB → signal → peerA 发送 Answer SDP。
  • peerA 调用 setRemoteDescription(answer),设置为远端描述,协商正式完成。

到这一步为止,媒体能力协商完成(Codec、方向等确认)。

ICE 候选协商(网络通路探测)

  • 双方浏览器后台通过 STUN 向公网发送绑定请求,收集本地的候选地址(ICE candidate)。
  • 每收集到一个 ICE 候选地址,会触发 onicecandidate 事件。

发送和添加 ICE 候选(网络路径建立)

  • 每次 onicecandidate 被触发,候选地址通过信令发送给对方:
    • peerA → signal → peerB
    • peerB → signal → peerA
  • 收到对方候选后,使用 addIceCandidate() 添加。

当 ICE 连接状态变为 connected 或 completed 时,即可开始进行媒体传输。

媒体流建立

  • 媒体协商完成,网络路径打通后:
    • peerB 触发 onAddStream或 ontrack 事件,表示收到远端音视频流。
    • 同样地,peerA 也会收到对方的流。

重点流程

阶段 关键函数 作用
媒体协商 createOffer / createAnswer 生成 SDP
setLocalDescription / setRemoteDescription 设置 SDP 本地/远端描述
网络协商 onicecandidate / addIceCandidate 交换并使用候选地址建立连接
信令传输 signal 中转 SDP 和 ICE,但不传输媒体
媒体传输 addTrack / ontrack 接收对方音视频流

重要函数

createOffer()

作用

生成一个 SDP Offer,描述本地支持的音视频媒体能力(如 codec、媒体方向、分辨率、带宽等)。

使用示例

javascript 复制代码
const offer = await pc.createOffer();

应用场景

  • 发起方调用,用于开始媒体协商;
  • 会触发 ICE 候选的收集。

createAnswer()

作用

接收到对方 SDP Offer 后,根据自己的能力生成一个 SDP Answer。

使用示例

javascript 复制代码
const answer = await pc.createAnswer();

应用场景

  • 接收方调用,用于回应媒体协商;
  • 也会触发 ICE 候选收集。

setLocalDescription(desc)

作用

设置本地描述(Offer 或 Answer),并开始 ICE 候选收集。

使用示例

javascript 复制代码
await pc.setLocalDescription(offer); // offer 或 answer 都可以

说明

  • 必须在发送 SDP 之前调用;
  • 必须配合 createOffer 或 createAnswer 使用。

setRemoteDescription(desc)

作用

设置远端 SDP 描述(对方发送的 offer 或 answer),用于协商建立媒体连接。

使用示例

javascript 复制代码
await pc.setRemoteDescription(remoteOffer);

注意

  • 必须在 createAnswer() 前调用(即先设置远端再生成应答);
  • SDP 是通过信令服务器中转接收的。

onicecandidate

作用

监听本地收集到的每一个 ICE 候选地址(IP + port),用于网络路径穿透。

示例

javascript 复制代码
pc.onicecandidate = (event) => {
  if (event.candidate) {
    sendCandidateToPeer(event.candidate); // 通过信令发送
  }
};

注意

  • 每收集一个候选地址就触发一次;
  • 全部收集完毕会收到一个 null 的 event.candidate。

addIceCandidate(candidate)

作用

将从对方接收到的 ICE 候选加入本地 ICE 代理中,用于连接建立。

示例

javascript 复制代码
await pc.addIceCandidate(remoteCandidate);

注意

  • 一定要在 setRemoteDescription 之后调用;
  • 多次调用用于添加多个候选。

addTrack

作用

添加音频或视频轨道(MediaStreamTrack)。

示例

js 复制代码
const stream = await navigator.mediaDevices.getUserMedia({ video: true, audio: true });
const pc = new RTCPeerConnection();

stream.getTracks().forEach(track => {
  pc.addTrack(track, stream); // 同一个 MediaStream 确保同步
});

特性

特性 说明
精细轨道控制 每次添加单个 MediaStreamTrack(音频或视频)
支持多轨道发送 可多次调用添加多个音频/视频轨
支持轨道同步 若多个轨道来自同一个 MediaStream,浏览器会自动尝试同步播放
返回 RTCRtpSender 可动态设置编码参数、带宽、分辨率等
支持动态添加轨道 连接建立后也可添加轨道,会触发重新协商
可用于替代 addStream 现代 WebRTC 标准推荐使用,addStream() 已废弃
与 ontrack 配套使用 远端通过 ontrack 事件接收媒体轨道
轨道标签与 ID 传递 SDP 会携带轨道的 id 和所属流 stream id(用于标识同步组)
支持 simulcast(多编码) 搭配 RTCRtpSender.setParameters() 支持多编码流发送

ontrack

作用

当远端添加了新的媒体轨(音轨/视频轨)时触发,一般用于播放远程视频或音频。

示例

javascript 复制代码
pc.ontrack = (event) => {
  remoteVideo.srcObject = event.streams[0];
};

特性

  • 可接收多个轨道;
  • 是 addTrack() 和 recvonly 类型媒体方向配合使用的标准回调。

发起端与接收端

发起端

js 复制代码
const pc = new RTCPeerConnection();

pc.onicecandidate = e => sendCandidate(e.candidate);
pc.ontrack = e => remoteVideo.srcObject = e.streams[0];

localStream.getTracks().forEach(track => {
  pc.addTrack(track, localStream);
});

const offer = await pc.createOffer();
await pc.setLocalDescription(offer);
sendOffer(offer);

接收端

js 复制代码
const pc = new RTCPeerConnection();

pc.onicecandidate = e => sendCandidate(e.candidate);
pc.ontrack = e => remoteVideo.srcObject = e.streams[0];

await pc.setRemoteDescription(offer);
localStream.getTracks().forEach(track => {
  pc.addTrack(track, localStream);
});
const answer = await pc.createAnswer();
await pc.setLocalDescription(answer);
sendAnswer(answer);

总结

  • 目的:确保双方选用兼容的音视频编解码器、分辨率、码率和传输参数,实现高质量的实时通信。
  • 载体:基于 SDP(Session Description Protocol)协议,描述媒体的相关能力。
  • 流程 :
    • 一方创建并发送包含自身支持能力的 SDP Offer
    • 对方收到后生成 SDP Answer,确认兼容参数
    • 双方通过设置本地和远端描述完成协商
  • 内容包括 :
    • 编解码器列表(如 VP8、H.264、Opus)
    • 媒体流方向(发送、接收、双向)
    • 带宽限制和媒体属性
    • 网络传输细节(ICE、DTLS安全层)
  • 结果:协商成功后,双方基于共同支持的参数建立安全稳定的音视频流通道。
相关推荐
草根大哥18 小时前
08-横向扩容与突发高并发:加机器到底买到了什么
运维·webrtc·srt·横向扩容·whip·自建cdn
草根大哥21 小时前
07-P2P 直连与节点池调度
webrtc·p2p·srt·whip·whep·自建cdn
草根大哥21 小时前
06-可观测性:端到端延迟、稳定性与画质
webrtc·srt·whip·自建cdn·ppcdn
大文说跨境21 小时前
用 Python 自动化检测浏览器环境指纹:WebRTC 泄露、Canvas 哈希与时区一致性校验
python·自动化·webrtc
EasyDSS2 天前
基于WebRTC的视频会议系统:私有化视频会议平台EasyDSS视频会议到底能干什么
音视频·webrtc·easydss
福大大架构师每日一题5 天前
RustDesk 1.5.0发布:WebRTC、HDR 色调映射、剪贴板同步与百余项修复全面汇总
webrtc
福大大架构师每日一题10 天前
webrtc-rs/webrtc v0.21.0发布:API 可扩展、异步集成、确定性时间、ICE 重启与 SCTP 全面修复
webrtc
智联视频超融合平台10 天前
WebRTC + AI:实时音视频中嵌入神经网络推理的架构设计
人工智能·webrtc·实时音视频
P1Browser11 天前
换了 IP 还是被关联:WebRTC 泄露本地 IP 的三种触发路径
网络协议·tcp/ip·webrtc
福大大架构师每日一题12 天前
pion/webrtc v4.2.22 最新发布:TCP mux 写缓冲区调整至 4 MB,补齐缺失媒体方向处理
tcp/ip·webrtc·媒体