1 /* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
4
5 use anonymizer::{AnonymizingClone, StatefulSdpAnonymizer};
6 use attribute_type::{
7 maybe_print_param, SdpAttribute, SdpAttributeRtpmap, SdpAttributeSctpmap, SdpAttributeType,
8 };
9 use error::{SdpParserError, SdpParserInternalError};
10 use std::fmt;
11 use {SdpBandwidth, SdpConnection, SdpLine, SdpType};
12
13 /*
14 * RFC4566
15 * media-field = %x6d "=" media SP port ["/" integer]
16 * SP proto 1*(SP fmt) CRLF
17 */
18 #[derive(Clone)]
19 #[cfg_attr(feature = "serialize", derive(Serialize))]
20 #[cfg_attr(feature = "enhanced_debug", derive(Debug))]
21 pub struct SdpMediaLine {
22 pub media: SdpMediaValue,
23 pub port: u32,
24 pub port_count: u32,
25 pub proto: SdpProtocolValue,
26 pub formats: SdpFormatList,
27 }
28
29 impl fmt::Display for SdpMediaLine {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result30 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
31 write!(
32 f,
33 "{media} {port}{pcount} {proto} {formats}",
34 media = self.media,
35 port = self.port,
36 pcount = maybe_print_param("/", self.port_count, 0),
37 proto = self.proto,
38 formats = self.formats
39 )
40 }
41 }
42
43 #[derive(Debug, PartialEq, Clone)]
44 #[cfg_attr(feature = "serialize", derive(Serialize))]
45 pub enum SdpMediaValue {
46 Audio,
47 Video,
48 Application,
49 }
50
51 impl fmt::Display for SdpMediaValue {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result52 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
53 match *self {
54 SdpMediaValue::Audio => "audio",
55 SdpMediaValue::Video => "video",
56 SdpMediaValue::Application => "application",
57 }
58 .fmt(f)
59 }
60 }
61
62 #[derive(Debug, PartialEq, Clone)]
63 #[cfg_attr(feature = "serialize", derive(Serialize))]
64 pub enum SdpProtocolValue {
65 RtpAvp, /* RTP/AVP [RFC4566] */
66 RtpAvpf, /* RTP/AVPF [RFC4585] */
67 RtpSavp, /* RTP/SAVP [RFC3711] */
68 RtpSavpf, /* RTP/SAVPF [RFC5124] */
69 TcpDtlsRtpSavp, /* TCP/DTLS/RTP/SAVP [RFC7850] */
70 TcpDtlsRtpSavpf, /* TCP/DTLS/RTP/SAVPF [RFC7850] */
71 UdpTlsRtpSavp, /* UDP/TLS/RTP/SAVP [RFC5764] */
72 UdpTlsRtpSavpf, /* UDP/TLS/RTP/SAVPF [RFC5764] */
73 DtlsSctp, /* DTLS/SCTP [draft-ietf-mmusic-sctp-sdp-07] */
74 UdpDtlsSctp, /* UDP/DTLS/SCTP [draft-ietf-mmusic-sctp-sdp-26] */
75 TcpDtlsSctp, /* TCP/DTLS/SCTP [draft-ietf-mmusic-sctp-sdp-26] */
76 }
77
78 impl fmt::Display for SdpProtocolValue {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result79 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
80 match *self {
81 SdpProtocolValue::RtpAvp => "RTP/AVP",
82 SdpProtocolValue::RtpAvpf => "RTP/AVPF",
83 SdpProtocolValue::RtpSavp => "RTP/SAVP",
84 SdpProtocolValue::RtpSavpf => "RTP/SAVPF",
85 SdpProtocolValue::TcpDtlsRtpSavp => "TCP/DTLS/RTP/SAVP",
86 SdpProtocolValue::TcpDtlsRtpSavpf => "TCP/DTLS/RTP/SAVPF",
87 SdpProtocolValue::UdpTlsRtpSavp => "UDP/TLS/RTP/SAVP",
88 SdpProtocolValue::UdpTlsRtpSavpf => "UDP/TLS/RTP/SAVPF",
89 SdpProtocolValue::DtlsSctp => "DTLS/SCTP",
90 SdpProtocolValue::UdpDtlsSctp => "UDP/DTLS/SCTP",
91 SdpProtocolValue::TcpDtlsSctp => "TCP/DTLS/SCTP",
92 }
93 .fmt(f)
94 }
95 }
96
97 #[derive(Clone)]
98 #[cfg_attr(feature = "serialize", derive(Serialize))]
99 #[cfg_attr(feature = "enhanced_debug", derive(Debug))]
100 pub enum SdpFormatList {
101 Integers(Vec<u32>),
102 Strings(Vec<String>),
103 }
104
105 impl fmt::Display for SdpFormatList {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result106 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
107 match *self {
108 SdpFormatList::Integers(ref x) => maybe_vector_to_string!("{}", x, " "),
109 SdpFormatList::Strings(ref x) => x.join(" "),
110 }
111 .fmt(f)
112 }
113 }
114
115 /*
116 * RFC4566
117 * media-descriptions = *( media-field
118 * information-field
119 * *connection-field
120 * bandwidth-fields
121 * key-field
122 * attribute-fields )
123 */
124 #[derive(Clone)]
125 #[cfg_attr(feature = "serialize", derive(Serialize))]
126 #[cfg_attr(feature = "enhanced_debug", derive(Debug))]
127 pub struct SdpMedia {
128 media: SdpMediaLine,
129 connection: Option<SdpConnection>,
130 bandwidth: Vec<SdpBandwidth>,
131 attribute: Vec<SdpAttribute>,
132 // unsupported values:
133 // information: Option<String>,
134 // key: Option<String>,
135 }
136
137 impl fmt::Display for SdpMedia {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result138 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
139 write!(
140 f,
141 "m={mline}\r\n{bw}{connection}{attributes}",
142 mline = self.media,
143 bw = maybe_vector_to_string!("b={}\r\n", self.bandwidth, "\r\nb="),
144 connection = option_to_string!("c={}\r\n", self.connection),
145 attributes = maybe_vector_to_string!("a={}\r\n", self.attribute, "\r\na=")
146 )
147 }
148 }
149
150 impl SdpMedia {
new(media: SdpMediaLine) -> SdpMedia151 pub fn new(media: SdpMediaLine) -> SdpMedia {
152 SdpMedia {
153 media,
154 connection: None,
155 bandwidth: Vec::new(),
156 attribute: Vec::new(),
157 }
158 }
159
get_type(&self) -> &SdpMediaValue160 pub fn get_type(&self) -> &SdpMediaValue {
161 &self.media.media
162 }
163
set_port(&mut self, port: u32)164 pub fn set_port(&mut self, port: u32) {
165 self.media.port = port;
166 }
167
get_port(&self) -> u32168 pub fn get_port(&self) -> u32 {
169 self.media.port
170 }
171
get_port_count(&self) -> u32172 pub fn get_port_count(&self) -> u32 {
173 self.media.port_count
174 }
175
get_proto(&self) -> &SdpProtocolValue176 pub fn get_proto(&self) -> &SdpProtocolValue {
177 &self.media.proto
178 }
179
get_formats(&self) -> &SdpFormatList180 pub fn get_formats(&self) -> &SdpFormatList {
181 &self.media.formats
182 }
183
get_bandwidth(&self) -> &Vec<SdpBandwidth>184 pub fn get_bandwidth(&self) -> &Vec<SdpBandwidth> {
185 &self.bandwidth
186 }
187
add_bandwidth(&mut self, bw: SdpBandwidth)188 pub fn add_bandwidth(&mut self, bw: SdpBandwidth) {
189 self.bandwidth.push(bw)
190 }
191
get_attributes(&self) -> &Vec<SdpAttribute>192 pub fn get_attributes(&self) -> &Vec<SdpAttribute> {
193 &self.attribute
194 }
195
add_attribute(&mut self, attr: SdpAttribute) -> Result<(), SdpParserInternalError>196 pub fn add_attribute(&mut self, attr: SdpAttribute) -> Result<(), SdpParserInternalError> {
197 if !attr.allowed_at_media_level() {
198 return Err(SdpParserInternalError::Generic(format!(
199 "{} not allowed at media level",
200 attr
201 )));
202 }
203 self.attribute.push(attr);
204 Ok(())
205 }
206
get_attribute(&self, t: SdpAttributeType) -> Option<&SdpAttribute>207 pub fn get_attribute(&self, t: SdpAttributeType) -> Option<&SdpAttribute> {
208 self.attribute
209 .iter()
210 .find(|a| SdpAttributeType::from(*a) == t)
211 }
212
remove_attribute(&mut self, t: SdpAttributeType)213 pub fn remove_attribute(&mut self, t: SdpAttributeType) {
214 self.attribute.retain(|a| SdpAttributeType::from(a) != t);
215 }
216
set_attribute(&mut self, attr: SdpAttribute) -> Result<(), SdpParserInternalError>217 pub fn set_attribute(&mut self, attr: SdpAttribute) -> Result<(), SdpParserInternalError> {
218 self.remove_attribute(SdpAttributeType::from(&attr));
219 self.add_attribute(attr)
220 }
221
remove_codecs(&mut self)222 pub fn remove_codecs(&mut self) {
223 match self.media.formats {
224 SdpFormatList::Integers(_) => self.media.formats = SdpFormatList::Integers(Vec::new()),
225 SdpFormatList::Strings(_) => self.media.formats = SdpFormatList::Strings(Vec::new()),
226 }
227
228 self.attribute.retain({
229 |x| {
230 !matches!(
231 *x,
232 SdpAttribute::Rtpmap(_)
233 | SdpAttribute::Fmtp(_)
234 | SdpAttribute::Rtcpfb(_)
235 | SdpAttribute::Sctpmap(_)
236 | SdpAttribute::SctpPort(_)
237 )
238 }
239 });
240 }
241
add_codec(&mut self, rtpmap: SdpAttributeRtpmap) -> Result<(), SdpParserInternalError>242 pub fn add_codec(&mut self, rtpmap: SdpAttributeRtpmap) -> Result<(), SdpParserInternalError> {
243 match self.media.formats {
244 SdpFormatList::Integers(ref mut x) => x.push(u32::from(rtpmap.payload_type)),
245 SdpFormatList::Strings(ref mut x) => x.push(rtpmap.payload_type.to_string()),
246 }
247
248 self.add_attribute(SdpAttribute::Rtpmap(rtpmap))?;
249 Ok(())
250 }
251
get_attributes_of_type(&self, t: SdpAttributeType) -> Vec<&SdpAttribute>252 pub fn get_attributes_of_type(&self, t: SdpAttributeType) -> Vec<&SdpAttribute> {
253 self.attribute
254 .iter()
255 .filter(|a| SdpAttributeType::from(*a) == t)
256 .collect()
257 }
258
get_connection(&self) -> &Option<SdpConnection>259 pub fn get_connection(&self) -> &Option<SdpConnection> {
260 &self.connection
261 }
262
set_connection(&mut self, c: SdpConnection)263 pub fn set_connection(&mut self, c: SdpConnection) {
264 self.connection = Some(c)
265 }
266
add_datachannel( &mut self, name: String, port: u16, streams: u16, msg_size: u32, ) -> Result<(), SdpParserInternalError>267 pub fn add_datachannel(
268 &mut self,
269 name: String,
270 port: u16,
271 streams: u16,
272 msg_size: u32,
273 ) -> Result<(), SdpParserInternalError> {
274 // Only one allowed, for now. This may change as the specs (and deployments) evolve.
275 match self.media.proto {
276 SdpProtocolValue::UdpDtlsSctp | SdpProtocolValue::TcpDtlsSctp => {
277 // new data channel format according to draft 21
278 self.media.formats = SdpFormatList::Strings(vec![name]);
279 self.set_attribute(SdpAttribute::SctpPort(u64::from(port)))?;
280 }
281 _ => {
282 // old data channels format according to draft 05
283 self.media.formats = SdpFormatList::Integers(vec![u32::from(port)]);
284 self.set_attribute(SdpAttribute::Sctpmap(SdpAttributeSctpmap {
285 port,
286 channels: u32::from(streams),
287 }))?;
288 }
289 }
290 if msg_size > 0 {
291 self.set_attribute(SdpAttribute::MaxMessageSize(u64::from(msg_size)))?;
292 }
293 self.media.media = SdpMediaValue::Application;
294
295 Ok(())
296 }
297 }
298
299 impl AnonymizingClone for SdpMedia {
masked_clone(&self, anon: &mut StatefulSdpAnonymizer) -> Self300 fn masked_clone(&self, anon: &mut StatefulSdpAnonymizer) -> Self {
301 let mut masked = SdpMedia {
302 media: self.media.clone(),
303 bandwidth: self.bandwidth.clone(),
304 connection: self.connection.clone(),
305 attribute: Vec::new(),
306 };
307 for i in &self.attribute {
308 masked.attribute.push(i.masked_clone(anon));
309 }
310 masked
311 }
312 }
313
parse_media_token(value: &str) -> Result<SdpMediaValue, SdpParserInternalError>314 fn parse_media_token(value: &str) -> Result<SdpMediaValue, SdpParserInternalError> {
315 Ok(match value.to_lowercase().as_ref() {
316 "audio" => SdpMediaValue::Audio,
317 "video" => SdpMediaValue::Video,
318 "application" => SdpMediaValue::Application,
319 _ => {
320 return Err(SdpParserInternalError::Unsupported(format!(
321 "unsupported media value: {}",
322 value
323 )));
324 }
325 })
326 }
327
parse_protocol_token(value: &str) -> Result<SdpProtocolValue, SdpParserInternalError>328 fn parse_protocol_token(value: &str) -> Result<SdpProtocolValue, SdpParserInternalError> {
329 Ok(match value.to_uppercase().as_ref() {
330 "RTP/AVP" => SdpProtocolValue::RtpAvp,
331 "RTP/AVPF" => SdpProtocolValue::RtpAvpf,
332 "RTP/SAVP" => SdpProtocolValue::RtpSavp,
333 "RTP/SAVPF" => SdpProtocolValue::RtpSavpf,
334 "TCP/DTLS/RTP/SAVP" => SdpProtocolValue::TcpDtlsRtpSavp,
335 "TCP/DTLS/RTP/SAVPF" => SdpProtocolValue::TcpDtlsRtpSavpf,
336 "UDP/TLS/RTP/SAVP" => SdpProtocolValue::UdpTlsRtpSavp,
337 "UDP/TLS/RTP/SAVPF" => SdpProtocolValue::UdpTlsRtpSavpf,
338 "DTLS/SCTP" => SdpProtocolValue::DtlsSctp,
339 "UDP/DTLS/SCTP" => SdpProtocolValue::UdpDtlsSctp,
340 "TCP/DTLS/SCTP" => SdpProtocolValue::TcpDtlsSctp,
341 _ => {
342 return Err(SdpParserInternalError::Unsupported(format!(
343 "unsupported protocol value: {}",
344 value
345 )));
346 }
347 })
348 }
349
parse_media(value: &str) -> Result<SdpType, SdpParserInternalError>350 pub fn parse_media(value: &str) -> Result<SdpType, SdpParserInternalError> {
351 let mv: Vec<&str> = value.split_whitespace().collect();
352 if mv.len() < 4 {
353 return Err(SdpParserInternalError::Generic(
354 "media attribute must have at least four tokens".to_string(),
355 ));
356 }
357 let media = parse_media_token(mv[0])?;
358 let mut ptokens = mv[1].split('/');
359 let port = match ptokens.next() {
360 None => {
361 return Err(SdpParserInternalError::Generic(
362 "missing port token".to_string(),
363 ));
364 }
365 Some(p) => p.parse::<u32>()?,
366 };
367 if port > 65535 {
368 return Err(SdpParserInternalError::Generic(
369 "media port token is too big".to_string(),
370 ));
371 }
372 let port_count = match ptokens.next() {
373 None => 0,
374 Some(c) => c.parse::<u32>()?,
375 };
376 let proto = parse_protocol_token(mv[2])?;
377 let fmt_slice: &[&str] = &mv[3..];
378 let formats = match media {
379 SdpMediaValue::Audio | SdpMediaValue::Video => {
380 let mut fmt_vec: Vec<u32> = vec![];
381 for num in fmt_slice {
382 let fmt_num = num.parse::<u32>()?;
383 match fmt_num {
384 0 | // PCMU
385 8 | // PCMA
386 9 | // G722
387 13 | // Comfort Noise
388 35 ..= 63 | 96 ..= 127 => (), // dynamic range
389 _ => return Err(SdpParserInternalError::Generic(
390 "format number in media line is out of range".to_string()))
391 };
392 fmt_vec.push(fmt_num);
393 }
394 SdpFormatList::Integers(fmt_vec)
395 }
396 SdpMediaValue::Application => {
397 let mut fmt_vec: Vec<String> = vec![];
398 // TODO enforce length == 1 and content 'webrtc-datachannel' only?
399 for token in fmt_slice {
400 fmt_vec.push(String::from(*token));
401 }
402 SdpFormatList::Strings(fmt_vec)
403 }
404 };
405 let m = SdpMediaLine {
406 media,
407 port,
408 port_count,
409 proto,
410 formats,
411 };
412 trace!("media: {}, {}, {}, {}", m.media, m.port, m.proto, m.formats);
413 Ok(SdpType::Media(m))
414 }
415
parse_media_vector(lines: &mut Vec<SdpLine>) -> Result<Vec<SdpMedia>, SdpParserError>416 pub fn parse_media_vector(lines: &mut Vec<SdpLine>) -> Result<Vec<SdpMedia>, SdpParserError> {
417 let mut media_sections: Vec<SdpMedia> = Vec::new();
418
419 let media_line = lines.remove(0);
420 let mut sdp_media = match media_line.sdp_type {
421 SdpType::Media(v) => SdpMedia::new(v),
422 _ => {
423 return Err(SdpParserError::Sequence {
424 message: "first line in media section needs to be a media line".to_string(),
425 line_number: media_line.line_number,
426 });
427 }
428 };
429
430 while !lines.is_empty() {
431 let line = lines.remove(0);
432 let _line_number = line.line_number;
433 match line.sdp_type {
434 SdpType::Connection(c) => {
435 if sdp_media.connection.is_some() {
436 return Err(SdpParserError::Sequence {
437 message: "connection type already exists at this media level".to_string(),
438 line_number: _line_number,
439 });
440 }
441
442 sdp_media.set_connection(c);
443 }
444 SdpType::Bandwidth(b) => sdp_media.add_bandwidth(b),
445 SdpType::Attribute(a) => {
446 match a {
447 SdpAttribute::DtlsMessage(_) => {
448 // Ignore this attribute on media level
449 Ok(())
450 }
451 SdpAttribute::Rtpmap(rtpmap) => {
452 sdp_media.add_attribute(SdpAttribute::Rtpmap(SdpAttributeRtpmap {
453 payload_type: rtpmap.payload_type,
454 codec_name: rtpmap.codec_name.clone(),
455 frequency: rtpmap.frequency,
456 channels: rtpmap.channels,
457 }))
458 }
459 _ => sdp_media.add_attribute(a),
460 }
461 .map_err(|e: SdpParserInternalError| SdpParserError::Sequence {
462 message: format!("{}", e),
463 line_number: _line_number,
464 })?
465 }
466 SdpType::Media(v) => {
467 media_sections.push(sdp_media);
468 sdp_media = SdpMedia::new(v);
469 }
470
471 SdpType::Origin(_) | SdpType::Session(_) | SdpType::Timing(_) | SdpType::Version(_) => {
472 return Err(SdpParserError::Sequence {
473 message: "invalid type in media section".to_string(),
474 line_number: line.line_number,
475 });
476 }
477 };
478 }
479
480 media_sections.push(sdp_media);
481
482 Ok(media_sections)
483 }
484
485 #[cfg(test)]
create_dummy_media_section() -> SdpMedia486 pub fn create_dummy_media_section() -> SdpMedia {
487 let media_line = SdpMediaLine {
488 media: SdpMediaValue::Audio,
489 port: 9,
490 port_count: 0,
491 proto: SdpProtocolValue::RtpSavpf,
492 formats: SdpFormatList::Integers(Vec::new()),
493 };
494 SdpMedia::new(media_line)
495 }
496
497 #[cfg(test)]
498 mod tests {
499 use super::*;
500 use address::{AddressType, ExplicitlyTypedAddress};
501 use attribute_type::{
502 SdpAttributeFmtp, SdpAttributeFmtpParameters, SdpAttributePayloadType, SdpAttributeRtcpFb,
503 SdpAttributeRtcpFbType,
504 };
505 use std::convert::TryFrom;
506
507 // TODO is this useful outside of tests?
508 impl SdpFormatList {
len(&self) -> usize509 fn len(&self) -> usize {
510 match *self {
511 SdpFormatList::Integers(ref x) => x.len(),
512 SdpFormatList::Strings(ref x) => x.len(),
513 }
514 }
515 }
516
add_dummy_attributes(media: &mut SdpMedia)517 pub fn add_dummy_attributes(media: &mut SdpMedia) {
518 assert!(media
519 .add_attribute(SdpAttribute::Rtcpfb(SdpAttributeRtcpFb {
520 payload_type: SdpAttributePayloadType::Wildcard,
521 feedback_type: SdpAttributeRtcpFbType::Ack,
522 parameter: "".to_string(),
523 extra: "".to_string(),
524 },))
525 .is_ok());
526 assert!(media
527 .add_attribute(SdpAttribute::Fmtp(SdpAttributeFmtp {
528 payload_type: 1,
529 parameters: SdpAttributeFmtpParameters {
530 packetization_mode: 0,
531 level_asymmetry_allowed: false,
532 profile_level_id: 0x0042_0010,
533 max_fs: 0,
534 max_cpb: 0,
535 max_dpb: 0,
536 max_br: 0,
537 max_mbps: 0,
538 usedtx: false,
539 stereo: false,
540 useinbandfec: false,
541 cbr: false,
542 max_fr: 0,
543 maxplaybackrate: 48000,
544 maxaveragebitrate: 0,
545 ptime: 0,
546 minptime: 0,
547 maxptime: 0,
548 encodings: Vec::new(),
549 dtmf_tones: "".to_string(),
550 rtx: None,
551 unknown_tokens: Vec::new()
552 }
553 },))
554 .is_ok());
555 assert!(media
556 .add_attribute(SdpAttribute::Sctpmap(SdpAttributeSctpmap {
557 port: 5000,
558 channels: 2,
559 }))
560 .is_ok());
561 assert!(media.add_attribute(SdpAttribute::BundleOnly).is_ok());
562 assert!(media.add_attribute(SdpAttribute::SctpPort(5000)).is_ok());
563
564 assert!(media.get_attribute(SdpAttributeType::Rtpmap).is_some());
565 assert!(media.get_attribute(SdpAttributeType::Rtcpfb).is_some());
566 assert!(media.get_attribute(SdpAttributeType::Fmtp).is_some());
567 assert!(media.get_attribute(SdpAttributeType::Sctpmap).is_some());
568 assert!(media.get_attribute(SdpAttributeType::SctpPort).is_some());
569 assert!(media.get_attribute(SdpAttributeType::BundleOnly).is_some());
570 }
571
check_parse(media_line_str: &str) -> SdpMediaLine572 fn check_parse(media_line_str: &str) -> SdpMediaLine {
573 if let Ok(SdpType::Media(media_line)) = parse_media(media_line_str) {
574 media_line
575 } else {
576 unreachable!()
577 }
578 }
579
check_parse_and_serialize(media_line_str: &str)580 fn check_parse_and_serialize(media_line_str: &str) {
581 let parsed = check_parse(media_line_str);
582 assert_eq!(parsed.to_string(), media_line_str.to_string());
583 }
584
585 #[test]
test_get_set_port()586 fn test_get_set_port() {
587 let mut msection = create_dummy_media_section();
588 assert_eq!(msection.get_port(), 9);
589 msection.set_port(2048);
590 assert_eq!(msection.get_port(), 2048);
591 }
592
593 #[test]
test_add_codec() -> Result<(), SdpParserInternalError>594 fn test_add_codec() -> Result<(), SdpParserInternalError> {
595 let mut msection = create_dummy_media_section();
596 msection.add_codec(SdpAttributeRtpmap::new(96, "foobar".to_string(), 1000))?;
597 assert_eq!(msection.get_formats().len(), 1);
598 assert!(msection.get_attribute(SdpAttributeType::Rtpmap).is_some());
599
600 let mut msection = create_dummy_media_section();
601 msection.media.formats = SdpFormatList::Strings(Vec::new());
602 msection.add_codec(SdpAttributeRtpmap::new(97, "boofar".to_string(), 1001))?;
603 assert_eq!(msection.get_formats().len(), 1);
604 assert!(msection.get_attribute(SdpAttributeType::Rtpmap).is_some());
605 Ok(())
606 }
607
608 #[test]
test_remove_codecs() -> Result<(), SdpParserInternalError>609 fn test_remove_codecs() -> Result<(), SdpParserInternalError> {
610 let mut msection = create_dummy_media_section();
611 msection.add_codec(SdpAttributeRtpmap::new(96, "foobar".to_string(), 1000))?;
612 assert_eq!(msection.get_formats().len(), 1);
613 assert!(msection.get_attribute(SdpAttributeType::Rtpmap).is_some());
614 msection.remove_codecs();
615 assert_eq!(msection.get_formats().len(), 0);
616 assert!(msection.get_attribute(SdpAttributeType::Rtpmap).is_none());
617
618 let mut msection = create_dummy_media_section();
619 msection.media.formats = SdpFormatList::Strings(Vec::new());
620 msection.add_codec(SdpAttributeRtpmap::new(97, "boofar".to_string(), 1001))?;
621 assert_eq!(msection.get_formats().len(), 1);
622
623 add_dummy_attributes(&mut msection);
624
625 msection.remove_codecs();
626 assert_eq!(msection.get_formats().len(), 0);
627 assert!(msection.get_attribute(SdpAttributeType::Rtpmap).is_none());
628 assert!(msection.get_attribute(SdpAttributeType::Rtcpfb).is_none());
629 assert!(msection.get_attribute(SdpAttributeType::Fmtp).is_none());
630 assert!(msection.get_attribute(SdpAttributeType::Sctpmap).is_none());
631 assert!(msection.get_attribute(SdpAttributeType::SctpPort).is_none());
632 Ok(())
633 }
634
635 #[test]
test_add_datachannel() -> Result<(), SdpParserInternalError>636 fn test_add_datachannel() -> Result<(), SdpParserInternalError> {
637 let mut msection = create_dummy_media_section();
638 msection.add_datachannel("foo".to_string(), 5000, 256, 0)?;
639 assert_eq!(*msection.get_type(), SdpMediaValue::Application);
640 assert!(msection.get_attribute(SdpAttributeType::SctpPort).is_none());
641 assert!(msection
642 .get_attribute(SdpAttributeType::MaxMessageSize)
643 .is_none());
644 assert!(msection.get_attribute(SdpAttributeType::Sctpmap).is_some());
645 match *msection.get_attribute(SdpAttributeType::Sctpmap).unwrap() {
646 SdpAttribute::Sctpmap(ref s) => {
647 assert_eq!(s.port, 5000);
648 assert_eq!(s.channels, 256);
649 }
650 _ => unreachable!(),
651 }
652
653 let mut msection = create_dummy_media_section();
654 msection.add_datachannel("foo".to_string(), 5000, 256, 1234)?;
655 assert_eq!(*msection.get_type(), SdpMediaValue::Application);
656 assert!(msection.get_attribute(SdpAttributeType::SctpPort).is_none());
657 assert!(msection
658 .get_attribute(SdpAttributeType::MaxMessageSize)
659 .is_some());
660 match *msection
661 .get_attribute(SdpAttributeType::MaxMessageSize)
662 .unwrap()
663 {
664 SdpAttribute::MaxMessageSize(m) => {
665 assert_eq!(m, 1234);
666 }
667 _ => unreachable!(),
668 }
669
670 let mut msection = create_dummy_media_section();
671 msection.media.proto = SdpProtocolValue::UdpDtlsSctp;
672 msection.add_datachannel("foo".to_string(), 5000, 256, 5678)?;
673 assert_eq!(*msection.get_type(), SdpMediaValue::Application);
674 assert!(msection.get_attribute(SdpAttributeType::Sctpmap).is_none());
675 assert!(msection.get_attribute(SdpAttributeType::SctpPort).is_some());
676 match *msection.get_attribute(SdpAttributeType::SctpPort).unwrap() {
677 SdpAttribute::SctpPort(s) => {
678 assert_eq!(s, 5000);
679 }
680 _ => unreachable!(),
681 }
682 assert!(msection
683 .get_attribute(SdpAttributeType::MaxMessageSize)
684 .is_some());
685 match *msection
686 .get_attribute(SdpAttributeType::MaxMessageSize)
687 .unwrap()
688 {
689 SdpAttribute::MaxMessageSize(m) => {
690 assert_eq!(m, 5678);
691 }
692 _ => unreachable!(),
693 }
694 Ok(())
695 }
696
697 #[test]
test_parse_media_token() -> Result<(), SdpParserInternalError>698 fn test_parse_media_token() -> Result<(), SdpParserInternalError> {
699 let audio = parse_media_token("audio")?;
700 assert_eq!(audio, SdpMediaValue::Audio);
701 let video = parse_media_token("VIDEO")?;
702 assert_eq!(video, SdpMediaValue::Video);
703 let app = parse_media_token("aPplIcatIOn")?;
704 assert_eq!(app, SdpMediaValue::Application);
705
706 assert!(parse_media_token("").is_err());
707 assert!(parse_media_token("foobar").is_err());
708 Ok(())
709 }
710
711 #[test]
test_parse_protocol_rtp_token() -> Result<(), SdpParserInternalError>712 fn test_parse_protocol_rtp_token() -> Result<(), SdpParserInternalError> {
713 fn parse_and_serialize_protocol_token(
714 token: &str,
715 result: SdpProtocolValue,
716 ) -> Result<(), SdpParserInternalError> {
717 let rtps = parse_protocol_token(token)?;
718 assert_eq!(rtps, result);
719 assert_eq!(rtps.to_string(), token.to_uppercase());
720 Ok(())
721 }
722 parse_and_serialize_protocol_token("rtp/avp", SdpProtocolValue::RtpAvp)?;
723 parse_and_serialize_protocol_token("rtp/avpf", SdpProtocolValue::RtpAvpf)?;
724 parse_and_serialize_protocol_token("rtp/savp", SdpProtocolValue::RtpSavp)?;
725 parse_and_serialize_protocol_token("rtp/savpf", SdpProtocolValue::RtpSavpf)?;
726 parse_and_serialize_protocol_token("udp/tls/rtp/savp", SdpProtocolValue::UdpTlsRtpSavp)?;
727 parse_and_serialize_protocol_token("udp/tls/rtp/savpf", SdpProtocolValue::UdpTlsRtpSavpf)?;
728 parse_and_serialize_protocol_token("TCP/dtls/rtp/savp", SdpProtocolValue::TcpDtlsRtpSavp)?;
729 parse_and_serialize_protocol_token(
730 "tcp/DTLS/rtp/savpf",
731 SdpProtocolValue::TcpDtlsRtpSavpf,
732 )?;
733
734 assert!(parse_protocol_token("").is_err());
735 assert!(parse_protocol_token("foobar").is_err());
736 Ok(())
737 }
738
739 #[test]
test_parse_protocol_sctp_token() -> Result<(), SdpParserInternalError>740 fn test_parse_protocol_sctp_token() -> Result<(), SdpParserInternalError> {
741 fn parse_and_serialize_protocol_token(
742 token: &str,
743 result: SdpProtocolValue,
744 ) -> Result<(), SdpParserInternalError> {
745 let rtps = parse_protocol_token(token)?;
746 assert_eq!(rtps, result);
747 assert_eq!(rtps.to_string(), token.to_uppercase());
748 Ok(())
749 }
750 parse_and_serialize_protocol_token("dtLs/ScTP", SdpProtocolValue::DtlsSctp)?;
751 parse_and_serialize_protocol_token("udp/DTLS/sctp", SdpProtocolValue::UdpDtlsSctp)?;
752 parse_and_serialize_protocol_token("tcp/dtls/SCTP", SdpProtocolValue::TcpDtlsSctp)?;
753 Ok(())
754 }
755
756 #[test]
test_media_works()757 fn test_media_works() {
758 check_parse_and_serialize("audio 9 UDP/TLS/RTP/SAVPF 109");
759 check_parse_and_serialize("video 9 UDP/TLS/RTP/SAVPF 126");
760 check_parse_and_serialize("application 9 DTLS/SCTP 5000");
761 check_parse_and_serialize("application 9 UDP/DTLS/SCTP webrtc-datachannel");
762
763 check_parse_and_serialize("audio 9 UDP/TLS/RTP/SAVPF 109 9 0 8");
764 check_parse_and_serialize("audio 0 UDP/TLS/RTP/SAVPF 8");
765 check_parse_and_serialize("audio 9/2 UDP/TLS/RTP/SAVPF 8");
766 }
767
768 #[test]
test_media_missing_token()769 fn test_media_missing_token() {
770 assert!(parse_media("video 9 UDP/TLS/RTP/SAVPF").is_err());
771 }
772
773 #[test]
test_media_invalid_port_number()774 fn test_media_invalid_port_number() {
775 assert!(parse_media("video 75123 UDP/TLS/RTP/SAVPF 8").is_err());
776 }
777
778 #[test]
test_media_invalid_type()779 fn test_media_invalid_type() {
780 assert!(parse_media("invalid 9 UDP/TLS/RTP/SAVPF 8").is_err());
781 }
782
783 #[test]
test_media_invalid_port()784 fn test_media_invalid_port() {
785 assert!(parse_media("audio / UDP/TLS/RTP/SAVPF 8").is_err());
786 }
787
788 #[test]
test_media_invalid_transport()789 fn test_media_invalid_transport() {
790 assert!(parse_media("audio 9 invalid/invalid 8").is_err());
791 }
792
793 #[test]
test_media_invalid_payload()794 fn test_media_invalid_payload() {
795 assert!(parse_media("audio 9 UDP/TLS/RTP/SAVPF 300").is_err());
796 }
797
798 #[test]
test_media_vector_first_line_failure()799 fn test_media_vector_first_line_failure() {
800 let mut sdp_lines: Vec<SdpLine> = Vec::new();
801 let line = SdpLine {
802 line_number: 0,
803 sdp_type: SdpType::Session("hello".to_string()),
804 text: "".to_owned(),
805 };
806 sdp_lines.push(line);
807 assert!(parse_media_vector(&mut sdp_lines).is_err());
808 }
809
810 #[test]
test_media_vector_multiple_connections()811 fn test_media_vector_multiple_connections() {
812 let mut sdp_lines: Vec<SdpLine> = Vec::new();
813 let media_line = SdpMediaLine {
814 media: SdpMediaValue::Audio,
815 port: 9,
816 port_count: 0,
817 proto: SdpProtocolValue::RtpSavpf,
818 formats: SdpFormatList::Integers(Vec::new()),
819 };
820 let media = SdpLine {
821 line_number: 0,
822 sdp_type: SdpType::Media(media_line),
823 text: "".to_owned(),
824 };
825 sdp_lines.push(media);
826 let c = SdpConnection {
827 address: ExplicitlyTypedAddress::try_from((AddressType::IpV4, "127.0.0.1")).unwrap(),
828 ttl: None,
829 amount: None,
830 };
831 let c1 = SdpLine {
832 line_number: 1,
833 sdp_type: SdpType::Connection(c.clone()),
834 text: "".to_owned(),
835 };
836 sdp_lines.push(c1);
837 let c2 = SdpLine {
838 line_number: 2,
839 sdp_type: SdpType::Connection(c),
840 text: "".to_owned(),
841 };
842 sdp_lines.push(c2);
843 assert!(parse_media_vector(&mut sdp_lines).is_err());
844 }
845
846 #[test]
test_media_vector_invalid_types()847 fn test_media_vector_invalid_types() {
848 let mut sdp_lines: Vec<SdpLine> = Vec::new();
849 let media_line = SdpMediaLine {
850 media: SdpMediaValue::Audio,
851 port: 9,
852 port_count: 0,
853 proto: SdpProtocolValue::RtpSavpf,
854 formats: SdpFormatList::Integers(Vec::new()),
855 };
856 let media = SdpLine {
857 line_number: 0,
858 sdp_type: SdpType::Media(media_line),
859 text: "".to_owned(),
860 };
861 sdp_lines.push(media);
862 use SdpTiming;
863 let t = SdpTiming { start: 0, stop: 0 };
864 let tline = SdpLine {
865 line_number: 1,
866 sdp_type: SdpType::Timing(t),
867 text: "".to_owned(),
868 };
869 sdp_lines.push(tline);
870 assert!(parse_media_vector(&mut sdp_lines).is_err());
871 }
872
873 #[test]
test_media_vector_invalid_media_level_attribute()874 fn test_media_vector_invalid_media_level_attribute() {
875 let mut sdp_lines: Vec<SdpLine> = Vec::new();
876 let media_line = SdpMediaLine {
877 media: SdpMediaValue::Audio,
878 port: 9,
879 port_count: 0,
880 proto: SdpProtocolValue::RtpSavpf,
881 formats: SdpFormatList::Integers(Vec::new()),
882 };
883 let media = SdpLine {
884 line_number: 0,
885 sdp_type: SdpType::Media(media_line),
886 text: "".to_owned(),
887 };
888 sdp_lines.push(media);
889 let a = SdpAttribute::IceLite;
890 let aline = SdpLine {
891 line_number: 1,
892 sdp_type: SdpType::Attribute(a),
893 text: "".to_owned(),
894 };
895 sdp_lines.push(aline);
896 assert!(parse_media_vector(&mut sdp_lines).is_err());
897 }
898 }
899