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 extern crate webrtc_sdp;
6 
7 #[cfg(test)]
check_parse_and_serialize(sdp_str: &str)8 fn check_parse_and_serialize(sdp_str: &str) {
9     let parsed_sdp = webrtc_sdp::parse_sdp(sdp_str, true);
10     assert!(parsed_sdp.is_ok());
11     let serialized_sdp = parsed_sdp.unwrap().to_string();
12     assert_eq!(serialized_sdp, sdp_str)
13 }
14 
15 #[test]
parse_minimal_sdp()16 fn parse_minimal_sdp() {
17     let sdp_str = "v=0\r\n\
18                    o=- 1 1 IN IP4 0.0.0.0\r\n\
19                    s=-\r\n\
20                    t=0 0\r\n\
21                    c=IN IP4 0.0.0.0\r\n\
22                    m=audio 0 UDP/TLS/RTP/SAVPF 0\r\n";
23     let sdp_res = webrtc_sdp::parse_sdp(sdp_str, true);
24     assert!(sdp_res.is_ok());
25     let sdp_opt = sdp_res.ok();
26     assert!(sdp_opt.is_some());
27     let sdp = sdp_opt.unwrap();
28     assert_eq!(sdp.get_version(), 0);
29     let o = sdp.get_origin();
30     assert_eq!(o.username, "-");
31     assert_eq!(o.session_id, 1);
32     assert_eq!(o.session_version, 1);
33     assert_eq!(sdp.get_session(), &Some("-".to_owned()));
34     assert!(sdp.timing.is_some());
35     assert!(sdp.get_connection().is_some());
36     assert_eq!(sdp.attribute.len(), 0);
37     assert_eq!(sdp.media.len(), 1);
38 
39     let msection = &(sdp.media[0]);
40     assert_eq!(
41         *msection.get_type(),
42         webrtc_sdp::media_type::SdpMediaValue::Audio
43     );
44     assert_eq!(msection.get_port(), 0);
45     assert_eq!(msection.get_port_count(), 0);
46     assert_eq!(
47         *msection.get_proto(),
48         webrtc_sdp::media_type::SdpProtocolValue::UdpTlsRtpSavpf
49     );
50     assert!(msection.get_attributes().is_empty());
51     assert!(msection.get_bandwidth().is_empty());
52     assert!(msection.get_connection().is_none());
53 
54     check_parse_and_serialize(sdp_str);
55 }
56 
57 #[test]
parse_minimal_sdp_with_emtpy_lines()58 fn parse_minimal_sdp_with_emtpy_lines() {
59     let sdp = "v=0\r\n
60 \r\n
61 o=- 0 0 IN IP4 0.0.0.0\r\n
62  \r\n
63 s=-\r\n
64 c=IN IP4 0.0.0.0\r\n
65 t=0 0\r\n
66 m=audio 0 UDP/TLS/RTP/SAVPF 0\r\n";
67     let sdp_res = webrtc_sdp::parse_sdp(sdp, false);
68     assert!(sdp_res.is_ok());
69     let sdp_opt = sdp_res.ok();
70     assert!(sdp_opt.is_some());
71     let sdp = sdp_opt.unwrap();
72     assert_eq!(sdp.get_version(), 0);
73     assert_eq!(sdp.get_session(), &Some("-".to_owned()));
74 }
75 
76 #[test]
parse_minimal_sdp_with_single_space_session()77 fn parse_minimal_sdp_with_single_space_session() {
78     let sdp = "v=0\r\n
79 \r\n
80 o=- 0 0 IN IP4 0.0.0.0\r\n
81  \r\n
82 s= \r\n
83 c=IN IP4 0.0.0.0\r\n
84 t=0 0\r\n
85 m=audio 0 UDP/TLS/RTP/SAVPF 0\r\n";
86     let sdp_res = webrtc_sdp::parse_sdp(sdp, false);
87     assert!(sdp_res.is_ok());
88     let sdp_opt = sdp_res.ok();
89     assert!(sdp_opt.is_some());
90     let sdp = sdp_opt.unwrap();
91     assert_eq!(sdp.get_version(), 0);
92     assert_eq!(sdp.get_session(), &None);
93 }
94 
95 #[test]
parse_minimal_sdp_with_most_session_types()96 fn parse_minimal_sdp_with_most_session_types() {
97     let sdp_str = "v=0\r\n\
98                    o=- 0 0 IN IP4 0.0.0.0\r\n\
99                    s=-\r\n\
100                    t=0 0\r\n\
101                    b=AS:1\r\n\
102                    b=CT:123\r\n\
103                    b=TIAS:12345\r\n\
104                    b=UNKNOWN:9\r\n\
105                    c=IN IP6 ::1/1/1\r\n\
106                    a=ice-options:trickle\r\n\
107                    m=audio 0 UDP/TLS/RTP/SAVPF 0\r\n";
108     let sdp_res = webrtc_sdp::parse_sdp(sdp_str, false);
109     assert!(sdp_res.is_ok());
110     let sdp_opt = sdp_res.ok();
111     assert!(sdp_opt.is_some());
112     let sdp = sdp_opt.unwrap();
113     assert_eq!(sdp.version, 0);
114     assert_eq!(sdp.session, Some("-".to_owned()));
115     assert!(sdp.get_connection().is_some());
116 
117     check_parse_and_serialize(sdp_str);
118 }
119 
120 #[test]
parse_minimal_sdp_with_most_media_types()121 fn parse_minimal_sdp_with_most_media_types() {
122     let sdp_str = "v=0\r\n\
123                    o=- 0 0 IN IP4 0.0.0.0\r\n\
124                    s=-\r\n\
125                    t=0 0\r\n\
126                    m=video 0 UDP/TLS/RTP/SAVPF 0\r\n\
127                    b=AS:1\r\n\
128                    b=CT:123\r\n\
129                    b=TIAS:12345\r\n\
130                    c=IN IP4 0.0.0.0\r\n\
131                    a=sendrecv\r\n";
132     let sdp_res = webrtc_sdp::parse_sdp(sdp_str, false);
133     assert!(sdp_res.is_ok());
134     let sdp_opt = sdp_res.ok();
135     assert!(sdp_opt.is_some());
136     let sdp = sdp_opt.unwrap();
137     assert_eq!(sdp.version, 0);
138     assert_eq!(sdp.session, Some("-".to_owned()));
139     assert_eq!(sdp.attribute.len(), 0);
140     assert_eq!(sdp.media.len(), 1);
141 
142     let msection = &(sdp.media[0]);
143     assert_eq!(
144         *msection.get_type(),
145         webrtc_sdp::media_type::SdpMediaValue::Video
146     );
147     assert_eq!(msection.get_port(), 0);
148     assert_eq!(
149         *msection.get_proto(),
150         webrtc_sdp::media_type::SdpProtocolValue::UdpTlsRtpSavpf
151     );
152     assert!(!msection.get_bandwidth().is_empty());
153     assert!(!msection.get_connection().is_none());
154     assert!(!msection.get_attributes().is_empty());
155     assert!(msection
156         .get_attribute(webrtc_sdp::attribute_type::SdpAttributeType::Sendrecv)
157         .is_some());
158 
159     check_parse_and_serialize(sdp_str);
160 }
161 
162 #[test]
parse_firefox_audio_offer()163 fn parse_firefox_audio_offer() {
164     let sdp_str = "v=0\r\n\
165 o=mozilla...THIS_IS_SDPARTA-52.0a1 506705521068071134 0 IN IP4 0.0.0.0\r\n\
166 s=-\r\n\
167 t=0 0\r\n\
168 a=fingerprint:sha-256 CD:34:D1:62:16:95:7B:B7:EB:74:E2:39:27:97:EB:0B:23:73:AC:BC:BF:2F:E3:91:CB:57:A9:9D:4A:A2:0B:40\r\n\
169 a=group:BUNDLE sdparta_0\r\n\
170 a=ice-options:trickle\r\n\
171 a=msid-semantic:WMS *\r\n\
172 m=audio 9 UDP/TLS/RTP/SAVPF 109 9 0 8\r\n\
173 c=IN IP4 0.0.0.0\r\n\
174 a=sendrecv\r\n\
175 a=extmap:1/sendonly urn:ietf:params:rtp-hdrext:ssrc-audio-level\r\n\
176 a=fmtp:109 maxplaybackrate=48000;stereo=1;useinbandfec=1\r\n\
177 a=ice-pwd:e3baa26dd2fa5030d881d385f1e36cce\r\n\
178 a=ice-ufrag:58b99ead\r\n\
179 a=mid:sdparta_0\r\n\
180 a=msid:{5a990edd-0568-ac40-8d97-310fc33f3411} {218cfa1c-617d-2249-9997-60929ce4c405}\r\n\
181 a=rtcp-mux\r\n\
182 a=rtpmap:109 opus/48000/2\r\n\
183 a=rtpmap:9 G722/8000/1\r\n\
184 a=rtpmap:0 PCMU/8000\r\n\
185 a=rtpmap:8 PCMA/8000\r\n\
186 a=setup:actpass\r\n\
187 a=ssrc:2655508255 cname:{735484ea-4f6c-f74a-bd66-7425f8476c2e}\r\n";
188     let sdp_res = webrtc_sdp::parse_sdp(sdp_str, true);
189     assert!(sdp_res.is_ok());
190     let sdp_opt = sdp_res.ok();
191     assert!(sdp_opt.is_some());
192     let sdp = sdp_opt.unwrap();
193     assert_eq!(sdp.version, 0);
194     assert_eq!(sdp.media.len(), 1);
195 
196     let msection = &(sdp.media[0]);
197     assert_eq!(
198         *msection.get_type(),
199         webrtc_sdp::media_type::SdpMediaValue::Audio
200     );
201     assert_eq!(msection.get_port(), 9);
202     assert_eq!(msection.get_port_count(), 0);
203     assert_eq!(
204         *msection.get_proto(),
205         webrtc_sdp::media_type::SdpProtocolValue::UdpTlsRtpSavpf
206     );
207     assert!(msection.get_connection().is_some());
208     assert!(msection.get_bandwidth().is_empty());
209     assert!(!msection.get_attributes().is_empty());
210     assert!(msection
211         .get_attribute(webrtc_sdp::attribute_type::SdpAttributeType::Sendrecv)
212         .is_some());
213     assert!(msection
214         .get_attribute(webrtc_sdp::attribute_type::SdpAttributeType::Extmap)
215         .is_some());
216     assert!(msection
217         .get_attribute(webrtc_sdp::attribute_type::SdpAttributeType::Fmtp)
218         .is_some());
219     assert!(msection
220         .get_attribute(webrtc_sdp::attribute_type::SdpAttributeType::IcePwd)
221         .is_some());
222     assert!(msection
223         .get_attribute(webrtc_sdp::attribute_type::SdpAttributeType::IceUfrag)
224         .is_some());
225     assert!(msection
226         .get_attribute(webrtc_sdp::attribute_type::SdpAttributeType::Mid)
227         .is_some());
228     assert!(msection
229         .get_attribute(webrtc_sdp::attribute_type::SdpAttributeType::Mid)
230         .is_some());
231     assert!(msection
232         .get_attribute(webrtc_sdp::attribute_type::SdpAttributeType::Msid)
233         .is_some());
234     assert!(msection
235         .get_attribute(webrtc_sdp::attribute_type::SdpAttributeType::RtcpMux)
236         .is_some());
237     assert_eq!(
238         msection
239             .get_attributes_of_type(webrtc_sdp::attribute_type::SdpAttributeType::Rtpmap)
240             .len(),
241         4
242     );
243     assert!(msection
244         .get_attribute(webrtc_sdp::attribute_type::SdpAttributeType::Setup)
245         .is_some());
246     assert!(msection
247         .get_attribute(webrtc_sdp::attribute_type::SdpAttributeType::Ssrc)
248         .is_some());
249 }
250 
251 #[test]
parse_firefox_video_offer()252 fn parse_firefox_video_offer() {
253     let sdp_str = "v=0\r\n\
254 o=mozilla...THIS_IS_SDPARTA-52.0a1 506705521068071134 0 IN IP4 0.0.0.0\r\n\
255 s=-\r\n\
256 t=0 0\r\n\
257 a=extmap-allow-mixed\r\n
258 a=fingerprint:sha-256 CD:34:D1:62:16:95:7B:B7:EB:74:E2:39:27:97:EB:0B:23:73:AC:BC:BF:2F:E3:91:CB:57:A9:9D:4A:A2:0B:40\r\n\
259 a=group:BUNDLE sdparta_2\r\n\
260 a=ice-options:trickle\r\n\
261 a=msid-semantic:WMS *\r\n\
262 m=video 9 UDP/TLS/RTP/SAVPF 126 120 97\r\n\
263 c=IN IP4 0.0.0.0\r\n\
264 a=recvonly\r\n\
265 a=fmtp:126 profile-level-id=42e01f;level-asymmetry-allowed=1;packetization-mode=1\r\n\
266 a=fmtp:120 max-fs=12288;max-fr=60\r\n\
267 a=fmtp:97 profile-level-id=42e01f;level-asymmetry-allowed=1\r\n\
268 a=ice-pwd:e3baa26dd2fa5030d881d385f1e36cce\r\n\
269 a=ice-ufrag:58b99ead\r\n\
270 a=mid:sdparta_2\r\n\
271 a=rtcp-fb:126 nack\r\n\
272 a=rtcp-fb:126 nack pli\r\n\
273 a=rtcp-fb:126 ccm fir\r\n\
274 a=rtcp-fb:126 goog-remb\r\n\
275 a=rtcp-fb:120 nack\r\n\
276 a=rtcp-fb:120 nack pli\r\n\
277 a=rtcp-fb:120 ccm fir\r\n\
278 a=rtcp-fb:120 goog-remb\r\n\
279 a=rtcp-fb:97 nack\r\n\
280 a=rtcp-fb:97 nack pli\r\n\
281 a=rtcp-fb:97 ccm fir\r\n\
282 a=rtcp-fb:97 goog-remb\r\n\
283 a=rtcp-mux\r\n\
284 a=rtpmap:126 H264/90000\r\n\
285 a=rtpmap:120 VP8/90000\r\n\
286 a=rtpmap:97 H264/90000\r\n\
287 a=setup:actpass\r\n\
288 a=extmap-allow-mixed\r\n
289 a=ssrc:2709871439 cname:{735484ea-4f6c-f74a-bd66-7425f8476c2e}";
290     let sdp_res = webrtc_sdp::parse_sdp(sdp_str, true);
291     assert!(sdp_res.is_ok());
292     let sdp_opt = sdp_res.ok();
293     assert!(sdp_opt.is_some());
294     let sdp = sdp_opt.unwrap();
295     assert_eq!(sdp.version, 0);
296     assert_eq!(sdp.media.len(), 1);
297 
298     let msection = &(sdp.media[0]);
299     assert_eq!(
300         *msection.get_type(),
301         webrtc_sdp::media_type::SdpMediaValue::Video
302     );
303     assert_eq!(msection.get_port(), 9);
304     assert_eq!(
305         *msection.get_proto(),
306         webrtc_sdp::media_type::SdpProtocolValue::UdpTlsRtpSavpf
307     );
308     assert!(msection.get_connection().is_some());
309     assert!(msection.get_bandwidth().is_empty());
310     assert!(!msection.get_attributes().is_empty());
311     assert!(msection
312         .get_attribute(webrtc_sdp::attribute_type::SdpAttributeType::Recvonly)
313         .is_some());
314     assert!(!msection
315         .get_attribute(webrtc_sdp::attribute_type::SdpAttributeType::Extmap)
316         .is_some());
317     assert_eq!(
318         msection
319             .get_attributes_of_type(webrtc_sdp::attribute_type::SdpAttributeType::Fmtp)
320             .len(),
321         3
322     );
323     assert!(msection
324         .get_attribute(webrtc_sdp::attribute_type::SdpAttributeType::IcePwd)
325         .is_some());
326     assert!(msection
327         .get_attribute(webrtc_sdp::attribute_type::SdpAttributeType::IceUfrag)
328         .is_some());
329     assert!(msection
330         .get_attribute(webrtc_sdp::attribute_type::SdpAttributeType::Mid)
331         .is_some());
332     assert!(msection
333         .get_attribute(webrtc_sdp::attribute_type::SdpAttributeType::Mid)
334         .is_some());
335     assert!(!msection
336         .get_attribute(webrtc_sdp::attribute_type::SdpAttributeType::Msid)
337         .is_some());
338     assert_eq!(
339         msection
340             .get_attributes_of_type(webrtc_sdp::attribute_type::SdpAttributeType::Rtcpfb)
341             .len(),
342         12
343     );
344     assert!(msection
345         .get_attribute(webrtc_sdp::attribute_type::SdpAttributeType::RtcpMux)
346         .is_some());
347     assert_eq!(
348         msection
349             .get_attributes_of_type(webrtc_sdp::attribute_type::SdpAttributeType::Rtpmap)
350             .len(),
351         3
352     );
353     assert!(msection
354         .get_attribute(webrtc_sdp::attribute_type::SdpAttributeType::Setup)
355         .is_some());
356     assert!(msection
357         .get_attribute(webrtc_sdp::attribute_type::SdpAttributeType::Ssrc)
358         .is_some());
359 }
360 #[test]
parse_firefox_datachannel_offer()361 fn parse_firefox_datachannel_offer() {
362     let sdp_str = "v=0\r\n\
363     o=mozilla...THIS_IS_SDPARTA-52.0a2 3327975756663609975 0 IN IP4 0.0.0.0\r\n\
364     s=-\r\n\
365     t=0 0\r\n\
366     a=sendrecv\r\n\
367     a=fingerprint:sha-256 AC:72:CB:D6:1E:A3:A3:B0:E7:97:77:25:03:4B:5B:FF:19:6C:02:C6:93:7D:EB:5C:81:6F:36:D9:02:32:F8:23\r\n\
368     a=ice-options:trickle\r\n\
369     a=msid-semantic:WMS *\r\n\
370     m=application 49760 DTLS/SCTP 5000\r\n\
371     c=IN IP4 172.16.156.106\r\n\
372     a=candidate:0 1 UDP 2122252543 172.16.156.106 49760 typ host\r\n\
373     a=sendrecv\r\n\
374     a=end-of-candidates\r\n\
375     a=ice-pwd:24f485c580129b36447b65df77429a82\r\n\
376     a=ice-ufrag:4cba30fe\r\n\
377     a=mid:sdparta_0\r\n\
378     a=sctpmap:5000 webrtc-datachannel 256\r\n\
379     a=setup:active\r\n\
380     a=ssrc:3376683177 cname:{62f78ee0-620f-a043-86ca-b69f189f1aea}\r\n";
381     let sdp_res = webrtc_sdp::parse_sdp(sdp_str, true);
382     assert!(sdp_res.is_ok());
383     let sdp_opt = sdp_res.ok();
384     assert!(sdp_opt.is_some());
385     let sdp = sdp_opt.unwrap();
386     assert_eq!(sdp.version, 0);
387     assert_eq!(sdp.media.len(), 1);
388 
389     let msection = &(sdp.media[0]);
390     assert_eq!(
391         *msection.get_type(),
392         webrtc_sdp::media_type::SdpMediaValue::Application
393     );
394     assert_eq!(msection.get_port(), 49760);
395     assert_eq!(
396         *msection.get_proto(),
397         webrtc_sdp::media_type::SdpProtocolValue::DtlsSctp
398     );
399     assert!(msection.get_connection().is_some());
400     assert!(msection.get_bandwidth().is_empty());
401     assert!(!msection.get_attributes().is_empty());
402     assert!(msection
403         .get_attribute(webrtc_sdp::attribute_type::SdpAttributeType::Sendrecv)
404         .is_some());
405     assert!(!msection
406         .get_attribute(webrtc_sdp::attribute_type::SdpAttributeType::Extmap)
407         .is_some());
408     assert!(msection
409         .get_attribute(webrtc_sdp::attribute_type::SdpAttributeType::IcePwd)
410         .is_some());
411     assert!(msection
412         .get_attribute(webrtc_sdp::attribute_type::SdpAttributeType::IceUfrag)
413         .is_some());
414     assert!(msection
415         .get_attribute(webrtc_sdp::attribute_type::SdpAttributeType::EndOfCandidates)
416         .is_some());
417     assert!(msection
418         .get_attribute(webrtc_sdp::attribute_type::SdpAttributeType::Mid)
419         .is_some());
420     assert!(!msection
421         .get_attribute(webrtc_sdp::attribute_type::SdpAttributeType::Msid)
422         .is_some());
423     assert!(!msection
424         .get_attribute(webrtc_sdp::attribute_type::SdpAttributeType::Rtcpfb)
425         .is_some());
426     assert!(!msection
427         .get_attribute(webrtc_sdp::attribute_type::SdpAttributeType::RtcpMux)
428         .is_some());
429     assert!(!msection
430         .get_attribute(webrtc_sdp::attribute_type::SdpAttributeType::Rtpmap)
431         .is_some());
432     assert!(msection
433         .get_attribute(webrtc_sdp::attribute_type::SdpAttributeType::Sctpmap)
434         .is_some());
435     assert!(msection
436         .get_attribute(webrtc_sdp::attribute_type::SdpAttributeType::Setup)
437         .is_some());
438     assert!(msection
439         .get_attribute(webrtc_sdp::attribute_type::SdpAttributeType::Ssrc)
440         .is_some());
441 
442     check_parse_and_serialize(sdp_str);
443 }
444 
445 #[test]
parse_chrome_audio_video_offer()446 fn parse_chrome_audio_video_offer() {
447     let sdp = "v=0\r\n
448 o=- 3836772544440436510 2 IN IP4 127.0.0.1\r\n
449 s=-\r\n
450 t=0 0\r\n
451 a=group:BUNDLE audio video\r\n
452 a=msid-semantic: WMS HWpbmTmXleVSnlssQd80bPuw9cxQFroDkkBP\r\n
453 m=audio 9 UDP/TLS/RTP/SAVPF 111 103 104 9 0 8 106 105 13 126\r\n
454 c=IN IP4 0.0.0.0\r\n
455 a=rtcp:9 IN IP4 0.0.0.0\r\n
456 a=ice-ufrag:A4by\r\n
457 a=ice-pwd:Gfvb2rbYMiW0dZz8ZkEsXICs\r\n
458 a=fingerprint:sha-256 15:B0:92:1F:C7:40:EE:22:A6:AF:26:EF:EA:FF:37:1D:B3:EF:11:0B:8B:73:4F:01:7D:C9:AE:26:4F:87:E0:95\r\n
459 a=setup:actpass\r\n
460 a=mid:audio\r\n
461 a=extmap:1 urn:ietf:params:rtp-hdrext:ssrc-audio-level\r\n
462 a=sendrecv\r\n
463 a=rtcp-mux\r\n
464 a=rtpmap:111 opus/48000/2\r\n
465 a=rtcp-fb:111 transport-cc\r\n
466 a=fmtp:111 minptime=10;useinbandfec=1\r\n
467 a=rtpmap:103 ISAC/16000\r\n
468 a=rtpmap:104 ISAC/32000\r\n
469 a=rtpmap:9 G722/8000\r\n
470 a=rtpmap:0 PCMU/8000\r\n
471 a=rtpmap:8 PCMA/8000\r\n
472 a=rtpmap:106 CN/32000\r\n
473 a=rtpmap:105 CN/16000\r\n
474 a=rtpmap:13 CN/8000\r\n
475 a=rtpmap:126 telephone-event/8000\r\n
476 a=ssrc:162559313 cname:qPTZ+BI+42mgbOi+\r\n
477 a=ssrc:162559313 msid:HWpbmTmXleVSnlssQd80bPuw9cxQFroDkkBP f6188af5-d8d6-462c-9c75-f12bc41fe322\r\n
478 a=ssrc:162559313 mslabel:HWpbmTmXleVSnlssQd80bPuw9cxQFroDkkBP\r\n
479 a=ssrc:162559313 label:f6188af5-d8d6-462c-9c75-f12bc41fe322\r\n
480 m=video 9 UDP/TLS/RTP/SAVPF 100 101 107 116 117 96 97 99 98\r\n
481 c=IN IP4 0.0.0.0\r\n
482 a=rtcp:9 IN IP4 0.0.0.0\r\n
483 a=ice-ufrag:A4by\r\n
484 a=ice-pwd:Gfvb2rbYMiW0dZz8ZkEsXICs\r\n
485 a=fingerprint:sha-256 15:B0:92:1F:C7:40:EE:22:A6:AF:26:EF:EA:FF:37:1D:B3:EF:11:0B:8B:73:4F:01:7D:C9:AE:26:4F:87:E0:95\r\n
486 a=setup:actpass\r\n
487 a=mid:video\r\n
488 a=extmap:2 urn:ietf:params:rtp-hdrext:toffset\r\n
489 a=extmap:3 http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time\r\n
490 a=extmap:4 urn:3gpp:video-orientation\r\n
491 a=extmap:5 http://www.ietf.org/id/draft-holmer-rmcat-transport-wide-cc-extensions-01\r\n
492 a=extmap:6 http://www.webrtc.org/experiments/rtp-hdrext/playout-delay\r\n
493 a=sendrecv\r\n
494 a=rtcp-mux\r\n
495 a=rtcp-rsize\r\n
496 a=rtpmap:100 VP8/90000\r\n
497 a=rtcp-fb:100 ccm fir\r\n
498 a=rtcp-fb:100 nack\r\n
499 a=rtcp-fb:100 nack pli\r\n
500 a=rtcp-fb:100 goog-remb\r\n
501 a=rtcp-fb:100 transport-cc\r\n
502 a=rtpmap:101 VP9/90000\r\n
503 a=rtcp-fb:101 ccm fir\r\n
504 a=rtcp-fb:101 nack\r\n
505 a=rtcp-fb:101 nack pli\r\n
506 a=rtcp-fb:101 goog-remb\r\n
507 a=rtcp-fb:101 transport-cc\r\n
508 a=rtpmap:107 H264/90000\r\n
509 a=rtcp-fb:107 ccm fir\r\n
510 a=rtcp-fb:107 nack\r\n
511 a=rtcp-fb:107 nack pli\r\n
512 a=rtcp-fb:107 goog-remb\r\n
513 a=rtcp-fb:107 transport-cc\r\n
514 a=fmtp:107 level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=42e01f\r\n
515 a=rtpmap:116 red/90000\r\n
516 a=rtpmap:117 ulpfec/90000\r\n
517 a=rtpmap:96 rtx/90000\r\n
518 a=fmtp:96 apt=100\r\n
519 a=rtpmap:97 rtx/90000\r\n
520 a=fmtp:97 apt=101\r\n
521 a=rtpmap:99 rtx/90000\r\n
522 a=fmtp:99 apt=107\r\n
523 a=rtpmap:98 rtx/90000\r\n
524 a=fmtp:98 apt=116\r\n
525 a=ssrc-group:FID 3156517279 2673335628\r\n
526 a=ssrc:3156517279 cname:qPTZ+BI+42mgbOi+\r\n
527 a=ssrc:3156517279 msid:HWpbmTmXleVSnlssQd80bPuw9cxQFroDkkBP b6ec5178-c611-403f-bbec-3833ed547c09\r\n
528 a=ssrc:3156517279 mslabel:HWpbmTmXleVSnlssQd80bPuw9cxQFroDkkBP\r\n
529 a=ssrc:3156517279 label:b6ec5178-c611-403f-bbec-3833ed547c09\r\n
530 a=ssrc:2673335628 cname:qPTZ+BI+42mgbOi+\r\n
531 a=ssrc:2673335628 msid:HWpbmTmXleVSnlssQd80bPuw9cxQFroDkkBP b6ec5178-c611-403f-bbec-3833ed547c09\r\n
532 a=ssrc:2673335628 mslabel:HWpbmTmXleVSnlssQd80bPuw9cxQFroDkkBP\r\n
533 a=ssrc:2673335628 label:b6ec5178-c611-403f-bbec-3833ed547c09\r\n";
534     let sdp_res = webrtc_sdp::parse_sdp(sdp, true);
535     assert!(sdp_res.is_ok());
536     let sdp_opt = sdp_res.ok();
537     assert!(sdp_opt.is_some());
538     let sdp = sdp_opt.unwrap();
539     assert_eq!(sdp.version, 0);
540     assert_eq!(sdp.media.len(), 2);
541 
542     let msection1 = &(sdp.media[0]);
543     assert_eq!(
544         *msection1.get_type(),
545         webrtc_sdp::media_type::SdpMediaValue::Audio
546     );
547     assert_eq!(msection1.get_port(), 9);
548     assert_eq!(
549         *msection1.get_proto(),
550         webrtc_sdp::media_type::SdpProtocolValue::UdpTlsRtpSavpf
551     );
552     assert!(!msection1.get_attributes().is_empty());
553     assert!(msection1.get_connection().is_some());
554     assert!(msection1.get_bandwidth().is_empty());
555 
556     let msection2 = &(sdp.media[1]);
557     assert_eq!(
558         *msection2.get_type(),
559         webrtc_sdp::media_type::SdpMediaValue::Video
560     );
561     assert_eq!(msection2.get_port(), 9);
562     assert_eq!(
563         *msection2.get_proto(),
564         webrtc_sdp::media_type::SdpProtocolValue::UdpTlsRtpSavpf
565     );
566     assert!(!msection2.get_attributes().is_empty());
567     assert!(msection2.get_connection().is_some());
568     assert!(msection2.get_bandwidth().is_empty());
569 }
570 
571 #[test]
parse_firefox_simulcast_offer()572 fn parse_firefox_simulcast_offer() {
573     let sdp = "v=0\r\n
574 o=mozilla...THIS_IS_SDPARTA-55.0a1 983028567300715536 0 IN IP4 0.0.0.0\r\n
575 s=-\r\n
576 t=0 0\r\n
577 a=fingerprint:sha-256 68:42:13:88:B6:C1:7D:18:79:07:8A:C6:DC:28:D6:DC:DD:E3:C9:41:E7:80:A7:FE:02:65:FB:76:A0:CD:58:ED\r\n
578 a=ice-options:trickle\r\n
579 a=msid-semantic:WMS *\r\n
580 m=video 9 UDP/TLS/RTP/SAVPF 120 121 126 97\r\n
581 c=IN IP4 0.0.0.0\r\n
582 a=sendrecv\r\n
583 a=extmap:1 http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time\r\n
584 a=extmap:2 urn:ietf:params:rtp-hdrext:toffset\r\n
585 a=extmap:3/sendonly urn:ietf:params:rtp-hdrext:sdes:rtp-stream-id\r\n
586 a=fmtp:126 profile-level-id=42e01f;level-asymmetry-allowed=1;packetization-mode=1\r\n
587 a=fmtp:97 profile-level-id=42e01f;level-asymmetry-allowed=1\r\n
588 a=fmtp:120 max-fs=12288;max-fr=60\r\n
589 a=fmtp:121 max-fs=12288;max-fr=60\r\n
590 a=ice-pwd:4af388405d558b91f5ba6c2c48f161bf\r\n
591 a=ice-ufrag:ce1ac488\r\n
592 a=mid:sdparta_0\r\n
593 a=msid:{fb6d1fa3-d993-f244-a0fe-d9fb99214c23} {8be9a0f7-9272-6c42-90f3-985d55bd8de5}\r\n
594 a=rid:foo send\r\n
595 a=rid:bar send\r\n
596 a=rtcp-fb:120 nack\r\n
597 a=rtcp-fb:120 nack pli\r\n
598 a=rtcp-fb:120 ccm fir\r\n
599 a=rtcp-fb:120 goog-remb\r\n
600 a=rtcp-fb:121 nack\r\n
601 a=rtcp-fb:121 nack pli\r\n
602 a=rtcp-fb:121 ccm fir\r\n
603 a=rtcp-fb:121 goog-remb\r\n
604 a=rtcp-fb:126 nack\r\n
605 a=rtcp-fb:126 nack pli\r\n
606 a=rtcp-fb:126 ccm fir\r\n
607 a=rtcp-fb:126 goog-remb\r\n
608 a=rtcp-fb:97 nack\r\n
609 a=rtcp-fb:97 nack pli\r\n
610 a=rtcp-fb:97 ccm fir\r\n
611 a=rtcp-fb:97 goog-remb\r\n
612 a=rtcp-mux\r\n
613 a=rtpmap:120 VP8/90000\r\n
614 a=rtpmap:121 VP9/90000\r\n
615 a=rtpmap:126 H264/90000\r\n
616 a=rtpmap:97 H264/90000\r\n
617 a=setup:actpass\r\n
618 a=simulcast: send rid=foo;bar\r\n
619 a=ssrc:2988475468 cname:{77067f00-2e8d-8b4c-8992-cfe338f56851}\r\n
620 a=ssrc:1649784806 cname:{77067f00-2e8d-8b4c-8992-cfe338f56851}\r\n";
621     let sdp_res = webrtc_sdp::parse_sdp(sdp, true);
622     assert!(sdp_res.is_ok());
623     let sdp_opt = sdp_res.ok();
624     assert!(sdp_opt.is_some());
625     let sdp = sdp_opt.unwrap();
626     assert_eq!(sdp.version, 0);
627     assert_eq!(sdp.media.len(), 1);
628 }
629 
630 #[test]
parse_firefox_simulcast_answer()631 fn parse_firefox_simulcast_answer() {
632     let sdp_str = "v=0\r\n\
633     o=mozilla...THIS_IS_SDPARTA-55.0a1 7548296603161351381 0 IN IP4 0.0.0.0\r\n\
634     s=-\r\n\
635     t=0 0\r\n\
636     a=fingerprint:sha-256 B1:47:49:4F:7D:83:03:BE:E9:FC:73:A3:FB:33:38:40:0B:3B:6A:56:78:EB:EE:D5:6D:2D:D5:3A:B6:13:97:E7\r\n\
637     a=ice-options:trickle\r\n\
638     a=msid-semantic:WMS *\r\n\
639     m=video 9 UDP/TLS/RTP/SAVPF 120\r\n\
640     c=IN IP4 0.0.0.0\r\n
641     a=recvonly\r\n\
642     a=extmap:1 http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time\r\n\
643     a=extmap:2 urn:ietf:params:rtp-hdrext:toffset\r\n\
644     a=fmtp:120 max-fs=12288;max-fr=60\r\n\
645     a=ice-pwd:c886e2caf2ae397446312930cd1afe51\r\n\
646     a=ice-ufrag:f57396c0\r\n\
647     a=mid:sdparta_0\r\n\
648     a=rtcp-fb:120 nack\r\n\
649     a=rtcp-fb:120 nack pli\r\n\
650     a=rtcp-fb:120 ccm fir\r\n\
651     a=rtcp-fb:120 goog-remb\r\n\
652     a=rtcp-mux\r\n\
653     a=rtpmap:120 VP8/90000\r\n\
654     a=setup:active\r\n\
655     a=ssrc:2564157021 cname:{cae1cd32-7433-5b48-8dc8-8e3f8b2f96cd}\r\n\
656     a=simulcast: recv rid=foo;bar\r\n\
657     a=rid:foo recv\r\n\
658     a=rid:bar recv\r\n\
659     a=extmap:3/recvonly urn:ietf:params:rtp-hdrext:sdes:rtp-stream-id\r\n";
660     let sdp_res = webrtc_sdp::parse_sdp(sdp_str, true);
661     assert!(sdp_res.is_ok());
662     let sdp_opt = sdp_res.ok();
663     assert!(sdp_opt.is_some());
664     let sdp = sdp_opt.unwrap();
665     assert_eq!(sdp.version, 0);
666     assert_eq!(sdp.media.len(), 1);
667 }
668 
669 #[test]
parse_and_serialize_sdp_with_unusual_attributes()670 fn parse_and_serialize_sdp_with_unusual_attributes() {
671     let sdp_str = "v=0\r\n\
672                    o=- 0 0 IN IP6 2001:db8::4444\r\n\
673                    s=-\r\n\
674                    t=0 0\r\n\
675                    a=ice-pacing:500\r\n\
676                    m=video 0 UDP/TLS/RTP/SAVPF 0\r\n\
677                    b=UNSUPPORTED:12345\r\n\
678                    c=IN IP6 ::1\r\n\
679                    a=rtcp:9 IN IP6 2001:db8::8888\r\n\
680                    a=rtcp-fb:* nack\r\n\
681                    a=extmap:1/recvonly urn:ietf:params:rtp-hdrext:toffset\r\n\
682                    a=extmap:2/sendonly urn:ietf:params:rtp-hdrext:toffset\r\n\
683                    a=extmap:3/sendrecv urn:ietf:params:rtp-hdrext:toffset\r\n\
684                    a=imageattr:* send [x=330,y=250,sar=[1.1,1.3,1.9],q=0.1] recv [x=800,y=[50,80,30],sar=1.1]\r\n\
685                    a=imageattr:97 send [x=[480:16:800],y=[100,200,300],par=[1.2-1.3],q=0.6] [x=1080,y=[144:176],sar=[0.5-0.7]] recv *\r\n\
686                    a=sendrecv\r\n";
687 
688     check_parse_and_serialize(sdp_str);
689 }
690