File
Blob: firmware/vendor/str0m/tests/keyframe-requests.rs
| 1 | //! Tests for PLI/FIR keyframe request handling. |
| 2 | |
| 3 | use std::net::Ipv4Addr; |
| 4 | |
| 5 | use str0m::media::{Direction, KeyframeRequestKind, MediaKind}; |
| 6 | use str0m::{Event, RtcError}; |
| 7 | |
| 8 | mod common; |
| 9 | use common::{Peer, TestRtc, init_crypto_default, init_log, negotiate, progress}; |
| 10 | |
| 11 | /// Test PLI (Picture Loss Indication) request is sent and received. |
| 12 | #[test] |
| 13 | fn keyframe_request_pli() -> Result<(), RtcError> { |
| 14 | init_log(); |
| 15 | init_crypto_default(); |
| 16 | |
| 17 | let mut l = TestRtc::new(Peer::Left); |
| 18 | let mut r = TestRtc::new(Peer::Right); |
| 19 | |
| 20 | l.add_host_candidate((Ipv4Addr::new(1, 1, 1, 1), 1000).into()); |
| 21 | r.add_host_candidate((Ipv4Addr::new(2, 2, 2, 2), 2000).into()); |
| 22 | |
| 23 | // L sends video to R |
| 24 | let mid = negotiate(&mut l, &mut r, |change| { |
| 25 | change.add_media(MediaKind::Video, Direction::SendRecv, None, None, None) |
| 26 | }); |
| 27 | |
| 28 | loop { |
| 29 | if l.is_connected() && r.is_connected() { |
| 30 | break; |
| 31 | } |
| 32 | progress(&mut l, &mut r)?; |
| 33 | } |
| 34 | |
| 35 | let max = l.last.max(r.last); |
| 36 | l.last = max; |
| 37 | r.last = max; |
| 38 | |
| 39 | // Send video from L |
| 40 | let params = l.params_vp8(); |
| 41 | let pt = params.pt(); |
| 42 | |
| 43 | for i in 0..20 { |
| 44 | let wallclock = l.start + l.duration(); |
| 45 | let time = l.duration().into(); |
| 46 | l.writer(mid) |
| 47 | .unwrap() |
| 48 | .write(pt, wallclock, time, vec![0x10, 0x00, 0x00, i as u8])?; |
| 49 | progress(&mut l, &mut r)?; |
| 50 | } |
| 51 | |
| 52 | // R requests a keyframe via PLI |
| 53 | if let Some(mut writer) = r.writer(mid) { |
| 54 | if writer.is_request_keyframe_possible(KeyframeRequestKind::Pli) { |
| 55 | writer.request_keyframe(None, KeyframeRequestKind::Pli)?; |
| 56 | } |
| 57 | } |
| 58 | |
| 59 | // Progress until L receives the keyframe request |
| 60 | let mut found_keyframe_request = false; |
| 61 | for _ in 0..100 { |
| 62 | progress(&mut l, &mut r)?; |
| 63 | |
| 64 | for (_, event) in &l.events { |
| 65 | if let Event::KeyframeRequest(req) = event { |
| 66 | if req.mid == mid && req.kind == KeyframeRequestKind::Pli { |
| 67 | found_keyframe_request = true; |
| 68 | break; |
| 69 | } |
| 70 | } |
| 71 | } |
| 72 | |
| 73 | if found_keyframe_request { |
| 74 | break; |
| 75 | } |
| 76 | } |
| 77 | |
| 78 | assert!( |
| 79 | found_keyframe_request, |
| 80 | "L should receive a PLI keyframe request from R" |
| 81 | ); |
| 82 | |
| 83 | Ok(()) |
| 84 | } |
| 85 | |
| 86 | /// Test FIR (Full Intra Request) is sent and received. |
| 87 | #[test] |
| 88 | fn keyframe_request_fir() -> Result<(), RtcError> { |
| 89 | init_log(); |
| 90 | init_crypto_default(); |
| 91 | |
| 92 | let mut l = TestRtc::new(Peer::Left); |
| 93 | let mut r = TestRtc::new(Peer::Right); |
| 94 | |
| 95 | l.add_host_candidate((Ipv4Addr::new(1, 1, 1, 1), 1000).into()); |
| 96 | r.add_host_candidate((Ipv4Addr::new(2, 2, 2, 2), 2000).into()); |
| 97 | |
| 98 | let mid = negotiate(&mut l, &mut r, |change| { |
| 99 | change.add_media(MediaKind::Video, Direction::SendRecv, None, None, None) |
| 100 | }); |
| 101 | |
| 102 | loop { |
| 103 | if l.is_connected() && r.is_connected() { |
| 104 | break; |
| 105 | } |
| 106 | progress(&mut l, &mut r)?; |
| 107 | } |
| 108 | |
| 109 | let max = l.last.max(r.last); |
| 110 | l.last = max; |
| 111 | r.last = max; |
| 112 | |
| 113 | // Send video from L |
| 114 | let params = l.params_vp8(); |
| 115 | let pt = params.pt(); |
| 116 | |
| 117 | for i in 0..20 { |
| 118 | let wallclock = l.start + l.duration(); |
| 119 | let time = l.duration().into(); |
| 120 | l.writer(mid) |
| 121 | .unwrap() |
| 122 | .write(pt, wallclock, time, vec![0x10, 0x00, 0x00, i as u8])?; |
| 123 | progress(&mut l, &mut r)?; |
| 124 | } |
| 125 | |
| 126 | // R requests a keyframe via FIR |
| 127 | if let Some(mut writer) = r.writer(mid) { |
| 128 | if writer.is_request_keyframe_possible(KeyframeRequestKind::Fir) { |
| 129 | writer.request_keyframe(None, KeyframeRequestKind::Fir)?; |
| 130 | } |
| 131 | } |
| 132 | |
| 133 | let mut found_keyframe_request = false; |
| 134 | for _ in 0..100 { |
| 135 | progress(&mut l, &mut r)?; |
| 136 | |
| 137 | for (_, event) in &l.events { |
| 138 | if let Event::KeyframeRequest(req) = event { |
| 139 | if req.mid == mid && req.kind == KeyframeRequestKind::Fir { |
| 140 | found_keyframe_request = true; |
| 141 | break; |
| 142 | } |
| 143 | } |
| 144 | } |
| 145 | |
| 146 | if found_keyframe_request { |
| 147 | break; |
| 148 | } |
| 149 | } |
| 150 | |
| 151 | assert!( |
| 152 | found_keyframe_request, |
| 153 | "L should receive a FIR keyframe request from R" |
| 154 | ); |
| 155 | |
| 156 | Ok(()) |
| 157 | } |
| 158 | |
| 159 | /// Test is_request_keyframe_possible() method. |
| 160 | #[test] |
| 161 | fn keyframe_request_possibility_check() -> Result<(), RtcError> { |
| 162 | init_log(); |
| 163 | init_crypto_default(); |
| 164 | |
| 165 | let mut l = TestRtc::new(Peer::Left); |
| 166 | let mut r = TestRtc::new(Peer::Right); |
| 167 | |
| 168 | l.add_host_candidate((Ipv4Addr::new(1, 1, 1, 1), 1000).into()); |
| 169 | r.add_host_candidate((Ipv4Addr::new(2, 2, 2, 2), 2000).into()); |
| 170 | |
| 171 | let mid = negotiate(&mut l, &mut r, |change| { |
| 172 | change.add_media(MediaKind::Video, Direction::SendRecv, None, None, None) |
| 173 | }); |
| 174 | |
| 175 | loop { |
| 176 | if l.is_connected() && r.is_connected() { |
| 177 | break; |
| 178 | } |
| 179 | progress(&mut l, &mut r)?; |
| 180 | } |
| 181 | |
| 182 | // Send some video to establish the stream |
| 183 | let params = l.params_vp8(); |
| 184 | let pt = params.pt(); |
| 185 | |
| 186 | for i in 0..5 { |
| 187 | let wallclock = l.start + l.duration(); |
| 188 | let time = l.duration().into(); |
| 189 | l.writer(mid) |
| 190 | .unwrap() |
| 191 | .write(pt, wallclock, time, vec![0x10, 0x00, 0x00, i as u8])?; |
| 192 | progress(&mut l, &mut r)?; |
| 193 | } |
| 194 | |
| 195 | // Check that keyframe requests are possible |
| 196 | if let Some(writer) = r.writer(mid) { |
| 197 | let pli_possible = writer.is_request_keyframe_possible(KeyframeRequestKind::Pli); |
| 198 | let fir_possible = writer.is_request_keyframe_possible(KeyframeRequestKind::Fir); |
| 199 | |
| 200 | // At least one should be possible with default config |
| 201 | assert!( |
| 202 | pli_possible || fir_possible, |
| 203 | "At least PLI or FIR should be possible" |
| 204 | ); |
| 205 | } |
| 206 | |
| 207 | Ok(()) |
| 208 | } |