Skip to content
File

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

rust416 lines
1//! G722 timestamp / clock-rate tests.
2//!
3//! G722 samples audio at 16 kHz but, per RFC 3551 §4.5.2, its RTP clock rate is
4//! 8000 Hz (kept at 8000 to stay backwards compatible with RFC 1890, which
5//! incorrectly used this value):
6//! <https://en.wikipedia.org/wiki/RTP_payload_formats#cite_note-55>
7 
8use std::collections::VecDeque;
9use std::net::Ipv4Addr;
10use std::time::{Duration, Instant};
11 
12use str0m::format::Codec;
13use str0m::media::{Direction, Frequency, MediaKind, MediaTime};
14use str0m::rtp::{RtpWrite, Ssrc};
15use str0m::{Event, Rtc, RtcError};
16 
17mod common;
18use common::{Peer, TestRtc, connect_l_r_with_rtc, init_crypto_default, init_log, progress};
19 
20/// G722 is a 16 kHz codec, but per RFC 3551 §4.5.2 its RTP timestamp clock runs at
21/// 8000 Hz. In the media (samples/frame) API str0m presents G722 as 16 kHz both
22/// when writing and when reading, and maps to/from the 8 kHz RTP clock on the
23/// wire internally. See
24/// <https://en.wikipedia.org/wiki/RTP_payload_formats#cite_note-55>
25#[test]
26pub fn g722_media_mode_is_16khz_both_ways() -> Result<(), RtcError> {
27 init_log();
28 init_crypto_default();
29 
30 let mut l = TestRtc::new_with_config(Peer::Left, |c| c.clear_codecs().enable_g722(true));
31 let mut r = TestRtc::new_with_config(Peer::Right, |c| c.clear_codecs().enable_g722(true));
32 
33 l.add_host_candidate((Ipv4Addr::new(1, 1, 1, 1), 1000).into());
34 r.add_host_candidate((Ipv4Addr::new(2, 2, 2, 2), 2000).into());
35 
36 let mut change = l.sdp_api();
37 let mid = change.add_media(MediaKind::Audio, Direction::SendRecv, None, None, None);
38 let (offer, pending) = change.apply().unwrap();
39 
40 let answer = r.rtc.sdp_api().accept_offer(offer)?;
41 l.rtc.sdp_api().accept_answer(pending, answer)?;
42 
43 loop {
44 if l.is_connected() || r.is_connected() {
45 break;
46 }
47 progress(&mut l, &mut r)?;
48 }
49 
50 let max = l.last.max(r.last);
51 l.last = max;
52 r.last = max;
53 
54 let params = l
55 .rtc
56 .codec_config()
57 .find(|p| p.spec().codec == Codec::G722)
58 .cloned()
59 .expect("G722 to be negotiated");
60 // The codec is considered 16 kHz in the media API.
61 assert_eq!(params.spec().clock_rate, Frequency::SIXTEEN_KHZ);
62 let pt = params.pt();
63 
64 // 20 ms of G722 at 64 kbit/s = 160 octets. Each 20 ms frame advances the
65 // 16 kHz media time by 320 samples (and the 8 kHz RTP timestamp by 160).
66 let data = vec![3_u8; 160];
67 let mut samples: u64 = 0;
68 
69 loop {
70 {
71 let wallclock = l.start + l.duration();
72 let time = MediaTime::new(samples, Frequency::SIXTEEN_KHZ);
73 l.writer(mid)
74 .unwrap()
75 .write(pt, wallclock, time, data.clone())?;
76 }
77 samples += 320;
78 
79 progress(&mut l, &mut r)?;
80 
81 if l.duration() > Duration::from_secs(2) {
82 break;
83 }
84 }
85 
86 let media: Vec<_> = r
87 .events
88 .iter()
89 .filter_map(|(_, e)| match e {
90 Event::MediaData(d) => Some(d),
91 _ => None,
92 })
93 .collect();
94 
95 assert!(!media.is_empty(), "R received no G722 MediaData");
96 
97 for d in &media {
98 // The media (samples/frame) API presents G722 as a 16 kHz codec ...
99 assert_eq!(d.params.spec().clock_rate, Frequency::SIXTEEN_KHZ);
100 // ... and the received media time is in that same 16 kHz clock rate.
101 assert_eq!(
102 d.time.frequency(),
103 Frequency::SIXTEEN_KHZ,
104 "media-mode G722 receive time should be 16 kHz"
105 );
106 // The 16 kHz media time advances in whole 20 ms frames (320 samples).
107 assert_eq!(
108 d.time.numer() % 320,
109 0,
110 "expected 16 kHz frame-aligned time"
111 );
112 }
113 
114 Ok(())
115}
116 
117fn rtp_mode_g722(peer: Peer, now: Instant) -> Rtc {
118 let mut b = Rtc::builder()
119 .set_rtp_mode(true)
120 .enable_raw_packets(true)
121 .clear_codecs()
122 .enable_g722(true);
123 if let Some(crypto) = peer.crypto_provider() {
124 b = b.set_crypto_provider(crypto);
125 }
126 b.build(now)
127}
128 
129/// In RTP mode the user works directly with raw RTP timestamps, which for G722
130/// are the 8 kHz wire values (RFC 3551 §4.5.2). str0m must not apply any 16 kHz
131/// scaling in either direction. See
132/// <https://en.wikipedia.org/wiki/RTP_payload_formats#cite_note-55>
133#[test]
134pub fn g722_rtp_mode_is_8khz_both_ways() -> Result<(), RtcError> {
135 init_log();
136 init_crypto_default();
137 
138 let now = Instant::now();
139 let (mut l, mut r) = connect_l_r_with_rtc(
140 rtp_mode_g722(Peer::Left, now),
141 rtp_mode_g722(Peer::Right, now),
142 );
143 
144 let mid = "aud".into();
145 let ssrc: Ssrc = 1.into();
146 
147 l.direct_api().declare_media(mid, MediaKind::Audio);
148 l.direct_api().declare_stream_tx(ssrc, None, mid, None);
149 r.direct_api().declare_media(mid, MediaKind::Audio);
150 r.direct_api().expect_stream_rx(ssrc, None, mid, None);
151 
152 let max = l.last.max(r.last);
153 l.last = max;
154 r.last = max;
155 
156 let pt = l
157 .rtc
158 .codec_config()
159 .find(|p| p.spec().codec == Codec::G722)
160 .map(|p| p.pt())
161 .expect("G722 PT");
162 
163 // The user supplies the raw 8 kHz RTP timestamp; 20 ms frames advance it by 160.
164 let base_ts: u32 = 8_000_000;
165 let data: &[u8] = &[0x1, 0x2, 0x3, 0x4];
166 
167 let mut to_write: VecDeque<(u32, u64)> = VecDeque::from(vec![
168 (base_ts, 100),
169 (base_ts + 160, 101),
170 (base_ts + 320, 102),
171 ]);
172 
173 let mut write_at = l.last + Duration::from_millis(300);
174 
175 loop {
176 if l.start + l.duration() > write_at {
177 write_at = l.last + Duration::from_millis(300);
178 if let Some((time, seq)) = to_write.pop_front() {
179 let wallclock = l.start + l.duration();
180 let mut direct = l.direct_api();
181 let stream = direct.stream_tx(&ssrc).unwrap();
182 stream.write_rtp(RtpWrite::new(pt, seq.into(), time, wallclock, data));
183 }
184 }
185 
186 progress(&mut l, &mut r)?;
187 
188 if l.duration() > Duration::from_secs(4) {
189 break;
190 }
191 }
192 
193 let media: Vec<_> = r
194 .events
195 .iter()
196 .filter_map(|(_, e)| match e {
197 Event::RtpPacket(v) => Some(v),
198 _ => None,
199 })
200 .collect();
201 
202 assert_eq!(media.len(), 3, "expected 3 RTP packets at R");
203 
204 // RTP mode passes the 8 kHz wire timestamp through verbatim (no 16 kHz scaling).
205 assert_eq!(media[0].header.timestamp, base_ts);
206 assert_eq!(media[1].header.timestamp, base_ts + 160);
207 assert_eq!(media[2].header.timestamp, base_ts + 320);
208 
209 for v in &media {
210 assert_eq!(
211 v.time.frequency(),
212 Frequency::EIGHT_KHZ,
213 "RTP-mode G722 time should be at the 8 kHz RTP clock rate"
214 );
215 }
216 
217 Ok(())
218}
219 
220/// Cross-mode: a sample (frame) mode sender writes 16 kHz media time, and an
221/// RTP mode receiver sees the 8 kHz wire timestamps (RFC 3551 §4.5.2). This
222/// verifies the 16 kHz -> 8 kHz mapping happens on the send side. See
223/// <https://en.wikipedia.org/wiki/RTP_payload_formats#cite_note-55>
224#[test]
225pub fn g722_sample_send_to_rtp_receive() -> Result<(), RtcError> {
226 init_log();
227 init_crypto_default();
228 
229 let now = Instant::now();
230 // L writes via the sample/frame API (16 kHz media time).
231 let rtc_l = Rtc::builder().clear_codecs().enable_g722(true).build(now);
232 // R reads raw RTP packets (8 kHz wire clock).
233 let rtc_r = Rtc::builder()
234 .set_rtp_mode(true)
235 .clear_codecs()
236 .enable_g722(true)
237 .build(now);
238 
239 let (mut l, mut r) = connect_l_r_with_rtc(rtc_l, rtc_r);
240 
241 let mid = "aud".into();
242 let ssrc: Ssrc = 1.into();
243 
244 l.direct_api().declare_media(mid, MediaKind::Audio);
245 l.direct_api().declare_stream_tx(ssrc, None, mid, None);
246 r.direct_api().declare_media(mid, MediaKind::Audio);
247 r.direct_api().expect_stream_rx(ssrc, None, mid, None);
248 
249 let max = l.last.max(r.last);
250 l.last = max;
251 r.last = max;
252 
253 let pt = l
254 .rtc
255 .codec_config()
256 .find(|p| p.spec().codec == Codec::G722)
257 .map(|p| p.pt())
258 .expect("G722 PT");
259 
260 let data = vec![7u8; 160];
261 // 5 frames of 20 ms; the 16 kHz media time advances 320 samples per frame.
262 let mut frames: VecDeque<u64> = VecDeque::from(vec![0, 320, 640, 960, 1280]);
263 let mut write_at = l.last + Duration::from_millis(300);
264 
265 loop {
266 if l.start + l.duration() > write_at {
267 write_at = l.last + Duration::from_millis(300);
268 if let Some(samples) = frames.pop_front() {
269 let wallclock = l.start + l.duration();
270 let time = MediaTime::new(samples, Frequency::SIXTEEN_KHZ);
271 l.writer(mid)
272 .unwrap()
273 .write(pt, wallclock, time, data.clone())?;
274 }
275 }
276 
277 progress(&mut l, &mut r)?;
278 
279 if l.duration() > Duration::from_secs(4) {
280 break;
281 }
282 }
283 
284 let media: Vec<_> = r
285 .events
286 .iter()
287 .filter_map(|(_, e)| match e {
288 Event::RtpPacket(v) => Some(v),
289 _ => None,
290 })
291 .collect();
292 
293 assert_eq!(media.len(), 5, "expected 5 RTP packets at R");
294 
295 // The RTP receiver sees the 8 kHz wire clock; each 20 ms frame advances 160.
296 let base = media[0].header.timestamp;
297 for (i, v) in media.iter().enumerate() {
298 assert_eq!(
299 v.time.frequency(),
300 Frequency::EIGHT_KHZ,
301 "RTP-mode receive time should be 8 kHz"
302 );
303 assert_eq!(
304 v.header.timestamp,
305 base + (i as u32) * 160,
306 "8 kHz wire timestamp mismatch at frame {i}"
307 );
308 }
309 
310 Ok(())
311}
312 
313/// Cross-mode: an RTP mode sender writes raw 8 kHz wire timestamps, and a sample
314/// (frame) mode receiver sees 16 kHz media time. This verifies the 8 kHz -> 16 kHz
315/// mapping happens on the receive side. See
316/// <https://en.wikipedia.org/wiki/RTP_payload_formats#cite_note-55>
317#[test]
318pub fn g722_rtp_send_to_sample_receive() -> Result<(), RtcError> {
319 init_log();
320 init_crypto_default();
321 
322 let now = Instant::now();
323 // L writes raw RTP at the 8 kHz wire clock.
324 let rtc_l = Rtc::builder()
325 .set_rtp_mode(true)
326 .clear_codecs()
327 .enable_g722(true)
328 .build(now);
329 // R reads frames via the sample API (16 kHz media time), no reorder hold-back.
330 let rtc_r = Rtc::builder()
331 .set_reordering_size_audio(0)
332 .clear_codecs()
333 .enable_g722(true)
334 .build(now);
335 
336 let (mut l, mut r) = connect_l_r_with_rtc(rtc_l, rtc_r);
337 
338 let mid = "aud".into();
339 let ssrc_tx: Ssrc = 1.into();
340 
341 l.direct_api().declare_media(mid, MediaKind::Audio);
342 l.direct_api().declare_stream_tx(ssrc_tx, None, mid, None);
343 r.direct_api().declare_media(mid, MediaKind::Audio);
344 
345 let max = l.last.max(r.last);
346 l.last = max;
347 r.last = max;
348 
349 let pt = l
350 .rtc
351 .codec_config()
352 .find(|p| p.spec().codec == Codec::G722)
353 .map(|p| p.pt())
354 .expect("G722 PT");
355 let ssrc = l.direct_api().stream_tx_by_mid(mid, None).unwrap().ssrc();
356 
357 let data: &[u8] = &[0x1, 0x2, 0x3, 0x4];
358 let base_ts: u32 = 8_000_000;
359 // 5 frames of 20 ms; the 8 kHz wire timestamp advances 160 per frame.
360 let mut frames: VecDeque<(u32, u64)> = VecDeque::from(vec![
361 (base_ts, 100),
362 (base_ts + 160, 101),
363 (base_ts + 320, 102),
364 (base_ts + 480, 103),
365 (base_ts + 640, 104),
366 ]);
367 let mut write_at = l.last + Duration::from_millis(300);
368 
369 loop {
370 if l.start + l.duration() > write_at {
371 write_at = l.last + Duration::from_millis(300);
372 if let Some((ts, seq)) = frames.pop_front() {
373 let wallclock = l.start + l.duration();
374 let mut direct = l.direct_api();
375 let stream = direct.stream_tx(&ssrc).unwrap();
376 stream.write_rtp(RtpWrite::new(pt, seq.into(), ts, wallclock, data));
377 }
378 }
379 
380 progress(&mut l, &mut r)?;
381 
382 if l.duration() > Duration::from_secs(4) {
383 break;
384 }
385 }
386 
387 let media: Vec<_> = r
388 .events
389 .iter()
390 .filter_map(|(_, e)| match e {
391 Event::MediaData(v) => Some(v),
392 _ => None,
393 })
394 .collect();
395 
396 assert_eq!(media.len(), 5, "expected 5 frames at R");
397 
398 // The sample receiver presents G722 as 16 kHz; each 20 ms frame advances 320.
399 let base = media[0].time.numer();
400 for (i, m) in media.iter().enumerate() {
401 assert_eq!(m.params.spec().clock_rate, Frequency::SIXTEEN_KHZ);
402 assert_eq!(
403 m.time.frequency(),
404 Frequency::SIXTEEN_KHZ,
405 "sample-mode receive time should be 16 kHz"
406 );
407 assert_eq!(
408 m.time.numer(),
409 base + (i as u64) * 320,
410 "16 kHz media time mismatch at frame {i}"
411 );
412 }
413 
414 Ok(())
415}