Skip to main content

str0m/rtp/
ext.rs

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 -= Duration::from_secs(64);
947        }
948
949        self.abs_send_time = Some(offset + relative_64_secs);
950    }
951}
952
953/// Space for storing user extension values via [`ExtensionSerializer`].
954#[derive(Clone, Default)]
955pub struct UserExtensionValues {
956    map: Option<AnyMap>,
957}
958
959// The "AnyMap" idea is borrowed from the http crate but replacing Box for Any.
960type AnyMap = HashMap<TypeId, Arc<dyn Any + Send + Sync>, BuildHasherDefault<IdHasher>>;
961
962// No point in hashing the TypeId, since it is already unique.
963#[derive(Default)]
964struct IdHasher(u64);
965
966impl Hasher for IdHasher {
967    fn write(&mut self, _: &[u8]) {
968        unreachable!("TypeId calls write_u64");
969    }
970
971    #[inline]
972    fn write_u64(&mut self, id: u64) {
973        self.0 = id;
974    }
975
976    #[inline]
977    fn finish(&self) -> u64 {
978        self.0
979    }
980}
981
982// TODO: I don't see a good way of comparing this. Is there one?
983impl PartialEq for UserExtensionValues {
984    fn eq(&self, other: &Self) -> bool {
985        let (Some(m1), Some(m2)) = (&self.map, &other.map) else {
986            return self.map.is_none() == other.map.is_none();
987        };
988
989        for k1 in m1.keys() {
990            if !m2.contains_key(k1) {
991                return false;
992            }
993        }
994
995        for k2 in m2.keys() {
996            if !m1.contains_key(k2) {
997                return false;
998            }
999        }
1000
1001        true
1002    }
1003}
1004
1005impl Eq for UserExtensionValues {}
1006
1007impl UserExtensionValues {
1008    /// Set a user extension value.
1009    ///
1010    /// This uses the type of the value as "key", i.e. it can only hold a single
1011    /// per type. The user should make a wrapper type for the extension they want
1012    /// to parse/write.
1013    ///
1014    /// ```
1015    /// # use str0m::rtp::ExtensionValues;
1016    /// let mut exts = ExtensionValues::default();
1017    ///
1018    /// #[derive(Debug, PartialEq, Eq)]
1019    /// struct MySpecialType(u8);
1020    ///
1021    /// exts.user_values.set(MySpecialType(42));
1022    /// ```
1023    pub fn set<T: Send + Sync + 'static>(&mut self, val: T) {
1024        // TODO: Consider simplifying to "self.set_arc(Arc::new(val))";
1025        self.map
1026            .get_or_insert_with(HashMap::default)
1027            .insert(TypeId::of::<T>(), Arc::new(val));
1028    }
1029
1030    /// Get a user extension value (by type).
1031    /// ```
1032    /// # use str0m::rtp::ExtensionValues;
1033    /// let mut exts = ExtensionValues::default();
1034    ///
1035    /// #[derive(Debug, PartialEq, Eq)]
1036    /// struct MySpecialType(u8);
1037    ///
1038    /// exts.user_values.set(MySpecialType(42));
1039    ///
1040    /// let v = exts.user_values.get::<MySpecialType>();
1041    ///
1042    /// assert_eq!(v, Some(&MySpecialType(42)));
1043    /// ```
1044    pub fn get<T: Send + Sync + 'static>(&self) -> Option<&T> {
1045        self.map
1046            .as_ref()
1047            .and_then(|map| map.get(&TypeId::of::<T>()))
1048            // unwrap here is OK because TypeId::of::<T> is guaranteed to be unique
1049            .map(|boxed| (&**boxed as &(dyn Any + 'static)).downcast_ref().unwrap())
1050    }
1051
1052    /// Like .set(), but takes an Arc, which can be nice to avoid cloning
1053    /// large extension values.
1054    pub fn set_arc<T: Send + Sync + 'static>(&mut self, val: Arc<T>) {
1055        self.map
1056            .get_or_insert_with(HashMap::default)
1057            .insert(TypeId::of::<T>(), val);
1058    }
1059
1060    /// Like .get(), but clones and returns the Arc, which can be nice to
1061    /// avoid cloning large extension values.
1062    pub fn get_arc<T: Send + Sync + 'static>(&self) -> Option<Arc<T>> {
1063        self.map
1064            .as_ref()?
1065            .get(&TypeId::of::<T>())?
1066            .clone()
1067            .downcast()
1068            .ok()
1069    }
1070
1071    /// Remove a user extension value (by type).
1072    pub fn remove<T: Send + Sync + 'static>(&mut self) {
1073        if let Some(map) = &mut self.map {
1074            map.remove(&TypeId::of::<T>());
1075        }
1076    }
1077}
1078
1079impl UnwindSafe for UserExtensionValues {}
1080
1081impl fmt::Debug for ExtensionValues {
1082    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1083        write!(f, "ExtensionValues {{")?;
1084
1085        if let Some(t) = self.mid {
1086            write!(f, " mid: {t}")?;
1087        }
1088        if let Some(t) = self.rid {
1089            write!(f, " rid: {t}")?;
1090        }
1091        if let Some(t) = self.rid_repair {
1092            write!(f, " rid_repair: {t}")?;
1093        }
1094        if let Some(t) = self.abs_send_time {
1095            write!(f, " abs_send_time: {:?}", t)?;
1096        }
1097        if let Some(t) = &self.abs_capture_time {
1098            write!(f, " abs_capture_time: {:?}", t)?;
1099        }
1100        if let Some(t) = self.voice_activity {
1101            write!(f, " voice_activity: {t}")?;
1102        }
1103        if let Some(t) = self.audio_level {
1104            write!(f, " audio_level: {t}")?;
1105        }
1106        if let Some(t) = self.tx_time_offs {
1107            write!(f, " tx_time_offs: {t}")?;
1108        }
1109        if let Some(t) = self.video_orientation {
1110            write!(f, " video_orientation: {t:?}")?;
1111        }
1112        if let Some(t) = self.transport_cc {
1113            write!(f, " transport_cc: {t}")?;
1114        }
1115        if let Some(t) = self.play_delay_min {
1116            write!(f, " play_delay_min: {}", t.as_seconds())?;
1117        }
1118        if let Some(t) = self.play_delay_max {
1119            write!(f, " play_delay_max: {}", t.as_seconds())?;
1120        }
1121        if let Some(t) = self.video_content_type {
1122            write!(f, " video_content_type: {t}")?;
1123        }
1124        if let Some(t) = &self.video_timing {
1125            write!(f, " video_timing: {t:?}")?;
1126        }
1127        if let Some(t) = &self.frame_mark {
1128            write!(f, " frame_mark: {t}")?;
1129        }
1130
1131        write!(f, " }}")?;
1132        Ok(())
1133    }
1134}
1135
1136/// Absolute capture time from the `abs-capture-time` RTP header extension.
1137///
1138/// Contains the NTP wall-clock timestamp of original frame capture, and
1139/// optionally the sender's estimate of the offset between its own clock
1140/// and the capture system's clock.
1141#[derive(Debug, Clone, Copy, PartialEq, Eq)]
1142pub struct AbsCaptureTime {
1143    /// NTP wall-clock timestamp of original frame capture.
1144    pub capture_time: SystemTime,
1145    /// Sender's estimate of offset between its own NTP clock and the
1146    /// capture system's NTP clock. Signed NTP fixed-point (Q32.32):
1147    /// upper 32 bits whole seconds, lower 32 bits fractional.
1148    pub clock_offset: Option<i64>,
1149}
1150
1151/// 2^32 as f64, for NTP Q32.32 fixed-point conversion.
1152const NTP_F32: f64 = 4_294_967_296.0;
1153
1154impl AbsCaptureTime {
1155    /// Clock offset in seconds, if present.
1156    ///
1157    /// Per the spec: `Capture NTP Clock = Sender NTP Clock + offset`.
1158    pub fn clock_offset_secs(&self) -> Option<f64> {
1159        self.clock_offset.map(|v| v as f64 / NTP_F32)
1160    }
1161
1162    /// Set the clock offset in seconds.
1163    pub fn set_clock_offset(&mut self, secs: f64) {
1164        self.clock_offset = Some((secs * NTP_F32) as i64);
1165    }
1166}
1167
1168#[derive(Debug, Clone, Copy, PartialEq, Eq)]
1169pub struct VideoTiming {
1170    // 0x01 = extension is set due to timer.
1171    // 0x02 - extension is set because the frame is larger than usual.
1172    pub flags: u8,
1173    pub encode_start: u16,
1174    pub encode_finish: u16,
1175    pub packetize_complete: u16,
1176    pub last_left_pacer: u16,
1177}
1178
1179impl fmt::Display for Extension {
1180    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1181        use Extension::*;
1182        write!(
1183            f,
1184            "{}",
1185            match self {
1186                AbsoluteSendTime => "abs-send-time",
1187                AbsoluteCaptureTime => "abs-capture-time",
1188                AudioLevel => "ssrc-audio-level",
1189                TransmissionTimeOffset => "toffset",
1190                VideoOrientation => "video-orientation",
1191                TransportSequenceNumber => "transport-wide-cc",
1192                PlayoutDelay => "playout-delay",
1193                VideoContentType => "video-content-type",
1194                VideoTiming => "video-timing",
1195                RtpStreamId => "rtp-stream-id",
1196                RepairedRtpStreamId => "repaired-rtp-stream-id",
1197                RtpMid => "mid",
1198                FrameMarking => "frame-marking07",
1199                ColorSpace => "color-space",
1200                UnknownUri(uri, _) => uri,
1201            }
1202        )
1203    }
1204}
1205
1206impl fmt::Debug for ExtensionMap {
1207    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1208        write!(f, "Extensions(")?;
1209        let joined = self
1210            .0
1211            .iter()
1212            .enumerate()
1213            .filter_map(|(i, v)| v.as_ref().map(|v| (i + 1, v)))
1214            .map(|(i, v)| format!("{}={}", i, v.ext))
1215            .collect::<Vec<_>>()
1216            .join(", ");
1217        write!(f, "{joined}")?;
1218        write!(f, ")")?;
1219        Ok(())
1220    }
1221}
1222
1223/// How the video is rotated.
1224#[derive(Debug, Clone, Copy, PartialEq, Eq)]
1225pub enum VideoOrientation {
1226    /// Not rotated.
1227    Deg0 = 0,
1228    /// 90 degress clockwise.
1229    Deg90 = 3,
1230    /// Upside down.
1231    Deg180 = 2,
1232    /// 90 degrees counter clockwise.
1233    Deg270 = 1,
1234}
1235
1236#[cfg(feature = "drv")]
1237crate::drv_identity_copy!(VideoOrientation);
1238
1239impl From<u8> for VideoOrientation {
1240    fn from(value: u8) -> Self {
1241        match value {
1242            1 => Self::Deg270,
1243            2 => Self::Deg180,
1244            3 => Self::Deg90,
1245            _ => Self::Deg0,
1246        }
1247    }
1248}
1249
1250impl PartialEq for Extension {
1251    fn eq(&self, other: &Self) -> bool {
1252        match (self, other) {
1253            (Extension::AbsoluteSendTime, Extension::AbsoluteSendTime) => true,
1254            (Extension::AbsoluteCaptureTime, Extension::AbsoluteCaptureTime) => true,
1255            (Extension::AudioLevel, Extension::AudioLevel) => true,
1256            (Extension::TransmissionTimeOffset, Extension::TransmissionTimeOffset) => true,
1257            (Extension::VideoOrientation, Extension::VideoOrientation) => true,
1258            (Extension::TransportSequenceNumber, Extension::TransportSequenceNumber) => true,
1259            (Extension::PlayoutDelay, Extension::PlayoutDelay) => true,
1260            (Extension::VideoContentType, Extension::VideoContentType) => true,
1261            (Extension::VideoTiming, Extension::VideoTiming) => true,
1262            (Extension::RtpStreamId, Extension::RtpStreamId) => true,
1263            (Extension::RepairedRtpStreamId, Extension::RepairedRtpStreamId) => true,
1264            (Extension::RtpMid, Extension::RtpMid) => true,
1265            (Extension::FrameMarking, Extension::FrameMarking) => true,
1266            (Extension::ColorSpace, Extension::ColorSpace) => true,
1267            (Extension::UnknownUri(uri1, _), Extension::UnknownUri(uri2, _)) => uri1 == uri2,
1268            _ => false,
1269        }
1270    }
1271}
1272
1273impl Eq for Extension {}
1274
1275#[cfg(test)]
1276mod test {
1277    use super::*;
1278
1279    #[test]
1280    fn abs_send_time() {
1281        let now = Instant::now() + Duration::from_secs(1000);
1282
1283        let mut exts = ExtensionMap::empty();
1284        exts.set(4, Extension::AbsoluteSendTime);
1285        let ev = ExtensionValues {
1286            abs_send_time: Some(now),
1287            ..Default::default()
1288        };
1289
1290        let mut buf = vec![0_u8; 8];
1291        exts.write_to(&mut buf[..], &ev, ExtensionsForm::OneByte);
1292
1293        let mut ev2 = ExtensionValues::default();
1294        exts.parse(&buf, ExtensionsForm::OneByte, &mut ev2);
1295
1296        // Let's pretend a 50 millisecond network latency.
1297        ev2.update_absolute_send_time(now + Duration::from_millis(50));
1298
1299        let now2 = ev2.abs_send_time.unwrap();
1300
1301        let abs = if now > now2 { now - now2 } else { now2 - now };
1302
1303        assert!(abs < Duration::from_millis(1));
1304    }
1305
1306    #[test]
1307    fn abs_send_time_two_byte_form() {
1308        let now = Instant::now() + Duration::from_secs(1000);
1309
1310        let mut exts = ExtensionMap::empty();
1311        exts.set(16, Extension::AbsoluteSendTime);
1312        let ev = ExtensionValues {
1313            abs_send_time: Some(now),
1314            ..Default::default()
1315        };
1316
1317        let mut buf = vec![0_u8; 8];
1318        assert_eq!(ExtensionsForm::TwoByte, exts.form(&ev));
1319        exts.write_to(&mut buf[..], &ev, ExtensionsForm::TwoByte);
1320
1321        let mut ev2 = ExtensionValues::default();
1322        exts.parse(&buf, ExtensionsForm::TwoByte, &mut ev2);
1323
1324        // Let's pretend a 50 millisecond network latency.
1325        ev2.update_absolute_send_time(now + Duration::from_millis(50));
1326
1327        let now2 = ev2.abs_send_time.unwrap();
1328
1329        let abs = if now > now2 { now - now2 } else { now2 - now };
1330
1331        assert!(abs < Duration::from_millis(1));
1332    }
1333
1334    #[test]
1335    fn abs_capture_time_short_form() {
1336        let now = SystemTime::now();
1337
1338        let mut exts = ExtensionMap::empty();
1339        exts.set(5, Extension::AbsoluteCaptureTime);
1340        let ev = ExtensionValues {
1341            abs_capture_time: Some(AbsCaptureTime {
1342                capture_time: now,
1343                clock_offset: None,
1344            }),
1345            ..Default::default()
1346        };
1347
1348        let mut buf = vec![0_u8; 16];
1349        exts.write_to(&mut buf[..], &ev, ExtensionsForm::OneByte);
1350
1351        let mut ev2 = ExtensionValues::default();
1352        exts.parse(&buf, ExtensionsForm::OneByte, &mut ev2);
1353
1354        let act = ev2.abs_capture_time.unwrap();
1355
1356        // Allow for small rounding errors in NTP conversion
1357        let abs = if now > act.capture_time {
1358            now.duration_since(act.capture_time)
1359        } else {
1360            act.capture_time.duration_since(now)
1361        }
1362        .unwrap();
1363
1364        assert!(abs < Duration::from_millis(1));
1365        assert_eq!(act.clock_offset, None);
1366    }
1367
1368    #[test]
1369    fn abs_capture_time_extended_form() {
1370        let now = SystemTime::now();
1371        let clock_offset: i64 = 123456789;
1372
1373        let mut exts = ExtensionMap::empty();
1374        exts.set(5, Extension::AbsoluteCaptureTime);
1375        let ev = ExtensionValues {
1376            abs_capture_time: Some(AbsCaptureTime {
1377                capture_time: now,
1378                clock_offset: Some(clock_offset),
1379            }),
1380            ..Default::default()
1381        };
1382
1383        let mut buf = vec![0_u8; 24];
1384        exts.write_to(&mut buf[..], &ev, ExtensionsForm::OneByte);
1385
1386        let mut ev2 = ExtensionValues::default();
1387        exts.parse(&buf, ExtensionsForm::OneByte, &mut ev2);
1388
1389        let act = ev2.abs_capture_time.unwrap();
1390
1391        // Allow for small rounding errors in NTP conversion
1392        let abs = if now > act.capture_time {
1393            now.duration_since(act.capture_time)
1394        } else {
1395            act.capture_time.duration_since(now)
1396        }
1397        .unwrap();
1398
1399        assert!(abs < Duration::from_millis(1));
1400        assert_eq!(act.clock_offset, Some(clock_offset));
1401    }
1402
1403    #[test]
1404    fn abs_capture_time_two_byte_form() {
1405        let now = SystemTime::now();
1406        let clock_offset: i64 = -987654321;
1407
1408        let mut exts = ExtensionMap::empty();
1409        exts.set(16, Extension::AbsoluteCaptureTime);
1410        let ev = ExtensionValues {
1411            abs_capture_time: Some(AbsCaptureTime {
1412                capture_time: now,
1413                clock_offset: Some(clock_offset),
1414            }),
1415            ..Default::default()
1416        };
1417
1418        // Should require two-byte form due to extension ID > 14
1419        assert_eq!(ExtensionsForm::TwoByte, exts.form(&ev));
1420
1421        let mut buf = vec![0_u8; 24];
1422        exts.write_to(&mut buf[..], &ev, ExtensionsForm::TwoByte);
1423
1424        let mut ev2 = ExtensionValues::default();
1425        exts.parse(&buf, ExtensionsForm::TwoByte, &mut ev2);
1426
1427        let act = ev2.abs_capture_time.unwrap();
1428
1429        // Allow for small rounding errors in NTP conversion
1430        let abs = if now > act.capture_time {
1431            now.duration_since(act.capture_time)
1432        } else {
1433            act.capture_time.duration_since(now)
1434        }
1435        .unwrap();
1436
1437        assert!(abs < Duration::from_millis(1));
1438        assert_eq!(act.clock_offset, Some(clock_offset));
1439    }
1440
1441    #[test]
1442    fn playout_delay() {
1443        let mut exts = ExtensionMap::empty();
1444        exts.set(2, Extension::PlayoutDelay);
1445        let ev = ExtensionValues {
1446            play_delay_min: Some(MediaTime::from_hundredths(100)),
1447            play_delay_max: Some(MediaTime::from_hundredths(200)),
1448            ..Default::default()
1449        };
1450
1451        let mut buf = vec![0_u8; 8];
1452        exts.write_to(&mut buf[..], &ev, ExtensionsForm::OneByte);
1453
1454        let mut ev2 = ExtensionValues::default();
1455        exts.parse(&buf, ExtensionsForm::OneByte, &mut ev2);
1456
1457        assert_eq!(ev.play_delay_min, ev2.play_delay_min);
1458        assert_eq!(ev.play_delay_max, ev2.play_delay_max);
1459    }
1460
1461    #[test]
1462    fn remap_exts_audio() {
1463        use Extension::*;
1464
1465        let mut e1 = ExtensionMap::standard();
1466        let mut e2 = ExtensionMap::empty();
1467        e2.set(14, TransportSequenceNumber);
1468
1469        println!("{:?}", e1.iter_video().collect::<Vec<_>>());
1470
1471        e1.remap(&e2.iter_audio().collect::<Vec<_>>());
1472
1473        // e1 should have adjusted the TransportSequenceNumber for audio
1474        assert_eq!(
1475            e1.iter_audio().collect::<Vec<_>>(),
1476            vec![
1477                (1, &AudioLevel),
1478                (2, &AbsoluteSendTime),
1479                (4, &RtpMid),
1480                (10, &RtpStreamId),
1481                (11, &RepairedRtpStreamId),
1482                (14, &TransportSequenceNumber)
1483            ]
1484        );
1485
1486        // e1 should have adjusted the TransportSequenceNumber for vudeo
1487        assert_eq!(
1488            e1.iter_video().collect::<Vec<_>>(),
1489            vec![
1490                (2, &AbsoluteSendTime),
1491                (4, &RtpMid),
1492                (10, &RtpStreamId),
1493                (11, &RepairedRtpStreamId),
1494                (13, &VideoOrientation),
1495                (14, &TransportSequenceNumber),
1496            ]
1497        );
1498    }
1499
1500    #[test]
1501    fn remap_exts_video() {
1502        use Extension::*;
1503
1504        let mut e1 = ExtensionMap::empty();
1505        e1.set(3, TransportSequenceNumber);
1506        e1.set(4, VideoOrientation);
1507        e1.set(5, VideoContentType);
1508        let mut e2 = ExtensionMap::empty();
1509        e2.set(14, TransportSequenceNumber);
1510        e2.set(12, VideoOrientation);
1511
1512        e1.remap(&e2.iter_video().collect::<Vec<_>>());
1513
1514        // e1 should have adjusted to e2.
1515        assert_eq!(
1516            e1.iter_video().collect::<Vec<_>>(),
1517            vec![
1518                (5, &VideoContentType),
1519                (12, &VideoOrientation),
1520                (14, &TransportSequenceNumber)
1521            ]
1522        );
1523    }
1524
1525    #[test]
1526    fn remap_exts_swaparoo() {
1527        use Extension::*;
1528
1529        let mut e1 = ExtensionMap::empty();
1530        e1.set(12, TransportSequenceNumber);
1531        e1.set(14, VideoOrientation);
1532        let mut e2 = ExtensionMap::empty();
1533        e2.set(14, TransportSequenceNumber);
1534        e2.set(12, VideoOrientation);
1535
1536        e1.remap(&e2.iter_video().collect::<Vec<_>>());
1537
1538        // just make sure the logic isn't wrong for 12-14 -> 14-12
1539        assert_eq!(
1540            e1.iter_video().collect::<Vec<_>>(),
1541            vec![(12, &VideoOrientation), (14, &TransportSequenceNumber)]
1542        );
1543    }
1544
1545    #[test]
1546    fn remap_exts_illegal() {
1547        use Extension::*;
1548
1549        let mut e1 = ExtensionMap::empty();
1550        e1.set(12, TransportSequenceNumber);
1551        e1.set(14, VideoOrientation);
1552
1553        let mut e2 = ExtensionMap::empty();
1554        e2.set(14, TransportSequenceNumber);
1555        e2.set(12, VideoOrientation);
1556
1557        let mut e3 = ExtensionMap::empty();
1558        // Illegal change of already negotiated/locked number
1559        e3.set(1, TransportSequenceNumber);
1560        e3.set(12, AudioLevel); // change of type for existing.
1561
1562        // First apply e2
1563        e1.remap(&e2.iter_video().collect::<Vec<_>>());
1564
1565        println!("{:#?}", e1.0);
1566        assert_eq!(
1567            e1.iter_video().collect::<Vec<_>>(),
1568            vec![(12, &VideoOrientation), (14, &TransportSequenceNumber)]
1569        );
1570
1571        // Now attempt e3
1572        e1.remap(&e3.iter_audio().collect::<Vec<_>>());
1573
1574        println!("{:#?}", e1.0);
1575        // At this point we should have not allowed the change, but remain as it was in first apply.
1576        assert_eq!(
1577            e1.iter_video().collect::<Vec<_>>(),
1578            vec![(12, &VideoOrientation), (14, &TransportSequenceNumber)]
1579        );
1580    }
1581}