1 #[cfg(test)] 2 mod extmap_test; 3 4 use super::direction::*; 5 use super::error::{Error, Result}; 6 use crate::description::common::*; 7 8 use std::fmt; 9 use std::io; 10 use url::Url; 11 12 /// Default ext values 13 pub const DEF_EXT_MAP_VALUE_ABS_SEND_TIME: usize = 1; 14 pub const DEF_EXT_MAP_VALUE_TRANSPORT_CC: usize = 2; 15 pub const DEF_EXT_MAP_VALUE_SDES_MID: usize = 3; 16 pub const DEF_EXT_MAP_VALUE_SDES_RTP_STREAM_ID: usize = 4; 17 18 pub const ABS_SEND_TIME_URI: &str = "http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time"; 19 pub const TRANSPORT_CC_URI: &str = 20 "http://www.ietf.org/id/draft-holmer-rmcat-transport-wide-cc-extensions-01"; 21 pub const SDES_MID_URI: &str = "urn:ietf:params:rtp-hdrext:sdes:mid"; 22 pub const SDES_RTP_STREAM_ID_URI: &str = "urn:ietf:params:rtp-hdrext:sdes:rtp-stream-id"; 23 pub const AUDIO_LEVEL_URI: &str = "urn:ietf:params:rtp-hdrext:ssrc-audio-level"; 24 25 /// ExtMap represents the activation of a single RTP header extension 26 #[derive(Debug, Clone, Default)] 27 pub struct ExtMap { 28 pub value: isize, 29 pub direction: Direction, 30 pub uri: Option<Url>, 31 pub ext_attr: Option<String>, 32 } 33 34 impl fmt::Display for ExtMap { 35 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 36 let mut output = format!("{}", self.value); 37 if self.direction != Direction::Unspecified { 38 output += format!("/{}", self.direction).as_str(); 39 } 40 41 if let Some(uri) = &self.uri { 42 output += format!(" {}", uri).as_str(); 43 } 44 45 if let Some(ext_attr) = &self.ext_attr { 46 output += format!(" {}", ext_attr).as_str(); 47 } 48 49 write!(f, "{}", output) 50 } 51 } 52 53 impl ExtMap { 54 /// converts this object to an Attribute 55 pub fn convert(&self) -> Attribute { 56 Attribute { 57 key: "extmap".to_string(), 58 value: Some(self.to_string()), 59 } 60 } 61 62 /// unmarshal creates an Extmap from a string 63 pub fn unmarshal<R: io::BufRead>(reader: &mut R) -> Result<Self> { 64 let mut line = String::new(); 65 reader.read_line(&mut line)?; 66 let parts: Vec<&str> = line.trim().splitn(2, ':').collect(); 67 if parts.len() != 2 { 68 return Err(Error::ParseExtMap(line)); 69 } 70 71 let fields: Vec<&str> = parts[1].split_whitespace().collect(); 72 if fields.len() < 2 { 73 return Err(Error::ParseExtMap(line)); 74 } 75 76 let valdir: Vec<&str> = fields[0].split('/').collect(); 77 let value = valdir[0].parse::<isize>()?; 78 if !(1..=246).contains(&value) { 79 return Err(Error::ParseExtMap(format!( 80 "{} -- extmap key must be in the range 1-256", 81 valdir[0] 82 ))); 83 } 84 85 let mut direction = Direction::Unspecified; 86 if valdir.len() == 2 { 87 direction = Direction::new(valdir[1]); 88 if direction == Direction::Unspecified { 89 return Err(Error::ParseExtMap(format!( 90 "unknown direction from {}", 91 valdir[1] 92 ))); 93 } 94 } 95 96 let uri = Some(Url::parse(fields[1])?); 97 98 let ext_attr = if fields.len() == 3 { 99 Some(fields[2].to_owned()) 100 } else { 101 None 102 }; 103 104 Ok(ExtMap { 105 value, 106 direction, 107 uri, 108 ext_attr, 109 }) 110 } 111 112 /// marshal creates a string from an ExtMap 113 pub fn marshal(&self) -> String { 114 "extmap:".to_string() + self.to_string().as_str() 115 } 116 } 117