File
Blob: firmware/vendor/str0m/tests/twcc.rs
| 1 | use std::net::Ipv4Addr; |
| 2 | use std::time::{Duration, Instant}; |
| 3 | |
| 4 | use str0m::format::Codec; |
| 5 | use str0m::media::{Direction, MediaKind}; |
| 6 | use str0m::rtp::rtcp::Twcc; |
| 7 | use str0m::{Rtc, RtcError}; |
| 8 | use tracing::info_span; |
| 9 | |
| 10 | mod common; |
| 11 | use common::{TestRtc, init_crypto_default, init_log, negotiate, progress}; |
| 12 | |
| 13 | #[test] |
| 14 | pub fn twcc() -> Result<(), RtcError> { |
| 15 | init_log(); |
| 16 | init_crypto_default(); |
| 17 | |
| 18 | let now = Instant::now(); |
| 19 | let l_rtc = Rtc::builder().enable_raw_packets(true).build(now); |
| 20 | let r_rtc = Rtc::builder().enable_raw_packets(true).build(now); |
| 21 | |
| 22 | let mut l = TestRtc::new_with_rtc(info_span!("L"), l_rtc); |
| 23 | let mut r = TestRtc::new_with_rtc(info_span!("R"), r_rtc); |
| 24 | |
| 25 | l.add_host_candidate((Ipv4Addr::new(1, 1, 1, 1), 1000).into()); |
| 26 | r.add_host_candidate((Ipv4Addr::new(2, 2, 2, 2), 2000).into()); |
| 27 | |
| 28 | let mid = negotiate(&mut l, &mut r, |change| { |
| 29 | change.add_media(MediaKind::Video, Direction::SendOnly, None, None, None) |
| 30 | }); |
| 31 | |
| 32 | loop { |
| 33 | if l.is_connected() || r.is_connected() { |
| 34 | break; |
| 35 | } |
| 36 | progress(&mut l, &mut r)?; |
| 37 | } |
| 38 | |
| 39 | let max = l.last.max(r.last); |
| 40 | l.last = max; |
| 41 | r.last = max; |
| 42 | |
| 43 | let params = l.params_vp8(); |
| 44 | assert_eq!(params.spec().codec, Codec::Vp8); |
| 45 | let pt = params.pt(); |
| 46 | |
| 47 | let data_a = [1_u8; 80]; |
| 48 | |
| 49 | loop { |
| 50 | { |
| 51 | let wallclock = l.start + l.duration(); |
| 52 | let time = l.duration().into(); |
| 53 | l.writer(mid).unwrap().write(pt, wallclock, time, data_a)?; |
| 54 | } |
| 55 | |
| 56 | progress(&mut l, &mut r)?; |
| 57 | |
| 58 | if l.duration() > Duration::from_secs(10) { |
| 59 | break; |
| 60 | } |
| 61 | } |
| 62 | |
| 63 | let (sent_twcc, received_twcc) = { |
| 64 | use str0m::rtp::{RawPacket, rtcp::Rtcp}; |
| 65 | let r_twcc: Vec<_> = r |
| 66 | .events |
| 67 | .iter() |
| 68 | .filter_map(|(_, e)| { |
| 69 | if let Some(RawPacket::RtcpTx(Rtcp::Twcc(twcc))) = e.as_raw_packet() { |
| 70 | Some(twcc) |
| 71 | } else { |
| 72 | None |
| 73 | } |
| 74 | }) |
| 75 | .collect(); |
| 76 | |
| 77 | let l_twcc: Vec<_> = l |
| 78 | .events |
| 79 | .iter() |
| 80 | .filter_map(|(_, e)| { |
| 81 | if let Some(RawPacket::RtcpRx(Rtcp::Twcc(twcc))) = e.as_raw_packet() { |
| 82 | Some(twcc) |
| 83 | } else { |
| 84 | None |
| 85 | } |
| 86 | }) |
| 87 | .collect(); |
| 88 | (r_twcc, l_twcc) |
| 89 | }; |
| 90 | |
| 91 | assert!(!sent_twcc.is_empty(), "Should've sent TWCC"); |
| 92 | assert!( |
| 93 | sent_twcc.len() == received_twcc.len(), |
| 94 | "The number of TWCC packets received should match what was sent" |
| 95 | ); |
| 96 | assert!( |
| 97 | sent_twcc == received_twcc, |
| 98 | "The same TWCC packets that were sent should be received" |
| 99 | ); |
| 100 | |
| 101 | let sent_is_consecutive = sent_twcc |
| 102 | .iter() |
| 103 | .fold((true, None), |(consecutive, last), packet| { |
| 104 | let consecutive = consecutive |
| 105 | && last |
| 106 | .map(|l: &Twcc| l.feedback_count.wrapping_add(1) == packet.feedback_count) |
| 107 | .unwrap_or(true); |
| 108 | |
| 109 | (consecutive, Some(packet)) |
| 110 | }) |
| 111 | .0; |
| 112 | |
| 113 | assert!(!sent_twcc.is_empty(), "Should have sent some TWCC"); |
| 114 | assert!( |
| 115 | sent_is_consecutive, |
| 116 | "Sent TWCC packets should contain no gaps in terms of `feedback_count`" |
| 117 | ); |
| 118 | |
| 119 | Ok(()) |
| 120 | } |