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
387impl FromStr for TransportProto {
388    // FIXME use the never type when it is stable
389    type Err = ();
390
391    fn from_str(s: &str) -> Result<Self, Self::Err> {
392        // The strings are case-insensitive, but the spec (RFC 8866) uses lower-case of the "udp" protocol
393        // and upper-case for the others so keeping it the same
394        if "udp".eq_ignore_ascii_case(s) {
395            Ok(TransportProto::Udp)
396        } else if "RTP/AVP".eq_ignore_ascii_case(s) {
397            Ok(TransportProto::RtpAvp)
398        } else if "RTP/AVPF".eq_ignore_ascii_case(s) {
399            Ok(TransportProto::RtpAvpf)
400        } else if "RTP/SAVP".eq_ignore_ascii_case(s) {
401            Ok(TransportProto::RtpSavp)
402        } else if "RTP/SAVPF".eq_ignore_ascii_case(s) {
403            Ok(TransportProto::RtpSavpf)
404        } else if "TCP/DTLS/RTP/SAVP".eq_ignore_ascii_case(s) {
405            Ok(TransportProto::TcpDtlsRtpSavp)
406        } else if "TCP/DTLS/RTP/SAVPF".eq_ignore_ascii_case(s) {
407            Ok(TransportProto::TcpDtlsRtpSavpf)
408        } else if "UDP/TLS/RTP/SAVP".eq_ignore_ascii_case(s) {
409            Ok(TransportProto::UdpTlsRtpSavp)
410        } else if "UDP/TLS/RTP/SAVPF".eq_ignore_ascii_case(s) {
411            Ok(TransportProto::UdpTlsRtpSavpf)
412        } else if "UDP/DTLS/SCTP".eq_ignore_ascii_case(s) {
413            Ok(TransportProto::UdpDtlsSctp)
414        } else if "TCP/DTLS/SCTP".eq_ignore_ascii_case(s) {
415            Ok(TransportProto::TcpDtlsSctp)
416        } else if "DTLS/SCTP".eq_ignore_ascii_case(s) {
417            Ok(TransportProto::DtlsSctp)
418        } else {
419            Ok(TransportProto::Other(s.to_string()))
420        }
421    }
422}
423
424impl From<&str> for TransportProto {
425    fn from(value: &str) -> Self {
426        TransportProto::from_str(value).expect("infallible")
427    }
428}
429
430impl Display for TransportProto {
431    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
432        f.write_str(self.as_str())
433    }
434}
435
436/// Secure Hash functions
437///
438/// See [RFC 8122 Table 1](https://datatracker.ietf.org/doc/html/rfc8122#section-8)
439#[derive(Debug, Clone, PartialEq, Eq)]
440#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
441pub enum HashFunc {
442    Sha1,
443    Sha224,
444    Sha256,
445    Sha384,
446    Sha512,
447    Md5,
448    Md2,
449    Other(String),
450}
451
452impl HashFunc {
453    pub fn new(hash_func: impl AsRef<str>) -> Self {
454        let hash_func = hash_func.as_ref();
455
456        if hash_func.eq_ignore_ascii_case("sha-1") {
457            HashFunc::Sha1
458        } else if hash_func.eq_ignore_ascii_case("sha-224") {
459            HashFunc::Sha224
460        } else if hash_func.eq_ignore_ascii_case("sha-256") {
461            HashFunc::Sha256
462        } else if hash_func.eq_ignore_ascii_case("sha-384") {
463            HashFunc::Sha384
464        } else if hash_func.eq_ignore_ascii_case("sha-512") {
465            HashFunc::Sha512
466        } else if hash_func.eq_ignore_ascii_case("md-5") {
467            HashFunc::Md5
468        } else if hash_func.eq_ignore_ascii_case("md-2") {
469            HashFunc::Md2
470        } else {
471            HashFunc::Other(hash_func.to_string())
472        }
473    }
474
475    pub fn as_str(&self) -> &str {
476        match self {
477            HashFunc::Sha1 => "sha-1",
478            HashFunc::Sha224 => "sha-224",
479            HashFunc::Sha256 => "sha-256",
480            HashFunc::Sha384 => "sha-384",
481            HashFunc::Sha512 => "sha-512",
482            HashFunc::Md5 => "md-5",
483            HashFunc::Md2 => "md-2",
484            HashFunc::Other(s) => s.as_str(),
485        }
486    }
487}
488
489/// Semantics for Group Attribute
490///
491/// See [RFC 5576 Section 12.3](https://datatracker.ietf.org/doc/html/rfc5576#section-12.3)
492#[derive(Debug, Clone, PartialEq, Eq)]
493#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
494pub enum GroupSemantics {
495    /// Lip Synchronization
496    ///
497    /// See [RFC 5888 Section 7](https://datatracker.ietf.org/doc/html/rfc5888#section-7)
498    LS,
499    /// Flow Identification
500    ///
501    /// See [RFC 5888 Section 8](https://datatracker.ietf.org/doc/html/rfc5888#section-8)
502    FID,
503    /// Single Reservation Flow
504    ///
505    /// See [RFC 3524 Section 2](https://datatracker.ietf.org/doc/html/rfc3524#section-2)
506    SRF,
507    /// Alternative Network Address Types
508    ///
509    /// See [RFC 4091 Section 3](https://datatracker.ietf.org/doc/html/rfc4091#section-3)
510    ANAT,
511    /// Forward Error Correction
512    ///
513    /// See [RFC 4756 Section 4](https://datatracker.ietf.org/doc/html/rfc4756#section-4)
514    FEC,
515    /// Decoding Dependency
516    ///
517    /// See [RFC 5583 Section 5.2.1](https://datatracker.ietf.org/doc/html/rfc5583#section-5.2.1)
518    DDP,
519    /// Other Semantics
520    Other(String),
521}
522
523impl GroupSemantics {
524    pub fn new(semantics: impl AsRef<str>) -> Self {
525        let semantics = semantics.as_ref();
526
527        if "LS".eq_ignore_ascii_case(semantics) {
528            GroupSemantics::LS
529        } else if "FID".eq_ignore_ascii_case(semantics) {
530            GroupSemantics::FID
531        } else if "SRF".eq_ignore_ascii_case(semantics) {
532            GroupSemantics::SRF
533        } else if "ANAT".eq_ignore_ascii_case(semantics) {
534            GroupSemantics::ANAT
535        } else if "FEC".eq_ignore_ascii_case(semantics) {
536            GroupSemantics::FEC
537        } else if "DDP".eq_ignore_ascii_case(semantics) {
538            GroupSemantics::DDP
539        } else {
540            GroupSemantics::Other(semantics.to_string())
541        }
542    }
543
544    pub fn as_str(&self) -> &str {
545        match self {
546            GroupSemantics::LS => "LS",
547            GroupSemantics::FID => "FID",
548            GroupSemantics::SRF => "SRF",
549            GroupSemantics::ANAT => "ANAT",
550            GroupSemantics::DDP => "DDP",
551            GroupSemantics::FEC => "FEC",
552            GroupSemantics::Other(s) => s.as_str(),
553        }
554    }
555}
556
557/// Encryption and authentication algorithms for Crypto attribute
558///
559/// See [RFC 4568 Section 10.3.2](https://datatracker.ietf.org/doc/html/rfc4568#section-10.3.2)
560///
561/// Note: `F8_128_HMAC_SHA1_32` appears in the [RFC 4568 Section 9.2](https://datatracker.ietf.org/doc/html/rfc4568#section-9.2)
562/// grammar but was never registered in the IANA registry, so it is not defined as a variant here.
563/// It will be parsed as `Other`.
564#[derive(Debug, PartialEq, Clone, Eq)]
565#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
566pub enum CryptoSuite {
567    /// AES_CM_128_HMAC_SHA1_80
568    AesCm128HmacSha1_80,
569    /// AES_CM_128_HMAC_SHA1_32
570    AesCm128HmacSha1_32,
571    /// F8_128_HMAC_SHA1_80
572    F8_128HmacSha1_80,
573    /// Other Crypto Suite
574    Other(String),
575}
576
577impl CryptoSuite {
578    pub fn new(crypto_suite: impl AsRef<str>) -> Self {
579        let crypto_suite = crypto_suite.as_ref();
580
581        if "AES_CM_128_HMAC_SHA1_32".eq_ignore_ascii_case(crypto_suite) {
582            CryptoSuite::AesCm128HmacSha1_32
583        } else if "F8_128_HMAC_SHA1_80".eq_ignore_ascii_case(crypto_suite) {
584            CryptoSuite::F8_128HmacSha1_80
585        } else if "AES_CM_128_HMAC_SHA1_80".eq_ignore_ascii_case(crypto_suite) {
586            CryptoSuite::AesCm128HmacSha1_80
587        } else {
588            CryptoSuite::Other(crypto_suite.to_string())
589        }
590    }
591
592    pub fn as_str(&self) -> &str {
593        match self {
594            CryptoSuite::AesCm128HmacSha1_80 => "AES_CM_128_HMAC_SHA1_80",
595            CryptoSuite::AesCm128HmacSha1_32 => "AES_CM_128_HMAC_SHA1_32",
596            CryptoSuite::F8_128HmacSha1_80 => "F8_128_HMAC_SHA1_80",
597            CryptoSuite::Other(s) => s.as_str(),
598        }
599    }
600}
601
602/// Signals whether FEC is applied before or after SRTP processing
603///
604/// See [RFC 4568 Section 6.3.5](https://datatracker.ietf.org/doc/html/rfc4568#section-6.3.5)
605#[derive(Debug, PartialEq, Clone, Eq, Copy)]
606#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
607pub enum FecOrder {
608    /// FEC is applied before SRTP processing
609    FecSrtp,
610    /// FEC is applied after SRTP processing
611    SrtpFec,
612}
613
614/// SRTP session parameters
615///
616/// See [RFC 4568 Section 6.3](https://datatracker.ietf.org/doc/html/rfc4568#section-6.3)
617#[derive(Debug, PartialEq, Clone, Eq)]
618#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
619pub enum SrtpSessionParam {
620    /// Key Derivation Rate
621    ///
622    /// See [RFC 4568 Section 6.3.1](https://datatracker.ietf.org/doc/html/rfc4568#section-6.3.1)
623    Kdr(u8),
624    /// Signals that the SRTP packets are without encryption
625    ///
626    /// See [RFC 4568 Section 6.3.2](https://datatracker.ietf.org/doc/html/rfc4568#section-6.3.2)
627    UnencryptedSrtp,
628    /// Signals that the SRTCP packets are without encryption
629    ///
630    /// See [RFC 4568 Section 6.3.2](https://datatracker.ietf.org/doc/html/rfc4568#section-6.3.2)
631    UnencryptedSrtcp,
632    /// Signals that the SRTP packets are not authenticated. (Not recommended)
633    ///
634    /// See [RFC 4568 Section 6.3.3](https://datatracker.ietf.org/doc/html/rfc4568#section-6.3.3)
635    UnauthenticatedSrtp,
636    /// Signals whether FEC is applied before or after SRTP processing
637    ///
638    /// See [RFC 4568 Section 6.3.4](https://datatracker.ietf.org/doc/html/rfc4568#section-6.3.4)
639    FecOrder(FecOrder),
640    /// Signals the use of separate master key(s) for forward error correction
641    ///
642    /// See [RFC 4568 Section 6.3.5](https://datatracker.ietf.org/doc/html/rfc4568#section-6.3.5)
643    FecKey(Vec<SrtpKeyParam>),
644    /// Window Size Hint - provides a hint for how big the SRTP Window size should be
645    ///
646    /// See [RFC 4568 Section 6.3.6](https://datatracker.ietf.org/doc/html/rfc4568#section-6.3.6)
647    Wsh(u8),
648    /// Unknown parameter
649    Extension(String),
650}
651
652/// Type of the Candidate
653///
654/// See [RFC 8839 Section 5.1](https://datatracker.ietf.org/doc/html/rfc8839#section-5.1)
655#[derive(Debug, PartialEq, Clone, Eq)]
656#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
657pub enum CandidateType {
658    /// Host
659    Host,
660    /// Server-reflexive
661    Srflx,
662    /// Peer-reflexive
663    Prflx,
664    /// Relay
665    Relay,
666    /// Unknown type
667    Other(String),
668}
669
670impl CandidateType {
671    pub fn new(cand_type: impl AsRef<str>) -> Self {
672        let cand_type = cand_type.as_ref();
673
674        if "host".eq_ignore_ascii_case(cand_type) {
675            CandidateType::Host
676        } else if "srflx".eq_ignore_ascii_case(cand_type) {
677            CandidateType::Srflx
678        } else if "prflx".eq_ignore_ascii_case(cand_type) {
679            CandidateType::Prflx
680        } else if "relay".eq_ignore_ascii_case(cand_type) {
681            CandidateType::Relay
682        } else {
683            CandidateType::Other(cand_type.to_string())
684        }
685    }
686
687    pub fn as_str(&self) -> &str {
688        match self {
689            CandidateType::Host => "host",
690            CandidateType::Srflx => "srflx",
691            CandidateType::Prflx => "prflx",
692            CandidateType::Relay => "relay",
693            CandidateType::Other(o) => o.as_str(),
694        }
695    }
696}
697
698/// RTCP Positive feedback values
699///
700/// See [RFC 4585 Section 4.2](https://datatracker.ietf.org/doc/html/rfc4585#section-4.2)
701#[derive(Debug, PartialEq, Clone, Eq)]
702#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
703pub enum RtcpFbAck {
704    /// Reference Picture Selection Indication
705    Rpsi,
706    /// Application layer feedback
707    App(Option<String>),
708    /// Congestion Control Feedback
709    ///
710    /// See [RFC 8888 Section 6](https://datatracker.ietf.org/doc/html/rfc8888#section-6)
711    Ccfb,
712    /// Other Ack types
713    Other(String),
714}
715
716/// RTCP Negative feedback values
717///
718/// See [RFC 4585 Section 4.2](https://datatracker.ietf.org/doc/html/rfc4585#section-4.2)
719#[derive(Debug, PartialEq, Clone, Eq)]
720#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
721pub enum RtcpFbNack {
722    /// Picture Loss Indication
723    Pli,
724    /// Slice Loss Indication
725    Sli,
726    /// Reference Picture Selection Indication
727    Rpsi,
728    /// Application layer feedback
729    App(Option<String>),
730    /// Explicit Congestion Notification
731    ///
732    /// See [RFC 6679 Section 6.2](https://datatracker.ietf.org/doc/html/rfc6679#section-6.2)
733    Ecn,
734    /// Other Nack types
735    Other(String),
736}
737
738/// Codec Control using RTCP feedback messages
739///
740/// See [RFC 5104 Section 7.1](https://datatracker.ietf.org/doc/html/rfc5104#section-7.1)
741#[derive(Debug, PartialEq, Clone, Eq)]
742#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
743pub enum RtcpFbCcm {
744    /// Full Intra Request
745    Fir,
746    /// Temporary Maximum Media Stream Bit Rate
747    Tmmbr(Option<String>),
748    /// Temporal-Spatial Trade-off
749    Tstr,
750    /// Video Back Channel Messages
751    Vbcm(Vec<u8>),
752    /// Other messages (for future commands/Indications)
753    Other(String),
754}
755
756/// Types of RTCP feedback values
757#[derive(Debug, PartialEq, Clone, Eq)]
758#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
759pub enum RtcpFbVal {
760    /// Positive Acknowledgement
761    Ack(Option<RtcpFbAck>),
762    /// Negative Acknowledgement
763    Nack(Option<RtcpFbNack>),
764    /// Minimum interval between two Regular RTCP packets in milliseconds
765    TrrInt(u64),
766    /// Codec Control messages
767    Ccm(RtcpFbCcm),
768    /// Transport-wide Congestion Control
769    TransportCc,
770    /// Others Rtcp Fb types
771    Other(String),
772}
773
774impl RtcpFbVal {
775    pub fn is_ack(&self) -> bool {
776        matches!(self, RtcpFbVal::Ack(None))
777    }
778
779    pub fn is_ack_rpsi(&self) -> bool {
780        matches!(self, RtcpFbVal::Ack(Some(RtcpFbAck::Rpsi)))
781    }
782
783    pub fn is_ack_app(&self) -> bool {
784        matches!(self, RtcpFbVal::Ack(Some(RtcpFbAck::App(_))))
785    }
786
787    pub fn is_ack_ccfb(&self) -> bool {
788        matches!(self, RtcpFbVal::Ack(Some(RtcpFbAck::Ccfb)))
789    }
790
791    pub fn is_nack(&self) -> bool {
792        matches!(self, RtcpFbVal::Nack(None))
793    }
794
795    pub fn is_nack_pli(&self) -> bool {
796        matches!(self, RtcpFbVal::Nack(Some(RtcpFbNack::Pli)))
797    }
798
799    pub fn is_nack_sli(&self) -> bool {
800        matches!(self, RtcpFbVal::Nack(Some(RtcpFbNack::Sli)))
801    }
802
803    pub fn is_nack_rpsi(&self) -> bool {
804        matches!(self, RtcpFbVal::Nack(Some(RtcpFbNack::Rpsi)))
805    }
806
807    pub fn is_nack_app(&self) -> bool {
808        matches!(self, RtcpFbVal::Nack(Some(RtcpFbNack::App(_))))
809    }
810
811    pub fn is_nack_ecn(&self) -> bool {
812        matches!(self, RtcpFbVal::Nack(Some(RtcpFbNack::Ecn)))
813    }
814
815    pub fn is_trr_int(&self) -> bool {
816        matches!(self, RtcpFbVal::TrrInt(_))
817    }
818
819    pub fn is_ccm_fir(&self) -> bool {
820        matches!(self, RtcpFbVal::Ccm(RtcpFbCcm::Fir))
821    }
822
823    pub fn is_ccm_tmmbr(&self) -> bool {
824        matches!(self, RtcpFbVal::Ccm(RtcpFbCcm::Tmmbr(_)))
825    }
826
827    pub fn is_ccm_tstr(&self) -> bool {
828        matches!(self, RtcpFbVal::Ccm(RtcpFbCcm::Tstr))
829    }
830
831    pub fn is_ccm_vbcm(&self) -> bool {
832        matches!(self, RtcpFbVal::Ccm(RtcpFbCcm::Vbcm(_)))
833    }
834
835    pub fn is_transport_cc(&self) -> bool {
836        matches!(self, RtcpFbVal::TransportCc)
837    }
838}
839
840impl From<RtcpFbAck> for RtcpFbVal {
841    fn from(value: RtcpFbAck) -> Self {
842        RtcpFbVal::Ack(Some(value))
843    }
844}
845
846impl From<RtcpFbNack> for RtcpFbVal {
847    fn from(value: RtcpFbNack) -> Self {
848        RtcpFbVal::Nack(Some(value))
849    }
850}
851
852impl From<RtcpFbCcm> for RtcpFbVal {
853    fn from(value: RtcpFbCcm) -> Self {
854        RtcpFbVal::Ccm(value)
855    }
856}
857
858impl Display for RtcpFbVal {
859    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
860        match self {
861            RtcpFbVal::Ack(ack) => {
862                write!(f, "ack")?;
863                if let Some(ack) = ack {
864                    match ack {
865                        RtcpFbAck::Rpsi => write!(f, " rpsi")?,
866                        RtcpFbAck::Ccfb => write!(f, " ccfb")?,
867                        RtcpFbAck::App(app) => {
868                            write!(f, " app")?;
869                            if let Some(app_param) = app {
870                                f.write_char(' ')?;
871                                f.write_str(app_param)?;
872                            }
873                        }
874                        RtcpFbAck::Other(other) => {
875                            f.write_char(' ')?;
876                            f.write_str(other)?;
877                        }
878                    }
879                }
880                Ok(())
881            }
882            RtcpFbVal::Nack(nack) => {
883                write!(f, "nack")?;
884                if let Some(nack) = nack {
885                    match nack {
886                        RtcpFbNack::Pli => write!(f, " pli")?,
887                        RtcpFbNack::Sli => write!(f, " sli")?,
888                        RtcpFbNack::Rpsi => write!(f, " rpsi")?,
889                        RtcpFbNack::Ecn => write!(f, " ecn")?,
890                        RtcpFbNack::App(app) => {
891                            write!(f, " app")?;
892                            if let Some(app_param) = app {
893                                f.write_char(' ')?;
894                                f.write_str(app_param)?;
895                            }
896                        }
897                        RtcpFbNack::Other(other) => {
898                            f.write_char(' ')?;
899                            f.write_str(other)?;
900                        }
901                    }
902                }
903                Ok(())
904            }
905            RtcpFbVal::TrrInt(trr_int) => write!(f, "trr-int {trr_int}"),
906            RtcpFbVal::Ccm(ccm) => {
907                write!(f, "ccm")?;
908                match ccm {
909                    RtcpFbCcm::Fir => write!(f, " fir")?,
910                    RtcpFbCcm::Tstr => write!(f, " tstr")?,
911                    RtcpFbCcm::Tmmbr(smaxpr) => {
912                        write!(f, " tmmbr")?;
913                        if let Some(smaxpr) = smaxpr {
914                            f.write_char(' ')?;
915                            f.write_str(smaxpr)?;
916                        }
917                    }
918                    RtcpFbCcm::Vbcm(vbcm) => {
919                        write!(f, " vbcm")?;
920                        for v in vbcm {
921                            f.write_char(' ')?;
922                            write!(f, "{v}")?;
923                        }
924                    }
925                    RtcpFbCcm::Other(other) => {
926                        f.write_char(' ')?;
927                        f.write_str(other)?;
928                    }
929                }
930                Ok(())
931            }
932            RtcpFbVal::TransportCc => f.write_str("transport-cc"),
933            RtcpFbVal::Other(other) => f.write_str(other),
934        }
935    }
936}
937
938/// Source attribute types
939///
940/// See [RFC 5576 Section 6](https://datatracker.ietf.org/doc/html/rfc5576#section-6)
941#[derive(Debug, Clone, PartialEq, Eq)]
942#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
943pub enum SsrcAttribute {
944    /// See [RFC 5576 Section 6.1](https://datatracker.ietf.org/doc/html/rfc5576#section-6.1)
945    Cname,
946    /// See [RFC 5576 Section 6.2](https://datatracker.ietf.org/doc/html/rfc5576#section-6.2)
947    PreviousSsrc,
948    /// See [RFC 5576 Section 6.3](https://datatracker.ietf.org/doc/html/rfc5576#section-6.3)
949    Fmtp,
950    /// Rtcp attribute
951    Rtcp,
952    /// Reference Clock attribute
953    ReferenceClock,
954    /// Media Clock Source attribute
955    MediaClockSource,
956    /// See [RFC 5576 Section 6.4](https://datatracker.ietf.org/doc/html/rfc5576#section-6.4)
957    Other(String),
958}
959
960impl SsrcAttribute {
961    pub fn new(attribute: impl AsRef<str>) -> Self {
962        use crate::{Fmtp, MediaClockSource, ReferenceClock, Rtcp};
963
964        let attr = attribute.as_ref();
965        if "cname".eq_ignore_ascii_case(attr) {
966            SsrcAttribute::Cname
967        } else if "previous-ssrc".eq_ignore_ascii_case(attr) {
968            SsrcAttribute::PreviousSsrc
969        } else if <Fmtp as TypedAttribute>::NAME.eq_ignore_ascii_case(attr) {
970            SsrcAttribute::Fmtp
971        } else if <Rtcp as TypedAttribute>::NAME.eq_ignore_ascii_case(attr) {
972            SsrcAttribute::Rtcp
973        } else if <ReferenceClock as TypedAttribute>::NAME.eq_ignore_ascii_case(attr) {
974            SsrcAttribute::ReferenceClock
975        } else if <MediaClockSource as TypedAttribute>::NAME.eq_ignore_ascii_case(attr) {
976            SsrcAttribute::MediaClockSource
977        } else {
978            SsrcAttribute::Other(attr.to_string())
979        }
980    }
981
982    pub fn as_str(&self) -> &str {
983        use crate::{Fmtp, MediaClockSource, ReferenceClock, Rtcp};
984
985        match self {
986            SsrcAttribute::Cname => "cname",
987            SsrcAttribute::PreviousSsrc => "previous-ssrc",
988            SsrcAttribute::Fmtp => <Fmtp as TypedAttribute>::NAME,
989            SsrcAttribute::Rtcp => <Rtcp as TypedAttribute>::NAME,
990            SsrcAttribute::ReferenceClock => <ReferenceClock as TypedAttribute>::NAME,
991            SsrcAttribute::MediaClockSource => <MediaClockSource as TypedAttribute>::NAME,
992            SsrcAttribute::Other(other) => other.as_str(),
993        }
994    }
995}
996
997impl<T: TypedAttribute> From<T> for SsrcAttribute {
998    fn from(_attr: T) -> Self {
999        SsrcAttribute::new(T::NAME)
1000    }
1001}
1002
1003/// Payload format for which feedback messages may be used
1004#[derive(Debug, PartialEq, Clone, Eq)]
1005#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
1006pub enum RtcpFbPt {
1007    /// Fixed payload format
1008    Fmt(u8),
1009    /// Applies to all formats
1010    Wildcard,
1011}
1012
1013impl From<u8> for RtcpFbPt {
1014    fn from(pt: u8) -> Self {
1015        RtcpFbPt::Fmt(pt)
1016    }
1017}
1018
1019/// Candidate connection address type
1020///
1021/// Can be IPv4, IPv6 or a FQDN
1022#[derive(Debug, PartialEq, Clone, Eq)]
1023#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
1024pub enum CandidateAddress {
1025    IpAddr(std::net::IpAddr),
1026    FQDN(String),
1027}