Skip to content
File

Blob: firmware/vendor/str0m/tests/keyframes.rs

rust589 lines
1use std::net::Ipv4Addr;
2use std::time::Duration;
3 
4use str0m::format::{Codec, CodecExtra};
5use str0m::media::{Direction, MediaKind};
6use str0m::rtp::RtpWrite;
7use str0m::{Event, RtcError};
8 
9mod common;
10use common::{Peer, TestRtc, init_log, progress};
11use common::{av1_data, h264_data, h265_data, h266_data, init_crypto_default, vp8_data, vp9_data};
12 
13#[test]
14pub fn test_vp8_keyframes_detection() -> Result<(), RtcError> {
15 init_log();
16 init_crypto_default();
17 
18 let mut l = TestRtc::new(Peer::Left);
19 let mut r = TestRtc::new(Peer::Right);
20 
21 l.add_host_candidate((Ipv4Addr::new(1, 1, 1, 1), 1000).into());
22 r.add_host_candidate((Ipv4Addr::new(2, 2, 2, 2), 2000).into());
23 
24 // The change is on the L (sending side) with Direction::SendRecv.
25 let mut change = l.sdp_api();
26 let mid = change.add_media(MediaKind::Video, Direction::SendOnly, None, None, None);
27 let (offer, pending) = change.apply().unwrap();
28 
29 let answer = r.rtc.sdp_api().accept_offer(offer)?;
30 l.rtc.sdp_api().accept_answer(pending, answer)?;
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 = vp8_data();
48 
49 for (relative, header, payload) in data {
50 // Keep RTC time progressed to be "in sync" with the test data.
51 while (l.last - max) < relative {
52 progress(&mut l, &mut r)?;
53 }
54 
55 let absolute = max + relative;
56 
57 let mut direct = l.direct_api();
58 let tx = direct.stream_tx_by_mid(mid, None).unwrap();
59 tx.write_rtp(
60 RtpWrite::new(
61 pt,
62 header.sequence_number(None),
63 header.timestamp,
64 absolute,
65 payload,
66 )
67 .marker(header.marker)
68 .ext_vals(header.ext_vals)
69 .nackable(true),
70 );
71 
72 progress(&mut l, &mut r)?;
73 
74 if l.duration() > Duration::from_secs(5) {
75 break;
76 }
77 }
78 
79 let iter = r.events.iter().filter_map(|(_, e)| {
80 if let Event::MediaData(d) = e {
81 Some(d)
82 } else {
83 None
84 }
85 });
86 
87 for data in iter {
88 let CodecExtra::Vp8(extra) = data.codec_extra else {
89 panic!("Got non VP8 CodecExtra")
90 };
91 let assume_keyframe = data.seq_range.contains(&14260.into())
92 || data.seq_range.contains(&14262.into())
93 || data.seq_range.contains(&14265.into());
94 if extra.is_keyframe {
95 assert!(assume_keyframe, "Expected keyframe");
96 } else {
97 assert!(!assume_keyframe, "Not expected keyframe");
98 }
99 }
100 
101 Ok(())
102}
103 
104#[test]
105pub fn test_vp9_keyframes_detection() -> Result<(), RtcError> {
106 init_log();
107 init_crypto_default();
108 
109 let mut l = TestRtc::new(Peer::Left);
110 let mut r = TestRtc::new(Peer::Right);
111 
112 l.add_host_candidate((Ipv4Addr::new(1, 1, 1, 1), 1000).into());
113 r.add_host_candidate((Ipv4Addr::new(2, 2, 2, 2), 2000).into());
114 
115 // The change is on the L (sending side) with Direction::SendRecv.
116 let mut change = l.sdp_api();
117 let mid = change.add_media(MediaKind::Video, Direction::SendOnly, None, None, None);
118 let (offer, pending) = change.apply().unwrap();
119 
120 let answer = r.rtc.sdp_api().accept_offer(offer)?;
121 l.rtc.sdp_api().accept_answer(pending, answer)?;
122 
123 loop {
124 if l.is_connected() || r.is_connected() {
125 break;
126 }
127 progress(&mut l, &mut r)?;
128 }
129 
130 let max = l.last.max(r.last);
131 l.last = max;
132 r.last = max;
133 
134 let params = l.params_vp9();
135 assert_eq!(params.spec().codec, Codec::Vp9);
136 let pt = params.pt();
137 
138 let data = vp9_data();
139 
140 for (relative, header, payload) in data {
141 // Keep RTC time progressed to be "in sync" with the test data.
142 while (l.last - max) < relative {
143 progress(&mut l, &mut r)?;
144 }
145 
146 let absolute = max + relative;
147 
148 let mut direct = l.direct_api();
149 let tx = direct.stream_tx_by_mid(mid, None).unwrap();
150 tx.write_rtp(
151 RtpWrite::new(
152 pt,
153 header.sequence_number(None),
154 header.timestamp,
155 absolute,
156 payload,
157 )
158 .marker(header.marker)
159 .ext_vals(header.ext_vals)
160 .nackable(true),
161 );
162 
163 progress(&mut l, &mut r)?;
164 
165 if l.duration() > Duration::from_secs(5) {
166 break;
167 }
168 }
169 
170 let iter = r.events.iter().filter_map(|(_, e)| {
171 if let Event::MediaData(d) = e {
172 Some(d)
173 } else {
174 None
175 }
176 });
177 
178 for data in iter {
179 let CodecExtra::Vp9(extra) = data.codec_extra else {
180 panic!("Got non VP9 CodecExtra")
181 };
182 let assume_keyframe = data.seq_range.contains(&19357.into())
183 || data.seq_range.contains(&20296.into())
184 || data.seq_range.contains(&20301.into())
185 || data.seq_range.contains(&20351.into());
186 if extra.is_keyframe {
187 assert!(assume_keyframe, "Expected keyframe");
188 } else {
189 assert!(!assume_keyframe, "Not expected keyframe");
190 }
191 }
192 
193 Ok(())
194}
195 
196#[test]
197pub fn test_h264_keyframes_detection() -> Result<(), RtcError> {
198 init_log();
199 init_crypto_default();
200 
201 let mut l = TestRtc::new(Peer::Left);
202 let mut r = TestRtc::new(Peer::Right);
203 
204 l.add_host_candidate((Ipv4Addr::new(1, 1, 1, 1), 1000).into());
205 r.add_host_candidate((Ipv4Addr::new(2, 2, 2, 2), 2000).into());
206 
207 // The change is on the L (sending side) with Direction::SendRecv.
208 let mut change = l.sdp_api();
209 let mid = change.add_media(MediaKind::Video, Direction::SendOnly, None, None, None);
210 let (offer, pending) = change.apply().unwrap();
211 
212 let answer = r.rtc.sdp_api().accept_offer(offer)?;
213 l.rtc.sdp_api().accept_answer(pending, answer)?;
214 
215 loop {
216 if l.is_connected() || r.is_connected() {
217 break;
218 }
219 let _ = progress(&mut l, &mut r);
220 }
221 
222 let max = l.last.max(r.last);
223 l.last = max;
224 r.last = max;
225 
226 let params = l.params_h264();
227 assert_eq!(params.spec().codec, Codec::H264);
228 let pt = params.pt();
229 
230 let data = h264_data();
231 
232 for (relative, header, payload) in data {
233 // Keep RTC time progressed to be "in sync" with the test data.
234 while (l.last - max) < relative {
235 let _ = progress(&mut l, &mut r);
236 }
237 
238 let absolute = max + relative;
239 
240 let mut direct = l.direct_api();
241 let tx = direct.stream_tx_by_mid(mid, None).unwrap();
242 tx.write_rtp(
243 RtpWrite::new(
244 pt,
245 header.sequence_number(None),
246 header.timestamp,
247 absolute,
248 payload,
249 )
250 .marker(header.marker)
251 .ext_vals(header.ext_vals)
252 .nackable(true),
253 );
254 
255 let _ = progress(&mut l, &mut r);
256 
257 if l.duration() > Duration::from_secs(5) {
258 break;
259 }
260 }
261 
262 let iter = r.events.iter().filter_map(|(_, e)| {
263 if let Event::MediaData(d) = e {
264 Some(d)
265 } else {
266 None
267 }
268 });
269 
270 for data in iter {
271 let CodecExtra::H264(extra) = data.codec_extra else {
272 panic!("Got non H264 CodecExtra")
273 };
274 let assume_keyframe = data.seq_range.contains(&19249.into())
275 || data.seq_range.contains(&19251.into())
276 || data.seq_range.contains(&19301.into())
277 || data.seq_range.contains(&19351.into())
278 || data.seq_range.contains(&19403.into())
279 || data.seq_range.contains(&19453.into())
280 || data.seq_range.contains(&19503.into());
281 if extra.is_keyframe {
282 assert!(assume_keyframe, "Expected keyframe");
283 } else {
284 assert!(!assume_keyframe, "Not expected keyframe");
285 }
286 }
287 
288 Ok(())
289}
290 
291#[test]
292fn test_av1_keyframes_detection() -> Result<(), RtcError> {
293 init_log();
294 init_crypto_default();
295 
296 let mut l = TestRtc::new(Peer::Left);
297 let mut r = TestRtc::new(Peer::Right);
298 
299 l.add_host_candidate((Ipv4Addr::new(1, 1, 1, 1), 1000).into());
300 r.add_host_candidate((Ipv4Addr::new(2, 2, 2, 2), 2000).into());
301 
302 // The change is on the L (sending side) with Direction::SendOnly.
303 let mut change = l.sdp_api();
304 let mid = change.add_media(MediaKind::Video, Direction::SendOnly, None, None, None);
305 let (offer, pending) = change.apply().unwrap();
306 
307 let answer = r.rtc.sdp_api().accept_offer(offer)?;
308 l.rtc.sdp_api().accept_answer(pending, answer)?;
309 
310 loop {
311 if l.is_connected() || r.is_connected() {
312 break;
313 }
314 progress(&mut l, &mut r)?;
315 }
316 
317 let max = l.last.max(r.last);
318 l.last = max;
319 r.last = max;
320 
321 let params = l.params_av1();
322 assert_eq!(params.spec().codec, Codec::Av1);
323 let pt = params.pt();
324 
325 let data = av1_data();
326 
327 for (relative, header, payload) in data {
328 // Keep RTC time progressed to be "in sync" with the test data.
329 while (l.last - max) < relative {
330 progress(&mut l, &mut r)?;
331 }
332 
333 let absolute = max + relative;
334 
335 let mut direct = l.direct_api();
336 let tx = direct.stream_tx_by_mid(mid, None).unwrap();
337 tx.write_rtp(
338 RtpWrite::new(
339 pt,
340 header.sequence_number(None),
341 header.timestamp,
342 absolute,
343 payload,
344 )
345 .marker(header.marker)
346 .ext_vals(header.ext_vals)
347 .nackable(true),
348 );
349 
350 progress(&mut l, &mut r)?;
351 
352 if l.duration() > Duration::from_secs(5) {
353 break;
354 }
355 }
356 
357 let iter = r.events.iter().filter_map(|(_, e)| {
358 if let Event::MediaData(d) = e {
359 Some(d)
360 } else {
361 None
362 }
363 });
364 
365 for data in iter {
366 let CodecExtra::Av1(extra) = data.codec_extra else {
367 panic!("Got non AV1 CodecExtra")
368 };
369 let assume_keyframe =
370 data.seq_range.contains(&7486.into()) || data.seq_range.contains(&7485.into());
371 if extra.is_keyframe {
372 assert!(assume_keyframe, "Expected keyframe");
373 } else {
374 assert!(!assume_keyframe, "Not expected keyframe");
375 }
376 }
377 
378 Ok(())
379}
380 
381#[test]
382pub fn test_h265_keyframes_detection() -> Result<(), RtcError> {
383 init_log();
384 init_crypto_default();
385 
386 let mut l = TestRtc::new(Peer::Left);
387 let mut r = TestRtc::new(Peer::Right);
388 
389 l.add_host_candidate((Ipv4Addr::new(1, 1, 1, 1), 1000).into());
390 r.add_host_candidate((Ipv4Addr::new(2, 2, 2, 2), 2000).into());
391 
392 // The change is on the L (sending side) with Direction::SendOnly.
393 let mut change = l.sdp_api();
394 let mid = change.add_media(MediaKind::Video, Direction::SendOnly, None, None, None);
395 let (offer, pending) = change.apply().unwrap();
396 
397 let answer = r.rtc.sdp_api().accept_offer(offer)?;
398 l.rtc.sdp_api().accept_answer(pending, answer)?;
399 
400 loop {
401 if l.is_connected() || r.is_connected() {
402 break;
403 }
404 progress(&mut l, &mut r)?;
405 }
406 
407 let max = l.last.max(r.last);
408 l.last = max;
409 r.last = max;
410 
411 let params = l.params_h265();
412 assert_eq!(params.spec().codec, Codec::H265);
413 let pt = params.pt();
414 
415 let expected_keyframe_seqs: Vec<u16> = include_str!("data/h265.txt")
416 .lines()
417 .filter(|l| l.contains("Keyframe"))
418 .filter_map(|l| {
419 let i = l.find("Seq=")?;
420 let s = &l[i + 4..];
421 let end = s
422 .find(|c: char| !c.is_ascii_digit())
423 .unwrap_or_else(|| s.len());
424 s[..end].parse::<u16>().ok()
425 })
426 .collect();
427 
428 let data = h265_data();
429 
430 for (relative, header, payload) in data {
431 // Keep RTC time progressed to be "in sync" with the test data.
432 while (l.last - max) < relative {
433 progress(&mut l, &mut r)?;
434 }
435 
436 let absolute = max + relative;
437 
438 let mut direct = l.direct_api();
439 let tx = direct.stream_tx_by_mid(mid, None).unwrap();
440 tx.write_rtp(
441 RtpWrite::new(
442 pt,
443 header.sequence_number(None),
444 header.timestamp,
445 absolute,
446 payload,
447 )
448 .marker(header.marker)
449 .ext_vals(header.ext_vals)
450 .nackable(true),
451 );
452 
453 progress(&mut l, &mut r)?;
454 
455 if l.duration() > Duration::from_secs(5) {
456 break;
457 }
458 }
459 
460 let iter = r.events.iter().filter_map(|(_, e)| {
461 if let Event::MediaData(d) = e {
462 Some(d)
463 } else {
464 None
465 }
466 });
467 
468 for data in iter {
469 let CodecExtra::H265(extra) = data.codec_extra else {
470 panic!("Got non H265 CodecExtra")
471 };
472 let assume_keyframe = expected_keyframe_seqs
473 .iter()
474 .any(|&s| data.seq_range.contains(&(u64::from(s)).into()));
475 if extra.is_keyframe {
476 assert!(assume_keyframe, "Expected keyframe");
477 } else {
478 assert!(!assume_keyframe, "Not expected keyframe");
479 }
480 }
481 
482 Ok(())
483}
484 
485#[test]
486pub fn test_h266_keyframes_detection() -> Result<(), RtcError> {
487 init_log();
488 init_crypto_default();
489 
490 // H.266 is opt-in (disabled by default), so enable it on both peers.
491 let mut l = TestRtc::new_with_config(Peer::Left, |c| c.enable_h266(true));
492 let mut r = TestRtc::new_with_config(Peer::Right, |c| c.enable_h266(true));
493 
494 l.add_host_candidate((Ipv4Addr::new(1, 1, 1, 1), 1000).into());
495 r.add_host_candidate((Ipv4Addr::new(2, 2, 2, 2), 2000).into());
496 
497 // The change is on the L (sending side) with Direction::SendOnly.
498 let mut change = l.sdp_api();
499 let mid = change.add_media(MediaKind::Video, Direction::SendOnly, None, None, None);
500 let (offer, pending) = change.apply().unwrap();
501 
502 let answer = r.rtc.sdp_api().accept_offer(offer)?;
503 l.rtc.sdp_api().accept_answer(pending, answer)?;
504 
505 loop {
506 if l.is_connected() || r.is_connected() {
507 break;
508 }
509 progress(&mut l, &mut r)?;
510 }
511 
512 let max = l.last.max(r.last);
513 l.last = max;
514 r.last = max;
515 
516 let params = l.params_h266();
517 assert_eq!(params.spec().codec, Codec::H266);
518 let pt = params.pt();
519 
520 let expected_keyframe_seqs: Vec<u16> = include_str!("data/h266.txt")
521 .lines()
522 .filter(|l| l.contains("Keyframe"))
523 .filter_map(|l| {
524 let i = l.find("Seq=")?;
525 let s = &l[i + 4..];
526 let end = s
527 .find(|c: char| !c.is_ascii_digit())
528 .unwrap_or_else(|| s.len());
529 s[..end].parse::<u16>().ok()
530 })
531 .collect();
532 
533 let data = h266_data();
534 
535 for (relative, header, payload) in data {
536 // Keep RTC time progressed to be "in sync" with the test data.
537 while (l.last - max) < relative {
538 progress(&mut l, &mut r)?;
539 }
540 
541 let absolute = max + relative;
542 
543 let mut direct = l.direct_api();
544 let tx = direct.stream_tx_by_mid(mid, None).unwrap();
545 tx.write_rtp(
546 RtpWrite::new(
547 pt,
548 header.sequence_number(None),
549 header.timestamp,
550 absolute,
551 payload,
552 )
553 .marker(header.marker)
554 .ext_vals(header.ext_vals)
555 .nackable(true),
556 );
557 
558 progress(&mut l, &mut r)?;
559 
560 if l.duration() > Duration::from_secs(5) {
561 break;
562 }
563 }
564 
565 let iter = r.events.iter().filter_map(|(_, e)| {
566 if let Event::MediaData(d) = e {
567 Some(d)
568 } else {
569 None
570 }
571 });
572 
573 for data in iter {
574 let CodecExtra::H266(extra) = data.codec_extra else {
575 panic!("Got non H266 CodecExtra")
576 };
577 let assume_keyframe = expected_keyframe_seqs
578 .iter()
579 .any(|&s| data.seq_range.contains(&(u64::from(s)).into()));
580 if extra.is_keyframe {
581 assert!(assume_keyframe, "Expected keyframe");
582 } else {
583 assert!(!assume_keyframe, "Not expected keyframe");
584 }
585 }
586 
587 Ok(())
588}