use std::{
fmt::{Display, Write},
net::IpAddr,
str::FromStr,
};
use crate::{attributes::SrtpKeyParam, TypedAttribute};
#[derive(Debug, PartialEq, Eq, Clone)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum ParseEnumError {
Invalid(String),
}
impl std::error::Error for ParseEnumError {}
impl std::fmt::Display for ParseEnumError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
ParseEnumError::Invalid(s) => {
write!(f, "Failed to parse {s} as an enum type")
}
}
}
}
#[derive(Debug, PartialEq, Eq, Clone)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum NetType {
In,
Tn,
Atm,
Pstn,
Other(String),
}
impl NetType {
pub fn as_str(&self) -> &str {
match self {
NetType::In => "IN",
NetType::Tn => "TN",
NetType::Atm => "ATM",
NetType::Pstn => "PSTN",
NetType::Other(nettype) => nettype.as_str(),
}
}
}
impl FromStr for NetType {
type Err = ();
fn from_str(s: &str) -> Result<Self, Self::Err> {
if "IN".eq_ignore_ascii_case(s) {
Ok(NetType::In)
} else if "TN".eq_ignore_ascii_case(s) {
Ok(NetType::Tn)
} else if "ATM".eq_ignore_ascii_case(s) {
Ok(NetType::Atm)
} else if "PSTN".eq_ignore_ascii_case(s) {
Ok(NetType::Pstn)
} else {
Ok(NetType::Other(s.to_string()))
}
}
}
impl From<&str> for NetType {
fn from(value: &str) -> Self {
NetType::from_str(value).expect("infallible")
}
}
impl Display for NetType {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(self.as_str())
}
}
#[derive(Debug, PartialEq, Eq, Clone)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum AddrType {
Ip4,
Ip6,
Other(String),
}
impl AddrType {
pub fn new(addrtype: impl AsRef<str>) -> Self {
let addrtype = addrtype.as_ref();
if "IP4".eq_ignore_ascii_case(addrtype) {
AddrType::Ip4
} else if "IP6".eq_ignore_ascii_case(addrtype) {
AddrType::Ip6
} else {
AddrType::Other(addrtype.to_string())
}
}
pub fn is_ip(&self) -> bool {
matches!(self, AddrType::Ip4 | AddrType::Ip6)
}
pub fn as_str(&self) -> &str {
match self {
AddrType::Ip4 => "IP4",
AddrType::Ip6 => "IP6",
AddrType::Other(other) => other.as_str(),
}
}
}
impl FromStr for AddrType {
type Err = ();
fn from_str(s: &str) -> Result<Self, Self::Err> {
if "IP4".eq_ignore_ascii_case(s) {
Ok(AddrType::Ip4)
} else if "IP6".eq_ignore_ascii_case(s) {
Ok(AddrType::Ip6)
} else {
Ok(AddrType::Other(s.to_string()))
}
}
}
impl From<&str> for AddrType {
fn from(value: &str) -> Self {
AddrType::from_str(value).expect("infallible")
}
}
impl From<IpAddr> for AddrType {
fn from(addr: IpAddr) -> Self {
match addr {
IpAddr::V4(_) => AddrType::Ip4,
IpAddr::V6(_) => AddrType::Ip6,
}
}
}
impl Display for AddrType {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(self.as_str())
}
}
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum BandwidthType {
Ct,
As,
Rr,
Rs,
}
impl BandwidthType {
pub fn as_str(&self) -> &'static str {
match self {
BandwidthType::As => "AS",
BandwidthType::Ct => "CT",
BandwidthType::Rr => "RR",
BandwidthType::Rs => "RS",
}
}
}
impl FromStr for BandwidthType {
type Err = ParseEnumError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
if "AS".eq_ignore_ascii_case(s) {
Ok(BandwidthType::As)
} else if "CT".eq_ignore_ascii_case(s) {
Ok(BandwidthType::Ct)
} else if "RR".eq_ignore_ascii_case(s) {
Ok(BandwidthType::Rr)
} else if "RS".eq_ignore_ascii_case(s) {
Ok(BandwidthType::Rs)
} else {
Err(ParseEnumError::Invalid(s.to_string()))
}
}
}
impl Display for BandwidthType {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(self.as_str())
}
}
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum KeyMethod {
Clear,
Base64,
Uri,
Prompt,
}
impl KeyMethod {
pub fn as_str(&self) -> &'static str {
match self {
KeyMethod::Clear => "clear",
KeyMethod::Base64 => "base64",
KeyMethod::Uri => "uri",
KeyMethod::Prompt => "prompt",
}
}
}
impl FromStr for KeyMethod {
type Err = ParseEnumError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s {
"clear" => Ok(KeyMethod::Clear),
"base64" => Ok(KeyMethod::Base64),
"uri" => Ok(KeyMethod::Uri),
"prompt" => Ok(KeyMethod::Prompt),
_ => Err(ParseEnumError::Invalid(s.to_string())),
}
}
}
impl Display for KeyMethod {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(self.as_str())
}
}
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum MediaType {
Audio,
Video,
Text,
Application,
Message,
Image,
}
impl MediaType {
pub fn as_str(&self) -> &'static str {
match self {
MediaType::Audio => "audio",
MediaType::Video => "video",
MediaType::Text => "text",
MediaType::Application => "application",
MediaType::Message => "message",
MediaType::Image => "image",
}
}
}
impl FromStr for MediaType {
type Err = ParseEnumError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
if "audio".eq_ignore_ascii_case(s) {
Ok(MediaType::Audio)
} else if "video".eq_ignore_ascii_case(s) {
Ok(MediaType::Video)
} else if "text".eq_ignore_ascii_case(s) {
Ok(MediaType::Text)
} else if "application".eq_ignore_ascii_case(s) {
Ok(MediaType::Application)
} else if "message".eq_ignore_ascii_case(s) {
Ok(MediaType::Message)
} else if "image".eq_ignore_ascii_case(s) {
Ok(MediaType::Image)
} else {
Err(ParseEnumError::Invalid(s.to_string()))
}
}
}
impl Display for MediaType {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(self.as_str())
}
}
#[derive(Debug, PartialEq, Eq, Clone)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum TransportProto {
Udp,
RtpAvp,
RtpSavp,
RtpAvpf,
RtpSavpf,
TcpDtlsRtpSavp,
TcpDtlsRtpSavpf,
UdpTlsRtpSavp,
UdpTlsRtpSavpf,
UdpDtlsSctp,
TcpDtlsSctp,
DtlsSctp,
Other(String),
}
impl TransportProto {
pub fn as_str(&self) -> &str {
match self {
TransportProto::Udp => "udp",
TransportProto::RtpAvp => "RTP/AVP",
TransportProto::RtpAvpf => "RTP/AVPF",
TransportProto::RtpSavp => "RTP/SAVP",
TransportProto::RtpSavpf => "RTP/SAVPF",
TransportProto::TcpDtlsRtpSavp => "TCP/DTLS/RTP/SAVP",
TransportProto::TcpDtlsRtpSavpf => "TCP/DTLS/RTP/SAVPF",
TransportProto::UdpTlsRtpSavp => "UDP/TLS/RTP/SAVP",
TransportProto::UdpTlsRtpSavpf => "UDP/TLS/RTP/SAVPF",
TransportProto::UdpDtlsSctp => "UDP/DTLS/SCTP",
TransportProto::TcpDtlsSctp => "TCP/DTLS/SCTP",
TransportProto::DtlsSctp => "DTLS/SCTP",
TransportProto::Other(proto) => proto.as_str(),
}
}
}
impl FromStr for TransportProto {
type Err = ();
fn from_str(s: &str) -> Result<Self, Self::Err> {
if "udp".eq_ignore_ascii_case(s) {
Ok(TransportProto::Udp)
} else if "RTP/AVP".eq_ignore_ascii_case(s) {
Ok(TransportProto::RtpAvp)
} else if "RTP/AVPF".eq_ignore_ascii_case(s) {
Ok(TransportProto::RtpAvpf)
} else if "RTP/SAVP".eq_ignore_ascii_case(s) {
Ok(TransportProto::RtpSavp)
} else if "RTP/SAVPF".eq_ignore_ascii_case(s) {
Ok(TransportProto::RtpSavpf)
} else if "TCP/DTLS/RTP/SAVP".eq_ignore_ascii_case(s) {
Ok(TransportProto::TcpDtlsRtpSavp)
} else if "TCP/DTLS/RTP/SAVPF".eq_ignore_ascii_case(s) {
Ok(TransportProto::TcpDtlsRtpSavpf)
} else if "UDP/TLS/RTP/SAVP".eq_ignore_ascii_case(s) {
Ok(TransportProto::UdpTlsRtpSavp)
} else if "UDP/TLS/RTP/SAVPF".eq_ignore_ascii_case(s) {
Ok(TransportProto::UdpTlsRtpSavpf)
} else if "UDP/DTLS/SCTP".eq_ignore_ascii_case(s) {
Ok(TransportProto::UdpDtlsSctp)
} else if "TCP/DTLS/SCTP".eq_ignore_ascii_case(s) {
Ok(TransportProto::TcpDtlsSctp)
} else if "DTLS/SCTP".eq_ignore_ascii_case(s) {
Ok(TransportProto::DtlsSctp)
} else {
Ok(TransportProto::Other(s.to_string()))
}
}
}
impl From<&str> for TransportProto {
fn from(value: &str) -> Self {
TransportProto::from_str(value).expect("infallible")
}
}
impl Display for TransportProto {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(self.as_str())
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum HashFunc {
Sha1,
Sha224,
Sha256,
Sha384,
Sha512,
Md5,
Md2,
Other(String),
}
impl HashFunc {
pub fn new(hash_func: impl AsRef<str>) -> Self {
let hash_func = hash_func.as_ref();
if hash_func.eq_ignore_ascii_case("sha-1") {
HashFunc::Sha1
} else if hash_func.eq_ignore_ascii_case("sha-224") {
HashFunc::Sha224
} else if hash_func.eq_ignore_ascii_case("sha-256") {
HashFunc::Sha256
} else if hash_func.eq_ignore_ascii_case("sha-384") {
HashFunc::Sha384
} else if hash_func.eq_ignore_ascii_case("sha-512") {
HashFunc::Sha512
} else if hash_func.eq_ignore_ascii_case("md-5") {
HashFunc::Md5
} else if hash_func.eq_ignore_ascii_case("md-2") {
HashFunc::Md2
} else {
HashFunc::Other(hash_func.to_string())
}
}
pub fn as_str(&self) -> &str {
match self {
HashFunc::Sha1 => "sha-1",
HashFunc::Sha224 => "sha-224",
HashFunc::Sha256 => "sha-256",
HashFunc::Sha384 => "sha-384",
HashFunc::Sha512 => "sha-512",
HashFunc::Md5 => "md-5",
HashFunc::Md2 => "md-2",
HashFunc::Other(s) => s.as_str(),
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum GroupSemantics {
LS,
FID,
SRF,
ANAT,
FEC,
DDP,
Other(String),
}
impl GroupSemantics {
pub fn new(semantics: impl AsRef<str>) -> Self {
let semantics = semantics.as_ref();
if "LS".eq_ignore_ascii_case(semantics) {
GroupSemantics::LS
} else if "FID".eq_ignore_ascii_case(semantics) {
GroupSemantics::FID
} else if "SRF".eq_ignore_ascii_case(semantics) {
GroupSemantics::SRF
} else if "ANAT".eq_ignore_ascii_case(semantics) {
GroupSemantics::ANAT
} else if "FEC".eq_ignore_ascii_case(semantics) {
GroupSemantics::FEC
} else if "DDP".eq_ignore_ascii_case(semantics) {
GroupSemantics::DDP
} else {
GroupSemantics::Other(semantics.to_string())
}
}
pub fn as_str(&self) -> &str {
match self {
GroupSemantics::LS => "LS",
GroupSemantics::FID => "FID",
GroupSemantics::SRF => "SRF",
GroupSemantics::ANAT => "ANAT",
GroupSemantics::DDP => "DDP",
GroupSemantics::FEC => "FEC",
GroupSemantics::Other(s) => s.as_str(),
}
}
}
#[derive(Debug, PartialEq, Clone, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum CryptoSuite {
AesCm128HmacSha1_80,
AesCm128HmacSha1_32,
F8_128HmacSha1_80,
Other(String),
}
impl CryptoSuite {
pub fn new(crypto_suite: impl AsRef<str>) -> Self {
let crypto_suite = crypto_suite.as_ref();
if "AES_CM_128_HMAC_SHA1_32".eq_ignore_ascii_case(crypto_suite) {
CryptoSuite::AesCm128HmacSha1_32
} else if "F8_128_HMAC_SHA1_80".eq_ignore_ascii_case(crypto_suite) {
CryptoSuite::F8_128HmacSha1_80
} else if "AES_CM_128_HMAC_SHA1_80".eq_ignore_ascii_case(crypto_suite) {
CryptoSuite::AesCm128HmacSha1_80
} else {
CryptoSuite::Other(crypto_suite.to_string())
}
}
pub fn as_str(&self) -> &str {
match self {
CryptoSuite::AesCm128HmacSha1_80 => "AES_CM_128_HMAC_SHA1_80",
CryptoSuite::AesCm128HmacSha1_32 => "AES_CM_128_HMAC_SHA1_32",
CryptoSuite::F8_128HmacSha1_80 => "F8_128_HMAC_SHA1_80",
CryptoSuite::Other(s) => s.as_str(),
}
}
}
#[derive(Debug, PartialEq, Clone, Eq, Copy)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum FecOrder {
FecSrtp,
SrtpFec,
}
#[derive(Debug, PartialEq, Clone, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum SrtpSessionParam {
Kdr(u8),
UnencryptedSrtp,
UnencryptedSrtcp,
UnauthenticatedSrtp,
FecOrder(FecOrder),
FecKey(Vec<SrtpKeyParam>),
Wsh(u8),
Extension(String),
}
#[derive(Debug, PartialEq, Clone, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum CandidateType {
Host,
Srflx,
Prflx,
Relay,
Other(String),
}
impl CandidateType {
pub fn new(cand_type: impl AsRef<str>) -> Self {
let cand_type = cand_type.as_ref();
if "host".eq_ignore_ascii_case(cand_type) {
CandidateType::Host
} else if "srflx".eq_ignore_ascii_case(cand_type) {
CandidateType::Srflx
} else if "prflx".eq_ignore_ascii_case(cand_type) {
CandidateType::Prflx
} else if "relay".eq_ignore_ascii_case(cand_type) {
CandidateType::Relay
} else {
CandidateType::Other(cand_type.to_string())
}
}
pub fn as_str(&self) -> &str {
match self {
CandidateType::Host => "host",
CandidateType::Srflx => "srflx",
CandidateType::Prflx => "prflx",
CandidateType::Relay => "relay",
CandidateType::Other(o) => o.as_str(),
}
}
}
#[derive(Debug, PartialEq, Clone, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum RtcpFbAck {
Rpsi,
App(Option<String>),
Ccfb,
Other(String),
}
#[derive(Debug, PartialEq, Clone, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum RtcpFbNack {
Pli,
Sli,
Rpsi,
App(Option<String>),
Ecn,
Other(String),
}
#[derive(Debug, PartialEq, Clone, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum RtcpFbCcm {
Fir,
Tmmbr(Option<String>),
Tstr,
Vbcm(Vec<u8>),
Other(String),
}
#[derive(Debug, PartialEq, Clone, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum RtcpFbVal {
Ack(Option<RtcpFbAck>),
Nack(Option<RtcpFbNack>),
TrrInt(u64),
Ccm(RtcpFbCcm),
TransportCc,
Other(String),
}
impl RtcpFbVal {
pub fn is_ack(&self) -> bool {
matches!(self, RtcpFbVal::Ack(None))
}
pub fn is_ack_rpsi(&self) -> bool {
matches!(self, RtcpFbVal::Ack(Some(RtcpFbAck::Rpsi)))
}
pub fn is_ack_app(&self) -> bool {
matches!(self, RtcpFbVal::Ack(Some(RtcpFbAck::App(_))))
}
pub fn is_ack_ccfb(&self) -> bool {
matches!(self, RtcpFbVal::Ack(Some(RtcpFbAck::Ccfb)))
}
pub fn is_nack(&self) -> bool {
matches!(self, RtcpFbVal::Nack(None))
}
pub fn is_nack_pli(&self) -> bool {
matches!(self, RtcpFbVal::Nack(Some(RtcpFbNack::Pli)))
}
pub fn is_nack_sli(&self) -> bool {
matches!(self, RtcpFbVal::Nack(Some(RtcpFbNack::Sli)))
}
pub fn is_nack_rpsi(&self) -> bool {
matches!(self, RtcpFbVal::Nack(Some(RtcpFbNack::Rpsi)))
}
pub fn is_nack_app(&self) -> bool {
matches!(self, RtcpFbVal::Nack(Some(RtcpFbNack::App(_))))
}
pub fn is_nack_ecn(&self) -> bool {
matches!(self, RtcpFbVal::Nack(Some(RtcpFbNack::Ecn)))
}
pub fn is_trr_int(&self) -> bool {
matches!(self, RtcpFbVal::TrrInt(_))
}
pub fn is_ccm_fir(&self) -> bool {
matches!(self, RtcpFbVal::Ccm(RtcpFbCcm::Fir))
}
pub fn is_ccm_tmmbr(&self) -> bool {
matches!(self, RtcpFbVal::Ccm(RtcpFbCcm::Tmmbr(_)))
}
pub fn is_ccm_tstr(&self) -> bool {
matches!(self, RtcpFbVal::Ccm(RtcpFbCcm::Tstr))
}
pub fn is_ccm_vbcm(&self) -> bool {
matches!(self, RtcpFbVal::Ccm(RtcpFbCcm::Vbcm(_)))
}
pub fn is_transport_cc(&self) -> bool {
matches!(self, RtcpFbVal::TransportCc)
}
}
impl From<RtcpFbAck> for RtcpFbVal {
fn from(value: RtcpFbAck) -> Self {
RtcpFbVal::Ack(Some(value))
}
}
impl From<RtcpFbNack> for RtcpFbVal {
fn from(value: RtcpFbNack) -> Self {
RtcpFbVal::Nack(Some(value))
}
}
impl From<RtcpFbCcm> for RtcpFbVal {
fn from(value: RtcpFbCcm) -> Self {
RtcpFbVal::Ccm(value)
}
}
impl Display for RtcpFbVal {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
RtcpFbVal::Ack(ack) => {
write!(f, "ack")?;
if let Some(ack) = ack {
match ack {
RtcpFbAck::Rpsi => write!(f, " rpsi")?,
RtcpFbAck::Ccfb => write!(f, " ccfb")?,
RtcpFbAck::App(app) => {
write!(f, " app")?;
if let Some(app_param) = app {
f.write_char(' ')?;
f.write_str(app_param)?;
}
}
RtcpFbAck::Other(other) => {
f.write_char(' ')?;
f.write_str(other)?;
}
}
}
Ok(())
}
RtcpFbVal::Nack(nack) => {
write!(f, "nack")?;
if let Some(nack) = nack {
match nack {
RtcpFbNack::Pli => write!(f, " pli")?,
RtcpFbNack::Sli => write!(f, " sli")?,
RtcpFbNack::Rpsi => write!(f, " rpsi")?,
RtcpFbNack::Ecn => write!(f, " ecn")?,
RtcpFbNack::App(app) => {
write!(f, " app")?;
if let Some(app_param) = app {
f.write_char(' ')?;
f.write_str(app_param)?;
}
}
RtcpFbNack::Other(other) => {
f.write_char(' ')?;
f.write_str(other)?;
}
}
}
Ok(())
}
RtcpFbVal::TrrInt(trr_int) => write!(f, "trr-int {trr_int}"),
RtcpFbVal::Ccm(ccm) => {
write!(f, "ccm")?;
match ccm {
RtcpFbCcm::Fir => write!(f, " fir")?,
RtcpFbCcm::Tstr => write!(f, " tstr")?,
RtcpFbCcm::Tmmbr(smaxpr) => {
write!(f, " tmmbr")?;
if let Some(smaxpr) = smaxpr {
f.write_char(' ')?;
f.write_str(smaxpr)?;
}
}
RtcpFbCcm::Vbcm(vbcm) => {
write!(f, " vbcm")?;
for v in vbcm {
f.write_char(' ')?;
write!(f, "{v}")?;
}
}
RtcpFbCcm::Other(other) => {
f.write_char(' ')?;
f.write_str(other)?;
}
}
Ok(())
}
RtcpFbVal::TransportCc => f.write_str("transport-cc"),
RtcpFbVal::Other(other) => f.write_str(other),
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum SsrcAttribute {
Cname,
PreviousSsrc,
Fmtp,
Rtcp,
ReferenceClock,
MediaClockSource,
Other(String),
}
impl SsrcAttribute {
pub fn new(attribute: impl AsRef<str>) -> Self {
use crate::{Fmtp, MediaClockSource, ReferenceClock, Rtcp};
let attr = attribute.as_ref();
if "cname".eq_ignore_ascii_case(attr) {
SsrcAttribute::Cname
} else if "previous-ssrc".eq_ignore_ascii_case(attr) {
SsrcAttribute::PreviousSsrc
} else if <Fmtp as TypedAttribute>::NAME.eq_ignore_ascii_case(attr) {
SsrcAttribute::Fmtp
} else if <Rtcp as TypedAttribute>::NAME.eq_ignore_ascii_case(attr) {
SsrcAttribute::Rtcp
} else if <ReferenceClock as TypedAttribute>::NAME.eq_ignore_ascii_case(attr) {
SsrcAttribute::ReferenceClock
} else if <MediaClockSource as TypedAttribute>::NAME.eq_ignore_ascii_case(attr) {
SsrcAttribute::MediaClockSource
} else {
SsrcAttribute::Other(attr.to_string())
}
}
pub fn as_str(&self) -> &str {
use crate::{Fmtp, MediaClockSource, ReferenceClock, Rtcp};
match self {
SsrcAttribute::Cname => "cname",
SsrcAttribute::PreviousSsrc => "previous-ssrc",
SsrcAttribute::Fmtp => <Fmtp as TypedAttribute>::NAME,
SsrcAttribute::Rtcp => <Rtcp as TypedAttribute>::NAME,
SsrcAttribute::ReferenceClock => <ReferenceClock as TypedAttribute>::NAME,
SsrcAttribute::MediaClockSource => <MediaClockSource as TypedAttribute>::NAME,
SsrcAttribute::Other(other) => other.as_str(),
}
}
}
impl<T: TypedAttribute> From<T> for SsrcAttribute {
fn from(_attr: T) -> Self {
SsrcAttribute::new(T::NAME)
}
}
#[derive(Debug, PartialEq, Clone, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum RtcpFbPt {
Fmt(u8),
Wildcard,
}
impl From<u8> for RtcpFbPt {
fn from(pt: u8) -> Self {
RtcpFbPt::Fmt(pt)
}
}
#[derive(Debug, PartialEq, Clone, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum CandidateAddress {
IpAddr(std::net::IpAddr),
FQDN(String),
}