Skip to content
File

Blob: firmware/vendor/str0m/tests/stale-network-time-after-pause.rs

rust517 lines
1use std::net::Ipv4Addr;
2use std::time::{Duration, Instant};
3 
4use str0m::format::Codec;
5use str0m::media::MediaKind;
6use str0m::net::Receive;
7use str0m::rtp::{RtpWrite, Ssrc};
8use str0m::{Candidate, Event, Input, Output, Rtc, RtcError};
9 
10mod common;
11use common::{Peer, PendingPacket, TestRtc, init_crypto_default, init_log, progress};
12 
13fn connect_direct() -> Result<(TestRtc, TestRtc), RtcError> {
14 let now = Instant::now();
15 let mut l = TestRtc::new_with_rtc(Peer::Left.span(), Rtc::new(now));
16 let mut r = TestRtc::new_with_rtc(
17 Peer::Right.span(),
18 Rtc::builder().set_reordering_size_video(10).build(now),
19 );
20 
21 let host1 = Candidate::host((Ipv4Addr::new(1, 1, 1, 1), 1000).into(), "udp")?.clone();
22 let host2 = Candidate::host((Ipv4Addr::new(2, 2, 2, 2), 2000).into(), "udp")?.clone();
23 l.add_local_candidate(host1.clone());
24 l.add_remote_candidate(host2.clone());
25 r.add_local_candidate(host2);
26 r.add_remote_candidate(host1);
27 
28 let finger_l = l.direct_api().local_dtls_fingerprint().clone();
29 let finger_r = r.direct_api().local_dtls_fingerprint().clone();
30 l.direct_api().set_remote_fingerprint(finger_r);
31 r.direct_api().set_remote_fingerprint(finger_l);
32 
33 let creds_l = l.direct_api().local_ice_credentials();
34 let creds_r = r.direct_api().local_ice_credentials();
35 l.direct_api().set_remote_ice_credentials(creds_r);
36 r.direct_api().set_remote_ice_credentials(creds_l);
37 
38 l.direct_api().set_ice_controlling(true);
39 r.direct_api().set_ice_controlling(false);
40 
41 l.direct_api().start_dtls(true)?;
42 r.direct_api().start_dtls(false)?;
43 l.direct_api().start_sctp(true);
44 r.direct_api().start_sctp(false);
45 
46 while !l.is_connected() || !r.is_connected() {
47 progress(&mut l, &mut r)?;
48 }
49 
50 Ok((l, r))
51}
52 
53fn flush_transmits(rtc: &mut TestRtc, time: Instant) -> Result<Vec<PendingPacket>, RtcError> {
54 let mut packets = Vec::new();
55 
56 rtc.rtc.handle_input(Input::Timeout(time))?;
57 
58 loop {
59 match rtc.rtc.poll_output()? {
60 Output::Timeout(v) => {
61 let tick = rtc.last + rtc.forced_time_advance;
62 rtc.last = if v == rtc.last { tick } else { tick.min(v) };
63 break;
64 }
65 Output::Transmit(v) => {
66 packets.push(PendingPacket {
67 proto: v.proto,
68 source: v.source,
69 destination: v.destination,
70 contents: v.contents.to_vec(),
71 });
72 }
73 Output::Event(v) => rtc.events.push((time, v)),
74 }
75 }
76 
77 Ok(packets)
78}
79 
80fn wait_for_transmits(
81 rtc: &mut TestRtc,
82 earliest: Instant,
83 max_wait: Duration,
84) -> Result<(Instant, Vec<PendingPacket>), RtcError> {
85 let deadline = earliest + max_wait;
86 
87 loop {
88 let time = rtc.last.max(earliest);
89 let packets = flush_transmits(rtc, time)?;
90 
91 if !packets.is_empty() {
92 return Ok((time, packets));
93 }
94 
95 assert!(
96 time < deadline,
97 "expected at least one packet by {:?}, but sender emitted none",
98 deadline.saturating_duration_since(earliest)
99 );
100 }
101}
102 
103fn deliver_packet(rtc: &mut TestRtc, time: Instant, packet: PendingPacket) -> Result<(), RtcError> {
104 let input = Input::Receive(
105 time,
106 Receive {
107 proto: packet.proto,
108 source: packet.source,
109 destination: packet.destination,
110 contents: (&packet.contents[..]).try_into()?,
111 },
112 );
113 
114 rtc.rtc.handle_input(input)?;
115 
116 loop {
117 match rtc.rtc.poll_output()? {
118 Output::Timeout(v) => {
119 let tick = rtc.last + rtc.forced_time_advance;
120 rtc.last = if v == rtc.last { tick } else { tick.min(v) };
121 break;
122 }
123 Output::Transmit(_) => {}
124 Output::Event(v) => rtc.events.push((time, v)),
125 }
126 }
127 
128 Ok(())
129}
130 
131fn measure_fresh_age_after_pause(pause_duration: Duration) -> Result<Duration, RtcError> {
132 let (mut l, mut r) = connect_direct()?;
133 
134 let mid = "vid".into();
135 let ssrc: Ssrc = 42.into();
136 
137 l.direct_api().declare_media(mid, MediaKind::Video);
138 l.direct_api().declare_stream_tx(ssrc, None, mid, None);
139 
140 r.direct_api().declare_media(mid, MediaKind::Video);
141 r.direct_api().expect_stream_rx(ssrc, None, mid, None);
142 
143 let max = l.last.max(r.last);
144 l.last = max;
145 r.last = max;
146 
147 let pt = l.params_vp8().pt();
148 assert_eq!(l.params_vp8().spec().codec, Codec::Vp8);
149 
150 let frame_timestamp = 90_000u32;
151 let first_write_at = l.last + Duration::from_millis(20);
152 
153 {
154 let mut direct = l.direct_api();
155 let tx = direct.stream_tx_by_mid(mid, None).unwrap();
156 tx.write_rtp(
157 RtpWrite::new(
158 pt,
159 10_000u64.into(),
160 frame_timestamp,
161 first_write_at,
162 [0x10, 0x00, 0xAA, 0xBB],
163 )
164 .nackable(true),
165 );
166 }
167 let (first_emit_at, mut first_packets) =
168 wait_for_transmits(&mut l, first_write_at, Duration::from_millis(50))?;
169 assert_eq!(first_packets.len(), 1, "expected one first-fragment packet");
170 deliver_packet(&mut r, first_emit_at, first_packets.remove(0))?;
171 
172 {
173 let mut direct = l.direct_api();
174 let tx = direct.stream_tx_by_mid(mid, None).unwrap();
175 tx.write_rtp(
176 RtpWrite::new(
177 pt,
178 10_001u64.into(),
179 frame_timestamp,
180 first_write_at + Duration::from_millis(5),
181 [0x00, 0xCC, 0xDD],
182 )
183 .marker(true)
184 .nackable(true),
185 );
186 }
187 let (_, delayed_packets) = wait_for_transmits(
188 &mut l,
189 first_write_at + Duration::from_millis(5),
190 Duration::from_millis(50),
191 )?;
192 assert!(
193 !delayed_packets.is_empty(),
194 "expected at least one packet when flushing delayed tail"
195 );
196 
197 let pause_end = l.last + pause_duration;
198 while l.last < pause_end || r.last < pause_end {
199 progress(&mut l, &mut r)?;
200 }
201 
202 assert!(
203 r.events
204 .iter()
205 .any(|(_, e)| matches!(e, Event::StreamPaused(p) if p.paused)),
206 "receiver never entered paused state"
207 );
208 
209 let before_tail_event_count = r.events.len();
210 let delivery_time = pause_end + Duration::from_millis(20);
211 for packet in delayed_packets {
212 deliver_packet(&mut r, delivery_time, packet)?;
213 }
214 
215 assert!(
216 !r.events[before_tail_event_count..]
217 .iter()
218 .any(|(_, e)| matches!(e, Event::MediaData(_))),
219 "did not expect stale MediaData from the delayed pre-pause tail"
220 );
221 
222 let fresh_first_at = delivery_time + Duration::from_millis(20);
223 let fresh_frame_timestamp = frame_timestamp + 3_000;
224 
225 {
226 let mut direct = l.direct_api();
227 let tx = direct.stream_tx_by_mid(mid, None).unwrap();
228 tx.write_rtp(
229 RtpWrite::new(
230 pt,
231 10_002u64.into(),
232 fresh_frame_timestamp,
233 fresh_first_at,
234 [0x10, 0x00, 0x11, 0x22],
235 )
236 .nackable(true),
237 );
238 }
239 let (fresh_first_emit_at, mut fresh_first_packets) =
240 wait_for_transmits(&mut l, fresh_first_at, Duration::from_millis(50))?;
241 assert_eq!(
242 fresh_first_packets.len(),
243 1,
244 "expected one fresh first-fragment packet"
245 );
246 deliver_packet(&mut r, fresh_first_emit_at, fresh_first_packets.remove(0))?;
247 
248 {
249 let mut direct = l.direct_api();
250 let tx = direct.stream_tx_by_mid(mid, None).unwrap();
251 tx.write_rtp(
252 RtpWrite::new(
253 pt,
254 10_003u64.into(),
255 fresh_frame_timestamp,
256 fresh_first_at + Duration::from_millis(5),
257 [0x00, 0x33, 0x44],
258 )
259 .marker(true)
260 .nackable(true),
261 );
262 }
263 let (fresh_tail_emit_at, fresh_tail_packets) = wait_for_transmits(
264 &mut l,
265 fresh_first_at + Duration::from_millis(5),
266 Duration::from_millis(50),
267 )?;
268 let before_fresh_event_count = r.events.len();
269 for packet in fresh_tail_packets {
270 deliver_packet(&mut r, fresh_tail_emit_at, packet)?;
271 }
272 
273 let media = r.events[before_fresh_event_count..]
274 .iter()
275 .find_map(|(event_time, e)| match e {
276 Event::MediaData(data) => Some((*event_time, data)),
277 _ => None,
278 })
279 .expect("expected MediaData from the fresh post-pause frame");
280 
281 let age = media.0.saturating_duration_since(media.1.network_time);
282 
283 assert_eq!(*media.1.seq_range.start(), 10_002u64.into());
284 assert_eq!(*media.1.seq_range.end(), 10_003u64.into());
285 
286 Ok(age)
287}
288 
289fn collect_fresh_ages_after_repeated_pauses(
290 pause_durations: &[Duration],
291) -> Result<Vec<Duration>, RtcError> {
292 let (mut l, mut r) = connect_direct()?;
293 
294 let mid = "vid".into();
295 let ssrc: Ssrc = 42.into();
296 
297 l.direct_api().declare_media(mid, MediaKind::Video);
298 l.direct_api().declare_stream_tx(ssrc, None, mid, None);
299 
300 r.direct_api().declare_media(mid, MediaKind::Video);
301 r.direct_api().expect_stream_rx(ssrc, None, mid, None);
302 
303 let max = l.last.max(r.last);
304 l.last = max;
305 r.last = max;
306 
307 let pt = l.params_vp8().pt();
308 assert_eq!(l.params_vp8().spec().codec, Codec::Vp8);
309 
310 let mut fresh_ages = Vec::new();
311 
312 for (i, pause_duration) in pause_durations.iter().copied().enumerate() {
313 let seq = 20_000u64 + (i as u64) * 2;
314 let frame_timestamp = 90_000u32 + (i as u32) * 3_000;
315 let first_write_at = l.last + Duration::from_millis(20);
316 
317 {
318 let mut direct = l.direct_api();
319 let tx = direct.stream_tx_by_mid(mid, None).unwrap();
320 tx.write_rtp(
321 RtpWrite::new(
322 pt,
323 seq.into(),
324 frame_timestamp,
325 first_write_at,
326 [0x10, 0x00, 0xAA, 0xBB, i as u8],
327 )
328 .nackable(true),
329 );
330 }
331 let (first_emit_at, mut first_packets) =
332 wait_for_transmits(&mut l, first_write_at, Duration::from_millis(50))?;
333 assert_eq!(first_packets.len(), 1, "expected one first-fragment packet");
334 deliver_packet(&mut r, first_emit_at, first_packets.remove(0))?;
335 
336 {
337 let mut direct = l.direct_api();
338 let tx = direct.stream_tx_by_mid(mid, None).unwrap();
339 tx.write_rtp(
340 RtpWrite::new(
341 pt,
342 (seq + 1).into(),
343 frame_timestamp,
344 first_write_at + Duration::from_millis(5),
345 [0x00, 0xCC, 0xDD, i as u8],
346 )
347 .marker(true)
348 .nackable(true),
349 );
350 }
351 let (_, packets) = wait_for_transmits(
352 &mut l,
353 first_write_at + Duration::from_millis(5),
354 Duration::from_millis(50),
355 )?;
356 assert!(
357 !packets.is_empty(),
358 "expected at least one packet when flushing delayed tail"
359 );
360 let pause_end = l.last + pause_duration;
361 while l.last < pause_end || r.last < pause_end {
362 progress(&mut l, &mut r)?;
363 }
364 
365 assert!(
366 r.events
367 .iter()
368 .any(|(_, e)| matches!(e, Event::StreamPaused(p) if p.paused)),
369 "receiver never entered paused state"
370 );
371 
372 let before_tail_event_count = r.events.len();
373 let delivery_time = pause_end + Duration::from_millis(20);
374 for packet in packets {
375 deliver_packet(&mut r, delivery_time, packet)?;
376 }
377 
378 assert_eq!(
379 r.events[before_tail_event_count..]
380 .iter()
381 .filter(|(_, e)| matches!(e, Event::MediaData(_)))
382 .count(),
383 0,
384 "did not expect stale MediaData from delayed pre-pause packets"
385 );
386 
387 let fresh_first_at = delivery_time + Duration::from_millis(20);
388 let fresh_frame_timestamp = frame_timestamp + 3_000;
389 let fresh_seq = 30_000u64 + (i as u64) * 2;
390 
391 {
392 let mut direct = l.direct_api();
393 let tx = direct.stream_tx_by_mid(mid, None).unwrap();
394 tx.write_rtp(
395 RtpWrite::new(
396 pt,
397 fresh_seq.into(),
398 fresh_frame_timestamp,
399 fresh_first_at,
400 [0x10, 0x00, 0x55, 0x66, i as u8],
401 )
402 .nackable(true),
403 );
404 }
405 let (fresh_first_emit_at, mut fresh_first_packets) =
406 wait_for_transmits(&mut l, fresh_first_at, Duration::from_millis(50))?;
407 assert_eq!(
408 fresh_first_packets.len(),
409 1,
410 "expected one fresh first-fragment packet"
411 );
412 deliver_packet(&mut r, fresh_first_emit_at, fresh_first_packets.remove(0))?;
413 
414 {
415 let mut direct = l.direct_api();
416 let tx = direct.stream_tx_by_mid(mid, None).unwrap();
417 tx.write_rtp(
418 RtpWrite::new(
419 pt,
420 (fresh_seq + 1).into(),
421 fresh_frame_timestamp,
422 fresh_first_at + Duration::from_millis(5),
423 [0x00, 0x77, 0x88, i as u8],
424 )
425 .marker(true)
426 .nackable(true),
427 );
428 }
429 let (fresh_tail_emit_at, fresh_tail_packets) = wait_for_transmits(
430 &mut l,
431 fresh_first_at + Duration::from_millis(5),
432 Duration::from_millis(50),
433 )?;
434 let before_fresh_event_count = r.events.len();
435 for packet in fresh_tail_packets {
436 deliver_packet(&mut r, fresh_tail_emit_at, packet)?;
437 }
438 
439 let (event_time, media) = r.events[before_fresh_event_count..]
440 .iter()
441 .find_map(|(event_time, e)| match e {
442 Event::MediaData(data) => Some((*event_time, data)),
443 _ => None,
444 })
445 .expect("expected MediaData from the fresh post-pause frame");
446 
447 assert_eq!(*media.seq_range.start(), fresh_seq.into());
448 assert_eq!(*media.seq_range.end(), (fresh_seq + 1).into());
449 fresh_ages.push(event_time.saturating_duration_since(media.network_time));
450 }
451 
452 Ok(fresh_ages)
453}
454 
455#[test]
456fn delayed_vp8_tail_packet_after_pause_is_dropped() -> Result<(), RtcError> {
457 init_log();
458 init_crypto_default();
459 
460 let age = measure_fresh_age_after_pause(Duration::from_millis(2200))?;
461 
462 assert!(
463 age < Duration::from_millis(200),
464 "expected fresh MediaData after pause recovery, got age {:?}",
465 age
466 );
467 
468 Ok(())
469}
470 
471#[test]
472fn longer_pauses_still_recover_with_fresh_media() -> Result<(), RtcError> {
473 init_log();
474 init_crypto_default();
475 
476 let pause_durations = [
477 Duration::from_millis(2200),
478 Duration::from_millis(4200),
479 Duration::from_millis(8200),
480 ];
481 
482 let observed_ages: Vec<_> = pause_durations
483 .into_iter()
484 .map(measure_fresh_age_after_pause)
485 .collect::<Result<_, _>>()?;
486 
487 assert!(
488 observed_ages
489 .iter()
490 .all(|age| *age < Duration::from_millis(200)),
491 "expected fresh recovery after long pauses, got {:?}",
492 observed_ages
493 );
494 
495 Ok(())
496}
497 
498#[test]
499fn repeated_pause_cycles_recover_without_stale_media() -> Result<(), RtcError> {
500 init_log();
501 init_crypto_default();
502 
503 let ages = collect_fresh_ages_after_repeated_pauses(&[
504 Duration::from_millis(2200),
505 Duration::from_millis(4200),
506 Duration::from_millis(6200),
507 ])?;
508 
509 assert!(
510 ages.iter().all(|age| *age < Duration::from_millis(200)),
511 "expected fresh media after each pause cycle, got {:?}",
512 ages
513 );
514 
515 Ok(())
516}