收发器(Transceiver)机制:RTCRtpTransceiver 概念、addTrack 与 addTransceiver 的区别、方向属性
聊到 WebRTC 的媒体协商,有个概念你迟早得正面硬刚——收发器(Transceiver)。说实话,我早年刚接触 WebRTC 时,总觉得这玩意儿有点抽象。后来在做一个多人视频会议项目时,被一个诡异的“音频没声音、视频黑屏”的 bug 折磨了两天,最后发现是收发器方向属性配错了。嗯,从那以后,我再也不敢小看它了。
今天我们就来把 RTCRtpTransceiver 彻底讲透。包括它是什么、怎么用、以及那几个让人容易混淆的方向属性。
1. 什么是 RTCRtpTransceiver?
简单说,一个收发器就是一条“双向车道”。它同时管理一个发送器(RTCRtpSender)和一个接收器(RTCRtpReceiver)。
- 发送器:负责把本地音视频数据编码后发出去。
- 接收器:负责接收远端的音视频数据并解码。
收发器把这两个东西绑定在一起,统一管理。你想想看,每次媒体协商时,SDP 里每一行 m= 描述,背后其实就对应着一个收发器。
核心要点:一个 RTCRtpTransceiver 对应 SDP 中的一个媒体行(m-line)。它决定了这条媒体流是发、是收、还是双向。
我个人习惯把收发器理解成“一个媒体通道的控制器”。你不需要分别操作 sender 和 receiver,直接操作 transceiver 就够了。
2. addTrack 与 addTransceiver 的区别
这是面试常考题,也是实际开发中容易踩坑的地方。我们直接看对比。
| 特性 | addTrack() |
addTransceiver() |
|---|---|---|
| 返回值 | RTCRtpSender |
RTCRtpTransceiver |
| 是否自动创建接收器 | 是(隐式创建) | 是(显式创建) |
| 能否指定方向 | 不能(默认 sendrecv) |
能(可指定方向) |
| 能否设置编码参数 | 不能 | 能(如码率、分辨率等) |
| 适用场景 | 简单的一对一通话 | 复杂的多人会议、SFU 场景 |
addTrack 的用法:
const sender = pc.addTrack(track, stream);
// 返回的是 sender,但内部自动创建了一个 transceiver
// 你无法直接控制这个 transceiver 的方向
addTransceiver 的用法:
const transceiver = pc.addTransceiver('video', {
direction: 'sendonly',
streams: [stream],
sendEncodings: [
{ maxBitrate: 500000 },
{ maxBitrate: 200000, scaleResolutionDownBy: 2 }
]
});
// 返回的是 transceiver,你可以直接控制方向、编码参数等
我在项目中遇到过一个问题:用 addTrack 添加了音频轨,结果对方一直收不到。排查了半天,发现是因为 addTrack 默认创建的是 sendrecv 方向,但我的场景只需要单向发送。换成 addTransceiver 并指定 sendonly 就解决了。
我的建议:除非你写 demo,否则优先用 addTransceiver。它给你更多控制权,后期调试也方便。
3. 方向属性:sendrecv / sendonly / recvonly / inactive
这四个方向属性,说白了就是告诉对方:“我这边的收发器,打算怎么用这条媒体通道。”
sendrecv:双向。我既能发也能收。这是默认值。sendonly:只发不收。比如你只推流,不看对方的画面。recvonly:只收不发。比如你只看别人的画面,自己不推流。inactive:既不收也不发。相当于暂时禁用这条通道。
你可能会问:“方向属性到底在哪起作用?”
答案是:在 SDP 协商时。每个 m= 行里都有一个 a=sendrecv 或类似的属性行。双方交换 SDP 后,会根据各自的方向属性做“与运算”,得出最终的有效方向。
方向协商规则:
- 本地
sendonly+ 远端recvonly= 单向发送成功 - 本地
sendrecv+ 远端sendrecv= 双向通信 - 本地
sendonly+ 远端sendonly= 两边都想发,没人收,凉凉
我曾经在调试一个 SFU 转发服务时,发现某路视频流始终无法显示。查了 SDP 才发现,SFU 那边设置的是 recvonly,但客户端这边也是 recvonly——两边都只想收,没人发,当然没画面。改了一端为 sendonly 后,问题解决。
避坑指南:
- 不要滥用
inactive。我曾经用它来“暂停”一条流,结果重新激活时忘了改方向,导致后续协商一直失败。 - 多人场景下,建议用
addTransceiver并显式指定方向,避免默认sendrecv带来的带宽浪费。
4. 知识体系图:收发器核心逻辑
下面这张图帮你理清收发器、发送器、接收器以及方向属性的关系。
从图中你可以看到:收发器是顶层控制器,它握着方向属性的开关,同时指挥着发送器和接收器干活。方向属性决定了这两个“小弟”谁上场、谁休息。
5. 实际开发中的选择建议
说了这么多,到底什么时候用 addTrack,什么时候用 addTransceiver?
- 简单的一对一通话:用
addTrack就够了,代码简洁。 - 多人会议、SFU 场景:必须用
addTransceiver。你需要精确控制每条流的方向和编码参数。 - 需要动态切换方向:比如从“只看画面”切换到“我也要发言”,用
transceiver.direction属性直接改,比重新 addTrack 方便得多。
小技巧:调试时,打印 pc.getTransceivers() 看看所有收发器的状态。我经常用这招排查“为什么对方收不到我的流”之类的问题。
好了,关于收发器机制,今天就聊到这。记住一句话:收发器是 WebRTC 媒体协商的基石。搞懂它,后面学 SDP 协商、Simulcast、SVC 都会轻松很多。