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    /// The expected size of a [`ReportBlock`] as stored in a [`SenderReport`](crate::SenderReport)
14    /// or a [`ReceiverReport`](crate::ReceiverReport).
15    pub const EXPECTED_SIZE: usize = 24;
16
17    /// Parse data into a [`ReportBlock`].
18    pub fn parse(data: &'a [u8]) -> Result<Self, RtcpParseError> {
19        if data.len() < Self::EXPECTED_SIZE {
20            return Err(RtcpParseError::Truncated {
21                expected: Self::EXPECTED_SIZE,
22                actual: data.len(),
23            });
24        }
25        if data.len() > Self::EXPECTED_SIZE {
26            return Err(RtcpParseError::TooLarge {
27                expected: Self::EXPECTED_SIZE,
28                actual: data.len(),
29            });
30        }
31        Ok(Self {
32            data: data.try_into().unwrap(),
33        })
34    }
35
36    /// The SSRC that this report describes
37    pub fn ssrc(&self) -> u32 {
38        u32_from_be_bytes(&self.data[0..4])
39    }
40
41    /// The fractional part (out of 256) of packets that have been lost
42    pub fn fraction_lost(&self) -> u8 {
43        self.data[4]
44    }
45
46    /// Total count of packets that have been lost.  This is a 24-bit value.
47    pub fn cumulative_lost(&self) -> u32 {
48        u32_from_be_bytes(&self.data[4..8]) & 0xffffff
49    }
50
51    /// Extended sequence number
52    pub fn extended_sequence_number(&self) -> u32 {
53        u32_from_be_bytes(&self.data[8..12])
54    }
55
56    /// The interarrival jitter of this receiver
57    pub fn interarrival_jitter(&self) -> u32 {
58        u32_from_be_bytes(&self.data[12..16])
59    }
60
61    /// The NTP 16.16 fixed point time that a sender report was last received
62    pub fn last_sender_report_timestamp(&self) -> u32 {
63        u32_from_be_bytes(&self.data[16..20])
64    }
65
66    /// 16.16 fixed point duration since the last sender report was received
67    pub fn delay_since_last_sender_report_timestamp(&self) -> u32 {
68        u32_from_be_bytes(&self.data[20..24])
69    }
70
71    /// Create a new [`ReportBlockBuilder`]
72    pub fn builder(ssrc: u32) -> ReportBlockBuilder {
73        ReportBlockBuilder::new(ssrc)
74    }
75}
76
77/// Report Block Builder
78#[derive(Debug, Eq, PartialEq)]
79#[must_use = "The builder must be built to be used"]
80pub struct ReportBlockBuilder {
81    ssrc: u32,
82    fraction_lost: u8,
83    cumulative_lost: u32,
84    extended_sequence_number: u32,
85    interarrival_jitter: u32,
86    last_sender_report_timestamp: u32,
87    delay_since_last_sender_report_timestamp: u32,
88}
89
90impl ReportBlockBuilder {
91    fn new(ssrc: u32) -> Self {
92        ReportBlockBuilder {
93            ssrc,
94            fraction_lost: 0,
95            cumulative_lost: 0,
96            extended_sequence_number: 0,
97            interarrival_jitter: 0,
98            last_sender_report_timestamp: 0,
99            delay_since_last_sender_report_timestamp: 0,
100        }
101    }
102
103    /// The fraction (out of 256) of packets lost
104    pub fn fraction_lost(mut self, fraction_lost: u8) -> Self {
105        self.fraction_lost = fraction_lost;
106        self
107    }
108
109    /// The cumulative count of packets lost.  Value must be limited to the sie of a 24-bit value.
110    pub fn cumulative_lost(mut self, cumulative_lost: u32) -> Self {
111        self.cumulative_lost = cumulative_lost;
112        self
113    }
114
115    /// The extended sequence number
116    pub fn extended_sequence_number(mut self, extended_sequence_number: u32) -> Self {
117        self.extended_sequence_number = extended_sequence_number;
118        self
119    }
120
121    /// The inter arrival jitter
122    pub fn interarrival_jitter(mut self, interarrival_jitter: u32) -> Self {
123        self.interarrival_jitter = interarrival_jitter;
124        self
125    }
126
127    /// The NTP 16.16 fixed point time of the last sender report
128    pub fn last_sender_report_timestamp(mut self, last_sender_report_timestamp: u32) -> Self {
129        self.last_sender_report_timestamp = last_sender_report_timestamp;
130        self
131    }
132
133    /// The NTP 16.16 fixed point duration since the last sender report
134    pub fn delay_since_last_sender_report_timestamp(
135        mut self,
136        delay_since_last_sender_report_timestamp: u32,
137    ) -> Self {
138        self.delay_since_last_sender_report_timestamp = delay_since_last_sender_report_timestamp;
139        self
140    }
141
142    /// Calculates the size required to write this Report Block.
143    ///
144    /// Returns an error if:
145    ///
146    /// * The cumulative_lost is out of range.
147    pub(crate) fn calculate_size(&self) -> Result<usize, RtcpWriteError> {
148        if self.cumulative_lost & !0xffffff != 0 {
149            return Err(RtcpWriteError::CumulativeLostTooLarge {
150                value: self.cumulative_lost,
151                max: 0xffffff,
152            });
153        }
154
155        Ok(ReportBlock::EXPECTED_SIZE)
156    }
157
158    /// Writes this Report Block into `buf` without any validity checks.
159    ///
160    /// Returns the number of bytes written.
161    ///
162    /// # Panic
163    ///
164    /// Panics if the buf is not large enough.
165    #[inline]
166    pub(crate) fn write_into_unchecked(&self, buf: &mut [u8]) -> usize {
167        buf[0..4].copy_from_slice(&self.ssrc.to_be_bytes());
168        buf[4..8].copy_from_slice(&self.cumulative_lost.to_be_bytes());
169        buf[4] = self.fraction_lost;
170        buf[8..12].copy_from_slice(&self.extended_sequence_number.to_be_bytes());
171        buf[12..16].copy_from_slice(&self.interarrival_jitter.to_be_bytes());
172        buf[16..20].copy_from_slice(&self.last_sender_report_timestamp.to_be_bytes());
173        buf[20..].copy_from_slice(&self.delay_since_last_sender_report_timestamp.to_be_bytes());
174
175        ReportBlock::EXPECTED_SIZE
176    }
177}
178
179#[cfg(test)]
180mod tests {
181    use super::*;
182
183    #[test]
184    fn parse_report_block() {
185        let data = [
186            0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef, 0x02, 0x24, 0x46, 0x68, 0x8a, 0xac,
187            0xce, 0xe0, 0xf1, 0xd3, 0xb5, 0x97, 0x79, 0x5b, 0x3d, 0x1f,
188        ];
189        let rb = ReportBlock::parse(&data).unwrap();
190        assert_eq!(rb.ssrc(), 0x1234567);
191        assert_eq!(rb.fraction_lost(), 0x89);
192        assert_eq!(rb.cumulative_lost(), 0xabcdef);
193        assert_eq!(rb.extended_sequence_number(), 0x02244668);
194        assert_eq!(rb.interarrival_jitter(), 0x8aaccee0);
195        assert_eq!(rb.last_sender_report_timestamp(), 0xf1d3b597);
196        assert_eq!(rb.delay_since_last_sender_report_timestamp(), 0x795b3d1f);
197    }
198
199    #[test]
200    fn build_report_block() {
201        let rbb = ReportBlock::builder(0x1234567)
202            .fraction_lost(0x89)
203            .cumulative_lost(0xabcdef)
204            .extended_sequence_number(0x02244668)
205            .interarrival_jitter(0x8aaccee0)
206            .last_sender_report_timestamp(0xf1d3b597)
207            .delay_since_last_sender_report_timestamp(0x795b3d1f);
208        let req_size = rbb.calculate_size().unwrap();
209        assert_eq!(req_size, ReportBlock::EXPECTED_SIZE);
210
211        let mut buf = [0; ReportBlock::EXPECTED_SIZE];
212        let len = rbb.write_into_unchecked(&mut buf);
213        assert_eq!(len, ReportBlock::EXPECTED_SIZE);
214        assert_eq!(
215            buf,
216            [
217                0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef, 0x02, 0x24, 0x46, 0x68, 0x8a, 0xac,
218                0xce, 0xe0, 0xf1, 0xd3, 0xb5, 0x97, 0x79, 0x5b, 0x3d, 0x1f,
219            ]
220        );
221    }
222
223    #[test]
224    fn short_report_block() {
225        assert_eq!(
226            ReportBlock::parse(&[0]),
227            Err(RtcpParseError::Truncated {
228                expected: 24,
229                actual: 1
230            })
231        );
232    }
233
234    #[test]
235    fn too_large_report_block() {
236        let data = [0; 25];
237        assert_eq!(
238            ReportBlock::parse(&data),
239            Err(RtcpParseError::TooLarge {
240                expected: 24,
241                actual: 25
242            })
243        );
244    }
245}