use anyhow::{anyhow, bail}; use std::str::FromStr; pub struct SessionDescription { pub version: String, pub originator: String, pub session_name: String, pub session_information: Option, pub description_uri: Option, pub email_address: Option, pub phone_number: Option, pub connection_information: Option, pub bandwidth: Vec, pub time_descriptions: Vec, pub time_zone_adjustments: Option, pub encryption_key: Option, pub attributes: Vec, pub media_descriptions: Vec, } pub struct TimeDescription { pub time: String, pub repeat_times: Vec, } pub struct MediaDescription { pub name: String, pub title: Option, pub connection_information: Option, pub bandwidth: Vec, pub encryption_key: Option, pub attributes: Vec, } impl FromStr for SessionDescription { type Err = anyhow::Error; fn from_str(s: &str) -> Result { let mut version = None; let mut originator = None; let mut session_name = None; let mut session_information = None; let mut description_uri = None; let mut email_address = None; let mut phone_number = None; let mut connection_information = None; let mut encryption_key = None; let mut time_zone_adjustments = None; let mut bandwidth = Vec::new(); let mut time_descriptions = Vec::new(); let mut media_descriptions = Vec::new(); let mut attributes = Vec::new(); for line in s.lines() { let (r#type, value) = line.split_once("=").ok_or(anyhow!("sdp line invalid"))?; match r#type { "v" => version = Some(value.to_string()), "o" => originator = Some(value.to_string()), "s" => session_name = Some(value.to_string()), "i" => session_information = Some(value.to_string()), "u" => description_uri = Some(value.to_string()), "e" => email_address = Some(value.to_string()), "p" => phone_number = Some(value.to_string()), "t" => time_descriptions.push(TimeDescription { repeat_times: Vec::new(), time: value.to_string(), }), "r" => time_descriptions .last_mut() .ok_or(anyhow!("no time desc"))? .repeat_times .push(value.to_string()), "z" => time_zone_adjustments = Some(value.to_string()), "m" => media_descriptions.push(MediaDescription { connection_information: None, name: value.to_string(), title: None, encryption_key: None, attributes: Vec::new(), bandwidth: Vec::new(), }), "a" => { if let Some(media_desc) = media_descriptions.last_mut() { media_desc.attributes.push(value.to_string()) } else { attributes.push(value.to_string()) } } "b" => { if let Some(media_desc) = media_descriptions.last_mut() { media_desc.bandwidth.push(value.to_string()) } else { bandwidth.push(value.to_string()) } } "k" => { if let Some(key) = media_descriptions.last_mut() { key.encryption_key = Some(value.to_string()) } else { encryption_key = Some(value.to_string()) } } "c" => { if let Some(media_desc) = media_descriptions.last_mut() { media_desc.connection_information = Some(value.to_string()) } else { connection_information = Some(value.to_string()) } } x => bail!("unknown sdp type ({x:?})"), } } Ok(Self { bandwidth, connection_information, attributes, description_uri, email_address, encryption_key, phone_number, session_information, time_zone_adjustments, media_descriptions, originator: originator.ok_or(anyhow!("originator missing"))?, session_name: session_name.ok_or(anyhow!("session name missing"))?, version: version.ok_or(anyhow!("version missing"))?, time_descriptions, }) } }