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