rtcp_types/feedback/
nack.rs

1// SPDX-License-Identifier: MIT OR Apache-2.0
2
3use std::collections::BTreeSet;
4
5use crate::feedback::FciFeedbackPacketType;
6use crate::{prelude::*, utils::u16_from_be_bytes};
7use crate::{RtcpParseError, RtcpWriteError};
8
9/// An iterator over a parsed list of Negative Acknowledgements
10pub struct NackParserEntryIter<'a> {
11    parser: &'a Nack<'a>,
12    i: usize,
13    mask_i: usize,
14}
15
16impl NackParserEntryIter<'_> {
17    fn decode_entry(entry: &[u8]) -> (u16, u16) {
18        (
19            u16_from_be_bytes(&entry[0..2]),
20            u16_from_be_bytes(&entry[2..4]),
21        )
22    }
23}
24
25impl std::iter::Iterator for NackParserEntryIter<'_> {
26    type Item = u16;
27
28    fn next(&mut self) -> Option<Self::Item> {
29        loop {
30            if self.mask_i > 16 {
31                self.mask_i = 0;
32                self.i += 1;
33            }
34            let idx = self.i * 4;
35            if idx + 3 >= self.parser.data.len() {
36                return None;
37            }
38            let (base, mask) = NackParserEntryIter::decode_entry(&self.parser.data[idx..]);
39            if self.mask_i == 0 {
40                self.mask_i += 1;
41                return Some(base);
42            }
43
44            loop {
45                let mask = mask >> (self.mask_i - 1);
46                if (mask & 0x1) > 0 {
47                    self.mask_i += 1;
48                    let ret = base.wrapping_add(self.mask_i as u16 - 1);
49                    return Some(ret);
50                }
51                self.mask_i += 1;
52                if self.mask_i > 16 {
53                    break;
54                }
55            }
56        }
57    }
58}
59
60/// Generic NACK FCI information as specified in RFC 4585
61#[derive(Debug)]
62pub struct Nack<'a> {
63    data: &'a [u8],
64}
65
66impl Nack<'_> {
67    /// The list of RTP sequence numbers that is being NACKed.
68    pub fn entries(&self) -> impl Iterator<Item = u16> + '_ {
69        NackParserEntryIter {
70            parser: self,
71            i: 0,
72            mask_i: 0,
73        }
74    }
75
76    /// Create a new [`NackBuilder`]
77    pub fn builder() -> NackBuilder {
78        NackBuilder::default()
79    }
80}
81
82impl<'a> FciParser<'a> for Nack<'a> {
83    const PACKET_TYPE: FciFeedbackPacketType = FciFeedbackPacketType::TRANSPORT;
84    const FCI_FORMAT: u8 = 1;
85
86    fn parse(data: &'a [u8]) -> Result<Self, RtcpParseError> {
87        Ok(Self { data })
88    }
89}
90
91struct NackBuilderEntryIter<I: Iterator<Item = u16>> {
92    i: usize,
93    base_entry: Option<u16>,
94    seq_iter: I,
95    last_entry: u16,
96}
97
98fn encode_entry(base: u16, mask: u16) -> [u8; 4] {
99    [
100        ((base & 0xff00) >> 8) as u8,
101        (base & 0xff) as u8,
102        ((mask & 0xff00) >> 8) as u8,
103        (mask & 0xff) as u8,
104    ]
105}
106
107impl<I: Iterator<Item = u16>> std::iter::Iterator for NackBuilderEntryIter<I> {
108    type Item = [u8; 4];
109
110    fn next(&mut self) -> Option<Self::Item> {
111        let mut bitmask = 0;
112        for entry in self.seq_iter.by_ref() {
113            self.last_entry = entry;
114            if let Some(base) = self.base_entry {
115                let diff = entry.wrapping_sub(base);
116                if diff > 16 {
117                    // bitmask will overflow next iteration, return the current value
118                    let ret = encode_entry(base, bitmask);
119                    self.base_entry = Some(entry);
120                    return Some(ret);
121                }
122                if diff > 0 {
123                    bitmask |= 1 << (diff - 1);
124                }
125                self.i += 1;
126            } else {
127                // initial set up for the first value
128                self.base_entry = Some(entry);
129                self.i += 1;
130                continue;
131            }
132        }
133
134        if let Some(base) = self.base_entry {
135            // we need to output the final entry
136            let ret = encode_entry(base, bitmask);
137            self.base_entry = None;
138            return Some(ret);
139        }
140        None
141    }
142}
143
144/// A builder for [`Nack`]
145#[derive(Debug, Default)]
146pub struct NackBuilder {
147    rtp_seq: BTreeSet<u16>,
148}
149
150impl NackBuilder {
151    /// Add a RTP sequence number as negatively acknowledged
152    pub fn add_rtp_sequence(mut self, rtp_sequence: u16) -> Self {
153        self.rtp_seq.insert(rtp_sequence);
154        self
155    }
156
157    fn entries(&self) -> impl Iterator<Item = [u8; 4]> + '_ {
158        NackBuilderEntryIter {
159            i: 0,
160            base_entry: None,
161            seq_iter: self.rtp_seq.iter().copied(),
162            last_entry: 0,
163        }
164    }
165}
166
167impl FciBuilder<'_> for NackBuilder {
168    fn format(&self) -> u8 {
169        1
170    }
171
172    fn supports_feedback_type(&self) -> FciFeedbackPacketType {
173        FciFeedbackPacketType::TRANSPORT
174    }
175}
176
177impl RtcpPacketWriter for NackBuilder {
178    fn calculate_size(&self) -> Result<usize, RtcpWriteError> {
179        let entries = self.entries().count();
180        if entries > u16::MAX as usize - 2 {
181            return Err(RtcpWriteError::TooManyNack);
182        }
183        Ok(entries * 4)
184    }
185
186    fn write_into_unchecked(&self, buf: &mut [u8]) -> usize {
187        let mut idx = 0;
188        let mut end = idx;
189
190        for entry in self.entries() {
191            end += 4;
192            buf[idx..end].copy_from_slice(&entry);
193            idx = end;
194        }
195        end
196    }
197
198    fn get_padding(&self) -> Option<u8> {
199        None
200    }
201}
202
203#[cfg(test)]
204mod tests {
205    use super::*;
206    use crate::feedback::TransportFeedback;
207
208    fn nack_build_parse_n_consecutive_timestamps(start: u16, n: u16, fci: &[u8]) {
209        nack_build_parse_n_m_timestamps(start, n, 1, fci)
210    }
211
212    fn nack_build_parse_n_m_timestamps(start: u16, n: u16, m: u16, fci: &[u8]) {
213        assert!(n > 1);
214        let r = (n + 1) % m;
215        let req_len = TransportFeedback::MIN_PACKET_LEN + ((n - r + 16) / 17 * 4) as usize;
216        let mut data = vec![0; req_len];
217        let mut expected = vec![0; req_len];
218        const TEMPLATE: [u8; 12] = [
219            0x81, 0xcd, 0x00, 0x00, 0x98, 0x76, 0x54, 0x32, 0x10, 0xfe, 0xdc, 0xba,
220        ];
221        expected[0..12].copy_from_slice(&TEMPLATE);
222        expected[3] = (req_len / 4 - 1) as u8;
223        expected[12..12 + fci.len()].copy_from_slice(fci);
224        let nack = {
225            let mut fci = Nack::builder();
226            for i in (0..=n - 1).step_by(m as usize) {
227                fci = fci.add_rtp_sequence(start + i);
228            }
229            TransportFeedback::builder_owned(fci)
230                .sender_ssrc(0x98765432)
231                .media_ssrc(0x10fedcba)
232        };
233        assert_eq!(nack.calculate_size().unwrap(), req_len);
234        let len = nack.write_into(&mut data).unwrap();
235        assert_eq!(len, req_len);
236        assert_eq!(data, expected);
237
238        let fb = TransportFeedback::parse(&data).unwrap();
239        assert_eq!(fb.sender_ssrc(), 0x98765432);
240        assert_eq!(fb.media_ssrc(), 0x10fedcba);
241        let nack = fb.parse_fci::<Nack>().unwrap();
242        let mut nack_iter = nack.entries();
243        for i in (0..=n - 1).step_by(m as usize) {
244            assert_eq!(nack_iter.next(), Some(0x1234 + i));
245        }
246        assert_eq!(nack_iter.next(), None);
247    }
248
249    #[test]
250    fn nack_build_parse_2_consecutive_timestamps() {
251        nack_build_parse_n_consecutive_timestamps(0x1234, 2, &[0x12, 0x34, 0x00, 0x01]);
252    }
253
254    #[test]
255    fn nack_build_parse_16_consecutive_timestamps() {
256        nack_build_parse_n_consecutive_timestamps(0x1234, 16, &[0x12, 0x34, 0x7f, 0xff]);
257    }
258
259    #[test]
260    fn nack_build_parse_17_consecutive_timestamps() {
261        nack_build_parse_n_consecutive_timestamps(0x1234, 17, &[0x12, 0x34, 0xff, 0xff]);
262    }
263
264    #[test]
265    fn nack_build_parse_18_consecutive_timestamps() {
266        nack_build_parse_n_consecutive_timestamps(
267            0x1234,
268            18,
269            &[0x12, 0x34, 0xff, 0xff, 0x12, 0x45, 0x00, 0x00],
270        );
271    }
272
273    #[test]
274    fn nack_build_parse_12_2_timestamps() {
275        nack_build_parse_n_m_timestamps(0x1234, 12, 2, &[0x12, 0x34, 0x02, 0b1010_1010]);
276    }
277
278    #[test]
279    fn nack_build_ref_2_consecutive_timestamps() {
280        let n = 2;
281        let m = 1;
282        let fci = &[0x12, 0x34, 0x00, 0x01];
283
284        let r = (n + 1) % m;
285        let req_len = TransportFeedback::MIN_PACKET_LEN + ((n - r + 16) / 17 * 4) as usize;
286        let mut data = vec![0; req_len];
287        let mut expected = vec![0; req_len];
288        const TEMPLATE: [u8; 12] = [
289            0x81, 0xcd, 0x00, 0x00, 0x98, 0x76, 0x54, 0x32, 0x10, 0xfe, 0xdc, 0xba,
290        ];
291        expected[0..12].copy_from_slice(&TEMPLATE);
292        expected[3] = (req_len / 4 - 1) as u8;
293        expected[12..12 + fci.len()].copy_from_slice(fci);
294        let mut fci = Nack::builder();
295        for i in (0..=n - 1).step_by(m as usize) {
296            fci = fci.add_rtp_sequence(0x1234 + i);
297        }
298        let nack = TransportFeedback::builder(&fci)
299            .sender_ssrc(0x98765432)
300            .media_ssrc(0x10fedcba);
301        assert_eq!(nack.calculate_size().unwrap(), req_len);
302        let len = nack.write_into(&mut data).unwrap();
303        assert_eq!(len, req_len);
304        assert_eq!(data, expected);
305    }
306}