rtc_interceptor/flexfec/
coverage.rs1use super::bit_array::BitArray;
4
5pub const MAX_MEDIA_PACKETS: u32 = 110;
10
11pub const MAX_FEC_PACKETS: u32 = MAX_MEDIA_PACKETS;
13
14#[derive(Debug, Clone)]
21pub struct ProtectionCoverage {
22 masks: Vec<BitArray>,
23 num_fec_packets: u32,
24 num_media_packets: u32,
25}
26
27impl ProtectionCoverage {
28 pub fn new(num_media_packets: u32, num_fec_packets: u32) -> Option<Self> {
32 if num_media_packets == 0 || num_media_packets > MAX_MEDIA_PACKETS {
33 return None;
34 }
35
36 let mut coverage = Self {
37 masks: vec![BitArray::new(); MAX_FEC_PACKETS as usize],
38 num_fec_packets: 0,
39 num_media_packets: 0,
40 };
41 coverage.update(num_media_packets, num_fec_packets);
42 Some(coverage)
43 }
44
45 pub fn update(&mut self, num_media_packets: u32, num_fec_packets: u32) {
50 if num_media_packets == 0 || num_media_packets > MAX_MEDIA_PACKETS {
51 return;
52 }
53 if num_media_packets == self.num_media_packets && num_fec_packets == self.num_fec_packets {
54 return;
55 }
56
57 self.num_media_packets = num_media_packets;
58 self.num_fec_packets = num_fec_packets.min(MAX_FEC_PACKETS);
59 for mask in &mut self.masks {
60 mask.reset();
61 }
62
63 for fec_index in 0..self.num_fec_packets {
64 let mut media_index = fec_index;
65 while media_index < num_media_packets {
66 self.masks[fec_index as usize].set_bit(media_index);
67 media_index += self.num_fec_packets;
68 }
69 }
70 }
71
72 pub fn num_fec_packets(&self) -> u32 {
74 self.num_fec_packets
75 }
76
77 pub fn num_media_packets(&self) -> u32 {
79 self.num_media_packets
80 }
81
82 pub fn mask(&self, fec_index: u32) -> Option<&BitArray> {
84 (fec_index < self.num_fec_packets).then(|| &self.masks[fec_index as usize])
85 }
86
87 pub fn covered_by(&self, fec_index: u32) -> Vec<u32> {
93 let Some(mask) = self.mask(fec_index) else {
94 return Vec::new();
95 };
96 (0..self.num_media_packets)
97 .filter(|&media_index| mask.bit(media_index))
98 .collect()
99 }
100}
101
102#[cfg(test)]
103mod tests {
104 use super::*;
105
106 fn grid(coverage: &ProtectionCoverage) -> Vec<Vec<u32>> {
109 (0..coverage.num_fec_packets())
110 .map(|fec_index| coverage.covered_by(fec_index))
111 .collect()
112 }
113
114 #[test]
115 fn one_repair_packet_covers_everything() {
116 let coverage = ProtectionCoverage::new(5, 1).expect("valid shape");
117 assert_eq!(vec![vec![0, 1, 2, 3, 4]], grid(&coverage));
118 }
119
120 #[test]
123 fn repair_packets_interleave_rather_than_taking_contiguous_blocks() {
124 let coverage = ProtectionCoverage::new(6, 2).expect("valid shape");
125 assert_eq!(vec![vec![0, 2, 4], vec![1, 3, 5]], grid(&coverage));
126
127 let coverage = ProtectionCoverage::new(7, 3).expect("valid shape");
128 assert_eq!(vec![vec![0, 3, 6], vec![1, 4], vec![2, 5]], grid(&coverage));
129 }
130
131 #[test]
132 fn every_media_packet_is_covered_exactly_once() {
133 for num_media in 1..=20u32 {
134 for num_fec in 1..=5u32 {
135 let coverage = ProtectionCoverage::new(num_media, num_fec).expect("valid shape");
136 let mut covered: Vec<u32> = grid(&coverage).into_iter().flatten().collect();
137 covered.sort_unstable();
138
139 assert_eq!(
140 (0..num_media).collect::<Vec<_>>(),
141 covered,
142 "{num_media} media packets across {num_fec} repair packets"
143 );
144 }
145 }
146 }
147
148 #[test]
151 fn surplus_repair_packets_cover_nothing() {
152 let coverage = ProtectionCoverage::new(2, 4).expect("valid shape");
153 assert_eq!(vec![vec![0], vec![1], vec![], vec![]], grid(&coverage));
154 }
155
156 #[test]
157 fn an_impossible_shape_is_rejected() {
158 assert!(
159 ProtectionCoverage::new(0, 1).is_none(),
160 "nothing to protect"
161 );
162 assert!(
163 ProtectionCoverage::new(MAX_MEDIA_PACKETS + 1, 1).is_none(),
164 "beyond what the packet masks can describe"
165 );
166 assert!(ProtectionCoverage::new(MAX_MEDIA_PACKETS, 1).is_some());
167 }
168
169 #[test]
170 fn the_full_range_reaches_the_last_mask() {
171 let coverage = ProtectionCoverage::new(MAX_MEDIA_PACKETS, 1).expect("valid shape");
172 let mask = coverage.mask(0).expect("one repair packet");
173
174 assert!(mask.bit(0), "the first media packet");
175 assert!(mask.bit(MAX_MEDIA_PACKETS - 1), "and the 110th");
176 assert_ne!(0, mask.mask3(), "which only the third mask can describe");
177 }
178
179 #[test]
180 fn updating_to_the_same_shape_changes_nothing() {
181 let mut coverage = ProtectionCoverage::new(6, 2).expect("valid shape");
182 let before = grid(&coverage);
183
184 coverage.update(6, 2);
185 assert_eq!(before, grid(&coverage));
186
187 coverage.update(4, 2);
188 assert_eq!(
189 vec![vec![0, 2], vec![1, 3]],
190 grid(&coverage),
191 "a different shape does recompute"
192 );
193 }
194
195 #[test]
196 fn an_impossible_update_leaves_the_previous_coverage_intact() {
197 let mut coverage = ProtectionCoverage::new(6, 2).expect("valid shape");
198 let before = grid(&coverage);
199
200 coverage.update(0, 2);
201 coverage.update(MAX_MEDIA_PACKETS + 1, 2);
202
203 assert_eq!(before, grid(&coverage));
204 }
205
206 #[test]
207 fn asking_about_a_repair_packet_that_does_not_exist_yields_nothing() {
208 let coverage = ProtectionCoverage::new(5, 2).expect("valid shape");
209 assert!(coverage.mask(2).is_none());
210 assert!(coverage.covered_by(2).is_empty());
211 assert!(coverage.covered_by(u32::MAX).is_empty());
212 }
213}