1use crate::flexfec::bit_array::BitArray;
4use crate::flexfec::coverage::ProtectionCoverage;
5use shared::marshal::{Marshal, MarshalSize};
6
7pub(crate) const BASE_RTP_HEADER_SIZE: usize = 12;
9
10pub(crate) const BASE_HEADER_SIZE: usize = 20;
13
14const MASK2_SIZE: usize = 4;
16
17const MASK3_SIZE: usize = 8;
19
20#[derive(Debug)]
51pub struct FlexFec03Encoder {
52 payload_type: u8,
53 ssrc: u32,
54 next_sequence_number: u16,
55 coverage: Option<ProtectionCoverage>,
56}
57
58impl FlexFec03Encoder {
59 pub fn new(payload_type: u8, ssrc: u32) -> Self {
64 Self {
65 payload_type,
66 ssrc,
67 next_sequence_number: 0,
68 coverage: None,
69 }
70 }
71
72 pub fn with_base_sequence_number(mut self, sequence_number: u16) -> Self {
74 self.next_sequence_number = sequence_number;
75 self
76 }
77
78 pub fn next_sequence_number(&self) -> u16 {
80 self.next_sequence_number
81 }
82
83 pub fn encode(
90 &mut self,
91 media_packets: &[rtp::Packet],
92 num_fec_packets: u32,
93 ) -> Vec<rtp::Packet> {
94 if media_packets.is_empty() || num_fec_packets == 0 {
95 return Vec::new();
96 }
97
98 let consecutive = media_packets.windows(2).all(|pair| {
99 pair[1].header.sequence_number == pair[0].header.sequence_number.wrapping_add(1)
100 });
101 if !consecutive {
102 return Vec::new();
103 }
104
105 let num_media_packets = media_packets.len() as u32;
106 match &mut self.coverage {
107 Some(coverage) => coverage.update(num_media_packets, num_fec_packets),
108 None => match ProtectionCoverage::new(num_media_packets, num_fec_packets) {
109 Some(coverage) => self.coverage = Some(coverage),
110 None => return Vec::new(),
111 },
112 }
113 let Some(coverage) = &self.coverage else {
114 return Vec::new();
115 };
116 if coverage.num_media_packets() != num_media_packets {
117 return Vec::new();
119 }
120
121 let num_fec_packets = coverage.num_fec_packets();
122 let base_sequence_number = media_packets[0].header.sequence_number;
123 let mut repair_packets = Vec::with_capacity(num_fec_packets as usize);
124 for fec_index in 0..num_fec_packets {
125 if let Some(packet) = self.encode_one(fec_index, base_sequence_number, media_packets) {
126 repair_packets.push(packet);
127 }
128 }
129 repair_packets
130 }
131
132 fn encode_one(
133 &mut self,
134 fec_index: u32,
135 base_sequence_number: u16,
136 media_packets: &[rtp::Packet],
137 ) -> Option<rtp::Packet> {
138 let coverage = self.coverage.as_ref()?;
139 let covered = coverage.covered_by(fec_index);
140 if covered.is_empty() {
141 return None;
143 }
144 let mask = *coverage.mask(fec_index)?;
145
146 let mask2 = mask.mask2();
147 let mask3 = mask.mask3_draft03();
148 let header_size = BASE_HEADER_SIZE
149 + if mask2 != 0 || mask3 != 0 {
150 MASK2_SIZE
151 } else {
152 0
153 }
154 + if mask3 != 0 { MASK3_SIZE } else { 0 };
155
156 let max_payload = covered
159 .iter()
160 .map(|&index| media_packets[index as usize].marshal_size() - BASE_RTP_HEADER_SIZE)
161 .max()?;
162
163 let mut payload = vec![0u8; header_size + max_payload];
164 let (header, repair) = payload.split_at_mut(header_size);
165
166 let mut protected_ssrc = None;
167 for &index in &covered {
168 let media_packet = &media_packets[index as usize];
169 let size = media_packet.marshal_size();
170 let mut buffer = vec![0u8; size];
171 media_packet.marshal_to(&mut buffer).ok()?;
172
173 protected_ssrc.get_or_insert(media_packet.header.ssrc);
174
175 header[0] ^= buffer[0];
178 header[1] ^= buffer[1];
179 header[0] &= 0b0011_1111;
181
182 let length_recovery = (size - BASE_RTP_HEADER_SIZE) as u16;
183 header[2] ^= (length_recovery >> 8) as u8;
184 header[3] ^= length_recovery as u8;
185
186 for byte in 4..8 {
190 header[byte] ^= buffer[byte];
191 }
192
193 for (target, &source) in repair.iter_mut().zip(&buffer[BASE_RTP_HEADER_SIZE..]) {
194 *target ^= source;
195 }
196 }
197
198 header[8] = 1; header[9..12].fill(0); header[12..16].copy_from_slice(&protected_ssrc?.to_be_bytes());
201 header[16..18].copy_from_slice(&base_sequence_number.to_be_bytes());
202 header[18..20].copy_from_slice(&mask.mask1().to_be_bytes());
203
204 if mask2 == 0 && mask3 == 0 {
207 header[18] |= 0b1000_0000;
208 } else {
209 header[20..24].copy_from_slice(&mask2.to_be_bytes());
210 if mask3 == 0 {
211 header[20] |= 0b1000_0000;
212 } else {
213 header[24..32].copy_from_slice(&mask3.to_be_bytes());
214 header[24] |= 0b1000_0000;
215 }
216 }
217
218 let sequence_number = self.next_sequence_number;
219 self.next_sequence_number = self.next_sequence_number.wrapping_add(1);
220
221 Some(rtp::Packet {
222 header: rtp::header::Header {
223 version: 2,
224 payload_type: self.payload_type,
225 sequence_number,
226 timestamp: media_packets[covered[0] as usize].header.timestamp,
230 ssrc: self.ssrc,
231 csrc: Vec::new(),
232 ..Default::default()
233 },
234 payload: payload.into(),
235 })
236 }
237}
238
239pub(crate) fn parse_packet_mask(header: &[u8]) -> Option<(BitArray, usize)> {
243 if header.len() < BASE_HEADER_SIZE {
244 return None;
245 }
246
247 let mut mask = BitArray::new();
248 let mask1 = u16::from_be_bytes([header[18] & 0b0111_1111, header[19]]);
249 for bit in 0..15 {
250 if mask1 & (1 << (14 - bit)) != 0 {
251 mask.set_bit(bit);
252 }
253 }
254 if header[18] & 0b1000_0000 != 0 {
255 return Some((mask, BASE_HEADER_SIZE));
256 }
257
258 if header.len() < BASE_HEADER_SIZE + MASK2_SIZE {
259 return None;
260 }
261 let mask2 = u32::from_be_bytes([header[20] & 0b0111_1111, header[21], header[22], header[23]]);
262 for bit in 0..31 {
263 if mask2 & (1 << (30 - bit)) != 0 {
264 mask.set_bit(15 + bit);
265 }
266 }
267 if header[20] & 0b1000_0000 != 0 {
268 return Some((mask, BASE_HEADER_SIZE + MASK2_SIZE));
269 }
270
271 if header.len() < BASE_HEADER_SIZE + MASK2_SIZE + MASK3_SIZE {
272 return None;
273 }
274 let mut mask3_bytes = [0u8; 8];
275 mask3_bytes.copy_from_slice(&header[24..32]);
276 mask3_bytes[0] &= 0b0111_1111;
277 let mask3 = u64::from_be_bytes(mask3_bytes);
278 for bit in 0..63 {
279 if mask3 & (1 << (62 - bit)) != 0 {
280 mask.set_bit(46 + bit);
281 }
282 }
283 Some((mask, BASE_HEADER_SIZE + MASK2_SIZE + MASK3_SIZE))
284}
285
286#[cfg(test)]
287mod tests {
288 use super::*;
289
290 const MEDIA_SSRC: u32 = 476_325_762;
291 const REPAIR_SSRC: u32 = 867_589_674;
292 const REPAIR_PT: u8 = 49;
293
294 fn media_packet(sequence_number: u16, payload: &[u8]) -> rtp::Packet {
295 rtp::Packet {
296 header: rtp::header::Header {
297 version: 2,
298 marker: true,
299 payload_type: 96,
300 sequence_number,
301 timestamp: 3_653_407_706,
302 ssrc: MEDIA_SSRC,
303 ..Default::default()
304 },
305 payload: payload.to_vec().into(),
306 }
307 }
308
309 fn run(count: u16) -> Vec<rtp::Packet> {
310 (0..count)
311 .map(|i| media_packet(100 + i, &[1, 2, 3, 4, 5, i as u8]))
312 .collect()
313 }
314
315 fn encoder() -> FlexFec03Encoder {
316 FlexFec03Encoder::new(REPAIR_PT, REPAIR_SSRC)
317 }
318
319 #[test]
322 fn repair_packets_form_their_own_stream() {
323 let mut encoder = encoder().with_base_sequence_number(1000);
324 let repair = encoder.encode(&run(4), 2);
325
326 assert_eq!(2, repair.len());
327 for (offset, packet) in repair.iter().enumerate() {
328 assert_eq!(REPAIR_SSRC, packet.header.ssrc, "not the media SSRC");
329 assert_eq!(REPAIR_PT, packet.header.payload_type);
330 assert_eq!(1000 + offset as u16, packet.header.sequence_number);
331 assert_eq!(2, packet.header.version);
332 }
333 assert_eq!(1002, encoder.next_sequence_number());
334 }
335
336 #[test]
340 fn a_block_with_a_gap_is_refused() {
341 let mut packets = run(3);
342 packets[2].header.sequence_number = 105; assert!(encoder().encode(&packets, 1).is_empty());
345 }
346
347 #[test]
348 fn an_out_of_order_block_is_refused() {
349 let mut packets = run(2);
350 packets.swap(0, 1);
351
352 assert!(encoder().encode(&packets, 1).is_empty());
353 }
354
355 #[test]
356 fn a_block_that_wraps_the_sequence_space_is_accepted() {
357 let packets = vec![
358 media_packet(65534, &[1]),
359 media_packet(65535, &[2]),
360 media_packet(0, &[3]),
361 ];
362 assert_eq!(
363 1,
364 encoder().encode(&packets, 1).len(),
365 "0 follows 65535: consecutive, not a gap"
366 );
367 }
368
369 #[test]
370 fn nothing_to_protect_produces_nothing() {
371 assert!(encoder().encode(&[], 1).is_empty());
372 assert!(encoder().encode(&run(3), 0).is_empty());
373 }
374
375 #[test]
378 fn surplus_repair_packets_are_not_emitted() {
379 let repair = encoder().encode(&run(2), 4);
380 assert_eq!(
381 2,
382 repair.len(),
383 "two media packets can back two repair packets"
384 );
385 }
386
387 #[test]
388 fn a_block_longer_than_the_masks_can_describe_is_refused() {
389 let packets = run(crate::flexfec::coverage::MAX_MEDIA_PACKETS as u16 + 1);
390 assert!(
391 encoder().encode(&packets, 1).is_empty(),
392 "the caller must split the block"
393 );
394 }
395
396 #[test]
401 fn the_header_names_the_stream_and_block_it_protects() {
402 let repair = encoder().encode(&run(4), 1);
403 let payload = &repair[0].payload;
404
405 assert_eq!(1, payload[8], "SSRCCount: draft-03 protects one stream");
406 assert_eq!(&[0, 0, 0], &payload[9..12], "reserved");
407 assert_eq!(
408 MEDIA_SSRC.to_be_bytes(),
409 payload[12..16],
410 "the protected stream"
411 );
412 assert_eq!(
413 100u16.to_be_bytes(),
414 payload[16..18],
415 "the block's base sequence number"
416 );
417 }
418
419 #[test]
421 fn a_short_block_carries_one_mask_with_the_k_bit_set() {
422 let repair = encoder().encode(&run(4), 1);
423 let payload = &repair[0].payload;
424
425 assert_eq!(
426 BASE_HEADER_SIZE + 6,
427 payload.len(),
428 "20-byte header plus the longest protected payload"
429 );
430 assert_ne!(0, payload[18] & 0b1000_0000, "k-bit set: no further masks");
431
432 let (mask, header_size) = parse_packet_mask(payload).expect("parses");
433 assert_eq!(BASE_HEADER_SIZE, header_size);
434 assert_eq!(
435 vec![true, true, true, true],
436 (0..4).map(|bit| mask.bit(bit)).collect::<Vec<_>>(),
437 "all four media packets covered"
438 );
439 }
440
441 #[test]
442 fn a_longer_block_adds_the_second_mask() {
443 let repair = encoder().encode(&run(20), 1);
444 let payload = &repair[0].payload;
445
446 assert_eq!(
447 0,
448 payload[18] & 0b1000_0000,
449 "k-bit clear: another mask follows"
450 );
451 assert_ne!(0, payload[20] & 0b1000_0000, "and that one is the last");
452
453 let (mask, header_size) = parse_packet_mask(payload).expect("parses");
454 assert_eq!(BASE_HEADER_SIZE + MASK2_SIZE, header_size);
455 for bit in 0..20 {
456 assert!(mask.bit(bit), "media packet {bit} covered");
457 }
458 assert!(!mask.bit(20), "and nothing beyond the block");
459 }
460
461 #[test]
462 fn a_block_beyond_46_packets_adds_the_third_mask() {
463 let repair = encoder().encode(&run(60), 1);
464 let payload = &repair[0].payload;
465
466 assert_eq!(0, payload[18] & 0b1000_0000);
467 assert_eq!(0, payload[20] & 0b1000_0000);
468 assert_ne!(0, payload[24] & 0b1000_0000, "the third mask is the last");
469
470 let (mask, header_size) = parse_packet_mask(payload).expect("parses");
471 assert_eq!(BASE_HEADER_SIZE + MASK2_SIZE + MASK3_SIZE, header_size);
472 for bit in 0..60 {
473 assert!(mask.bit(bit), "media packet {bit} covered");
474 }
475 }
476
477 #[test]
480 fn the_declared_mask_matches_the_interleaved_coverage() {
481 let repair = encoder().encode(&run(6), 2);
482 assert_eq!(2, repair.len());
483
484 let (first, _) = parse_packet_mask(&repair[0].payload).expect("parses");
485 let (second, _) = parse_packet_mask(&repair[1].payload).expect("parses");
486
487 assert_eq!(
488 vec![0, 2, 4],
489 (0..6).filter(|&bit| first.bit(bit)).collect::<Vec<_>>()
490 );
491 assert_eq!(
492 vec![1, 3, 5],
493 (0..6).filter(|&bit| second.bit(bit)).collect::<Vec<_>>()
494 );
495 }
496
497 #[test]
505 fn recovery_fields_are_the_xor_of_the_protected_packets() {
506 let media = run(4);
507 let repair = encoder().encode(&media, 1);
508 let payload = &repair[0].payload;
509
510 let mut expected = [0u8; 8];
511 let mut expected_length = 0u16;
512 for packet in &media {
513 let mut buffer = vec![0u8; packet.marshal_size()];
514 packet.marshal_to(&mut buffer).expect("marshal");
515 expected[0] ^= buffer[0];
516 expected[1] ^= buffer[1];
517 for byte in 4..8 {
518 expected[byte] ^= buffer[byte];
519 }
520 expected_length ^= (packet.marshal_size() - BASE_RTP_HEADER_SIZE) as u16;
521 }
522 expected[0] &= 0b0011_1111;
523
524 assert_eq!(expected[0], payload[0], "flags and CC recovery");
525 assert_eq!(expected[1], payload[1], "marker and payload type recovery");
526 assert_eq!(
527 expected_length.to_be_bytes(),
528 payload[2..4],
529 "length recovery"
530 );
531 assert_eq!(expected[4..8], payload[4..8], "timestamp recovery");
532 }
533
534 #[test]
535 fn the_repair_payload_is_the_xor_of_the_protected_payloads() {
536 let media = run(4);
537 let repair = encoder().encode(&media, 1);
538 let header_size = parse_packet_mask(&repair[0].payload).expect("parses").1;
539 let repair_payload = &repair[0].payload[header_size..];
540
541 let mut expected = vec![0u8; media.iter().map(|p| p.payload.len()).max().unwrap()];
542 for packet in &media {
543 for (target, &source) in expected.iter_mut().zip(packet.payload.iter()) {
544 *target ^= source;
545 }
546 }
547
548 assert_eq!(expected.as_slice(), repair_payload);
549 }
550
551 #[test]
554 fn the_repair_payload_is_as_long_as_the_largest_protected_packet() {
555 let media = vec![
556 media_packet(1, &[1, 2, 3]),
557 media_packet(2, &[1, 2, 3, 4, 5, 6, 7, 8]),
558 media_packet(3, &[9]),
559 ];
560 let repair = encoder().encode(&media, 1);
561 let header_size = parse_packet_mask(&repair[0].payload).expect("parses").1;
562
563 assert_eq!(8, repair[0].payload.len() - header_size);
564 }
565
566 #[test]
569 fn the_version_bits_are_not_recovered() {
570 let repair = encoder().encode(&run(3), 1);
571 assert_eq!(
572 0,
573 repair[0].payload[0] & 0b1100_0000,
574 "the two version bits are zeroed"
575 );
576 }
577}