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
387impl FromStr for TransportProto {
388 type Err = ();
390
391 fn from_str(s: &str) -> Result<Self, Self::Err> {
392 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#[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#[derive(Debug, Clone, PartialEq, Eq)]
493#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
494pub enum GroupSemantics {
495 LS,
499 FID,
503 SRF,
507 ANAT,
511 FEC,
515 DDP,
519 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#[derive(Debug, PartialEq, Clone, Eq)]
565#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
566pub enum CryptoSuite {
567 AesCm128HmacSha1_80,
569 AesCm128HmacSha1_32,
571 F8_128HmacSha1_80,
573 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#[derive(Debug, PartialEq, Clone, Eq, Copy)]
606#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
607pub enum FecOrder {
608 FecSrtp,
610 SrtpFec,
612}
613
614#[derive(Debug, PartialEq, Clone, Eq)]
618#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
619pub enum SrtpSessionParam {
620 Kdr(u8),
624 UnencryptedSrtp,
628 UnencryptedSrtcp,
632 UnauthenticatedSrtp,
636 FecOrder(FecOrder),
640 FecKey(Vec<SrtpKeyParam>),
644 Wsh(u8),
648 Extension(String),
650}
651
652#[derive(Debug, PartialEq, Clone, Eq)]
656#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
657pub enum CandidateType {
658 Host,
660 Srflx,
662 Prflx,
664 Relay,
666 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#[derive(Debug, PartialEq, Clone, Eq)]
702#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
703pub enum RtcpFbAck {
704 Rpsi,
706 App(Option<String>),
708 Ccfb,
712 Other(String),
714}
715
716#[derive(Debug, PartialEq, Clone, Eq)]
720#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
721pub enum RtcpFbNack {
722 Pli,
724 Sli,
726 Rpsi,
728 App(Option<String>),
730 Ecn,
734 Other(String),
736}
737
738#[derive(Debug, PartialEq, Clone, Eq)]
742#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
743pub enum RtcpFbCcm {
744 Fir,
746 Tmmbr(Option<String>),
748 Tstr,
750 Vbcm(Vec<u8>),
752 Other(String),
754}
755
756#[derive(Debug, PartialEq, Clone, Eq)]
758#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
759pub enum RtcpFbVal {
760 Ack(Option<RtcpFbAck>),
762 Nack(Option<RtcpFbNack>),
764 TrrInt(u64),
766 Ccm(RtcpFbCcm),
768 TransportCc,
770 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#[derive(Debug, Clone, PartialEq, Eq)]
942#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
943pub enum SsrcAttribute {
944 Cname,
946 PreviousSsrc,
948 Fmtp,
950 Rtcp,
952 ReferenceClock,
954 MediaClockSource,
956 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#[derive(Debug, PartialEq, Clone, Eq)]
1005#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
1006pub enum RtcpFbPt {
1007 Fmt(u8),
1009 Wildcard,
1011}
1012
1013impl From<u8> for RtcpFbPt {
1014 fn from(pt: u8) -> Self {
1015 RtcpFbPt::Fmt(pt)
1016 }
1017}
1018
1019#[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}