rtcp_types/xr/
loss_rle.rs

1// SPDX-License-Identifier: MIT OR Apache-2.0
2
3use crate::prelude::*;
4use crate::xr::rle::{Rle, RleBuilder, RleChunk};
5use crate::xr::{XrBlockBuilder, XrBlockParser};
6use crate::{RtcpParseError, RtcpWriteError};
7
8use super::XrBlockStaticType;
9
10/// Run-Length-Encoded packet loss information as specified in RFC 3611
11#[derive(Debug)]
12pub struct LossRle<'a> {
13    rle: Rle<'a>,
14}
15
16impl XrBlockStaticType for LossRle<'_> {
17    const BLOCK_TYPE: u8 = 0x1;
18}
19
20impl<'a> XrBlockParser<'a> for LossRle<'a> {
21    fn parse(data: &'a [u8]) -> Result<Self, RtcpParseError> {
22        let rle = Rle::parse(data)?;
23        let ret = Self { rle };
24        Ok(ret)
25    }
26
27    #[inline(always)]
28    fn header_data(&self) -> [u8; 4] {
29        self.rle.block.header_data()
30    }
31}
32
33impl LossRle<'_> {
34    /// The amount of thinning applied to the sequence number space. Every 2^thinning sequence
35    /// number has been reported
36    pub fn thinning(&self) -> u8 {
37        self.rle.thinning()
38    }
39
40    /// The SSRC of the media being reported on
41    pub fn media_ssrc(&self) -> u32 {
42        self.rle.media_ssrc()
43    }
44
45    /// This is the (inclusive) start of the sequence number range being reported in this Rle block.
46    /// This start value is included in the range.
47    pub fn begin(&self) -> u16 {
48        self.rle.begin()
49    }
50
51    /// This is the (exclusive) end of the sequence number range being reported in this Rle block.
52    /// This end value is not included in the range.
53    pub fn end(&self) -> u16 {
54        self.rle.end()
55    }
56
57    /// An iterator over the sequence numbers in this [`LossRle`].
58    pub fn sequence_iter(&self) -> impl Iterator<Item = u16> + '_ {
59        self.rle.sequence_iter()
60    }
61
62    /// An iterator over the chunks in this [`LossRle`].
63    ///
64    /// This returns chunks as they are stored without any sequence number translation applied.
65    /// i.e. each chunk starts from a sequence number of 0.
66    pub fn chunk_iter(&self) -> impl Iterator<Item = RleChunk> + '_ {
67        self.rle.chunk_iter()
68    }
69
70    /// Returns a [`LossRleBuilder`] for constructing a [`LossRle`] block.
71    pub fn builder() -> LossRleBuilder {
72        let mut builder = LossRleBuilder {
73            rle: Rle::builder(),
74        };
75        builder.rle = builder.rle.block_type(Self::BLOCK_TYPE);
76        builder
77    }
78}
79
80/// Builder for a [`LossRle`]
81#[derive(Debug, Default)]
82pub struct LossRleBuilder {
83    rle: RleBuilder,
84}
85
86impl LossRleBuilder {
87    /// Set the SSRC the [`LossRle`] refers to.
88    pub fn ssrc(mut self, ssrc: u32) -> Self {
89        self.rle = self.rle.ssrc(ssrc);
90        self
91    }
92
93    /// Set the start of the sequence number range.
94    pub fn begin(mut self, begin: u16) -> Self {
95        self.rle = self.rle.begin(begin);
96        self
97    }
98
99    /// Set the end of the sequence number range.
100    pub fn end(mut self, end: u16) -> Self {
101        self.rle = self.rle.end(end);
102        self
103    }
104
105    /// Set the thinning value for the [`LossRle`].
106    ///
107    /// Thinning signals that ever this block reports on every 2^thinning sequence number.
108    pub fn thinning(mut self, thinning: u8) -> Self {
109        assert!(thinning <= 0xf);
110        self.rle = self.rle.thinning(thinning);
111        self
112    }
113
114    /// Add a chunk for this [`LossRle`]
115    pub fn add_chunk(mut self, chunk: RleChunk) -> Self {
116        self.rle = self.rle.add_chunk(chunk);
117        self
118    }
119}
120
121impl XrBlockBuilder<'_> for LossRleBuilder {
122    fn type_specific_byte(&self) -> u8 {
123        self.rle.type_specific_byte()
124    }
125}
126
127impl RtcpPacketWriter for LossRleBuilder {
128    fn calculate_size(&self) -> Result<usize, RtcpWriteError> {
129        self.rle.calculate_size()
130    }
131
132    fn write_into_unchecked(&self, buf: &mut [u8]) -> usize {
133        self.rle.write_into_unchecked(buf)
134    }
135
136    fn get_padding(&self) -> Option<u8> {
137        None
138    }
139}
140
141#[cfg(test)]
142mod tests {
143    use super::*;
144
145    #[test]
146    fn loss_rle_builder_simple() {
147        let builder = LossRle::builder()
148            .ssrc(0x1357_9864)
149            .begin(400)
150            .end(500)
151            .thinning(1)
152            .add_chunk(RleChunk::RunLength(50));
153        let len = builder.calculate_size().unwrap();
154        let mut buf = vec![0; len];
155        builder.write_into_unchecked(&mut buf);
156        println!("{buf:x?}");
157
158        let rle = LossRle::parse(&buf).unwrap();
159        assert_eq!(rle.media_ssrc(), 0x1357_9864);
160        assert_eq!(rle.thinning(), 1);
161        assert_eq!(rle.begin(), 400);
162        assert_eq!(rle.end(), 500);
163        let expected = (400..500).filter(|x| x % 2 == 0).collect::<Vec<_>>();
164        let sequence = rle.sequence_iter().collect::<Vec<_>>();
165        assert_eq!(sequence, expected);
166    }
167}