rtcp_types/feedback/
twcc.rs

1// SPDX-License-Identifier: MIT OR Apache-2.0
2
3use std::cmp;
4
5use crate::feedback::FciFeedbackPacketType;
6use crate::prelude::*;
7use crate::utils::{pad_to_4bytes, u16_from_be_bytes};
8use crate::{RtcpParseError, RtcpWriteError};
9
10/// Maximum representable TWCC reference time.
11///
12/// The reference time is encoded in 24 bits as multiples of 64 ms.
13pub const TWCC_MAX_REFERENCE_TIME: u32 = (1 << 24) - 1;
14
15/// Transport-Wide Congestion Control FCI block
16#[derive(Debug)]
17pub struct Twcc<'a> {
18    data: &'a [u8],
19}
20
21impl<'a> Twcc<'a> {
22    /// See [`TwccBuilder::new`]
23    pub fn builder(
24        base_seq: u16,
25        reference_time: u32,
26        feedback_packet_count: u8,
27        status_list: &[TwccPacketStatus],
28        max_size: Option<usize>,
29    ) -> TwccBuilder {
30        TwccBuilder::new(
31            base_seq,
32            reference_time,
33            feedback_packet_count,
34            status_list,
35            max_size,
36        )
37    }
38
39    /// The transport-wide sequence number of the first packet in this feedback.
40    ///
41    /// This number is not necessarily increased for every feedback; in the case of reordering it may be decreased
42    pub fn base_sequence_number(&self) -> u16 {
43        u16_from_be_bytes(&self.data[0..2])
44    }
45
46    /// Signed integer indicating an absolute reference time in some (unknown) time base chosen by the sender of the
47    /// feedback packets.
48    ///
49    /// The value is to be interpreted in multiples of 64ms.
50    ///
51    /// The first recv delta in this packet is relative to the reference time.
52    ///
53    /// The reference time makes it possible to calculate the delta between feedbacks even if some feedback packets are
54    /// lost, since it always uses the same time base.
55    pub fn reference_time(&self) -> u32 {
56        u32::from_be_bytes([0, self.data[4], self.data[5], self.data[6]])
57    }
58
59    /// A counter incremented by one for each feedback packet sent. Used to detect feedback packet losses.
60    pub fn feedback_packet_count(&self) -> u8 {
61        self.data[7]
62    }
63
64    fn packet_status_count(&self) -> u16 {
65        u16_from_be_bytes(&self.data[2..4])
66    }
67
68    fn packet_chunks(&self) -> impl Iterator<Item = PacketStatusChunk> + 'a {
69        let mut remaining_status_count = self.packet_status_count();
70
71        self.data[8..].chunks_exact(2).map_while(move |chunk| {
72            if remaining_status_count == 0 {
73                return None;
74            }
75
76            let chunk = u16_from_be_bytes(chunk);
77
78            let chunk = if chunk & (1 << 15) == 0 {
79                PacketStatusChunk::RunLength(StatusBits::from_two_bits(chunk >> 13), chunk & 0x1FFF)
80            } else if chunk & (1 << 14) == 0 {
81                PacketStatusChunk::Vector1Bit(chunk & 0x3FFF)
82            } else {
83                PacketStatusChunk::Vector2Bit(chunk & 0x3FFF)
84            };
85
86            remaining_status_count = remaining_status_count.saturating_sub(chunk.max_len());
87
88            Some(chunk)
89        })
90    }
91
92    /// Returns an iterator over all packets described by this TWCC feedback.
93    ///
94    /// The iterator yields `(sequence_number, status)` pairs in ascending sequence number order, starting from
95    ///  [`Twcc::base_sequence_number`].
96    pub fn packets(
97        &self,
98    ) -> impl Iterator<Item = Result<(u16, TwccPacketStatus), RtcpParseError>> + 'a {
99        let mut remaining_packet_status_count = self.packet_status_count();
100        let states = self.packet_chunks().flat_map(move |chunk| {
101            let packet_status_iter = chunk
102                .packet_status_iter()
103                .take(remaining_packet_status_count.into());
104
105            remaining_packet_status_count =
106                remaining_packet_status_count.saturating_sub(chunk.max_len());
107
108            packet_status_iter
109        });
110
111        let packet_chunks_count = self.packet_chunks().count();
112        let mut deltas = self.data[8 + packet_chunks_count * 2..].iter();
113
114        let mut sequence_number = self.base_sequence_number();
115
116        states.map(move |status_bits| -> Result<_, RtcpParseError> {
117            let packet_sequence_number = sequence_number;
118            sequence_number = sequence_number.wrapping_add(1);
119
120            let packet_status = match status_bits {
121                StatusBits::NotReceived => TwccPacketStatus::NotReceived,
122                StatusBits::ReceivedSmallDelta => {
123                    // Single byte delta (0..63.75ms)
124                    let delta_byte = *deltas.next().ok_or(RtcpParseError::TwccDeltaTruncated)?;
125
126                    TwccPacketStatus::Received {
127                        delta: i16::from(delta_byte),
128                    }
129                }
130                StatusBits::ReceivedLargeOrNegativeDelta => {
131                    // Two byte delta (-8192.0..8191.75 ms)
132                    let delta_byte0 = *deltas.next().ok_or(RtcpParseError::TwccDeltaTruncated)?;
133                    let delta_byte1 = *deltas.next().ok_or(RtcpParseError::TwccDeltaTruncated)?;
134
135                    TwccPacketStatus::Received {
136                        delta: i16::from_be_bytes([delta_byte0, delta_byte1]),
137                    }
138                }
139                StatusBits::Reserved => return Err(RtcpParseError::TwccReservedPacketStatus),
140            };
141
142            Ok((packet_sequence_number, packet_status))
143        })
144    }
145}
146
147impl<'a> FciParser<'a> for Twcc<'a> {
148    const PACKET_TYPE: FciFeedbackPacketType = FciFeedbackPacketType::TRANSPORT;
149    const FCI_FORMAT: u8 = 15;
150
151    fn parse(data: &'a [u8]) -> Result<Self, RtcpParseError> {
152        if data.len() < 8 {
153            return Err(RtcpParseError::Truncated {
154                expected: 8,
155                actual: data.len(),
156            });
157        }
158        Ok(Self { data })
159    }
160}
161
162/// Status of a single packet in a TWCC feedback packet
163#[derive(Debug, Clone, Copy, PartialEq, Eq)]
164pub enum TwccPacketStatus {
165    /// Packet was not received.
166    NotReceived,
167
168    /// Packet was received, with the given reception time delta in microseconds.
169    Received {
170        /// Delta is measured relative to the TWCC reference time, with a resolution of 250 µs.
171        ///
172        /// This value represents that delta as multiple of 250 µs.
173        delta: i16,
174    },
175}
176
177impl TwccPacketStatus {
178    fn to_bits(self) -> StatusBits {
179        match self {
180            TwccPacketStatus::NotReceived => StatusBits::NotReceived,
181            TwccPacketStatus::Received { delta } => {
182                if (0..=255).contains(&delta) {
183                    StatusBits::ReceivedSmallDelta
184                } else {
185                    StatusBits::ReceivedLargeOrNegativeDelta
186                }
187            }
188        }
189    }
190}
191
192/// Builder for a TWCC (Transport-Wide Congestion Control) FCI packet
193#[derive(Debug)]
194pub struct TwccBuilder {
195    base_seq: u16,
196    reference_time: u32,
197    feedback_packet_count: u8,
198    packet_status_count: u16,
199    chunks: Vec<PacketStatusChunk>,
200    deltas: Vec<u8>,
201}
202
203impl TwccBuilder {
204    /// Create a new `TwccBuilder` with the given packet status list.
205    ///
206    /// `max_size` limits the number of bytes the FCI portion of the RTCP packet can use.
207    /// If set, [`TwccBuilder::packet_status_count`] must be used to check how many status entries have been consumed.
208    /// Any remaining status entries must be encoded in a separate feedback packet.
209    pub fn new(
210        base_seq: u16,
211        reference_time: u32,
212        feedback_packet_count: u8,
213        status_list: &[TwccPacketStatus],
214        max_size: Option<usize>,
215    ) -> TwccBuilder {
216        let mut this = TwccBuilder {
217            base_seq,
218            reference_time,
219            feedback_packet_count,
220            packet_status_count: 0,
221            chunks: Vec::new(),
222            deltas: Vec::new(),
223        };
224
225        this.set_status_list(status_list, max_size);
226
227        this
228    }
229
230    fn set_status_list(&mut self, mut status_list: &[TwccPacketStatus], max_size: Option<usize>) {
231        while let Some((mut chunk, mut consumed)) =
232            PacketStatusChunk::from_packet_status_list(status_list)
233        {
234            // Check if the added chunk exceeds the provided max_size
235            if let Some(max_size) = max_size {
236                let projected_size = self.calculate_projected_size(status_list, consumed);
237
238                if projected_size > max_size {
239                    // Generated chunk is too large, if the chunk is a run length, it can be shortened to fit.
240                    //
241                    // Avoids returning 0 consumed packets if status only contains more Received packet status entries
242                    // than `max_size` can fit.
243                    if let PacketStatusChunk::RunLength(
244                        status_bits @ (StatusBits::ReceivedSmallDelta
245                        | StatusBits::ReceivedLargeOrNegativeDelta),
246                        run_length,
247                    ) = &mut chunk
248                    {
249                        let bytes_per_delta = match status_bits {
250                            StatusBits::ReceivedSmallDelta => 1,
251                            StatusBits::ReceivedLargeOrNegativeDelta => 2,
252                            _ => unreachable!(),
253                        };
254
255                        let overshoot = pad_to_4bytes(projected_size - max_size);
256                        if overshoot / bytes_per_delta > usize::from(*run_length - 1) {
257                            return;
258                        }
259
260                        *run_length -= (overshoot / bytes_per_delta) as u16;
261                        consumed -= overshoot / bytes_per_delta;
262                    } else {
263                        return;
264                    }
265                }
266            }
267
268            // Abort if there's more packet status entries than can fit into packet_status_count
269            let packet_status_count = match self.packet_status_count.checked_add(consumed as u16) {
270                Some(packet_status_count) => packet_status_count,
271                _ => {
272                    return;
273                }
274            };
275
276            self.packet_status_count = packet_status_count;
277            self.chunks.push(chunk);
278
279            // Add deltas from consumed packet status entries
280            for packet_status in &status_list[..consumed] {
281                match *packet_status {
282                    TwccPacketStatus::NotReceived => {
283                        // No delta to add
284                    }
285                    TwccPacketStatus::Received { delta } => {
286                        if let Ok(delta) = u8::try_from(delta) {
287                            self.deltas.push(delta);
288                        } else {
289                            self.deltas.extend(delta.to_be_bytes());
290                        }
291                    }
292                }
293            }
294
295            status_list = &status_list[consumed..];
296        }
297    }
298
299    fn calculate_projected_size(
300        &mut self,
301        status_list: &[TwccPacketStatus],
302        consumed: usize,
303    ) -> usize {
304        let additional_deltas_size: usize = status_list
305            .iter()
306            .take(consumed)
307            .map(|packet_status| match packet_status.to_bits() {
308                StatusBits::ReceivedSmallDelta => 1,
309                StatusBits::ReceivedLargeOrNegativeDelta => 2,
310                _ => 0,
311            })
312            .sum();
313
314        let additional_size = additional_deltas_size + 2;
315        let current_size = self.chunks.len() * 2 + self.deltas.len();
316
317        pad_to_4bytes(8 + current_size + additional_size)
318    }
319
320    /// Number of packet status entries contained in this builder.
321    pub fn packet_status_count(&self) -> usize {
322        usize::from(self.packet_status_count)
323    }
324}
325
326impl FciBuilder<'_> for TwccBuilder {
327    fn format(&self) -> u8 {
328        Twcc::FCI_FORMAT
329    }
330
331    fn supports_feedback_type(&self) -> FciFeedbackPacketType {
332        Twcc::PACKET_TYPE
333    }
334}
335
336impl RtcpPacketWriter for TwccBuilder {
337    fn calculate_size(&self) -> Result<usize, RtcpWriteError> {
338        if self.reference_time > TWCC_MAX_REFERENCE_TIME {
339            return Err(RtcpWriteError::TwccReferenceTimeTooLarge);
340        }
341
342        let packet_chunks = self.chunks.len() * 2;
343        let deltas = self.deltas.len();
344
345        Ok(pad_to_4bytes(8 + packet_chunks + deltas))
346    }
347
348    fn write_into_unchecked(&self, buf: &mut [u8]) -> usize {
349        buf[0..2].copy_from_slice(&self.base_seq.to_be_bytes());
350        buf[2..4].copy_from_slice(&self.packet_status_count.to_be_bytes());
351        buf[4..7].copy_from_slice(&self.reference_time.to_be_bytes()[1..]);
352        buf[7] = self.feedback_packet_count;
353
354        let mut idx = 8;
355
356        for chunk in &self.chunks {
357            buf[idx..(idx + 2)].copy_from_slice(&chunk.to_u16().to_be_bytes());
358            idx += 2;
359        }
360
361        buf[idx..idx + self.deltas.len()].copy_from_slice(&self.deltas);
362
363        pad_to_4bytes(idx + self.deltas.len())
364    }
365
366    fn get_padding(&self) -> Option<u8> {
367        None
368    }
369}
370
371#[derive(Debug, Clone, Copy, PartialEq, Eq)]
372enum PacketStatusChunk {
373    /// Indicates a packet status for n packets (13 bit length)
374    RunLength(StatusBits, u16),
375
376    /// Status vector with 1 bits per packet status (14 bits, 14 packets)
377    Vector1Bit(u16),
378
379    /// Status vector with 2 bits per packet status (14 bits, 7 packets)
380    Vector2Bit(u16),
381}
382
383impl PacketStatusChunk {
384    /// Consume items from a list of TwccPacketStatus into a single PacketStatusChunk
385    ///
386    /// Returns the chunk and the number of consumed TwccPacketStatus
387    fn from_packet_status_list(
388        status_list: &[TwccPacketStatus],
389    ) -> Option<(PacketStatusChunk, usize)> {
390        const RUN_LENGTH_MINIMUM: usize = 7;
391        const CUTOFF_1BIT: usize = 14;
392        const CUTOFF_2BIT: usize = 7;
393
394        let first_status_bits = status_list.first()?.to_bits();
395        let run_length = status_list
396            .iter()
397            .take_while(|packet_status| packet_status.to_bits() == first_status_bits)
398            .take(0x1FFF)
399            .count();
400
401        let num_one_bit_status = status_list
402            .iter()
403            .take_while(|packet_status| packet_status.to_bits().is_one_bit())
404            .take(14)
405            .count();
406
407        if run_length > RUN_LENGTH_MINIMUM && run_length >= num_one_bit_status {
408            // Encode run length
409
410            Some((
411                PacketStatusChunk::RunLength(first_status_bits, run_length as u16),
412                run_length,
413            ))
414        } else if (status_list.len() == num_one_bit_status && num_one_bit_status > CUTOFF_2BIT)
415            || num_one_bit_status == CUTOFF_1BIT
416        {
417            // Encode one bit vector
418
419            let num_one_bit_status = cmp::min(num_one_bit_status, CUTOFF_1BIT);
420
421            let mut bits = 0u16;
422
423            for (i, status) in status_list.iter().take(num_one_bit_status).enumerate() {
424                debug_assert!(status.to_bits().is_one_bit());
425                bits |= (status.to_bits() as u16) << (CUTOFF_1BIT - (i + 1));
426            }
427
428            Some((PacketStatusChunk::Vector1Bit(bits), num_one_bit_status))
429        } else {
430            // Encode two bit vector
431
432            let mut bits = 0u16;
433
434            for (i, status) in status_list.iter().take(CUTOFF_2BIT).enumerate() {
435                bits |= (status.to_bits() as u16) << ((CUTOFF_2BIT - (i + 1)) * 2);
436            }
437
438            Some((
439                PacketStatusChunk::Vector2Bit(bits),
440                status_list.len().min(CUTOFF_2BIT),
441            ))
442        }
443    }
444
445    /// Maximum number of packet statuses this chunk represents
446    fn max_len(&self) -> u16 {
447        match self {
448            PacketStatusChunk::RunLength(.., len) => *len,
449            PacketStatusChunk::Vector1Bit(_) => 14,
450            PacketStatusChunk::Vector2Bit(_) => 7,
451        }
452    }
453
454    fn packet_status_iter(mut self) -> impl Iterator<Item = StatusBits> {
455        (0..self.max_len()).map_while(move |offset| {
456            let status = match &mut self {
457                PacketStatusChunk::RunLength(status, len) => {
458                    *len -= 1;
459                    *status
460                }
461                PacketStatusChunk::Vector1Bit(bits) => {
462                    StatusBits::from_one_bit(*bits >> (13 - offset))
463                }
464                PacketStatusChunk::Vector2Bit(bits) => {
465                    StatusBits::from_two_bits(*bits >> (12 - (offset * 2)))
466                }
467            };
468
469            Some(status)
470        })
471    }
472
473    fn to_u16(self) -> u16 {
474        match self {
475            PacketStatusChunk::RunLength(status, run_length) => {
476                //  0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
477                // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
478                // |T| S |       Run Length        |
479                // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
480                // T = 0 for Run Length Chunk
481                // S = status
482                ((status as u16) << 13) | (run_length & 0x1F_FF)
483            }
484            PacketStatusChunk::Vector1Bit(bits) => {
485                //  0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
486                // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
487                // |T|S|       symbol list         |
488                // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
489                // T = 1 for Status Vector Chunk
490                // S = 0 so this vector only contains 1 bit per status (Received = 0 and NotReceived = 1)
491                0x8000 | (bits & 0x3F_FF)
492            }
493            PacketStatusChunk::Vector2Bit(bits) => {
494                //  0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
495                // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
496                // |T|S|       symbol list         |
497                // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
498                // T = 1 for Status Vector Chunk
499                // S = 1 so this vector contains 2 bits per status (See TwccPacketStatus)
500                0xC000 | (bits & 0x3F_FF)
501            }
502        }
503    }
504}
505
506#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
507#[repr(u16)]
508enum StatusBits {
509    NotReceived = 0,
510    ReceivedSmallDelta = 1,
511    ReceivedLargeOrNegativeDelta = 2,
512    Reserved = 3,
513}
514
515impl StatusBits {
516    fn from_two_bits(bits: u16) -> StatusBits {
517        match bits & 0b11 {
518            0 => StatusBits::NotReceived,
519            1 => StatusBits::ReceivedSmallDelta,
520            2 => StatusBits::ReceivedLargeOrNegativeDelta,
521            3 => StatusBits::Reserved,
522            _ => unreachable!(),
523        }
524    }
525
526    fn from_one_bit(bit: u16) -> StatusBits {
527        match bit & 0b1 {
528            0 => StatusBits::NotReceived,
529            1 => StatusBits::ReceivedSmallDelta,
530            _ => unreachable!(),
531        }
532    }
533
534    fn is_one_bit(&self) -> bool {
535        matches!(
536            self,
537            StatusBits::NotReceived | StatusBits::ReceivedSmallDelta
538        )
539    }
540}
541
542#[cfg(test)]
543mod tests {
544    use super::*;
545    use rand::{random, Rng};
546
547    #[test]
548    fn parse_packet_chunk() {
549        use PacketStatusChunk as C;
550        use StatusBits as B;
551
552        assert!(C::RunLength(B::NotReceived, 64)
553            .packet_status_iter()
554            .all(|c| c == StatusBits::NotReceived));
555        assert_eq!(
556            C::RunLength(B::NotReceived, 64)
557                .packet_status_iter()
558                .count(),
559            64
560        );
561
562        assert_eq!(
563            C::Vector1Bit(0b00_10_01_01_10_00_00_00)
564                .packet_status_iter()
565                .collect::<Vec<_>>(),
566            [
567                B::ReceivedSmallDelta,
568                B::NotReceived,
569                B::NotReceived,
570                B::ReceivedSmallDelta,
571                B::NotReceived,
572                B::ReceivedSmallDelta,
573                B::ReceivedSmallDelta,
574                B::NotReceived,
575                B::NotReceived,
576                B::NotReceived,
577                B::NotReceived,
578                B::NotReceived,
579                B::NotReceived,
580                B::NotReceived,
581            ],
582        );
583
584        assert_eq!(
585            C::Vector2Bit(0b00_10_01_01_10_00_11_00)
586                .packet_status_iter()
587                .take(6)
588                .collect::<Vec<_>>(),
589            [
590                B::ReceivedLargeOrNegativeDelta,
591                B::ReceivedSmallDelta,
592                B::ReceivedSmallDelta,
593                B::ReceivedLargeOrNegativeDelta,
594                B::NotReceived,
595                B::Reserved,
596            ],
597        );
598    }
599
600    #[test]
601    fn serialize_packet_chunk() {
602        use PacketStatusChunk as C;
603        use StatusBits as B;
604
605        assert_eq!(
606            C::RunLength(B::ReceivedSmallDelta, 64).to_u16(),
607            0b0010_0000_0100_0000
608        );
609        assert_eq!(
610            C::RunLength(B::NotReceived, 256).to_u16(),
611            0b0000_0001_0000_0000
612        );
613        assert_eq!(
614            C::RunLength(B::ReceivedLargeOrNegativeDelta, 1024).to_u16(),
615            0b0100_0100_0000_0000
616        );
617
618        assert_eq!(
619            C::Vector1Bit(0b0011_0011_0011_0011).to_u16(),
620            0b1011_0011_0011_0011
621        );
622
623        assert_eq!(
624            C::Vector1Bit(0b0000_1100_1100_1100).to_u16(),
625            0b1000_1100_1100_1100
626        );
627
628        assert_eq!(
629            C::Vector2Bit(0b0011_0011_0011_0011).to_u16(),
630            0b1111_0011_0011_0011
631        );
632        assert_eq!(
633            C::Vector2Bit(0b0000_1100_1100_1100).to_u16(),
634            0b1100_1100_1100_1100
635        );
636    }
637
638    #[test]
639    fn packet_chunk_from_status() {
640        let (chunk, consumed) =
641            PacketStatusChunk::from_packet_status_list(&[TwccPacketStatus::NotReceived]).unwrap();
642        assert_eq!(consumed, 1);
643        assert_eq!(chunk, PacketStatusChunk::Vector2Bit(0));
644
645        let (chunk, consumed) = PacketStatusChunk::from_packet_status_list(&[
646            TwccPacketStatus::Received { delta: 0 },
647            TwccPacketStatus::NotReceived,
648            TwccPacketStatus::Received { delta: -1 },
649        ])
650        .unwrap();
651        assert_eq!(consumed, 3);
652        assert_eq!(
653            chunk,
654            PacketStatusChunk::Vector2Bit(0b00_01_00_10_00_00_00_00)
655        );
656
657        // 2 Bit even when list is longer due to negative delta
658        let (chunk, consumed) = PacketStatusChunk::from_packet_status_list(&[
659            TwccPacketStatus::Received { delta: 0 },
660            TwccPacketStatus::NotReceived,
661            TwccPacketStatus::Received { delta: -1 },
662            TwccPacketStatus::NotReceived,
663            TwccPacketStatus::NotReceived,
664            TwccPacketStatus::NotReceived,
665            TwccPacketStatus::Received { delta: 1 },
666            TwccPacketStatus::NotReceived,
667            TwccPacketStatus::NotReceived,
668        ])
669        .unwrap();
670        assert_eq!(consumed, 7);
671        assert_eq!(
672            chunk,
673            PacketStatusChunk::Vector2Bit(0b00_01_00_10_00_00_00_01)
674        );
675
676        // 1 Bit when list is longer than 7
677        let (chunk, consumed) = PacketStatusChunk::from_packet_status_list(&[
678            TwccPacketStatus::Received { delta: 0 },
679            TwccPacketStatus::NotReceived,
680            TwccPacketStatus::Received { delta: 1 },
681            TwccPacketStatus::NotReceived,
682            TwccPacketStatus::NotReceived,
683            TwccPacketStatus::NotReceived,
684            TwccPacketStatus::Received { delta: 0 },
685            TwccPacketStatus::NotReceived,
686            TwccPacketStatus::NotReceived,
687        ])
688        .unwrap();
689        assert_eq!(consumed, 9);
690        assert_eq!(
691            chunk,
692            PacketStatusChunk::Vector1Bit(0b00_10_10_00_10_00_00_00)
693        );
694
695        // Run length when viable
696        let mut status = vec![TwccPacketStatus::NotReceived; 26];
697        status.push(TwccPacketStatus::Received { delta: -1 });
698
699        let (chunk, consumed) = PacketStatusChunk::from_packet_status_list(&status).unwrap();
700        assert_eq!(consumed, 26);
701        assert_eq!(
702            chunk,
703            PacketStatusChunk::RunLength(StatusBits::NotReceived, 26)
704        );
705    }
706
707    fn build_and_parse_all(mut status_list: &[TwccPacketStatus], max_size: Option<usize>) {
708        let mut base_seq = rand::random::<u16>();
709
710        while !status_list.is_empty() {
711            let fci = Twcc::builder(base_seq, 0, rand::random(), status_list, max_size);
712
713            let consumed = fci.packet_status_count();
714            assert_ne!(consumed, 0);
715
716            let size = fci.calculate_size().unwrap();
717            if let Some(max_size) = max_size {
718                assert!(size <= max_size, "max_size: {max_size}, size: {size}");
719            }
720
721            let mut buf = vec![0u8; size];
722            fci.write_into(&mut buf).unwrap();
723
724            let twcc = Twcc::parse(&buf).unwrap();
725            assert_eq!(
726                twcc.packets()
727                    .enumerate()
728                    .map(|(i, result)| {
729                        let (seq, p) = result.unwrap();
730                        let expected_seq = base_seq.wrapping_add(i.try_into().unwrap());
731                        assert_eq!(seq, expected_seq);
732                        p
733                    })
734                    .collect::<Vec<_>>(),
735                status_list[..consumed],
736            );
737
738            base_seq = base_seq.wrapping_add(consumed.try_into().unwrap());
739            status_list = &status_list[consumed..];
740        }
741    }
742
743    #[test]
744    fn random_permutations() {
745        let mut status_list = Vec::new();
746
747        for _ in 0..100 {
748            status_list.clear();
749
750            let len = rand::thread_rng().gen_range(200..1000);
751
752            for _ in 0..len {
753                if rand::thread_rng().gen_bool(0.05) {
754                    status_list.extend(std::iter::repeat_n(
755                        TwccPacketStatus::NotReceived,
756                        rand::thread_rng().gen_range(1..3000),
757                    ));
758                } else if rand::thread_rng().gen_bool(0.8) {
759                    status_list.push(TwccPacketStatus::Received {
760                        delta: rand::thread_rng().gen_range(0..20),
761                    });
762                } else {
763                    status_list.push(TwccPacketStatus::Received { delta: random() });
764                }
765            }
766
767            build_and_parse_all(&status_list, Some(rand::thread_rng().gen_range(800..1500)));
768        }
769    }
770
771    #[test]
772    fn too_many_deltas_for_max_size() {
773        const MAX_SIZE_FOR_1000_STATUS: usize = 1012;
774
775        let status_list = vec![TwccPacketStatus::Received { delta: 0 }; 2000];
776
777        let builder = TwccBuilder::new(0, 0, 0, &status_list, Some(MAX_SIZE_FOR_1000_STATUS));
778
779        assert_eq!(builder.packet_status_count(), 1000);
780
781        let builder = TwccBuilder::new(
782            0,
783            0,
784            0,
785            &status_list[builder.packet_status_count()..],
786            Some(MAX_SIZE_FOR_1000_STATUS),
787        );
788
789        assert_eq!(builder.packet_status_count(), 1000);
790    }
791
792    #[test]
793    fn missing_deltas() {
794        let status_list = vec![TwccPacketStatus::Received { delta: 123 }; 5];
795        let builder = TwccBuilder::new(100, 0, 0, &status_list, None);
796
797        let mut buffer = vec![0u8; builder.calculate_size().unwrap()];
798        builder.write_into(&mut buffer).unwrap();
799
800        // Truncate deltas from then end
801        buffer.truncate(buffer.len() - 3);
802
803        let parsed = Twcc::parse(&buffer).unwrap();
804        let packets = parsed.packets().collect::<Vec<_>>();
805
806        assert!(matches!(
807            packets[0],
808            Ok((100, TwccPacketStatus::Received { delta: 123 }))
809        ));
810        assert!(matches!(
811            packets[1],
812            Ok((101, TwccPacketStatus::Received { delta: 123 }))
813        ));
814        assert!(matches!(
815            packets[2],
816            Ok((102, TwccPacketStatus::Received { delta: 123 }))
817        ));
818        assert!(matches!(
819            packets[3],
820            Err(RtcpParseError::TwccDeltaTruncated)
821        ));
822        assert!(matches!(
823            packets[4],
824            Err(RtcpParseError::TwccDeltaTruncated)
825        ));
826    }
827}