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