Paced Sending
带节奏的发送
The paced sender, often referred to as just the “pacer”, is a part of the WebRTC RTP stack used primarily to smooth the flow of packets sent onto the network.
带节奏的发送器,通常被称为“起搏器”,是WebRTC RTP堆栈的一部分,主要用于平滑发送到网络上的数据包流。
Background
背景
Consider a video stream at 5Mbps and 30fps. This would in an ideal world result in each frame being ~21kB large and packetized into 18 RTP packets. While the average bitrate over say a one second sliding window would be a correct 5Mbps, on a shorter time scale it can be seen as a burst of 167Mbps every 33ms, each followed by a 32ms silent period. Further, it is quite common that video encoders overshoot the target frame size in case of sudden movement especially dealing with screensharing. Frames being 10x or even 100x larger than the ideal size is an all too real scenario. These packet bursts can cause several issues, such as congesting networks and causing buffer bloat or even packet loss. Most sessions have more than one media stream, e.g. a video and an audio track. If you put a frame on the wire in one go, and those packets take 100ms to reach the other side - that means you have now blocked any audio packets from reaching the remote end in time as well.
考虑5Mbps和30fps的视频流。在理想情况下,这将导致每个帧约21kB大,并被打包为18个RTP分组。虽然一秒滑动窗口的平均比特率是正确的5Mbps,但在较短的时间尺度上,它可以被视为每33ms爆发167Mbps,然后是32ms的静默期。此外,在突然移动的情况下,视频编码器超调目标帧大小是很常见的,尤其是在处理屏幕共享时。比理想尺寸大10倍甚至100倍的相框是一个非常真实的场景。这些数据包突发可能会导致一些问题,例如网络拥塞,导致缓冲区膨胀,甚至数据包丢失。大多数会话具有多个媒体流,例如视频和音轨。如果你一次在线路上放一个帧,这些数据包需要100毫秒才能到达另一端,这意味着你现在也阻止了任何音频数据包及时到达远端。
The paced sender solves this by having a buffer in which media is queued, and then using a leaky bucket algorithm to pace them onto the network. The buffer contains separate fifo streams for all media tracks so that e.g. audio can be prioritized over video - and equal prio streams can be sent in a round-robin fashion to avoid any one stream blocking others.
有带节奏的发送器通过有一个缓冲区来解决这个问题,媒体在缓冲区中排队,然后使用漏桶算法将它们按节奏放到网络上。缓冲区包含用于所有媒体轨道的独立fifo流,以便例如音频可以优先于视频,并且可以以循环方式发送相等的prio流,以避免任何一个流阻塞其他流。
Since the pacer is in control of the bitrate sent on the wire, it is also used to generate padding in cases where a minimum send rate is required - and to generate packet trains if bitrate probing is used.
由于节奏器控制着线路上发送的比特率,因此在需要最小发送速率的情况下,它还用于生成填充,如果使用比特率探测,则用于生成分组串。
Life of a Packet
数据包的寿命
The typical path for media packets when using the paced sender looks something like this:
使用带节奏的发送器时,媒体数据包的典型路径如下所示:
RTPSenderVideo
orRTPSenderAudio
packetizes media into RTP packets.RTPSenderVideo或
RTPSenderAudio
将媒体分组为RTP分组。- The packets are sent to the RTPSender class for transmission.
- 数据包被发送到RTPSender类进行传输。
- The pacer is called via RtpPacketSender interface to enqueue the packet batch.
- 通过RtpPacketSender接口调用起搏器,以将数据包批次排入队列。
- The packets are put into a queue within the pacer awaiting opportune moments to send them.
- 数据包被放入起搏器内的队列中,等待合适的时机发送。
- At a calculated time, the pacer calls the
PacingController::PacketSender()
callback method, normally implemented by the PacketRouter class. - 在计算的时间,起搏器调用PacingController::PacketSender()回调方法,该方法通常由PacketRouter类实现。
- The router forwards the packet to the correct RTP module based on the packet's SSRC, and in which the
RTPSenderEgress
class makes final time stamping, potentially records it for retransmissions etc. - 路由器根据数据包的SSRC将数据包转发到正确的RTP模块,RTPSenderEgress类在其中进行最后的时间戳,可能会记录它以供重新传输等。
- The packet is sent to the low-level
Transport
interface, after which it is now out of scope. - 数据包被发送到低层传输接口,之后它就超出了作用域。
Asynchronously to this, the estimated available send bandwidth is determined - and the target send rate is set on the RtpPacketPacer
via the void SetPacingRates(DataRate pacing_rate, DataRate padding_rate)
method.
与此同步,确定估计的可用发送带宽,并通过void SetPacingRates(DataRate pacing_rate,DataRate padding_rate)方法在RtpPacketPacer上设置目标发送速率。
Packet Prioritization
数据包优先级
The pacer prioritized packets based on two criteria:
起搏器根据两个标准对数据包进行优先级排序:
- Packet type, with most to least prioritized:
- 数据包类型,优先级从高到低:
- Audio
- 音频
- Retransmissions
- 重新发送
- Video and FEC
- 视频和前向纠错
- Padding
- 填充
- Enqueue order
- 排队顺序
The enqueue order is enforced on a per stream (SSRC) basis. Given equal priority, the RoundRobinPacketQueue alternates between media streams to ensure no stream needlessly blocks others.
排队顺序是在每个流(SSRC)的基础上强制执行的。在同等优先级的情况下,RoundRobinPacketQueue在媒体流之间交替,以确保没有流不必要地阻塞其他流。
Implementations
实现
The main class to use is called TaskQueuePacedSender. It uses a task queue to manage thread safety and schedule delayed tasks, but delegates most of the actual work to the PacingController
class. This way, it's possible to develop a custom pacer with different scheduling mechanism - but ratain the same pacing logic.
要使用的主类称为TaskQueuePacedSender。它使用任务队列来管理线程安全和调度延迟的任务,但将大部分实际工作委托给PacingController类。通过这种方式,可以开发具有不同调度机制的自定义起搏器,但需要相同的起搏逻辑。
The Packet Router
数据包路由器
An adjacent component called PacketRouter is used to route packets coming out of the pacer and into the correct RTP module. It has the following functions:
一个称为PacketRouter的相邻组件用于将来自起搏器的数据包路由到正确的RTP模块。它具有以下功能:
- The
SendPacket
method looks up an RTP module with an SSRC corresponding to the packet for further routing to the network. - SendPacket方法查找具有与分组相对应的SSRC的RTP模块,以便进一步路由到网络。
- If send-side bandwidth estimation is used, it populates the transport-wide sequence number extension.
- 如果使用发送端带宽估计,则它填充传输范围的序列号扩展。
- Generate padding. Modules supporting payload-based padding are prioritized, with the last module to have sent media always being the first choice.
- 生成填充。支持基于有效载荷的填充的模块被优先考虑,最后一个发送媒体的模块始终是第一选择。
- Returns any generated FEC after having sent media.
- 在发送媒体后返回任何生成的FEC。
- Forwards REMB and/or TransportFeedback messages to suitable RTP modules.
- 将REMB或TransportFeedback消息转发到合适的RTP模块。
At present the FEC is generated on a per SSRC basis, so is always returned from an RTP module after sending media. Hopefully one day we will support covering multiple streams with a single FlexFEC stream - and the packet router is the likely place for that FEC generator to live. It may even be used for FEC padding as an alternative to RTX.
目前,FEC是在每个SSRC的基础上生成的,因此总是在发送媒体之后从RTP模块返回。希望有一天我们能支持用一个FlexFEC流覆盖多个流,而分组路由器可能是FEC生成器的生存之地。它甚至可以用于FEC填充,作为RTX的替代方案。
The API
The section outlines the classes and methods relevant to a few different use cases of the pacer.
本节概述了与起搏器的几个不同用例相关的类和方法。
Packet sending
数据包发送
For sending packets, use RtpPacketSender::EnqueuePackets(std::vector<std::unique_ptr<RtpPacketToSend>> packets)
The pacer takes a PacingController::PacketSender
as constructor argument, this callback is used when it's time to actually send packets.
对于发送数据包,请使用RtpPacketSender::EnqueuePackets(std::vector<std::unique_ptr<RtpPacketToSend>> packets),
起搏器将PacingController::PacketSender作为构造函数参数,在实际发送数据包时使用此回调。
Send rates
发送速率
To control the send rate, use void SetPacingRates(DataRate pacing_rate, DataRate padding_rate)
If the packet queue becomes empty and the send rate drops below padding_rate
, the pacer will request padding packets from the PacketRouter
.
要控制发送速率,请使用void SetPacingRates(DataRate pacing_rate, DataRate padding_rate)
。如果数据包队列变空,发送速率降至padding_rate以下,则起搏器将从PacketRouter请求填充数据包。
In order to completely suspend/resume sending data (e.g. due to network availability), use the Pause()
and Resume()
methods.
为了完全暂停/恢复发送数据(例如,由于网络可用性),请使用Pause()和resume()方法。
The specified pacing rate may be overriden in some cases, e.g. due to extreme encoder overshoot. Use void SetQueueTimeLimit(TimeDelta limit)
to specify the longest time you want packets to spend waiting in the pacer queue (pausing excluded). The actual send rate may then be increased past the pacing_rate to try to make the average queue time less than that requested limit. The rationale for this is that if the send queue is say longer than three seconds, it's better to risk packet loss and then try to recover using a key-frame rather than cause severe delays.
在某些情况下,指定的起搏速率可能会被高估,例如,由于编码器过冲过大。使用void SetQueueTimeLimit(TimeDelta limit)指定数据包在起搏器队列中等待的最长时间(暂停除外)。然后可以将实际发送速率增加超过pacing_ rate,以试图使平均队列时间小于所请求的限制。这样做的理由是,如果发送队列超过三秒,最好冒着数据包丢失的风险,然后尝试使用关键帧进行恢复,而不是造成严重延迟。
Bandwidth estimation
带宽估计
If the bandwidth estimator supports bandwidth probing, it may request a cluster of packets to be sent at a specified rate in order to gauge if this causes increased delay/loss on the network. Use the void CreateProbeCluster(...)
method - packets sent via this PacketRouter
will be marked with the corresponding cluster_id in the attached PacedPacketInfo
struct.
如果带宽估计器支持带宽探测,则它可以请求以指定的速率发送分组簇,以便衡量这是否会导致网络上的延迟/损失增加。使用void CreateProbeCluster(…)方法-通过此PacketRouter发送的数据包将在附加的PacedPacketInfo结构中标记相应的cluster_id。
If congestion window pushback is used, the state can be updated using SetCongestionWindow()
and UpdateOutstandingData()
.
如果使用拥塞窗口推回,则可以使用SetCongressionWindow()和UpdateOutstandingData()更新状态。
A few more methods control how we pace: * SetAccountForAudioPackets()
determines if audio packets count into bandwidth consumed. * SetIncludeOverhead()
determines if the entire RTP packet size counts into bandwidth used (otherwise just media payload). * SetTransportOverhead()
sets an additional data size consumed per packet, representing e.g. UDP/IP headers.
还有一些方法控制我们的速度:*SetAccountForAudioPackets()确定音频数据包是否计入所消耗的带宽.*SetIncludeOverhead()确定整个RTP数据包大小是否计入所使用的带宽(否则仅为媒体有效载荷)*SetTransportOverhead()设置每个数据包消耗的额外数据大小,例如表示UDP/IP头。
Stats
状态
Several methods are used to gather statistics in pacer state:
有几种方法可用于收集起搏器状态下的统计信息:
OldestPacketWaitTime()
time since the oldest packet in the queue was added.- OldestPacketWaitTime() 自添加队列中最旧的数据包以来的时间。
QueueSizeData()
total bytes currently in the queue.- QueueSizeData() 队列中当前的总字节数。
FirstSentPacketTime()
absolute time the first packet was sent.- FirstSentPacketTime() 发送第一个数据包的绝对时间。
ExpectedQueueTime()
total bytes in the queue divided by the send rate.- ExpectedQueueTime() 队列中的总字节数除以发送速率。