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 Ok(match s {
72 "IN" => NetType::In,
73 "TN" => NetType::Tn,
74 "ATM" => NetType::Atm,
75 "PSTN" => NetType::Pstn,
76 other => NetType::Other(other.to_string()),
77 })
78 }
79}
80
81impl From<&str> for NetType {
82 fn from(value: &str) -> Self {
83 NetType::from_str(value).expect("infallible")
84 }
85}
86
87impl Display for NetType {
88 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
89 f.write_str(self.as_str())
90 }
91}
92
93#[derive(Debug, PartialEq, Eq, Clone)]
98#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
99pub enum AddrType {
100 Ip4,
102 Ip6,
104 Other(String),
106}
107
108impl AddrType {
109 pub fn new(addrtype: impl AsRef<str>) -> Self {
110 match addrtype.as_ref() {
111 "IP4" => AddrType::Ip4,
112 "IP6" => AddrType::Ip6,
113 other => AddrType::Other(other.to_string()),
114 }
115 }
116
117 pub fn is_ip(&self) -> bool {
119 matches!(self, AddrType::Ip4 | AddrType::Ip6)
120 }
121
122 pub fn as_str(&self) -> &str {
123 match self {
124 AddrType::Ip4 => "IP4",
125 AddrType::Ip6 => "IP6",
126 AddrType::Other(other) => other.as_str(),
127 }
128 }
129}
130
131impl FromStr for AddrType {
132 type Err = ();
134
135 fn from_str(s: &str) -> Result<Self, Self::Err> {
136 Ok(match s {
137 "IP4" => AddrType::Ip4,
138 "IP6" => AddrType::Ip6,
139 other => AddrType::Other(other.to_string()),
140 })
141 }
142}
143
144impl From<&str> for AddrType {
145 fn from(value: &str) -> Self {
146 AddrType::from_str(value).expect("infallible")
147 }
148}
149
150impl From<IpAddr> for AddrType {
151 fn from(addr: IpAddr) -> Self {
152 match addr {
153 IpAddr::V4(_) => AddrType::Ip4,
154 IpAddr::V6(_) => AddrType::Ip6,
155 }
156 }
157}
158
159impl Display for AddrType {
160 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
161 f.write_str(self.as_str())
162 }
163}
164
165#[derive(Debug, PartialEq, Eq, Clone, Copy)]
169#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
170pub enum BandwidthType {
171 Ct,
173 As,
175 Rr,
177 Rs,
179}
180
181impl BandwidthType {
182 pub fn as_str(&self) -> &'static str {
183 match self {
184 BandwidthType::As => "AS",
185 BandwidthType::Ct => "CT",
186 BandwidthType::Rr => "RR",
187 BandwidthType::Rs => "RS",
188 }
189 }
190}
191
192impl FromStr for BandwidthType {
193 type Err = ParseEnumError;
194
195 fn from_str(s: &str) -> Result<Self, Self::Err> {
196 match s {
197 "AS" => Ok(BandwidthType::As),
198 "CT" => Ok(BandwidthType::Ct),
199 "RR" => Ok(BandwidthType::Rr),
200 "RS" => Ok(BandwidthType::Rs),
201 _ => Err(ParseEnumError::Invalid(s.to_string())),
202 }
203 }
204}
205
206impl Display for BandwidthType {
207 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
208 f.write_str(self.as_str())
209 }
210}
211
212#[derive(Debug, PartialEq, Eq, Clone, Copy)]
217#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
218pub enum KeyMethod {
219 Clear,
221 Base64,
223 Uri,
225 Prompt,
227}
228
229impl KeyMethod {
230 pub fn as_str(&self) -> &'static str {
231 match self {
232 KeyMethod::Clear => "clear",
233 KeyMethod::Base64 => "base64",
234 KeyMethod::Uri => "uri",
235 KeyMethod::Prompt => "prompt",
236 }
237 }
238}
239
240impl FromStr for KeyMethod {
241 type Err = ParseEnumError;
242
243 fn from_str(s: &str) -> Result<Self, Self::Err> {
244 match s {
246 "clear" => Ok(KeyMethod::Clear),
247 "base64" => Ok(KeyMethod::Base64),
248 "uri" => Ok(KeyMethod::Uri),
249 "prompt" => Ok(KeyMethod::Prompt),
250 _ => Err(ParseEnumError::Invalid(s.to_string())),
251 }
252 }
253}
254
255impl Display for KeyMethod {
256 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
257 f.write_str(self.as_str())
258 }
259}
260
261#[derive(Debug, PartialEq, Eq, Clone, Copy)]
265#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
266pub enum MediaType {
267 Audio,
269 Video,
271 Text,
273 Application,
275 Message,
277 Image,
279}
280
281impl MediaType {
282 pub fn as_str(&self) -> &'static str {
283 match self {
284 MediaType::Audio => "audio",
285 MediaType::Video => "video",
286 MediaType::Text => "text",
287 MediaType::Application => "application",
288 MediaType::Message => "message",
289 MediaType::Image => "image",
290 }
291 }
292}
293
294impl FromStr for MediaType {
295 type Err = ParseEnumError;
296
297 fn from_str(s: &str) -> Result<Self, Self::Err> {
298 match s {
299 "audio" => Ok(MediaType::Audio),
300 "video" => Ok(MediaType::Video),
301 "text" => Ok(MediaType::Text),
302 "application" => Ok(MediaType::Application),
303 "message" => Ok(MediaType::Message),
304 "image" => Ok(MediaType::Image),
305 _ => Err(ParseEnumError::Invalid(s.to_string())),
306 }
307 }
308}
309
310impl Display for MediaType {
311 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
312 f.write_str(self.as_str())
313 }
314}
315
316#[derive(Debug, PartialEq, Eq, Clone)]
320#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
321pub enum TransportProto {
322 Udp,
324 RtpAvp,
326 RtpSavp,
328 RtpAvpf,
330 RtpSavpf,
332 TcpDtlsRtpSavp,
334 TcpDtlsRtpSavpf,
336 UdpTlsRtpSavp,
338 UdpTlsRtpSavpf,
340 UdpDtlsSctp,
341 TcpDtlsSctp,
342 DtlsSctp,
343 Other(String),
344}
345
346impl TransportProto {
347 pub fn as_str(&self) -> &str {
348 match self {
349 TransportProto::Udp => "udp",
352 TransportProto::RtpAvp => "RTP/AVP",
353 TransportProto::RtpAvpf => "RTP/AVPF",
354 TransportProto::RtpSavp => "RTP/SAVP",
355 TransportProto::RtpSavpf => "RTP/SAVPF",
356 TransportProto::TcpDtlsRtpSavp => "TCP/DTLS/RTP/SAVP",
357 TransportProto::TcpDtlsRtpSavpf => "TCP/DTLS/RTP/SAVPF",
358 TransportProto::UdpTlsRtpSavp => "UDP/TLS/RTP/SAVP",
359 TransportProto::UdpTlsRtpSavpf => "UDP/TLS/RTP/SAVPF",
360 TransportProto::UdpDtlsSctp => "UDP/DTLS/SCTP",
361 TransportProto::TcpDtlsSctp => "TCP/DTLS/SCTP",
362 TransportProto::DtlsSctp => "DTLS/SCTP",
363 TransportProto::Other(proto) => proto.as_str(),
364 }
365 }
366
367 pub fn is_rtp(&self) -> bool {
374 let proto = self.as_str();
375 proto.starts_with("RTP/") || proto.contains("/RTP/")
376 }
377
378 pub fn is_sctp(&self) -> bool {
380 self.as_str().ends_with("/SCTP")
381 }
382
383 pub fn is_secure(&self) -> bool {
386 self.is_rtp() && self.as_str().contains("SAVP")
387 }
388
389 pub fn is_avpf(&self) -> bool {
392 self.is_rtp() && self.as_str().ends_with("AVPF")
393 }
394}
395
396impl FromStr for TransportProto {
397 type Err = ();
399
400 fn from_str(s: &str) -> Result<Self, Self::Err> {
401 Ok(match s {
402 "udp" => TransportProto::Udp,
403 "RTP/AVP" => TransportProto::RtpAvp,
404 "RTP/AVPF" => TransportProto::RtpAvpf,
405 "RTP/SAVP" => TransportProto::RtpSavp,
406 "RTP/SAVPF" => TransportProto::RtpSavpf,
407 "TCP/DTLS/RTP/SAVP" => TransportProto::TcpDtlsRtpSavp,
408 "TCP/DTLS/RTP/SAVPF" => TransportProto::TcpDtlsRtpSavpf,
409 "UDP/TLS/RTP/SAVP" => TransportProto::UdpTlsRtpSavp,
410 "UDP/TLS/RTP/SAVPF" => TransportProto::UdpTlsRtpSavpf,
411 "UDP/DTLS/SCTP" => TransportProto::UdpDtlsSctp,
412 "TCP/DTLS/SCTP" => TransportProto::TcpDtlsSctp,
413 "DTLS/SCTP" => TransportProto::DtlsSctp,
414 other => TransportProto::Other(other.to_string()),
415 })
416 }
417}
418
419impl From<&str> for TransportProto {
420 fn from(value: &str) -> Self {
421 TransportProto::from_str(value).expect("infallible")
422 }
423}
424
425impl Display for TransportProto {
426 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
427 f.write_str(self.as_str())
428 }
429}
430
431#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
435#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
436pub enum RidDirection {
437 Send,
439 Recv,
441}
442
443impl RidDirection {
444 pub fn as_str(&self) -> &'static str {
445 match self {
446 RidDirection::Send => "send",
447 RidDirection::Recv => "recv",
448 }
449 }
450}
451
452impl FromStr for RidDirection {
453 type Err = ParseEnumError;
454
455 fn from_str(s: &str) -> Result<Self, Self::Err> {
456 match s {
457 "send" => Ok(RidDirection::Send),
458 "recv" => Ok(RidDirection::Recv),
459 _ => Err(ParseEnumError::Invalid(s.to_string())),
460 }
461 }
462}
463
464impl std::fmt::Display for RidDirection {
465 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
466 f.write_str(self.as_str())
467 }
468}
469
470#[derive(Debug, Clone, PartialEq, Eq)]
474#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
475pub enum HashFunc {
476 Sha1,
477 Sha224,
478 Sha256,
479 Sha384,
480 Sha512,
481 Md5,
482 Md2,
483 Other(String),
484}
485
486impl HashFunc {
487 pub fn new(hash_func: impl AsRef<str>) -> Self {
488 let hash_func = hash_func.as_ref();
489
490 if hash_func.eq_ignore_ascii_case("sha-1") {
491 HashFunc::Sha1
492 } else if hash_func.eq_ignore_ascii_case("sha-224") {
493 HashFunc::Sha224
494 } else if hash_func.eq_ignore_ascii_case("sha-256") {
495 HashFunc::Sha256
496 } else if hash_func.eq_ignore_ascii_case("sha-384") {
497 HashFunc::Sha384
498 } else if hash_func.eq_ignore_ascii_case("sha-512") {
499 HashFunc::Sha512
500 } else if hash_func.eq_ignore_ascii_case("md-5") {
501 HashFunc::Md5
502 } else if hash_func.eq_ignore_ascii_case("md-2") {
503 HashFunc::Md2
504 } else {
505 HashFunc::Other(hash_func.to_string())
506 }
507 }
508
509 pub fn as_str(&self) -> &str {
510 match self {
511 HashFunc::Sha1 => "sha-1",
512 HashFunc::Sha224 => "sha-224",
513 HashFunc::Sha256 => "sha-256",
514 HashFunc::Sha384 => "sha-384",
515 HashFunc::Sha512 => "sha-512",
516 HashFunc::Md5 => "md-5",
517 HashFunc::Md2 => "md-2",
518 HashFunc::Other(s) => s.as_str(),
519 }
520 }
521}
522
523#[derive(Debug, Clone, PartialEq, Eq)]
528#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
529pub enum GroupSemantics {
530 LS,
534 FID,
538 SRF,
542 ANAT,
546 FEC,
550 DDP,
554 Bundle,
558 Other(String),
560}
561
562impl GroupSemantics {
563 pub fn new(semantics: impl AsRef<str>) -> Self {
564 let semantics = semantics.as_ref();
565
566 if "LS".eq_ignore_ascii_case(semantics) {
567 GroupSemantics::LS
568 } else if "FID".eq_ignore_ascii_case(semantics) {
569 GroupSemantics::FID
570 } else if "SRF".eq_ignore_ascii_case(semantics) {
571 GroupSemantics::SRF
572 } else if "ANAT".eq_ignore_ascii_case(semantics) {
573 GroupSemantics::ANAT
574 } else if "FEC".eq_ignore_ascii_case(semantics) {
575 GroupSemantics::FEC
576 } else if "DDP".eq_ignore_ascii_case(semantics) {
577 GroupSemantics::DDP
578 } else if "BUNDLE".eq_ignore_ascii_case(semantics) {
579 GroupSemantics::Bundle
580 } else {
581 GroupSemantics::Other(semantics.to_string())
582 }
583 }
584
585 pub fn as_str(&self) -> &str {
586 match self {
587 GroupSemantics::LS => "LS",
588 GroupSemantics::FID => "FID",
589 GroupSemantics::SRF => "SRF",
590 GroupSemantics::ANAT => "ANAT",
591 GroupSemantics::DDP => "DDP",
592 GroupSemantics::FEC => "FEC",
593 GroupSemantics::Bundle => "BUNDLE",
594 GroupSemantics::Other(s) => s.as_str(),
595 }
596 }
597}
598
599#[derive(Debug, PartialEq, Clone, Eq)]
607#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
608pub enum CryptoSuite {
609 AesCm128HmacSha1_80,
611 AesCm128HmacSha1_32,
613 F8_128HmacSha1_80,
615 Other(String),
617}
618
619impl CryptoSuite {
620 pub fn new(crypto_suite: impl AsRef<str>) -> Self {
621 let crypto_suite = crypto_suite.as_ref();
622
623 if "AES_CM_128_HMAC_SHA1_32".eq_ignore_ascii_case(crypto_suite) {
624 CryptoSuite::AesCm128HmacSha1_32
625 } else if "F8_128_HMAC_SHA1_80".eq_ignore_ascii_case(crypto_suite) {
626 CryptoSuite::F8_128HmacSha1_80
627 } else if "AES_CM_128_HMAC_SHA1_80".eq_ignore_ascii_case(crypto_suite) {
628 CryptoSuite::AesCm128HmacSha1_80
629 } else {
630 CryptoSuite::Other(crypto_suite.to_string())
631 }
632 }
633
634 pub fn as_str(&self) -> &str {
635 match self {
636 CryptoSuite::AesCm128HmacSha1_80 => "AES_CM_128_HMAC_SHA1_80",
637 CryptoSuite::AesCm128HmacSha1_32 => "AES_CM_128_HMAC_SHA1_32",
638 CryptoSuite::F8_128HmacSha1_80 => "F8_128_HMAC_SHA1_80",
639 CryptoSuite::Other(s) => s.as_str(),
640 }
641 }
642}
643
644#[derive(Debug, PartialEq, Clone, Eq, Copy)]
648#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
649pub enum FecOrder {
650 FecSrtp,
652 SrtpFec,
654}
655
656#[derive(Debug, PartialEq, Clone, Eq)]
660#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
661pub enum SrtpSessionParam {
662 Kdr(u8),
666 UnencryptedSrtp,
670 UnencryptedSrtcp,
674 UnauthenticatedSrtp,
678 FecOrder(FecOrder),
682 FecKey(Vec<SrtpKeyParam>),
686 Wsh(u8),
690 Extension(String),
692}
693
694#[derive(Debug, PartialEq, Clone, Eq)]
698#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
699pub enum CandidateType {
700 Host,
702 Srflx,
704 Prflx,
706 Relay,
708 Other(String),
710}
711
712impl CandidateType {
713 pub fn new(cand_type: impl AsRef<str>) -> Self {
714 let cand_type = cand_type.as_ref();
715
716 if "host".eq_ignore_ascii_case(cand_type) {
717 CandidateType::Host
718 } else if "srflx".eq_ignore_ascii_case(cand_type) {
719 CandidateType::Srflx
720 } else if "prflx".eq_ignore_ascii_case(cand_type) {
721 CandidateType::Prflx
722 } else if "relay".eq_ignore_ascii_case(cand_type) {
723 CandidateType::Relay
724 } else {
725 CandidateType::Other(cand_type.to_string())
726 }
727 }
728
729 pub fn as_str(&self) -> &str {
730 match self {
731 CandidateType::Host => "host",
732 CandidateType::Srflx => "srflx",
733 CandidateType::Prflx => "prflx",
734 CandidateType::Relay => "relay",
735 CandidateType::Other(o) => o.as_str(),
736 }
737 }
738}
739
740#[derive(Debug, PartialEq, Clone, Eq)]
744#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
745pub enum RtcpFbAck {
746 Rpsi,
748 App(Option<String>),
750 Ccfb,
754 Other(String),
756}
757
758#[derive(Debug, PartialEq, Clone, Eq)]
762#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
763pub enum RtcpFbNack {
764 Pli,
766 Sli,
768 Rpsi,
770 App(Option<String>),
772 Ecn,
776 Other(String),
778}
779
780#[derive(Debug, PartialEq, Clone, Eq)]
784#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
785pub enum RtcpFbCcm {
786 Fir,
788 Tmmbr(Option<String>),
790 Tstr,
792 Vbcm(Vec<u8>),
794 Other(String),
796}
797
798#[derive(Debug, PartialEq, Clone, Eq)]
800#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
801pub enum RtcpFbVal {
802 Ack(Option<RtcpFbAck>),
804 Nack(Option<RtcpFbNack>),
806 TrrInt(u64),
808 Ccm(RtcpFbCcm),
810 TransportCc,
812 Other(String),
814}
815
816impl RtcpFbVal {
817 pub fn is_ack(&self) -> bool {
818 matches!(self, RtcpFbVal::Ack(None))
819 }
820
821 pub fn is_ack_rpsi(&self) -> bool {
822 matches!(self, RtcpFbVal::Ack(Some(RtcpFbAck::Rpsi)))
823 }
824
825 pub fn is_ack_app(&self) -> bool {
826 matches!(self, RtcpFbVal::Ack(Some(RtcpFbAck::App(_))))
827 }
828
829 pub fn is_ack_ccfb(&self) -> bool {
830 matches!(self, RtcpFbVal::Ack(Some(RtcpFbAck::Ccfb)))
831 }
832
833 pub fn is_nack(&self) -> bool {
834 matches!(self, RtcpFbVal::Nack(None))
835 }
836
837 pub fn is_nack_pli(&self) -> bool {
838 matches!(self, RtcpFbVal::Nack(Some(RtcpFbNack::Pli)))
839 }
840
841 pub fn is_nack_sli(&self) -> bool {
842 matches!(self, RtcpFbVal::Nack(Some(RtcpFbNack::Sli)))
843 }
844
845 pub fn is_nack_rpsi(&self) -> bool {
846 matches!(self, RtcpFbVal::Nack(Some(RtcpFbNack::Rpsi)))
847 }
848
849 pub fn is_nack_app(&self) -> bool {
850 matches!(self, RtcpFbVal::Nack(Some(RtcpFbNack::App(_))))
851 }
852
853 pub fn is_nack_ecn(&self) -> bool {
854 matches!(self, RtcpFbVal::Nack(Some(RtcpFbNack::Ecn)))
855 }
856
857 pub fn is_trr_int(&self) -> bool {
858 matches!(self, RtcpFbVal::TrrInt(_))
859 }
860
861 pub fn is_ccm_fir(&self) -> bool {
862 matches!(self, RtcpFbVal::Ccm(RtcpFbCcm::Fir))
863 }
864
865 pub fn is_ccm_tmmbr(&self) -> bool {
866 matches!(self, RtcpFbVal::Ccm(RtcpFbCcm::Tmmbr(_)))
867 }
868
869 pub fn is_ccm_tstr(&self) -> bool {
870 matches!(self, RtcpFbVal::Ccm(RtcpFbCcm::Tstr))
871 }
872
873 pub fn is_ccm_vbcm(&self) -> bool {
874 matches!(self, RtcpFbVal::Ccm(RtcpFbCcm::Vbcm(_)))
875 }
876
877 pub fn is_transport_cc(&self) -> bool {
878 matches!(self, RtcpFbVal::TransportCc)
879 }
880}
881
882impl From<RtcpFbAck> for RtcpFbVal {
883 fn from(value: RtcpFbAck) -> Self {
884 RtcpFbVal::Ack(Some(value))
885 }
886}
887
888impl From<RtcpFbNack> for RtcpFbVal {
889 fn from(value: RtcpFbNack) -> Self {
890 RtcpFbVal::Nack(Some(value))
891 }
892}
893
894impl From<RtcpFbCcm> for RtcpFbVal {
895 fn from(value: RtcpFbCcm) -> Self {
896 RtcpFbVal::Ccm(value)
897 }
898}
899
900impl Display for RtcpFbVal {
901 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
902 match self {
903 RtcpFbVal::Ack(ack) => {
904 write!(f, "ack")?;
905 if let Some(ack) = ack {
906 match ack {
907 RtcpFbAck::Rpsi => write!(f, " rpsi")?,
908 RtcpFbAck::Ccfb => write!(f, " ccfb")?,
909 RtcpFbAck::App(app) => {
910 write!(f, " app")?;
911 if let Some(app_param) = app {
912 f.write_char(' ')?;
913 f.write_str(app_param)?;
914 }
915 }
916 RtcpFbAck::Other(other) => {
917 f.write_char(' ')?;
918 f.write_str(other)?;
919 }
920 }
921 }
922 Ok(())
923 }
924 RtcpFbVal::Nack(nack) => {
925 write!(f, "nack")?;
926 if let Some(nack) = nack {
927 match nack {
928 RtcpFbNack::Pli => write!(f, " pli")?,
929 RtcpFbNack::Sli => write!(f, " sli")?,
930 RtcpFbNack::Rpsi => write!(f, " rpsi")?,
931 RtcpFbNack::Ecn => write!(f, " ecn")?,
932 RtcpFbNack::App(app) => {
933 write!(f, " app")?;
934 if let Some(app_param) = app {
935 f.write_char(' ')?;
936 f.write_str(app_param)?;
937 }
938 }
939 RtcpFbNack::Other(other) => {
940 f.write_char(' ')?;
941 f.write_str(other)?;
942 }
943 }
944 }
945 Ok(())
946 }
947 RtcpFbVal::TrrInt(trr_int) => write!(f, "trr-int {trr_int}"),
948 RtcpFbVal::Ccm(ccm) => {
949 write!(f, "ccm")?;
950 match ccm {
951 RtcpFbCcm::Fir => write!(f, " fir")?,
952 RtcpFbCcm::Tstr => write!(f, " tstr")?,
953 RtcpFbCcm::Tmmbr(smaxpr) => {
954 write!(f, " tmmbr")?;
955 if let Some(smaxpr) = smaxpr {
956 f.write_char(' ')?;
957 f.write_str(smaxpr)?;
958 }
959 }
960 RtcpFbCcm::Vbcm(vbcm) => {
961 write!(f, " vbcm")?;
962 for v in vbcm {
963 f.write_char(' ')?;
964 write!(f, "{v}")?;
965 }
966 }
967 RtcpFbCcm::Other(other) => {
968 f.write_char(' ')?;
969 f.write_str(other)?;
970 }
971 }
972 Ok(())
973 }
974 RtcpFbVal::TransportCc => f.write_str("transport-cc"),
975 RtcpFbVal::Other(other) => f.write_str(other),
976 }
977 }
978}
979
980#[derive(Debug, Clone, PartialEq, Eq)]
984#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
985pub enum SsrcAttribute {
986 Cname,
988 PreviousSsrc,
990 Fmtp,
992 Rtcp,
994 ReferenceClock,
996 MediaClockSource,
998 Other(String),
1000}
1001
1002impl SsrcAttribute {
1003 pub fn new(attribute: impl AsRef<str>) -> Self {
1004 use crate::{Fmtp, MediaClockSource, ReferenceClock, Rtcp};
1005
1006 match attribute.as_ref() {
1007 "cname" => SsrcAttribute::Cname,
1008 "previous-ssrc" => SsrcAttribute::PreviousSsrc,
1009 <Fmtp as TypedAttribute>::NAME => SsrcAttribute::Fmtp,
1010 <Rtcp as TypedAttribute>::NAME => SsrcAttribute::Rtcp,
1011 <ReferenceClock as TypedAttribute>::NAME => SsrcAttribute::ReferenceClock,
1012 <MediaClockSource as TypedAttribute>::NAME => SsrcAttribute::MediaClockSource,
1013 other => SsrcAttribute::Other(other.to_string()),
1014 }
1015 }
1016
1017 pub fn as_str(&self) -> &str {
1018 use crate::{Fmtp, MediaClockSource, ReferenceClock, Rtcp};
1019
1020 match self {
1021 SsrcAttribute::Cname => "cname",
1022 SsrcAttribute::PreviousSsrc => "previous-ssrc",
1023 SsrcAttribute::Fmtp => <Fmtp as TypedAttribute>::NAME,
1024 SsrcAttribute::Rtcp => <Rtcp as TypedAttribute>::NAME,
1025 SsrcAttribute::ReferenceClock => <ReferenceClock as TypedAttribute>::NAME,
1026 SsrcAttribute::MediaClockSource => <MediaClockSource as TypedAttribute>::NAME,
1027 SsrcAttribute::Other(other) => other.as_str(),
1028 }
1029 }
1030}
1031
1032impl<T: TypedAttribute> From<T> for SsrcAttribute {
1033 fn from(_attr: T) -> Self {
1034 SsrcAttribute::new(T::NAME)
1035 }
1036}
1037
1038#[derive(Debug, PartialEq, Clone, Eq)]
1040#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
1041pub enum RtcpFbPt {
1042 Fmt(u8),
1044 Wildcard,
1046}
1047
1048impl From<u8> for RtcpFbPt {
1049 fn from(pt: u8) -> Self {
1050 RtcpFbPt::Fmt(pt)
1051 }
1052}
1053
1054#[derive(Debug, PartialEq, Clone, Eq)]
1058#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
1059pub enum CandidateAddress {
1060 IpAddr(std::net::IpAddr),
1061 FQDN(String),
1062}
1063
1064#[cfg(test)]
1065mod tests {
1066 use super::*;
1067
1068 #[test]
1069 fn transport_proto_classification() {
1070 let cases = [
1071 (TransportProto::Udp, false, false, false, false),
1073 (TransportProto::RtpAvp, true, false, false, false),
1074 (TransportProto::RtpAvpf, true, false, false, true),
1075 (TransportProto::RtpSavp, true, false, true, false),
1076 (TransportProto::RtpSavpf, true, false, true, true),
1077 (TransportProto::TcpDtlsRtpSavp, true, false, true, false),
1078 (TransportProto::TcpDtlsRtpSavpf, true, false, true, true),
1079 (TransportProto::UdpTlsRtpSavp, true, false, true, false),
1080 (TransportProto::UdpTlsRtpSavpf, true, false, true, true),
1081 (TransportProto::UdpDtlsSctp, false, true, false, false),
1082 (TransportProto::TcpDtlsSctp, false, true, false, false),
1083 (TransportProto::DtlsSctp, false, true, false, false),
1084 (
1085 TransportProto::Other("FOO".into()),
1086 false,
1087 false,
1088 false,
1089 false,
1090 ),
1091 (
1092 TransportProto::Other("RTP/AV".into()),
1093 true,
1094 false,
1095 false,
1096 false,
1097 ),
1098 (
1099 TransportProto::Other("RTP/AVP/TCP".into()),
1100 true,
1101 false,
1102 false,
1103 false,
1104 ),
1105 (
1106 TransportProto::Other("UDP/TLS/RTP/SAVPF".into()),
1107 true,
1108 false,
1109 true,
1110 true,
1111 ),
1112 (
1113 TransportProto::Other("AVPF".into()),
1114 false,
1115 false,
1116 false,
1117 false,
1118 ),
1119 ];
1120
1121 for (proto, rtp, sctp, secure, avpf) in cases {
1122 assert_eq!(proto.is_rtp(), rtp, "{:?} is_rtp", proto);
1123 assert_eq!(proto.is_sctp(), sctp, "{:?} is_sctp", proto);
1124 assert_eq!(proto.is_secure(), secure, "{:?} is_secure", proto);
1125 assert_eq!(proto.is_avpf(), avpf, "{:?} is_avpf", proto);
1126 }
1127 }
1128
1129 #[test]
1130 fn group_semantics_case() {
1131 let cases = [
1134 ("ls", GroupSemantics::LS),
1135 ("LS", GroupSemantics::LS),
1136 ("fid", GroupSemantics::FID),
1137 ("srf", GroupSemantics::SRF),
1138 ("anat", GroupSemantics::ANAT),
1139 ("fec", GroupSemantics::FEC),
1140 ("ddp", GroupSemantics::DDP),
1141 ("bundle", GroupSemantics::Bundle),
1142 ("Bundle", GroupSemantics::Bundle),
1143 ("BUNDLE", GroupSemantics::Bundle),
1144 ("foo", GroupSemantics::Other("foo".to_string())),
1145 ];
1146
1147 for (value, semantics) in cases {
1148 assert_eq!(GroupSemantics::new(value), semantics, "{value}");
1149 }
1150
1151 let cases = [
1153 (GroupSemantics::LS, "LS"),
1154 (GroupSemantics::FID, "FID"),
1155 (GroupSemantics::SRF, "SRF"),
1156 (GroupSemantics::ANAT, "ANAT"),
1157 (GroupSemantics::FEC, "FEC"),
1158 (GroupSemantics::DDP, "DDP"),
1159 (GroupSemantics::Bundle, "BUNDLE"),
1160 (GroupSemantics::Other("foo".to_string()), "foo"),
1161 ];
1162
1163 for (semantics, value) in cases {
1164 assert_eq!(semantics.as_str(), value, "{semantics:?}");
1165 }
1166 }
1167}