/* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ extern crate url; use std::convert::TryFrom; use std::fmt; use std::iter; use std::str::FromStr; use error::SdpParserInternalError; use network::{parse_network_type, parse_unicast_address}; use SdpType; use address::{Address, AddressType, ExplicitlyTypedAddress}; use anonymizer::{AnonymizingClone, StatefulSdpAnonymizer}; // Serialization helper marcos and functions #[macro_export] macro_rules! option_to_string { ($fmt_str:expr, $opt:expr) => { match $opt { Some(ref x) => format!($fmt_str, x), None => "".to_string(), } }; } #[macro_export] macro_rules! write_option_string { ($f:expr, $fmt_str:expr, $opt:expr) => { match $opt { Some(ref x) => write!($f, $fmt_str, x), None => Ok(()), } }; } #[macro_export] macro_rules! maybe_vector_to_string { ($fmt_str:expr, $vec:expr, $sep:expr) => { match $vec.len() { 0 => "".to_string(), _ => format!( $fmt_str, $vec.iter() .map(ToString::to_string) .collect::>() .join($sep) ), } }; } #[macro_export] macro_rules! non_empty_string_vec { ( $( $x:expr ),* ) => { { let mut temp_vec = Vec::new(); $( if !$x.is_empty() { temp_vec.push($x); } )* temp_vec } }; } pub fn maybe_print_param(name: &str, param: T, default_value: T) -> String where T: PartialEq + ToString, { if param != default_value { name.to_owned() + ¶m.to_string() } else { "".to_string() } } pub fn maybe_print_bool_param(name: &str, param: bool, default_value: bool) -> String { if param != default_value { name.to_owned() + "=" + &(param as i32).to_string() } else { "".to_string() } } #[derive(Debug, Clone, PartialEq)] #[cfg_attr(feature = "serialize", derive(Serialize))] pub enum SdpSingleDirection { // This is explicitly 1 and 2 to match the defines in the C++ glue code. Send = 1, Recv = 2, } impl fmt::Display for SdpSingleDirection { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match *self { SdpSingleDirection::Send => "send", SdpSingleDirection::Recv => "recv", } .fmt(f) } } #[derive(Debug, PartialEq, Clone)] #[cfg_attr(feature = "serialize", derive(Serialize))] pub enum SdpAttributePayloadType { PayloadType(u8), Wildcard, // Wildcard means "*", } impl fmt::Display for SdpAttributePayloadType { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match *self { SdpAttributePayloadType::PayloadType(pt) => pt.fmt(f), SdpAttributePayloadType::Wildcard => "*".fmt(f), } } } #[derive(Debug, Clone, PartialEq)] #[cfg_attr(feature = "serialize", derive(Serialize))] pub enum SdpAttributeCandidateTransport { Udp, Tcp, } impl fmt::Display for SdpAttributeCandidateTransport { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match *self { SdpAttributeCandidateTransport::Udp => "UDP", SdpAttributeCandidateTransport::Tcp => "TCP", } .fmt(f) } } #[derive(Debug, Clone, PartialEq)] #[cfg_attr(feature = "serialize", derive(Serialize))] pub enum SdpAttributeCandidateType { Host, Srflx, Prflx, Relay, } impl fmt::Display for SdpAttributeCandidateType { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match *self { SdpAttributeCandidateType::Host => "host", SdpAttributeCandidateType::Srflx => "srflx", SdpAttributeCandidateType::Prflx => "prflx", SdpAttributeCandidateType::Relay => "relay", } .fmt(f) } } #[derive(Debug, Clone, PartialEq)] #[cfg_attr(feature = "serialize", derive(Serialize))] pub enum SdpAttributeCandidateTcpType { Active, Passive, Simultaneous, } impl fmt::Display for SdpAttributeCandidateTcpType { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match *self { SdpAttributeCandidateTcpType::Active => "active", SdpAttributeCandidateTcpType::Passive => "passive", SdpAttributeCandidateTcpType::Simultaneous => "so", } .fmt(f) } } #[derive(Clone)] #[cfg_attr(feature = "serialize", derive(Serialize))] #[cfg_attr(feature = "enhanced_debug", derive(Debug))] pub struct SdpAttributeCandidate { pub foundation: String, pub component: u32, pub transport: SdpAttributeCandidateTransport, pub priority: u64, pub address: Address, pub port: u32, pub c_type: SdpAttributeCandidateType, pub raddr: Option
, pub rport: Option, pub tcp_type: Option, pub generation: Option, pub ufrag: Option, pub networkcost: Option, pub unknown_extensions: Vec<(String, String)>, } impl fmt::Display for SdpAttributeCandidate { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!( f, "{foundation} {component} {transport} {priority} \ {address} {port} typ {ctype}\ {raddr}{rport}{tcp_type}{generation}{ufrag}{cost}\ {unknown}", foundation = self.foundation, component = self.component, transport = self.transport, priority = self.priority, address = self.address, port = self.port, ctype = self.c_type, raddr = option_to_string!(" raddr {}", self.raddr), rport = option_to_string!(" rport {}", self.rport), tcp_type = option_to_string!(" tcptype {}", self.tcp_type), generation = option_to_string!(" generation {}", self.generation), ufrag = option_to_string!(" ufrag {}", self.ufrag), cost = option_to_string!(" network-cost {}", self.networkcost), unknown = self .unknown_extensions .iter() .map(|&(ref name, ref value)| format!(" {} {}", name, value)) .collect::() ) } } impl SdpAttributeCandidate { pub fn new( foundation: String, component: u32, transport: SdpAttributeCandidateTransport, priority: u64, address: Address, port: u32, c_type: SdpAttributeCandidateType, ) -> SdpAttributeCandidate { SdpAttributeCandidate { foundation, component, transport, priority, address, port, c_type, raddr: None, rport: None, tcp_type: None, generation: None, ufrag: None, networkcost: None, unknown_extensions: Vec::new(), } } fn set_remote_address(&mut self, addr: Address) { self.raddr = Some(addr) } fn set_remote_port(&mut self, p: u32) { self.rport = Some(p) } fn set_tcp_type(&mut self, t: SdpAttributeCandidateTcpType) { self.tcp_type = Some(t) } fn set_generation(&mut self, g: u32) { self.generation = Some(g) } fn set_ufrag(&mut self, u: String) { self.ufrag = Some(u) } fn set_network_cost(&mut self, n: u32) { self.networkcost = Some(n) } fn add_unknown_extension(&mut self, name: String, value: String) { self.unknown_extensions.push((name, value)); } } impl AnonymizingClone for SdpAttributeCandidate { fn masked_clone(&self, anonymizer: &mut StatefulSdpAnonymizer) -> Self { let mut masked = self.clone(); masked.address = anonymizer.mask_address(&self.address); masked.port = anonymizer.mask_port(self.port); masked.raddr = self .raddr .clone() .map(|addr| anonymizer.mask_address(&addr)); masked.rport = self.rport.map(|port| anonymizer.mask_port(port)); masked } } #[derive(Clone)] #[cfg_attr(feature = "serialize", derive(Serialize))] #[cfg_attr(feature = "enhanced_debug", derive(Debug))] pub enum SdpAttributeDtlsMessage { Client(String), Server(String), } impl fmt::Display for SdpAttributeDtlsMessage { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match *self { SdpAttributeDtlsMessage::Client(ref msg) => format!("client {}", msg), SdpAttributeDtlsMessage::Server(ref msg) => format!("server {}", msg), } .fmt(f) } } #[derive(Clone)] #[cfg_attr(feature = "serialize", derive(Serialize))] #[cfg_attr(feature = "enhanced_debug", derive(Debug))] pub struct SdpAttributeRemoteCandidate { pub component: u32, pub address: Address, pub port: u32, } impl fmt::Display for SdpAttributeRemoteCandidate { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!( f, "{component} {addr} {port}", component = self.component, addr = self.address, port = self.port ) } } impl AnonymizingClone for SdpAttributeRemoteCandidate { fn masked_clone(&self, anon: &mut StatefulSdpAnonymizer) -> Self { SdpAttributeRemoteCandidate { address: anon.mask_address(&self.address), port: anon.mask_port(self.port), component: self.component, } } } #[derive(Clone)] #[cfg_attr(feature = "serialize", derive(Serialize))] #[cfg_attr(feature = "enhanced_debug", derive(Debug))] pub struct SdpAttributeSimulcastId { pub id: String, pub paused: bool, } impl SdpAttributeSimulcastId { pub fn new(idstr: &str) -> SdpAttributeSimulcastId { if let Some(idstr) = idstr.strip_prefix('~') { SdpAttributeSimulcastId { id: idstr.to_string(), paused: true, } } else { SdpAttributeSimulcastId { id: idstr.to_string(), paused: false, } } } } impl fmt::Display for SdpAttributeSimulcastId { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { if self.paused { write!(f, "~")?; } self.id.fmt(f) } } #[repr(C)] #[derive(Clone)] #[cfg_attr(feature = "serialize", derive(Serialize))] #[cfg_attr(feature = "enhanced_debug", derive(Debug))] pub struct SdpAttributeSimulcastVersion { pub ids: Vec, } impl SdpAttributeSimulcastVersion { pub fn new(idlist: &str) -> SdpAttributeSimulcastVersion { SdpAttributeSimulcastVersion { ids: idlist .split(',') .map(SdpAttributeSimulcastId::new) .collect(), } } } impl fmt::Display for SdpAttributeSimulcastVersion { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { self.ids .iter() .map(ToString::to_string) .collect::>() .join(",") .fmt(f) } } #[derive(Clone)] #[cfg_attr(feature = "serialize", derive(Serialize))] #[cfg_attr(feature = "enhanced_debug", derive(Debug))] pub struct SdpAttributeSimulcast { pub send: Vec, pub receive: Vec, } impl fmt::Display for SdpAttributeSimulcast { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { non_empty_string_vec![ maybe_vector_to_string!("send {}", self.send, ";"), maybe_vector_to_string!("recv {}", self.receive, ";") ] .join(" ") .fmt(f) } } #[derive(Clone)] #[cfg_attr(feature = "serialize", derive(Serialize))] #[cfg_attr(feature = "enhanced_debug", derive(Debug))] pub struct SdpAttributeRtcp { pub port: u16, pub unicast_addr: Option, } impl SdpAttributeRtcp { pub fn new(port: u16) -> SdpAttributeRtcp { SdpAttributeRtcp { port, unicast_addr: None, } } fn set_addr(&mut self, addr: ExplicitlyTypedAddress) { self.unicast_addr = Some(addr) } } impl fmt::Display for SdpAttributeRtcp { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match self.unicast_addr { Some(ref addr) => write!(f, "{} {}", self.port, addr), None => self.port.fmt(f), } } } #[derive(Clone)] #[cfg_attr(feature = "serialize", derive(Serialize))] #[cfg_attr(feature = "enhanced_debug", derive(Debug))] pub enum SdpAttributeRtcpFbType { Ack = 0, Ccm = 2, // This is explicitly 2 to make the conversion to the // enum used in the glue-code possible. The glue code has "app" // in the place of 1 Nack, TrrInt, Remb, TransCc, } impl fmt::Display for SdpAttributeRtcpFbType { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match *self { SdpAttributeRtcpFbType::Ack => "ack", SdpAttributeRtcpFbType::Ccm => "ccm", SdpAttributeRtcpFbType::Nack => "nack", SdpAttributeRtcpFbType::TrrInt => "trr-int", SdpAttributeRtcpFbType::Remb => "goog-remb", SdpAttributeRtcpFbType::TransCc => "transport-cc", } .fmt(f) } } #[derive(Clone)] #[cfg_attr(feature = "serialize", derive(Serialize))] #[cfg_attr(feature = "enhanced_debug", derive(Debug))] pub struct SdpAttributeRtcpFb { pub payload_type: SdpAttributePayloadType, pub feedback_type: SdpAttributeRtcpFbType, pub parameter: String, pub extra: String, } impl fmt::Display for SdpAttributeRtcpFb { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "{} {}", self.payload_type, self.feedback_type,)?; if !self.parameter.is_empty() { write!( f, " {}{}", self.parameter, maybe_print_param(" ", self.extra.clone(), "".to_string()), )?; } Ok(()) } } #[derive(Clone)] #[cfg_attr(feature = "serialize", derive(Serialize))] #[cfg_attr(feature = "enhanced_debug", derive(Debug))] pub enum SdpAttributeDirection { Recvonly, Sendonly, Sendrecv, } impl fmt::Display for SdpAttributeDirection { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match *self { SdpAttributeDirection::Recvonly => "recvonly", SdpAttributeDirection::Sendonly => "sendonly", SdpAttributeDirection::Sendrecv => "sendrecv", } .fmt(f) } } #[derive(Clone)] #[cfg_attr(feature = "serialize", derive(Serialize))] #[cfg_attr(feature = "enhanced_debug", derive(Debug))] pub struct SdpAttributeExtmap { pub id: u16, pub direction: Option, pub url: String, pub extension_attributes: Option, } impl fmt::Display for SdpAttributeExtmap { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!( f, "{id}{direction} {url}{ext}", id = self.id, direction = option_to_string!("/{}", self.direction), url = self.url, ext = option_to_string!(" {}", self.extension_attributes) ) } } #[derive(Clone, Copy)] #[cfg_attr(feature = "serialize", derive(Serialize))] #[cfg_attr(feature = "enhanced_debug", derive(Debug))] pub struct RtxFmtpParameters { pub apt: u8, pub rtx_time: Option, } impl fmt::Display for RtxFmtpParameters { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { if let Some(rtx_time) = self.rtx_time { write!(f, "apt={};rtx-time={}", self.apt, rtx_time) } else { write!(f, "apt={}", self.apt) } } } #[derive(Clone)] #[cfg_attr(feature = "serialize", derive(Serialize))] #[cfg_attr(feature = "enhanced_debug", derive(Debug))] pub struct SdpAttributeFmtpParameters { // H264 // TODO(bug 1466859): Support sprop-parameter-sets // pub static const max_sprop_len: u32 = 128, // pub sprop_parameter_sets: [u8, max_sprop_len], pub packetization_mode: u32, pub level_asymmetry_allowed: bool, pub profile_level_id: u32, pub max_fs: u32, pub max_cpb: u32, pub max_dpb: u32, pub max_br: u32, pub max_mbps: u32, // VP8 and VP9 // max_fs, already defined in H264 pub max_fr: u32, // Opus https://tools.ietf.org/html/rfc7587 pub maxplaybackrate: u32, pub maxaveragebitrate: u32, pub usedtx: bool, pub stereo: bool, pub useinbandfec: bool, pub cbr: bool, pub ptime: u32, pub minptime: u32, pub maxptime: u32, // Red pub encodings: Vec, // telephone-event pub dtmf_tones: String, // RTX pub rtx: Option, // Unknown pub unknown_tokens: Vec, } impl fmt::Display for SdpAttributeFmtpParameters { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { if let Some(ref rtx) = self.rtx { // rtx return write!(f, "{}", rtx); } if !self.dtmf_tones.is_empty() { // telephone-event return write!(f, "{}", self.dtmf_tones); } else if !self.encodings.is_empty() { // red encodings return self .encodings .iter() .map(ToString::to_string) .collect::>() .join("/") .fmt(f); }; write!( f, "{}", non_empty_string_vec![ maybe_print_param( "profile-level-id=", format!("{:06x}", self.profile_level_id), "420010".to_string() ), maybe_print_bool_param( "level-asymmetry-allowed", self.level_asymmetry_allowed, false ), maybe_print_param("packetization-mode=", self.packetization_mode, 0), maybe_print_param("max-fs=", self.max_fs, 0), maybe_print_param("max-cpb=", self.max_cpb, 0), maybe_print_param("max-dpb=", self.max_dpb, 0), maybe_print_param("max-br=", self.max_br, 0), maybe_print_param("max-mbps=", self.max_mbps, 0), maybe_print_param("max-fr=", self.max_fr, 0), maybe_print_param("maxplaybackrate=", self.maxplaybackrate, 48000), maybe_print_param("maxaveragebitrate=", self.maxaveragebitrate, 0), maybe_print_param("ptime=", self.ptime, 0), maybe_print_param("minptime=", self.minptime, 0), maybe_print_param("maxptime=", self.maxptime, 0), maybe_print_bool_param("usedtx", self.usedtx, false), maybe_print_bool_param("stereo", self.stereo, false), maybe_print_bool_param("useinbandfec", self.useinbandfec, false), maybe_print_bool_param("cbr", self.cbr, false), maybe_vector_to_string!("{}", self.unknown_tokens, ",") ] .join(";") ) } } #[derive(Clone)] #[cfg_attr(feature = "serialize", derive(Serialize))] #[cfg_attr(feature = "enhanced_debug", derive(Debug))] pub struct SdpAttributeFmtp { pub payload_type: u8, pub parameters: SdpAttributeFmtpParameters, } impl fmt::Display for SdpAttributeFmtp { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!( f, "{pt} {parameter}", pt = self.payload_type, parameter = self.parameters ) } } #[derive(Clone, Copy, PartialEq, Eq)] #[cfg_attr(feature = "serialize", derive(Serialize))] #[cfg_attr(feature = "enhanced_debug", derive(Debug))] pub enum SdpAttributeFingerprintHashType { Sha1, Sha224, Sha256, Sha384, Sha512, } impl SdpAttributeFingerprintHashType { pub fn try_from_name(name: &str) -> Result { match name { "sha-1" => Ok(Self::Sha1), "sha-224" => Ok(Self::Sha224), "sha-256" => Ok(Self::Sha256), "sha-384" => Ok(Self::Sha384), "sha-512" => Ok(Self::Sha512), unknown => Err(SdpParserInternalError::Unsupported(format!( "fingerprint contains an unsupported hash algorithm '{}'", unknown ))), } } pub fn octet_count(&self) -> usize { match self { Self::Sha1 => 20, Self::Sha224 => 28, Self::Sha256 => 32, Self::Sha384 => 48, Self::Sha512 => 64, } } pub fn parse_octets(&self, octets_string: &str) -> Result, SdpParserInternalError> { let bytes = octets_string .split(':') .map(|byte_token| { if byte_token.len() != 2 { return Err(SdpParserInternalError::Generic( "fingerpint's byte tokens must have 2 hexdigits".to_string(), )); } Ok(u8::from_str_radix(byte_token, 16)?) }) .collect::, _>>()?; if bytes.len() != self.octet_count() { return Err(SdpParserInternalError::Generic(format!( "fingerprint has {} bytes but should have {} bytes", bytes.len(), self.octet_count(), ))); } Ok(bytes) } } impl fmt::Display for SdpAttributeFingerprintHashType { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match *self { SdpAttributeFingerprintHashType::Sha1 => "sha-1", SdpAttributeFingerprintHashType::Sha224 => "sha-224", SdpAttributeFingerprintHashType::Sha256 => "sha-256", SdpAttributeFingerprintHashType::Sha384 => "sha-384", SdpAttributeFingerprintHashType::Sha512 => "sha-512", } .fmt(f) } } #[derive(Clone, PartialEq, Eq)] #[cfg_attr(feature = "serialize", derive(Serialize))] #[cfg_attr(feature = "enhanced_debug", derive(Debug))] pub struct SdpAttributeFingerprint { pub hash_algorithm: SdpAttributeFingerprintHashType, pub fingerprint: Vec, } impl TryFrom<(SdpAttributeFingerprintHashType, Vec)> for SdpAttributeFingerprint { type Error = SdpParserInternalError; fn try_from( parts: (SdpAttributeFingerprintHashType, Vec), ) -> Result { let (hash_algorithm, fingerprint) = parts; match (hash_algorithm.octet_count(), fingerprint.len()) { (a, b) if a == b => Ok(Self { hash_algorithm, fingerprint, }), (a, b) => Err(SdpParserInternalError::Generic(format!( "Hash algoritm expects {} fingerprint bytes not {}", a, b ))), } } } impl fmt::Display for SdpAttributeFingerprint { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!( f, "{hash} {fp}", hash = self.hash_algorithm, fp = self .fingerprint .iter() .map(|byte| format!("{:02X}", byte)) .collect::>() .join(":") ) } } impl AnonymizingClone for SdpAttributeFingerprint { fn masked_clone(&self, anon: &mut StatefulSdpAnonymizer) -> Self { SdpAttributeFingerprint { hash_algorithm: self.hash_algorithm, fingerprint: anon.mask_cert_finger_print(&self.fingerprint), } } } fn imageattr_discrete_value_list_to_string(values: &[T]) -> String where T: ToString, { match values.len() { 1 => values[0].to_string(), _ => format!( "[{}]", values .iter() .map(ToString::to_string) .collect::>() .join(",") ), } } #[derive(Debug, PartialEq, Clone)] #[cfg_attr(feature = "serialize", derive(Serialize))] pub enum SdpAttributeImageAttrXyRange { Range(u32, u32, Option), // min, max, step DiscreteValues(Vec), } impl fmt::Display for SdpAttributeImageAttrXyRange { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match *self { SdpAttributeImageAttrXyRange::Range(ref min, ref max, ref step_opt) => { write!(f, "[{}:", min)?; if step_opt.is_some() { write!(f, "{}:", step_opt.unwrap())?; } write!(f, "{}]", max) } SdpAttributeImageAttrXyRange::DiscreteValues(ref values) => { write!(f, "{}", imageattr_discrete_value_list_to_string(values)) } } } } #[derive(Debug, PartialEq, Clone)] #[cfg_attr(feature = "serialize", derive(Serialize))] pub enum SdpAttributeImageAttrSRange { Range(f32, f32), // min, max DiscreteValues(Vec), } impl fmt::Display for SdpAttributeImageAttrSRange { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match *self { SdpAttributeImageAttrSRange::Range(ref min, ref max) => write!(f, "[{}-{}]", min, max), SdpAttributeImageAttrSRange::DiscreteValues(ref values) => { write!(f, "{}", imageattr_discrete_value_list_to_string(values)) } } } } #[derive(Debug, PartialEq, Clone)] #[cfg_attr(feature = "serialize", derive(Serialize))] pub struct SdpAttributeImageAttrPRange { pub min: f32, pub max: f32, } impl fmt::Display for SdpAttributeImageAttrPRange { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "[{}-{}]", self.min, self.max) } } #[derive(Debug, PartialEq, Clone)] #[cfg_attr(feature = "serialize", derive(Serialize))] pub struct SdpAttributeImageAttrSet { pub x: SdpAttributeImageAttrXyRange, pub y: SdpAttributeImageAttrXyRange, pub sar: Option, pub par: Option, pub q: Option, } impl fmt::Display for SdpAttributeImageAttrSet { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "[x={x},y={y}", x = self.x, y = self.y)?; write_option_string!(f, ",sar={}", self.sar)?; write_option_string!(f, ",par={}", self.par)?; write_option_string!(f, ",q={}", self.q)?; write!(f, "]") } } #[derive(Debug, PartialEq, Clone)] #[cfg_attr(feature = "serialize", derive(Serialize))] pub enum SdpAttributeImageAttrSetList { Sets(Vec), Wildcard, } impl fmt::Display for SdpAttributeImageAttrSetList { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match *self { SdpAttributeImageAttrSetList::Sets(ref sets) => sets .iter() .map(ToString::to_string) .collect::>() .join(" ") .fmt(f), SdpAttributeImageAttrSetList::Wildcard => "*".fmt(f), } } } #[derive(Clone)] #[cfg_attr(feature = "serialize", derive(Serialize))] #[cfg_attr(feature = "enhanced_debug", derive(Debug))] pub struct SdpAttributeImageAttr { pub pt: SdpAttributePayloadType, pub send: SdpAttributeImageAttrSetList, pub recv: SdpAttributeImageAttrSetList, } impl fmt::Display for SdpAttributeImageAttr { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { let maybe_sets_to_string = |set_list| match set_list { SdpAttributeImageAttrSetList::Sets(sets) => match sets.len() { 0 => None, _ => Some(SdpAttributeImageAttrSetList::Sets(sets)), }, x => Some(x), }; self.pt.fmt(f)?; write_option_string!(f, " send {}", maybe_sets_to_string(self.send.clone()))?; write_option_string!(f, " recv {}", maybe_sets_to_string(self.recv.clone())) } } #[derive(Clone)] #[cfg_attr(feature = "serialize", derive(Serialize))] #[cfg_attr(feature = "enhanced_debug", derive(Debug))] pub struct SdpAttributeSctpmap { pub port: u16, pub channels: u32, } impl fmt::Display for SdpAttributeSctpmap { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!( f, "{port} webrtc-datachannel {channels}", port = self.port, channels = self.channels ) } } #[derive(Clone)] #[cfg_attr(feature = "serialize", derive(Serialize))] #[cfg_attr(feature = "enhanced_debug", derive(Debug))] pub enum SdpAttributeGroupSemantic { LipSynchronization, // RFC5888 FlowIdentification, // RFC5888 SingleReservationFlow, // RFC3524 AlternateNetworkAddressType, // RFC4091 ForwardErrorCorrection, // RFC5956 DecodingDependency, // RFC5583 Bundle, // draft-ietc-mmusic-bundle } impl fmt::Display for SdpAttributeGroupSemantic { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match *self { SdpAttributeGroupSemantic::LipSynchronization => "LS", SdpAttributeGroupSemantic::FlowIdentification => "FID", SdpAttributeGroupSemantic::SingleReservationFlow => "SRF", SdpAttributeGroupSemantic::AlternateNetworkAddressType => "ANAT", SdpAttributeGroupSemantic::ForwardErrorCorrection => "FEC", SdpAttributeGroupSemantic::DecodingDependency => "DDP", SdpAttributeGroupSemantic::Bundle => "BUNDLE", } .fmt(f) } } #[derive(Clone)] #[cfg_attr(feature = "serialize", derive(Serialize))] #[cfg_attr(feature = "enhanced_debug", derive(Debug))] pub struct SdpAttributeGroup { pub semantics: SdpAttributeGroupSemantic, pub tags: Vec, } impl fmt::Display for SdpAttributeGroup { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!( f, "{}{}", self.semantics, maybe_vector_to_string!(" {}", self.tags, " ") ) } } #[derive(Clone)] #[cfg_attr(feature = "serialize", derive(Serialize))] #[cfg_attr(feature = "enhanced_debug", derive(Debug))] pub struct SdpAttributeMsid { pub id: String, pub appdata: Option, } impl fmt::Display for SdpAttributeMsid { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { self.id.fmt(f)?; write_option_string!(f, " {}", self.appdata) } } #[derive(Clone, Debug)] #[cfg_attr(feature = "serialize", derive(Serialize))] pub struct SdpAttributeMsidSemantic { pub semantic: String, pub msids: Vec, } impl fmt::Display for SdpAttributeMsidSemantic { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "{} ", self.semantic)?; match self.msids.len() { 0 => "*".fmt(f), _ => self.msids.join(" ").fmt(f), } } } #[derive(Clone)] #[cfg_attr(feature = "serialize", derive(Serialize))] #[cfg_attr(feature = "enhanced_debug", derive(Debug))] pub struct SdpAttributeRidParameters { pub max_width: u32, pub max_height: u32, pub max_fps: u32, pub max_fs: u32, pub max_br: u32, pub max_pps: u32, pub unknown: Vec, } impl fmt::Display for SdpAttributeRidParameters { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { non_empty_string_vec![ maybe_print_param("max-width=", self.max_width, 0), maybe_print_param("max-height=", self.max_height, 0), maybe_print_param("max-fps=", self.max_fps, 0), maybe_print_param("max-fs=", self.max_fs, 0), maybe_print_param("max-br=", self.max_br, 0), maybe_print_param("max-pps=", self.max_pps, 0), maybe_vector_to_string!("{}", self.unknown, ";") ] .join(";") .fmt(f) } } #[derive(Clone)] #[cfg_attr(feature = "serialize", derive(Serialize))] #[cfg_attr(feature = "enhanced_debug", derive(Debug))] pub struct SdpAttributeRid { pub id: String, pub direction: SdpSingleDirection, pub formats: Vec, pub params: SdpAttributeRidParameters, pub depends: Vec, } impl fmt::Display for SdpAttributeRid { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!( f, "{id} {direction}{format}", id = self.id, direction = self.direction, format = match non_empty_string_vec![ maybe_vector_to_string!("pt={}", self.formats, ","), self.params.to_string(), maybe_vector_to_string!("depends={}", self.depends, ",") ] .join(";") .as_str() { "" => "".to_string(), x => format!(" {}", x), } ) } } #[derive(Clone)] #[cfg_attr(feature = "serialize", derive(Serialize))] #[cfg_attr(feature = "enhanced_debug", derive(Debug))] pub struct SdpAttributeRtpmap { pub payload_type: u8, pub codec_name: String, pub frequency: u32, pub channels: Option, } impl SdpAttributeRtpmap { pub fn new(payload_type: u8, codec_name: String, frequency: u32) -> SdpAttributeRtpmap { SdpAttributeRtpmap { payload_type, codec_name, frequency, channels: None, } } fn set_channels(&mut self, c: u32) { self.channels = Some(c) } } impl fmt::Display for SdpAttributeRtpmap { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!( f, "{pt} {name}/{freq}", pt = self.payload_type, name = self.codec_name, freq = self.frequency )?; write_option_string!(f, "/{}", self.channels) } } #[derive(Clone)] #[cfg_attr(feature = "serialize", derive(Serialize))] #[cfg_attr(feature = "enhanced_debug", derive(Debug))] pub enum SdpAttributeSetup { Active, Actpass, Holdconn, Passive, } impl fmt::Display for SdpAttributeSetup { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match *self { SdpAttributeSetup::Active => "active", SdpAttributeSetup::Actpass => "actpass", SdpAttributeSetup::Holdconn => "holdconn", SdpAttributeSetup::Passive => "passive", } .fmt(f) } } #[derive(Clone)] #[cfg_attr(feature = "serialize", derive(Serialize))] #[cfg_attr(feature = "enhanced_debug", derive(Debug))] pub struct SdpAttributeSsrc { pub id: u32, pub attribute: Option, pub value: Option, } impl SdpAttributeSsrc { pub fn new(id: u32) -> SdpAttributeSsrc { SdpAttributeSsrc { id, attribute: None, value: None, } } fn set_attribute(&mut self, a: &str) { if a.find(':') == None { self.attribute = Some(a.to_string()); } else { let v: Vec<&str> = a.splitn(2, ':').collect(); self.attribute = Some(v[0].to_string()); self.value = Some(v[1].to_string()); } } } impl fmt::Display for SdpAttributeSsrc { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { self.id.fmt(f)?; write_option_string!(f, " {}", self.attribute)?; write_option_string!(f, ":{}", self.value) } } impl AnonymizingClone for SdpAttributeSsrc { fn masked_clone(&self, anon: &mut StatefulSdpAnonymizer) -> Self { Self { id: self.id, attribute: self.attribute.clone(), value: self.attribute.as_ref().and_then(|attribute| { match (attribute.to_lowercase().as_str(), &self.value) { ("cname", Some(ref cname)) => Some(anon.mask_cname(cname.as_str())), (_, Some(_)) => self.value.clone(), (_, None) => None, } }), } } } #[derive(Clone)] #[cfg_attr(feature = "serialize", derive(Serialize))] #[cfg_attr(feature = "enhanced_debug", derive(Debug))] pub enum SdpSsrcGroupSemantic { Duplication, // RFC7104 FlowIdentification, // RFC5576 ForwardErrorCorrection, // RFC5576 ForwardErrorCorrectionFr, // RFC5956 Sim, // not registered with IANA, but used in hangouts } impl fmt::Display for SdpSsrcGroupSemantic { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match *self { SdpSsrcGroupSemantic::Duplication => "DUP", SdpSsrcGroupSemantic::FlowIdentification => "FID", SdpSsrcGroupSemantic::ForwardErrorCorrection => "FEC", SdpSsrcGroupSemantic::ForwardErrorCorrectionFr => "FEC-FR", SdpSsrcGroupSemantic::Sim => "SIM", } .fmt(f) } } #[derive(Clone)] #[cfg_attr(feature = "serialize", derive(Serialize))] #[cfg_attr(feature = "enhanced_debug", derive(Debug))] pub enum SdpAttribute { BundleOnly, Candidate(SdpAttributeCandidate), DtlsMessage(SdpAttributeDtlsMessage), EndOfCandidates, Extmap(SdpAttributeExtmap), ExtmapAllowMixed, Fingerprint(SdpAttributeFingerprint), Fmtp(SdpAttributeFmtp), Group(SdpAttributeGroup), IceLite, IceMismatch, IceOptions(Vec), IcePacing(u64), IcePwd(String), IceUfrag(String), Identity(String), ImageAttr(SdpAttributeImageAttr), Inactive, Label(String), MaxMessageSize(u64), MaxPtime(u64), Mid(String), Msid(SdpAttributeMsid), MsidSemantic(SdpAttributeMsidSemantic), Ptime(u64), Rid(SdpAttributeRid), Recvonly, RemoteCandidate(SdpAttributeRemoteCandidate), Rtpmap(SdpAttributeRtpmap), Rtcp(SdpAttributeRtcp), Rtcpfb(SdpAttributeRtcpFb), RtcpMux, RtcpMuxOnly, // RFC8858 RtcpRsize, Sctpmap(SdpAttributeSctpmap), SctpPort(u64), Sendonly, Sendrecv, Setup(SdpAttributeSetup), Simulcast(SdpAttributeSimulcast), Ssrc(SdpAttributeSsrc), SsrcGroup(SdpSsrcGroupSemantic, Vec), } impl SdpAttribute { pub fn allowed_at_session_level(&self) -> bool { match *self { SdpAttribute::BundleOnly | SdpAttribute::Candidate(..) | SdpAttribute::Fmtp(..) | SdpAttribute::IceMismatch | SdpAttribute::ImageAttr(..) | SdpAttribute::Label(..) | SdpAttribute::MaxMessageSize(..) | SdpAttribute::MaxPtime(..) | SdpAttribute::Mid(..) | SdpAttribute::Msid(..) | SdpAttribute::Ptime(..) | SdpAttribute::Rid(..) | SdpAttribute::RemoteCandidate(..) | SdpAttribute::Rtpmap(..) | SdpAttribute::Rtcp(..) | SdpAttribute::Rtcpfb(..) | SdpAttribute::RtcpMux | SdpAttribute::RtcpMuxOnly | SdpAttribute::RtcpRsize | SdpAttribute::Sctpmap(..) | SdpAttribute::SctpPort(..) | SdpAttribute::Simulcast(..) | SdpAttribute::Ssrc(..) | SdpAttribute::SsrcGroup(..) => false, SdpAttribute::DtlsMessage { .. } | SdpAttribute::EndOfCandidates | SdpAttribute::Extmap(..) | SdpAttribute::ExtmapAllowMixed | SdpAttribute::Fingerprint(..) | SdpAttribute::Group(..) | SdpAttribute::IceLite | SdpAttribute::IceOptions(..) | SdpAttribute::IcePacing(..) | SdpAttribute::IcePwd(..) | SdpAttribute::IceUfrag(..) | SdpAttribute::Identity(..) | SdpAttribute::Inactive | SdpAttribute::MsidSemantic(..) | SdpAttribute::Recvonly | SdpAttribute::Sendonly | SdpAttribute::Sendrecv | SdpAttribute::Setup(..) => true, } } pub fn allowed_at_media_level(&self) -> bool { match *self { SdpAttribute::DtlsMessage { .. } | SdpAttribute::Group(..) | SdpAttribute::IceLite | SdpAttribute::IcePacing(..) | SdpAttribute::Identity(..) | SdpAttribute::MsidSemantic(..) => false, SdpAttribute::BundleOnly | SdpAttribute::Candidate(..) | SdpAttribute::EndOfCandidates | SdpAttribute::Extmap(..) | SdpAttribute::ExtmapAllowMixed | SdpAttribute::Fingerprint(..) | SdpAttribute::Fmtp(..) | SdpAttribute::IceMismatch | SdpAttribute::IceOptions(..) | SdpAttribute::IcePwd(..) | SdpAttribute::IceUfrag(..) | SdpAttribute::ImageAttr(..) | SdpAttribute::Inactive | SdpAttribute::Label(..) | SdpAttribute::MaxMessageSize(..) | SdpAttribute::MaxPtime(..) | SdpAttribute::Mid(..) | SdpAttribute::Msid(..) | SdpAttribute::Ptime(..) | SdpAttribute::Rid(..) | SdpAttribute::Recvonly | SdpAttribute::RemoteCandidate(..) | SdpAttribute::Rtpmap(..) | SdpAttribute::Rtcp(..) | SdpAttribute::Rtcpfb(..) | SdpAttribute::RtcpMux | SdpAttribute::RtcpMuxOnly | SdpAttribute::RtcpRsize | SdpAttribute::Sctpmap(..) | SdpAttribute::SctpPort(..) | SdpAttribute::Sendonly | SdpAttribute::Sendrecv | SdpAttribute::Setup(..) | SdpAttribute::Simulcast(..) | SdpAttribute::Ssrc(..) | SdpAttribute::SsrcGroup(..) => true, } } } impl FromStr for SdpAttribute { type Err = SdpParserInternalError; fn from_str(line: &str) -> Result { let tokens: Vec<_> = line.splitn(2, ':').collect(); let name = tokens[0].to_lowercase(); let val = match tokens.get(1) { Some(x) => x.trim(), None => "", }; if tokens.len() > 1 { match name.as_str() { "bundle-only" | "end-of-candidates" | "extmap-allow-mixed" | "ice-lite" | "ice-mismatch" | "inactive" | "recvonly" | "rtcp-mux" | "rtcp-mux-only" | "rtcp-rsize" | "sendonly" | "sendrecv" => { return Err(SdpParserInternalError::Generic(format!( "{} attribute is not allowed to have a value", name ))); } _ => (), } } match name.as_str() { "bundle-only" => Ok(SdpAttribute::BundleOnly), "dtls-message" => parse_dtls_message(val), "end-of-candidates" => Ok(SdpAttribute::EndOfCandidates), "ice-lite" => Ok(SdpAttribute::IceLite), "ice-mismatch" => Ok(SdpAttribute::IceMismatch), "extmap-allow-mixed" => Ok(SdpAttribute::ExtmapAllowMixed), "ice-pwd" => Ok(SdpAttribute::IcePwd(string_or_empty(val)?)), "ice-ufrag" => Ok(SdpAttribute::IceUfrag(string_or_empty(val)?)), "identity" => Ok(SdpAttribute::Identity(string_or_empty(val)?)), "imageattr" => parse_image_attr(val), "inactive" => Ok(SdpAttribute::Inactive), "label" => Ok(SdpAttribute::Label(string_or_empty(val)?)), "max-message-size" => Ok(SdpAttribute::MaxMessageSize(val.parse()?)), "maxptime" => Ok(SdpAttribute::MaxPtime(val.parse()?)), "mid" => Ok(SdpAttribute::Mid(string_or_empty(val)?)), "msid-semantic" => parse_msid_semantic(val), "ptime" => Ok(SdpAttribute::Ptime(val.parse()?)), "ice-pacing" => parse_ice_pacing(val), "rid" => parse_rid(val), "recvonly" => Ok(SdpAttribute::Recvonly), "rtcp-mux" => Ok(SdpAttribute::RtcpMux), "rtcp-mux-only" => Ok(SdpAttribute::RtcpMuxOnly), "rtcp-rsize" => Ok(SdpAttribute::RtcpRsize), "sendonly" => Ok(SdpAttribute::Sendonly), "sendrecv" => Ok(SdpAttribute::Sendrecv), "ssrc-group" => parse_ssrc_group(val), "sctp-port" => parse_sctp_port(val), "candidate" => parse_candidate(val), "extmap" => parse_extmap(val), "fingerprint" => parse_fingerprint(val), "fmtp" => parse_fmtp(val), "group" => parse_group(val), "ice-options" => parse_ice_options(val), "msid" => parse_msid(val), "remote-candidates" => parse_remote_candidates(val), "rtpmap" => parse_rtpmap(val), "rtcp" => parse_rtcp(val), "rtcp-fb" => parse_rtcp_fb(val), "sctpmap" => parse_sctpmap(val), "setup" => parse_setup(val), "simulcast" => parse_simulcast(val), "ssrc" => parse_ssrc(val), _ => Err(SdpParserInternalError::Unsupported(format!( "Unknown attribute type {}", name ))), } } } impl fmt::Display for SdpAttribute { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { let attr_type_name = SdpAttributeType::from(self).to_string(); let attr_to_string = |attr_str: String| attr_type_name + ":" + &attr_str; match *self { SdpAttribute::BundleOnly => SdpAttributeType::BundleOnly.to_string(), SdpAttribute::Candidate(ref a) => attr_to_string(a.to_string()), SdpAttribute::DtlsMessage(ref a) => attr_to_string(a.to_string()), SdpAttribute::EndOfCandidates => SdpAttributeType::EndOfCandidates.to_string(), SdpAttribute::Extmap(ref a) => attr_to_string(a.to_string()), SdpAttribute::ExtmapAllowMixed => SdpAttributeType::ExtmapAllowMixed.to_string(), SdpAttribute::Fingerprint(ref a) => attr_to_string(a.to_string()), SdpAttribute::Fmtp(ref a) => attr_to_string(a.to_string()), SdpAttribute::Group(ref a) => attr_to_string(a.to_string()), SdpAttribute::IceLite => SdpAttributeType::IceLite.to_string(), SdpAttribute::IceMismatch => SdpAttributeType::IceMismatch.to_string(), SdpAttribute::IceOptions(ref a) => attr_to_string(a.join(" ")), SdpAttribute::IcePacing(ref a) => attr_to_string(a.to_string()), SdpAttribute::IcePwd(ref a) => attr_to_string(a.to_string()), SdpAttribute::IceUfrag(ref a) => attr_to_string(a.to_string()), SdpAttribute::Identity(ref a) => attr_to_string(a.to_string()), SdpAttribute::ImageAttr(ref a) => attr_to_string(a.to_string()), SdpAttribute::Inactive => SdpAttributeType::Inactive.to_string(), SdpAttribute::Label(ref a) => attr_to_string(a.to_string()), SdpAttribute::MaxMessageSize(ref a) => attr_to_string(a.to_string()), SdpAttribute::MaxPtime(ref a) => attr_to_string(a.to_string()), SdpAttribute::Mid(ref a) => attr_to_string(a.to_string()), SdpAttribute::Msid(ref a) => attr_to_string(a.to_string()), SdpAttribute::MsidSemantic(ref a) => attr_to_string(a.to_string()), SdpAttribute::Ptime(ref a) => attr_to_string(a.to_string()), SdpAttribute::Rid(ref a) => attr_to_string(a.to_string()), SdpAttribute::Recvonly => SdpAttributeType::Recvonly.to_string(), SdpAttribute::RemoteCandidate(ref a) => attr_to_string(a.to_string()), SdpAttribute::Rtpmap(ref a) => attr_to_string(a.to_string()), SdpAttribute::Rtcp(ref a) => attr_to_string(a.to_string()), SdpAttribute::Rtcpfb(ref a) => attr_to_string(a.to_string()), SdpAttribute::RtcpMux => SdpAttributeType::RtcpMux.to_string(), SdpAttribute::RtcpMuxOnly => SdpAttributeType::RtcpMuxOnly.to_string(), SdpAttribute::RtcpRsize => SdpAttributeType::RtcpRsize.to_string(), SdpAttribute::Sctpmap(ref a) => attr_to_string(a.to_string()), SdpAttribute::SctpPort(ref a) => attr_to_string(a.to_string()), SdpAttribute::Sendonly => SdpAttributeType::Sendonly.to_string(), SdpAttribute::Sendrecv => SdpAttributeType::Sendrecv.to_string(), SdpAttribute::Setup(ref a) => attr_to_string(a.to_string()), SdpAttribute::Simulcast(ref a) => attr_to_string(a.to_string()), SdpAttribute::Ssrc(ref a) => attr_to_string(a.to_string()), SdpAttribute::SsrcGroup(ref a, ref ssrcs) => { let stringified_ssrcs: Vec = ssrcs.iter().map(|ssrc| ssrc.to_string()).collect(); attr_to_string(a.to_string()) + " " + &stringified_ssrcs.join(" ") } } .fmt(f) } } impl AnonymizingClone for SdpAttribute { fn masked_clone(&self, anon: &mut StatefulSdpAnonymizer) -> Self { match self { SdpAttribute::Candidate(i) => SdpAttribute::Candidate(i.masked_clone(anon)), SdpAttribute::Fingerprint(i) => SdpAttribute::Fingerprint(i.masked_clone(anon)), SdpAttribute::IcePwd(i) => SdpAttribute::IcePwd(anon.mask_ice_password(i)), SdpAttribute::IceUfrag(i) => SdpAttribute::IceUfrag(anon.mask_ice_user(i)), SdpAttribute::RemoteCandidate(i) => SdpAttribute::RemoteCandidate(i.masked_clone(anon)), SdpAttribute::Ssrc(i) => SdpAttribute::Ssrc(i.masked_clone(anon)), _ => self.clone(), } } } #[derive(Clone, PartialEq)] pub enum SdpAttributeType { BundleOnly, Candidate, DtlsMessage, EndOfCandidates, Extmap, ExtmapAllowMixed, Fingerprint, Fmtp, Group, IceLite, IceMismatch, IceOptions, IcePacing, IcePwd, IceUfrag, Identity, ImageAttr, Inactive, Label, MaxMessageSize, MaxPtime, Mid, Msid, MsidSemantic, Ptime, Rid, Recvonly, RemoteCandidate, Rtpmap, Rtcp, Rtcpfb, RtcpMux, RtcpMuxOnly, RtcpRsize, Sctpmap, SctpPort, Sendonly, Sendrecv, Setup, Simulcast, Ssrc, SsrcGroup, } impl<'a> From<&'a SdpAttribute> for SdpAttributeType { fn from(other: &SdpAttribute) -> Self { match *other { SdpAttribute::BundleOnly { .. } => SdpAttributeType::BundleOnly, SdpAttribute::Candidate { .. } => SdpAttributeType::Candidate, SdpAttribute::DtlsMessage { .. } => SdpAttributeType::DtlsMessage, SdpAttribute::EndOfCandidates { .. } => SdpAttributeType::EndOfCandidates, SdpAttribute::Extmap { .. } => SdpAttributeType::Extmap, SdpAttribute::ExtmapAllowMixed { .. } => SdpAttributeType::ExtmapAllowMixed, SdpAttribute::Fingerprint { .. } => SdpAttributeType::Fingerprint, SdpAttribute::Fmtp { .. } => SdpAttributeType::Fmtp, SdpAttribute::Group { .. } => SdpAttributeType::Group, SdpAttribute::IceLite { .. } => SdpAttributeType::IceLite, SdpAttribute::IceMismatch { .. } => SdpAttributeType::IceMismatch, SdpAttribute::IceOptions { .. } => SdpAttributeType::IceOptions, SdpAttribute::IcePacing { .. } => SdpAttributeType::IcePacing, SdpAttribute::IcePwd { .. } => SdpAttributeType::IcePwd, SdpAttribute::IceUfrag { .. } => SdpAttributeType::IceUfrag, SdpAttribute::Identity { .. } => SdpAttributeType::Identity, SdpAttribute::ImageAttr { .. } => SdpAttributeType::ImageAttr, SdpAttribute::Inactive { .. } => SdpAttributeType::Inactive, SdpAttribute::Label { .. } => SdpAttributeType::Label, SdpAttribute::MaxMessageSize { .. } => SdpAttributeType::MaxMessageSize, SdpAttribute::MaxPtime { .. } => SdpAttributeType::MaxPtime, SdpAttribute::Mid { .. } => SdpAttributeType::Mid, SdpAttribute::Msid { .. } => SdpAttributeType::Msid, SdpAttribute::MsidSemantic { .. } => SdpAttributeType::MsidSemantic, SdpAttribute::Ptime { .. } => SdpAttributeType::Ptime, SdpAttribute::Rid { .. } => SdpAttributeType::Rid, SdpAttribute::Recvonly { .. } => SdpAttributeType::Recvonly, SdpAttribute::RemoteCandidate { .. } => SdpAttributeType::RemoteCandidate, SdpAttribute::Rtcp { .. } => SdpAttributeType::Rtcp, SdpAttribute::Rtcpfb { .. } => SdpAttributeType::Rtcpfb, SdpAttribute::RtcpMux { .. } => SdpAttributeType::RtcpMux, SdpAttribute::RtcpMuxOnly { .. } => SdpAttributeType::RtcpMuxOnly, SdpAttribute::RtcpRsize { .. } => SdpAttributeType::RtcpRsize, SdpAttribute::Rtpmap { .. } => SdpAttributeType::Rtpmap, SdpAttribute::Sctpmap { .. } => SdpAttributeType::Sctpmap, SdpAttribute::SctpPort { .. } => SdpAttributeType::SctpPort, SdpAttribute::Sendonly { .. } => SdpAttributeType::Sendonly, SdpAttribute::Sendrecv { .. } => SdpAttributeType::Sendrecv, SdpAttribute::Setup { .. } => SdpAttributeType::Setup, SdpAttribute::Simulcast { .. } => SdpAttributeType::Simulcast, SdpAttribute::Ssrc { .. } => SdpAttributeType::Ssrc, SdpAttribute::SsrcGroup { .. } => SdpAttributeType::SsrcGroup, } } } impl fmt::Display for SdpAttributeType { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match *self { SdpAttributeType::BundleOnly => "bundle-only", SdpAttributeType::Candidate => "candidate", SdpAttributeType::DtlsMessage => "dtls-message", SdpAttributeType::EndOfCandidates => "end-of-candidates", SdpAttributeType::Extmap => "extmap", SdpAttributeType::ExtmapAllowMixed => "extmap-allow-mixed", SdpAttributeType::Fingerprint => "fingerprint", SdpAttributeType::Fmtp => "fmtp", SdpAttributeType::Group => "group", SdpAttributeType::IceLite => "ice-lite", SdpAttributeType::IceMismatch => "ice-mismatch", SdpAttributeType::IceOptions => "ice-options", SdpAttributeType::IcePacing => "ice-pacing", SdpAttributeType::IcePwd => "ice-pwd", SdpAttributeType::IceUfrag => "ice-ufrag", SdpAttributeType::Identity => "identity", SdpAttributeType::ImageAttr => "imageattr", SdpAttributeType::Inactive => "inactive", SdpAttributeType::Label => "label", SdpAttributeType::MaxMessageSize => "max-message-size", SdpAttributeType::MaxPtime => "maxptime", SdpAttributeType::Mid => "mid", SdpAttributeType::Msid => "msid", SdpAttributeType::MsidSemantic => "msid-semantic", SdpAttributeType::Ptime => "ptime", SdpAttributeType::Rid => "rid", SdpAttributeType::Recvonly => "recvonly", SdpAttributeType::RemoteCandidate => "remote-candidates", SdpAttributeType::Rtpmap => "rtpmap", SdpAttributeType::Rtcp => "rtcp", SdpAttributeType::Rtcpfb => "rtcp-fb", SdpAttributeType::RtcpMux => "rtcp-mux", SdpAttributeType::RtcpMuxOnly => "rtcp-mux-only", SdpAttributeType::RtcpRsize => "rtcp-rsize", SdpAttributeType::Sctpmap => "sctpmap", SdpAttributeType::SctpPort => "sctp-port", SdpAttributeType::Sendonly => "sendonly", SdpAttributeType::Sendrecv => "sendrecv", SdpAttributeType::Setup => "setup", SdpAttributeType::Simulcast => "simulcast", SdpAttributeType::Ssrc => "ssrc", SdpAttributeType::SsrcGroup => "ssrc-group", } .fmt(f) } } fn string_or_empty(to_parse: &str) -> Result { if to_parse.is_empty() { Err(SdpParserInternalError::Generic( "This attribute is required to have a value".to_string(), )) } else { Ok(to_parse.to_string()) } } fn parse_payload_type(to_parse: &str) -> Result { Ok(match to_parse { "*" => SdpAttributePayloadType::Wildcard, _ => SdpAttributePayloadType::PayloadType(to_parse.parse::()?), }) } fn parse_single_direction(to_parse: &str) -> Result { match to_parse { "send" => Ok(SdpSingleDirection::Send), "recv" => Ok(SdpSingleDirection::Recv), x => Err(SdpParserInternalError::Generic(format!( "Unknown direction description found: '{:}'", x ))), } } /////////////////////////////////////////////////////////////////////////// // a=ssrc-group, RFC5576 //------------------------------------------------------------------------- // a=ssrc-group: ... fn parse_ssrc_group(to_parse: &str) -> Result { let mut tokens = to_parse.split_whitespace(); let semantics = match tokens.next() { None => { return Err(SdpParserInternalError::Generic( "Ssrc group attribute is missing semantics".to_string(), )); } Some(x) => match x.to_uppercase().as_ref() { "DUP" => SdpSsrcGroupSemantic::Duplication, "FID" => SdpSsrcGroupSemantic::FlowIdentification, "FEC" => SdpSsrcGroupSemantic::ForwardErrorCorrection, "FEC-FR" => SdpSsrcGroupSemantic::ForwardErrorCorrectionFr, "SIM" => SdpSsrcGroupSemantic::Sim, unknown => { return Err(SdpParserInternalError::Unsupported(format!( "Unknown ssrc semantic '{:?}' found", unknown ))); } }, }; let mut ssrcs = Vec::new(); for token in tokens { match parse_ssrc(token) { Ok(SdpAttribute::Ssrc(ssrc)) => { ssrcs.push(ssrc); } Err(err) => { return Err(err); } _ => unreachable!(), } } if ssrcs.is_empty() { return Err(SdpParserInternalError::Generic( "Ssrc group must contain at least one ssrc".to_string(), )); } Ok(SdpAttribute::SsrcGroup(semantics, ssrcs)) } /////////////////////////////////////////////////////////////////////////// // a=sctp-port, draft-ietf-mmusic-sctp-sdp-26#section-15.2.1 //------------------------------------------------------------------------- // no ABNF given fn parse_sctp_port(to_parse: &str) -> Result { let port = to_parse.parse()?; if port > 65535 { return Err(SdpParserInternalError::Generic(format!( "Sctpport port {} can only be a bit 16bit number", port ))); } Ok(SdpAttribute::SctpPort(port)) } /////////////////////////////////////////////////////////////////////////// // a=candidate, RFC5245 //------------------------------------------------------------------------- // // candidate-attribute = "candidate" ":" foundation SP component-id SP // transport SP // priority SP // connection-address SP ;from RFC 4566 // port ;port from RFC 4566 // SP cand-type // [SP rel-addr] // [SP rel-port] // *(SP extension-att-name SP // extension-att-value) // foundation = 1*32ice-char // component-id = 1*5DIGIT // transport = "UDP" / transport-extension // transport-extension = token ; from RFC 3261 // priority = 1*10DIGIT // cand-type = "typ" SP candidate-types // candidate-types = "host" / "srflx" / "prflx" / "relay" / token // rel-addr = "raddr" SP connection-address // rel-port = "rport" SP port // extension-att-name = byte-string ;from RFC 4566 // extension-att-value = byte-string // ice-char = ALPHA / DIGIT / "+" / "/" fn parse_candidate(to_parse: &str) -> Result { let tokens: Vec<&str> = to_parse.split_whitespace().collect(); if tokens.len() < 8 { return Err(SdpParserInternalError::Generic( "Candidate needs to have minimum eigth tokens".to_string(), )); } let component = tokens[1].parse::()?; let transport = match tokens[2].to_lowercase().as_ref() { "udp" => SdpAttributeCandidateTransport::Udp, "tcp" => SdpAttributeCandidateTransport::Tcp, _ => { return Err(SdpParserInternalError::Generic( "Unknonw candidate transport value".to_string(), )); } }; let priority = tokens[3].parse::()?; let address = Address::from_str(tokens[4])?; let port = tokens[5].parse::()?; if port > 65535 { return Err(SdpParserInternalError::Generic( "ICE candidate port can only be a bit 16bit number".to_string(), )); } match tokens[6].to_lowercase().as_ref() { "typ" => (), _ => { return Err(SdpParserInternalError::Generic( "Candidate attribute token must be 'typ'".to_string(), )); } }; let cand_type = match tokens[7].to_lowercase().as_ref() { "host" => SdpAttributeCandidateType::Host, "srflx" => SdpAttributeCandidateType::Srflx, "prflx" => SdpAttributeCandidateType::Prflx, "relay" => SdpAttributeCandidateType::Relay, _ => { return Err(SdpParserInternalError::Generic( "Unknow candidate type value".to_string(), )); } }; let mut cand = SdpAttributeCandidate::new( tokens[0].to_string(), component, transport, priority, address, port, cand_type, ); if tokens.len() > 8 { let mut index = 8; while tokens.len() > index + 1 { match tokens[index].to_lowercase().as_ref() { "generation" => { let generation = tokens[index + 1].parse::()?; cand.set_generation(generation); index += 2; } "network-cost" => { let cost = tokens[index + 1].parse::()?; cand.set_network_cost(cost); index += 2; } "raddr" => { let addr = parse_unicast_address(tokens[index + 1])?; cand.set_remote_address(addr); index += 2; } "rport" => { let port = tokens[index + 1].parse::()?; if port > 65535 { return Err(SdpParserInternalError::Generic( "ICE candidate rport can only be a bit 16bit number".to_string(), )); } cand.set_remote_port(port); index += 2; } "tcptype" => { cand.set_tcp_type(match tokens[index + 1].to_lowercase().as_ref() { "active" => SdpAttributeCandidateTcpType::Active, "passive" => SdpAttributeCandidateTcpType::Passive, "so" => SdpAttributeCandidateTcpType::Simultaneous, _ => { return Err(SdpParserInternalError::Generic( "Unknown tcptype value in candidate line".to_string(), )); } }); index += 2; } "ufrag" => { let ufrag = tokens[index + 1]; cand.set_ufrag(ufrag.to_string()); index += 2; } _ => { let name = tokens[index].to_string(); let value = tokens[index + 1].to_string(); cand.add_unknown_extension(name, value); index += 2; } }; } if tokens.len() > index { return Err(SdpParserInternalError::Unsupported( "Ice candidate extension name without value".to_string(), )); } } Ok(SdpAttribute::Candidate(cand)) } /////////////////////////////////////////////////////////////////////////// // a=dtls-message, draft-rescorla-dtls-in-sdp //------------------------------------------------------------------------- // attribute =/ dtls-message-attribute // // dtls-message-attribute = "dtls-message" ":" role SP value // // role = "client" / "server" // // value = 1*(ALPHA / DIGIT / "+" / "/" / "=" ) // ; base64 encoded message fn parse_dtls_message(to_parse: &str) -> Result { let tokens: Vec<&str> = to_parse.split(' ').collect(); if tokens.len() != 2 { return Err(SdpParserInternalError::Generic( "dtls-message must have a role token and a value token.".to_string(), )); } Ok(SdpAttribute::DtlsMessage(match tokens[0] { "client" => SdpAttributeDtlsMessage::Client(tokens[1].to_string()), "server" => SdpAttributeDtlsMessage::Server(tokens[1].to_string()), e => { return Err(SdpParserInternalError::Generic(format!( "dtls-message has unknown role token '{}'", e ))); } })) } // Returns true if valid byte-string as defined by RFC 4566 // https://tools.ietf.org/html/rfc4566 fn valid_byte_string(input: &str) -> bool { !(input.contains(0x00 as char) || input.contains(0x0A as char) || input.contains(0x0D as char)) } /////////////////////////////////////////////////////////////////////////// // a=extmap, RFC5285 //------------------------------------------------------------------------- // RFC5285 // extmap = mapentry SP extensionname [SP extensionattributes] // // extensionname = URI // // direction = "sendonly" / "recvonly" / "sendrecv" / "inactive" // // mapentry = "extmap:" 1*5DIGIT ["/" direction] // // extensionattributes = byte-string // // URI = // // byte-string = // // SP = // // DIGIT = fn parse_extmap(to_parse: &str) -> Result { let tokens: Vec<&str> = to_parse.split_whitespace().collect(); if tokens.len() < 2 { return Err(SdpParserInternalError::Generic( "Extmap needs to have at least two tokens".to_string(), )); } let id: u16; let mut direction: Option = None; if tokens[0].find('/') == None { id = tokens[0].parse::()?; } else { let id_dir: Vec<&str> = tokens[0].splitn(2, '/').collect(); id = id_dir[0].parse::()?; direction = Some(match id_dir[1].to_lowercase().as_ref() { "recvonly" => SdpAttributeDirection::Recvonly, "sendonly" => SdpAttributeDirection::Sendonly, "sendrecv" => SdpAttributeDirection::Sendrecv, _ => { return Err(SdpParserInternalError::Generic( "Unsupported direction in extmap value".to_string(), )); } }) } // Consider replacing to_parse.split_whitespace() above with splitn on space. Would we want the pattern to split on any amout of any kind of whitespace? let extension_attributes = if tokens.len() == 2 { None } else { let ext_string: String = tokens[2..].join(" "); if !valid_byte_string(&ext_string) { return Err(SdpParserInternalError::Generic( "Illegal character in extmap extension attributes".to_string(), )); } Some(ext_string) }; Ok(SdpAttribute::Extmap(SdpAttributeExtmap { id, direction, url: tokens[1].to_string(), extension_attributes, })) } /////////////////////////////////////////////////////////////////////////// // a=fingerprint, RFC4572 //------------------------------------------------------------------------- // fingerprint-attribute = "fingerprint" ":" hash-func SP fingerprint // // hash-func = "sha-1" / "sha-224" / "sha-256" / // "sha-384" / "sha-512" / // "md5" / "md2" / token // ; Additional hash functions can only come // ; from updates to RFC 3279 // // fingerprint = 2UHEX *(":" 2UHEX) // ; Each byte in upper-case hex, separated // ; by colons. // // UHEX = DIGIT / %x41-46 ; A-F uppercase fn parse_fingerprint(to_parse: &str) -> Result { let tokens: Vec<&str> = to_parse.split_whitespace().collect(); if tokens.len() != 2 { return Err(SdpParserInternalError::Generic( "Fingerprint needs to have two tokens".to_string(), )); } let hash_algorithm = SdpAttributeFingerprintHashType::try_from_name(tokens[0])?; let bytes = hash_algorithm.parse_octets(tokens[1])?; let fingerprint = SdpAttributeFingerprint::try_from((hash_algorithm, bytes))?; Ok(SdpAttribute::Fingerprint(fingerprint)) } /////////////////////////////////////////////////////////////////////////// // a=fmtp, RFC4566, RFC5576 //------------------------------------------------------------------------- // a=fmtp: fn parse_fmtp(to_parse: &str) -> Result { let tokens: Vec<&str> = to_parse.splitn(2, ' ').collect(); // Support space seperated parameter blocks if tokens.len() < 2 { return Err(SdpParserInternalError::Unsupported( "Fmtp attributes require a payload type and a parameter block.".to_string(), )); } let payload_token = tokens[0]; // Default initiliaze SdpAttributeFmtpParameters let mut parameters = SdpAttributeFmtpParameters { packetization_mode: 0, level_asymmetry_allowed: false, profile_level_id: 0x0042_0010, max_fs: 0, max_cpb: 0, max_dpb: 0, max_br: 0, max_mbps: 0, usedtx: false, stereo: false, useinbandfec: false, cbr: false, max_fr: 0, maxplaybackrate: 48000, maxaveragebitrate: 0, ptime: 0, minptime: 0, maxptime: 0, encodings: Vec::new(), dtmf_tones: "".to_string(), rtx: None, unknown_tokens: Vec::new(), }; for parameter_token in tokens[1..].iter() { if parameter_token.contains('=') { // Permit Leading/Trailing/Inner ';' by filtering out empty splits let parameter_tokens: Vec<&str> = parameter_token .split(';') .filter(|token| !token.is_empty()) .collect(); for parameter_token in parameter_tokens.iter() { let name_value_pair: Vec<&str> = parameter_token.splitn(2, '=').collect(); if name_value_pair.len() != 2 { return Err(SdpParserInternalError::Generic( "A fmtp parameter must be either a telephone event, a parameter list or a red codec list" .to_string(), )); } let parse_bool = |val: &str, param_name: &str| -> Result { match val.parse::()? { 0 => Ok(false), 1 => Ok(true), _ => Err(SdpParserInternalError::Generic(format!( "The fmtp parameter '{:}' must be 0 or 1", param_name ))), } }; let parameter_name = name_value_pair[0]; let parameter_val = name_value_pair[1]; match parameter_name.to_uppercase().as_str() { // H264 "PROFILE-LEVEL-ID" => parameters.profile_level_id = match u32::from_str_radix( parameter_val, 16, )? { x @ 0..=0x00ff_ffff => x, _ => return Err(SdpParserInternalError::Generic( "The fmtp parameter 'profile-level-id' must be in range [0,0xffffff]" .to_string(), )), }, "PACKETIZATION-MODE" => { parameters.packetization_mode = match parameter_val.parse::()? { x @ 0..=2 => x, _ => { return Err(SdpParserInternalError::Generic( "The fmtp parameter 'packetization-mode' must be 0,1 or 2" .to_string(), )); } } } "LEVEL-ASYMMETRY-ALLOWED" => { parameters.level_asymmetry_allowed = parse_bool(parameter_val, "level-asymmetry-allowed")? } "MAX-MBPS" => parameters.max_mbps = parameter_val.parse::()?, "MAX-FS" => parameters.max_fs = parameter_val.parse::()?, "MAX-CPB" => parameters.max_cpb = parameter_val.parse::()?, "MAX-DPB" => parameters.max_dpb = parameter_val.parse::()?, "MAX-BR" => parameters.max_br = parameter_val.parse::()?, // VP8 and VP9 "MAX-FR" => parameters.max_fr = parameter_val.parse::()?, //Opus https://tools.ietf.org/html/rfc7587 "MAXPLAYBACKRATE" => { parameters.maxplaybackrate = parameter_val.parse::()? } "MAXAVERAGEBITRATE" => { parameters.maxaveragebitrate = parameter_val.parse::()? } "PTIME" => parameters.ptime = parameter_val.parse::()?, "MAXPTIME" => parameters.maxptime = parameter_val.parse::()?, "MINPTIME" => parameters.minptime = parameter_val.parse::()?, "USEDTX" => parameters.usedtx = parse_bool(parameter_val, "usedtx")?, "STEREO" => parameters.stereo = parse_bool(parameter_val, "stereo")?, "USEINBANDFEC" => { parameters.useinbandfec = parse_bool(parameter_val, "useinbandfec")? } "CBR" => parameters.cbr = parse_bool(parameter_val, "cbr")?, "APT" => { parameters.rtx = Some(RtxFmtpParameters { apt: parameter_val.parse::()?, rtx_time: None, }) } "RTX-TIME" => { if let Some(ref mut rtx) = parameters.rtx { rtx.rtx_time = Some(parameter_val.parse::()?) } else { return Err(SdpParserInternalError::Generic( "RTX codec must have an APT field".to_string(), )); } } _ => parameters .unknown_tokens .push((*parameter_token).to_string()), } } } else if parameter_token.contains('/') { let encodings: Vec<&str> = parameter_token.split('/').collect(); for encoding in encodings { match encoding.parse::()? { x @ 0..=128 => parameters.encodings.push(x), _ => { return Err(SdpParserInternalError::Generic( "Red codec must be in range [0,128]".to_string(), )); } } } } else { // This is the case for the 'telephone-event' codec let dtmf_tones: Vec<&str> = parameter_token.split(',').collect(); let mut dtmf_tone_is_ok = true; // This closure verifies the output of some_number_as_string.parse::().ok() like calls let validate_digits = |digit_option: Option| -> Option { match digit_option { Some(x) => match x { 0..=100 => Some(x), _ => None, }, None => None, } }; // This loop does some sanity checking on the passed dtmf tones for dtmf_tone in dtmf_tones { let dtmf_tone_range: Vec<&str> = dtmf_tone.splitn(2, '-').collect(); dtmf_tone_is_ok = match dtmf_tone_range.len() { // In this case the dtmf tone is a range 2 => { match validate_digits(dtmf_tone_range[0].parse::().ok()) { Some(l) => match validate_digits(dtmf_tone_range[1].parse::().ok()) { Some(u) => { // Check that the first part of the range is smaller than the second part l < u } None => false, }, None => false, } } // In this case the dtmf tone is a single tone 1 => validate_digits(dtmf_tone.parse::().ok()).is_some(), _ => false, }; if !dtmf_tone_is_ok { break; } } // Set the parsed dtmf tones or in case the parsing was insuccessfull, set it to the default "0-15" parameters.dtmf_tones = if dtmf_tone_is_ok { (*parameter_token).to_string() } else { "0-15".to_string() }; } } Ok(SdpAttribute::Fmtp(SdpAttributeFmtp { payload_type: payload_token.parse::()?, parameters, })) } /////////////////////////////////////////////////////////////////////////// // a=group, RFC5888 //------------------------------------------------------------------------- // group-attribute = "a=group:" semantics // *(SP identification-tag) // semantics = "LS" / "FID" / semantics-extension // semantics-extension = token // identification-tag = token fn parse_group(to_parse: &str) -> Result { let mut tokens = to_parse.split_whitespace(); let semantics = match tokens.next() { None => { return Err(SdpParserInternalError::Generic( "Group attribute is missing semantics token".to_string(), )); } Some(x) => match x.to_uppercase().as_ref() { "LS" => SdpAttributeGroupSemantic::LipSynchronization, "FID" => SdpAttributeGroupSemantic::FlowIdentification, "SRF" => SdpAttributeGroupSemantic::SingleReservationFlow, "ANAT" => SdpAttributeGroupSemantic::AlternateNetworkAddressType, "FEC" => SdpAttributeGroupSemantic::ForwardErrorCorrection, "DDP" => SdpAttributeGroupSemantic::DecodingDependency, "BUNDLE" => SdpAttributeGroupSemantic::Bundle, unknown => { return Err(SdpParserInternalError::Unsupported(format!( "Unknown group semantic '{:?}' found", unknown ))); } }, }; Ok(SdpAttribute::Group(SdpAttributeGroup { semantics, tags: tokens.map(ToString::to_string).collect(), })) } /////////////////////////////////////////////////////////////////////////// // a=ice-options, draft-ietf-mmusic-ice-sip-sdp //------------------------------------------------------------------------- // ice-options = "ice-options:" ice-option-tag // 0*(SP ice-option-tag) // ice-option-tag = 1*ice-char fn parse_ice_options(to_parse: &str) -> Result { if to_parse.is_empty() { return Err(SdpParserInternalError::Generic( "ice-options is required to have a value".to_string(), )); } Ok(SdpAttribute::IceOptions( to_parse .split_whitespace() .map(ToString::to_string) .collect(), )) } /////////////////////////////////////////////////////////////////////////// // a=ice-pacing, draft-ietf-mmusic-ice-sip-sdp //------------------------------------------------------------------------- // ice-pacing-att = "ice-pacing:" pacing-value // pacing-value = 1*10DIGIT fn parse_ice_pacing(to_parse: &str) -> Result { let parsed = to_parse.parse::()?; if parsed >= 1_00_00_00_00_00 { return Err(SdpParserInternalError::Generic( "ice-pacing value is not a 10 digit integer".to_string(), )); } Ok(SdpAttribute::IcePacing(parsed)) } fn parse_imageattr_tokens(to_parse: &str, separator: char) -> Vec { let mut tokens = Vec::new(); let mut open_braces_counter = 0; let mut current_tokens = Vec::new(); for token in to_parse.split(separator) { if token.contains('[') { open_braces_counter += 1; } if token.contains(']') { open_braces_counter -= 1; } current_tokens.push(token.to_string()); if open_braces_counter == 0 { tokens.push(current_tokens.join(&separator.to_string())); current_tokens = Vec::new(); } } tokens } fn parse_imagettr_braced_token(to_parse: &str) -> Option<&str> { if !to_parse.starts_with('[') { return None; } if !to_parse.ends_with(']') { return None; } Some(&to_parse[1..to_parse.len() - 1]) } fn parse_image_attr_xyrange( to_parse: &str, ) -> Result { if to_parse.starts_with('[') { let value_tokens = parse_imagettr_braced_token(to_parse).ok_or_else(|| { SdpParserInternalError::Generic( "imageattr's xyrange has no closing tag ']'".to_string(), ) })?; if to_parse.contains(':') { // Range values let range_tokens: Vec<&str> = value_tokens.split(':').collect(); if range_tokens.len() == 3 { Ok(SdpAttributeImageAttrXyRange::Range( range_tokens[0].parse::()?, range_tokens[2].parse::()?, Some(range_tokens[1].parse::()?), )) } else if range_tokens.len() == 2 { Ok(SdpAttributeImageAttrXyRange::Range( range_tokens[0].parse::()?, range_tokens[1].parse::()?, None, )) } else { Err(SdpParserInternalError::Generic( "imageattr's xyrange must contain 2 or 3 fields".to_string(), )) } } else { // Discrete values let values = value_tokens .split(',') .map(str::parse::) .collect::, _>>()?; if values.len() < 2 { return Err(SdpParserInternalError::Generic( "imageattr's discrete value list must have at least two elements".to_string(), )); } Ok(SdpAttributeImageAttrXyRange::DiscreteValues(values)) } } else { Ok(SdpAttributeImageAttrXyRange::DiscreteValues(vec![ to_parse.parse::()? ])) } } fn parse_image_attr_set( to_parse: &str, ) -> Result { let mut tokens = parse_imageattr_tokens(to_parse, ',').into_iter(); let x_token = tokens.next().ok_or_else(|| { SdpParserInternalError::Generic("imageattr set is missing the 'x=' token".to_string()) })?; if !x_token.starts_with("x=") { return Err(SdpParserInternalError::Generic( "The first token in an imageattr set must begin with 'x='".to_string(), )); } let x = parse_image_attr_xyrange(&x_token[2..])?; let y_token = tokens.next().ok_or_else(|| { SdpParserInternalError::Generic("imageattr set is missing the 'y=' token".to_string()) })?; if !y_token.starts_with("y=") { return Err(SdpParserInternalError::Generic( "The second token in an imageattr set must begin with 'y='".to_string(), )); } let y = parse_image_attr_xyrange(&y_token[2..])?; let mut sar = None; let mut par = None; let mut q = None; let parse_ps_range = |resolution_range: &str| -> Result<(f32, f32), SdpParserInternalError> { let minmax_pair: Vec<&str> = resolution_range.split('-').collect(); if minmax_pair.len() != 2 { return Err(SdpParserInternalError::Generic( "imageattr's par and sar ranges must have two components".to_string(), )); } let min = minmax_pair[0].parse::()?; let max = minmax_pair[1].parse::()?; if min >= max { return Err(SdpParserInternalError::Generic( "In imageattr's par and sar ranges, first must be < than the second".to_string(), )); } Ok((min, max)) }; for current_token in tokens { if let Some(value_token) = current_token.strip_prefix("sar=") { if value_token.starts_with('[') { let sar_values = parse_imagettr_braced_token(value_token).ok_or_else(|| { SdpParserInternalError::Generic( "imageattr's sar value is missing closing tag ']'".to_string(), ) })?; if value_token.contains('-') { // Range let range = parse_ps_range(sar_values)?; sar = Some(SdpAttributeImageAttrSRange::Range(range.0, range.1)) } else if value_token.contains(',') { // Discrete values let values = sar_values .split(',') .map(str::parse::) .collect::, _>>()?; if values.len() < 2 { return Err(SdpParserInternalError::Generic( "imageattr's sar discrete value list must have at least two values" .to_string(), )); } // Check that all the values are ascending let mut last_value = 0.0; for value in &values { if last_value >= *value { return Err(SdpParserInternalError::Generic( "imageattr's sar discrete value list must contain ascending values" .to_string(), )); } last_value = *value; } sar = Some(SdpAttributeImageAttrSRange::DiscreteValues(values)) } } else { sar = Some(SdpAttributeImageAttrSRange::DiscreteValues(vec![ value_token.parse::()?, ])) } } else if let Some(braced_value_token) = current_token.strip_prefix("par=") { if !braced_value_token.starts_with('[') { return Err(SdpParserInternalError::Generic( "imageattr's par value must start with '['".to_string(), )); } let par_values = parse_imagettr_braced_token(braced_value_token).ok_or_else(|| { SdpParserInternalError::Generic( "imageattr's par value must be enclosed with ']'".to_string(), ) })?; let range = parse_ps_range(par_values)?; par = Some(SdpAttributeImageAttrPRange { min: range.0, max: range.1, }) } else if let Some(qval) = current_token.strip_prefix("q=") { q = Some(qval.parse::()?); } } Ok(SdpAttributeImageAttrSet { x, y, sar, par, q }) } fn parse_image_attr_set_list( tokens: &mut iter::Peekable, ) -> Result where I: Iterator + Clone, { let parse_set = |set_token: &str| -> Result { parse_image_attr_set(parse_imagettr_braced_token(set_token).ok_or_else(|| { SdpParserInternalError::Generic("imageattr sets must be enclosed by ']'".to_string()) })?) }; match tokens .next() .ok_or_else(|| { SdpParserInternalError::Generic( "imageattr must have a parameter set after a direction token".to_string(), ) })? .as_str() { "*" => Ok(SdpAttributeImageAttrSetList::Wildcard), x => { let mut sets = vec![parse_set(x)?]; while let Some(set_str) = tokens.clone().peek() { if set_str.starts_with('[') { sets.push(parse_set(&tokens.next().unwrap())?); } else { break; } } Ok(SdpAttributeImageAttrSetList::Sets(sets)) } } } /////////////////////////////////////////////////////////////////////////// // a=imageattr, RFC6236 //------------------------------------------------------------------------- // image-attr = "imageattr:" PT 1*2( 1*WSP ( "send" / "recv" ) // 1*WSP attr-list ) // PT = 1*DIGIT / "*" // attr-list = ( set *(1*WSP set) ) / "*" // ; WSP and DIGIT defined in [RFC5234] // // set= "[" "x=" xyrange "," "y=" xyrange *( "," key-value ) "]" // ; x is the horizontal image size range (pixel count) // ; y is the vertical image size range (pixel count) // // key-value = ( "sar=" srange ) // / ( "par=" prange ) // / ( "q=" qvalue ) // ; Key-value MAY be extended with other keyword // ; parameters. // ; At most, one instance each of sar, par, or q // ; is allowed in a set. // ; // ; sar (sample aspect ratio) is the sample aspect ratio // ; associated with the set (optional, MAY be ignored) // ; par (picture aspect ratio) is the allowed // ; ratio between the display's x and y physical // ; size (optional) // ; q (optional, range [0.0..1.0], default value 0.5) // ; is the preference for the given set, // ; a higher value means a higher preference // // onetonine = "1" / "2" / "3" / "4" / "5" / "6" / "7" / "8" / "9" // ; Digit between 1 and 9 // xyvalue = onetonine *5DIGIT // ; Digit between 1 and 9 that is // ; followed by 0 to 5 other digits // step = xyvalue // xyrange = ( "[" xyvalue ":" [ step ":" ] xyvalue "]" ) // ; Range between a lower and an upper value // ; with an optional step, default step = 1 // ; The rightmost occurrence of xyvalue MUST have a // ; higher value than the leftmost occurrence. // / ( "[" xyvalue 1*( "," xyvalue ) "]" ) // ; Discrete values separated by ',' // / ( xyvalue ) // ; A single value // spvalue = ( "0" "." onetonine *3DIGIT ) // ; Values between 0.1000 and 0.9999 // / ( onetonine "." 1*4DIGIT ) // ; Values between 1.0000 and 9.9999 // srange = ( "[" spvalue 1*( "," spvalue ) "]" ) // ; Discrete values separated by ','. // ; Each occurrence of spvalue MUST be // ; greater than the previous occurrence. // / ( "[" spvalue "-" spvalue "]" ) // ; Range between a lower and an upper level (inclusive) // ; The second occurrence of spvalue MUST have a higher // ; value than the first // / ( spvalue ) // ; A single value // // prange = ( "[" spvalue "-" spvalue "]" ) // ; Range between a lower and an upper level (inclusive) // ; The second occurrence of spvalue MUST have a higher // ; value than the first // // qvalue = ( "0" "." 1*2DIGIT ) // / ( "1" "." 1*2("0") ) // ; Values between 0.00 and 1.00 fn parse_image_attr(to_parse: &str) -> Result { let mut tokens = parse_imageattr_tokens(to_parse, ' ').into_iter().peekable(); let pt = parse_payload_type( tokens .next() .ok_or_else(|| { SdpParserInternalError::Generic("imageattr requires a payload token".to_string()) })? .as_str(), )?; let first_direction = parse_single_direction( tokens .next() .ok_or_else(|| { SdpParserInternalError::Generic( "imageattr's second token must be a direction token".to_string(), ) })? .as_str(), )?; let first_set_list = parse_image_attr_set_list(&mut tokens)?; let mut second_set_list = SdpAttributeImageAttrSetList::Sets(Vec::new()); // Check if there is a second direction defined if let Some(direction_token) = tokens.next() { if parse_single_direction(direction_token.as_str())? == first_direction { return Err(SdpParserInternalError::Generic( "imageattr's second direction token must be different from the first one" .to_string(), )); } second_set_list = parse_image_attr_set_list(&mut tokens)?; } if tokens.next().is_some() { return Err(SdpParserInternalError::Generic( "imageattr must not contain any token after the second set list".to_string(), )); } Ok(SdpAttribute::ImageAttr(match first_direction { SdpSingleDirection::Send => SdpAttributeImageAttr { pt, send: first_set_list, recv: second_set_list, }, SdpSingleDirection::Recv => SdpAttributeImageAttr { pt, send: second_set_list, recv: first_set_list, }, })) } /////////////////////////////////////////////////////////////////////////// // a=msid, draft-ietf-mmusic-msid //------------------------------------------------------------------------- // msid-attr = "msid:" identifier [ SP appdata ] // identifier = 1*64token-char ; see RFC 4566 // appdata = 1*64token-char ; see RFC 4566 fn parse_msid(to_parse: &str) -> Result { let mut tokens = to_parse.split_whitespace(); let id = match tokens.next() { None => { return Err(SdpParserInternalError::Generic( "Msid attribute is missing msid-id token".to_string(), )); } Some(x) => x.to_string(), }; let appdata = tokens.next().map(|x| x.to_string()); Ok(SdpAttribute::Msid(SdpAttributeMsid { id, appdata })) } /////////////////////////////////////////////////////////////////////////// // a=msid-semantic, draft-ietf-mmusic-msid //------------------------------------------------------------------------- // msid-semantic-attr = "msid-semantic:" msid-semantic msid-list // msid-semantic = token ; see RFC 4566 // msid-list = *(" " msid-id) / " *" fn parse_msid_semantic(to_parse: &str) -> Result { let tokens: Vec<_> = to_parse.split_whitespace().collect(); if tokens.is_empty() { return Err(SdpParserInternalError::Generic( "Msid-semantic attribute is missing msid-semantic token".to_string(), )); } // TODO: Should msids be checked to ensure they are non empty? let semantic = SdpAttributeMsidSemantic { semantic: tokens[0].to_string(), msids: tokens[1..].iter().map(ToString::to_string).collect(), }; Ok(SdpAttribute::MsidSemantic(semantic)) } /////////////////////////////////////////////////////////////////////////// // a=rid, draft-ietf-mmusic-rid //------------------------------------------------------------------------- // rid-syntax = %s"a=rid:" rid-id SP rid-dir // [ rid-pt-param-list / rid-param-list ] // rid-id = 1*(alpha-numeric / "-" / "_") // alpha-numeric = < as defined in {{RFC4566}} > // rid-dir = %s"send" / %s"recv" // rid-pt-param-list = SP rid-fmt-list *(";" rid-param) // rid-param-list = SP rid-param *(";" rid-param) // rid-fmt-list = %s"pt=" fmt *( "," fmt ) // fmt = < as defined in {{RFC4566}} > // rid-param = rid-width-param // / rid-height-param // / rid-fps-param // / rid-fs-param // / rid-br-param // / rid-pps-param // / rid-bpp-param // / rid-depend-param // / rid-param-other // rid-width-param = %s"max-width" [ "=" int-param-val ] // rid-height-param = %s"max-height" [ "=" int-param-val ] // rid-fps-param = %s"max-fps" [ "=" int-param-val ] // rid-fs-param = %s"max-fs" [ "=" int-param-val ] // rid-br-param = %s"max-br" [ "=" int-param-val ] // rid-pps-param = %s"max-pps" [ "=" int-param-val ] // rid-bpp-param = %s"max-bpp" [ "=" float-param-val ] // rid-depend-param = %s"depend=" rid-list // rid-param-other = 1*(alpha-numeric / "-") [ "=" param-val ] // rid-list = rid-id *( "," rid-id ) // int-param-val = 1*DIGIT // float-param-val = 1*DIGIT "." 1*DIGIT // param-val = *( %x20-58 / %x60-7E ) // ; Any printable character except semicolon fn parse_rid(to_parse: &str) -> Result { let tokens: Vec<&str> = to_parse.splitn(3, ' ').collect(); if tokens.len() < 2 { return Err(SdpParserInternalError::Generic( "A rid attribute must at least have an id and a direction token.".to_string(), )); } // Default initilize let mut params = SdpAttributeRidParameters { max_width: 0, max_height: 0, max_fps: 0, max_fs: 0, max_br: 0, max_pps: 0, unknown: Vec::new(), }; let mut formats: Vec = Vec::new(); let mut depends: Vec = Vec::new(); if let Some(param_token) = tokens.get(2) { let mut parameters = param_token.split(';').peekable(); // The 'pt' parameter must be the first parameter if present, so it // cannot be checked along with the other parameters below if let Some(maybe_fmt_parameter) = parameters.clone().peek() { if let Some(fmt_list) = maybe_fmt_parameter.strip_prefix("pt=") { for fmt in fmt_list.split(',') { formats.push(fmt.trim().parse::()?); } parameters.next(); } } for param in parameters { // TODO: Bug 1225877. Add support for params without '=' let param_value_pair: Vec<&str> = param.splitn(2, '=').collect(); if param_value_pair.len() != 2 { return Err(SdpParserInternalError::Generic( "A rid parameter needs to be of form 'param=value'".to_string(), )); } match param_value_pair[0] { "max-width" => params.max_width = param_value_pair[1].parse::()?, "max-height" => params.max_height = param_value_pair[1].parse::()?, "max-fps" => params.max_fps = param_value_pair[1].parse::()?, "max-fs" => params.max_fs = param_value_pair[1].parse::()?, "max-br" => params.max_br = param_value_pair[1].parse::()?, "max-pps" => params.max_pps = param_value_pair[1].parse::()?, "depends" => { depends.extend(param_value_pair[1].split(',').map(ToString::to_string)); } _ => params.unknown.push(param.to_string()), } } } Ok(SdpAttribute::Rid(SdpAttributeRid { id: tokens[0].to_string(), direction: parse_single_direction(tokens[1])?, formats, params, depends, })) } /////////////////////////////////////////////////////////////////////////// // a=remote-candiate, RFC5245 //------------------------------------------------------------------------- // remote-candidate-att = "remote-candidates" ":" remote-candidate // 0*(SP remote-candidate) // remote-candidate = component-ID SP connection-address SP port fn parse_remote_candidates(to_parse: &str) -> Result { let mut tokens = to_parse.split_whitespace(); let component = match tokens.next() { None => { return Err(SdpParserInternalError::Generic( "Remote-candidate attribute is missing component ID".to_string(), )); } Some(x) => x.parse::()?, }; let address = match tokens.next() { None => { return Err(SdpParserInternalError::Generic( "Remote-candidate attribute is missing connection address".to_string(), )); } Some(x) => parse_unicast_address(x)?, }; let port = match tokens.next() { None => { return Err(SdpParserInternalError::Generic( "Remote-candidate attribute is missing port number".to_string(), )); } Some(x) => x.parse::()?, }; if port > 65535 { return Err(SdpParserInternalError::Generic( "Remote-candidate port can only be a bit 16bit number".to_string(), )); }; Ok(SdpAttribute::RemoteCandidate(SdpAttributeRemoteCandidate { component, address, port, })) } /////////////////////////////////////////////////////////////////////////// // a=rtpmap, RFC4566 //------------------------------------------------------------------------- // a=rtpmap: / [/] fn parse_rtpmap(to_parse: &str) -> Result { let mut tokens = to_parse.split_whitespace(); let payload_type: u8 = match tokens.next() { None => { return Err(SdpParserInternalError::Generic( "Rtpmap missing payload type".to_string(), )); } Some(x) => { let pt = x.parse::()?; if pt > 127 { return Err(SdpParserInternalError::Generic( "Rtpmap payload type must be less then 127".to_string(), )); }; pt } }; let mut parameters = match tokens.next() { None => { return Err(SdpParserInternalError::Generic( "Rtpmap missing payload type".to_string(), )); } Some(x) => x.split('/'), }; let name = match parameters.next() { None => { return Err(SdpParserInternalError::Generic( "Rtpmap missing codec name".to_string(), )); } Some(x) => x.to_string(), }; let frequency = match parameters.next() { None => { return Err(SdpParserInternalError::Generic( "Rtpmap missing codec name".to_string(), )); } Some(x) => x.parse::()?, }; let mut rtpmap = SdpAttributeRtpmap::new(payload_type, name, frequency); if let Some(x) = parameters.next() { rtpmap.set_channels(x.parse::()?) }; Ok(SdpAttribute::Rtpmap(rtpmap)) } /////////////////////////////////////////////////////////////////////////// // a=rtcp, RFC3605 //------------------------------------------------------------------------- // rtcp-attribute = "a=rtcp:" port [nettype space addrtype space // connection-address] CRLF fn parse_rtcp(to_parse: &str) -> Result { let mut tokens = to_parse.split_whitespace(); let port = match tokens.next() { None => { return Err(SdpParserInternalError::Generic( "Rtcp attribute is missing port number".to_string(), )); } Some(x) => x.parse::()?, }; let mut rtcp = SdpAttributeRtcp::new(port); match tokens.next() { None => (), Some(x) => { parse_network_type(x)?; match tokens.next() { None => { return Err(SdpParserInternalError::Generic( "Rtcp attribute is missing address type token".to_string(), )); } Some(x) => { let addrtype = AddressType::from_str(x)?; let addr = match tokens.next() { None => { return Err(SdpParserInternalError::Generic( "Rtcp attribute is missing ip address token".to_string(), )); } Some(x) => match ExplicitlyTypedAddress::try_from((addrtype, x)) { Ok(address) => address, Err(e) => return Err(e), }, }; rtcp.set_addr(addr); } }; } }; Ok(SdpAttribute::Rtcp(rtcp)) } /////////////////////////////////////////////////////////////////////////// // a=rtcp-fb, RFC4585 //------------------------------------------------------------------------- // rtcp-fb-syntax = "a=rtcp-fb:" rtcp-fb-pt SP rtcp-fb-val CRLF // // rtcp-fb-pt = "*" ; wildcard: applies to all formats // / fmt ; as defined in SDP spec // // rtcp-fb-val = "ack" rtcp-fb-ack-param // / "nack" rtcp-fb-nack-param // / "trr-int" SP 1*DIGIT // / rtcp-fb-id rtcp-fb-param // // rtcp-fb-id = 1*(alpha-numeric / "-" / "_") // // rtcp-fb-param = SP "app" [SP byte-string] // / SP token [SP byte-string] // / ; empty // // rtcp-fb-ack-param = SP "rpsi" // / SP "app" [SP byte-string] // / SP token [SP byte-string] // / ; empty // // rtcp-fb-nack-param = SP "pli" // / SP "sli" // / SP "rpsi" // / SP "app" [SP byte-string] // / SP token [SP byte-string] // / ; empty fn parse_rtcp_fb(to_parse: &str) -> Result { let tokens: Vec<&str> = to_parse.splitn(4, ' ').collect(); // Parse this in advance to use it later in the parameter switch let feedback_type = match tokens.get(1) { Some(x) => match *x { "ack" => SdpAttributeRtcpFbType::Ack, "ccm" => SdpAttributeRtcpFbType::Ccm, "nack" => SdpAttributeRtcpFbType::Nack, "trr-int" => SdpAttributeRtcpFbType::TrrInt, "goog-remb" => SdpAttributeRtcpFbType::Remb, "transport-cc" => SdpAttributeRtcpFbType::TransCc, _ => { return Err(SdpParserInternalError::Unsupported(format!( "Unknown rtcpfb feedback type: {:?}", x ))); } }, None => { return Err(SdpParserInternalError::Generic( "Error parsing rtcpfb: no feedback type".to_string(), )); } }; // Parse this in advance to make the initilization block below better readable let parameter = match feedback_type { SdpAttributeRtcpFbType::Ack => match tokens.get(2) { Some(x) => match *x { "rpsi" | "app" => (*x).to_string(), _ => { return Err(SdpParserInternalError::Unsupported(format!( "Unknown rtcpfb ack parameter: {:?}", x ))); } }, None => { return Err(SdpParserInternalError::Unsupported( "The rtcpfb ack feeback type needs a parameter:".to_string(), )); } }, SdpAttributeRtcpFbType::Ccm => match tokens.get(2) { Some(x) => match *x { "fir" | "tmmbr" | "tstr" | "vbcm" => (*x).to_string(), _ => { return Err(SdpParserInternalError::Unsupported(format!( "Unknown rtcpfb ccm parameter: {:?}", x ))); } }, None => "".to_string(), }, SdpAttributeRtcpFbType::Nack => match tokens.get(2) { Some(x) => match *x { "sli" | "pli" | "rpsi" | "app" => (*x).to_string(), _ => { return Err(SdpParserInternalError::Unsupported(format!( "Unknown rtcpfb nack parameter: {:?}", x ))); } }, None => "".to_string(), }, SdpAttributeRtcpFbType::TrrInt => match tokens.get(2) { Some(x) => match x { _ if x.parse::().is_ok() => (*x).to_string(), _ => { return Err(SdpParserInternalError::Generic(format!( "Unknown rtcpfb trr-int parameter: {:?}", x ))); } }, None => { return Err(SdpParserInternalError::Generic( "The rtcpfb trr-int feedback type needs a parameter".to_string(), )); } }, SdpAttributeRtcpFbType::Remb | SdpAttributeRtcpFbType::TransCc => match tokens.get(2) { Some(x) => { return Err(SdpParserInternalError::Unsupported(format!( "Unknown rtcpfb {} parameter: {:?}", feedback_type, x ))); } None => "".to_string(), }, }; Ok(SdpAttribute::Rtcpfb(SdpAttributeRtcpFb { payload_type: parse_payload_type(tokens[0])?, feedback_type, parameter, extra: match tokens.get(3) { Some(x) => (*x).to_string(), None => "".to_string(), }, })) } /////////////////////////////////////////////////////////////////////////// // a=sctpmap, draft-ietf-mmusic-sctp-sdp-05 //------------------------------------------------------------------------- // sctpmap-attr = "a=sctpmap:" sctpmap-number media-subtypes // [streams] // sctpmap-number = 1*DIGIT // protocol = labelstring // labelstring = text // text = byte-string // streams = 1*DIGIT // // Note: this was replace in later versions of the draft by sctp-port fn parse_sctpmap(to_parse: &str) -> Result { let tokens: Vec<&str> = to_parse.split_whitespace().collect(); if tokens.len() != 3 { return Err(SdpParserInternalError::Generic( "Sctpmap needs to have three tokens".to_string(), )); } let port = tokens[0].parse::()?; if tokens[1].to_lowercase() != "webrtc-datachannel" { return Err(SdpParserInternalError::Generic( "Unsupported sctpmap type token".to_string(), )); } Ok(SdpAttribute::Sctpmap(SdpAttributeSctpmap { port, channels: tokens[2].parse::()?, })) } /////////////////////////////////////////////////////////////////////////// // a=setup, RFC4145 //------------------------------------------------------------------------- // setup-attr = "a=setup:" role // role = "active" / "passive" / "actpass" / "holdconn" fn parse_setup(to_parse: &str) -> Result { Ok(SdpAttribute::Setup( match to_parse.to_lowercase().as_ref() { "active" => SdpAttributeSetup::Active, "actpass" => SdpAttributeSetup::Actpass, "holdconn" => SdpAttributeSetup::Holdconn, "passive" => SdpAttributeSetup::Passive, _ => { return Err(SdpParserInternalError::Generic( "Unsupported setup value".to_string(), )); } }, )) } fn parse_simulcast_version_list( to_parse: &str, ) -> Result, SdpParserInternalError> { let make_version_list = |to_parse: &str| { to_parse .split(';') .map(SdpAttributeSimulcastVersion::new) .collect() }; if to_parse.contains('=') { let mut descriptor_versionlist_pair = to_parse.splitn(2, '='); match descriptor_versionlist_pair.next().unwrap() { // TODO Bug 1470568 "rid" => Ok(make_version_list( descriptor_versionlist_pair.next().unwrap(), )), descriptor => Err(SdpParserInternalError::Generic(format!( "Simulcast attribute has unknown list descriptor '{:?}'", descriptor ))), } } else { Ok(make_version_list(to_parse)) } } /////////////////////////////////////////////////////////////////////////// // a=simulcast, draft-ietf-mmusic-sdp-simulcast //------------------------------------------------------------------------- // Old draft-04 // sc-attr = "a=simulcast:" 1*2( WSP sc-str-list ) [WSP sc-pause-list] // sc-str-list = sc-dir WSP sc-id-type "=" sc-alt-list *( ";" sc-alt-list ) // sc-pause-list = "paused=" sc-alt-list // sc-dir = "send" / "recv" // sc-id-type = "pt" / "rid" / token // sc-alt-list = sc-id *( "," sc-id ) // sc-id = fmt / rid-identifier / token // ; WSP defined in [RFC5234] // ; fmt, token defined in [RFC4566] // ; rid-identifier defined in [I-D.pthatcher-mmusic-rid] // // New draft 14, need to parse this for now, will eventually emit it // sc-value = ( sc-send [SP sc-recv] ) / ( sc-recv [SP sc-send] ) // sc-send = %s"send" SP sc-str-list // sc-recv = %s"recv" SP sc-str-list // sc-str-list = sc-alt-list *( ";" sc-alt-list ) // sc-alt-list = sc-id *( "," sc-id ) // sc-id-paused = "~" // sc-id = [sc-id-paused] rid-id // ; SP defined in [RFC5234] // ; rid-id defined in [I-D.ietf-mmusic-rid] fn parse_simulcast(to_parse: &str) -> Result { // TODO: Bug 1225877: Stop accepting all kinds of whitespace here, and only accept SP let mut tokens = to_parse.trim().split_whitespace(); let first_direction = match tokens.next() { Some(x) => parse_single_direction(x)?, None => { return Err(SdpParserInternalError::Generic( "Simulcast attribute is missing send/recv value".to_string(), )); } }; let first_version_list = match tokens.next() { Some(x) => parse_simulcast_version_list(x)?, None => { return Err(SdpParserInternalError::Generic( "Simulcast attribute must have an alternatives list after the direction token" .to_string(), )); } }; let mut second_version_list = Vec::new(); if let Some(x) = tokens.next() { if parse_single_direction(x)? == first_direction { return Err(SdpParserInternalError::Generic( "Simulcast attribute has defined two times the same direction".to_string(), )); } second_version_list = match tokens.next() { Some(x) => parse_simulcast_version_list(x)?, None => { return Err(SdpParserInternalError::Generic(format!( "{:?}{:?}", "Simulcast has defined a second direction but", "no second list of simulcast stream versions" ))); } } } Ok(SdpAttribute::Simulcast(match first_direction { SdpSingleDirection::Send => SdpAttributeSimulcast { send: first_version_list, receive: second_version_list, }, SdpSingleDirection::Recv => SdpAttributeSimulcast { send: second_version_list, receive: first_version_list, }, })) } /////////////////////////////////////////////////////////////////////////// // a=ssrc, RFC5576 //------------------------------------------------------------------------- // ssrc-attr = "ssrc:" ssrc-id SP attribute // ; The base definition of "attribute" is in RFC 4566. // ; (It is the content of "a=" lines.) // // ssrc-id = integer ; 0 .. 2**32 - 1 fn parse_ssrc(to_parse: &str) -> Result { let mut tokens = to_parse.splitn(2, ' '); let ssrc_id = match tokens.next() { None => { return Err(SdpParserInternalError::Generic( "Ssrc attribute is missing ssrc-id value".to_string(), )); } Some(x) => x.parse::()?, }; let mut ssrc = SdpAttributeSsrc::new(ssrc_id); match tokens.next() { None => (), Some(x) => ssrc.set_attribute(x), }; Ok(SdpAttribute::Ssrc(ssrc)) } pub fn parse_attribute(value: &str) -> Result { Ok(SdpType::Attribute(value.trim().parse()?)) } #[cfg(test)] #[path = "./attribute_type_tests.rs"] mod tests;