实时传输协议 (RTP) 简介
**实时传输协议**(**RTP**),在 RFC 3550 中定义,是一种 IETF 标准协议,用于启用实时连接以交换需要实时优先级的数据。本文概述了 RTP 的功能以及它在 WebRTC 上下文中的工作原理。
注意:WebRTC 实际上使用 **SRTP**(安全实时传输协议)来确保交换的数据是安全且经过身份验证的。
将延迟降至最低对于 WebRTC 尤其重要,因为面对面通信需要以尽可能少的 延迟 执行。一个用户说某些话与另一个用户听到它之间的时间延迟越大,发生交叉对话和其他形式的混淆的可能性就越大。
RTP 的主要功能
在检查 RTP 在 WebRTC 上下文中的使用之前,了解 RTP 的功能和局限性很有用。RTP 是一种数据传输协议,其任务是在当前条件下尽可能高效地在两个端点之间移动数据。这些条件可能会受到网络协议栈底层、物理网络连接、中间网络、远程端点的性能、噪音水平、流量水平等等的影响。
由于 RTP 是一种数据传输,因此它由密切相关的 **RTP 控制协议**(**RTCP**)增强,该协议在 RFC 3550,第 6 节 中定义。RTCP 添加了包括 **服务质量**(**QoS**)监控、参与者信息共享等功能。它不足以完全管理用户、成员资格、权限等,但提供了无限制多用户通信会话所需的基础。
RTCP 与 RTP 在同一 RFC 中定义这一事实暗示了这两个协议之间紧密的相互关系。
RTP 的功能
RTP 在 WebRTC 方面的主要优势包括
- 通常延迟较低。
- 数据包被编号并加时间戳,以便在乱序到达时重新组装。这使得使用 RTP 发送的数据可以在不保证排序甚至不保证交付的传输上交付。
- 这意味着 RTP 可以(但不是必须)在 UDP 之上使用,以利用其性能以及多路复用和校验和功能。
- RTP 支持组播;虽然这对于 WebRTC 来说目前并不重要,但将来可能会很重要,届时 WebRTC(希望)将增强以支持多用户对话。
- RTP 不限于在视听通信中使用。它可用于任何形式的连续或活动数据传输,包括数据流、活动徽章或状态显示更新,或控制和测量信息传输。
RTP 不做的事情
RTP 本身并没有提供所有可能的功能,这就是 WebRTC 也使用其他协议的原因。RTP 不包括的一些更值得注意的事情
- RTP **不**保证 **服务质量**(**QoS**)。
- 虽然 RTP 旨在用于延迟敏感的场景,但它本身不提供任何确保 QoS 的功能。相反,它仅提供允许在协议栈的其他位置实现 QoS 所需的信息。
- RTP 不处理可能需要的资源的分配或预留。
在 WebRTC 目的方面,这些内容在 WebRTC 基础设施中的各个位置处理。例如,RTCP 处理 QoS 监控。
RTCPeerConnection 和 RTP
每个 RTCPeerConnection
都有方法可以访问服务对等连接的 RTP 传输列表。这些对应于 RTCPeerConnection
支持的以下三种类型的传输
RTCRtpSender
-
RTCRtpSender
处理将MediaStreamTrack
数据编码和传输到远程对等方。可以通过调用RTCPeerConnection.getSenders()
获取给定连接的发件人。 RTCRtpReceiver
-
RTCRtpReceiver
提供检查和获取有关传入MediaStreamTrack
数据的信息的能力。可以通过调用RTCPeerConnection.getReceivers()
获取连接的接收器。 RTCRtpTransceiver
-
RTCRtpTransceiver
是一对一个 RTP 发件人和一个 RTP 接收器,它们共享一个 SDPmid
属性,这意味着它们共享相同的 SDP 媒体 m 行(表示双向 SRTP 流)。这些由RTCPeerConnection.getTransceivers()
方法返回,并且每个mid
和 transceiver 之间存在一对一关系,其中mid
对于每个RTCPeerConnection
都是唯一的。
利用 RTP 实现“保持”功能
因为 RTCPeerConnection
的流是使用 RTP 和 上面 的接口实现的,所以您可以利用由此提供的对流内部的访问权限进行调整。您可以执行的最简单的事情之一是实现“保持”功能,其中通话参与者可以点击一个按钮并关闭其麦克风,开始向另一方发送音乐,并停止接受传入的音频。
在下面的示例中,我们将参考打开和关闭“保持”模式的对等方为本地对等方,并将被置于保持状态的用户称为远程对等方。
激活保持模式
本地对等方
当本地用户决定启用保持模式时,将调用下面的 enableHold()
方法。它接受包含在通话保持期间播放的音频的 MediaStream
作为输入。
async function enableHold(audioStream) {
try {
await audioTransceiver.sender.replaceTrack(audioStream.getAudioTracks()[0]);
audioTransceiver.receiver.track.enabled = false;
audioTransceiver.direction = "sendonly";
} catch (err) {
/* handle the error */
}
}
在 try
块中的三行代码执行以下步骤
- 用包含保持音乐的
MediaStreamTrack
替换其传出的音频轨道。 - 禁用传入的音频轨道。
- 将音频转发器切换到仅发送模式。
这会触发 RTCPeerConnection
的重新协商,方法是向其发送 negotiationneeded
事件,您的代码会响应该事件,使用 RTCPeerConnection.createOffer
生成 SDP 提供,并通过信令服务器将其发送到远程对等方。
audioStream
包含要播放的音频,用于替换本地对等方的麦克风音频,其来源可以是任何地方。一种可能性是使用一个隐藏的<audio>
元素,并使用HTMLAudioElement.captureStream()
获取其音频流。
远程对等方
在远程对等方上,当我们收到方向设置为"sendonly"
的SDP offer时,我们会使用holdRequested()
方法处理它,该方法接受SDP offer字符串作为输入。
async function holdRequested(offer) {
try {
await peerConnection.setRemoteDescription(offer);
await audioTransceiver.sender.replaceTrack(null);
audioTransceiver.direction = "recvonly";
await sendAnswer();
} catch (err) {
/* handle the error */
}
}
这里采取的步骤是
- 通过调用
RTCPeerConnection.setRemoteDescription()
将远程描述设置为指定的offer
。 - 用
null
替换音频转发器的RTCRtpSender
的轨道,表示没有轨道。这停止了在转发器上发送音频。 - 将音频转发器的
direction
属性设置为"recvonly"
,指示转发器只接收音频而不发送任何音频。 - SDP answer是使用名为
sendAnswer()
的方法生成并发送的,该方法使用createAnswer()
生成answer,然后通过信令服务将生成的SDP发送到另一个对等方。
停用保持模式
本地对等方
当本地用户点击界面小部件以禁用保持模式时,会调用disableHold()
方法来开始恢复正常功能的过程。
async function disableHold(micStream) {
await audioTransceiver.sender.replaceTrack(micStream.getAudioTracks()[0]);
audioTransceiver.receiver.track.enabled = true;
audioTransceiver.direction = "sendrecv";
}
这会反转enableHold()
中采取的步骤,如下所示
- 音频转发器的
RTCRtpSender
的轨道被替换为指定流的第一个音频轨道。 - 重新启用转发器的传入音频轨道。
- 将音频转发器的方向设置为
"sendrecv"
,表示它应该返回到同时发送和接收流式音频,而不是仅发送。
就像启用保持模式时一样,这会再次触发协商,导致你的代码向远程对等方发送新的offer。
远程对等方
当远程对等方收到"sendrecv"
offer时,它会调用其holdEnded()
方法
async function holdEnded(offer, micStream) {
try {
await peerConnection.setRemoteDescription(offer);
await audioTransceiver.sender.replaceTrack(micStream.getAudioTracks()[0]);
audioTransceiver.direction = "sendrecv";
await sendAnswer();
} catch (err) {
/* handle the error */
}
}
这里在try
块中采取的步骤是
- 通过调用
setRemoteDescription()
将接收到的offer存储为远程描述。 - 音频转发器的
RTCRtpSender
的replaceTrack()
方法用于将输出音频轨道设置为麦克风音频流的第一个轨道。 - 将转发器的方向设置为
"sendrecv"
,表示它应该恢复发送和接收音频。
从这一点开始,麦克风重新启用,远程用户能够再次听到本地用户,并与他们交谈。