1use crate::{utils::u32_from_be_bytes, RtcpParseError, RtcpWriteError};
4
5#[derive(Debug, PartialEq, Eq)]
8pub struct ReportBlock<'a> {
9 data: &'a [u8; ReportBlock::EXPECTED_SIZE],
10}
11
12impl<'a> ReportBlock<'a> {
13 pub const EXPECTED_SIZE: usize = 24;
14
15 pub fn parse(data: &'a [u8]) -> Result<Self, RtcpParseError> {
17 if data.len() < Self::EXPECTED_SIZE {
18 return Err(RtcpParseError::Truncated {
19 expected: Self::EXPECTED_SIZE,
20 actual: data.len(),
21 });
22 }
23 if data.len() > Self::EXPECTED_SIZE {
24 return Err(RtcpParseError::TooLarge {
25 expected: Self::EXPECTED_SIZE,
26 actual: data.len(),
27 });
28 }
29 Ok(Self {
30 data: data.try_into().unwrap(),
31 })
32 }
33
34 pub fn ssrc(&self) -> u32 {
36 u32_from_be_bytes(&self.data[0..4])
37 }
38
39 pub fn fraction_lost(&self) -> u8 {
41 self.data[4]
42 }
43
44 pub fn cumulative_lost(&self) -> u32 {
46 u32_from_be_bytes(&self.data[4..8]) & 0xffffff
47 }
48
49 pub fn extended_sequence_number(&self) -> u32 {
51 u32_from_be_bytes(&self.data[8..12])
52 }
53
54 pub fn interarrival_jitter(&self) -> u32 {
56 u32_from_be_bytes(&self.data[12..16])
57 }
58
59 pub fn last_sender_report_timestamp(&self) -> u32 {
61 u32_from_be_bytes(&self.data[16..20])
62 }
63
64 pub fn delay_since_last_sender_report_timestamp(&self) -> u32 {
66 u32_from_be_bytes(&self.data[20..24])
67 }
68
69 pub fn builder(ssrc: u32) -> ReportBlockBuilder {
71 ReportBlockBuilder::new(ssrc)
72 }
73}
74
75#[derive(Debug, Eq, PartialEq)]
77#[must_use = "The builder must be built to be used"]
78pub struct ReportBlockBuilder {
79 ssrc: u32,
80 fraction_lost: u8,
81 cumulative_lost: u32,
82 extended_sequence_number: u32,
83 interarrival_jitter: u32,
84 last_sender_report_timestamp: u32,
85 delay_since_last_sender_report_timestamp: u32,
86}
87
88impl ReportBlockBuilder {
89 pub fn new(ssrc: u32) -> Self {
90 ReportBlockBuilder {
91 ssrc,
92 fraction_lost: 0,
93 cumulative_lost: 0,
94 extended_sequence_number: 0,
95 interarrival_jitter: 0,
96 last_sender_report_timestamp: 0,
97 delay_since_last_sender_report_timestamp: 0,
98 }
99 }
100
101 pub fn fraction_lost(mut self, fraction_lost: u8) -> Self {
103 self.fraction_lost = fraction_lost;
104 self
105 }
106
107 pub fn cumulative_lost(mut self, cumulative_lost: u32) -> Self {
109 self.cumulative_lost = cumulative_lost;
110 self
111 }
112
113 pub fn extended_sequence_number(mut self, extended_sequence_number: u32) -> Self {
115 self.extended_sequence_number = extended_sequence_number;
116 self
117 }
118
119 pub fn interarrival_jitter(mut self, interarrival_jitter: u32) -> Self {
121 self.interarrival_jitter = interarrival_jitter;
122 self
123 }
124
125 pub fn last_sender_report_timestamp(mut self, last_sender_report_timestamp: u32) -> Self {
127 self.last_sender_report_timestamp = last_sender_report_timestamp;
128 self
129 }
130
131 pub fn delay_since_last_sender_report_timestamp(
133 mut self,
134 delay_since_last_sender_report_timestamp: u32,
135 ) -> Self {
136 self.delay_since_last_sender_report_timestamp = delay_since_last_sender_report_timestamp;
137 self
138 }
139
140 pub(crate) fn calculate_size(&self) -> Result<usize, RtcpWriteError> {
146 if self.cumulative_lost & !0xffffff != 0 {
147 return Err(RtcpWriteError::CumulativeLostTooLarge {
148 value: self.cumulative_lost,
149 max: 0xffffff,
150 });
151 }
152
153 Ok(ReportBlock::EXPECTED_SIZE)
154 }
155
156 #[inline]
164 pub(crate) fn write_into_unchecked(&self, buf: &mut [u8]) -> usize {
165 buf[0..4].copy_from_slice(&self.ssrc.to_be_bytes());
166 buf[4..8].copy_from_slice(&self.cumulative_lost.to_be_bytes());
167 buf[4] = self.fraction_lost;
168 buf[8..12].copy_from_slice(&self.extended_sequence_number.to_be_bytes());
169 buf[12..16].copy_from_slice(&self.interarrival_jitter.to_be_bytes());
170 buf[16..20].copy_from_slice(&self.last_sender_report_timestamp.to_be_bytes());
171 buf[20..].copy_from_slice(&self.delay_since_last_sender_report_timestamp.to_be_bytes());
172
173 ReportBlock::EXPECTED_SIZE
174 }
175}
176
177#[cfg(test)]
178mod tests {
179 use super::*;
180
181 #[test]
182 fn parse_report_block() {
183 let data = [
184 0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef, 0x02, 0x24, 0x46, 0x68, 0x8a, 0xac,
185 0xce, 0xe0, 0xf1, 0xd3, 0xb5, 0x97, 0x79, 0x5b, 0x3d, 0x1f,
186 ];
187 let rb = ReportBlock::parse(&data).unwrap();
188 assert_eq!(rb.ssrc(), 0x1234567);
189 assert_eq!(rb.fraction_lost(), 0x89);
190 assert_eq!(rb.cumulative_lost(), 0xabcdef);
191 assert_eq!(rb.extended_sequence_number(), 0x02244668);
192 assert_eq!(rb.interarrival_jitter(), 0x8aaccee0);
193 assert_eq!(rb.last_sender_report_timestamp(), 0xf1d3b597);
194 assert_eq!(rb.delay_since_last_sender_report_timestamp(), 0x795b3d1f);
195 }
196
197 #[test]
198 fn build_report_block() {
199 let rbb = ReportBlock::builder(0x1234567)
200 .fraction_lost(0x89)
201 .cumulative_lost(0xabcdef)
202 .extended_sequence_number(0x02244668)
203 .interarrival_jitter(0x8aaccee0)
204 .last_sender_report_timestamp(0xf1d3b597)
205 .delay_since_last_sender_report_timestamp(0x795b3d1f);
206 let req_size = rbb.calculate_size().unwrap();
207 assert_eq!(req_size, ReportBlock::EXPECTED_SIZE);
208
209 let mut buf = [0; ReportBlock::EXPECTED_SIZE];
210 let len = rbb.write_into_unchecked(&mut buf);
211 assert_eq!(len, ReportBlock::EXPECTED_SIZE);
212 assert_eq!(
213 buf,
214 [
215 0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef, 0x02, 0x24, 0x46, 0x68, 0x8a, 0xac,
216 0xce, 0xe0, 0xf1, 0xd3, 0xb5, 0x97, 0x79, 0x5b, 0x3d, 0x1f,
217 ]
218 );
219 }
220
221 #[test]
222 fn short_report_block() {
223 assert_eq!(
224 ReportBlock::parse(&[0]),
225 Err(RtcpParseError::Truncated {
226 expected: 24,
227 actual: 1
228 })
229 );
230 }
231
232 #[test]
233 fn too_large_report_block() {
234 let data = [0; 25];
235 assert_eq!(
236 ReportBlock::parse(&data),
237 Err(RtcpParseError::TooLarge {
238 expected: 24,
239 actual: 25
240 })
241 );
242 }
243}