Skip to content
File

Blob: firmware/vendor/str0m/tests/user-rtp-header-extension.rs

rust306 lines
1use std::net::Ipv4Addr;
2use std::time::{Duration, Instant};
3 
4use str0m::Rtc;
5use str0m::change::SdpOffer;
6use str0m::format::Codec;
7use str0m::media::{Direction, MediaKind};
8use str0m::rtp::Extension;
9use str0m::rtp::ExtensionSerializer;
10use str0m::rtp::ExtensionValues;
11use str0m::{Event, RtcError};
12use tracing::info_span;
13 
14mod common;
15use common::{TestRtc, init_crypto_default, init_log, progress};
16 
17#[test]
18pub fn user_rtp_header_extension() -> Result<(), RtcError> {
19 init_log();
20 init_crypto_default();
21 
22 #[derive(Debug, PartialEq, Eq)]
23 struct MyValue(u16);
24 
25 #[derive(Debug)]
26 struct MyValueSerializer;
27 
28 impl ExtensionSerializer for MyValueSerializer {
29 fn write_to(&self, buf: &mut [u8], ev: &ExtensionValues) -> usize {
30 // Does output have space?
31 if buf.len() < 2 {
32 return 0;
33 }
34 
35 // Is there a value set?
36 let Some(my_value) = ev.user_values.get::<MyValue>() else {
37 return 0;
38 };
39 
40 // u16 is 2 bytes
41 buf[..2].copy_from_slice(&my_value.0.to_be_bytes());
42 
43 2
44 }
45 
46 fn parse_value(&self, buf: &[u8], ev: &mut ExtensionValues) -> bool {
47 // Is buffer big enough to hold the value?
48 if buf.len() < 2 {
49 return false;
50 }
51 
52 let v = u16::from_be_bytes([buf[0], buf[1]]);
53 let my_value = MyValue(v);
54 
55 // Save parsed value
56 ev.user_values.set(my_value);
57 
58 true
59 }
60 
61 fn is_audio(&self) -> bool {
62 true
63 }
64 
65 fn is_video(&self) -> bool {
66 true
67 }
68 }
69 
70 let user_ext = Extension::with_serializer("http://my-special-extension", MyValueSerializer);
71 
72 // Both L and R must have the uri + serializer configured.
73 let now = Instant::now();
74 let rtc_l = Rtc::builder()
75 //
76 .set_extension(12, user_ext.clone())
77 .build(now);
78 let rtc_r = Rtc::builder()
79 //
80 .set_extension(12, user_ext)
81 .build(now);
82 
83 let mut l = TestRtc::new_with_rtc(info_span!("L"), rtc_l);
84 let mut r = TestRtc::new_with_rtc(info_span!("R"), rtc_r);
85 
86 l.add_host_candidate((Ipv4Addr::new(1, 1, 1, 1), 1000).into());
87 r.add_host_candidate((Ipv4Addr::new(2, 2, 2, 2), 2000).into());
88 
89 // The change is on the L (sending side) with Direction::SendRecv.
90 let mut change = l.sdp_api();
91 let mid = change.add_media(MediaKind::Audio, Direction::SendRecv, None, None, None);
92 let (offer, pending) = change.apply().unwrap();
93 let offer_str = offer.to_sdp_string();
94 let offer_parsed =
95 SdpOffer::from_sdp_string(&offer_str).expect("Should parse offer from string");
96 let answer = r.rtc.sdp_api().accept_offer(offer_parsed)?;
97 l.rtc.sdp_api().accept_answer(pending, answer)?;
98 
99 // Verify that the extension is negotiated.
100 let ext_l = l.media(mid).unwrap().remote_extmap();
101 assert_eq!(
102 ext_l.lookup(12).map(|e| e.as_uri()),
103 Some("http://my-special-extension")
104 );
105 
106 let ext_r = r.media(mid).unwrap().remote_extmap();
107 assert_eq!(
108 ext_r.lookup(12).map(|e| e.as_uri()),
109 Some("http://my-special-extension")
110 );
111 
112 loop {
113 if l.is_connected() || r.is_connected() {
114 break;
115 }
116 progress(&mut l, &mut r)?;
117 }
118 
119 let max = l.last.max(r.last);
120 l.last = max;
121 r.last = max;
122 
123 let params = l.params_opus();
124 assert_eq!(params.spec().codec, Codec::Opus);
125 let pt = params.pt();
126 
127 let data_a = vec![1_u8; 80];
128 
129 loop {
130 let wallclock = l.start + l.duration();
131 let time = l.duration().into();
132 
133 l.writer(mid)
134 .unwrap()
135 // Set my bespoke RTP header value.
136 .user_extension_value(MyValue(42))
137 .write(pt, wallclock, time, data_a.clone())?;
138 
139 progress(&mut l, &mut r)?;
140 
141 if l.duration() > Duration::from_secs(3) {
142 break;
143 }
144 }
145 
146 let iter = r.events.iter().filter_map(|(_, e)| {
147 if let Event::MediaData(d) = e {
148 Some(d)
149 } else {
150 None
151 }
152 });
153 
154 // Assert every media write got the value through.
155 for data in iter {
156 let v = data.ext_vals.user_values.get::<MyValue>();
157 assert_eq!(v, Some(&MyValue(42)));
158 }
159 
160 Ok(())
161}
162 
163#[test]
164pub fn user_rtp_header_extension_two_byte_form() -> Result<(), RtcError> {
165 init_log();
166 init_crypto_default();
167 
168 #[derive(Debug, PartialEq, Eq, Clone)]
169 struct MyValue(Vec<u8>);
170 
171 #[derive(Debug)]
172 struct MyValueSerializer;
173 
174 impl ExtensionSerializer for MyValueSerializer {
175 fn write_to(&self, buf: &mut [u8], ev: &ExtensionValues) -> usize {
176 let Some(my_value) = ev.user_values.get::<MyValue>() else {
177 return 0;
178 };
179 
180 let my_len = my_value.0.len();
181 if buf.len() < my_len {
182 return 0;
183 }
184 
185 // u16 is 2 bytes
186 buf[..my_len].copy_from_slice(&my_value.0);
187 
188 my_len
189 }
190 
191 fn parse_value(&self, buf: &[u8], ev: &mut ExtensionValues) -> bool {
192 let my_value = MyValue(buf.to_vec());
193 
194 ev.user_values.set(my_value);
195 
196 true
197 }
198 
199 fn is_audio(&self) -> bool {
200 true
201 }
202 
203 fn is_video(&self) -> bool {
204 true
205 }
206 
207 fn requires_two_byte_form(&self, _ev: &ExtensionValues) -> bool {
208 true
209 }
210 }
211 
212 let user_ext = Extension::with_serializer("http://my-special-extension", MyValueSerializer);
213 
214 // Both L and R must have the uri + serializer configured.
215 let now = Instant::now();
216 let rtc_l = Rtc::builder()
217 //
218 .set_extension(12, user_ext.clone())
219 .build(now);
220 let rtc_r = Rtc::builder()
221 //
222 .set_extension(12, user_ext)
223 .build(now);
224 
225 let mut l = TestRtc::new_with_rtc(info_span!("L"), rtc_l);
226 let mut r = TestRtc::new_with_rtc(info_span!("R"), rtc_r);
227 
228 l.add_host_candidate((Ipv4Addr::new(1, 1, 1, 1), 1000).into());
229 r.add_host_candidate((Ipv4Addr::new(2, 2, 2, 2), 2000).into());
230 
231 // The change is on the L (sending side) with Direction::SendRecv.
232 let mut change = l.sdp_api();
233 let mid = change.add_media(MediaKind::Audio, Direction::SendRecv, None, None, None);
234 let (offer, pending) = change.apply().unwrap();
235 
236 let answer = r.rtc.sdp_api().accept_offer(offer)?;
237 l.rtc.sdp_api().accept_answer(pending, answer)?;
238 
239 // Verify that the extension is negotiated.
240 let ext_l = l.media(mid).unwrap().remote_extmap();
241 assert_eq!(
242 ext_l.lookup(12).map(|e| e.as_uri()),
243 Some("http://my-special-extension")
244 );
245 
246 let ext_r = r.media(mid).unwrap().remote_extmap();
247 assert_eq!(
248 ext_r.lookup(12).map(|e| e.as_uri()),
249 Some("http://my-special-extension")
250 );
251 
252 loop {
253 if l.is_connected() || r.is_connected() {
254 break;
255 }
256 progress(&mut l, &mut r)?;
257 }
258 
259 let max = l.last.max(r.last);
260 l.last = max;
261 r.last = max;
262 
263 let params = l.params_opus();
264 assert_eq!(params.spec().codec, Codec::Opus);
265 let pt = params.pt();
266 
267 let data_a = vec![1_u8; 80];
268 
269 let my_value = MyValue((0..100u8).collect());
270 loop {
271 let wallclock = l.start + l.duration();
272 let time = l.duration().into();
273 
274 l.writer(mid)
275 .unwrap()
276 // Set my bespoke RTP header value.
277 .user_extension_value(my_value.clone())
278 .write(pt, wallclock, time, data_a.clone())?;
279 
280 progress(&mut l, &mut r)?;
281 
282 if l.duration() > Duration::from_secs(3) {
283 break;
284 }
285 }
286 
287 let datas = r.events.iter().filter_map(|(_, e)| {
288 if let Event::MediaData(d) = e {
289 Some(d)
290 } else {
291 None
292 }
293 });
294 
295 // Assert every media write got the value through.
296 let mut empty = true;
297 for data in datas {
298 empty = false;
299 let v = data.ext_vals.user_values.get::<MyValue>();
300 assert_eq!(v, Some(&my_value));
301 }
302 assert!(!empty);
303 
304 Ok(())
305}