1use std::{
8 fmt::{Display, Write},
9 net::IpAddr,
10 str::FromStr,
11};
12
13use crate::{attributes::SrtpKeyParam, TypedAttribute};
14
15#[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#[derive(Debug, PartialEq, Eq, Clone)]
40#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
41pub enum NetType {
42 In,
44 Tn,
46 Atm,
48 Pstn,
50 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 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#[derive(Debug, PartialEq, Eq, Clone)]
102#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
103pub enum AddrType {
104 Ip4,
106 Ip6,
108 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 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 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#[derive(Debug, PartialEq, Eq, Clone, Copy)]
178#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
179pub enum BandwidthType {
180 Ct,
182 As,
184 Rr,
186 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#[derive(Debug, PartialEq, Eq, Clone, Copy)]
230#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
231pub enum KeyMethod {
232 Clear,
234 Base64,
236 Uri,
238 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 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#[derive(Debug, PartialEq, Eq, Clone, Copy)]
278#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
279pub enum MediaType {
280 Audio,
282 Video,
284 Text,
286 Application,
288 Message,
290 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#[derive(Debug, PartialEq, Eq, Clone)]
339#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
340pub enum TransportProto {
341 Udp,
343 RtpAvp,
345 RtpSavp,
347 RtpAvpf,
349 RtpSavpf,
351 TcpDtlsRtpSavp,
353 TcpDtlsRtpSavpf,
355 UdpTlsRtpSavp,
357 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 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 pub fn is_rtp(&self) -> bool {
393 let proto = self.as_str();
394 proto.starts_with("RTP/") || proto.contains("/RTP/")
395 }
396
397 pub fn is_sctp(&self) -> bool {
399 self.as_str().ends_with("/SCTP")
400 }
401
402 pub fn is_secure(&self) -> bool {
405 self.is_rtp() && self.as_str().contains("SAVP")
406 }
407
408 pub fn is_avpf(&self) -> bool {
411 self.is_rtp() && self.as_str().ends_with("AVPF")
412 }
413}
414
415impl FromStr for TransportProto {
416 type Err = ();
418
419 fn from_str(s: &str) -> Result<Self, Self::Err> {
420 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#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
468#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
469pub enum RidDirection {
470 Send,
472 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 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#[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#[derive(Debug, Clone, PartialEq, Eq)]
564#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
565pub enum GroupSemantics {
566 LS,
570 FID,
574 SRF,
578 ANAT,
582 FEC,
586 DDP,
590 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#[derive(Debug, PartialEq, Clone, Eq)]
636#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
637pub enum CryptoSuite {
638 AesCm128HmacSha1_80,
640 AesCm128HmacSha1_32,
642 F8_128HmacSha1_80,
644 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#[derive(Debug, PartialEq, Clone, Eq, Copy)]
677#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
678pub enum FecOrder {
679 FecSrtp,
681 SrtpFec,
683}
684
685#[derive(Debug, PartialEq, Clone, Eq)]
689#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
690pub enum SrtpSessionParam {
691 Kdr(u8),
695 UnencryptedSrtp,
699 UnencryptedSrtcp,
703 UnauthenticatedSrtp,
707 FecOrder(FecOrder),
711 FecKey(Vec<SrtpKeyParam>),
715 Wsh(u8),
719 Extension(String),
721}
722
723#[derive(Debug, PartialEq, Clone, Eq)]
727#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
728pub enum CandidateType {
729 Host,
731 Srflx,
733 Prflx,
735 Relay,
737 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#[derive(Debug, PartialEq, Clone, Eq)]
773#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
774pub enum RtcpFbAck {
775 Rpsi,
777 App(Option<String>),
779 Ccfb,
783 Other(String),
785}
786
787#[derive(Debug, PartialEq, Clone, Eq)]
791#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
792pub enum RtcpFbNack {
793 Pli,
795 Sli,
797 Rpsi,
799 App(Option<String>),
801 Ecn,
805 Other(String),
807}
808
809#[derive(Debug, PartialEq, Clone, Eq)]
813#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
814pub enum RtcpFbCcm {
815 Fir,
817 Tmmbr(Option<String>),
819 Tstr,
821 Vbcm(Vec<u8>),
823 Other(String),
825}
826
827#[derive(Debug, PartialEq, Clone, Eq)]
829#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
830pub enum RtcpFbVal {
831 Ack(Option<RtcpFbAck>),
833 Nack(Option<RtcpFbNack>),
835 TrrInt(u64),
837 Ccm(RtcpFbCcm),
839 TransportCc,
841 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#[derive(Debug, Clone, PartialEq, Eq)]
1013#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
1014pub enum SsrcAttribute {
1015 Cname,
1017 PreviousSsrc,
1019 Fmtp,
1021 Rtcp,
1023 ReferenceClock,
1025 MediaClockSource,
1027 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#[derive(Debug, PartialEq, Clone, Eq)]
1076#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
1077pub enum RtcpFbPt {
1078 Fmt(u8),
1080 Wildcard,
1082}
1083
1084impl From<u8> for RtcpFbPt {
1085 fn from(pt: u8) -> Self {
1086 RtcpFbPt::Fmt(pt)
1087 }
1088}
1089
1090#[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 (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}