1#[cfg(test)]
11mod extmap_test;
12
13use super::direction::*;
14use crate::description::common::*;
15use shared::error::{Error, Result};
16
17use std::fmt;
18use std::io;
19use url::Url;
20
21pub const DEF_EXT_MAP_VALUE_ABS_SEND_TIME: usize = 1;
23pub const DEF_EXT_MAP_VALUE_TRANSPORT_CC: usize = 2;
25pub const DEF_EXT_MAP_VALUE_SDES_MID: usize = 3;
27pub const DEF_EXT_MAP_VALUE_SDES_RTP_STREAM_ID: usize = 4;
29
30pub const ABS_SEND_TIME_URI: &str = "http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time";
32pub const TRANSPORT_CC_URI: &str =
34 "http://www.ietf.org/id/draft-holmer-rmcat-transport-wide-cc-extensions-01";
35pub const SDES_MID_URI: &str = "urn:ietf:params:rtp-hdrext:sdes:mid";
37pub const SDES_RTP_STREAM_ID_URI: &str = "urn:ietf:params:rtp-hdrext:sdes:rtp-stream-id";
39pub const SDES_REPAIR_RTP_STREAM_ID_URI: &str =
41 "urn:ietf:params:rtp-hdrext:sdes:repaired-rtp-stream-id";
42
43pub const AUDIO_LEVEL_URI: &str = "urn:ietf:params:rtp-hdrext:ssrc-audio-level";
45pub const VIDEO_ORIENTATION_URI: &str = "urn:3gpp:video-orientation";
47
48#[derive(Debug, Clone, Default)]
50pub struct ExtMap {
51 pub value: u16,
53 pub direction: Direction,
55 pub uri: Option<Url>,
57 pub ext_attr: Option<String>,
59}
60
61impl fmt::Display for ExtMap {
62 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
63 write!(f, "{}", self.value)?;
64
65 if self.direction != Direction::Unspecified {
66 write!(f, "/{}", self.direction)?;
67 }
68
69 if let Some(uri) = &self.uri {
70 write!(f, " {uri}")?;
71 }
72
73 if let Some(ext_attr) = &self.ext_attr {
74 write!(f, " {ext_attr}")?;
75 }
76
77 Ok(())
78 }
79}
80
81impl ExtMap {
82 pub fn convert(&self) -> Attribute {
84 Attribute {
85 key: "extmap".to_string(),
86 value: Some(self.to_string()),
87 }
88 }
89
90 pub fn unmarshal<R: io::BufRead>(reader: &mut R) -> Result<Self> {
92 let mut line = String::new();
93 reader.read_line(&mut line)?;
94 let parts: Vec<&str> = line.trim().splitn(2, ':').collect();
95 if parts.len() != 2 {
96 return Err(Error::ParseExtMap(line));
97 }
98
99 let fields: Vec<&str> = parts[1].split_whitespace().collect();
100 if fields.len() < 2 {
101 return Err(Error::ParseExtMap(line));
102 }
103
104 let valdir: Vec<&str> = fields[0].split('/').collect();
105 let value = valdir[0].parse::<u16>()?;
106 if !(1..=255).contains(&value) {
110 return Err(Error::ParseExtMap(format!(
111 "{} -- extmap key must be in the range 1-255",
112 valdir[0]
113 )));
114 }
115
116 let mut direction = Direction::Unspecified;
117 if valdir.len() == 2 {
118 direction = Direction::new(valdir[1]);
119 if direction == Direction::Unspecified {
120 return Err(Error::ParseExtMap(format!(
121 "unknown direction from {}",
122 valdir[1]
123 )));
124 }
125 }
126
127 let uri = Some(Url::parse(fields[1])?);
128
129 let ext_attr = if fields.len() == 3 {
130 Some(fields[2].to_owned())
131 } else {
132 None
133 };
134
135 Ok(ExtMap {
136 value,
137 direction,
138 uri,
139 ext_attr,
140 })
141 }
142
143 pub fn marshal(&self) -> String {
145 "extmap:".to_string() + self.to_string().as_str()
146 }
147}