Skip to main content

o_sfu_rfc/
webrtc.rs

1//! RFC references for this module:
2//! - WebRTC RTP usage profile: <https://www.rfc-editor.org/rfc/rfc8834>
3//! - ICE protocol: <https://www.rfc-editor.org/rfc/rfc8445>
4//! - ICE candidate grammar (legacy, still interoperable in SDP): <https://www.rfc-editor.org/rfc/rfc5245>
5//! - DTLS-SRTP protection profiles: <https://www.rfc-editor.org/rfc/rfc5764>
6//! - BUNDLE and MID signaling: <https://www.rfc-editor.org/rfc/rfc9143>
7//! - RTP payload restrictions and RID signaling: <https://www.rfc-editor.org/rfc/rfc8851>
8//! - RTP stream ID header extensions: <https://www.rfc-editor.org/rfc/rfc8852>
9//! - SDP simulcast signaling: <https://www.rfc-editor.org/rfc/rfc8853>
10//! - RTCP multiplexing: <https://www.rfc-editor.org/rfc/rfc5761>
11//! - WebRTC datagram multiplexing: <https://www.rfc-editor.org/rfc/rfc7983>
12//! - RTCP feedback SDP signaling: <https://www.rfc-editor.org/rfc/rfc4585>
13//! - RTP retransmission SDP signaling: <https://www.rfc-editor.org/rfc/rfc4588>
14//! - SDP source attributes: <https://www.rfc-editor.org/rfc/rfc5576>
15//! - SDP grammar: <https://www.rfc-editor.org/rfc/rfc8866>
16//! - SDP offer/answer: <https://www.rfc-editor.org/rfc/rfc3264>
17//! - SDP `setup` roles for connection-oriented media: <https://www.rfc-editor.org/rfc/rfc4145>
18//! - DTLS-SRTP offer/answer usage of `setup`: <https://www.rfc-editor.org/rfc/rfc5763>
19//! - Video frame marking RTP header extension: <https://www.rfc-editor.org/rfc/rfc9626>
20//! - Layer Refresh Request feedback: <https://www.rfc-editor.org/rfc/rfc9627>
21
22use std::fmt;
23
24const DTLS_MUX_FIRST_OCTET_START: u8 = 20;
25const DTLS_MUX_FIRST_OCTET_END: u8 = 63;
26
27/// Returns whether a datagram first octet selects DTLS in WebRTC multiplexing.
28///
29/// Reference: <https://www.rfc-editor.org/rfc/rfc7983.html#section-5>
30#[must_use]
31pub const fn is_dtls_mux_packet(first_octet: u8) -> bool {
32    first_octet >= DTLS_MUX_FIRST_OCTET_START && first_octet <= DTLS_MUX_FIRST_OCTET_END
33}
34
35/// ICE portocol registries used by WebRTC signaling.
36pub mod ice {
37    /// Separator between the peer and sender username fragments in a
38    /// connectivity-check credential.
39    ///
40    /// Reference: <https://www.rfc-editor.org/rfc/rfc8445.html#section-7.2.2>
41    pub const USERNAME_FRAGMENT_SEPARATOR: char = ':';
42
43    /// ICE component IDs for RTP and RTCP.
44    ///
45    /// Reference: RFC 8445 section 5.1.1.
46    pub mod component {
47        pub const RTP: u16 = 1;
48        pub const RTCP: u16 = 2;
49    }
50
51    /// ICE candidate type literals used by SDP candidate attributes.
52    ///
53    /// References:
54    /// - RFC 5245 section 15.1 candidate grammar (`typ host|srflx|prflx|relay`)
55    /// - RFC 8445 (semantic model preserved by the updated ICE specification)
56    pub mod candidate_type {
57        pub const HOST: &str = "host";
58        pub const SERVER_REFLEXIVE: &str = "srflx";
59        pub const PEER_REFLEXIVE: &str = "prflx";
60        pub const RELAYED: &str = "relay";
61    }
62
63    /// ICE candidate attribute grammar tokens used by SDP candidate lines.
64    ///
65    /// Reference: RFC 5245 section 15.1.
66    pub mod candidate_attribute {
67        pub const PREFIX: &str = "candidate:";
68        pub const TYPE_LABEL: &str = "typ";
69    }
70
71    /// Recommended ICE type-preference values.
72    ///
73    /// Reference: RFC 8445 section 5.1.2.2.
74    pub mod type_preference {
75        pub const HOST: u8 = 126;
76        pub const PEER_REFLEXIVE: u8 = 110;
77        pub const SERVER_REFLEXIVE: u8 = 100;
78        pub const RELAYED: u8 = 0;
79    }
80
81    /// ICE transport token used in SDP candidate lines.
82    ///
83    /// Reference: RFC 8445 section 5.1.1 and candidate grammar inherited from RFC 5245.
84    pub mod transport {
85        pub const UDP: &str = "udp";
86        pub const TCP: &str = "tcp";
87    }
88}
89
90/// ICE transport tokens used in SDP candidate lines.
91#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
92pub enum IceTransport {
93    Udp,
94    Tcp,
95}
96
97impl IceTransport {
98    #[must_use]
99    pub const fn as_str(self) -> &'static str {
100        match self {
101            Self::Udp => ice::transport::UDP,
102            Self::Tcp => ice::transport::TCP,
103        }
104    }
105
106    #[must_use]
107    pub fn parse(token: &str) -> Option<Self> {
108        if token.eq_ignore_ascii_case(ice::transport::UDP) {
109            return Some(Self::Udp);
110        }
111        if token.eq_ignore_ascii_case(ice::transport::TCP) {
112            return Some(Self::Tcp);
113        }
114        None
115    }
116}
117
118impl AsRef<str> for IceTransport {
119    fn as_ref(&self) -> &str {
120        match self {
121            Self::Udp => ice::transport::UDP,
122            Self::Tcp => ice::transport::TCP,
123        }
124    }
125}
126
127impl fmt::Display for IceTransport {
128    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
129        f.write_str(self.as_ref())
130    }
131}
132
133/// ICE candidate type tokens used in SDP candidate attributes.
134#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
135pub enum IceCandidateType {
136    Host,
137    ServerReflexive,
138    PeerReflexive,
139    Relayed,
140}
141
142impl IceCandidateType {
143    #[must_use]
144    pub const fn as_str(self) -> &'static str {
145        match self {
146            Self::Host => ice::candidate_type::HOST,
147            Self::ServerReflexive => ice::candidate_type::SERVER_REFLEXIVE,
148            Self::PeerReflexive => ice::candidate_type::PEER_REFLEXIVE,
149            Self::Relayed => ice::candidate_type::RELAYED,
150        }
151    }
152
153    #[must_use]
154    pub fn parse(token: &str) -> Option<Self> {
155        match token {
156            ice::candidate_type::HOST => Some(Self::Host),
157            ice::candidate_type::SERVER_REFLEXIVE => Some(Self::ServerReflexive),
158            ice::candidate_type::PEER_REFLEXIVE => Some(Self::PeerReflexive),
159            ice::candidate_type::RELAYED => Some(Self::Relayed),
160            _ => None,
161        }
162    }
163}
164
165impl AsRef<str> for IceCandidateType {
166    fn as_ref(&self) -> &str {
167        match self {
168            Self::Host => ice::candidate_type::HOST,
169            Self::ServerReflexive => ice::candidate_type::SERVER_REFLEXIVE,
170            Self::PeerReflexive => ice::candidate_type::PEER_REFLEXIVE,
171            Self::Relayed => ice::candidate_type::RELAYED,
172        }
173    }
174}
175
176impl fmt::Display for IceCandidateType {
177    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
178        f.write_str(self.as_ref())
179    }
180}
181
182/// MIME top-level media kinds used by ORTC and SDP payloads
183/// same as on web stream/tracks APIs.
184pub mod media_kind {
185    pub const AUDIO: &str = "audio";
186    pub const VIDEO: &str = "video";
187    pub const APPLICATION: &str = "application";
188}
189
190/// Technical media kind shared by RTP, SDP, and signaling metadata.
191#[derive(
192    Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, serde::Serialize, serde::Deserialize,
193)]
194#[serde(rename_all = "lowercase")]
195pub enum MediaKind {
196    Audio,
197    Video,
198}
199
200impl MediaKind {
201    #[must_use]
202    pub const fn as_str(self) -> &'static str {
203        match self {
204            Self::Audio => media_kind::AUDIO,
205            Self::Video => media_kind::VIDEO,
206        }
207    }
208
209    #[must_use]
210    pub const fn is_audio(self) -> bool {
211        matches!(self, Self::Audio)
212    }
213
214    #[must_use]
215    pub const fn is_video(self) -> bool {
216        matches!(self, Self::Video)
217    }
218}
219
220impl AsRef<str> for MediaKind {
221    fn as_ref(&self) -> &str {
222        self.as_str()
223    }
224}
225
226impl fmt::Display for MediaKind {
227    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
228        f.write_str(self.as_str())
229    }
230}
231
232/// RTCP feedback type and parameter tokens used by current WebRTC capability paylods.
233pub mod rtcp_feedback {
234    /// RTCP feedback kind tokens used in capability dictionnaries.
235    pub mod kind {
236        /// Generic NACK feedback type token.
237        ///
238        /// Reference: RFC 4585 section 6.2.1.
239        pub const NACK: &str = "nack";
240
241        /// Codec control message feedback type token.
242        ///
243        /// Reference: RFC 5104.
244        pub const CCM: &str = "ccm";
245
246        /// Google Receiver Estimated Maximum Bitrate token used by current browser stacks.
247        pub const GOOG_REMB: &str = "goog-remb";
248
249        /// Transport-wide congestion control feedback token.
250        ///
251        /// Reference:
252        /// <https://www.ietf.org/archive/id/draft-holmer-rmcat-transport-wide-cc-extensions-01.txt>
253        pub const TRANSPORT_CC: &str = "transport-cc";
254    }
255
256    /// RTCP feedback parameter tokens used by current WebRTC cpaability payloads.
257    pub mod parameter {
258        /// Picture loss indication parameter token.
259        ///
260        /// Reference: RFC 4585 section 6.3.1.
261        pub const PLI: &str = "pli";
262
263        /// Full intra request parameter token.
264        ///
265        /// Reference: RFC 5104 section 4.3.1.
266        pub const FIR: &str = "fir";
267
268        /// Layer Refresh Request parameter token.
269        ///
270        /// Reference: RFC 9627 section 6.
271        pub const LRR: &str = "lrr";
272    }
273}
274
275pub mod sdp {
276    /// Carriage-return line-feed SDP line ending.
277    ///
278    /// Reference: <https://www.rfc-editor.org/rfc/rfc8866.html#section-5>
279    pub const CRLF: &str = "\r\n";
280
281    /// Carriage-return octet stripped by line-oriented SDP parsers.
282    ///
283    /// Reference: <https://www.rfc-editor.org/rfc/rfc8866.html#section-5>
284    pub const CR: char = '\r';
285
286    /// Line-feed octet used to split SDP text while preserving line endings.
287    ///
288    /// Reference: <https://www.rfc-editor.org/rfc/rfc8866.html#section-5>
289    pub const LF: char = '\n';
290
291    /// ASCII space separating fields within an SDP value.
292    ///
293    /// Reference: <https://www.rfc-editor.org/rfc/rfc8866.html#section-5>
294    pub const SP: char = ' ';
295
296    /// Attribute field prefix.
297    ///
298    /// Reference: <https://www.rfc-editor.org/rfc/rfc8866.html#section-5.13>
299    pub const ATTR: &str = "a=";
300
301    /// Separator between an SDP attribute name and value.
302    ///
303    /// Reference: <https://www.rfc-editor.org/rfc/rfc8866.html#section-5.13>
304    pub const ATTR_SEP: char = ':';
305
306    /// Session-level end-of-candidates line.
307    ///
308    /// Reference: <https://www.rfc-editor.org/rfc/rfc8840.html#section-8>
309    pub const EOC_LINE: &str = "a=end-of-candidates\r\n";
310    /// Media description field prefix.
311    ///
312    /// Reference: <https://www.rfc-editor.org/rfc/rfc8866.html#section-5.14>
313    pub const MEDIA: &str = "m=";
314
315    /// Media description grammar tokens.
316    pub mod media {
317        /// Separator between an SDP media port and port count.
318        ///
319        /// Reference: <https://www.rfc-editor.org/rfc/rfc8866.html#section-5.14>
320        pub const PORT_SEP: char = '/';
321
322        /// Port value that rejects a media description in offer/answer.
323        ///
324        /// Reference: <https://www.rfc-editor.org/rfc/rfc3264.html#section-6>
325        pub const ZERO_PORT: &str = "0";
326    }
327
328    /// `a=extmap` grammar tokens.
329    pub mod extmap {
330        /// Separator between an extension ID and direction.
331        ///
332        /// Reference: <https://www.rfc-editor.org/rfc/rfc8285.html#section-5>
333        pub const DIR_SEP: char = '/';
334    }
335
336    /// `a=rtpmap` grammar tokens.
337    pub mod rtpmap {
338        /// Separator between encoding name, clock rate and encoding parameters.
339        ///
340        /// Reference: <https://www.rfc-editor.org/rfc/rfc8866.html#section-6.6>
341        pub const ENC_SEP: char = '/';
342    }
343
344    pub mod group_semantics {
345        /// `a=group:BUNDLE ...`
346        ///
347        /// Reference: RFC 9143.
348        pub const BUNDLE: &str = "BUNDLE";
349    }
350
351    pub mod ssrc_group_semantics {
352        /// Flow Identification semantics for `a=ssrc-group`.
353        ///
354        /// Reference: <https://www.rfc-editor.org/rfc/rfc5576.html#section-4.2>
355        pub const FID: &str = "FID";
356    }
357
358    pub mod attribute {
359        /// `a=extmap:<id> <uri>`
360        ///
361        /// Reference: RFC 8285 section 5.
362        pub const EXTMAP: &str = "extmap";
363
364        /// `a=fmtp:<format> <format-specific-parameters>`
365        ///
366        /// Reference: <https://www.rfc-editor.org/rfc/rfc8866.html#section-6.15>
367        pub const FMTP: &str = "fmtp";
368
369        /// `a=rtcp-fb:<pt> <feedback-type> [<feedback-parameter>]`
370        ///
371        /// Reference: <https://www.rfc-editor.org/rfc/rfc4585.html#section-4.2>
372        pub const RTCP_FB: &str = "rtcp-fb";
373
374        /// `a=rtcp-mux`
375        ///
376        /// Reference: RFC 5761.
377        pub const RTCP_MUX: &str = "rtcp-mux";
378
379        /// `a=rtpmap:<payload-type> <encoding-name>/<clock-rate>`
380        ///
381        /// Reference: <https://www.rfc-editor.org/rfc/rfc8866.html#section-6.6>
382        pub const RTPMAP: &str = "rtpmap";
383
384        /// `a=rid:<rid-id> <direction> ...`
385        ///
386        /// Reference: RFC 8851 section 4.
387        pub const RID: &str = "rid";
388
389        /// `a=simulcast:<send-or-recv-list> ...`
390        ///
391        /// Reference: RFC 8853 section 5.1.
392        pub const SIMULCAST: &str = "simulcast";
393
394        /// `a=ssrc-group:<semantics> <ssrc-id> ...`
395        ///
396        /// Reference: <https://www.rfc-editor.org/rfc/rfc5576.html#section-4.2>
397        pub const SSRC_GROUP: &str = "ssrc-group";
398
399        /// `a=ssrc:<ssrc-id> <attribute>`
400        ///
401        /// Reference: <https://www.rfc-editor.org/rfc/rfc5576.html#section-4.1>
402        pub const SSRC: &str = "ssrc";
403
404        /// `a=setup:<role>`
405        ///
406        /// References: RFC 4145, RFC 5763.
407        pub const SETUP: &str = "setup";
408
409        /// `a=mid:<mid>`
410        ///
411        /// Reference: RFC 9143 section 9.
412        pub const MID: &str = "mid";
413    }
414
415    /// `a=rid` directions and validation helpers.
416    pub mod rid {
417        pub const DIRECTION_SEND: &str = "send";
418        pub const DIRECTION_RECV: &str = "recv";
419        pub const MAX_ID_OCTETS: usize = 255;
420
421        /// Returns whether `value` is a valid RTP stream identifier.
422        ///
423        /// RFC 8852 section 3 constrains `RtpStreamId` and
424        /// `RepairedRtpStreamId` to 1-255 ASCII alphanumeric octets. RFC
425        /// Editor errata 7132 applies the same bound to RFC 8851 `rid-id`.
426        #[must_use]
427        pub fn is_id(value: &str) -> bool {
428            (1..=MAX_ID_OCTETS).contains(&value.len())
429                && value.as_bytes().iter().all(|byte| is_id_byte(*byte))
430        }
431
432        #[must_use]
433        pub const fn is_id_byte(value: u8) -> bool {
434            matches!(value, b'0'..=b'9' | b'A'..=b'Z' | b'a'..=b'z')
435        }
436    }
437
438    /// `a=rid` restriction parameter names.
439    ///
440    /// Reference: RFC 8851 section 12.2.
441    pub mod rid_restriction {
442        /// Separator between RID restrictions.
443        ///
444        /// Reference: <https://www.rfc-editor.org/rfc/rfc8851.html#section-4>
445        pub const PARAMETER_SEPARATOR: char = ';';
446
447        /// Separator between a RID restriction name and value.
448        ///
449        /// Reference: <https://www.rfc-editor.org/rfc/rfc8851.html#section-4>
450        pub const NAME_VALUE_SEPARATOR: char = '=';
451
452        pub const PAYLOAD_TYPES: &str = "pt";
453        pub const MAX_WIDTH: &str = "max-width";
454        pub const MAX_HEIGHT: &str = "max-height";
455        pub const MAX_FPS: &str = "max-fps";
456        pub const MAX_FRAME_SIZE: &str = "max-fs";
457        pub const MAX_BITRATE: &str = "max-br";
458        pub const MAX_PIXEL_RATE: &str = "max-pps";
459        pub const MAX_BITS_PER_PIXEL: &str = "max-bpp";
460        pub const DEPENDS_ON: &str = "depend";
461    }
462
463    /// `a=simulcast` list delimiters and prefixes.
464    ///
465    /// Reference: RFC 8853 section 5.1.
466    pub mod simulcast {
467        pub const DIRECTION_SEND: &str = super::rid::DIRECTION_SEND;
468        pub const DIRECTION_RECV: &str = super::rid::DIRECTION_RECV;
469        pub const STREAM_SEPARATOR: char = ';';
470        pub const ALTERNATIVE_SEPARATOR: char = ',';
471        pub const INITIAL_PAUSE_PREFIX: char = '~';
472
473        #[must_use]
474        pub fn strip_initial_pause_prefix(value: &str) -> Option<&str> {
475            value.strip_prefix(INITIAL_PAUSE_PREFIX)
476        }
477    }
478
479    pub mod transport_protocol {
480        /// `m=<media> <port> UDP/TLS/RTP/SAVPF ...`
481        ///
482        /// Reference: RFC 8829 section 5.8.
483        pub const UDP_TLS_RTP_SAVPF: &str = "UDP/TLS/RTP/SAVPF";
484
485        /// `m=<media> <port> UDP/TLS/RTP/SAVP ...`
486        ///
487        /// Reference: RFC 8829 section 5.8.
488        pub const UDP_TLS_RTP_SAVP: &str = "UDP/TLS/RTP/SAVP";
489
490        /// `m=<media> <port> RTP/SAVPF ...`
491        ///
492        /// Reference: RFC 8829 section 5.8.
493        pub const RTP_SAVPF: &str = "RTP/SAVPF";
494
495        /// `m=<media> <port> RTP/SAVP ...`
496        ///
497        /// Reference: RFC 8829 section 5.8.
498        pub const RTP_SAVP: &str = "RTP/SAVP";
499
500        /// `m=<media> <port> UDP/DTLS/SCTP ...`
501        ///
502        /// Reference: RFC 8841.
503        pub const UDP_DTLS_SCTP: &str = "UDP/DTLS/SCTP";
504
505        /// `m=<media> <port> TCP/DTLS/SCTP ...`
506        ///
507        /// Reference: RFC 8841.
508        pub const TCP_DTLS_SCTP: &str = "TCP/DTLS/SCTP";
509    }
510
511    pub mod setup_role {
512        pub const ACTIVE: &str = "active";
513        pub const PASSIVE: &str = "passive";
514        pub const ACTPASS: &str = "actpass";
515        pub const HOLDCONN: &str = "holdconn";
516    }
517
518    pub mod direction {
519        /// `a=inactive`
520        ///
521        /// Reference: <https://www.rfc-editor.org/rfc/rfc8866.html#section-6.7.4>
522        pub const INACTIVE: &str = "inactive";
523
524        /// `a=recvonly`
525        ///
526        /// Reference: <https://www.rfc-editor.org/rfc/rfc8866.html#section-6.7.1>
527        pub const RECV_ONLY: &str = "recvonly";
528
529        /// `a=sendrecv`
530        ///
531        /// Reference: <https://www.rfc-editor.org/rfc/rfc8866.html#section-6.7.2>
532        pub const SEND_RECV: &str = "sendrecv";
533
534        /// `a=sendonly`
535        ///
536        /// Reference: <https://www.rfc-editor.org/rfc/rfc8866.html#section-6.7.3>
537        pub const SEND_ONLY: &str = "sendonly";
538    }
539}
540
541/// Direction tokens used by RFC 8851 RID and RFC 8853 simulcast attributes.
542///
543/// Parsing uses the case-sensitive RFC tokens.
544#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
545pub enum RtpStreamDirection {
546    Send,
547    Recv,
548}
549
550impl RtpStreamDirection {
551    #[must_use]
552    pub const fn as_str(self) -> &'static str {
553        match self {
554            Self::Send => sdp::rid::DIRECTION_SEND,
555            Self::Recv => sdp::rid::DIRECTION_RECV,
556        }
557    }
558
559    #[must_use]
560    pub fn parse(token: &str) -> Option<Self> {
561        match token {
562            sdp::rid::DIRECTION_SEND => Some(Self::Send),
563            sdp::rid::DIRECTION_RECV => Some(Self::Recv),
564            _ => None,
565        }
566    }
567}
568
569impl AsRef<str> for RtpStreamDirection {
570    fn as_ref(&self) -> &str {
571        match self {
572            Self::Send => sdp::rid::DIRECTION_SEND,
573            Self::Recv => sdp::rid::DIRECTION_RECV,
574        }
575    }
576}
577
578impl fmt::Display for RtpStreamDirection {
579    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
580        f.write_str(self.as_ref())
581    }
582}
583
584/// DTLS fingerprint algorithms currently supported by the runtime.
585#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
586pub enum DtlsFingerprintAlgorithm {
587    Sha256,
588}
589
590impl DtlsFingerprintAlgorithm {
591    #[must_use]
592    pub const fn as_str(self) -> &'static str {
593        match self {
594            Self::Sha256 => "sha-256",
595        }
596    }
597
598    #[must_use]
599    pub fn parse(token: &str) -> Option<Self> {
600        if token.eq_ignore_ascii_case("sha-256") {
601            return Some(Self::Sha256);
602        }
603        None
604    }
605}
606
607impl AsRef<str> for DtlsFingerprintAlgorithm {
608    fn as_ref(&self) -> &str {
609        match self {
610            Self::Sha256 => "sha-256",
611        }
612    }
613}
614
615impl fmt::Display for DtlsFingerprintAlgorithm {
616    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
617        f.write_str(self.as_ref())
618    }
619}
620
621/// RTP header-extension URIs commonly needed by WebRTC endpoints.
622pub mod rtp_header_extension_uri {
623    macro_rules! rtp_header_extension_urn {
624        ($suffix:literal) => {
625            concat!("urn:ietf:params:rtp-hdrext:", $suffix)
626        };
627    }
628
629    macro_rules! rtp_header_extension_sdes_urn {
630        ($suffix:literal) => {
631            concat!("urn:ietf:params:rtp-hdrext:sdes:", $suffix)
632        };
633    }
634
635    /// MID RTP header extension URI.
636    ///
637    /// Reference: RFC 9143 section 16.4.
638    pub const MID: &str = rtp_header_extension_sdes_urn!("mid");
639
640    /// Audio level RTP header extension URI.
641    ///
642    /// Reference: RFC 6464 section 3.
643    pub const SSRC_AUDIO_LEVEL: &str = rtp_header_extension_urn!("ssrc-audio-level");
644
645    /// Mixer-to-client audio level RTP header extension URI.
646    ///
647    /// Reference: RFC 6465 section 4.
648    pub const CSRC_AUDIO_LEVEL: &str = rtp_header_extension_urn!("csrc-audio-level");
649
650    /// RTP stream ID extension URI.
651    ///
652    /// Reference: RFC 8852.
653    pub const RTP_STREAM_ID: &str = rtp_header_extension_sdes_urn!("rtp-stream-id");
654
655    /// Repaired RTP stream ID extension URI.
656    ///
657    /// Reference: RFC 8852.
658    pub const REPAIRED_RTP_STREAM_ID: &str =
659        rtp_header_extension_sdes_urn!("repaired-rtp-stream-id");
660
661    /// Video Frame Marking RTP header extension URI.
662    ///
663    /// Reference: RFC 9626 section 3.4.
664    pub const FRAME_MARKING: &str = rtp_header_extension_urn!("framemarking");
665
666    /// Absolute send time RTP header extension URI.
667    ///
668    /// The RTP header-extension framework identify extensions by URI string,
669    /// and that URI is signaled verbatim in SDP `a=extmap` lines. Even though
670    /// this identifier looks like an HTTP URL, it just is a protocol name.
671    ///
672    /// Current WebRTC stacks use this exact literal, so we preserve it for interoperability.
673    ///
674    /// Reference: <https://www.webrtc.org/experiments/rtp-hdrext/abs-send-time>
675    pub const ABS_SEND_TIME: &str = "http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time";
676
677    /// Transport-wide sequence number RTP header extension URI.
678    ///
679    /// This value is likewise the negotiated wire identifier carried in SDP
680    /// `a=extmap` lines. It is not dereferenced as a network resource; the
681    /// exact string itself is the interoperability key used to match the
682    /// extension.
683    ///
684    /// Browsers and other rtc ecosystems commonly advertise the
685    /// historical `...-01` draft URI literal, so we keeps that deployed
686    /// identifier instead of normalizing it to a different name.
687    ///
688    /// Reference:
689    /// <https://www.ietf.org/archive/id/draft-holmer-rmcat-transport-wide-cc-extensions-01.txt>
690    pub const TRANSPORT_WIDE_CC_DRAFT_01: &str =
691        "http://www.ietf.org/id/draft-holmer-rmcat-transport-wide-cc-extensions-01";
692}
693
694/// RTP header-extension URIs commonly needed by WebRTC endpoints.
695#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
696pub enum RtpHeaderExtensionUri {
697    Mid,
698    RtpStreamId,
699    RepairedRtpStreamId,
700    FrameMarking,
701    AbsSendTime,
702    TransportWideCcDraft01,
703    SsrcAudioLevel,
704    CsrcAudioLevel,
705    Other(String),
706}
707
708impl RtpHeaderExtensionUri {
709    #[must_use]
710    pub fn as_str(&self) -> &str {
711        match self {
712            Self::Mid => rtp_header_extension_uri::MID,
713            Self::RtpStreamId => rtp_header_extension_uri::RTP_STREAM_ID,
714            Self::RepairedRtpStreamId => rtp_header_extension_uri::REPAIRED_RTP_STREAM_ID,
715            Self::FrameMarking => rtp_header_extension_uri::FRAME_MARKING,
716            Self::AbsSendTime => rtp_header_extension_uri::ABS_SEND_TIME,
717            Self::TransportWideCcDraft01 => rtp_header_extension_uri::TRANSPORT_WIDE_CC_DRAFT_01,
718            Self::SsrcAudioLevel => rtp_header_extension_uri::SSRC_AUDIO_LEVEL,
719            Self::CsrcAudioLevel => rtp_header_extension_uri::CSRC_AUDIO_LEVEL,
720            Self::Other(uri) => uri.as_str(),
721        }
722    }
723}
724
725impl From<&str> for RtpHeaderExtensionUri {
726    fn from(value: &str) -> Self {
727        match value {
728            rtp_header_extension_uri::MID => Self::Mid,
729            rtp_header_extension_uri::RTP_STREAM_ID => Self::RtpStreamId,
730            rtp_header_extension_uri::REPAIRED_RTP_STREAM_ID => Self::RepairedRtpStreamId,
731            rtp_header_extension_uri::FRAME_MARKING => Self::FrameMarking,
732            rtp_header_extension_uri::ABS_SEND_TIME => Self::AbsSendTime,
733            rtp_header_extension_uri::TRANSPORT_WIDE_CC_DRAFT_01 => Self::TransportWideCcDraft01,
734            rtp_header_extension_uri::SSRC_AUDIO_LEVEL => Self::SsrcAudioLevel,
735            rtp_header_extension_uri::CSRC_AUDIO_LEVEL => Self::CsrcAudioLevel,
736            _ => Self::Other(value.to_owned()),
737        }
738    }
739}
740
741impl From<String> for RtpHeaderExtensionUri {
742    fn from(value: String) -> Self {
743        Self::from(value.as_str())
744    }
745}
746
747impl AsRef<str> for RtpHeaderExtensionUri {
748    fn as_ref(&self) -> &str {
749        self.as_str()
750    }
751}
752
753impl fmt::Display for RtpHeaderExtensionUri {
754    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
755        f.write_str(self.as_str())
756    }
757}
758
759/// RTCP SDES item type defined for MID.
760///
761/// Reference: RFC 9143 section 16.3.
762pub const RTCP_SDES_ITEM_MID: u8 = 15;
763
764#[cfg(test)]
765#[path = "TESTS/webrtc.rs"]
766mod tests;