Skip to content
File

Blob: firmware/vendor/str0m/src/rtp/ext.rs

rust1584 lines
1use std::any::Any;
2use std::any::TypeId;
3use std::collections::HashMap;
4use std::fmt;
5use std::fmt::Debug;
6use std::hash::BuildHasherDefault;
7use std::hash::Hasher;
8use std::panic::UnwindSafe;
9use std::str::from_utf8;
10use std::sync::Arc;
11use std::time::{Duration, Instant, SystemTime};
12 
13use crate::util::InstantExt;
14use crate::util::SystemTimeExt;
15use crate::util::already_happened;
16use crate::util::epoch_to_beginning;
17 
18use crate::rtp_::Frequency;
19 
20use super::mtime::MediaTime;
21use super::{Mid, Rid};
22 
23/// RTP header extensions.
24#[derive(Debug, Clone)]
25#[non_exhaustive]
26pub enum Extension {
27 /// <http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time>
28 AbsoluteSendTime,
29 /// <http://www.webrtc.org/experiments/rtp-hdrext/abs-capture-time>
30 AbsoluteCaptureTime,
31 /// <urn:ietf:params:rtp-hdrext:ssrc-audio-level>
32 AudioLevel,
33 /// <urn:ietf:params:rtp-hdrext:toffset>
34 ///
35 /// Use when a RTP packet is delayed by a send queue to indicate an offset in the "transmitter".
36 /// It effectively means we can set a timestamp offset exactly when the UDP packet leaves the
37 /// server.
38 TransmissionTimeOffset,
39 /// <urn:3gpp:video-orientation>
40 VideoOrientation,
41 /// <http://www.ietf.org/id/draft-holmer-rmcat-transport-wide-cc-extensions-01>
42 TransportSequenceNumber,
43 /// <http://www.webrtc.org/experiments/rtp-hdrext/playout-delay>
44 PlayoutDelay,
45 /// <http://www.webrtc.org/experiments/rtp-hdrext/video-content-type>
46 VideoContentType,
47 /// <http://www.webrtc.org/experiments/rtp-hdrext/video-timing>
48 VideoTiming,
49 /// <urn:ietf:params:rtp-hdrext:sdes:rtp-stream-id>
50 ///
51 /// UTF8 encoded identifier for the RTP stream. Not the same as SSRC, this is is designed to
52 /// avoid running out of SSRC for very large sessions.
53 RtpStreamId,
54 /// <urn:ietf:params:rtp-hdrext:sdes:rtp-stream-id>
55 ///
56 /// UTF8 encoded identifier referencing another RTP stream's RtpStreamId. If we see
57 /// this extension type, we know the stream is a repair stream.
58 RepairedRtpStreamId,
59 /// <urn:ietf:params:rtp-hdrext:sdes:mid>
60 RtpMid,
61 /// <http://tools.ietf.org/html/draft-ietf-avtext-framemarking-07>
62 FrameMarking,
63 /// <http://www.webrtc.org/experiments/rtp-hdrext/color-space>
64 ColorSpace,
65 
66 /// Not recognized URI, but it could still be user parseable.
67 #[doc(hidden)]
68 UnknownUri(String, Arc<dyn ExtensionSerializer>),
69}
70 
71// All header extensions must have a common "form", either using
72// 1 byte for the (ID, len) or 2 bytes for the (ID, len).
73// If one extension requires the two byte form
74// (probably because of its size, but possibly because of ID),
75// The form must be the two-byte variety for all of them.
76#[repr(u16)]
77#[derive(Clone, Copy, PartialEq, Eq, Debug)]
78pub(crate) enum ExtensionsForm {
79 // See RFC 8285 Section 4.2
80 // ID Range: 1..=14
81 // Length Range: 1..=16
82 OneByte = 0xBEDE,
83 // See RFC 8285 Section 4.3
84 // ID Range: 1..=255
85 // Length Range: 0..=255
86 TwoByte = 0x1000,
87}
88 
89pub const MAX_ID_ONE_BYTE_FORM: u8 = 14;
90// With the two byte form, it could be 255, but supporting larger values makes the ExtensionMap larger.
91// So we support only up to this ID for now.
92pub const MAX_ID: u8 = 16;
93 
94impl ExtensionsForm {
95 pub(crate) fn as_u16(self) -> u16 {
96 self as u16
97 }
98 
99 pub(crate) fn serialize(self) -> [u8; 2] {
100 // App bits set to 0
101 self.as_u16().to_be_bytes()
102 }
103 
104 pub(crate) fn parse(bytes: [u8; 2]) -> Option<Self> {
105 let serialized = u16::from_be_bytes(bytes);
106 if serialized == ExtensionsForm::OneByte.as_u16() {
107 Some(ExtensionsForm::OneByte)
108 // Ignore the app bits
109 } else if (serialized & 0xFFF0) == ExtensionsForm::TwoByte.as_u16() {
110 Some(ExtensionsForm::TwoByte)
111 } else {
112 None
113 }
114 }
115}
116 
117// TODO: think this through. Is it unwind safe?
118impl UnwindSafe for Extension {}
119 
120/// Trait for parsing/writing user RTP header extensions.
121pub trait ExtensionSerializer: Debug + Send + Sync + 'static {
122 /// Write the extension to the buffer of bytes. Must return the number
123 /// of bytes written. This can be 0 if the extension could not be serialized.
124 fn write_to(&self, buf: &mut [u8], ev: &ExtensionValues) -> usize;
125 
126 /// Parse a value and put it in the [`ExtensionValues::user_values`] field.
127 fn parse_value(&self, buf: &[u8], ev: &mut ExtensionValues) -> bool;
128 
129 /// Tell if this extension should be used for video media.
130 fn is_video(&self) -> bool;
131 
132 /// Tell if this extension should be used for audio media.
133 fn is_audio(&self) -> bool;
134 
135 /// When calling write_to, if the size of the written value may exceed 16 bytes,
136 /// or may be 0 bytes, the two byte header extension form must be used.
137 /// Otherwise, the one byte form may be used, which is usually the case.
138 fn requires_two_byte_form(&self, _ev: &ExtensionValues) -> bool {
139 false
140 }
141}
142 
143impl Extension {
144 fn requires_two_byte_form(&self, ev: &ExtensionValues) -> bool {
145 match self {
146 Extension::UnknownUri(_, serializer) => serializer.requires_two_byte_form(ev),
147 _ => false,
148 }
149 }
150}
151 
152/// This is a placeholder value for when the Extension URI are parsed in an SDP OFFER/ANSWER.
153/// The trait write_to() and parse_value() should never be called (that would be a bug).
154#[derive(Debug)]
155struct SdpUnknownUri;
156 
157impl ExtensionSerializer for SdpUnknownUri {
158 // If an unreachable happens, it's a bug.
159 fn write_to(&self, _buf: &mut [u8], _ev: &ExtensionValues) -> usize {
160 unreachable!("Incorrect ExtensionSerializer::write_to")
161 }
162 fn parse_value(&self, _buf: &[u8], _ev: &mut ExtensionValues) -> bool {
163 unreachable!("Incorrect ExtensionSerializer::parse_value")
164 }
165 fn is_video(&self) -> bool {
166 unreachable!("Incorrect ExtensionSerializer::is_video")
167 }
168 fn is_audio(&self) -> bool {
169 unreachable!("Incorrect ExtensionSerializer::is_audio")
170 }
171}
172 
173/// Mapping of extension URI to our enum
174const EXT_URI: &[(Extension, &str)] = &[
175 (
176 Extension::AbsoluteSendTime,
177 "http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time",
178 ),
179 (
180 Extension::AbsoluteCaptureTime,
181 "http://www.webrtc.org/experiments/rtp-hdrext/abs-capture-time",
182 ),
183 (
184 Extension::AudioLevel,
185 "urn:ietf:params:rtp-hdrext:ssrc-audio-level",
186 ),
187 (
188 Extension::TransmissionTimeOffset,
189 "urn:ietf:params:rtp-hdrext:toffset",
190 ),
191 (
192 Extension::VideoOrientation, //
193 "urn:3gpp:video-orientation",
194 ),
195 (
196 Extension::TransportSequenceNumber,
197 "http://www.ietf.org/id/draft-holmer-rmcat-transport-wide-cc-extensions-01",
198 ),
199 (
200 Extension::PlayoutDelay,
201 "http://www.webrtc.org/experiments/rtp-hdrext/playout-delay",
202 ),
203 (
204 Extension::VideoContentType,
205 "http://www.webrtc.org/experiments/rtp-hdrext/video-content-type",
206 ),
207 (
208 Extension::VideoTiming,
209 "http://www.webrtc.org/experiments/rtp-hdrext/video-timing",
210 ),
211 (
212 Extension::RtpStreamId,
213 "urn:ietf:params:rtp-hdrext:sdes:rtp-stream-id",
214 ),
215 (
216 Extension::RepairedRtpStreamId,
217 "urn:ietf:params:rtp-hdrext:sdes:repaired-rtp-stream-id",
218 ),
219 (
220 Extension::RtpMid, //
221 "urn:ietf:params:rtp-hdrext:sdes:mid",
222 ),
223 (
224 Extension::FrameMarking,
225 "http://tools.ietf.org/html/draft-ietf-avtext-framemarking-07",
226 ),
227 (
228 Extension::ColorSpace,
229 "http://www.webrtc.org/experiments/rtp-hdrext/color-space",
230 ),
231];
232 
233impl Extension {
234 /// Parses an extension from a URI. This only happens for incoming SDP OFFER/ANSWER
235 /// while the corresponding Extension with a potential ExtensionSerializer is
236 /// in Rtc::session.
237 pub(crate) fn from_sdp_uri(uri: &str) -> Self {
238 for (t, spec) in EXT_URI.iter() {
239 if *spec == uri {
240 return t.clone();
241 }
242 }
243 
244 Extension::UnknownUri(uri.to_string(), Arc::new(SdpUnknownUri))
245 }
246 
247 /// Extension for a uri not handled by str0m itself.
248 pub fn with_serializer(uri: &str, s: impl ExtensionSerializer) -> Self {
249 Extension::UnknownUri(uri.to_string(), Arc::new(s))
250 }
251 
252 /// Represents the extension as an URI.
253 pub fn as_uri(&self) -> &str {
254 for (t, spec) in EXT_URI.iter() {
255 if t == self {
256 return spec;
257 }
258 }
259 
260 if let Extension::UnknownUri(uri, _) = self {
261 return uri;
262 }
263 
264 "unknown"
265 }
266 
267 pub(crate) fn is_serialized(&self) -> bool {
268 if let Self::UnknownUri(_, s) = self {
269 // Check if this Arc contains the SdpUnknownUri.
270 let is_sdp = (s as &(dyn Any + 'static))
271 .downcast_ref::<SdpUnknownUri>()
272 .is_some();
273 
274 // If it is the SdpUnknownUri, we are not serializing. If this happens,
275 // it's probably a bug. The only way to construct SdpUnknownUri is via SDP,
276 // but those values are only for Eq-comparison vs the values in Session.
277 // The SdpUnknownUri should not even try to be serialized.
278 if is_sdp {
279 panic!("is_serialized on SdpUnkownUri, this is a bug");
280 }
281 }
282 true
283 }
284 
285 fn is_audio(&self) -> bool {
286 use Extension::*;
287 
288 if let UnknownUri(_, serializer) = self {
289 return serializer.is_audio();
290 }
291 
292 matches!(
293 self,
294 RtpStreamId
295 | RepairedRtpStreamId
296 | RtpMid
297 | AbsoluteSendTime
298 | AbsoluteCaptureTime
299 | AudioLevel
300 | TransportSequenceNumber
301 | TransmissionTimeOffset
302 | PlayoutDelay
303 )
304 }
305 
306 fn is_video(&self) -> bool {
307 use Extension::*;
308 
309 if let UnknownUri(_, serializer) = self {
310 return serializer.is_video();
311 }
312 
313 matches!(
314 self,
315 RtpStreamId
316 | RepairedRtpStreamId
317 | RtpMid
318 | AbsoluteSendTime
319 | AbsoluteCaptureTime
320 | VideoOrientation
321 | TransportSequenceNumber
322 | TransmissionTimeOffset
323 | PlayoutDelay
324 | VideoContentType
325 | VideoTiming
326 | FrameMarking
327 | ColorSpace
328 )
329 }
330}
331 
332// As of 2022-09-28, for audio google chrome offers these.
333// "a=extmap:1 urn:ietf:params:rtp-hdrext:ssrc-audio-level"
334// "a=extmap:2 http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time"
335// "a=extmap:3 http://www.ietf.org/id/draft-holmer-rmcat-transport-wide-cc-extensions-01"
336// "a=extmap:4 urn:ietf:params:rtp-hdrext:sdes:mid"
337//
338// For video these.
339// "a=extmap:2 http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time"
340// "a=extmap:3 http://www.ietf.org/id/draft-holmer-rmcat-transport-wide-cc-extensions-01"
341// "a=extmap:4 urn:ietf:params:rtp-hdrext:sdes:mid"
342// "a=extmap:5 http://www.webrtc.org/experiments/rtp-hdrext/playout-delay"
343// "a=extmap:6 http://www.webrtc.org/experiments/rtp-hdrext/video-content-type"
344// "a=extmap:7 http://www.webrtc.org/experiments/rtp-hdrext/video-timing"
345// "a=extmap:8 http://www.webrtc.org/experiments/rtp-hdrext/color-space"
346// "a=extmap:10 urn:ietf:params:rtp-hdrext:sdes:rtp-stream-id"
347// "a=extmap:11 urn:ietf:params:rtp-hdrext:sdes:repaired-rtp-stream-id"
348// "a=extmap:13 urn:3gpp:video-orientation"
349// "a=extmap:14 urn:ietf:params:rtp-hdrext:toffset"
350 
351/// Mapping between RTP extension id to what extension that is.
352#[derive(Clone, PartialEq, Eq)]
353pub struct ExtensionMap([Option<MapEntry>; MAX_ID as usize]); // index 0 is extmap:1.
354 
355#[derive(Debug, Clone, PartialEq, Eq)]
356struct MapEntry {
357 ext: Extension,
358 locked: bool,
359}
360 
361impl ExtensionMap {
362 /// Create an empty map.
363 pub fn empty() -> Self {
364 ExtensionMap(std::array::from_fn(|_| None))
365 }
366 
367 /// Creates a map with the "standard" mappings.
368 ///
369 /// The standard are taken from Chrome.
370 pub fn standard() -> Self {
371 let mut exts = Self::empty();
372 
373 exts.set(1, Extension::AudioLevel);
374 exts.set(2, Extension::AbsoluteSendTime);
375 exts.set(3, Extension::TransportSequenceNumber);
376 exts.set(4, Extension::RtpMid);
377 // exts.set_mapping(&ExtMap::new(8, Extension::ColorSpace));
378 exts.set(10, Extension::RtpStreamId);
379 exts.set(11, Extension::RepairedRtpStreamId);
380 exts.set(13, Extension::VideoOrientation);
381 
382 exts
383 }
384 
385 pub(crate) fn clear(&mut self) {
386 for i in &mut self.0 {
387 *i = None;
388 }
389 }
390 
391 /// Set a mapping for an extension.
392 ///
393 /// The id must be in 1..=MAX_ID (1-indexed).
394 pub fn set(&mut self, id: u8, ext: Extension) {
395 if id < 1 || id > MAX_ID {
396 debug!("Set RTP extension out of range 1-{}: {}", MAX_ID, id);
397 return;
398 }
399 let idx = id as usize - 1;
400 
401 let m = MapEntry { ext, locked: false };
402 
403 self.0[idx] = Some(m);
404 }
405 
406 /// Look up the extension for the id.
407 ///
408 /// The id must be in 1..=MAX_ID (1-indexed).
409 pub fn lookup(&self, id: u8) -> Option<&Extension> {
410 if id >= 1 && id <= MAX_ID {
411 self.0[id as usize - 1].as_ref().map(|m| &m.ext)
412 } else {
413 debug!("Lookup RTP extension out of range 1-{}: {}", MAX_ID, id);
414 None
415 }
416 }
417 
418 /// Finds the id for an extension (if mapped).
419 ///
420 /// The returned id will be 1-based.
421 pub fn id_of(&self, e: Extension) -> Option<u8> {
422 self.0
423 .iter()
424 .position(|x| x.as_ref().map(|e| &e.ext) == Some(&e))
425 .map(|p| p as u8 + 1)
426 }
427 
428 /// Returns an iterator over the elements of the extension map
429 pub fn iter(&self) -> impl Iterator<Item = (u8, &Extension)> + '_ {
430 self.0
431 .iter()
432 .enumerate()
433 .filter_map(|(i, e)| e.as_ref().map(|e| (i, e)))
434 .map(|(i, e)| ((i + 1) as u8, &e.ext))
435 }
436 
437 /// Returns an iterator over the audio or video elements of the extension map
438 pub fn iter_by_media_type(&self, audio: bool) -> impl Iterator<Item = (u8, &Extension)> + '_ {
439 self.iter().filter(move |(_id, ext)| {
440 if audio {
441 ext.is_audio()
442 } else {
443 ext.is_video()
444 }
445 })
446 }
447 
448 /// Returns an iterator over the audio elements of the extension map
449 #[allow(unused)]
450 pub fn iter_audio(&self) -> impl Iterator<Item = (u8, &Extension)> + '_ {
451 self.iter_by_media_type(true)
452 }
453 
454 /// Returns an iterator over the video elements of the extension map
455 #[allow(unused)]
456 pub fn iter_video(&self) -> impl Iterator<Item = (u8, &Extension)> + '_ {
457 self.iter_by_media_type(false)
458 }
459 
460 pub(crate) fn cloned_with_type(&self, audio: bool) -> Self {
461 let mut x = ExtensionMap::empty();
462 for (id, ext) in self.iter_by_media_type(audio) {
463 x.set(id, ext.clone());
464 }
465 x
466 }
467 
468 // https://tools.ietf.org/html/rfc5285
469 pub(crate) fn parse(
470 &self,
471 mut buf: &[u8],
472 form: ExtensionsForm,
473 ext_vals: &mut ExtensionValues,
474 ) {
475 loop {
476 if buf.is_empty() {
477 return;
478 }
479 
480 if buf[0] == 0 {
481 // padding
482 buf = &buf[1..];
483 continue;
484 }
485 
486 let (id, len) = match form {
487 ExtensionsForm::OneByte => {
488 let id = buf[0] >> 4;
489 let len = (buf[0] & 0xf) as usize + 1;
490 buf = &buf[1..];
491 
492 if id == 15 {
493 // If the ID value 15 is
494 // encountered, its length field should be ignored, processing of the
495 // entire extension should terminate at that point, and only the
496 // extension elements present prior to the element with ID 15
497 // considered.
498 return;
499 }
500 (id, len)
501 }
502 ExtensionsForm::TwoByte => {
503 if buf.len() < 2 {
504 trace!("Not enough ext header len: {} < {}", buf.len(), 2);
505 return;
506 }
507 let id = buf[0];
508 let len = buf[1] as usize;
509 buf = &buf[2..];
510 (id, len)
511 }
512 };
513 
514 if buf.len() < len {
515 trace!("Not enough type ext len: {} < {}", buf.len(), len);
516 return;
517 }
518 
519 let ext_buf = &buf[..len];
520 if let Some(ext) = self.lookup(id) {
521 ext.parse_value(ext_buf, ext_vals);
522 }
523 
524 buf = &buf[len..];
525 }
526 }
527 
528 pub(crate) fn form(&self, ev: &ExtensionValues) -> ExtensionsForm {
529 if self
530 .iter()
531 .any(|(id, ext)| id > MAX_ID_ONE_BYTE_FORM || ext.requires_two_byte_form(ev))
532 {
533 ExtensionsForm::TwoByte
534 } else {
535 ExtensionsForm::OneByte
536 }
537 }
538 
539 pub(crate) fn write_to(
540 &self,
541 ext_buf: &mut [u8],
542 ev: &ExtensionValues,
543 form: ExtensionsForm,
544 ) -> usize {
545 let orig_len = ext_buf.len();
546 let mut b = ext_buf;
547 
548 for (idx, x) in self.0.iter().enumerate() {
549 if let Some(v) = x {
550 match form {
551 ExtensionsForm::OneByte => {
552 if let Some(n) = v.ext.write_to(&mut b[1..], ev) {
553 assert!(n <= 16);
554 assert!(n > 0);
555 b[0] = (idx as u8 + 1) << 4 | (n as u8 - 1);
556 b = &mut b[1 + n..];
557 }
558 }
559 ExtensionsForm::TwoByte => {
560 if let Some(n) = v.ext.write_to(&mut b[2..], ev) {
561 b[0] = (idx + 1) as u8;
562 b[1] = n as u8;
563 b = &mut b[2 + n..];
564 }
565 }
566 };
567 }
568 }
569 
570 orig_len - b.len()
571 }
572 
573 pub(crate) fn remap(&mut self, remote_exts: &[(u8, &Extension)]) {
574 // Match remote numbers and lock down those we see for the first time.
575 for (id, ext) in remote_exts {
576 self.swap(*id, ext);
577 }
578 }
579 
580 fn swap(&mut self, id: u8, ext: &Extension) {
581 if id < 1 || id > MAX_ID {
582 return;
583 }
584 
585 // Mapping goes from 0 to 13.
586 let new_index = id as usize - 1;
587 
588 let Some(old_index) = self
589 .0
590 .iter()
591 .enumerate()
592 .find(|(_, m)| m.as_ref().map(|m| &m.ext) == Some(ext))
593 .map(|(i, _)| i)
594 else {
595 return;
596 };
597 
598 // Unwrap OK because index is checking just above.
599 let old = self.0[old_index].as_mut().unwrap();
600 
601 let is_change = new_index != old_index;
602 
603 // If either audio or video is locked, we got a previous extmap negotiation.
604 if is_change && old.locked {
605 warn!(
606 "Extmap locked by previous negotiation. Ignore change: {} -> {}",
607 old_index, new_index
608 );
609 return;
610 }
611 
612 // Locking must be done regardless of whether there was an actual change.
613 old.locked = true;
614 
615 if !is_change {
616 return;
617 }
618 
619 self.0.swap(old_index, new_index);
620 }
621}
622 
623impl Extension {
624 pub(crate) fn write_to(&self, buf: &mut [u8], ev: &ExtensionValues) -> Option<usize> {
625 use Extension::*;
626 match self {
627 AbsoluteSendTime => {
628 // 24 bit fixed point 6 bits for seconds, 18 for the decimals.
629 // wraps around at 64 seconds.
630 
631 // We assume the Instant is absolute.
632 let time_abs = ev.abs_send_time?;
633 
634 // This should be a 64 second offset from unix epoch.
635 let dur = time_abs.to_unix_duration();
636 
637 // Rebase to the 6.18 format.
638 let time_24 = MediaTime::from(dur)
639 .rebase(Frequency::FIXED_POINT_6_18)
640 .numer() as u32;
641 
642 buf[..3].copy_from_slice(&time_24.to_be_bytes()[1..]);
643 Some(3)
644 }
645 AbsoluteCaptureTime => {
646 let act = ev.abs_capture_time.as_ref()?;
647 let ntp_64 = act.capture_time.as_ntp_64();
648 
649 if let Some(offset) = act.clock_offset {
650 if buf.len() < 16 {
651 return None;
652 }
653 buf[..8].copy_from_slice(&ntp_64.to_be_bytes());
654 buf[8..16].copy_from_slice(&offset.to_be_bytes());
655 Some(16)
656 } else {
657 if buf.len() < 8 {
658 return None;
659 }
660 buf[..8].copy_from_slice(&ntp_64.to_be_bytes());
661 Some(8)
662 }
663 }
664 AudioLevel => {
665 let v1 = ev.audio_level?;
666 let v2 = ev.voice_activity?;
667 buf[0] = if v2 { 0x80 } else { 0 } | (-(0x7f & v1) as u8);
668 Some(1)
669 }
670 TransmissionTimeOffset => {
671 let v = ev.tx_time_offs?;
672 buf[..4].copy_from_slice(&v.to_be_bytes());
673 Some(4)
674 }
675 VideoOrientation => {
676 let v = ev.video_orientation?;
677 buf[0] = v as u8;
678 Some(1)
679 }
680 TransportSequenceNumber => {
681 let v = ev.transport_cc?;
682 buf[..2].copy_from_slice(&v.to_be_bytes());
683 Some(2)
684 }
685 PlayoutDelay => {
686 let v1 = ev.play_delay_min?.rebase(Frequency::HUNDREDTHS);
687 let v2 = ev.play_delay_max?.rebase(Frequency::HUNDREDTHS);
688 let min = (v1.numer() & 0xfff) as u32;
689 let max = (v2.numer() & 0xfff) as u32;
690 buf[0] = (min >> 4) as u8;
691 buf[1] = (min << 4) as u8 | (max >> 8) as u8;
692 buf[2] = max as u8;
693 Some(3)
694 }
695 VideoContentType => {
696 let v = ev.video_content_type?;
697 buf[0] = v;
698 Some(1)
699 }
700 VideoTiming => {
701 let v = ev.video_timing?;
702 buf[0] = v.flags;
703 buf[1..3].copy_from_slice(&v.encode_start.to_be_bytes());
704 buf[3..5].copy_from_slice(&v.encode_finish.to_be_bytes());
705 buf[5..7].copy_from_slice(&v.packetize_complete.to_be_bytes());
706 buf[7..9].copy_from_slice(&v.last_left_pacer.to_be_bytes());
707 // Reserved for network
708 buf[9..11].copy_from_slice(&0_u16.to_be_bytes());
709 buf[11..13].copy_from_slice(&0_u16.to_be_bytes());
710 Some(13)
711 }
712 RtpStreamId => {
713 let v = ev.rid?;
714 let l = v.len();
715 buf[..l].copy_from_slice(v.as_bytes());
716 Some(l)
717 }
718 RepairedRtpStreamId => {
719 let v = ev.rid_repair?;
720 let l = v.len();
721 buf[..l].copy_from_slice(v.as_bytes());
722 Some(l)
723 }
724 RtpMid => {
725 let v = ev.mid?;
726 let l = v.len();
727 buf[..l].copy_from_slice(v.as_bytes());
728 Some(l)
729 }
730 FrameMarking => {
731 let v = ev.frame_mark?;
732 buf[..4].copy_from_slice(&v.to_be_bytes());
733 Some(4)
734 }
735 ColorSpace => {
736 // TODO HDR color space
737 None
738 }
739 UnknownUri(_, serializer) => {
740 let n = serializer.write_to(buf, ev);
741 
742 if n == 0 { None } else { Some(n) }
743 }
744 }
745 }
746 
747 pub(crate) fn parse_value(&self, buf: &[u8], ev: &mut ExtensionValues) -> Option<()> {
748 use Extension::*;
749 match self {
750 // 3
751 AbsoluteSendTime => {
752 // 24 bit fixed point 6 bits for seconds, 18 for the decimals.
753 // wraps around at 64 seconds.
754 if buf.len() < 3 {
755 return None;
756 }
757 let time_24 = u32::from_be_bytes([0, buf[0], buf[1], buf[2]]);
758 
759 // Rebase to micros
760 let time_micros = MediaTime::from_fixed_point_6_18(time_24 as u64)
761 .rebase(Frequency::MICROS)
762 .numer();
763 
764 // This should be the duration in 0-64 seconds from a fixed 64 second offset
765 // from UNIX EPOCH. For now, we must save this as offset from _something else_ and
766 // fix the correct value when we have the exact Instant::now() to relate it to.
767 let time_dur = Duration::from_micros(time_micros);
768 
769 let time_tmp = already_happened() + time_dur;
770 ev.abs_send_time = Some(time_tmp);
771 }
772 // 8 or 16
773 AbsoluteCaptureTime => {
774 if buf.len() < 8 {
775 return None;
776 }
777 let ntp_64 = u64::from_be_bytes(buf[..8].try_into().unwrap());
778 let clock_offset =
779 (buf.len() >= 16).then(|| i64::from_be_bytes(buf[8..16].try_into().unwrap()));
780 ev.abs_capture_time = Some(AbsCaptureTime {
781 capture_time: SystemTime::from_ntp_64(ntp_64),
782 clock_offset,
783 });
784 }
785 // 1
786 AudioLevel => {
787 if buf.is_empty() {
788 return None;
789 }
790 ev.audio_level = Some(-(0x7f & buf[0] as i8));
791 ev.voice_activity = Some(buf[0] & 0x80 > 0);
792 }
793 // 3
794 TransmissionTimeOffset => {
795 if buf.len() < 4 {
796 return None;
797 }
798 ev.tx_time_offs = Some(u32::from_be_bytes([buf[0], buf[1], buf[2], buf[3]]));
799 }
800 // 1
801 VideoOrientation => {
802 if buf.is_empty() {
803 return None;
804 }
805 ev.video_orientation = Some(super::ext::VideoOrientation::from(buf[0] & 3));
806 }
807 // 2
808 TransportSequenceNumber => {
809 if buf.len() < 2 {
810 return None;
811 }
812 ev.transport_cc = Some(u16::from_be_bytes([buf[0], buf[1]]));
813 }
814 // 3
815 PlayoutDelay => {
816 if buf.len() < 3 {
817 return None;
818 }
819 let min = (buf[0] as u32) << 4 | (buf[1] as u32) >> 4;
820 let max = ((buf[1] & 0xf) as u32) << 8 | buf[2] as u32;
821 ev.play_delay_min = Some(MediaTime::from_hundredths(min as u64));
822 ev.play_delay_max = Some(MediaTime::from_hundredths(max as u64));
823 }
824 // 1
825 VideoContentType => {
826 if buf.is_empty() {
827 return None;
828 }
829 ev.video_content_type = Some(buf[0]);
830 }
831 // 13
832 VideoTiming => {
833 if buf.len() < 9 {
834 return None;
835 }
836 ev.video_timing = Some(self::VideoTiming {
837 flags: buf[0],
838 encode_start: u16::from_be_bytes([buf[1], buf[2]]),
839 encode_finish: u16::from_be_bytes([buf[3], buf[4]]),
840 packetize_complete: u16::from_be_bytes([buf[5], buf[6]]),
841 last_left_pacer: u16::from_be_bytes([buf[7], buf[8]]),
842 // 9 - 10 // reserved for network
843 // 11 - 12 // reserved for network
844 });
845 }
846 RtpStreamId => {
847 let s = from_utf8(buf).ok()?;
848 ev.rid = Some(s.into());
849 }
850 RepairedRtpStreamId => {
851 let s = from_utf8(buf).ok()?;
852 ev.rid_repair = Some(s.into());
853 }
854 RtpMid => {
855 let s = from_utf8(buf).ok()?;
856 ev.mid = Some(s.into());
857 }
858 FrameMarking => {
859 if buf.len() < 4 {
860 return None;
861 }
862 ev.frame_mark = Some(u32::from_be_bytes([buf[0], buf[1], buf[2], buf[3]]));
863 }
864 ColorSpace => {
865 // TODO HDR color space
866 }
867 UnknownUri(_, serializer) => {
868 let success = serializer.parse_value(buf, ev);
869 if !success {
870 return None;
871 }
872 }
873 }
874 
875 Some(())
876 }
877}
878 
879/// Values in an RTP header extension.
880///
881/// This is metadata that is available also without decrypting the SRTP packets.
882#[derive(Clone, Default, PartialEq, Eq)]
883pub struct ExtensionValues {
884 /// Audio level is measured in negative decibel. 0 is max and a "normal" value might be -30.
885 pub audio_level: Option<i8>,
886 
887 /// Indication that there is sound from a voice.
888 pub voice_activity: Option<bool>,
889 
890 /// Tell a receiver what rotation a video need to replay correctly.
891 pub video_orientation: Option<VideoOrientation>,
892 
893 // The values below are considered internal until we have a reason to expose them.
894 // Generally we want to avoid expose experimental features unless there are strong
895 // reasons to do so.
896 #[doc(hidden)]
897 pub video_content_type: Option<u8>, // 0 = unspecified, 1 = screenshare
898 #[doc(hidden)]
899 pub tx_time_offs: Option<u32>,
900 #[doc(hidden)]
901 pub abs_send_time: Option<Instant>,
902 #[doc(hidden)]
903 pub abs_capture_time: Option<AbsCaptureTime>,
904 #[doc(hidden)]
905 pub transport_cc: Option<u16>, // (buf[0] << 8) | buf[1];
906 #[doc(hidden)]
907 // https://webrtc.googlesource.com/src/+/refs/heads/master/docs/native-code/rtp-hdrext/playout-delay
908 pub play_delay_min: Option<MediaTime>,
909 #[doc(hidden)]
910 pub play_delay_max: Option<MediaTime>,
911 #[doc(hidden)]
912 pub video_timing: Option<VideoTiming>,
913 #[doc(hidden)]
914 pub rid: Option<Rid>,
915 #[doc(hidden)]
916 pub rid_repair: Option<Rid>,
917 #[doc(hidden)]
918 pub mid: Option<Mid>,
919 #[doc(hidden)]
920 pub frame_mark: Option<u32>,
921 
922 /// User values for [`ExtensionSerializer`] to parse into and write from.
923 pub user_values: UserExtensionValues,
924}
925impl ExtensionValues {
926 pub(crate) fn update_absolute_send_time(&mut self, now: Instant) {
927 let Some(v) = self.abs_send_time else {
928 return;
929 };
930 
931 // This should be 0-64 seconds, or we are not working with a newly parsed value.
932 let relative_64_secs = v - already_happened();
933 assert!(relative_64_secs <= Duration::from_secs(64));
934 
935 let now_since_epoch = now.to_unix_duration();
936 
937 let closest_64 = now_since_epoch.saturating_sub(Duration::from_micros(
938 now_since_epoch.as_micros() as u64 % 64_000_000,
939 ));
940 
941 let since_beginning = closest_64.saturating_sub(epoch_to_beginning());
942 
943 let mut offset = already_happened() + since_beginning;
944 
945 if offset + relative_64_secs > now {
946 offset = offset
947 .checked_sub(Duration::from_secs(64))
948 .unwrap_or(offset);
949 }
950 
951 self.abs_send_time = Some(offset + relative_64_secs);
952 }
953}
954 
955/// Space for storing user extension values via [`ExtensionSerializer`].
956#[derive(Clone, Default)]
957pub struct UserExtensionValues {
958 map: Option<AnyMap>,
959}
960 
961// The "AnyMap" idea is borrowed from the http crate but replacing Box for Any.
962type AnyMap = HashMap<TypeId, Arc<dyn Any + Send + Sync>, BuildHasherDefault<IdHasher>>;
963 
964// No point in hashing the TypeId, since it is already unique.
965#[derive(Default)]
966struct IdHasher(u64);
967 
968impl Hasher for IdHasher {
969 fn write(&mut self, _: &[u8]) {
970 unreachable!("TypeId calls write_u64");
971 }
972 
973 #[inline]
974 fn write_u64(&mut self, id: u64) {
975 self.0 = id;
976 }
977 
978 #[inline]
979 fn finish(&self) -> u64 {
980 self.0
981 }
982}
983 
984// TODO: I don't see a good way of comparing this. Is there one?
985impl PartialEq for UserExtensionValues {
986 fn eq(&self, other: &Self) -> bool {
987 let (Some(m1), Some(m2)) = (&self.map, &other.map) else {
988 return self.map.is_none() == other.map.is_none();
989 };
990 
991 for k1 in m1.keys() {
992 if !m2.contains_key(k1) {
993 return false;
994 }
995 }
996 
997 for k2 in m2.keys() {
998 if !m1.contains_key(k2) {
999 return false;
1000 }
1001 }
1002 
1003 true
1004 }
1005}
1006 
1007impl Eq for UserExtensionValues {}
1008 
1009impl UserExtensionValues {
1010 /// Set a user extension value.
1011 ///
1012 /// This uses the type of the value as "key", i.e. it can only hold a single
1013 /// per type. The user should make a wrapper type for the extension they want
1014 /// to parse/write.
1015 ///
1016 /// ```
1017 /// # use str0m::rtp::ExtensionValues;
1018 /// let mut exts = ExtensionValues::default();
1019 ///
1020 /// #[derive(Debug, PartialEq, Eq)]
1021 /// struct MySpecialType(u8);
1022 ///
1023 /// exts.user_values.set(MySpecialType(42));
1024 /// ```
1025 pub fn set<T: Send + Sync + 'static>(&mut self, val: T) {
1026 // TODO: Consider simplifying to "self.set_arc(Arc::new(val))";
1027 self.map
1028 .get_or_insert_with(HashMap::default)
1029 .insert(TypeId::of::<T>(), Arc::new(val));
1030 }
1031 
1032 /// Get a user extension value (by type).
1033 /// ```
1034 /// # use str0m::rtp::ExtensionValues;
1035 /// let mut exts = ExtensionValues::default();
1036 ///
1037 /// #[derive(Debug, PartialEq, Eq)]
1038 /// struct MySpecialType(u8);
1039 ///
1040 /// exts.user_values.set(MySpecialType(42));
1041 ///
1042 /// let v = exts.user_values.get::<MySpecialType>();
1043 ///
1044 /// assert_eq!(v, Some(&MySpecialType(42)));
1045 /// ```
1046 pub fn get<T: Send + Sync + 'static>(&self) -> Option<&T> {
1047 self.map
1048 .as_ref()
1049 .and_then(|map| map.get(&TypeId::of::<T>()))
1050 // unwrap here is OK because TypeId::of::<T> is guaranteed to be unique
1051 .map(|boxed| (&**boxed as &(dyn Any + 'static)).downcast_ref().unwrap())
1052 }
1053 
1054 /// Like .set(), but takes an Arc, which can be nice to avoid cloning
1055 /// large extension values.
1056 pub fn set_arc<T: Send + Sync + 'static>(&mut self, val: Arc<T>) {
1057 self.map
1058 .get_or_insert_with(HashMap::default)
1059 .insert(TypeId::of::<T>(), val);
1060 }
1061 
1062 /// Like .get(), but clones and returns the Arc, which can be nice to
1063 /// avoid cloning large extension values.
1064 pub fn get_arc<T: Send + Sync + 'static>(&self) -> Option<Arc<T>> {
1065 self.map
1066 .as_ref()?
1067 .get(&TypeId::of::<T>())?
1068 .clone()
1069 .downcast()
1070 .ok()
1071 }
1072 
1073 /// Remove a user extension value (by type).
1074 pub fn remove<T: Send + Sync + 'static>(&mut self) {
1075 if let Some(map) = &mut self.map {
1076 map.remove(&TypeId::of::<T>());
1077 }
1078 }
1079}
1080 
1081impl UnwindSafe for UserExtensionValues {}
1082 
1083impl fmt::Debug for ExtensionValues {
1084 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1085 write!(f, "ExtensionValues {{")?;
1086 
1087 if let Some(t) = self.mid {
1088 write!(f, " mid: {t}")?;
1089 }
1090 if let Some(t) = self.rid {
1091 write!(f, " rid: {t}")?;
1092 }
1093 if let Some(t) = self.rid_repair {
1094 write!(f, " rid_repair: {t}")?;
1095 }
1096 if let Some(t) = self.abs_send_time {
1097 write!(f, " abs_send_time: {:?}", t)?;
1098 }
1099 if let Some(t) = &self.abs_capture_time {
1100 write!(f, " abs_capture_time: {:?}", t)?;
1101 }
1102 if let Some(t) = self.voice_activity {
1103 write!(f, " voice_activity: {t}")?;
1104 }
1105 if let Some(t) = self.audio_level {
1106 write!(f, " audio_level: {t}")?;
1107 }
1108 if let Some(t) = self.tx_time_offs {
1109 write!(f, " tx_time_offs: {t}")?;
1110 }
1111 if let Some(t) = self.video_orientation {
1112 write!(f, " video_orientation: {t:?}")?;
1113 }
1114 if let Some(t) = self.transport_cc {
1115 write!(f, " transport_cc: {t}")?;
1116 }
1117 if let Some(t) = self.play_delay_min {
1118 write!(f, " play_delay_min: {}", t.as_seconds())?;
1119 }
1120 if let Some(t) = self.play_delay_max {
1121 write!(f, " play_delay_max: {}", t.as_seconds())?;
1122 }
1123 if let Some(t) = self.video_content_type {
1124 write!(f, " video_content_type: {t}")?;
1125 }
1126 if let Some(t) = &self.video_timing {
1127 write!(f, " video_timing: {t:?}")?;
1128 }
1129 if let Some(t) = &self.frame_mark {
1130 write!(f, " frame_mark: {t}")?;
1131 }
1132 
1133 write!(f, " }}")?;
1134 Ok(())
1135 }
1136}
1137 
1138/// Absolute capture time from the `abs-capture-time` RTP header extension.
1139///
1140/// Contains the NTP wall-clock timestamp of original frame capture, and
1141/// optionally the sender's estimate of the offset between its own clock
1142/// and the capture system's clock.
1143#[derive(Debug, Clone, Copy, PartialEq, Eq)]
1144pub struct AbsCaptureTime {
1145 /// NTP wall-clock timestamp of original frame capture.
1146 pub capture_time: SystemTime,
1147 /// Sender's estimate of offset between its own NTP clock and the
1148 /// capture system's NTP clock. Signed NTP fixed-point (Q32.32):
1149 /// upper 32 bits whole seconds, lower 32 bits fractional.
1150 pub clock_offset: Option<i64>,
1151}
1152 
1153/// 2^32 as f64, for NTP Q32.32 fixed-point conversion.
1154const NTP_F32: f64 = 4_294_967_296.0;
1155 
1156impl AbsCaptureTime {
1157 /// Clock offset in seconds, if present.
1158 ///
1159 /// Per the spec: `Capture NTP Clock = Sender NTP Clock + offset`.
1160 pub fn clock_offset_secs(&self) -> Option<f64> {
1161 self.clock_offset.map(|v| v as f64 / NTP_F32)
1162 }
1163 
1164 /// Set the clock offset in seconds.
1165 pub fn set_clock_offset(&mut self, secs: f64) {
1166 self.clock_offset = Some((secs * NTP_F32) as i64);
1167 }
1168}
1169 
1170#[derive(Debug, Clone, Copy, PartialEq, Eq)]
1171pub struct VideoTiming {
1172 // 0x01 = extension is set due to timer.
1173 // 0x02 - extension is set because the frame is larger than usual.
1174 pub flags: u8,
1175 pub encode_start: u16,
1176 pub encode_finish: u16,
1177 pub packetize_complete: u16,
1178 pub last_left_pacer: u16,
1179}
1180 
1181impl fmt::Display for Extension {
1182 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1183 use Extension::*;
1184 write!(
1185 f,
1186 "{}",
1187 match self {
1188 AbsoluteSendTime => "abs-send-time",
1189 AbsoluteCaptureTime => "abs-capture-time",
1190 AudioLevel => "ssrc-audio-level",
1191 TransmissionTimeOffset => "toffset",
1192 VideoOrientation => "video-orientation",
1193 TransportSequenceNumber => "transport-wide-cc",
1194 PlayoutDelay => "playout-delay",
1195 VideoContentType => "video-content-type",
1196 VideoTiming => "video-timing",
1197 RtpStreamId => "rtp-stream-id",
1198 RepairedRtpStreamId => "repaired-rtp-stream-id",
1199 RtpMid => "mid",
1200 FrameMarking => "frame-marking07",
1201 ColorSpace => "color-space",
1202 UnknownUri(uri, _) => uri,
1203 }
1204 )
1205 }
1206}
1207 
1208impl fmt::Debug for ExtensionMap {
1209 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1210 write!(f, "Extensions(")?;
1211 let joined = self
1212 .0
1213 .iter()
1214 .enumerate()
1215 .filter_map(|(i, v)| v.as_ref().map(|v| (i + 1, v)))
1216 .map(|(i, v)| format!("{}={}", i, v.ext))
1217 .collect::<Vec<_>>()
1218 .join(", ");
1219 write!(f, "{joined}")?;
1220 write!(f, ")")?;
1221 Ok(())
1222 }
1223}
1224 
1225/// How the video is rotated.
1226#[derive(Debug, Clone, Copy, PartialEq, Eq)]
1227pub enum VideoOrientation {
1228 /// Not rotated.
1229 Deg0 = 0,
1230 /// 90 degress clockwise.
1231 Deg90 = 3,
1232 /// Upside down.
1233 Deg180 = 2,
1234 /// 90 degrees counter clockwise.
1235 Deg270 = 1,
1236}
1237 
1238#[cfg(feature = "drv")]
1239crate::drv_identity_copy!(VideoOrientation);
1240 
1241impl From<u8> for VideoOrientation {
1242 fn from(value: u8) -> Self {
1243 match value {
1244 1 => Self::Deg270,
1245 2 => Self::Deg180,
1246 3 => Self::Deg90,
1247 _ => Self::Deg0,
1248 }
1249 }
1250}
1251 
1252impl PartialEq for Extension {
1253 fn eq(&self, other: &Self) -> bool {
1254 match (self, other) {
1255 (Extension::AbsoluteSendTime, Extension::AbsoluteSendTime) => true,
1256 (Extension::AbsoluteCaptureTime, Extension::AbsoluteCaptureTime) => true,
1257 (Extension::AudioLevel, Extension::AudioLevel) => true,
1258 (Extension::TransmissionTimeOffset, Extension::TransmissionTimeOffset) => true,
1259 (Extension::VideoOrientation, Extension::VideoOrientation) => true,
1260 (Extension::TransportSequenceNumber, Extension::TransportSequenceNumber) => true,
1261 (Extension::PlayoutDelay, Extension::PlayoutDelay) => true,
1262 (Extension::VideoContentType, Extension::VideoContentType) => true,
1263 (Extension::VideoTiming, Extension::VideoTiming) => true,
1264 (Extension::RtpStreamId, Extension::RtpStreamId) => true,
1265 (Extension::RepairedRtpStreamId, Extension::RepairedRtpStreamId) => true,
1266 (Extension::RtpMid, Extension::RtpMid) => true,
1267 (Extension::FrameMarking, Extension::FrameMarking) => true,
1268 (Extension::ColorSpace, Extension::ColorSpace) => true,
1269 (Extension::UnknownUri(uri1, _), Extension::UnknownUri(uri2, _)) => uri1 == uri2,
1270 _ => false,
1271 }
1272 }
1273}
1274 
1275impl Eq for Extension {}
1276 
1277#[cfg(test)]
1278mod test {
1279 use super::*;
1280 
1281 #[test]
1282 fn abs_send_time() {
1283 let now = Instant::now() + Duration::from_secs(1000);
1284 
1285 let mut exts = ExtensionMap::empty();
1286 exts.set(4, Extension::AbsoluteSendTime);
1287 let ev = ExtensionValues {
1288 abs_send_time: Some(now),
1289 ..Default::default()
1290 };
1291 
1292 let mut buf = vec![0_u8; 8];
1293 exts.write_to(&mut buf[..], &ev, ExtensionsForm::OneByte);
1294 
1295 let mut ev2 = ExtensionValues::default();
1296 exts.parse(&buf, ExtensionsForm::OneByte, &mut ev2);
1297 
1298 // Let's pretend a 50 millisecond network latency.
1299 ev2.update_absolute_send_time(now + Duration::from_millis(50));
1300 
1301 let now2 = ev2.abs_send_time.unwrap();
1302 
1303 let abs = if now > now2 { now - now2 } else { now2 - now };
1304 
1305 assert!(abs < Duration::from_millis(1));
1306 }
1307 
1308 #[test]
1309 fn abs_send_time_two_byte_form() {
1310 let now = Instant::now() + Duration::from_secs(1000);
1311 
1312 let mut exts = ExtensionMap::empty();
1313 exts.set(16, Extension::AbsoluteSendTime);
1314 let ev = ExtensionValues {
1315 abs_send_time: Some(now),
1316 ..Default::default()
1317 };
1318 
1319 let mut buf = vec![0_u8; 8];
1320 assert_eq!(ExtensionsForm::TwoByte, exts.form(&ev));
1321 exts.write_to(&mut buf[..], &ev, ExtensionsForm::TwoByte);
1322 
1323 let mut ev2 = ExtensionValues::default();
1324 exts.parse(&buf, ExtensionsForm::TwoByte, &mut ev2);
1325 
1326 // Let's pretend a 50 millisecond network latency.
1327 ev2.update_absolute_send_time(now + Duration::from_millis(50));
1328 
1329 let now2 = ev2.abs_send_time.unwrap();
1330 
1331 let abs = if now > now2 { now - now2 } else { now2 - now };
1332 
1333 assert!(abs < Duration::from_millis(1));
1334 }
1335 
1336 #[test]
1337 fn abs_capture_time_short_form() {
1338 let now = SystemTime::now();
1339 
1340 let mut exts = ExtensionMap::empty();
1341 exts.set(5, Extension::AbsoluteCaptureTime);
1342 let ev = ExtensionValues {
1343 abs_capture_time: Some(AbsCaptureTime {
1344 capture_time: now,
1345 clock_offset: None,
1346 }),
1347 ..Default::default()
1348 };
1349 
1350 let mut buf = vec![0_u8; 16];
1351 exts.write_to(&mut buf[..], &ev, ExtensionsForm::OneByte);
1352 
1353 let mut ev2 = ExtensionValues::default();
1354 exts.parse(&buf, ExtensionsForm::OneByte, &mut ev2);
1355 
1356 let act = ev2.abs_capture_time.unwrap();
1357 
1358 // Allow for small rounding errors in NTP conversion
1359 let abs = if now > act.capture_time {
1360 now.duration_since(act.capture_time)
1361 } else {
1362 act.capture_time.duration_since(now)
1363 }
1364 .unwrap();
1365 
1366 assert!(abs < Duration::from_millis(1));
1367 assert_eq!(act.clock_offset, None);
1368 }
1369 
1370 #[test]
1371 fn abs_capture_time_extended_form() {
1372 let now = SystemTime::now();
1373 let clock_offset: i64 = 123456789;
1374 
1375 let mut exts = ExtensionMap::empty();
1376 exts.set(5, Extension::AbsoluteCaptureTime);
1377 let ev = ExtensionValues {
1378 abs_capture_time: Some(AbsCaptureTime {
1379 capture_time: now,
1380 clock_offset: Some(clock_offset),
1381 }),
1382 ..Default::default()
1383 };
1384 
1385 let mut buf = vec![0_u8; 24];
1386 exts.write_to(&mut buf[..], &ev, ExtensionsForm::OneByte);
1387 
1388 let mut ev2 = ExtensionValues::default();
1389 exts.parse(&buf, ExtensionsForm::OneByte, &mut ev2);
1390 
1391 let act = ev2.abs_capture_time.unwrap();
1392 
1393 // Allow for small rounding errors in NTP conversion
1394 let abs = if now > act.capture_time {
1395 now.duration_since(act.capture_time)
1396 } else {
1397 act.capture_time.duration_since(now)
1398 }
1399 .unwrap();
1400 
1401 assert!(abs < Duration::from_millis(1));
1402 assert_eq!(act.clock_offset, Some(clock_offset));
1403 }
1404 
1405 #[test]
1406 fn abs_capture_time_two_byte_form() {
1407 let now = SystemTime::now();
1408 let clock_offset: i64 = -987654321;
1409 
1410 let mut exts = ExtensionMap::empty();
1411 exts.set(16, Extension::AbsoluteCaptureTime);
1412 let ev = ExtensionValues {
1413 abs_capture_time: Some(AbsCaptureTime {
1414 capture_time: now,
1415 clock_offset: Some(clock_offset),
1416 }),
1417 ..Default::default()
1418 };
1419 
1420 // Should require two-byte form due to extension ID > 14
1421 assert_eq!(ExtensionsForm::TwoByte, exts.form(&ev));
1422 
1423 let mut buf = vec![0_u8; 24];
1424 exts.write_to(&mut buf[..], &ev, ExtensionsForm::TwoByte);
1425 
1426 let mut ev2 = ExtensionValues::default();
1427 exts.parse(&buf, ExtensionsForm::TwoByte, &mut ev2);
1428 
1429 let act = ev2.abs_capture_time.unwrap();
1430 
1431 // Allow for small rounding errors in NTP conversion
1432 let abs = if now > act.capture_time {
1433 now.duration_since(act.capture_time)
1434 } else {
1435 act.capture_time.duration_since(now)
1436 }
1437 .unwrap();
1438 
1439 assert!(abs < Duration::from_millis(1));
1440 assert_eq!(act.clock_offset, Some(clock_offset));
1441 }
1442 
1443 #[test]
1444 fn playout_delay() {
1445 let mut exts = ExtensionMap::empty();
1446 exts.set(2, Extension::PlayoutDelay);
1447 let ev = ExtensionValues {
1448 play_delay_min: Some(MediaTime::from_hundredths(100)),
1449 play_delay_max: Some(MediaTime::from_hundredths(200)),
1450 ..Default::default()
1451 };
1452 
1453 let mut buf = vec![0_u8; 8];
1454 exts.write_to(&mut buf[..], &ev, ExtensionsForm::OneByte);
1455 
1456 let mut ev2 = ExtensionValues::default();
1457 exts.parse(&buf, ExtensionsForm::OneByte, &mut ev2);
1458 
1459 assert_eq!(ev.play_delay_min, ev2.play_delay_min);
1460 assert_eq!(ev.play_delay_max, ev2.play_delay_max);
1461 }
1462 
1463 #[test]
1464 fn remap_exts_audio() {
1465 use Extension::*;
1466 
1467 let mut e1 = ExtensionMap::standard();
1468 let mut e2 = ExtensionMap::empty();
1469 e2.set(14, TransportSequenceNumber);
1470 
1471 println!("{:?}", e1.iter_video().collect::<Vec<_>>());
1472 
1473 e1.remap(&e2.iter_audio().collect::<Vec<_>>());
1474 
1475 // e1 should have adjusted the TransportSequenceNumber for audio
1476 assert_eq!(
1477 e1.iter_audio().collect::<Vec<_>>(),
1478 vec![
1479 (1, &AudioLevel),
1480 (2, &AbsoluteSendTime),
1481 (4, &RtpMid),
1482 (10, &RtpStreamId),
1483 (11, &RepairedRtpStreamId),
1484 (14, &TransportSequenceNumber)
1485 ]
1486 );
1487 
1488 // e1 should have adjusted the TransportSequenceNumber for vudeo
1489 assert_eq!(
1490 e1.iter_video().collect::<Vec<_>>(),
1491 vec![
1492 (2, &AbsoluteSendTime),
1493 (4, &RtpMid),
1494 (10, &RtpStreamId),
1495 (11, &RepairedRtpStreamId),
1496 (13, &VideoOrientation),
1497 (14, &TransportSequenceNumber),
1498 ]
1499 );
1500 }
1501 
1502 #[test]
1503 fn remap_exts_video() {
1504 use Extension::*;
1505 
1506 let mut e1 = ExtensionMap::empty();
1507 e1.set(3, TransportSequenceNumber);
1508 e1.set(4, VideoOrientation);
1509 e1.set(5, VideoContentType);
1510 let mut e2 = ExtensionMap::empty();
1511 e2.set(14, TransportSequenceNumber);
1512 e2.set(12, VideoOrientation);
1513 
1514 e1.remap(&e2.iter_video().collect::<Vec<_>>());
1515 
1516 // e1 should have adjusted to e2.
1517 assert_eq!(
1518 e1.iter_video().collect::<Vec<_>>(),
1519 vec![
1520 (5, &VideoContentType),
1521 (12, &VideoOrientation),
1522 (14, &TransportSequenceNumber)
1523 ]
1524 );
1525 }
1526 
1527 #[test]
1528 fn remap_exts_swaparoo() {
1529 use Extension::*;
1530 
1531 let mut e1 = ExtensionMap::empty();
1532 e1.set(12, TransportSequenceNumber);
1533 e1.set(14, VideoOrientation);
1534 let mut e2 = ExtensionMap::empty();
1535 e2.set(14, TransportSequenceNumber);
1536 e2.set(12, VideoOrientation);
1537 
1538 e1.remap(&e2.iter_video().collect::<Vec<_>>());
1539 
1540 // just make sure the logic isn't wrong for 12-14 -> 14-12
1541 assert_eq!(
1542 e1.iter_video().collect::<Vec<_>>(),
1543 vec![(12, &VideoOrientation), (14, &TransportSequenceNumber)]
1544 );
1545 }
1546 
1547 #[test]
1548 fn remap_exts_illegal() {
1549 use Extension::*;
1550 
1551 let mut e1 = ExtensionMap::empty();
1552 e1.set(12, TransportSequenceNumber);
1553 e1.set(14, VideoOrientation);
1554 
1555 let mut e2 = ExtensionMap::empty();
1556 e2.set(14, TransportSequenceNumber);
1557 e2.set(12, VideoOrientation);
1558 
1559 let mut e3 = ExtensionMap::empty();
1560 // Illegal change of already negotiated/locked number
1561 e3.set(1, TransportSequenceNumber);
1562 e3.set(12, AudioLevel); // change of type for existing.
1563 
1564 // First apply e2
1565 e1.remap(&e2.iter_video().collect::<Vec<_>>());
1566 
1567 println!("{:#?}", e1.0);
1568 assert_eq!(
1569 e1.iter_video().collect::<Vec<_>>(),
1570 vec![(12, &VideoOrientation), (14, &TransportSequenceNumber)]
1571 );
1572 
1573 // Now attempt e3
1574 e1.remap(&e3.iter_audio().collect::<Vec<_>>());
1575 
1576 println!("{:#?}", e1.0);
1577 // At this point we should have not allowed the change, but remain as it was in first apply.
1578 assert_eq!(
1579 e1.iter_video().collect::<Vec<_>>(),
1580 vec![(12, &VideoOrientation), (14, &TransportSequenceNumber)]
1581 );
1582 }
1583}