rtcp_types/
report_block.rs

1// SPDX-License-Identifier: MIT OR Apache-2.0
2
3use crate::{utils::u32_from_be_bytes, RtcpParseError, RtcpWriteError};
4
5/// A report block as found in a [`SenderReport`](crate::SenderReport) or a
6/// [`ReceiverReport`](crate::ReceiverReport) for a received SSRC
7#[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    /// Parse data into a [`ReportBlock`].
16    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    /// The SSRC that this report describes
35    pub fn ssrc(&self) -> u32 {
36        u32_from_be_bytes(&self.data[0..4])
37    }
38
39    /// The fractional part (out of 256) of packets that have been lost
40    pub fn fraction_lost(&self) -> u8 {
41        self.data[4]
42    }
43
44    /// Total count of packets that have been lost.  This is a 24-bit value.
45    pub fn cumulative_lost(&self) -> u32 {
46        u32_from_be_bytes(&self.data[4..8]) & 0xffffff
47    }
48
49    /// Extended sequence number
50    pub fn extended_sequence_number(&self) -> u32 {
51        u32_from_be_bytes(&self.data[8..12])
52    }
53
54    /// The interarrival jitter of this receiver
55    pub fn interarrival_jitter(&self) -> u32 {
56        u32_from_be_bytes(&self.data[12..16])
57    }
58
59    /// The NTP 16.16 fixed point time that a sender report was last received
60    pub fn last_sender_report_timestamp(&self) -> u32 {
61        u32_from_be_bytes(&self.data[16..20])
62    }
63
64    /// 16.16 fixed point duration since the last sender report was received
65    pub fn delay_since_last_sender_report_timestamp(&self) -> u32 {
66        u32_from_be_bytes(&self.data[20..24])
67    }
68
69    /// Create a new [`ReportBlockBuilder`]
70    pub fn builder(ssrc: u32) -> ReportBlockBuilder {
71        ReportBlockBuilder::new(ssrc)
72    }
73}
74
75/// Report Block Builder
76#[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    /// The fraction (out of 256) of packets lost
102    pub fn fraction_lost(mut self, fraction_lost: u8) -> Self {
103        self.fraction_lost = fraction_lost;
104        self
105    }
106
107    /// The cumulative count of packets lost.  Value must be limited to the sie of a 24-bit value.
108    pub fn cumulative_lost(mut self, cumulative_lost: u32) -> Self {
109        self.cumulative_lost = cumulative_lost;
110        self
111    }
112
113    /// The extended sequence number
114    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    /// The inter arrival jitter
120    pub fn interarrival_jitter(mut self, interarrival_jitter: u32) -> Self {
121        self.interarrival_jitter = interarrival_jitter;
122        self
123    }
124
125    /// The NTP 16.16 fixed point time of the last sender report
126    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    /// The NTP 16.16 fixed point duration since the last sender report
132    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    /// Calculates the size required to write this Report Block.
141    ///
142    /// Returns an error if:
143    ///
144    /// * The cumulative_lost is out of range.
145    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    /// Writes this Report Block into `buf` without any validity checks.
157    ///
158    /// Returns the number of bytes written.
159    ///
160    /// # Panic
161    ///
162    /// Panics if the buf is not large enough.
163    #[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}