Skip to main content

rtc_interceptor/report/
sender.rs

1//! Sender Report Interceptor - Filters hop-by-hop RTCP feedback.
2
3use super::sender_stream::SenderStream;
4use crate::Interceptor;
5use crate::stream_info::StreamInfo;
6use crate::{AttributedPacket, Packet, TaggedPacket};
7use rtcp::header::PacketType;
8use sansio::Protocol;
9use shared::TransportContext;
10use shared::error::Error;
11use std::collections::{HashMap, VecDeque};
12use std::time::{Duration, Instant};
13
14/// Builder for the SenderReportInterceptor.
15///
16/// # Example
17///
18/// ```
19/// use rtc_interceptor::{Slot, Registry, SenderReportBuilder};
20/// use std::time::Duration;
21///
22/// // With default interval (1 second)
23/// let chain = Registry::new()
24///     .with(Slot::SenderReport, SenderReportBuilder::new().build())
25///     .build();
26///
27/// // With custom interval
28/// let chain = Registry::new()
29///     .with(Slot::SenderReport, SenderReportBuilder::new().with_interval(Duration::from_millis(500)).build())
30///     .build();
31///
32/// // With use_latest_packet enabled
33/// let chain = Registry::new()
34///     .with(Slot::SenderReport, SenderReportBuilder::new().with_use_latest_packet().build())
35///     .build();
36/// ```
37pub struct SenderReportBuilder {
38    /// Interval between sender reports.
39    interval: Duration,
40    /// Whether to always use the latest packet, even if out-of-order.
41    use_latest_packet: bool,
42}
43
44impl Default for SenderReportBuilder {
45    fn default() -> Self {
46        Self {
47            interval: Duration::from_secs(1),
48            use_latest_packet: false,
49        }
50    }
51}
52
53impl SenderReportBuilder {
54    /// Create a new builder with default settings.
55    ///
56    /// Default interval is 1 second.
57    pub fn new() -> Self {
58        Self::default()
59    }
60
61    /// Set a custom interval between sender reports.
62    ///
63    /// # Example
64    ///
65    /// ```
66    /// use rtc_interceptor::{Registry, SenderReportBuilder, Slot};
67    /// use std::time::Duration;
68    ///
69    /// let registry = Registry::new().with(
70    ///     Slot::SenderReport,
71    ///     SenderReportBuilder::new()
72    ///         .with_interval(Duration::from_millis(500))
73    ///         .build(),
74    /// );
75    /// ```
76    pub fn with_interval(mut self, interval: Duration) -> Self {
77        self.interval = interval;
78        self
79    }
80
81    /// Enable always using the latest packet for timestamp tracking,
82    /// even if it appears to be out-of-order based on sequence numbers.
83    ///
84    /// By default (disabled), only in-order packets update the RTP↔NTP
85    /// timestamp correlation. This prevents out-of-order packets from
86    /// corrupting the timestamp mapping.
87    ///
88    /// Enable this option when:
89    /// - Packets are guaranteed to arrive in order
90    /// - The application reorders packets before the interceptor
91    /// - You want the sender report to always reflect the most recent packet
92    ///
93    /// # Example
94    ///
95    /// ```
96    /// use rtc_interceptor::{Slot, Registry, SenderReportBuilder};
97    ///
98    /// let registry =
99    ///     Registry::new().with(Slot::SenderReport, SenderReportBuilder::new().with_use_latest_packet().build());
100    /// ```
101    pub fn with_use_latest_packet(mut self) -> Self {
102        self.use_latest_packet = true;
103        self
104    }
105
106    /// Create a builder function for use with Registry.
107    ///
108    /// This returns a closure that can be passed to `Registry::with()`.
109    ///
110    /// # Example
111    ///
112    /// ```
113    /// use rtc_interceptor::{Slot, Registry, SenderReportBuilder};
114    ///
115    /// let registry = Registry::new()
116    ///     .with(Slot::SenderReport, SenderReportBuilder::new().build());
117    /// ```
118    pub fn build(self) -> SenderReportInterceptor {
119        SenderReportInterceptor::new(self.interval, self.use_latest_packet)
120    }
121}
122
123/// Interceptor that filters hop-by-hop RTCP reports.
124///
125/// This interceptor filters out RTCP Receiver Reports and Transport-Specific
126/// Feedback, which are hop-by-hop reports that should not be forwarded
127/// end-to-end.
128///
129/// # Type Parameters
130///
131/// - `P`: The inner protocol being wrapped
132///
133/// # Example
134///
135/// ```
136/// use rtc_interceptor::{Slot, Registry, SenderReportBuilder};
137///
138/// let chain = Registry::new()
139///     .with(Slot::SenderReport, SenderReportBuilder::new().build())
140///     .build();
141/// ```
142pub struct SenderReportInterceptor {
143    interval: Duration,
144    next_timeout: Option<Instant>,
145
146    /// Whether to always use the latest packet, even if out-of-order.
147    use_latest_packet: bool,
148
149    streams: HashMap<u32, SenderStream>,
150
151    read_queue: VecDeque<TaggedPacket>,
152    write_queue: VecDeque<TaggedPacket>,
153}
154
155impl SenderReportInterceptor {
156    /// Create a new SenderReportInterceptor.
157    fn new(interval: Duration, use_latest_packet: bool) -> Self {
158        Self {
159            interval,
160            next_timeout: None,
161
162            use_latest_packet,
163
164            streams: HashMap::new(),
165
166            read_queue: VecDeque::new(),
167            write_queue: VecDeque::new(),
168        }
169    }
170
171    /// Check if an RTCP packet type should be filtered.
172    ///
173    /// Returns `true` for hop-by-hop report types that should not be forwarded:
174    /// - Receiver Report (201)
175    /// - Transport-Specific Feedback (205)
176    fn should_filter(packet_type: PacketType) -> bool {
177        packet_type == PacketType::ReceiverReport
178            || (packet_type == PacketType::TransportSpecificFeedback)
179    }
180}
181
182impl Protocol<TaggedPacket, TaggedPacket, ()> for SenderReportInterceptor {
183    type Rout = TaggedPacket;
184    type Wout = TaggedPacket;
185    type Eout = ();
186    type Error = Error;
187    type Time = Instant;
188
189    fn handle_read(&mut self, msg: TaggedPacket) -> Result<(), Self::Error> {
190        self.read_queue.push_back(msg);
191        Ok(())
192    }
193
194    fn poll_read(&mut self) -> Option<Self::Rout> {
195        self.read_queue.pop_front()
196    }
197
198    fn handle_write(&mut self, msg: TaggedPacket) -> Result<(), Self::Error> {
199        if let Packet::Rtp(rtp_packet) = &msg.message.packet
200            && let Some(stream) = self.streams.get_mut(&rtp_packet.header.ssrc)
201        {
202            stream.process_rtp(msg.now, rtp_packet);
203
204            // Arm the report timer from the first packet's instant (see nack::generator).
205            if self.next_timeout.is_none() {
206                self.next_timeout = Some(msg.now + self.interval);
207            }
208        }
209
210        self.write_queue.push_back(msg);
211
212        Ok(())
213    }
214
215    fn poll_write(&mut self) -> Option<TaggedPacket> {
216        // First drain generated RTCP reports
217        if let Some(pkt) = self.write_queue.pop_front() {
218            return Some(pkt);
219        }
220        None
221    }
222
223    fn handle_timeout(&mut self, now: Instant) -> Result<(), Error> {
224        if let Some(next_timeout) = self.next_timeout
225            && now >= next_timeout
226        {
227            self.next_timeout = Some(now + self.interval);
228
229            for stream in self.streams.values_mut() {
230                if let Some(rr) = stream.generate_report(now) {
231                    self.write_queue.push_back(TaggedPacket {
232                        now,
233                        transport: TransportContext::default(),
234                        message: AttributedPacket::new(Packet::Rtcp(vec![Box::new(rr)])),
235                    });
236                }
237            }
238        }
239        Ok(())
240    }
241
242    fn poll_timeout(&mut self) -> Option<Instant> {
243        self.next_timeout
244    }
245}
246
247impl Interceptor for SenderReportInterceptor {
248    fn bind_local_stream(&mut self, info: &StreamInfo) {
249        let stream = SenderStream::new(info.ssrc, info.clock_rate, self.use_latest_packet);
250        self.streams.insert(info.ssrc, stream);
251    }
252
253    fn unbind_local_stream(&mut self, info: &StreamInfo) {
254        self.streams.remove(&info.ssrc);
255    }
256
257    fn bind_remote_stream(&mut self, _info: &StreamInfo) {}
258
259    fn unbind_remote_stream(&mut self, _info: &StreamInfo) {}
260}