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