WebRTC(七):媒体能力协商

发布于:2025-06-26 ⋅ 阅读:(24) ⋅ 点赞:(0)

目的

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

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

P2P的基本流程

在这里插入图片描述

参与角色

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

流程详解

连接信令服务器

  • peerApeerB 分别通过 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 被触发,候选地址通过信令发送给对方:
    • peerAsignalpeerB
    • peerBsignalpeerA
  • 收到对方候选后,使用 addIceCandidate() 添加。

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

媒体流建立

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

重点流程

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

重要函数

createOffer()

作用

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

使用示例

const offer = await pc.createOffer();

应用场景

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

createAnswer()

作用

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

使用示例

const answer = await pc.createAnswer();

应用场景

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

setLocalDescription(desc)

作用

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

使用示例

await pc.setLocalDescription(offer); // offer 或 answer 都可以

说明

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

setRemoteDescription(desc)

作用

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

使用示例

await pc.setRemoteDescription(remoteOffer);

注意

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

onicecandidate

作用

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

示例

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

注意

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

addIceCandidate(candidate)

作用

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

示例

await pc.addIceCandidate(remoteCandidate);

注意

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

addTrack

作用

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

示例

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

作用

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

示例

pc.ontrack = (event) => {
  remoteVideo.srcObject = event.streams[0];
};

特性

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

发起端与接收端

发起端

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);

接收端

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安全层)
  • 结果:协商成功后,双方基于共同支持的参数建立安全稳定的音视频流通道。

网站公告

今日签到

点亮在社区的每一天
去签到