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};
#[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::<Vec<String>>()
.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<T>(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 {
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, }
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<Address>,
pub rport: Option<u32>,
pub tcp_type: Option<SdpAttributeCandidateTcpType>,
pub generation: Option<u32>,
pub ufrag: Option<String>,
pub networkcost: Option<u32>,
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(|(name, value)| format!(" {name} {value}"))
.collect::<String>()
)
}
}
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(msg) => format!("client {msg}"),
SdpAttributeDtlsMessage::Server(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<SdpAttributeSimulcastId>,
}
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::<Vec<String>>()
.join(",")
.fmt(f)
}
}
#[derive(Clone)]
#[cfg_attr(feature = "serialize", derive(Serialize))]
#[cfg_attr(feature = "enhanced_debug", derive(Debug))]
pub struct SdpAttributeSimulcast {
pub send: Vec<SdpAttributeSimulcastVersion>,
pub receive: Vec<SdpAttributeSimulcastVersion>,
}
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<ExplicitlyTypedAddress>,
}
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, 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<SdpAttributeDirection>,
pub url: String,
pub extension_attributes: Option<String>,
}
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<u32>,
}
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 {
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,
pub max_fr: u32,
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,
pub encodings: Vec<u8>,
pub dtmf_tones: String,
pub rtx: Option<RtxFmtpParameters>,
pub unknown_tokens: Vec<String>,
}
impl fmt::Display for SdpAttributeFmtpParameters {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
if let Some(ref rtx) = self.rtx {
return write!(f, "{rtx}");
}
if !self.dtmf_tones.is_empty() {
return write!(f, "{}", self.dtmf_tones);
} else if !self.encodings.is_empty() {
return self
.encodings
.iter()
.map(ToString::to_string)
.collect::<Vec<String>>()
.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<Self, SdpParserInternalError> {
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<Vec<u8>, 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::<Result<Vec<u8>, _>>()?;
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<u8>,
}
impl TryFrom<(SdpAttributeFingerprintHashType, Vec<u8>)> for SdpAttributeFingerprint {
type Error = SdpParserInternalError;
fn try_from(
parts: (SdpAttributeFingerprintHashType, Vec<u8>),
) -> Result<Self, SdpParserInternalError> {
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 {a} fingerprint bytes not {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!("{byte:02X}"))
.collect::<Vec<String>>()
.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<T>(values: &[T]) -> String
where
T: ToString,
{
match values.len() {
1 => values[0].to_string(),
_ => format!(
"[{}]",
values
.iter()
.map(ToString::to_string)
.collect::<Vec<String>>()
.join(",")
),
}
}
#[derive(Debug, PartialEq, Clone)]
#[cfg_attr(feature = "serialize", derive(Serialize))]
pub enum SdpAttributeImageAttrXyRange {
Range(u32, u32, Option<u32>), DiscreteValues(Vec<u32>),
}
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), DiscreteValues(Vec<f32>),
}
impl fmt::Display for SdpAttributeImageAttrSRange {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
SdpAttributeImageAttrSRange::Range(min, max) => write!(f, "[{min}-{max}]"),
SdpAttributeImageAttrSRange::DiscreteValues(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<SdpAttributeImageAttrSRange>,
pub par: Option<SdpAttributeImageAttrPRange>,
pub q: Option<f32>,
}
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<SdpAttributeImageAttrSet>),
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::<Vec<String>>()
.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, FlowIdentification, SingleReservationFlow, AlternateNetworkAddressType, ForwardErrorCorrection, DecodingDependency, 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<String>,
}
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<String>,
}
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<String>,
}
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<String>,
}
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<u16>,
pub params: SdpAttributeRidParameters,
pub depends: Vec<String>,
}
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<u32>,
}
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<String>,
pub value: Option<String>,
}
impl SdpAttributeSsrc {
pub fn new(id: u32) -> SdpAttributeSsrc {
SdpAttributeSsrc {
id,
attribute: None,
value: None,
}
}
fn set_attribute(&mut self, a: &str) {
if a.find(':').is_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, FlowIdentification, ForwardErrorCorrection, ForwardErrorCorrectionFr, Sim, }
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<String>),
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, RtcpRsize,
Sctpmap(SdpAttributeSctpmap),
SctpPort(u64),
Sendonly,
Sendrecv,
Setup(SdpAttributeSetup),
Simulcast(SdpAttributeSimulcast),
Ssrc(SdpAttributeSsrc),
SsrcGroup(SdpSsrcGroupSemantic, Vec<SdpAttributeSsrc>),
}
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<Self, Self::Err> {
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!(
"{name} attribute is not allowed to have a value",
)));
}
_ => (),
}
}
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<String> =
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<String, SdpParserInternalError> {
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<SdpAttributePayloadType, SdpParserInternalError> {
Ok(match to_parse {
"*" => SdpAttributePayloadType::Wildcard,
_ => SdpAttributePayloadType::PayloadType(to_parse.parse::<u8>()?),
})
}
fn parse_single_direction(to_parse: &str) -> Result<SdpSingleDirection, SdpParserInternalError> {
match to_parse {
"send" => Ok(SdpSingleDirection::Send),
"recv" => Ok(SdpSingleDirection::Recv),
x => Err(SdpParserInternalError::Generic(format!(
"Unknown direction description found: '{x:}'"
))),
}
}
fn parse_ssrc_group(to_parse: &str) -> Result<SdpAttribute, SdpParserInternalError> {
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 '{unknown:?}' found"
)));
}
},
};
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))
}
fn parse_sctp_port(to_parse: &str) -> Result<SdpAttribute, SdpParserInternalError> {
let port = to_parse.parse()?;
if port > 65535 {
return Err(SdpParserInternalError::Generic(format!(
"Sctpport port {port} can only be a bit 16bit number"
)));
}
Ok(SdpAttribute::SctpPort(port))
}
fn parse_candidate(to_parse: &str) -> Result<SdpAttribute, SdpParserInternalError> {
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::<u32>()?;
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::<u64>()?;
let address = Address::from_str(tokens[4])?;
let port = tokens[5].parse::<u32>()?;
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::<u32>()?;
cand.set_generation(generation);
index += 2;
}
"network-cost" => {
let cost = tokens[index + 1].parse::<u32>()?;
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::<u32>()?;
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))
}
fn parse_dtls_message(to_parse: &str) -> Result<SdpAttribute, SdpParserInternalError> {
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}'"
)));
}
}))
}
fn valid_byte_string(input: &str) -> bool {
!(input.contains(0x00 as char) || input.contains(0x0A as char) || input.contains(0x0D as char))
}
fn parse_extmap(to_parse: &str) -> Result<SdpAttribute, SdpParserInternalError> {
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<SdpAttributeDirection> = None;
if tokens[0].find('/').is_none() {
id = tokens[0].parse::<u16>()?;
} else {
let id_dir: Vec<&str> = tokens[0].splitn(2, '/').collect();
id = id_dir[0].parse::<u16>()?;
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(),
));
}
})
}
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,
}))
}
fn parse_fingerprint(to_parse: &str) -> Result<SdpAttribute, SdpParserInternalError> {
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))
}
fn parse_fmtp(to_parse: &str) -> Result<SdpAttribute, SdpParserInternalError> {
let tokens: Vec<&str> = to_parse.splitn(2, ' ').collect();
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];
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('=') {
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<bool, SdpParserInternalError> {
match val.parse::<u8>()? {
0 => Ok(false),
1 => Ok(true),
_ => Err(SdpParserInternalError::Generic(format!(
"The fmtp parameter '{param_name:}' must be 0 or 1"
))),
}
};
let parameter_name = name_value_pair[0];
let parameter_val = name_value_pair[1];
match parameter_name.to_uppercase().as_str() {
"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::<u32>()? {
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::<u32>()?,
"MAX-FS" => parameters.max_fs = parameter_val.parse::<u32>()?,
"MAX-CPB" => parameters.max_cpb = parameter_val.parse::<u32>()?,
"MAX-DPB" => parameters.max_dpb = parameter_val.parse::<u32>()?,
"MAX-BR" => parameters.max_br = parameter_val.parse::<u32>()?,
"MAX-FR" => parameters.max_fr = parameter_val.parse::<u32>()?,
"MAXPLAYBACKRATE" => {
parameters.maxplaybackrate = parameter_val.parse::<u32>()?
}
"MAXAVERAGEBITRATE" => {
parameters.maxaveragebitrate = parameter_val.parse::<u32>()?
}
"PTIME" => parameters.ptime = parameter_val.parse::<u32>()?,
"MAXPTIME" => parameters.maxptime = parameter_val.parse::<u32>()?,
"MINPTIME" => parameters.minptime = parameter_val.parse::<u32>()?,
"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::<u8>()?,
rtx_time: None,
})
}
"RTX-TIME" => {
if let Some(ref mut rtx) = parameters.rtx {
rtx.rtx_time = Some(parameter_val.parse::<u32>()?)
} 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::<u8>()? {
x @ 0..=128 => parameters.encodings.push(x),
_ => {
return Err(SdpParserInternalError::Generic(
"Red codec must be in range [0,128]".to_string(),
));
}
}
}
} else {
let dtmf_tones: Vec<&str> = parameter_token.split(',').collect();
let mut dtmf_tone_is_ok = true;
let validate_digits = |digit_option: Option<u8>| -> Option<u8> {
match digit_option {
Some(x) => match x {
0..=100 => Some(x),
_ => None,
},
None => None,
}
};
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() {
2 => {
match validate_digits(dtmf_tone_range[0].parse::<u8>().ok()) {
Some(l) => match validate_digits(dtmf_tone_range[1].parse::<u8>().ok())
{
Some(u) => {
l < u
}
None => false,
},
None => false,
}
}
1 => validate_digits(dtmf_tone.parse::<u8>().ok()).is_some(),
_ => false,
};
if !dtmf_tone_is_ok {
break;
}
}
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::<u8>()?,
parameters,
}))
}
fn parse_group(to_parse: &str) -> Result<SdpAttribute, SdpParserInternalError> {
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 '{unknown:?}' found",
)));
}
},
};
Ok(SdpAttribute::Group(SdpAttributeGroup {
semantics,
tags: tokens.map(ToString::to_string).collect(),
}))
}
fn parse_ice_options(to_parse: &str) -> Result<SdpAttribute, SdpParserInternalError> {
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(),
))
}
fn parse_ice_pacing(to_parse: &str) -> Result<SdpAttribute, SdpParserInternalError> {
let parsed = to_parse.parse::<u64>()?;
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<String> {
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<SdpAttributeImageAttrXyRange, SdpParserInternalError> {
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(':') {
let range_tokens: Vec<&str> = value_tokens.split(':').collect();
if range_tokens.len() == 3 {
Ok(SdpAttributeImageAttrXyRange::Range(
range_tokens[0].parse::<u32>()?,
range_tokens[2].parse::<u32>()?,
Some(range_tokens[1].parse::<u32>()?),
))
} else if range_tokens.len() == 2 {
Ok(SdpAttributeImageAttrXyRange::Range(
range_tokens[0].parse::<u32>()?,
range_tokens[1].parse::<u32>()?,
None,
))
} else {
Err(SdpParserInternalError::Generic(
"imageattr's xyrange must contain 2 or 3 fields".to_string(),
))
}
} else {
let values = value_tokens
.split(',')
.map(str::parse::<u32>)
.collect::<Result<Vec<u32>, _>>()?;
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::<u32>()?
]))
}
}
fn parse_image_attr_set(
to_parse: &str,
) -> Result<SdpAttributeImageAttrSet, SdpParserInternalError> {
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::<f32>()?;
let max = minmax_pair[1].parse::<f32>()?;
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('-') {
let range = parse_ps_range(sar_values)?;
sar = Some(SdpAttributeImageAttrSRange::Range(range.0, range.1))
} else if value_token.contains(',') {
let values = sar_values
.split(',')
.map(str::parse::<f32>)
.collect::<Result<Vec<f32>, _>>()?;
if values.len() < 2 {
return Err(SdpParserInternalError::Generic(
"imageattr's sar discrete value list must have at least two values"
.to_string(),
));
}
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::<f32>()?,
]))
}
} 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::<f32>()?);
}
}
Ok(SdpAttributeImageAttrSet { x, y, sar, par, q })
}
fn parse_image_attr_set_list<I>(
tokens: &mut iter::Peekable<I>,
) -> Result<SdpAttributeImageAttrSetList, SdpParserInternalError>
where
I: Iterator<Item = String> + Clone,
{
let parse_set = |set_token: &str| -> Result<SdpAttributeImageAttrSet, SdpParserInternalError> {
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))
}
}
}
fn parse_image_attr(to_parse: &str) -> Result<SdpAttribute, SdpParserInternalError> {
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());
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,
},
}))
}
fn parse_msid(to_parse: &str) -> Result<SdpAttribute, SdpParserInternalError> {
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 }))
}
fn parse_msid_semantic(to_parse: &str) -> Result<SdpAttribute, SdpParserInternalError> {
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(),
));
}
let semantic = SdpAttributeMsidSemantic {
semantic: tokens[0].to_string(),
msids: tokens[1..].iter().map(ToString::to_string).collect(),
};
Ok(SdpAttribute::MsidSemantic(semantic))
}
fn parse_rid(to_parse: &str) -> Result<SdpAttribute, SdpParserInternalError> {
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(),
));
}
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<u16> = Vec::new();
let mut depends: Vec<String> = Vec::new();
if let Some(param_token) = tokens.get(2) {
let mut parameters = param_token.split(';').peekable();
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::<u16>()?);
}
parameters.next();
}
}
for param in parameters {
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::<u32>()?,
"max-height" => params.max_height = param_value_pair[1].parse::<u32>()?,
"max-fps" => params.max_fps = param_value_pair[1].parse::<u32>()?,
"max-fs" => params.max_fs = param_value_pair[1].parse::<u32>()?,
"max-br" => params.max_br = param_value_pair[1].parse::<u32>()?,
"max-pps" => params.max_pps = param_value_pair[1].parse::<u32>()?,
"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,
}))
}
fn parse_remote_candidates(to_parse: &str) -> Result<SdpAttribute, SdpParserInternalError> {
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::<u32>()?,
};
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::<u32>()?,
};
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,
}))
}
fn parse_rtpmap(to_parse: &str) -> Result<SdpAttribute, SdpParserInternalError> {
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::<u8>()?;
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::<u32>()?,
};
let mut rtpmap = SdpAttributeRtpmap::new(payload_type, name, frequency);
if let Some(x) = parameters.next() {
rtpmap.set_channels(x.parse::<u32>()?)
};
Ok(SdpAttribute::Rtpmap(rtpmap))
}
fn parse_rtcp(to_parse: &str) -> Result<SdpAttribute, SdpParserInternalError> {
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::<u16>()?,
};
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))
}
fn parse_rtcp_fb(to_parse: &str) -> Result<SdpAttribute, SdpParserInternalError> {
let tokens: Vec<&str> = to_parse.splitn(4, ' ').collect();
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(),
));
}
};
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::<u32>().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 {feedback_type} parameter: {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(),
},
}))
}
fn parse_sctpmap(to_parse: &str) -> Result<SdpAttribute, SdpParserInternalError> {
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::<u16>()?;
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::<u32>()?,
}))
}
fn parse_setup(to_parse: &str) -> Result<SdpAttribute, SdpParserInternalError> {
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<Vec<SdpAttributeSimulcastVersion>, 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() {
"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))
}
}
fn parse_simulcast(to_parse: &str) -> Result<SdpAttribute, SdpParserInternalError> {
let mut tokens = to_parse.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,
},
}))
}
fn parse_ssrc(to_parse: &str) -> Result<SdpAttribute, SdpParserInternalError> {
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::<u32>()?,
};
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<SdpType, SdpParserInternalError> {
Ok(SdpType::Attribute(value.trim().parse()?))
}
#[cfg(test)]
#[path = "./attribute_type_tests.rs"]
mod tests;