1#[cfg(test)]
2mod util_test;
3
4use shared::error::{Error, Result};
5
6use std::collections::HashMap;
7use std::fmt;
8
9pub const ATTRIBUTE_KEY: &str = "a=";
11
12#[derive(Default, Debug, Copy, Clone, PartialEq, Eq)]
14pub enum ConnectionRole {
15 #[default]
16 Unspecified,
18
19 Active,
21
22 Passive,
24
25 Actpass,
27
28 Holdconn,
30}
31
32const CONNECTION_ROLE_ACTIVE_STR: &str = "active";
33const CONNECTION_ROLE_PASSIVE_STR: &str = "passive";
34const CONNECTION_ROLE_ACTPASS_STR: &str = "actpass";
35const CONNECTION_ROLE_HOLDCONN_STR: &str = "holdconn";
36
37impl fmt::Display for ConnectionRole {
38 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
39 let s = match self {
40 ConnectionRole::Active => CONNECTION_ROLE_ACTIVE_STR,
41 ConnectionRole::Passive => CONNECTION_ROLE_PASSIVE_STR,
42 ConnectionRole::Actpass => CONNECTION_ROLE_ACTPASS_STR,
43 ConnectionRole::Holdconn => CONNECTION_ROLE_HOLDCONN_STR,
44 _ => "Unspecified",
45 };
46 write!(f, "{s}")
47 }
48}
49
50impl From<u8> for ConnectionRole {
51 fn from(v: u8) -> Self {
52 match v {
53 1 => ConnectionRole::Active,
54 2 => ConnectionRole::Passive,
55 3 => ConnectionRole::Actpass,
56 4 => ConnectionRole::Holdconn,
57 _ => ConnectionRole::Unspecified,
58 }
59 }
60}
61
62impl From<&str> for ConnectionRole {
63 fn from(raw: &str) -> Self {
64 match raw {
65 CONNECTION_ROLE_ACTIVE_STR => ConnectionRole::Active,
66 CONNECTION_ROLE_PASSIVE_STR => ConnectionRole::Passive,
67 CONNECTION_ROLE_ACTPASS_STR => ConnectionRole::Actpass,
68 CONNECTION_ROLE_HOLDCONN_STR => ConnectionRole::Holdconn,
69 _ => ConnectionRole::Unspecified,
70 }
71 }
72}
73
74pub(crate) fn new_session_id() -> u64 {
79 let c = u64::MAX ^ (1u64 << 63);
80 rand::random::<u64>() & c
81}
82
83#[derive(Debug, Clone, Default, PartialEq, Eq)]
85pub struct Codec {
88 pub payload_type: u8,
90 pub name: String,
92 pub clock_rate: u32,
94 pub encoding_parameters: String,
96 pub fmtp: String,
98 pub rtcp_feedback: Vec<String>,
100}
101
102impl fmt::Display for Codec {
103 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
104 write!(
105 f,
106 "{} {}/{}/{} ({}) [",
107 self.payload_type, self.name, self.clock_rate, self.encoding_parameters, self.fmtp,
108 )?;
109
110 let mut first = true;
111 for part in &self.rtcp_feedback {
112 if first {
113 first = false;
114 write!(f, "{part}")?;
115 } else {
116 write!(f, ", {part}")?;
117 }
118 }
119
120 write!(f, "]")
121 }
122}
123
124pub(crate) fn parse_rtpmap(rtpmap: &str) -> Result<Codec> {
125 let split: Vec<&str> = rtpmap.split_whitespace().collect();
127 if split.len() != 2 {
128 return Err(Error::MissingWhitespace);
129 }
130
131 let pt_split: Vec<&str> = split[0].split(':').collect();
132 if pt_split.len() != 2 {
133 return Err(Error::MissingColon);
134 }
135 let payload_type = pt_split[1].parse::<u8>()?;
136
137 let split: Vec<&str> = split[1].split('/').collect();
138 let name = split[0].to_string();
139 let parts = split.len();
140 let clock_rate = if parts > 1 {
141 split[1].parse::<u32>()?
142 } else {
143 0
144 };
145 let encoding_parameters = if parts > 2 {
146 split[2].to_string()
147 } else {
148 "".to_string()
149 };
150
151 Ok(Codec {
152 payload_type,
153 name,
154 clock_rate,
155 encoding_parameters,
156 ..Default::default()
157 })
158}
159
160pub(crate) fn parse_fmtp(fmtp: &str) -> Result<Codec> {
161 let split: Vec<&str> = fmtp.split_whitespace().collect();
163 if split.len() != 2 {
164 return Err(Error::MissingWhitespace);
165 }
166
167 let fmtp = split[1].to_string();
168
169 let split: Vec<&str> = split[0].split(':').collect();
170 if split.len() != 2 {
171 return Err(Error::MissingColon);
172 }
173 let payload_type = split[1].parse::<u8>()?;
174
175 Ok(Codec {
176 payload_type,
177 fmtp,
178 ..Default::default()
179 })
180}
181
182pub(crate) fn parse_rtcp_fb(rtcp_fb: &str) -> Result<Codec> {
183 let split: Vec<&str> = rtcp_fb.splitn(2, ' ').collect();
185 if split.len() != 2 {
186 return Err(Error::MissingWhitespace);
187 }
188
189 let pt_split: Vec<&str> = split[0].split(':').collect();
190 if pt_split.len() != 2 {
191 return Err(Error::MissingColon);
192 }
193
194 Ok(Codec {
195 payload_type: pt_split[1].parse::<u8>()?,
196 rtcp_feedback: vec![split[1].to_string()],
197 ..Default::default()
198 })
199}
200
201pub(crate) fn merge_codecs(mut codec: Codec, codecs: &mut HashMap<u8, Codec>) {
202 if let Some(saved_codec) = codecs.get_mut(&codec.payload_type) {
203 if saved_codec.payload_type == 0 {
204 saved_codec.payload_type = codec.payload_type
205 }
206 if saved_codec.name.is_empty() {
207 saved_codec.name = codec.name
208 }
209 if saved_codec.clock_rate == 0 {
210 saved_codec.clock_rate = codec.clock_rate
211 }
212 if saved_codec.encoding_parameters.is_empty() {
213 saved_codec.encoding_parameters = codec.encoding_parameters
214 }
215 if saved_codec.fmtp.is_empty() {
216 saved_codec.fmtp = codec.fmtp
217 }
218 saved_codec.rtcp_feedback.append(&mut codec.rtcp_feedback);
219 } else {
220 codecs.insert(codec.payload_type, codec);
221 }
222}
223
224fn equivalent_fmtp(want: &str, got: &str) -> bool {
225 let mut want_split: Vec<&str> = want.split(';').collect();
226 let mut got_split: Vec<&str> = got.split(';').collect();
227
228 if want_split.len() != got_split.len() {
229 return false;
230 }
231
232 want_split.sort_unstable();
233 got_split.sort_unstable();
234
235 for (i, &want_part) in want_split.iter().enumerate() {
236 let want_part = want_part.trim();
237 let got_part = got_split[i].trim();
238 if got_part != want_part {
239 return false;
240 }
241 }
242
243 true
244}
245
246pub(crate) fn codecs_match(wanted: &Codec, got: &Codec) -> bool {
247 if !wanted.name.is_empty() && wanted.name.to_lowercase() != got.name.to_lowercase() {
248 return false;
249 }
250 if wanted.clock_rate != 0 && wanted.clock_rate != got.clock_rate {
251 return false;
252 }
253 if !wanted.encoding_parameters.is_empty()
254 && wanted.encoding_parameters != got.encoding_parameters
255 {
256 return false;
257 }
258 if !wanted.fmtp.is_empty() && !equivalent_fmtp(&wanted.fmtp, &got.fmtp) {
259 return false;
260 }
261
262 true
263}