2023-01-30 10:51:01 +00:00
|
|
|
<!-- go/cmark -->
|
|
|
|
|
<!--* freshness: {owner: 'sprang' reviewed: '2021-04-12'} *-->
|
2021-04-13 18:18:55 +02:00
|
|
|
|
|
|
|
|
# Paced Sending
|
|
|
|
|
|
2021-04-14 16:23:10 +02:00
|
|
|
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.
|
2021-04-13 18:18:55 +02:00
|
|
|
|
|
|
|
|
## Background
|
|
|
|
|
|
2021-04-14 16:23:10 +02:00
|
|
|
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.
|
|
|
|
|
|
|
|
|
|
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.
|
|
|
|
|
|
|
|
|
|
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.
|
2021-04-13 18:18:55 +02:00
|
|
|
|
|
|
|
|
## Life of a Packet
|
|
|
|
|
|
2021-04-14 16:23:10 +02:00
|
|
|
The typical path for media packets when using the paced sender looks something
|
|
|
|
|
like this:
|
|
|
|
|
|
|
|
|
|
1. `RTPSenderVideo` or `RTPSenderAudio` packetizes media into RTP packets.
|
|
|
|
|
2. The packets are sent to the [RTPSender] class for transmission.
|
|
|
|
|
3. The pacer is called via [RtpPacketSender] interface to enqueue the packet
|
|
|
|
|
batch.
|
|
|
|
|
4. The packets are put into a queue within the pacer awaiting opportune moments
|
|
|
|
|
to send them.
|
|
|
|
|
5. At a calculated time, the pacer calls the `PacingController::PacketSender()`
|
|
|
|
|
callback method, normally implemented by the [PacketRouter] class.
|
|
|
|
|
6. 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.
|
|
|
|
|
7. 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 -
|
2022-06-09 11:00:09 +02:00
|
|
|
and the target send rate is set on the `RtpPacketPacer` via the `void
|
2021-04-14 16:23:10 +02:00
|
|
|
SetPacingRates(DataRate pacing_rate, DataRate padding_rate)` method.
|
2021-04-13 18:18:55 +02:00
|
|
|
|
|
|
|
|
## Packet Prioritization
|
|
|
|
|
|
|
|
|
|
The pacer prioritized packets based on two criteria:
|
|
|
|
|
|
2021-04-14 16:23:10 +02:00
|
|
|
* Packet type, with most to least prioritized:
|
|
|
|
|
1. Audio
|
|
|
|
|
2. Retransmissions
|
|
|
|
|
3. Video and FEC
|
|
|
|
|
4. Padding
|
|
|
|
|
* Enqueue order
|
2021-04-13 18:18:55 +02:00
|
|
|
|
2021-04-14 16:23:10 +02:00
|
|
|
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.
|
2021-04-13 18:18:55 +02:00
|
|
|
|
|
|
|
|
## Implementations
|
|
|
|
|
|
2022-06-09 11:00:09 +02:00
|
|
|
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.
|
2021-04-13 18:18:55 +02:00
|
|
|
|
|
|
|
|
## The Packet Router
|
|
|
|
|
|
2021-04-14 16:23:10 +02:00
|
|
|
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:
|
2021-04-13 18:18:55 +02:00
|
|
|
|
2021-04-14 16:23:10 +02:00
|
|
|
* The `SendPacket` method looks up an RTP module with an SSRC corresponding to
|
|
|
|
|
the packet for further routing to the network.
|
|
|
|
|
* 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.
|
|
|
|
|
* Forwards REMB and/or TransportFeedback messages to suitable RTP modules.
|
2021-04-13 18:18:55 +02:00
|
|
|
|
2021-04-14 16:23:10 +02:00
|
|
|
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.
|
2021-04-13 18:18:55 +02:00
|
|
|
|
|
|
|
|
## The API
|
|
|
|
|
|
2021-04-14 16:23:10 +02:00
|
|
|
The section outlines the classes and methods relevant to a few different use
|
|
|
|
|
cases of the pacer.
|
2021-04-13 18:18:55 +02:00
|
|
|
|
|
|
|
|
### Packet sending
|
|
|
|
|
|
2021-04-14 16:23:10 +02:00
|
|
|
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.
|
2021-04-13 18:18:55 +02:00
|
|
|
|
|
|
|
|
### Send rates
|
|
|
|
|
|
2021-04-14 16:23:10 +02:00
|
|
|
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`.
|
2021-04-13 18:18:55 +02:00
|
|
|
|
2021-04-14 16:23:10 +02:00
|
|
|
In order to completely suspend/resume sending data (e.g. due to network
|
|
|
|
|
availability), use the `Pause()` and `Resume()` methods.
|
2021-04-13 18:18:55 +02:00
|
|
|
|
2021-04-14 16:23:10 +02:00
|
|
|
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.
|
2021-04-13 18:18:55 +02:00
|
|
|
|
|
|
|
|
### Bandwidth estimation
|
|
|
|
|
|
2021-04-14 16:23:10 +02:00
|
|
|
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
|
2022-05-16 19:58:40 +02:00
|
|
|
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.
|
2021-04-13 18:18:55 +02:00
|
|
|
|
2021-04-14 16:23:10 +02:00
|
|
|
If congestion window pushback is used, the state can be updated using
|
|
|
|
|
`SetCongestionWindow()` and `UpdateOutstandingData()`.
|
2021-04-13 18:18:55 +02:00
|
|
|
|
2021-04-14 16:23:10 +02:00
|
|
|
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.
|
2021-04-13 18:18:55 +02:00
|
|
|
|
|
|
|
|
### Stats
|
|
|
|
|
|
|
|
|
|
Several methods are used to gather statistics in pacer state:
|
|
|
|
|
|
2021-04-14 16:23:10 +02:00
|
|
|
* `OldestPacketWaitTime()` time since the oldest packet in the queue was
|
|
|
|
|
added.
|
|
|
|
|
* `QueueSizeData()` total bytes currently in the queue.
|
|
|
|
|
* `FirstSentPacketTime()` absolute time the first packet was sent.
|
|
|
|
|
* `ExpectedQueueTime()` total bytes in the queue divided by the send rate.
|
|
|
|
|
|
2021-07-22 15:40:44 +00:00
|
|
|
[RTPSender]: https://source.chromium.org/chromium/chromium/src/+/main:third_party/webrtc/modules/rtp_rtcp/source/rtp_sender.h;drc=77ee8542dd35d5143b5788ddf47fb7cdb96eb08e
|
2024-07-26 11:51:53 +00:00
|
|
|
[RtpPacketSender]: https://source.chromium.org/chromium/chromium/src/+/main:third_party/webrtc/api/rtp_packet_sender.h;drc=ea55b0872f14faab23a4e5dbcb6956369c8ed5dc
|
2021-07-22 15:40:44 +00:00
|
|
|
[RtpPacketPacer]: https://source.chromium.org/chromium/chromium/src/+/main:third_party/webrtc/modules/pacing/rtp_packet_pacer.h;drc=e7bc3a347760023dd4840cf6ebdd1e6c8592f4d7
|
|
|
|
|
[PacketRouter]: https://source.chromium.org/chromium/chromium/src/+/main:third_party/webrtc/modules/pacing/packet_router.h;drc=3d2210876e31d0bb5c7de88b27fd02ceb1f4e03e
|
|
|
|
|
[TaskQueuePacedSender]: https://source.chromium.org/chromium/chromium/src/+/main:third_party/webrtc/modules/pacing/task_queue_paced_sender.h;drc=5051693ada61bc7b78855c6fb3fa87a0394fa813
|
|
|
|
|
[RoundRobinPacketQueue]: https://source.chromium.org/chromium/chromium/src/+/main:third_party/webrtc/modules/pacing/round_robin_packet_queue.h;drc=b571ff48f8fe07678da5a854cd6c3f5dde02855f
|