rtc_interceptor/flexfec/draft03/receiver.rs
1//! Receive-side FlexFEC draft-03: recovers lost media and keeps repair packets off the wire.
2//!
3//! # No upstream counterpart
4//!
5//! `pion/interceptor` has a full draft-03 decoder that nothing constructs: `ConfigureFlexFEC03`
6//! registers only the encoder, and `newFECDecoder` appears outside its own tests nowhere. So this
7//! interceptor is new work, and the decisions it makes are ones upstream never had to:
8//!
9//! - **Repair packets are consumed, not forwarded.** They are not media; the application must
10//! never see them, and neither should the interceptors below, which would treat their sequence
11//! numbers as a media stream's and report gaps that do not exist.
12//! - **Recovered packets re-enter through `inner`.** A recovered packet has to look to every
13//! layer below exactly like one that arrived normally.
14//! - **Memory is bounded** by the decoder's own retention limits; a receive path that keeps every
15//! packet it has ever seen in case a repair packet turns up later is a leak.
16
17use super::decoder::FlexFec03Decoder;
18use crate::Interceptor;
19use crate::stream_info::StreamInfo;
20use crate::{Attribute, AttributedPacket, Packet, TaggedPacket};
21use sansio::Protocol;
22use shared::error::Error;
23use std::collections::{HashMap, VecDeque};
24use std::time::Instant;
25
26/// Builder for [`FlexFec03ReceiveInterceptor`].
27///
28/// # Example
29///
30/// ```
31/// use rtc_interceptor::{Slot, FlexFec03ReceiveBuilder, Registry};
32///
33/// let chain = Registry::new()
34/// .with(Slot::FecDecoder, FlexFec03ReceiveBuilder::new().build())
35/// .build();
36/// ```
37#[derive(Default)]
38pub struct FlexFec03ReceiveBuilder {}
39
40impl FlexFec03ReceiveBuilder {
41 /// Create a builder.
42 pub fn new() -> Self {
43 Self::default()
44 }
45
46 /// Build the interceptor.
47 pub fn build(self) -> FlexFec03ReceiveInterceptor {
48 FlexFec03ReceiveInterceptor::new()
49 }
50}
51
52/// Recovers media lost from streams protected by FlexFEC draft-03.
53///
54/// Belongs **early on the read path**, close to the wire: a recovered packet must be
55/// indistinguishable from one that arrived, so recovery has to happen before anything after it
56/// inspects sequence numbers — the NACK generator should not ask for a packet FEC is about to
57/// rebuild, and the jitter buffer should order the recovered packet along with the rest.
58pub struct FlexFec03ReceiveInterceptor {
59 /// Decoders keyed by the media SSRC they protect.
60 decoders: HashMap<u32, FlexFec03Decoder>,
61 /// Repair SSRC to the media SSRC it repairs, so a repair packet finds its decoder.
62 repair_to_media: HashMap<u32, u32>,
63 /// Inbound packets ready for the next interceptor: what passed through, plus anything the
64 /// decoder reconstructed.
65 read_queue: VecDeque<TaggedPacket>,
66 /// Outbound packets ready for the next interceptor: what passed through, plus
67 /// anything this one generated.
68 write_queue: VecDeque<TaggedPacket>,
69}
70
71impl FlexFec03ReceiveInterceptor {
72 fn new() -> Self {
73 Self {
74 read_queue: VecDeque::new(),
75 write_queue: VecDeque::new(),
76 decoders: HashMap::new(),
77 repair_to_media: HashMap::new(),
78 }
79 }
80
81 /// The media SSRCs currently protected.
82 pub fn protected_streams(&self) -> impl Iterator<Item = u32> + '_ {
83 self.decoders.keys().copied()
84 }
85}
86
87impl Protocol<TaggedPacket, TaggedPacket, ()> for FlexFec03ReceiveInterceptor {
88 type Rout = TaggedPacket;
89 type Wout = TaggedPacket;
90 type Eout = ();
91 type Error = Error;
92 type Time = Instant;
93
94 fn handle_read(&mut self, msg: TaggedPacket) -> Result<(), Self::Error> {
95 let Packet::Rtp(rtp_packet) = &msg.message.packet else {
96 self.read_queue.push_back(msg);
97 return Ok(());
98 };
99
100 let ssrc = rtp_packet.header.ssrc;
101 let now = msg.now;
102 let transport = msg.transport;
103
104 // A repair packet: hand it to the decoder and swallow it. Passing it on would put a
105 // non-media stream in front of every interceptor ahead.
106 if let Some(&media_ssrc) = self.repair_to_media.get(&ssrc) {
107 let recovered = match self.decoders.get_mut(&media_ssrc) {
108 Some(decoder) => decoder.decode(rtp_packet.clone()),
109 None => Vec::new(),
110 };
111 self.queue_recovered(now, transport, recovered);
112 return Ok(());
113 }
114
115 // A protected media packet: the decoder needs it in order to recover its neighbours, and
116 // it carries on as usual.
117 let recovered = match self.decoders.get_mut(&ssrc) {
118 Some(decoder) => decoder.decode(rtp_packet.clone()),
119 None => {
120 self.read_queue.push_back(msg);
121 return Ok(());
122 }
123 };
124
125 // The live packet arrived first, so it goes first; anything it made recoverable follows.
126 // Their order relative to each other is the jitter buffer's problem, not this one's.
127 self.read_queue.push_back(msg);
128 self.queue_recovered(now, transport, recovered);
129 Ok(())
130 }
131
132 fn poll_read(&mut self) -> Option<TaggedPacket> {
133 self.read_queue.pop_front()
134 }
135
136 fn handle_write(&mut self, msg: TaggedPacket) -> Result<(), Self::Error> {
137 self.write_queue.push_back(msg);
138 Ok(())
139 }
140
141 fn poll_write(&mut self) -> Option<Self::Wout> {
142 self.write_queue.pop_front()
143 }
144
145 fn handle_timeout(&mut self, _now: Instant) -> Result<(), Self::Error> {
146 Ok(())
147 }
148
149 fn poll_timeout(&mut self) -> Option<Self::Time> {
150 None
151 }
152}
153
154impl Interceptor for FlexFec03ReceiveInterceptor {
155 fn bind_remote_stream(&mut self, info: &StreamInfo) {
156 // Both halves, as everywhere else: without the repair SSRC there is nothing to route, and
157 // without the payload type the association was never negotiated.
158 if let (Some(ssrc_fec), Some(_)) = (info.ssrc_fec, info.payload_type_fec) {
159 self.decoders
160 .insert(info.ssrc, FlexFec03Decoder::new(ssrc_fec, info.ssrc));
161 self.repair_to_media.insert(ssrc_fec, info.ssrc);
162 }
163 }
164
165 fn unbind_remote_stream(&mut self, info: &StreamInfo) {
166 self.decoders.remove(&info.ssrc);
167 self.repair_to_media.retain(|_, media| *media != info.ssrc);
168 }
169
170 fn bind_local_stream(&mut self, _info: &StreamInfo) {}
171
172 fn unbind_local_stream(&mut self, _info: &StreamInfo) {}
173}
174
175impl FlexFec03ReceiveInterceptor {
176 /// Hold recovered packets for `poll_read`, which puts them back on the belt.
177 ///
178 /// They then traverse every interceptor ahead exactly as a packet that arrived normally does — which
179 /// is the point of recovery, and which the nested design achieved by re-injecting through
180 /// `inner` by hand.
181 fn queue_recovered(
182 &mut self,
183 now: std::time::Instant,
184 transport: shared::TransportContext,
185 recovered: Vec<rtp::Packet>,
186 ) {
187 for packet in recovered {
188 let mut message = AttributedPacket::new(Packet::Rtp(packet));
189 // Says how it got here: it was never on the wire in this form, so anything measuring
190 // the network — arrival times, loss — can tell it apart from a packet that was.
191 message.add(Attribute::RecoveredByFec);
192 self.read_queue.push_back(TaggedPacket {
193 now,
194 transport,
195 message,
196 });
197 }
198 }
199}