P2P Live Streaming: Relay Machinery for One-to-Many Real-Time Delivery
In a live stream, one broadcaster's video is watched by many viewers, all of whom want the same latest few seconds. Send it to everyone directly and the first thing to collapse is the broadcaster's uplink. This page walks through the relay machinery that overcomes that constraint: distribution trees and cascading, leader election as a control plane, PLI and keyframe control, and protocol bridging beyond WebRTC, with practical lessons learned from real implementations.
Why live streaming is hard
Live streaming is one-to-many, real-time delivery: a single sender and many viewers, all wanting the same latest few seconds, not just any correct copy of the data eventually. That framing already rules out the techniques that make other kinds of P2P distribution efficient.
A BitTorrent-style swarm is superb for VOD: a finished file that never changes, where peers can freely trade whichever pieces they happen to be missing. Live video breaks that assumption: everyone wants the same newest pieces at the same time, so the piece-exchange diversity a swarm depends on never gets the chance to develop. And naively sending WebRTC directly to every viewer means the broadcaster's own machine uploads n copies of the stream, so the broadcaster's uplink saturates linearly with audience size. This is the one-to-many cousin of WebRTC's full-mesh O(n²) problem: instead of every peer's uplink scaling with the group, one peer's uplink scales with the whole audience.
Distribution trees and cascading: keeping the uplink at one
The basic fix is a tree, or cascade, of relay nodes. Exactly one relay, the root, receives the stream directly from the broadcaster and redistributes it to other relays and viewers without re-encoding; every other relay in the tree receives its copy from the root or from another relay further up the tree, never from the broadcaster itself. However large the audience grows, the broadcaster's own uplink stays fixed at a single connection.
Large-scale conferencing infrastructure applies exactly the same idea under the name SFU cascading, chaining multiple SFUs together so that a single SFU's forwarding capacity is no longer the ceiling on room size.
A useful side effect shows up in P2P overlays that are only selectively connected rather than fully meshed: root-based redistribution still reaches relays that have no direct connection to the broadcaster at all, since they only ever need a path to some upstream relay, not to the source.
| Aspect | Direct fan-out (1:n) | Full mesh | Cascade/tree |
|---|---|---|---|
| Broadcaster uplink | Grows linearly with viewer count | Grows linearly with viewer count (plus every viewer meshes with every other) | Fixed at one connection regardless of audience size |
| Per-viewer load | Minimal: one download each | High: each viewer both uploads and downloads to every peer | Moderate: each relay forwards to only its own children |
| Impact of root/relay failure | N/A (no shared relay) | No single point of failure, but scales terribly | A relay's subtree loses its feed until it reconnects upstream |
| Suitable scale | A handful of viewers at most | Essentially never, for streaming | Large audiences, bounded by tree depth and fan-out |
Agreeing only on "who is the root": consensus as a control plane
Something still has to decide which relay gets to be the root. The natural tool is leader election, most commonly Raft (see distributed consensus), run among the pool of relay nodes. In practice a deliberate simplification works well here: use only Raft's leader election, not its log replication, since there is no ongoing log of application state to keep consistent: the elected leader's identity is the entire piece of agreed-upon state the system needs.
That simplification points at a broader governing principle: consensus costs a round trip per commit, so never route low-latency media through it. Consensus belongs on the control plane, deciding who redistributes, and the data plane (the actual audio and video) needs to stay entirely outside it.
A practical pitfall worth knowing in advance: Raft's textbook timings (an election timeout of roughly 150–300 ms, heartbeats around every 50 ms) assume a well-behaved datacenter LAN. Run those same numbers over jittery P2P/WebRTC paths and the algorithm reads an ordinary delayed heartbeat as "the leader is dead," triggering spurious re-elections. Election timeouts need to be relaxed to the order of seconds instead. Tune consensus timeouts to the network's actual latency profile, not the defaults.
If the leader disappears, the remaining relays elect a new one and every relay re-locks its upstream to the new leader's redistribution automatically, with no manual intervention required.
Keyframes and feedback: PLI and NACK
- Keyframe (IDR)
- An independently decodable video frame. Most frames a codec sends are deltas encoded against the previous frame, so a decoder that joins mid-stream, or loses a frame, is stuck until the next keyframe arrives. Keyframes are large, so encoders emit them mostly on request rather than on a fixed schedule.
- PLI (Picture Loss Indication)
- An RTCP feedback message a receiver sends upstream asking the encoder to produce a fresh keyframe as soon as possible.
- NACK
- An RTCP feedback message requesting retransmission of a specific missing packet, identified by sequence number.
Without NACK, the degradation path is blunt: a lost packet opens a gap in the RTP sequence number, that gap makes the containing frame impossible to reconstruct, and the only way to avoid feeding a corrupted frame into the decoding chain is to discard everything until the next keyframe arrives. Put plainly: without retransmission, a single lost packet stops being a packet-level problem and becomes a frame-level, seconds-long freeze.
The practical pattern that keeps this in check has three parts: periodic PLI (every few seconds, so a late-joining viewer is guaranteed a keyframe within one period), immediate loss-triggered PLI (waiting for the next scheduled period after a loss means a multi-second freeze), and debouncing (a single burst of loss opens many consecutive sequence-number gaps at once, and without debouncing that turns into a storm of redundant PLIs).
The tree-distribution trap is easy to miss: a leaf viewer's PLI targets its immediate upstream, the relay redistributing the root's track, not the original broadcaster. Unless the tree forwards that feedback back up toward the source, only the root's own PLI ever reaches the encoder, and every leaf's effective keyframe wait is bounded by the root's PLI period, not its own. The upstream feedback path deserves as much design attention as the downstream data path.
Protocol bridging: reaching beyond WebRTC
Not every viewer can speak WebRTC. Reaching players that can only play a URL, such as embedded devices or the video players built into games and metaverse platforms, requires a relay that translates WebRTC into RTSP, HLS, or whatever the target expects.
The guiding principle is depacketize, then repacketize, not re-encode. H264 video, for instance, is reassembled from its RTP payloads back into access units and re-wrapped for the target protocol's container, with the compressed bitstream itself left untouched. Transcoding is reserved for the parts the target genuinely cannot play, such as WebRTC's standard Opus audio into AAC, rather than applied wholesale. Minimizing what actually gets converted wins on both latency and quality.
One notable deployment shape: each viewer runs the bridge on their own machine and plays a loopback URL such as `rtsp://127.0.0.1/...` locally. Every viewer already receives the stream over the P2P swarm, so no dedicated streaming server, public IP address, or port forwarding is needed anywhere in the chain: the bridge and the player it feeds both live entirely inside the viewer's own machine.
A quietly important lesson from running bridges like this in practice: many players interpret a moment of data silence as a disconnection and tear the whole session down. While no real media data is available, during a brief stall for instance, the bridge needs to keep the session alive by sending decoder-plausible dummy data, such as repeated parameter sets, rather than sending nothing at all.
Lip sync: reconciling separate clocks
Video and audio travel as two entirely separate RTP streams, each in its own timestamp space: a 90 kHz clock for video, 48 kHz for audio being typical. Nothing about those numbers alone tells a receiver how to line the two up. That alignment comes from RTCP Sender Reports (SR): periodic messages mapping "this stream's RTP time corresponds to this wall-clock (NTP) time." Skip sending SRs and the player loses its sync reference entirely, and audio and video drift apart with no way to correct it. Established media libraries handle this implicitly, which is exactly what makes it the easiest thing to forget in a hand-rolled implementation.
A second practicality sits alongside it: a source's RTP timestamps can jump unexpectedly, whether from a stream switch or a burst of loss, and a relay passing media through needs to inspect the deltas between consecutive timestamps and replace anomalous jumps with a nominal step (20 ms for a typical audio frame, say), keeping the output timestamp sequence monotonic even when the input wasn't. This kind of timestamp rebasing is invisible when it works and immediately audible as a stutter or drift when it doesn't.
Where latency actually comes from
The relay layer itself contributes surprisingly little: roughly one frame's worth of delay for access-unit assembly, and about 20 ms for audio reframing. The dominant contributors sit at the two ends of the pipeline instead: the encoder's lookahead buffer on the way in, and the player's jitter and decode buffers on the way out.
Any serious discussion of where latency is going should start by separating the part a relay or transport choice can actually optimize from the part it structurally cannot.
For context: traditional RTMP-based pipelines commonly run from several seconds to tens of seconds of end-to-end latency; WebRTC-based delivery brings that down to sub-second. The ingest side of that shift is increasingly standardized around WHIP/WHEP, covered in the signaling section of WebRTC.
Related pages
P2P live streaming sits at the intersection of several other pages on this site. The scalability limits it works around are the same O(n²) full-mesh wall covered in WebRTC, and cascading relay trees are the streaming-specific answer to the SFU/MCU topology problem discussed there. Electing the tree's root is a direct application of distributed consensus, deliberately narrowed to leader election alone. The piece-diversity assumptions that make BitTorrent swarms so effective for VOD are exactly what live delivery lacks, which is why trees rather than swarms dominate here; membership and liveness information among relay nodes, by contrast, is a natural fit for a Gossip protocol. And the same tree-distribution and interest-management ideas resurface, at a different scale and with position data instead of media, in P2P and the metaverse.