本文以一个"老师直播授课、学生观看"的场景为例,梳理整个一对多 WebRTC 直播的实现流程,包括:
- 老师端媒体流发布
- 学生端媒体流接收
- 双端 PeerConnection 的建连流程
- addTransceiver 的作用与设计
本文的 demo 实现结构如下图所示:

核心需求
在这个场景中:
- 老师端发布自己的音视频流(推流)
- 学生端只接收老师的音视频流(拉流),不发布自己的。
实现要点
1. 角色区分
直播中所有人都加入同一个"房间",但角色不同:
- 老师端: 推流方,与每个学生建立 P2P,维护多个 PeerConnection,分别对应每个学生
- 学生: 只拉流不推流,仅维护一个 PeerConnection(连接到老师)

2. WebRTC 媒体流发布策略
老师端会获取本机摄像头/麦克风,并向每一个学生发送媒体流。
javascript
// 老师端
const pc = new RTCPeerConnection()
// 获取本地媒体流
localStream = await navigator.mediaDevices.getUserMedia({
video: true,
audio: true
})
// 添加轨道
localStream.getTracks().forEach(track => {
pc.addTrack(track, localStream!)
})
学生端不需要摄像头,也不发布任何多媒体,它只需要收老师的流。
php
// 学生端
const pc = new RTCPeerConnection()
pc.addTransceiver('audio', {direction: 'recvonly'})
pc.addTransceiver('video', {direction: 'recvonly'})
addTransceiver 解析
addTransceiver(kind, options) 它能让开发者在协商开始之前就明确媒体轨道的方向、数量和能力 。相比 addTrack() 或仅靠收 offer/answer 再决定行为,addTransceiver() 的控制力是最强的。
| 分类 | 字段名 | 类型 | 默认值 | 说明 |
|---|---|---|---|---|
| 基本 | kind |
"audio" / "video" |
无 | 媒体类型 |
| 方向控制 | direction |
string | "sendrecv" |
控制发送/接收方向 |
| Track 关联 | streams |
MediaStream[] | [] |
将 track 添加到哪些 stream |
| 编码控制 | sendEncodings |
RTCRtpEncodingParameters[] | [] |
控制码率/分辨率/Simulcast |
| (外部设置) | codecPreferences |
RTCRtpCodecCapability[] | 无 | 通过 transceiver.setCodecPreferences 设置 |
针对 direction 它提供四个选项:
| direction | 描述 | 使用场景 |
|---|---|---|
| sendonly | 只发送 | 推流、直播源 |
| recvonly | 只接收 | 观众、拉流端 |
| sendrecv | 双向发送接收 | 视频会议模式 |
| inactive | 暂不发送也不接收 | 预留媒体线路 |
回顾 P2P 实现流程

总结直播实现流程
- 学生端加入之后查询一次房间内所有人,找到老师后向其发起 P2P 连接;
- 老师端则做为被叫等待学生端的呼叫,从而和学生建立连接。
- 学生端 -> 发现老师 -> 发起呼叫
- 老师端 -> 收到 Offer -> 回复 Answer
- 双端交换 ICE -> 学生端接收轨道 -> 展示画面
最终形成:
- 老师端推流(addTrack)
- 学生端拉流(recvonly)
- 多个学生 = 老师端维护 N 个 RTCPeerConnection
- 每个学生只有 1 个 PeerConnection(只连老师)