1use crate::{
2 Bandwidth, Connection, Direction, ExtMap, Fingerprint, Fmtp, Group, IceCandidate, IceOptions,
3 IcePassword, IceUsernameFragment, Media, MediaDescription, Origin, Rtcp, RtpMap,
4 SessionDescription, Setup, SrtpCrypto, Ssrc, Time, UnknownAttribute,
5};
6use bytesstr::BytesStr;
7use internal::verbose_error_to_owned;
8use nom::Finish;
9
10#[derive(Debug, thiserror::Error)]
11pub enum ParseSessionDescriptionError {
12 #[error("{0}")]
13 ParseError(nom::error::VerboseError<String>),
14 #[error("message ended unexpectedly")]
15 Incomplete,
16 #[error("message is missing the origin field (o=)")]
17 MissingOrigin,
18 #[error("message is missing the name (s=) field")]
19 MissingName,
20 #[error("message is missing the time (t=) field")]
21 MissingTime,
22}
23
24impl From<nom::error::VerboseError<&str>> for ParseSessionDescriptionError {
25 fn from(value: nom::error::VerboseError<&str>) -> Self {
26 Self::ParseError(verbose_error_to_owned(value))
27 }
28}
29
30#[derive(Default)]
31pub(crate) struct Parser {
32 origin: Option<Origin>,
33 name: Option<BytesStr>,
34 connection: Option<Connection>,
35 bandwidth: Vec<Bandwidth>,
36 time: Option<Time>,
37 direction: Direction,
38 group: Vec<Group>,
39 extmap: Vec<ExtMap>,
40 extmap_allow_mixed: bool,
41 ice_options: IceOptions,
42 ice_lite: bool,
43 ice_ufrag: Option<IceUsernameFragment>,
44 ice_pwd: Option<IcePassword>,
45 setup: Option<Setup>,
46 fingerprint: Vec<Fingerprint>,
47 attributes: Vec<UnknownAttribute>,
48 media_descriptions: Vec<MediaDescription>,
49}
50
51impl Parser {
52 pub(crate) fn parse_line(
53 &mut self,
54 src: &BytesStr,
55 complete_line: &str,
56 ) -> Result<(), ParseSessionDescriptionError> {
57 let line = complete_line
58 .get(2..)
59 .ok_or(ParseSessionDescriptionError::Incomplete)?;
60
61 match complete_line.as_bytes() {
62 [b'v', b'=', b'0'] => {
63 }
65 [b's', b'=', ..] => {
66 self.name = Some(BytesStr::from_parse(src.as_ref(), line));
67 }
68 [b'o', b'=', ..] => {
69 let (_, o) = Origin::parse(src.as_ref(), line).finish()?;
70 self.origin = Some(o);
71 }
72 [b't', b'=', ..] => {
73 let (_, t) = Time::parse(line).finish()?;
74 self.time = Some(t);
75 }
76 [b'c', b'=', ..] => {
77 let (_, c) = Connection::parse(src.as_ref(), line).finish()?;
78
79 if let Some(media_description) = self.media_descriptions.last_mut() {
80 media_description.connection = Some(c);
81 } else {
82 self.connection = Some(c);
83 }
84 }
85 [b'b', b'=', ..] => {
86 let (_, b) = Bandwidth::parse(src.as_ref(), line).finish()?;
87
88 if let Some(media_description) = self.media_descriptions.last_mut() {
89 media_description.bandwidth.push(b);
90 } else {
91 self.bandwidth.push(b);
92 }
93 }
94 [b'm', b'=', ..] => {
95 let (_, media) = Media::parse(src.as_ref(), line).finish()?;
96
97 self.media_descriptions.push(MediaDescription {
98 media,
99 connection: None,
101 bandwidth: vec![],
102 direction: self.direction,
103 rtcp: None,
104 rtcp_mux: false,
105 mid: None,
106 rtpmap: vec![],
107 fmtp: vec![],
108 ice_ufrag: None,
109 ice_pwd: None,
110 ice_candidates: vec![],
111 ice_end_of_candidates: false,
112 crypto: vec![],
113 extmap: vec![],
114 extmap_allow_mixed: self.extmap_allow_mixed,
116 ssrc: vec![],
117 setup: self.setup,
118 fingerprint: vec![],
119 attributes: vec![],
120 });
121 }
122 [b'a', b'=', ..] => self.parse_attribute(src, line)?,
123 _ => {}
124 }
125
126 Ok(())
127 }
128
129 fn parse_attribute(
130 &mut self,
131 src: &BytesStr,
132 line: &str,
133 ) -> Result<(), ParseSessionDescriptionError> {
134 if let Some((name, value)) = line.split_once(':') {
135 self.parse_attribute_with_value(src, name, value)?;
136 } else {
137 self.parse_attribute_without_value(src, line);
138 }
139
140 Ok(())
141 }
142
143 fn parse_attribute_with_value(
144 &mut self,
145 src: &BytesStr,
146 name: &str,
147 value: &str,
148 ) -> Result<(), ParseSessionDescriptionError> {
149 match name {
150 "group" => {
151 let (_, group) = Group::parse(src.as_ref(), value).finish()?;
152 self.group.push(group);
153 }
154 "rtcp" => {
155 let (_, rtcp) = Rtcp::parse(src.as_ref(), value).finish()?;
156
157 if let Some(media_description) = self.media_descriptions.last_mut() {
158 media_description.rtcp = Some(rtcp);
159 }
160
161 }
163 "mid" => {
164 if let Some(media_description) = self.media_descriptions.last_mut() {
165 media_description.mid = Some(BytesStr::from_parse(src.as_ref(), value.trim()));
166 }
167
168 }
170 "rtpmap" => {
171 let (_, rtpmap) = RtpMap::parse(src.as_ref(), value).finish()?;
172
173 if let Some(media_description) = self.media_descriptions.last_mut() {
174 media_description.rtpmap.push(rtpmap);
175 }
176
177 }
179 "fmtp" => {
180 let (_, fmtp) = Fmtp::parse(src.as_ref(), value).finish()?;
181
182 if let Some(media_description) = self.media_descriptions.last_mut() {
183 media_description.fmtp.push(fmtp);
184 }
185
186 }
188 "ice-lite" => {
189 self.ice_lite = true;
190 }
191 "ice-options" => {
192 let (_, options) = IceOptions::parse(src.as_ref(), value).finish()?;
193 self.ice_options = options;
194 }
195 "ice-ufrag" => {
196 let (_, ufrag) = IceUsernameFragment::parse(src.as_ref(), value).finish()?;
197
198 if let Some(media_description) = self.media_descriptions.last_mut() {
199 media_description.ice_ufrag = Some(ufrag);
200 } else {
201 self.ice_ufrag = Some(ufrag);
202 }
203 }
204 "ice-pwd" => {
205 let (_, pwd) = IcePassword::parse(src.as_ref(), value).finish()?;
206
207 if let Some(media_description) = self.media_descriptions.last_mut() {
208 media_description.ice_pwd = Some(pwd);
209 } else {
210 self.ice_pwd = Some(pwd);
211 }
212 }
213 "candidate" => {
214 let (_, candidate) = IceCandidate::parse(src.as_ref(), value).finish()?;
215
216 if let Some(media_description) = self.media_descriptions.last_mut() {
217 media_description.ice_candidates.push(candidate);
218 }
219
220 }
222 "crypto" => {
223 let (_, crypto) = SrtpCrypto::parse(src.as_ref(), value).finish()?;
224
225 if let Some(media_description) = self.media_descriptions.last_mut() {
226 media_description.crypto.push(crypto);
227 }
228
229 }
231 "extmap" => {
232 let (_, extmap) = ExtMap::parse(src.as_ref(), value).finish()?;
233
234 if let Some(media_description) = self.media_descriptions.last_mut() {
235 media_description.extmap.push(extmap);
236 } else {
237 self.extmap.push(extmap);
238 }
239 }
240 "ssrc" => {
241 let (_, ssrc) = Ssrc::parse(src.as_ref(), value).finish()?;
242
243 if let Some(media_description) = self.media_descriptions.last_mut() {
244 media_description.ssrc.push(ssrc);
245 }
246
247 }
249 "setup" => {
250 let setup = match value {
251 "active" => Setup::Active,
252 "passive" => Setup::Passive,
253 "actpass" => Setup::ActPass,
254 "holdconn" => Setup::HoldConn,
255 _ => return Ok(()),
256 };
257
258 if let Some(media_description) = self.media_descriptions.last_mut() {
259 media_description.setup = Some(setup);
260 } else {
261 self.setup = Some(setup);
262 }
263 }
265 "fingerprint" => {
266 let (_, fingerprint) = Fingerprint::parse(src.as_ref(), value).finish()?;
267
268 if let Some(media_description) = self.media_descriptions.last_mut() {
269 media_description.fingerprint.push(fingerprint);
270 } else {
271 self.fingerprint.push(fingerprint)
272 }
273 }
274 _ => {
275 let attr = UnknownAttribute {
276 name: src.slice_ref(name),
277 value: Some(src.slice_ref(value)),
278 };
279
280 if let Some(media_description) = self.media_descriptions.last_mut() {
281 media_description.attributes.push(attr);
282 } else {
283 self.attributes.push(attr);
284 }
285 }
286 }
287
288 Ok(())
289 }
290
291 fn parse_attribute_without_value(&mut self, src: &BytesStr, line: &str) {
292 let direction = if let Some(media_description) = self.media_descriptions.last_mut() {
293 &mut media_description.direction
294 } else {
295 &mut self.direction
296 };
297
298 match line {
299 "sendrecv" => *direction = Direction::SendRecv,
300 "recvonly" => *direction = Direction::RecvOnly,
301 "sendonly" => *direction = Direction::SendOnly,
302 "inactive" => *direction = Direction::Inactive,
303 "extmap-allow-mixed" => {
304 if let Some(media_description) = self.media_descriptions.last_mut() {
305 media_description.extmap_allow_mixed = true;
306 } else {
307 self.extmap_allow_mixed = true;
308 }
309 }
310 "rtcp-mux" => {
311 if let Some(media_description) = self.media_descriptions.last_mut() {
312 media_description.rtcp_mux = true;
313 }
314 }
315 "end-of-candidates" => {
316 if let Some(media_description) = self.media_descriptions.last_mut() {
317 media_description.ice_end_of_candidates = true;
318 }
319
320 }
322 _ => {
323 let attr = UnknownAttribute {
324 name: src.slice_ref(line),
325 value: None,
326 };
327
328 if let Some(media_description) = self.media_descriptions.last_mut() {
329 media_description.attributes.push(attr);
330 } else {
331 self.attributes.push(attr);
332 }
333 }
334 }
335 }
336
337 pub(crate) fn finish(self) -> Result<SessionDescription, ParseSessionDescriptionError> {
338 Ok(SessionDescription {
339 origin: self
340 .origin
341 .ok_or(ParseSessionDescriptionError::MissingOrigin)?,
342 name: self.name.ok_or(ParseSessionDescriptionError::MissingName)?,
343 connection: self.connection,
344 bandwidth: self.bandwidth,
345 time: self.time.ok_or(ParseSessionDescriptionError::MissingTime)?,
346 direction: self.direction,
347 group: self.group,
348 extmap: self.extmap,
349 extmap_allow_mixed: self.extmap_allow_mixed,
350 ice_lite: self.ice_lite,
351 ice_options: self.ice_options,
352 ice_ufrag: self.ice_ufrag,
353 ice_pwd: self.ice_pwd,
354 setup: self.setup,
355 fingerprint: self.fingerprint,
356 attributes: self.attributes,
357 media_descriptions: self.media_descriptions,
358 })
359 }
360}