rtcp_types/xr/
mod.rs

1// SPDX-License-Identifier: MIT OR Apache-2.0
2
3use crate::{
4    prelude::*,
5    utils::{pad_to_4bytes, parser, u16_from_be_bytes, writer},
6    RtcpPacket, RtcpParseError, RtcpWriteError,
7};
8
9pub mod dlrr;
10pub mod duplicate_rle;
11pub mod loss_rle;
12pub mod packet_receipt_time;
13pub mod receiver_reference_time;
14pub(crate) mod rle;
15
16/// A parsed extended report packet as specified in RFC 3611.
17#[derive(Clone, Debug, PartialEq, Eq)]
18pub struct Xr<'a> {
19    data: &'a [u8],
20}
21
22impl RtcpPacket for Xr<'_> {
23    const MIN_PACKET_LEN: usize = 12;
24    const PACKET_TYPE: u8 = 207;
25}
26
27impl<'a> RtcpPacketParser<'a> for Xr<'a> {
28    fn parse(data: &'a [u8]) -> Result<Self, RtcpParseError> {
29        parser::check_packet::<Self>(data)?;
30        let mut offset = 8;
31        loop {
32            if offset >= data.len() {
33                break;
34            }
35            let block = XrBlock::parse(&data[offset..])?;
36            offset += block.length();
37        }
38        Ok(Self { data })
39    }
40
41    #[inline(always)]
42    fn header_data(&self) -> [u8; 4] {
43        self.data[..4].try_into().unwrap()
44    }
45}
46
47impl<'a> Xr<'a> {
48    /// Constructs a [`XrBuilder`] which refers to the provided [`XrBlockBuilder`].
49    pub fn builder() -> XrBuilder {
50        XrBuilder {
51            padding: 0,
52            sender_ssrc: 0,
53            blocks: vec![],
54        }
55    }
56
57    /// The (optional) padding used by this [`Xr`] packet
58    pub fn padding(&self) -> Option<u8> {
59        parser::parse_padding(self.data)
60    }
61
62    /// The SSRC of the sender sending this feedback
63    pub fn sender_ssrc(&self) -> u32 {
64        parser::parse_ssrc(self.data)
65    }
66
67    /// Iterator over the individual blocks in the [`Xr`] packet.
68    pub fn block_iter(&'a self) -> impl Iterator<Item = XrBlock<'a>> + 'a {
69        XrBlockIter {
70            xr: self,
71            offset: 8,
72        }
73    }
74    // TODO: add iterator
75}
76
77struct XrBlockIter<'a> {
78    xr: &'a Xr<'a>,
79    offset: usize,
80}
81
82impl<'a> Iterator for XrBlockIter<'a> {
83    type Item = XrBlock<'a>;
84
85    fn next(&mut self) -> Option<Self::Item> {
86        if self.offset > self.xr.data.len() {
87            return None;
88        }
89        let block = XrBlock::parse(&self.xr.data[self.offset..]).ok()?;
90        self.offset += block.length();
91        Some(block)
92    }
93}
94
95/// XR packet builder
96#[derive(Debug)]
97#[must_use = "The builder must be built to be used"]
98pub struct XrBuilder {
99    padding: u8,
100    sender_ssrc: u32,
101    blocks: Vec<Box<dyn XrBlockBuilder<'static>>>,
102}
103
104impl XrBuilder {
105    /// Set the SSRC this feedback packet is being sent from
106    pub fn sender_ssrc(mut self, sender_ssrc: u32) -> Self {
107        self.sender_ssrc = sender_ssrc;
108        self
109    }
110
111    /// Sets the number of padding bytes to use for this [`Xr`] packet.
112    pub fn padding(mut self, padding: u8) -> Self {
113        self.padding = padding;
114        self
115    }
116
117    /// Add an [`XrBlock`] to this [`Xr`] packet.
118    pub fn add_block(mut self, block: impl XrBlockBuilder<'static> + 'static) -> XrBuilder {
119        self.blocks.push(Box::new(block));
120        self
121    }
122}
123
124impl RtcpPacketWriter for XrBuilder {
125    /// Calculates the size required to write this Xr packet.
126    ///
127    /// Returns an error if:
128    ///
129    /// * The report block data is too large
130    /// * The report block fails to calculate a valid size
131    fn calculate_size(&self) -> Result<usize, RtcpWriteError> {
132        writer::check_padding(self.padding)?;
133
134        let mut len = 0;
135        for block in self.blocks.iter() {
136            len += pad_to_4bytes(block.calculate_size()?);
137        }
138
139        Ok(Xr::MIN_PACKET_LEN - 4 + len)
140    }
141
142    /// Write this Xr packet data into `buf` without any validity checks.
143    ///
144    /// Returns the number of bytes written.
145    ///
146    /// # Panic
147    ///
148    /// Panics if the buf is not large enough.
149    #[inline]
150    fn write_into_unchecked(&self, buf: &mut [u8]) -> usize {
151        let mut idx = writer::write_header_unchecked::<Xr>(self.padding, 0, buf);
152        buf[idx..idx + 4].copy_from_slice(&self.sender_ssrc.to_be_bytes());
153        idx += 4;
154
155        for block in self.blocks.iter() {
156            let len = block.write_into_unchecked(&mut buf[idx..]);
157            let padded_len = pad_to_4bytes(len);
158            if len != padded_len {
159                buf[len..padded_len].fill(0);
160            }
161            idx += padded_len;
162        }
163        idx
164    }
165
166    fn get_padding(&self) -> Option<u8> {
167        if self.padding == 0 {
168            return None;
169        }
170
171        Some(self.padding)
172    }
173}
174
175/// The common header of an XR report block
176#[derive(Debug)]
177pub struct XrBlock<'a> {
178    pub(crate) data: &'a [u8],
179}
180
181impl<'a> XrBlock<'a> {
182    /// Parse this [`XrBlock`] into a specific implementation.
183    pub fn parse_into<T: XrBlockParser<'a> + XrBlockStaticType>(
184        &self,
185    ) -> Result<T, RtcpParseError> {
186        if T::BLOCK_TYPE != self.block_type() {
187            return Err(RtcpParseError::WrongImplementation);
188        }
189        T::parse(self.data)
190    }
191}
192
193impl<'a> XrBlockParser<'a> for XrBlock<'a> {
194    fn parse(data: &'a [u8]) -> Result<Self, RtcpParseError> {
195        if data.len() < 4 {
196            return Err(RtcpParseError::Truncated {
197                expected: 4,
198                actual: data.len(),
199            });
200        }
201        let ret = Self { data };
202        if data.len() < ret.length() {
203            return Err(RtcpParseError::Truncated {
204                expected: ret.length(),
205                actual: data.len(),
206            });
207        }
208
209        Ok(ret)
210    }
211
212    #[inline(always)]
213    fn header_data(&self) -> [u8; 4] {
214        self.data[..4].try_into().unwrap()
215    }
216}
217
218/// Trait for parsing XR report block data in [`Xr`] packets
219///
220/// Implementers only need to return the 4 byte RTCP header
221/// from [`XrBlockParser::header_data`] to be able to use
222/// the getters for the common RTCP packet fields.
223pub trait XrBlockParser<'a>: Sized {
224    /// Parse the provided XR block
225    fn parse(data: &'a [u8]) -> Result<Self, RtcpParseError>;
226
227    /// Returns the common header for this XR Block.
228    fn header_data(&self) -> [u8; 4];
229}
230
231/// Extension trait providing helpers for parsing a [`XrBlock`].
232pub trait XrBlockParserExt<'a>: XrBlockParser<'a> {
233    /// The indicator for the particular XR block being parsed
234    fn block_type(&self) -> u8 {
235        self.header_data()[0]
236    }
237
238    /// The type specific value in the XR block header
239    fn type_specific_byte(&self) -> u8 {
240        self.header_data()[1]
241    }
242
243    /// The length (in bytes) of the XR block
244    fn length(&self) -> usize {
245        (u16_from_be_bytes(&self.header_data()[2..4]) as usize + 1) * 4
246    }
247}
248
249impl<'a, T: XrBlockParser<'a>> XrBlockParserExt<'a> for T {}
250
251/// A trait for implementations that contain a compile-time constant of the block type.
252pub trait XrBlockStaticType {
253    /// The block type identifier within a XR packet.
254    const BLOCK_TYPE: u8;
255}
256
257/// Trait for writing a particular XR block implementation with a [`XrBuilder`].
258pub trait XrBlockBuilder<'a>: RtcpPacketWriter {
259    /// The type specific byte to place in the XR block header
260    fn type_specific_byte(&self) -> u8;
261}
262
263/// Extension trait providing helpers for writing a [`XrBlock`] to a sequence of bytes.
264pub trait XrBlockBuilderExt<'a>: XrBlockBuilder<'a> {
265    /// Write the 4 byte [`XrBlock`] header into `buf` using the provided values.
266    fn write_header_unchecked(&self, buf: &mut [u8], block_type: u8, block_word_len: u16) -> usize {
267        buf[0] = block_type;
268        buf[1] = self.type_specific_byte();
269        buf[2..4].copy_from_slice(&block_word_len.to_be_bytes());
270        4
271    }
272}
273
274impl<'a, T: XrBlockBuilder<'a>> XrBlockBuilderExt<'a> for T {}
275
276pub(crate) fn xr_offset_sequence(seq: u16, start: u16, end: u16, thinning: u8) -> Option<u16> {
277    let seq = seq
278        .wrapping_mul(2u16.pow(thinning as u32))
279        .wrapping_add(start);
280    if end <= start {
281        if seq < start && seq >= end {
282            None
283        } else {
284            Some(seq)
285        }
286    } else if seq >= end {
287        None
288    } else {
289        Some(seq)
290    }
291}
292
293#[cfg(test)]
294mod tests {
295    use duplicate_rle::DuplicateRle;
296    use loss_rle::LossRle;
297
298    use super::*;
299
300    #[test]
301    fn xr_block_build_single() {
302        let loss = LossRle::builder()
303            .ssrc(0x8642_1357)
304            .begin(100)
305            .end(200)
306            .thinning(0)
307            .add_chunk(rle::RleChunk::RunLength(100));
308        let builder = Xr::builder().sender_ssrc(0x8642_1357).add_block(loss);
309        let len = builder.calculate_size().unwrap();
310        let mut buf = vec![0; len];
311        builder.write_into_unchecked(&mut buf);
312        println!("{buf:x?}");
313
314        let xr = Xr::parse(&buf).unwrap();
315        assert_eq!(xr.sender_ssrc(), 0x8642_1357);
316        let mut it = xr.block_iter();
317        let rle = it.next().unwrap().parse_into::<LossRle>().unwrap();
318        assert_eq!(rle.thinning(), 0);
319        assert_eq!(rle.begin(), 100);
320        assert_eq!(rle.end(), 200);
321    }
322
323    #[test]
324    fn xr_block_build_2block() {
325        let loss = LossRle::builder()
326            .ssrc(0x8642_1357)
327            .begin(100)
328            .end(200)
329            .thinning(0)
330            .add_chunk(rle::RleChunk::RunLength(100));
331        let duplicate = DuplicateRle::builder()
332            .ssrc(0x8642_1357)
333            .begin(101)
334            .end(202)
335            .thinning(1)
336            .add_chunk(rle::RleChunk::SkipLength(100));
337        let builder = Xr::builder()
338            .sender_ssrc(0x8642_1357)
339            .add_block(loss)
340            .add_block(duplicate);
341        let len = builder.calculate_size().unwrap();
342        let mut buf = vec![0; len];
343        builder.write_into_unchecked(&mut buf);
344        println!("{buf:x?}");
345
346        let xr = Xr::parse(&buf).unwrap();
347        assert_eq!(xr.sender_ssrc(), 0x8642_1357);
348        let mut it = xr.block_iter();
349        let rle = it.next().unwrap().parse_into::<LossRle>().unwrap();
350        assert_eq!(rle.thinning(), 0);
351        assert_eq!(rle.begin(), 100);
352        assert_eq!(rle.end(), 200);
353        let rle = it.next().unwrap().parse_into::<DuplicateRle>().unwrap();
354        assert_eq!(rle.thinning(), 1);
355        assert_eq!(rle.begin(), 101);
356        assert_eq!(rle.end(), 202);
357    }
358
359    #[test]
360    fn xr_block_parse_truncated_header() {
361        let data = [1, 0, 0];
362        assert!(matches!(
363            XrBlock::parse(&data),
364            Err(RtcpParseError::Truncated {
365                expected: 4,
366                actual: 3
367            })
368        ));
369    }
370
371    #[test]
372    fn xr_block_parse_truncated_block() {
373        let data = [1, 0, 0, 1, 4, 2, 3];
374        assert!(matches!(
375            XrBlock::parse(&data),
376            Err(RtcpParseError::Truncated {
377                expected: 8,
378                actual: 7
379            })
380        ));
381    }
382
383    #[test]
384    fn xr_offset_at_start() {
385        for thinning in 0..15 {
386            assert_eq!(xr_offset_sequence(0, 100, 200, thinning), Some(100));
387            assert_eq!(
388                xr_offset_sequence(0, u16::MAX - 100, 200, thinning),
389                Some(u16::MAX - 100)
390            );
391        }
392    }
393
394    #[test]
395    fn xr_offset_at_end() {
396        assert_eq!(xr_offset_sequence(99, 100, 200, 0), Some(199));
397        assert_eq!(xr_offset_sequence(100, 100, 200, 0), None);
398
399        assert_eq!(xr_offset_sequence(99, u16::MAX - 49, 50, 0), Some(49));
400        assert_eq!(xr_offset_sequence(100, u16::MAX - 49, 50, 0), None);
401    }
402
403    #[test]
404    fn xr_offset_thinning() {
405        assert_eq!(xr_offset_sequence(49, 100, 200, 1), Some(198));
406        assert_eq!(xr_offset_sequence(50, 100, 200, 1), None);
407        assert_eq!(xr_offset_sequence(24, 100, 200, 2), Some(196));
408        assert_eq!(xr_offset_sequence(25, 100, 200, 2), None);
409
410        assert_eq!(xr_offset_sequence(49, u16::MAX - 49, 50, 1), Some(48));
411        assert_eq!(xr_offset_sequence(50, u16::MAX - 49, 50, 1), None);
412        assert_eq!(xr_offset_sequence(24, u16::MAX - 49, 50, 2), Some(46));
413        assert_eq!(xr_offset_sequence(25, u16::MAX - 49, 50, 2), None);
414    }
415}