Skip to main content

sdp_types/
enums.rs

1// Copyright (C) 2026 Taruntej Kanakamalla <tarun@centricular.com>
2//
3// Licensed under the MIT license, see the LICENSE file or <http://opensource.org/licenses/MIT>
4
5//! Contains all the helper enums used by a Session, Media and other Attribute structs
6
7use std::{
8    fmt::{Display, Write},
9    net::IpAddr,
10    str::FromStr,
11};
12
13use crate::{attributes::SrtpKeyParam, TypedAttribute};
14
15/// Errors while parsing strings to Enum
16#[derive(Debug, PartialEq, Eq, Clone)]
17#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
18pub enum ParseEnumError {
19    Invalid(String),
20}
21
22impl std::error::Error for ParseEnumError {}
23
24impl std::fmt::Display for ParseEnumError {
25    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
26        match self {
27            ParseEnumError::Invalid(s) => {
28                write!(f, "Failed to parse {s} as an enum type")
29            }
30        }
31    }
32}
33
34/// Type of network of the originator or a connection of the session.
35///
36/// See [RFC 8866 Section 5.2](https://datatracker.ietf.org/doc/html/rfc8866#section-5.2),
37/// [RFC 8866 Section 5.7](https://datatracker.ietf.org/doc/html/rfc8866#section-5.7) and
38/// [RFC 8866 Section 8.2.6](https://datatracker.ietf.org/doc/html/rfc8866#section-8.2.6) for more details
39#[derive(Debug, PartialEq, Eq, Clone)]
40#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
41pub enum NetType {
42    /// Internet
43    In,
44    /// Telephone Network
45    Tn,
46    /// ATM Bearer Connection
47    Atm,
48    /// Public Switched Telephone Network
49    Pstn,
50    /// Other
51    Other(String),
52}
53
54impl NetType {
55    pub fn as_str(&self) -> &str {
56        match self {
57            NetType::In => "IN",
58            NetType::Tn => "TN",
59            NetType::Atm => "ATM",
60            NetType::Pstn => "PSTN",
61            NetType::Other(nettype) => nettype.as_str(),
62        }
63    }
64}
65
66impl FromStr for NetType {
67    // FIXME use the never type when it is stable
68    type Err = ();
69
70    fn from_str(s: &str) -> Result<Self, Self::Err> {
71        if "IN".eq_ignore_ascii_case(s) {
72            Ok(NetType::In)
73        } else if "TN".eq_ignore_ascii_case(s) {
74            Ok(NetType::Tn)
75        } else if "ATM".eq_ignore_ascii_case(s) {
76            Ok(NetType::Atm)
77        } else if "PSTN".eq_ignore_ascii_case(s) {
78            Ok(NetType::Pstn)
79        } else {
80            Ok(NetType::Other(s.to_string()))
81        }
82    }
83}
84
85impl From<&str> for NetType {
86    fn from(value: &str) -> Self {
87        NetType::from_str(value).expect("infallible")
88    }
89}
90
91impl Display for NetType {
92    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
93        f.write_str(self.as_str())
94    }
95}
96
97/// Type of address of the originator or a connection of the session
98///
99/// See [RFC 8866 Section 5.2](https://datatracker.ietf.org/doc/html/rfc8866#section-5.2),
100/// [RFC 8866 Section 5.7](https://datatracker.ietf.org/doc/html/rfc8866#section-5.7)
101#[derive(Debug, PartialEq, Eq, Clone)]
102#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
103pub enum AddrType {
104    /// IPv4 address
105    Ip4,
106    /// IPv6 address
107    Ip6,
108    /// Other
109    Other(String),
110}
111
112impl AddrType {
113    pub fn new(addrtype: impl AsRef<str>) -> Self {
114        let addrtype = addrtype.as_ref();
115        if "IP4".eq_ignore_ascii_case(addrtype) {
116            AddrType::Ip4
117        } else if "IP6".eq_ignore_ascii_case(addrtype) {
118            AddrType::Ip6
119        } else {
120            AddrType::Other(addrtype.to_string())
121        }
122    }
123
124    /// Whether `self` matches an IPv4 or IPv6 address.
125    pub fn is_ip(&self) -> bool {
126        matches!(self, AddrType::Ip4 | AddrType::Ip6)
127    }
128
129    pub fn as_str(&self) -> &str {
130        match self {
131            AddrType::Ip4 => "IP4",
132            AddrType::Ip6 => "IP6",
133            AddrType::Other(other) => other.as_str(),
134        }
135    }
136}
137
138impl FromStr for AddrType {
139    // FIXME use the never type when it is stable
140    type Err = ();
141
142    fn from_str(s: &str) -> Result<Self, Self::Err> {
143        if "IP4".eq_ignore_ascii_case(s) {
144            Ok(AddrType::Ip4)
145        } else if "IP6".eq_ignore_ascii_case(s) {
146            Ok(AddrType::Ip6)
147        } else {
148            Ok(AddrType::Other(s.to_string()))
149        }
150    }
151}
152
153impl From<&str> for AddrType {
154    fn from(value: &str) -> Self {
155        AddrType::from_str(value).expect("infallible")
156    }
157}
158
159impl From<IpAddr> for AddrType {
160    fn from(addr: IpAddr) -> Self {
161        match addr {
162            IpAddr::V4(_) => AddrType::Ip4,
163            IpAddr::V6(_) => AddrType::Ip6,
164        }
165    }
166}
167
168impl Display for AddrType {
169    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
170        f.write_str(self.as_str())
171    }
172}
173
174/// Type of the Bandwidth value.
175///
176/// See [RFC 8866 Section 5.8](https://datatracker.ietf.org/doc/html/rfc8866#section-5.8) for more details.
177#[derive(Debug, PartialEq, Eq, Clone, Copy)]
178#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
179pub enum BandwidthType {
180    /// Conference total - maximum bandwidth a session will use
181    Ct,
182    /// Application Specific maximum bandwidth
183    As,
184    /// Bandwidth assigned for RTCP reports by active receivers. See [RFC 3890 Section 1.1.3](https://datatracker.ietf.org/doc/html/rfc3890#section-1.1.3)
185    Rr,
186    /// Bandwidth assigned for RTCP reports by active senders. See [RFC 3890 Section 1.1.3](https://datatracker.ietf.org/doc/html/rfc3890#section-1.1.3)
187    Rs,
188}
189
190impl BandwidthType {
191    pub fn as_str(&self) -> &'static str {
192        match self {
193            BandwidthType::As => "AS",
194            BandwidthType::Ct => "CT",
195            BandwidthType::Rr => "RR",
196            BandwidthType::Rs => "RS",
197        }
198    }
199}
200
201impl FromStr for BandwidthType {
202    type Err = ParseEnumError;
203
204    fn from_str(s: &str) -> Result<Self, Self::Err> {
205        if "AS".eq_ignore_ascii_case(s) {
206            Ok(BandwidthType::As)
207        } else if "CT".eq_ignore_ascii_case(s) {
208            Ok(BandwidthType::Ct)
209        } else if "RR".eq_ignore_ascii_case(s) {
210            Ok(BandwidthType::Rr)
211        } else if "RS".eq_ignore_ascii_case(s) {
212            Ok(BandwidthType::Rs)
213        } else {
214            Err(ParseEnumError::Invalid(s.to_string()))
215        }
216    }
217}
218
219impl Display for BandwidthType {
220    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
221        f.write_str(self.as_str())
222    }
223}
224
225/// Method of encryption (Obsolete)
226///
227/// Note: This field is obsolete and MUST NOT be used. It is included only for legacy reasons
228/// See [RFC 8866 Section 5.12](https://datatracker.ietf.org/doc/html/rfc8866#section-5.12)
229#[derive(Debug, PartialEq, Eq, Clone, Copy)]
230#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
231pub enum KeyMethod {
232    /// Untransformed
233    Clear,
234    /// Base64 encoded
235    Base64,
236    /// URI to obtain the key
237    Uri,
238    /// User should be prompted for the key
239    Prompt,
240}
241
242impl KeyMethod {
243    pub fn as_str(&self) -> &'static str {
244        match self {
245            KeyMethod::Clear => "clear",
246            KeyMethod::Base64 => "base64",
247            KeyMethod::Uri => "uri",
248            KeyMethod::Prompt => "prompt",
249        }
250    }
251}
252
253impl FromStr for KeyMethod {
254    type Err = ParseEnumError;
255
256    fn from_str(s: &str) -> Result<Self, Self::Err> {
257        // case-sensitive
258        match s {
259            "clear" => Ok(KeyMethod::Clear),
260            "base64" => Ok(KeyMethod::Base64),
261            "uri" => Ok(KeyMethod::Uri),
262            "prompt" => Ok(KeyMethod::Prompt),
263            _ => Err(ParseEnumError::Invalid(s.to_string())),
264        }
265    }
266}
267
268impl Display for KeyMethod {
269    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
270        f.write_str(self.as_str())
271    }
272}
273
274/// The media type
275///
276/// See [RFC 8866 Section 5.14](https://datatracker.ietf.org/doc/html/rfc8866#section-5.14)
277#[derive(Debug, PartialEq, Eq, Clone, Copy)]
278#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
279pub enum MediaType {
280    /// Audio type
281    Audio,
282    /// Video type
283    Video,
284    /// Text type
285    Text,
286    /// Application type
287    Application,
288    /// Message type
289    Message,
290    /// Image type. See [RFC 6466](https://datatracker.ietf.org/doc/html/rfc6466)
291    Image,
292}
293
294impl MediaType {
295    pub fn as_str(&self) -> &'static str {
296        match self {
297            MediaType::Audio => "audio",
298            MediaType::Video => "video",
299            MediaType::Text => "text",
300            MediaType::Application => "application",
301            MediaType::Message => "message",
302            MediaType::Image => "image",
303        }
304    }
305}
306
307impl FromStr for MediaType {
308    type Err = ParseEnumError;
309
310    fn from_str(s: &str) -> Result<Self, Self::Err> {
311        if "audio".eq_ignore_ascii_case(s) {
312            Ok(MediaType::Audio)
313        } else if "video".eq_ignore_ascii_case(s) {
314            Ok(MediaType::Video)
315        } else if "text".eq_ignore_ascii_case(s) {
316            Ok(MediaType::Text)
317        } else if "application".eq_ignore_ascii_case(s) {
318            Ok(MediaType::Application)
319        } else if "message".eq_ignore_ascii_case(s) {
320            Ok(MediaType::Message)
321        } else if "image".eq_ignore_ascii_case(s) {
322            Ok(MediaType::Image)
323        } else {
324            Err(ParseEnumError::Invalid(s.to_string()))
325        }
326    }
327}
328
329impl Display for MediaType {
330    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
331        f.write_str(self.as_str())
332    }
333}
334
335/// Transport Protocol for the media
336///
337/// See [RFC 8866 Section 5.14](https://datatracker.ietf.org/doc/html/rfc8866#section-5.14)
338#[derive(Debug, PartialEq, Eq, Clone)]
339#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
340pub enum TransportProto {
341    /// Direct UDP
342    Udp,
343    /// RTP over UDP
344    RtpAvp,
345    /// Secure RTP over UDP
346    RtpSavp,
347    /// RTP over UDP with RTCP-based feedback
348    RtpAvpf,
349    /// Secure RTP over UDP with RTCP-based feedback
350    RtpSavpf,
351    /// RTP over TCP/DTLS
352    TcpDtlsRtpSavp,
353    /// RTP over TCP/DTLS with RTCP-based feedback
354    TcpDtlsRtpSavpf,
355    /// RTP over UDP/TLS
356    UdpTlsRtpSavp,
357    /// RTP over UDP/TLS with RTCP-based feedback
358    UdpTlsRtpSavpf,
359    UdpDtlsSctp,
360    TcpDtlsSctp,
361    DtlsSctp,
362    Other(String),
363}
364
365impl TransportProto {
366    pub fn as_str(&self) -> &str {
367        match self {
368            // The strings are case-insensitive, but the spec (RFC 8866) uses lower-case of the "udp" protocol
369            // and upper-case for the others so keeping it the same
370            TransportProto::Udp => "udp",
371            TransportProto::RtpAvp => "RTP/AVP",
372            TransportProto::RtpAvpf => "RTP/AVPF",
373            TransportProto::RtpSavp => "RTP/SAVP",
374            TransportProto::RtpSavpf => "RTP/SAVPF",
375            TransportProto::TcpDtlsRtpSavp => "TCP/DTLS/RTP/SAVP",
376            TransportProto::TcpDtlsRtpSavpf => "TCP/DTLS/RTP/SAVPF",
377            TransportProto::UdpTlsRtpSavp => "UDP/TLS/RTP/SAVP",
378            TransportProto::UdpTlsRtpSavpf => "UDP/TLS/RTP/SAVPF",
379            TransportProto::UdpDtlsSctp => "UDP/DTLS/SCTP",
380            TransportProto::TcpDtlsSctp => "TCP/DTLS/SCTP",
381            TransportProto::DtlsSctp => "DTLS/SCTP",
382            TransportProto::Other(proto) => proto.as_str(),
383        }
384    }
385
386    /// Whether this is an RTP transport (any of the `RTP/...` profiles).
387    ///
388    /// Profile strings are classified by their RTP profile component
389    /// (RFC 8866, section 5.7), so unregistered profiles containing it (e.g.
390    /// the legacy `RTP/AVP/TCP`) are also detected. Returns `false` for SCTP
391    /// transports and [`TransportProto::Udp`].
392    pub fn is_rtp(&self) -> bool {
393        let proto = self.as_str();
394        proto.starts_with("RTP/") || proto.contains("/RTP/")
395    }
396
397    /// Whether this is an SCTP transport (used for datachannels).
398    pub fn is_sctp(&self) -> bool {
399        self.as_str().ends_with("/SCTP")
400    }
401
402    /// Whether this is an RTP transport secured with SRTP (the `SAVP`
403    /// profiles).
404    pub fn is_secure(&self) -> bool {
405        self.is_rtp() && self.as_str().contains("SAVP")
406    }
407
408    /// Whether this RTP transport includes the RTP feedback (`F`) profile
409    /// extension, i.e. it supports per-PT `a=rtcp-fb` attributes.
410    pub fn is_avpf(&self) -> bool {
411        self.is_rtp() && self.as_str().ends_with("AVPF")
412    }
413}
414
415impl FromStr for TransportProto {
416    // FIXME use the never type when it is stable
417    type Err = ();
418
419    fn from_str(s: &str) -> Result<Self, Self::Err> {
420        // The strings are case-insensitive, but the spec (RFC 8866) uses lower-case of the "udp" protocol
421        // and upper-case for the others so keeping it the same
422        if "udp".eq_ignore_ascii_case(s) {
423            Ok(TransportProto::Udp)
424        } else if "RTP/AVP".eq_ignore_ascii_case(s) {
425            Ok(TransportProto::RtpAvp)
426        } else if "RTP/AVPF".eq_ignore_ascii_case(s) {
427            Ok(TransportProto::RtpAvpf)
428        } else if "RTP/SAVP".eq_ignore_ascii_case(s) {
429            Ok(TransportProto::RtpSavp)
430        } else if "RTP/SAVPF".eq_ignore_ascii_case(s) {
431            Ok(TransportProto::RtpSavpf)
432        } else if "TCP/DTLS/RTP/SAVP".eq_ignore_ascii_case(s) {
433            Ok(TransportProto::TcpDtlsRtpSavp)
434        } else if "TCP/DTLS/RTP/SAVPF".eq_ignore_ascii_case(s) {
435            Ok(TransportProto::TcpDtlsRtpSavpf)
436        } else if "UDP/TLS/RTP/SAVP".eq_ignore_ascii_case(s) {
437            Ok(TransportProto::UdpTlsRtpSavp)
438        } else if "UDP/TLS/RTP/SAVPF".eq_ignore_ascii_case(s) {
439            Ok(TransportProto::UdpTlsRtpSavpf)
440        } else if "UDP/DTLS/SCTP".eq_ignore_ascii_case(s) {
441            Ok(TransportProto::UdpDtlsSctp)
442        } else if "TCP/DTLS/SCTP".eq_ignore_ascii_case(s) {
443            Ok(TransportProto::TcpDtlsSctp)
444        } else if "DTLS/SCTP".eq_ignore_ascii_case(s) {
445            Ok(TransportProto::DtlsSctp)
446        } else {
447            Ok(TransportProto::Other(s.to_string()))
448        }
449    }
450}
451
452impl From<&str> for TransportProto {
453    fn from(value: &str) -> Self {
454        TransportProto::from_str(value).expect("infallible")
455    }
456}
457
458impl Display for TransportProto {
459    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
460        f.write_str(self.as_str())
461    }
462}
463
464/// Direction of a restricted RTP stream
465///
466/// See [RFC 8851 Section 4](https://datatracker.ietf.org/doc/html/rfc8851#section-4)
467#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
468#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
469pub enum RidDirection {
470    /// Sender-side restriction
471    Send,
472    /// Receiver-side restriction
473    Recv,
474}
475
476impl RidDirection {
477    pub fn as_str(&self) -> &'static str {
478        match self {
479            RidDirection::Send => "send",
480            RidDirection::Recv => "recv",
481        }
482    }
483}
484
485impl FromStr for RidDirection {
486    type Err = ParseEnumError;
487
488    fn from_str(s: &str) -> Result<Self, Self::Err> {
489        // The grammar (RFC 8851, section 10) is case-sensitive; parsing is
490        // case-insensitive like everywhere else in this crate.
491        if s.eq_ignore_ascii_case("send") {
492            Ok(RidDirection::Send)
493        } else if s.eq_ignore_ascii_case("recv") {
494            Ok(RidDirection::Recv)
495        } else {
496            Err(ParseEnumError::Invalid(s.to_string()))
497        }
498    }
499}
500
501impl std::fmt::Display for RidDirection {
502    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
503        f.write_str(self.as_str())
504    }
505}
506
507/// Secure Hash functions
508///
509/// See [RFC 8122 Table 1](https://datatracker.ietf.org/doc/html/rfc8122#section-8)
510#[derive(Debug, Clone, PartialEq, Eq)]
511#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
512pub enum HashFunc {
513    Sha1,
514    Sha224,
515    Sha256,
516    Sha384,
517    Sha512,
518    Md5,
519    Md2,
520    Other(String),
521}
522
523impl HashFunc {
524    pub fn new(hash_func: impl AsRef<str>) -> Self {
525        let hash_func = hash_func.as_ref();
526
527        if hash_func.eq_ignore_ascii_case("sha-1") {
528            HashFunc::Sha1
529        } else if hash_func.eq_ignore_ascii_case("sha-224") {
530            HashFunc::Sha224
531        } else if hash_func.eq_ignore_ascii_case("sha-256") {
532            HashFunc::Sha256
533        } else if hash_func.eq_ignore_ascii_case("sha-384") {
534            HashFunc::Sha384
535        } else if hash_func.eq_ignore_ascii_case("sha-512") {
536            HashFunc::Sha512
537        } else if hash_func.eq_ignore_ascii_case("md-5") {
538            HashFunc::Md5
539        } else if hash_func.eq_ignore_ascii_case("md-2") {
540            HashFunc::Md2
541        } else {
542            HashFunc::Other(hash_func.to_string())
543        }
544    }
545
546    pub fn as_str(&self) -> &str {
547        match self {
548            HashFunc::Sha1 => "sha-1",
549            HashFunc::Sha224 => "sha-224",
550            HashFunc::Sha256 => "sha-256",
551            HashFunc::Sha384 => "sha-384",
552            HashFunc::Sha512 => "sha-512",
553            HashFunc::Md5 => "md-5",
554            HashFunc::Md2 => "md-2",
555            HashFunc::Other(s) => s.as_str(),
556        }
557    }
558}
559
560/// Semantics for Group Attribute
561///
562/// See [RFC 5576 Section 12.3](https://datatracker.ietf.org/doc/html/rfc5576#section-12.3)
563#[derive(Debug, Clone, PartialEq, Eq)]
564#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
565pub enum GroupSemantics {
566    /// Lip Synchronization
567    ///
568    /// See [RFC 5888 Section 7](https://datatracker.ietf.org/doc/html/rfc5888#section-7)
569    LS,
570    /// Flow Identification
571    ///
572    /// See [RFC 5888 Section 8](https://datatracker.ietf.org/doc/html/rfc5888#section-8)
573    FID,
574    /// Single Reservation Flow
575    ///
576    /// See [RFC 3524 Section 2](https://datatracker.ietf.org/doc/html/rfc3524#section-2)
577    SRF,
578    /// Alternative Network Address Types
579    ///
580    /// See [RFC 4091 Section 3](https://datatracker.ietf.org/doc/html/rfc4091#section-3)
581    ANAT,
582    /// Forward Error Correction
583    ///
584    /// See [RFC 4756 Section 4](https://datatracker.ietf.org/doc/html/rfc4756#section-4)
585    FEC,
586    /// Decoding Dependency
587    ///
588    /// See [RFC 5583 Section 5.2.1](https://datatracker.ietf.org/doc/html/rfc5583#section-5.2.1)
589    DDP,
590    /// Other Semantics
591    Other(String),
592}
593
594impl GroupSemantics {
595    pub fn new(semantics: impl AsRef<str>) -> Self {
596        let semantics = semantics.as_ref();
597
598        if "LS".eq_ignore_ascii_case(semantics) {
599            GroupSemantics::LS
600        } else if "FID".eq_ignore_ascii_case(semantics) {
601            GroupSemantics::FID
602        } else if "SRF".eq_ignore_ascii_case(semantics) {
603            GroupSemantics::SRF
604        } else if "ANAT".eq_ignore_ascii_case(semantics) {
605            GroupSemantics::ANAT
606        } else if "FEC".eq_ignore_ascii_case(semantics) {
607            GroupSemantics::FEC
608        } else if "DDP".eq_ignore_ascii_case(semantics) {
609            GroupSemantics::DDP
610        } else {
611            GroupSemantics::Other(semantics.to_string())
612        }
613    }
614
615    pub fn as_str(&self) -> &str {
616        match self {
617            GroupSemantics::LS => "LS",
618            GroupSemantics::FID => "FID",
619            GroupSemantics::SRF => "SRF",
620            GroupSemantics::ANAT => "ANAT",
621            GroupSemantics::DDP => "DDP",
622            GroupSemantics::FEC => "FEC",
623            GroupSemantics::Other(s) => s.as_str(),
624        }
625    }
626}
627
628/// Encryption and authentication algorithms for Crypto attribute
629///
630/// See [RFC 4568 Section 10.3.2](https://datatracker.ietf.org/doc/html/rfc4568#section-10.3.2)
631///
632/// Note: `F8_128_HMAC_SHA1_32` appears in the [RFC 4568 Section 9.2](https://datatracker.ietf.org/doc/html/rfc4568#section-9.2)
633/// grammar but was never registered in the IANA registry, so it is not defined as a variant here.
634/// It will be parsed as `Other`.
635#[derive(Debug, PartialEq, Clone, Eq)]
636#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
637pub enum CryptoSuite {
638    /// AES_CM_128_HMAC_SHA1_80
639    AesCm128HmacSha1_80,
640    /// AES_CM_128_HMAC_SHA1_32
641    AesCm128HmacSha1_32,
642    /// F8_128_HMAC_SHA1_80
643    F8_128HmacSha1_80,
644    /// Other Crypto Suite
645    Other(String),
646}
647
648impl CryptoSuite {
649    pub fn new(crypto_suite: impl AsRef<str>) -> Self {
650        let crypto_suite = crypto_suite.as_ref();
651
652        if "AES_CM_128_HMAC_SHA1_32".eq_ignore_ascii_case(crypto_suite) {
653            CryptoSuite::AesCm128HmacSha1_32
654        } else if "F8_128_HMAC_SHA1_80".eq_ignore_ascii_case(crypto_suite) {
655            CryptoSuite::F8_128HmacSha1_80
656        } else if "AES_CM_128_HMAC_SHA1_80".eq_ignore_ascii_case(crypto_suite) {
657            CryptoSuite::AesCm128HmacSha1_80
658        } else {
659            CryptoSuite::Other(crypto_suite.to_string())
660        }
661    }
662
663    pub fn as_str(&self) -> &str {
664        match self {
665            CryptoSuite::AesCm128HmacSha1_80 => "AES_CM_128_HMAC_SHA1_80",
666            CryptoSuite::AesCm128HmacSha1_32 => "AES_CM_128_HMAC_SHA1_32",
667            CryptoSuite::F8_128HmacSha1_80 => "F8_128_HMAC_SHA1_80",
668            CryptoSuite::Other(s) => s.as_str(),
669        }
670    }
671}
672
673/// Signals whether FEC is applied before or after SRTP processing
674///
675/// See [RFC 4568 Section 6.3.5](https://datatracker.ietf.org/doc/html/rfc4568#section-6.3.5)
676#[derive(Debug, PartialEq, Clone, Eq, Copy)]
677#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
678pub enum FecOrder {
679    /// FEC is applied before SRTP processing
680    FecSrtp,
681    /// FEC is applied after SRTP processing
682    SrtpFec,
683}
684
685/// SRTP session parameters
686///
687/// See [RFC 4568 Section 6.3](https://datatracker.ietf.org/doc/html/rfc4568#section-6.3)
688#[derive(Debug, PartialEq, Clone, Eq)]
689#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
690pub enum SrtpSessionParam {
691    /// Key Derivation Rate
692    ///
693    /// See [RFC 4568 Section 6.3.1](https://datatracker.ietf.org/doc/html/rfc4568#section-6.3.1)
694    Kdr(u8),
695    /// Signals that the SRTP packets are without encryption
696    ///
697    /// See [RFC 4568 Section 6.3.2](https://datatracker.ietf.org/doc/html/rfc4568#section-6.3.2)
698    UnencryptedSrtp,
699    /// Signals that the SRTCP packets are without encryption
700    ///
701    /// See [RFC 4568 Section 6.3.2](https://datatracker.ietf.org/doc/html/rfc4568#section-6.3.2)
702    UnencryptedSrtcp,
703    /// Signals that the SRTP packets are not authenticated. (Not recommended)
704    ///
705    /// See [RFC 4568 Section 6.3.3](https://datatracker.ietf.org/doc/html/rfc4568#section-6.3.3)
706    UnauthenticatedSrtp,
707    /// Signals whether FEC is applied before or after SRTP processing
708    ///
709    /// See [RFC 4568 Section 6.3.4](https://datatracker.ietf.org/doc/html/rfc4568#section-6.3.4)
710    FecOrder(FecOrder),
711    /// Signals the use of separate master key(s) for forward error correction
712    ///
713    /// See [RFC 4568 Section 6.3.5](https://datatracker.ietf.org/doc/html/rfc4568#section-6.3.5)
714    FecKey(Vec<SrtpKeyParam>),
715    /// Window Size Hint - provides a hint for how big the SRTP Window size should be
716    ///
717    /// See [RFC 4568 Section 6.3.6](https://datatracker.ietf.org/doc/html/rfc4568#section-6.3.6)
718    Wsh(u8),
719    /// Unknown parameter
720    Extension(String),
721}
722
723/// Type of the Candidate
724///
725/// See [RFC 8839 Section 5.1](https://datatracker.ietf.org/doc/html/rfc8839#section-5.1)
726#[derive(Debug, PartialEq, Clone, Eq)]
727#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
728pub enum CandidateType {
729    /// Host
730    Host,
731    /// Server-reflexive
732    Srflx,
733    /// Peer-reflexive
734    Prflx,
735    /// Relay
736    Relay,
737    /// Unknown type
738    Other(String),
739}
740
741impl CandidateType {
742    pub fn new(cand_type: impl AsRef<str>) -> Self {
743        let cand_type = cand_type.as_ref();
744
745        if "host".eq_ignore_ascii_case(cand_type) {
746            CandidateType::Host
747        } else if "srflx".eq_ignore_ascii_case(cand_type) {
748            CandidateType::Srflx
749        } else if "prflx".eq_ignore_ascii_case(cand_type) {
750            CandidateType::Prflx
751        } else if "relay".eq_ignore_ascii_case(cand_type) {
752            CandidateType::Relay
753        } else {
754            CandidateType::Other(cand_type.to_string())
755        }
756    }
757
758    pub fn as_str(&self) -> &str {
759        match self {
760            CandidateType::Host => "host",
761            CandidateType::Srflx => "srflx",
762            CandidateType::Prflx => "prflx",
763            CandidateType::Relay => "relay",
764            CandidateType::Other(o) => o.as_str(),
765        }
766    }
767}
768
769/// RTCP Positive feedback values
770///
771/// See [RFC 4585 Section 4.2](https://datatracker.ietf.org/doc/html/rfc4585#section-4.2)
772#[derive(Debug, PartialEq, Clone, Eq)]
773#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
774pub enum RtcpFbAck {
775    /// Reference Picture Selection Indication
776    Rpsi,
777    /// Application layer feedback
778    App(Option<String>),
779    /// Congestion Control Feedback
780    ///
781    /// See [RFC 8888 Section 6](https://datatracker.ietf.org/doc/html/rfc8888#section-6)
782    Ccfb,
783    /// Other Ack types
784    Other(String),
785}
786
787/// RTCP Negative feedback values
788///
789/// See [RFC 4585 Section 4.2](https://datatracker.ietf.org/doc/html/rfc4585#section-4.2)
790#[derive(Debug, PartialEq, Clone, Eq)]
791#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
792pub enum RtcpFbNack {
793    /// Picture Loss Indication
794    Pli,
795    /// Slice Loss Indication
796    Sli,
797    /// Reference Picture Selection Indication
798    Rpsi,
799    /// Application layer feedback
800    App(Option<String>),
801    /// Explicit Congestion Notification
802    ///
803    /// See [RFC 6679 Section 6.2](https://datatracker.ietf.org/doc/html/rfc6679#section-6.2)
804    Ecn,
805    /// Other Nack types
806    Other(String),
807}
808
809/// Codec Control using RTCP feedback messages
810///
811/// See [RFC 5104 Section 7.1](https://datatracker.ietf.org/doc/html/rfc5104#section-7.1)
812#[derive(Debug, PartialEq, Clone, Eq)]
813#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
814pub enum RtcpFbCcm {
815    /// Full Intra Request
816    Fir,
817    /// Temporary Maximum Media Stream Bit Rate
818    Tmmbr(Option<String>),
819    /// Temporal-Spatial Trade-off
820    Tstr,
821    /// Video Back Channel Messages
822    Vbcm(Vec<u8>),
823    /// Other messages (for future commands/Indications)
824    Other(String),
825}
826
827/// Types of RTCP feedback values
828#[derive(Debug, PartialEq, Clone, Eq)]
829#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
830pub enum RtcpFbVal {
831    /// Positive Acknowledgement
832    Ack(Option<RtcpFbAck>),
833    /// Negative Acknowledgement
834    Nack(Option<RtcpFbNack>),
835    /// Minimum interval between two Regular RTCP packets in milliseconds
836    TrrInt(u64),
837    /// Codec Control messages
838    Ccm(RtcpFbCcm),
839    /// Transport-wide Congestion Control
840    TransportCc,
841    /// Others Rtcp Fb types
842    Other(String),
843}
844
845impl RtcpFbVal {
846    pub fn is_ack(&self) -> bool {
847        matches!(self, RtcpFbVal::Ack(None))
848    }
849
850    pub fn is_ack_rpsi(&self) -> bool {
851        matches!(self, RtcpFbVal::Ack(Some(RtcpFbAck::Rpsi)))
852    }
853
854    pub fn is_ack_app(&self) -> bool {
855        matches!(self, RtcpFbVal::Ack(Some(RtcpFbAck::App(_))))
856    }
857
858    pub fn is_ack_ccfb(&self) -> bool {
859        matches!(self, RtcpFbVal::Ack(Some(RtcpFbAck::Ccfb)))
860    }
861
862    pub fn is_nack(&self) -> bool {
863        matches!(self, RtcpFbVal::Nack(None))
864    }
865
866    pub fn is_nack_pli(&self) -> bool {
867        matches!(self, RtcpFbVal::Nack(Some(RtcpFbNack::Pli)))
868    }
869
870    pub fn is_nack_sli(&self) -> bool {
871        matches!(self, RtcpFbVal::Nack(Some(RtcpFbNack::Sli)))
872    }
873
874    pub fn is_nack_rpsi(&self) -> bool {
875        matches!(self, RtcpFbVal::Nack(Some(RtcpFbNack::Rpsi)))
876    }
877
878    pub fn is_nack_app(&self) -> bool {
879        matches!(self, RtcpFbVal::Nack(Some(RtcpFbNack::App(_))))
880    }
881
882    pub fn is_nack_ecn(&self) -> bool {
883        matches!(self, RtcpFbVal::Nack(Some(RtcpFbNack::Ecn)))
884    }
885
886    pub fn is_trr_int(&self) -> bool {
887        matches!(self, RtcpFbVal::TrrInt(_))
888    }
889
890    pub fn is_ccm_fir(&self) -> bool {
891        matches!(self, RtcpFbVal::Ccm(RtcpFbCcm::Fir))
892    }
893
894    pub fn is_ccm_tmmbr(&self) -> bool {
895        matches!(self, RtcpFbVal::Ccm(RtcpFbCcm::Tmmbr(_)))
896    }
897
898    pub fn is_ccm_tstr(&self) -> bool {
899        matches!(self, RtcpFbVal::Ccm(RtcpFbCcm::Tstr))
900    }
901
902    pub fn is_ccm_vbcm(&self) -> bool {
903        matches!(self, RtcpFbVal::Ccm(RtcpFbCcm::Vbcm(_)))
904    }
905
906    pub fn is_transport_cc(&self) -> bool {
907        matches!(self, RtcpFbVal::TransportCc)
908    }
909}
910
911impl From<RtcpFbAck> for RtcpFbVal {
912    fn from(value: RtcpFbAck) -> Self {
913        RtcpFbVal::Ack(Some(value))
914    }
915}
916
917impl From<RtcpFbNack> for RtcpFbVal {
918    fn from(value: RtcpFbNack) -> Self {
919        RtcpFbVal::Nack(Some(value))
920    }
921}
922
923impl From<RtcpFbCcm> for RtcpFbVal {
924    fn from(value: RtcpFbCcm) -> Self {
925        RtcpFbVal::Ccm(value)
926    }
927}
928
929impl Display for RtcpFbVal {
930    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
931        match self {
932            RtcpFbVal::Ack(ack) => {
933                write!(f, "ack")?;
934                if let Some(ack) = ack {
935                    match ack {
936                        RtcpFbAck::Rpsi => write!(f, " rpsi")?,
937                        RtcpFbAck::Ccfb => write!(f, " ccfb")?,
938                        RtcpFbAck::App(app) => {
939                            write!(f, " app")?;
940                            if let Some(app_param) = app {
941                                f.write_char(' ')?;
942                                f.write_str(app_param)?;
943                            }
944                        }
945                        RtcpFbAck::Other(other) => {
946                            f.write_char(' ')?;
947                            f.write_str(other)?;
948                        }
949                    }
950                }
951                Ok(())
952            }
953            RtcpFbVal::Nack(nack) => {
954                write!(f, "nack")?;
955                if let Some(nack) = nack {
956                    match nack {
957                        RtcpFbNack::Pli => write!(f, " pli")?,
958                        RtcpFbNack::Sli => write!(f, " sli")?,
959                        RtcpFbNack::Rpsi => write!(f, " rpsi")?,
960                        RtcpFbNack::Ecn => write!(f, " ecn")?,
961                        RtcpFbNack::App(app) => {
962                            write!(f, " app")?;
963                            if let Some(app_param) = app {
964                                f.write_char(' ')?;
965                                f.write_str(app_param)?;
966                            }
967                        }
968                        RtcpFbNack::Other(other) => {
969                            f.write_char(' ')?;
970                            f.write_str(other)?;
971                        }
972                    }
973                }
974                Ok(())
975            }
976            RtcpFbVal::TrrInt(trr_int) => write!(f, "trr-int {trr_int}"),
977            RtcpFbVal::Ccm(ccm) => {
978                write!(f, "ccm")?;
979                match ccm {
980                    RtcpFbCcm::Fir => write!(f, " fir")?,
981                    RtcpFbCcm::Tstr => write!(f, " tstr")?,
982                    RtcpFbCcm::Tmmbr(smaxpr) => {
983                        write!(f, " tmmbr")?;
984                        if let Some(smaxpr) = smaxpr {
985                            f.write_char(' ')?;
986                            f.write_str(smaxpr)?;
987                        }
988                    }
989                    RtcpFbCcm::Vbcm(vbcm) => {
990                        write!(f, " vbcm")?;
991                        for v in vbcm {
992                            f.write_char(' ')?;
993                            write!(f, "{v}")?;
994                        }
995                    }
996                    RtcpFbCcm::Other(other) => {
997                        f.write_char(' ')?;
998                        f.write_str(other)?;
999                    }
1000                }
1001                Ok(())
1002            }
1003            RtcpFbVal::TransportCc => f.write_str("transport-cc"),
1004            RtcpFbVal::Other(other) => f.write_str(other),
1005        }
1006    }
1007}
1008
1009/// Source attribute types
1010///
1011/// See [RFC 5576 Section 6](https://datatracker.ietf.org/doc/html/rfc5576#section-6)
1012#[derive(Debug, Clone, PartialEq, Eq)]
1013#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
1014pub enum SsrcAttribute {
1015    /// See [RFC 5576 Section 6.1](https://datatracker.ietf.org/doc/html/rfc5576#section-6.1)
1016    Cname,
1017    /// See [RFC 5576 Section 6.2](https://datatracker.ietf.org/doc/html/rfc5576#section-6.2)
1018    PreviousSsrc,
1019    /// See [RFC 5576 Section 6.3](https://datatracker.ietf.org/doc/html/rfc5576#section-6.3)
1020    Fmtp,
1021    /// Rtcp attribute
1022    Rtcp,
1023    /// Reference Clock attribute
1024    ReferenceClock,
1025    /// Media Clock Source attribute
1026    MediaClockSource,
1027    /// See [RFC 5576 Section 6.4](https://datatracker.ietf.org/doc/html/rfc5576#section-6.4)
1028    Other(String),
1029}
1030
1031impl SsrcAttribute {
1032    pub fn new(attribute: impl AsRef<str>) -> Self {
1033        use crate::{Fmtp, MediaClockSource, ReferenceClock, Rtcp};
1034
1035        let attr = attribute.as_ref();
1036        if "cname".eq_ignore_ascii_case(attr) {
1037            SsrcAttribute::Cname
1038        } else if "previous-ssrc".eq_ignore_ascii_case(attr) {
1039            SsrcAttribute::PreviousSsrc
1040        } else if <Fmtp as TypedAttribute>::NAME.eq_ignore_ascii_case(attr) {
1041            SsrcAttribute::Fmtp
1042        } else if <Rtcp as TypedAttribute>::NAME.eq_ignore_ascii_case(attr) {
1043            SsrcAttribute::Rtcp
1044        } else if <ReferenceClock as TypedAttribute>::NAME.eq_ignore_ascii_case(attr) {
1045            SsrcAttribute::ReferenceClock
1046        } else if <MediaClockSource as TypedAttribute>::NAME.eq_ignore_ascii_case(attr) {
1047            SsrcAttribute::MediaClockSource
1048        } else {
1049            SsrcAttribute::Other(attr.to_string())
1050        }
1051    }
1052
1053    pub fn as_str(&self) -> &str {
1054        use crate::{Fmtp, MediaClockSource, ReferenceClock, Rtcp};
1055
1056        match self {
1057            SsrcAttribute::Cname => "cname",
1058            SsrcAttribute::PreviousSsrc => "previous-ssrc",
1059            SsrcAttribute::Fmtp => <Fmtp as TypedAttribute>::NAME,
1060            SsrcAttribute::Rtcp => <Rtcp as TypedAttribute>::NAME,
1061            SsrcAttribute::ReferenceClock => <ReferenceClock as TypedAttribute>::NAME,
1062            SsrcAttribute::MediaClockSource => <MediaClockSource as TypedAttribute>::NAME,
1063            SsrcAttribute::Other(other) => other.as_str(),
1064        }
1065    }
1066}
1067
1068impl<T: TypedAttribute> From<T> for SsrcAttribute {
1069    fn from(_attr: T) -> Self {
1070        SsrcAttribute::new(T::NAME)
1071    }
1072}
1073
1074/// Payload format for which feedback messages may be used
1075#[derive(Debug, PartialEq, Clone, Eq)]
1076#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
1077pub enum RtcpFbPt {
1078    /// Fixed payload format
1079    Fmt(u8),
1080    /// Applies to all formats
1081    Wildcard,
1082}
1083
1084impl From<u8> for RtcpFbPt {
1085    fn from(pt: u8) -> Self {
1086        RtcpFbPt::Fmt(pt)
1087    }
1088}
1089
1090/// Candidate connection address type
1091///
1092/// Can be IPv4, IPv6 or a FQDN
1093#[derive(Debug, PartialEq, Clone, Eq)]
1094#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
1095pub enum CandidateAddress {
1096    IpAddr(std::net::IpAddr),
1097    FQDN(String),
1098}
1099
1100#[cfg(test)]
1101mod tests {
1102    use super::*;
1103
1104    #[test]
1105    fn transport_proto_classification() {
1106        let cases = [
1107            // (proto, is_rtp, is_sctp, is_secure, is_avpf)
1108            (TransportProto::Udp, false, false, false, false),
1109            (TransportProto::RtpAvp, true, false, false, false),
1110            (TransportProto::RtpAvpf, true, false, false, true),
1111            (TransportProto::RtpSavp, true, false, true, false),
1112            (TransportProto::RtpSavpf, true, false, true, true),
1113            (TransportProto::TcpDtlsRtpSavp, true, false, true, false),
1114            (TransportProto::TcpDtlsRtpSavpf, true, false, true, true),
1115            (TransportProto::UdpTlsRtpSavp, true, false, true, false),
1116            (TransportProto::UdpTlsRtpSavpf, true, false, true, true),
1117            (TransportProto::UdpDtlsSctp, false, true, false, false),
1118            (TransportProto::TcpDtlsSctp, false, true, false, false),
1119            (TransportProto::DtlsSctp, false, true, false, false),
1120            (
1121                TransportProto::Other("FOO".into()),
1122                false,
1123                false,
1124                false,
1125                false,
1126            ),
1127            (
1128                TransportProto::Other("RTP/AV".into()),
1129                true,
1130                false,
1131                false,
1132                false,
1133            ),
1134            (
1135                TransportProto::Other("RTP/AVP/TCP".into()),
1136                true,
1137                false,
1138                false,
1139                false,
1140            ),
1141            (
1142                TransportProto::Other("UDP/TLS/RTP/SAVPF".into()),
1143                true,
1144                false,
1145                true,
1146                true,
1147            ),
1148            (
1149                TransportProto::Other("AVPF".into()),
1150                false,
1151                false,
1152                false,
1153                false,
1154            ),
1155        ];
1156
1157        for (proto, rtp, sctp, secure, avpf) in cases {
1158            assert_eq!(proto.is_rtp(), rtp, "{:?} is_rtp", proto);
1159            assert_eq!(proto.is_sctp(), sctp, "{:?} is_sctp", proto);
1160            assert_eq!(proto.is_secure(), secure, "{:?} is_secure", proto);
1161            assert_eq!(proto.is_avpf(), avpf, "{:?} is_avpf", proto);
1162        }
1163    }
1164}