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;