1 /*
2  *  Copyright 2011 The WebRTC project authors. All Rights Reserved.
3  *
4  *  Use of this source code is governed by a BSD-style license
5  *  that can be found in the LICENSE file in the root of the source
6  *  tree. An additional intellectual property rights grant can be found
7  *  in the file PATENTS.  All contributing project authors may
8  *  be found in the AUTHORS file in the root of the source tree.
9  */
10 
11 #include <stdio.h>
12 #include <string.h>
13 
14 #include <algorithm>
15 #include <cstdint>
16 #include <map>
17 #include <memory>
18 #include <string>
19 #include <utility>
20 #include <vector>
21 
22 #include "absl/algorithm/container.h"
23 #include "absl/memory/memory.h"
24 #include "absl/strings/str_replace.h"
25 #include "api/array_view.h"
26 #include "api/crypto_params.h"
27 #include "api/jsep_session_description.h"
28 #include "api/media_types.h"
29 #include "api/rtp_parameters.h"
30 #include "api/rtp_transceiver_interface.h"
31 #include "media/base/codec.h"
32 #include "media/base/media_constants.h"
33 #include "media/base/stream_params.h"
34 #include "p2p/base/p2p_constants.h"
35 #include "p2p/base/port.h"
36 #include "p2p/base/transport_description.h"
37 #include "p2p/base/transport_info.h"
38 #include "pc/media_session.h"
39 #include "pc/session_description.h"
40 #include "rtc_base/checks.h"
41 #include "rtc_base/message_digest.h"
42 #include "rtc_base/socket_address.h"
43 #include "rtc_base/ssl_fingerprint.h"
44 #include "rtc_base/string_encode.h"
45 #include "test/gmock.h"
46 #include "test/gtest.h"
47 
48 #ifdef WEBRTC_ANDROID
49 #include "pc/test/android_test_initializer.h"
50 #endif
51 #include "pc/webrtc_sdp.h"
52 
53 using cricket::AudioCodec;
54 using cricket::AudioContentDescription;
55 using cricket::Candidate;
56 using cricket::ContentGroup;
57 using cricket::ContentInfo;
58 using cricket::CryptoParams;
59 using cricket::DataCodec;
60 using cricket::ICE_CANDIDATE_COMPONENT_RTCP;
61 using cricket::ICE_CANDIDATE_COMPONENT_RTP;
62 using cricket::kFecSsrcGroupSemantics;
63 using cricket::LOCAL_PORT_TYPE;
64 using cricket::MediaProtocolType;
65 using cricket::RELAY_PORT_TYPE;
66 using cricket::RidDescription;
67 using cricket::RidDirection;
68 using cricket::RtpDataContentDescription;
69 using cricket::SctpDataContentDescription;
70 using cricket::SessionDescription;
71 using cricket::SimulcastDescription;
72 using cricket::SimulcastLayer;
73 using cricket::StreamParams;
74 using cricket::STUN_PORT_TYPE;
75 using cricket::TransportDescription;
76 using cricket::TransportInfo;
77 using cricket::VideoCodec;
78 using cricket::VideoContentDescription;
79 using ::testing::ElementsAre;
80 using ::testing::Field;
81 using webrtc::IceCandidateCollection;
82 using webrtc::IceCandidateInterface;
83 using webrtc::JsepIceCandidate;
84 using webrtc::JsepSessionDescription;
85 using webrtc::RtpExtension;
86 using webrtc::RtpTransceiverDirection;
87 using webrtc::SdpParseError;
88 using webrtc::SdpType;
89 using webrtc::SessionDescriptionInterface;
90 
91 typedef std::vector<AudioCodec> AudioCodecs;
92 typedef std::vector<Candidate> Candidates;
93 
94 static const uint32_t kDefaultSctpPort = 5000;
95 static const uint16_t kUnusualSctpPort = 9556;
96 static const char kSessionTime[] = "t=0 0\r\n";
97 static const uint32_t kCandidatePriority = 2130706432U;  // pref = 1.0
98 static const char kAttributeIceUfragVoice[] = "a=ice-ufrag:ufrag_voice\r\n";
99 static const char kAttributeIcePwdVoice[] = "a=ice-pwd:pwd_voice\r\n";
100 static const char kAttributeIceUfragVideo[] = "a=ice-ufrag:ufrag_video\r\n";
101 static const char kAttributeIcePwdVideo[] = "a=ice-pwd:pwd_video\r\n";
102 static const uint32_t kCandidateGeneration = 2;
103 static const char kCandidateFoundation1[] = "a0+B/1";
104 static const char kCandidateFoundation2[] = "a0+B/2";
105 static const char kCandidateFoundation3[] = "a0+B/3";
106 static const char kCandidateFoundation4[] = "a0+B/4";
107 static const char kAttributeCryptoVoice[] =
108     "a=crypto:1 AES_CM_128_HMAC_SHA1_32 "
109     "inline:NzB4d1BINUAvLEw6UzF3WSJ+PSdFcGdUJShpX1Zj|2^20|1:32 "
110     "dummy_session_params\r\n";
111 static const char kAttributeCryptoVideo[] =
112     "a=crypto:1 AES_CM_128_HMAC_SHA1_80 "
113     "inline:d0RmdmcmVCspeEc3QGZiNWpVLFJhQX1cfHAwJSoj|2^20|1:32\r\n";
114 static const char kFingerprint[] =
115     "a=fingerprint:sha-1 "
116     "4A:AD:B9:B1:3F:82:18:3B:54:02:12:DF:3E:5D:49:6B:19:E5:7C:AB\r\n";
117 static const char kExtmapAllowMixed[] = "a=extmap-allow-mixed\r\n";
118 static const int kExtmapId = 1;
119 static const char kExtmapUri[] = "http://example.com/082005/ext.htm#ttime";
120 static const char kExtmap[] =
121     "a=extmap:1 http://example.com/082005/ext.htm#ttime\r\n";
122 static const char kExtmapWithDirectionAndAttribute[] =
123     "a=extmap:1/sendrecv http://example.com/082005/ext.htm#ttime a1 a2\r\n";
124 static const char kExtmapWithDirectionAndAttributeEncrypted[] =
125     "a=extmap:1/sendrecv urn:ietf:params:rtp-hdrext:encrypt "
126     "http://example.com/082005/ext.htm#ttime a1 a2\r\n";
127 
128 static const uint8_t kIdentityDigest[] = {
129     0x4A, 0xAD, 0xB9, 0xB1, 0x3F, 0x82, 0x18, 0x3B, 0x54, 0x02,
130     0x12, 0xDF, 0x3E, 0x5D, 0x49, 0x6B, 0x19, 0xE5, 0x7C, 0xAB};
131 
132 static const char kDtlsSctp[] = "DTLS/SCTP";
133 static const char kUdpDtlsSctp[] = "UDP/DTLS/SCTP";
134 static const char kTcpDtlsSctp[] = "TCP/DTLS/SCTP";
135 
136 struct CodecParams {
137   int max_ptime;
138   int ptime;
139   int min_ptime;
140   int sprop_stereo;
141   int stereo;
142   int useinband;
143   int maxaveragebitrate;
144 };
145 
146 // TODO(deadbeef): In these reference strings, use "a=fingerprint" by default
147 // instead of "a=crypto", and have an explicit test for adding "a=crypto".
148 // Currently it's the other way around.
149 
150 // Reference sdp string
151 static const char kSdpFullString[] =
152     "v=0\r\n"
153     "o=- 18446744069414584320 18446462598732840960 IN IP4 127.0.0.1\r\n"
154     "s=-\r\n"
155     "t=0 0\r\n"
156     "a=msid-semantic: WMS local_stream_1\r\n"
157     "m=audio 2345 RTP/SAVPF 111 103 104\r\n"
158     "c=IN IP4 74.125.127.126\r\n"
159     "a=rtcp:2347 IN IP4 74.125.127.126\r\n"
160     "a=candidate:a0+B/1 1 udp 2130706432 192.168.1.5 1234 typ host "
161     "generation 2\r\n"
162     "a=candidate:a0+B/1 2 udp 2130706432 192.168.1.5 1235 typ host "
163     "generation 2\r\n"
164     "a=candidate:a0+B/2 1 udp 2130706432 ::1 1238 typ host "
165     "generation 2\r\n"
166     "a=candidate:a0+B/2 2 udp 2130706432 ::1 1239 typ host "
167     "generation 2\r\n"
168     "a=candidate:a0+B/3 1 udp 2130706432 74.125.127.126 2345 typ srflx "
169     "raddr 192.168.1.5 rport 2346 "
170     "generation 2\r\n"
171     "a=candidate:a0+B/3 2 udp 2130706432 74.125.127.126 2347 typ srflx "
172     "raddr 192.168.1.5 rport 2348 "
173     "generation 2\r\n"
174     "a=ice-ufrag:ufrag_voice\r\na=ice-pwd:pwd_voice\r\n"
175     "a=mid:audio_content_name\r\n"
176     "a=sendrecv\r\n"
177     "a=rtcp-mux\r\n"
178     "a=rtcp-rsize\r\n"
179     "a=crypto:1 AES_CM_128_HMAC_SHA1_32 "
180     "inline:NzB4d1BINUAvLEw6UzF3WSJ+PSdFcGdUJShpX1Zj|2^20|1:32 "
181     "dummy_session_params\r\n"
182     "a=rtpmap:111 opus/48000/2\r\n"
183     "a=rtpmap:103 ISAC/16000\r\n"
184     "a=rtpmap:104 ISAC/32000\r\n"
185     "a=ssrc:1 cname:stream_1_cname\r\n"
186     "a=ssrc:1 msid:local_stream_1 audio_track_id_1\r\n"
187     "a=ssrc:1 mslabel:local_stream_1\r\n"
188     "a=ssrc:1 label:audio_track_id_1\r\n"
189     "m=video 3457 RTP/SAVPF 120\r\n"
190     "c=IN IP4 74.125.224.39\r\n"
191     "a=rtcp:3456 IN IP4 74.125.224.39\r\n"
192     "a=candidate:a0+B/1 2 udp 2130706432 192.168.1.5 1236 typ host "
193     "generation 2\r\n"
194     "a=candidate:a0+B/1 1 udp 2130706432 192.168.1.5 1237 typ host "
195     "generation 2\r\n"
196     "a=candidate:a0+B/2 2 udp 2130706432 ::1 1240 typ host "
197     "generation 2\r\n"
198     "a=candidate:a0+B/2 1 udp 2130706432 ::1 1241 typ host "
199     "generation 2\r\n"
200     "a=candidate:a0+B/4 2 udp 2130706432 74.125.224.39 3456 typ relay "
201     "generation 2\r\n"
202     "a=candidate:a0+B/4 1 udp 2130706432 74.125.224.39 3457 typ relay "
203     "generation 2\r\n"
204     "a=ice-ufrag:ufrag_video\r\na=ice-pwd:pwd_video\r\n"
205     "a=mid:video_content_name\r\n"
206     "a=sendrecv\r\n"
207     "a=crypto:1 AES_CM_128_HMAC_SHA1_80 "
208     "inline:d0RmdmcmVCspeEc3QGZiNWpVLFJhQX1cfHAwJSoj|2^20|1:32\r\n"
209     "a=rtpmap:120 VP8/90000\r\n"
210     "a=ssrc-group:FEC 2 3\r\n"
211     "a=ssrc:2 cname:stream_1_cname\r\n"
212     "a=ssrc:2 msid:local_stream_1 video_track_id_1\r\n"
213     "a=ssrc:2 mslabel:local_stream_1\r\n"
214     "a=ssrc:2 label:video_track_id_1\r\n"
215     "a=ssrc:3 cname:stream_1_cname\r\n"
216     "a=ssrc:3 msid:local_stream_1 video_track_id_1\r\n"
217     "a=ssrc:3 mslabel:local_stream_1\r\n"
218     "a=ssrc:3 label:video_track_id_1\r\n";
219 
220 // SDP reference string without the candidates.
221 static const char kSdpString[] =
222     "v=0\r\n"
223     "o=- 18446744069414584320 18446462598732840960 IN IP4 127.0.0.1\r\n"
224     "s=-\r\n"
225     "t=0 0\r\n"
226     "a=msid-semantic: WMS local_stream_1\r\n"
227     "m=audio 9 RTP/SAVPF 111 103 104\r\n"
228     "c=IN IP4 0.0.0.0\r\n"
229     "a=rtcp:9 IN IP4 0.0.0.0\r\n"
230     "a=ice-ufrag:ufrag_voice\r\na=ice-pwd:pwd_voice\r\n"
231     "a=mid:audio_content_name\r\n"
232     "a=sendrecv\r\n"
233     "a=rtcp-mux\r\n"
234     "a=rtcp-rsize\r\n"
235     "a=crypto:1 AES_CM_128_HMAC_SHA1_32 "
236     "inline:NzB4d1BINUAvLEw6UzF3WSJ+PSdFcGdUJShpX1Zj|2^20|1:32 "
237     "dummy_session_params\r\n"
238     "a=rtpmap:111 opus/48000/2\r\n"
239     "a=rtpmap:103 ISAC/16000\r\n"
240     "a=rtpmap:104 ISAC/32000\r\n"
241     "a=ssrc:1 cname:stream_1_cname\r\n"
242     "a=ssrc:1 msid:local_stream_1 audio_track_id_1\r\n"
243     "a=ssrc:1 mslabel:local_stream_1\r\n"
244     "a=ssrc:1 label:audio_track_id_1\r\n"
245     "m=video 9 RTP/SAVPF 120\r\n"
246     "c=IN IP4 0.0.0.0\r\n"
247     "a=rtcp:9 IN IP4 0.0.0.0\r\n"
248     "a=ice-ufrag:ufrag_video\r\na=ice-pwd:pwd_video\r\n"
249     "a=mid:video_content_name\r\n"
250     "a=sendrecv\r\n"
251     "a=crypto:1 AES_CM_128_HMAC_SHA1_80 "
252     "inline:d0RmdmcmVCspeEc3QGZiNWpVLFJhQX1cfHAwJSoj|2^20|1:32\r\n"
253     "a=rtpmap:120 VP8/90000\r\n"
254     "a=ssrc-group:FEC 2 3\r\n"
255     "a=ssrc:2 cname:stream_1_cname\r\n"
256     "a=ssrc:2 msid:local_stream_1 video_track_id_1\r\n"
257     "a=ssrc:2 mslabel:local_stream_1\r\n"
258     "a=ssrc:2 label:video_track_id_1\r\n"
259     "a=ssrc:3 cname:stream_1_cname\r\n"
260     "a=ssrc:3 msid:local_stream_1 video_track_id_1\r\n"
261     "a=ssrc:3 mslabel:local_stream_1\r\n"
262     "a=ssrc:3 label:video_track_id_1\r\n";
263 
264 static const char kSdpRtpDataChannelString[] =
265     "m=application 9 RTP/SAVPF 101\r\n"
266     "c=IN IP4 0.0.0.0\r\n"
267     "a=rtcp:9 IN IP4 0.0.0.0\r\n"
268     "a=ice-ufrag:ufrag_data\r\n"
269     "a=ice-pwd:pwd_data\r\n"
270     "a=mid:data_content_name\r\n"
271     "a=sendrecv\r\n"
272     "a=crypto:1 AES_CM_128_HMAC_SHA1_80 "
273     "inline:FvLcvU2P3ZWmQxgPAgcDu7Zl9vftYElFOjEzhWs5\r\n"
274     "a=rtpmap:101 google-data/90000\r\n"
275     "a=ssrc:10 cname:data_channel_cname\r\n"
276     "a=ssrc:10 msid:data_channel data_channeld0\r\n"
277     "a=ssrc:10 mslabel:data_channel\r\n"
278     "a=ssrc:10 label:data_channeld0\r\n";
279 
280 // draft-ietf-mmusic-sctp-sdp-03
281 static const char kSdpSctpDataChannelString[] =
282     "m=application 9 UDP/DTLS/SCTP 5000\r\n"
283     "c=IN IP4 0.0.0.0\r\n"
284     "a=ice-ufrag:ufrag_data\r\n"
285     "a=ice-pwd:pwd_data\r\n"
286     "a=mid:data_content_name\r\n"
287     "a=sctpmap:5000 webrtc-datachannel 1024\r\n";
288 
289 // draft-ietf-mmusic-sctp-sdp-12
290 // Note - this is invalid per draft-ietf-mmusic-sctp-sdp-26,
291 // since the separator after "sctp-port" needs to be a colon.
292 static const char kSdpSctpDataChannelStringWithSctpPort[] =
293     "m=application 9 UDP/DTLS/SCTP webrtc-datachannel\r\n"
294     "a=sctp-port 5000\r\n"
295     "c=IN IP4 0.0.0.0\r\n"
296     "a=ice-ufrag:ufrag_data\r\n"
297     "a=ice-pwd:pwd_data\r\n"
298     "a=mid:data_content_name\r\n";
299 
300 // draft-ietf-mmusic-sctp-sdp-26
301 static const char kSdpSctpDataChannelStringWithSctpColonPort[] =
302     "m=application 9 UDP/DTLS/SCTP webrtc-datachannel\r\n"
303     "a=sctp-port:5000\r\n"
304     "c=IN IP4 0.0.0.0\r\n"
305     "a=ice-ufrag:ufrag_data\r\n"
306     "a=ice-pwd:pwd_data\r\n"
307     "a=mid:data_content_name\r\n";
308 
309 static const char kSdpSctpDataChannelWithCandidatesString[] =
310     "m=application 2345 UDP/DTLS/SCTP 5000\r\n"
311     "c=IN IP4 74.125.127.126\r\n"
312     "a=candidate:a0+B/1 1 udp 2130706432 192.168.1.5 1234 typ host "
313     "generation 2\r\n"
314     "a=candidate:a0+B/2 1 udp 2130706432 ::1 1238 typ host "
315     "generation 2\r\n"
316     "a=candidate:a0+B/3 1 udp 2130706432 74.125.127.126 2345 typ srflx "
317     "raddr 192.168.1.5 rport 2346 "
318     "generation 2\r\n"
319     "a=ice-ufrag:ufrag_data\r\n"
320     "a=ice-pwd:pwd_data\r\n"
321     "a=mid:data_content_name\r\n"
322     "a=sctpmap:5000 webrtc-datachannel 1024\r\n";
323 
324 static const char kSdpConferenceString[] =
325     "v=0\r\n"
326     "o=- 18446744069414584320 18446462598732840960 IN IP4 127.0.0.1\r\n"
327     "s=-\r\n"
328     "t=0 0\r\n"
329     "a=msid-semantic: WMS\r\n"
330     "m=audio 9 RTP/SAVPF 111 103 104\r\n"
331     "c=IN IP4 0.0.0.0\r\n"
332     "a=x-google-flag:conference\r\n"
333     "m=video 9 RTP/SAVPF 120\r\n"
334     "c=IN IP4 0.0.0.0\r\n"
335     "a=x-google-flag:conference\r\n";
336 
337 static const char kSdpSessionString[] =
338     "v=0\r\n"
339     "o=- 18446744069414584320 18446462598732840960 IN IP4 127.0.0.1\r\n"
340     "s=-\r\n"
341     "t=0 0\r\n"
342     "a=msid-semantic: WMS local_stream\r\n";
343 
344 static const char kSdpAudioString[] =
345     "m=audio 9 RTP/SAVPF 111\r\n"
346     "c=IN IP4 0.0.0.0\r\n"
347     "a=rtcp:9 IN IP4 0.0.0.0\r\n"
348     "a=ice-ufrag:ufrag_voice\r\na=ice-pwd:pwd_voice\r\n"
349     "a=mid:audio_content_name\r\n"
350     "a=sendrecv\r\n"
351     "a=rtpmap:111 opus/48000/2\r\n"
352     "a=ssrc:1 cname:stream_1_cname\r\n"
353     "a=ssrc:1 msid:local_stream audio_track_id_1\r\n"
354     "a=ssrc:1 mslabel:local_stream\r\n"
355     "a=ssrc:1 label:audio_track_id_1\r\n";
356 
357 static const char kSdpVideoString[] =
358     "m=video 9 RTP/SAVPF 120\r\n"
359     "c=IN IP4 0.0.0.0\r\n"
360     "a=rtcp:9 IN IP4 0.0.0.0\r\n"
361     "a=ice-ufrag:ufrag_video\r\na=ice-pwd:pwd_video\r\n"
362     "a=mid:video_content_name\r\n"
363     "a=sendrecv\r\n"
364     "a=rtpmap:120 VP8/90000\r\n"
365     "a=ssrc:2 cname:stream_1_cname\r\n"
366     "a=ssrc:2 msid:local_stream video_track_id_1\r\n"
367     "a=ssrc:2 mslabel:local_stream\r\n"
368     "a=ssrc:2 label:video_track_id_1\r\n";
369 
370 // Reference sdp string using bundle-only.
371 static const char kBundleOnlySdpFullString[] =
372     "v=0\r\n"
373     "o=- 18446744069414584320 18446462598732840960 IN IP4 127.0.0.1\r\n"
374     "s=-\r\n"
375     "t=0 0\r\n"
376     "a=group:BUNDLE audio_content_name video_content_name\r\n"
377     "a=msid-semantic: WMS local_stream_1\r\n"
378     "m=audio 2345 RTP/SAVPF 111 103 104\r\n"
379     "c=IN IP4 74.125.127.126\r\n"
380     "a=rtcp:2347 IN IP4 74.125.127.126\r\n"
381     "a=candidate:a0+B/1 1 udp 2130706432 192.168.1.5 1234 typ host "
382     "generation 2\r\n"
383     "a=candidate:a0+B/1 2 udp 2130706432 192.168.1.5 1235 typ host "
384     "generation 2\r\n"
385     "a=candidate:a0+B/2 1 udp 2130706432 ::1 1238 typ host "
386     "generation 2\r\n"
387     "a=candidate:a0+B/2 2 udp 2130706432 ::1 1239 typ host "
388     "generation 2\r\n"
389     "a=candidate:a0+B/3 1 udp 2130706432 74.125.127.126 2345 typ srflx "
390     "raddr 192.168.1.5 rport 2346 "
391     "generation 2\r\n"
392     "a=candidate:a0+B/3 2 udp 2130706432 74.125.127.126 2347 typ srflx "
393     "raddr 192.168.1.5 rport 2348 "
394     "generation 2\r\n"
395     "a=ice-ufrag:ufrag_voice\r\na=ice-pwd:pwd_voice\r\n"
396     "a=mid:audio_content_name\r\n"
397     "a=sendrecv\r\n"
398     "a=rtcp-mux\r\n"
399     "a=rtcp-rsize\r\n"
400     "a=crypto:1 AES_CM_128_HMAC_SHA1_32 "
401     "inline:NzB4d1BINUAvLEw6UzF3WSJ+PSdFcGdUJShpX1Zj|2^20|1:32 "
402     "dummy_session_params\r\n"
403     "a=rtpmap:111 opus/48000/2\r\n"
404     "a=rtpmap:103 ISAC/16000\r\n"
405     "a=rtpmap:104 ISAC/32000\r\n"
406     "a=ssrc:1 cname:stream_1_cname\r\n"
407     "a=ssrc:1 msid:local_stream_1 audio_track_id_1\r\n"
408     "a=ssrc:1 mslabel:local_stream_1\r\n"
409     "a=ssrc:1 label:audio_track_id_1\r\n"
410     "m=video 0 RTP/SAVPF 120\r\n"
411     "c=IN IP4 0.0.0.0\r\n"
412     "a=rtcp:9 IN IP4 0.0.0.0\r\n"
413     "a=bundle-only\r\n"
414     "a=mid:video_content_name\r\n"
415     "a=sendrecv\r\n"
416     "a=crypto:1 AES_CM_128_HMAC_SHA1_80 "
417     "inline:d0RmdmcmVCspeEc3QGZiNWpVLFJhQX1cfHAwJSoj|2^20|1:32\r\n"
418     "a=rtpmap:120 VP8/90000\r\n"
419     "a=ssrc-group:FEC 2 3\r\n"
420     "a=ssrc:2 cname:stream_1_cname\r\n"
421     "a=ssrc:2 msid:local_stream_1 video_track_id_1\r\n"
422     "a=ssrc:2 mslabel:local_stream_1\r\n"
423     "a=ssrc:2 label:video_track_id_1\r\n"
424     "a=ssrc:3 cname:stream_1_cname\r\n"
425     "a=ssrc:3 msid:local_stream_1 video_track_id_1\r\n"
426     "a=ssrc:3 mslabel:local_stream_1\r\n"
427     "a=ssrc:3 label:video_track_id_1\r\n";
428 
429 // Plan B SDP reference string, with 2 streams, 2 audio tracks and 3 video
430 // tracks.
431 static const char kPlanBSdpFullString[] =
432     "v=0\r\n"
433     "o=- 18446744069414584320 18446462598732840960 IN IP4 127.0.0.1\r\n"
434     "s=-\r\n"
435     "t=0 0\r\n"
436     "a=msid-semantic: WMS local_stream_1 local_stream_2\r\n"
437     "m=audio 2345 RTP/SAVPF 111 103 104\r\n"
438     "c=IN IP4 74.125.127.126\r\n"
439     "a=rtcp:2347 IN IP4 74.125.127.126\r\n"
440     "a=candidate:a0+B/1 1 udp 2130706432 192.168.1.5 1234 typ host "
441     "generation 2\r\n"
442     "a=candidate:a0+B/1 2 udp 2130706432 192.168.1.5 1235 typ host "
443     "generation 2\r\n"
444     "a=candidate:a0+B/2 1 udp 2130706432 ::1 1238 typ host "
445     "generation 2\r\n"
446     "a=candidate:a0+B/2 2 udp 2130706432 ::1 1239 typ host "
447     "generation 2\r\n"
448     "a=candidate:a0+B/3 1 udp 2130706432 74.125.127.126 2345 typ srflx "
449     "raddr 192.168.1.5 rport 2346 "
450     "generation 2\r\n"
451     "a=candidate:a0+B/3 2 udp 2130706432 74.125.127.126 2347 typ srflx "
452     "raddr 192.168.1.5 rport 2348 "
453     "generation 2\r\n"
454     "a=ice-ufrag:ufrag_voice\r\na=ice-pwd:pwd_voice\r\n"
455     "a=mid:audio_content_name\r\n"
456     "a=sendrecv\r\n"
457     "a=rtcp-mux\r\n"
458     "a=rtcp-rsize\r\n"
459     "a=crypto:1 AES_CM_128_HMAC_SHA1_32 "
460     "inline:NzB4d1BINUAvLEw6UzF3WSJ+PSdFcGdUJShpX1Zj|2^20|1:32 "
461     "dummy_session_params\r\n"
462     "a=rtpmap:111 opus/48000/2\r\n"
463     "a=rtpmap:103 ISAC/16000\r\n"
464     "a=rtpmap:104 ISAC/32000\r\n"
465     "a=ssrc:1 cname:stream_1_cname\r\n"
466     "a=ssrc:1 msid:local_stream_1 audio_track_id_1\r\n"
467     "a=ssrc:1 mslabel:local_stream_1\r\n"
468     "a=ssrc:1 label:audio_track_id_1\r\n"
469     "a=ssrc:4 cname:stream_2_cname\r\n"
470     "a=ssrc:4 msid:local_stream_2 audio_track_id_2\r\n"
471     "a=ssrc:4 mslabel:local_stream_2\r\n"
472     "a=ssrc:4 label:audio_track_id_2\r\n"
473     "m=video 3457 RTP/SAVPF 120\r\n"
474     "c=IN IP4 74.125.224.39\r\n"
475     "a=rtcp:3456 IN IP4 74.125.224.39\r\n"
476     "a=candidate:a0+B/1 2 udp 2130706432 192.168.1.5 1236 typ host "
477     "generation 2\r\n"
478     "a=candidate:a0+B/1 1 udp 2130706432 192.168.1.5 1237 typ host "
479     "generation 2\r\n"
480     "a=candidate:a0+B/2 2 udp 2130706432 ::1 1240 typ host "
481     "generation 2\r\n"
482     "a=candidate:a0+B/2 1 udp 2130706432 ::1 1241 typ host "
483     "generation 2\r\n"
484     "a=candidate:a0+B/4 2 udp 2130706432 74.125.224.39 3456 typ relay "
485     "generation 2\r\n"
486     "a=candidate:a0+B/4 1 udp 2130706432 74.125.224.39 3457 typ relay "
487     "generation 2\r\n"
488     "a=ice-ufrag:ufrag_video\r\na=ice-pwd:pwd_video\r\n"
489     "a=mid:video_content_name\r\n"
490     "a=sendrecv\r\n"
491     "a=crypto:1 AES_CM_128_HMAC_SHA1_80 "
492     "inline:d0RmdmcmVCspeEc3QGZiNWpVLFJhQX1cfHAwJSoj|2^20|1:32\r\n"
493     "a=rtpmap:120 VP8/90000\r\n"
494     "a=ssrc-group:FEC 2 3\r\n"
495     "a=ssrc:2 cname:stream_1_cname\r\n"
496     "a=ssrc:2 msid:local_stream_1 video_track_id_1\r\n"
497     "a=ssrc:2 mslabel:local_stream_1\r\n"
498     "a=ssrc:2 label:video_track_id_1\r\n"
499     "a=ssrc:3 cname:stream_1_cname\r\n"
500     "a=ssrc:3 msid:local_stream_1 video_track_id_1\r\n"
501     "a=ssrc:3 mslabel:local_stream_1\r\n"
502     "a=ssrc:3 label:video_track_id_1\r\n"
503     "a=ssrc:5 cname:stream_2_cname\r\n"
504     "a=ssrc:5 msid:local_stream_2 video_track_id_2\r\n"
505     "a=ssrc:5 mslabel:local_stream_2\r\n"
506     "a=ssrc:5 label:video_track_id_2\r\n"
507     "a=ssrc:6 cname:stream_2_cname\r\n"
508     "a=ssrc:6 msid:local_stream_2 video_track_id_3\r\n"
509     "a=ssrc:6 mslabel:local_stream_2\r\n"
510     "a=ssrc:6 label:video_track_id_3\r\n";
511 
512 // Unified Plan SDP reference string, with 2 streams, 2 audio tracks and 3 video
513 // tracks.
514 static const char kUnifiedPlanSdpFullString[] =
515     "v=0\r\n"
516     "o=- 18446744069414584320 18446462598732840960 IN IP4 127.0.0.1\r\n"
517     "s=-\r\n"
518     "t=0 0\r\n"
519     "a=msid-semantic: WMS local_stream_1\r\n"
520     // Audio track 1, stream 1 (with candidates).
521     "m=audio 2345 RTP/SAVPF 111 103 104\r\n"
522     "c=IN IP4 74.125.127.126\r\n"
523     "a=rtcp:2347 IN IP4 74.125.127.126\r\n"
524     "a=candidate:a0+B/1 1 udp 2130706432 192.168.1.5 1234 typ host "
525     "generation 2\r\n"
526     "a=candidate:a0+B/1 2 udp 2130706432 192.168.1.5 1235 typ host "
527     "generation 2\r\n"
528     "a=candidate:a0+B/2 1 udp 2130706432 ::1 1238 typ host "
529     "generation 2\r\n"
530     "a=candidate:a0+B/2 2 udp 2130706432 ::1 1239 typ host "
531     "generation 2\r\n"
532     "a=candidate:a0+B/3 1 udp 2130706432 74.125.127.126 2345 typ srflx "
533     "raddr 192.168.1.5 rport 2346 "
534     "generation 2\r\n"
535     "a=candidate:a0+B/3 2 udp 2130706432 74.125.127.126 2347 typ srflx "
536     "raddr 192.168.1.5 rport 2348 "
537     "generation 2\r\n"
538     "a=ice-ufrag:ufrag_voice\r\na=ice-pwd:pwd_voice\r\n"
539     "a=mid:audio_content_name\r\n"
540     "a=msid:local_stream_1 audio_track_id_1\r\n"
541     "a=sendrecv\r\n"
542     "a=rtcp-mux\r\n"
543     "a=rtcp-rsize\r\n"
544     "a=crypto:1 AES_CM_128_HMAC_SHA1_32 "
545     "inline:NzB4d1BINUAvLEw6UzF3WSJ+PSdFcGdUJShpX1Zj|2^20|1:32 "
546     "dummy_session_params\r\n"
547     "a=rtpmap:111 opus/48000/2\r\n"
548     "a=rtpmap:103 ISAC/16000\r\n"
549     "a=rtpmap:104 ISAC/32000\r\n"
550     "a=ssrc:1 cname:stream_1_cname\r\n"
551     // Video track 1, stream 1 (with candidates).
552     "m=video 3457 RTP/SAVPF 120\r\n"
553     "c=IN IP4 74.125.224.39\r\n"
554     "a=rtcp:3456 IN IP4 74.125.224.39\r\n"
555     "a=candidate:a0+B/1 2 udp 2130706432 192.168.1.5 1236 typ host "
556     "generation 2\r\n"
557     "a=candidate:a0+B/1 1 udp 2130706432 192.168.1.5 1237 typ host "
558     "generation 2\r\n"
559     "a=candidate:a0+B/2 2 udp 2130706432 ::1 1240 typ host "
560     "generation 2\r\n"
561     "a=candidate:a0+B/2 1 udp 2130706432 ::1 1241 typ host "
562     "generation 2\r\n"
563     "a=candidate:a0+B/4 2 udp 2130706432 74.125.224.39 3456 typ relay "
564     "generation 2\r\n"
565     "a=candidate:a0+B/4 1 udp 2130706432 74.125.224.39 3457 typ relay "
566     "generation 2\r\n"
567     "a=ice-ufrag:ufrag_video\r\na=ice-pwd:pwd_video\r\n"
568     "a=mid:video_content_name\r\n"
569     "a=msid:local_stream_1 video_track_id_1\r\n"
570     "a=sendrecv\r\n"
571     "a=crypto:1 AES_CM_128_HMAC_SHA1_80 "
572     "inline:d0RmdmcmVCspeEc3QGZiNWpVLFJhQX1cfHAwJSoj|2^20|1:32\r\n"
573     "a=rtpmap:120 VP8/90000\r\n"
574     "a=ssrc-group:FEC 2 3\r\n"
575     "a=ssrc:2 cname:stream_1_cname\r\n"
576     "a=ssrc:3 cname:stream_1_cname\r\n"
577     // Audio track 2, stream 2.
578     "m=audio 9 RTP/SAVPF 111 103 104\r\n"
579     "c=IN IP4 0.0.0.0\r\n"
580     "a=rtcp:9 IN IP4 0.0.0.0\r\n"
581     "a=ice-ufrag:ufrag_voice_2\r\na=ice-pwd:pwd_voice_2\r\n"
582     "a=mid:audio_content_name_2\r\n"
583     "a=msid:local_stream_2 audio_track_id_2\r\n"
584     "a=sendrecv\r\n"
585     "a=rtcp-mux\r\n"
586     "a=rtcp-rsize\r\n"
587     "a=crypto:1 AES_CM_128_HMAC_SHA1_32 "
588     "inline:NzB4d1BINUAvLEw6UzF3WSJ+PSdFcGdUJShpX1Zj|2^20|1:32 "
589     "dummy_session_params\r\n"
590     "a=rtpmap:111 opus/48000/2\r\n"
591     "a=rtpmap:103 ISAC/16000\r\n"
592     "a=rtpmap:104 ISAC/32000\r\n"
593     "a=ssrc:4 cname:stream_2_cname\r\n"
594     // Video track 2, stream 2.
595     "m=video 9 RTP/SAVPF 120\r\n"
596     "c=IN IP4 0.0.0.0\r\n"
597     "a=rtcp:9 IN IP4 0.0.0.0\r\n"
598     "a=ice-ufrag:ufrag_video_2\r\na=ice-pwd:pwd_video_2\r\n"
599     "a=mid:video_content_name_2\r\n"
600     "a=msid:local_stream_2 video_track_id_2\r\n"
601     "a=sendrecv\r\n"
602     "a=crypto:1 AES_CM_128_HMAC_SHA1_80 "
603     "inline:d0RmdmcmVCspeEc3QGZiNWpVLFJhQX1cfHAwJSoj|2^20|1:32\r\n"
604     "a=rtpmap:120 VP8/90000\r\n"
605     "a=ssrc:5 cname:stream_2_cname\r\n"
606     // Video track 3, stream 2.
607     "m=video 9 RTP/SAVPF 120\r\n"
608     "c=IN IP4 0.0.0.0\r\n"
609     "a=rtcp:9 IN IP4 0.0.0.0\r\n"
610     "a=ice-ufrag:ufrag_video_3\r\na=ice-pwd:pwd_video_3\r\n"
611     "a=mid:video_content_name_3\r\n"
612     "a=msid:local_stream_2 video_track_id_3\r\n"
613     "a=sendrecv\r\n"
614     "a=crypto:1 AES_CM_128_HMAC_SHA1_80 "
615     "inline:d0RmdmcmVCspeEc3QGZiNWpVLFJhQX1cfHAwJSoj|2^20|1:32\r\n"
616     "a=rtpmap:120 VP8/90000\r\n"
617     "a=ssrc:6 cname:stream_2_cname\r\n";
618 
619 // Unified Plan SDP reference string:
620 // - audio track 1 has 1 a=msid lines
621 // - audio track 2 has 2 a=msid lines
622 // - audio track 3 has 1 a=msid line with the special "-" marker signifying that
623 //   there are 0 media stream ids.
624 // This Unified Plan SDP represents a SDP that signals the msid using both
625 // a=msid and a=ssrc msid semantics.
626 static const char kUnifiedPlanSdpFullStringWithSpecialMsid[] =
627     "v=0\r\n"
628     "o=- 18446744069414584320 18446462598732840960 IN IP4 127.0.0.1\r\n"
629     "s=-\r\n"
630     "t=0 0\r\n"
631     "a=msid-semantic: WMS local_stream_1\r\n"
632     // Audio track 1, with 1 stream id.
633     "m=audio 2345 RTP/SAVPF 111 103 104\r\n"
634     "c=IN IP4 74.125.127.126\r\n"
635     "a=rtcp:2347 IN IP4 74.125.127.126\r\n"
636     "a=candidate:a0+B/1 1 udp 2130706432 192.168.1.5 1234 typ host "
637     "generation 2\r\n"
638     "a=candidate:a0+B/1 2 udp 2130706432 192.168.1.5 1235 typ host "
639     "generation 2\r\n"
640     "a=candidate:a0+B/2 1 udp 2130706432 ::1 1238 typ host "
641     "generation 2\r\n"
642     "a=candidate:a0+B/2 2 udp 2130706432 ::1 1239 typ host "
643     "generation 2\r\n"
644     "a=candidate:a0+B/3 1 udp 2130706432 74.125.127.126 2345 typ srflx "
645     "raddr 192.168.1.5 rport 2346 "
646     "generation 2\r\n"
647     "a=candidate:a0+B/3 2 udp 2130706432 74.125.127.126 2347 typ srflx "
648     "raddr 192.168.1.5 rport 2348 "
649     "generation 2\r\n"
650     "a=ice-ufrag:ufrag_voice\r\na=ice-pwd:pwd_voice\r\n"
651     "a=mid:audio_content_name\r\n"
652     "a=sendrecv\r\n"
653     "a=msid:local_stream_1 audio_track_id_1\r\n"
654     "a=rtcp-mux\r\n"
655     "a=rtcp-rsize\r\n"
656     "a=crypto:1 AES_CM_128_HMAC_SHA1_32 "
657     "inline:NzB4d1BINUAvLEw6UzF3WSJ+PSdFcGdUJShpX1Zj|2^20|1:32 "
658     "dummy_session_params\r\n"
659     "a=rtpmap:111 opus/48000/2\r\n"
660     "a=rtpmap:103 ISAC/16000\r\n"
661     "a=rtpmap:104 ISAC/32000\r\n"
662     "a=ssrc:1 cname:stream_1_cname\r\n"
663     "a=ssrc:1 msid:local_stream_1 audio_track_id_1\r\n"
664     "a=ssrc:1 mslabel:local_stream_1\r\n"
665     "a=ssrc:1 label:audio_track_id_1\r\n"
666     // Audio track 2, with two stream ids.
667     "m=audio 9 RTP/SAVPF 111 103 104\r\n"
668     "c=IN IP4 0.0.0.0\r\n"
669     "a=rtcp:9 IN IP4 0.0.0.0\r\n"
670     "a=ice-ufrag:ufrag_voice_2\r\na=ice-pwd:pwd_voice_2\r\n"
671     "a=mid:audio_content_name_2\r\n"
672     "a=sendrecv\r\n"
673     "a=msid:local_stream_1 audio_track_id_2\r\n"
674     "a=msid:local_stream_2 audio_track_id_2\r\n"
675     "a=rtcp-mux\r\n"
676     "a=rtcp-rsize\r\n"
677     "a=crypto:1 AES_CM_128_HMAC_SHA1_32 "
678     "inline:NzB4d1BINUAvLEw6UzF3WSJ+PSdFcGdUJShpX1Zj|2^20|1:32 "
679     "dummy_session_params\r\n"
680     "a=rtpmap:111 opus/48000/2\r\n"
681     "a=rtpmap:103 ISAC/16000\r\n"
682     "a=rtpmap:104 ISAC/32000\r\n"
683     "a=ssrc:4 cname:stream_1_cname\r\n"
684     // The support for Plan B msid signaling only includes the
685     // first media stream id "local_stream_1."
686     "a=ssrc:4 msid:local_stream_1 audio_track_id_2\r\n"
687     "a=ssrc:4 mslabel:local_stream_1\r\n"
688     "a=ssrc:4 label:audio_track_id_2\r\n"
689     // Audio track 3, with no stream ids.
690     "m=audio 9 RTP/SAVPF 111 103 104\r\n"
691     "c=IN IP4 0.0.0.0\r\n"
692     "a=rtcp:9 IN IP4 0.0.0.0\r\n"
693     "a=ice-ufrag:ufrag_voice_3\r\na=ice-pwd:pwd_voice_3\r\n"
694     "a=mid:audio_content_name_3\r\n"
695     "a=sendrecv\r\n"
696     "a=msid:- audio_track_id_3\r\n"
697     "a=rtcp-mux\r\n"
698     "a=rtcp-rsize\r\n"
699     "a=crypto:1 AES_CM_128_HMAC_SHA1_32 "
700     "inline:NzB4d1BINUAvLEw6UzF3WSJ+PSdFcGdUJShpX1Zj|2^20|1:32 "
701     "dummy_session_params\r\n"
702     "a=rtpmap:111 opus/48000/2\r\n"
703     "a=rtpmap:103 ISAC/16000\r\n"
704     "a=rtpmap:104 ISAC/32000\r\n"
705     "a=ssrc:7 cname:stream_2_cname\r\n"
706     "a=ssrc:7 msid:- audio_track_id_3\r\n"
707     "a=ssrc:7 mslabel:-\r\n"
708     "a=ssrc:7 label:audio_track_id_3\r\n";
709 
710 // SDP string for unified plan without SSRCs
711 static const char kUnifiedPlanSdpFullStringNoSsrc[] =
712     "v=0\r\n"
713     "o=- 18446744069414584320 18446462598732840960 IN IP4 127.0.0.1\r\n"
714     "s=-\r\n"
715     "t=0 0\r\n"
716     "a=msid-semantic: WMS local_stream_1\r\n"
717     // Audio track 1, stream 1 (with candidates).
718     "m=audio 2345 RTP/SAVPF 111 103 104\r\n"
719     "c=IN IP4 74.125.127.126\r\n"
720     "a=rtcp:2347 IN IP4 74.125.127.126\r\n"
721     "a=candidate:a0+B/1 1 udp 2130706432 192.168.1.5 1234 typ host "
722     "generation 2\r\n"
723     "a=candidate:a0+B/1 2 udp 2130706432 192.168.1.5 1235 typ host "
724     "generation 2\r\n"
725     "a=candidate:a0+B/2 1 udp 2130706432 ::1 1238 typ host "
726     "generation 2\r\n"
727     "a=candidate:a0+B/2 2 udp 2130706432 ::1 1239 typ host "
728     "generation 2\r\n"
729     "a=candidate:a0+B/3 1 udp 2130706432 74.125.127.126 2345 typ srflx "
730     "raddr 192.168.1.5 rport 2346 "
731     "generation 2\r\n"
732     "a=candidate:a0+B/3 2 udp 2130706432 74.125.127.126 2347 typ srflx "
733     "raddr 192.168.1.5 rport 2348 "
734     "generation 2\r\n"
735     "a=ice-ufrag:ufrag_voice\r\na=ice-pwd:pwd_voice\r\n"
736     "a=mid:audio_content_name\r\n"
737     "a=msid:local_stream_1 audio_track_id_1\r\n"
738     "a=sendrecv\r\n"
739     "a=rtcp-mux\r\n"
740     "a=rtcp-rsize\r\n"
741     "a=crypto:1 AES_CM_128_HMAC_SHA1_32 "
742     "inline:NzB4d1BINUAvLEw6UzF3WSJ+PSdFcGdUJShpX1Zj|2^20|1:32 "
743     "dummy_session_params\r\n"
744     "a=rtpmap:111 opus/48000/2\r\n"
745     "a=rtpmap:103 ISAC/16000\r\n"
746     "a=rtpmap:104 ISAC/32000\r\n"
747     // Video track 1, stream 1 (with candidates).
748     "m=video 3457 RTP/SAVPF 120\r\n"
749     "c=IN IP4 74.125.224.39\r\n"
750     "a=rtcp:3456 IN IP4 74.125.224.39\r\n"
751     "a=candidate:a0+B/1 2 udp 2130706432 192.168.1.5 1236 typ host "
752     "generation 2\r\n"
753     "a=candidate:a0+B/1 1 udp 2130706432 192.168.1.5 1237 typ host "
754     "generation 2\r\n"
755     "a=candidate:a0+B/2 2 udp 2130706432 ::1 1240 typ host "
756     "generation 2\r\n"
757     "a=candidate:a0+B/2 1 udp 2130706432 ::1 1241 typ host "
758     "generation 2\r\n"
759     "a=candidate:a0+B/4 2 udp 2130706432 74.125.224.39 3456 typ relay "
760     "generation 2\r\n"
761     "a=candidate:a0+B/4 1 udp 2130706432 74.125.224.39 3457 typ relay "
762     "generation 2\r\n"
763     "a=ice-ufrag:ufrag_video\r\na=ice-pwd:pwd_video\r\n"
764     "a=mid:video_content_name\r\n"
765     "a=msid:local_stream_1 video_track_id_1\r\n"
766     "a=sendrecv\r\n"
767     "a=crypto:1 AES_CM_128_HMAC_SHA1_80 "
768     "inline:d0RmdmcmVCspeEc3QGZiNWpVLFJhQX1cfHAwJSoj|2^20|1:32\r\n"
769     "a=rtpmap:120 VP8/90000\r\n"
770     // Audio track 2, stream 2.
771     "m=audio 9 RTP/SAVPF 111 103 104\r\n"
772     "c=IN IP4 0.0.0.0\r\n"
773     "a=rtcp:9 IN IP4 0.0.0.0\r\n"
774     "a=ice-ufrag:ufrag_voice_2\r\na=ice-pwd:pwd_voice_2\r\n"
775     "a=mid:audio_content_name_2\r\n"
776     "a=msid:local_stream_2 audio_track_id_2\r\n"
777     "a=sendrecv\r\n"
778     "a=rtcp-mux\r\n"
779     "a=rtcp-rsize\r\n"
780     "a=crypto:1 AES_CM_128_HMAC_SHA1_32 "
781     "inline:NzB4d1BINUAvLEw6UzF3WSJ+PSdFcGdUJShpX1Zj|2^20|1:32 "
782     "dummy_session_params\r\n"
783     "a=rtpmap:111 opus/48000/2\r\n"
784     "a=rtpmap:103 ISAC/16000\r\n"
785     "a=rtpmap:104 ISAC/32000\r\n"
786     // Video track 2, stream 2.
787     "m=video 9 RTP/SAVPF 120\r\n"
788     "c=IN IP4 0.0.0.0\r\n"
789     "a=rtcp:9 IN IP4 0.0.0.0\r\n"
790     "a=ice-ufrag:ufrag_video_2\r\na=ice-pwd:pwd_video_2\r\n"
791     "a=mid:video_content_name_2\r\n"
792     "a=msid:local_stream_2 video_track_id_2\r\n"
793     "a=sendrecv\r\n"
794     "a=crypto:1 AES_CM_128_HMAC_SHA1_80 "
795     "inline:d0RmdmcmVCspeEc3QGZiNWpVLFJhQX1cfHAwJSoj|2^20|1:32\r\n"
796     "a=rtpmap:120 VP8/90000\r\n"
797     // Video track 3, stream 2.
798     "m=video 9 RTP/SAVPF 120\r\n"
799     "c=IN IP4 0.0.0.0\r\n"
800     "a=rtcp:9 IN IP4 0.0.0.0\r\n"
801     "a=ice-ufrag:ufrag_video_3\r\na=ice-pwd:pwd_video_3\r\n"
802     "a=mid:video_content_name_3\r\n"
803     "a=msid:local_stream_2 video_track_id_3\r\n"
804     "a=sendrecv\r\n"
805     "a=crypto:1 AES_CM_128_HMAC_SHA1_80 "
806     "inline:d0RmdmcmVCspeEc3QGZiNWpVLFJhQX1cfHAwJSoj|2^20|1:32\r\n"
807     "a=rtpmap:120 VP8/90000\r\n";
808 
809 // One candidate reference string as per W3c spec.
810 // candidate:<blah> not a=candidate:<blah>CRLF
811 static const char kRawCandidate[] =
812     "candidate:a0+B/1 1 udp 2130706432 192.168.1.5 1234 typ host generation 2";
813 // One candidate reference string.
814 static const char kSdpOneCandidate[] =
815     "a=candidate:a0+B/1 1 udp 2130706432 192.168.1.5 1234 typ host "
816     "generation 2\r\n";
817 
818 static const char kSdpTcpActiveCandidate[] =
819     "candidate:a0+B/1 1 tcp 2130706432 192.168.1.5 9 typ host "
820     "tcptype active generation 2";
821 static const char kSdpTcpPassiveCandidate[] =
822     "candidate:a0+B/1 1 tcp 2130706432 192.168.1.5 9 typ host "
823     "tcptype passive generation 2";
824 static const char kSdpTcpSOCandidate[] =
825     "candidate:a0+B/1 1 tcp 2130706432 192.168.1.5 9 typ host "
826     "tcptype so generation 2";
827 static const char kSdpTcpInvalidCandidate[] =
828     "candidate:a0+B/1 1 tcp 2130706432 192.168.1.5 9 typ host "
829     "tcptype invalid generation 2";
830 
831 // One candidate reference string with IPV6 address.
832 static const char kRawIPV6Candidate[] =
833     "candidate:a0+B/1 1 udp 2130706432 "
834     "abcd:abcd:abcd:abcd:abcd:abcd:abcd:abcd 1234 typ host generation 2";
835 
836 // One candidate reference string.
837 static const char kSdpOneCandidateWithUfragPwd[] =
838     "a=candidate:a0+B/1 1 udp 2130706432 192.168.1.5 1234 typ host network_name"
839     " eth0 ufrag user_rtp pwd password_rtp generation 2\r\n";
840 
841 static const char kRawHostnameCandidate[] =
842     "candidate:a0+B/1 1 udp 2130706432 a.test 1234 typ host generation 2";
843 
844 // Session id and version
845 static const char kSessionId[] = "18446744069414584320";
846 static const char kSessionVersion[] = "18446462598732840960";
847 
848 // ICE options.
849 static const char kIceOption1[] = "iceoption1";
850 static const char kIceOption2[] = "iceoption2";
851 static const char kIceOption3[] = "iceoption3";
852 
853 // ICE ufrags/passwords.
854 static const char kUfragVoice[] = "ufrag_voice";
855 static const char kPwdVoice[] = "pwd_voice";
856 static const char kUfragVideo[] = "ufrag_video";
857 static const char kPwdVideo[] = "pwd_video";
858 static const char kUfragData[] = "ufrag_data";
859 static const char kPwdData[] = "pwd_data";
860 
861 // Extra ufrags/passwords for extra unified plan m= sections.
862 static const char kUfragVoice2[] = "ufrag_voice_2";
863 static const char kPwdVoice2[] = "pwd_voice_2";
864 static const char kUfragVoice3[] = "ufrag_voice_3";
865 static const char kPwdVoice3[] = "pwd_voice_3";
866 static const char kUfragVideo2[] = "ufrag_video_2";
867 static const char kPwdVideo2[] = "pwd_video_2";
868 static const char kUfragVideo3[] = "ufrag_video_3";
869 static const char kPwdVideo3[] = "pwd_video_3";
870 
871 // Content name
872 static const char kAudioContentName[] = "audio_content_name";
873 static const char kVideoContentName[] = "video_content_name";
874 static const char kDataContentName[] = "data_content_name";
875 
876 // Extra content names for extra unified plan m= sections.
877 static const char kAudioContentName2[] = "audio_content_name_2";
878 static const char kAudioContentName3[] = "audio_content_name_3";
879 static const char kVideoContentName2[] = "video_content_name_2";
880 static const char kVideoContentName3[] = "video_content_name_3";
881 
882 // MediaStream 1
883 static const char kStreamId1[] = "local_stream_1";
884 static const char kStream1Cname[] = "stream_1_cname";
885 static const char kAudioTrackId1[] = "audio_track_id_1";
886 static const uint32_t kAudioTrack1Ssrc = 1;
887 static const char kVideoTrackId1[] = "video_track_id_1";
888 static const uint32_t kVideoTrack1Ssrc1 = 2;
889 static const uint32_t kVideoTrack1Ssrc2 = 3;
890 
891 // MediaStream 2
892 static const char kStreamId2[] = "local_stream_2";
893 static const char kStream2Cname[] = "stream_2_cname";
894 static const char kAudioTrackId2[] = "audio_track_id_2";
895 static const uint32_t kAudioTrack2Ssrc = 4;
896 static const char kVideoTrackId2[] = "video_track_id_2";
897 static const uint32_t kVideoTrack2Ssrc = 5;
898 static const char kVideoTrackId3[] = "video_track_id_3";
899 static const uint32_t kVideoTrack3Ssrc = 6;
900 static const char kAudioTrackId3[] = "audio_track_id_3";
901 static const uint32_t kAudioTrack3Ssrc = 7;
902 
903 // DataChannel
904 static const char kDataChannelLabel[] = "data_channel";
905 static const char kDataChannelMsid[] = "data_channeld0";
906 static const char kDataChannelCname[] = "data_channel_cname";
907 static const uint32_t kDataChannelSsrc = 10;
908 
909 // Candidate
910 static const char kDummyMid[] = "dummy_mid";
911 static const int kDummyIndex = 123;
912 
913 // Misc
914 static SdpType kDummyType = SdpType::kOffer;
915 
916 // Helper functions
917 
SdpDeserialize(const std::string & message,JsepSessionDescription * jdesc)918 static bool SdpDeserialize(const std::string& message,
919                            JsepSessionDescription* jdesc) {
920   return webrtc::SdpDeserialize(message, jdesc, NULL);
921 }
922 
SdpDeserializeCandidate(const std::string & message,JsepIceCandidate * candidate)923 static bool SdpDeserializeCandidate(const std::string& message,
924                                     JsepIceCandidate* candidate) {
925   return webrtc::SdpDeserializeCandidate(message, candidate, NULL);
926 }
927 
928 // Add some extra |newlines| to the |message| after |line|.
InjectAfter(const std::string & line,const std::string & newlines,std::string * message)929 static void InjectAfter(const std::string& line,
930                         const std::string& newlines,
931                         std::string* message) {
932   absl::StrReplaceAll({{line, line + newlines}}, message);
933 }
934 
Replace(const std::string & line,const std::string & newlines,std::string * message)935 static void Replace(const std::string& line,
936                     const std::string& newlines,
937                     std::string* message) {
938   absl::StrReplaceAll({{line, newlines}}, message);
939 }
940 
941 // Expect fail to parase |bad_sdp| and expect |bad_part| be part of the error
942 // message.
ExpectParseFailure(const std::string & bad_sdp,const std::string & bad_part)943 static void ExpectParseFailure(const std::string& bad_sdp,
944                                const std::string& bad_part) {
945   JsepSessionDescription desc(kDummyType);
946   SdpParseError error;
947   bool ret = webrtc::SdpDeserialize(bad_sdp, &desc, &error);
948   EXPECT_FALSE(ret);
949   EXPECT_NE(std::string::npos, error.line.find(bad_part.c_str()));
950 }
951 
952 // Expect fail to parse kSdpFullString if replace |good_part| with |bad_part|.
ExpectParseFailure(const char * good_part,const char * bad_part)953 static void ExpectParseFailure(const char* good_part, const char* bad_part) {
954   std::string bad_sdp = kSdpFullString;
955   Replace(good_part, bad_part, &bad_sdp);
956   ExpectParseFailure(bad_sdp, bad_part);
957 }
958 
959 // Expect fail to parse kSdpFullString if add |newlines| after |injectpoint|.
ExpectParseFailureWithNewLines(const std::string & injectpoint,const std::string & newlines,const std::string & bad_part)960 static void ExpectParseFailureWithNewLines(const std::string& injectpoint,
961                                            const std::string& newlines,
962                                            const std::string& bad_part) {
963   std::string bad_sdp = kSdpFullString;
964   InjectAfter(injectpoint, newlines, &bad_sdp);
965   ExpectParseFailure(bad_sdp, bad_part);
966 }
967 
ReplaceDirection(RtpTransceiverDirection direction,std::string * message)968 static void ReplaceDirection(RtpTransceiverDirection direction,
969                              std::string* message) {
970   std::string new_direction;
971   switch (direction) {
972     case RtpTransceiverDirection::kInactive:
973       new_direction = "a=inactive";
974       break;
975     case RtpTransceiverDirection::kSendOnly:
976       new_direction = "a=sendonly";
977       break;
978     case RtpTransceiverDirection::kRecvOnly:
979       new_direction = "a=recvonly";
980       break;
981     case RtpTransceiverDirection::kSendRecv:
982       new_direction = "a=sendrecv";
983       break;
984     case RtpTransceiverDirection::kStopped:
985     default:
986       RTC_NOTREACHED();
987       new_direction = "a=sendrecv";
988       break;
989   }
990   Replace("a=sendrecv", new_direction, message);
991 }
992 
ReplaceRejected(bool audio_rejected,bool video_rejected,std::string * message)993 static void ReplaceRejected(bool audio_rejected,
994                             bool video_rejected,
995                             std::string* message) {
996   if (audio_rejected) {
997     Replace("m=audio 9", "m=audio 0", message);
998     Replace(kAttributeIceUfragVoice, "", message);
999     Replace(kAttributeIcePwdVoice, "", message);
1000   }
1001   if (video_rejected) {
1002     Replace("m=video 9", "m=video 0", message);
1003     Replace(kAttributeIceUfragVideo, "", message);
1004     Replace(kAttributeIcePwdVideo, "", message);
1005   }
1006 }
1007 
1008 // WebRtcSdpTest
1009 
1010 class WebRtcSdpTest : public ::testing::Test {
1011  public:
WebRtcSdpTest()1012   WebRtcSdpTest() : jdesc_(kDummyType) {
1013 #ifdef WEBRTC_ANDROID
1014     webrtc::InitializeAndroidObjects();
1015 #endif
1016     // AudioContentDescription
1017     audio_desc_ = CreateAudioContentDescription();
1018     StreamParams audio_stream;
1019     audio_stream.id = kAudioTrackId1;
1020     audio_stream.cname = kStream1Cname;
1021     audio_stream.set_stream_ids({kStreamId1});
1022     audio_stream.ssrcs.push_back(kAudioTrack1Ssrc);
1023     audio_desc_->AddStream(audio_stream);
1024     rtc::SocketAddress audio_addr("74.125.127.126", 2345);
1025     audio_desc_->set_connection_address(audio_addr);
1026     desc_.AddContent(kAudioContentName, MediaProtocolType::kRtp,
1027                      absl::WrapUnique(audio_desc_));
1028 
1029     // VideoContentDescription
1030     video_desc_ = CreateVideoContentDescription();
1031     StreamParams video_stream;
1032     video_stream.id = kVideoTrackId1;
1033     video_stream.cname = kStream1Cname;
1034     video_stream.set_stream_ids({kStreamId1});
1035     video_stream.ssrcs.push_back(kVideoTrack1Ssrc1);
1036     video_stream.ssrcs.push_back(kVideoTrack1Ssrc2);
1037     cricket::SsrcGroup ssrc_group(kFecSsrcGroupSemantics, video_stream.ssrcs);
1038     video_stream.ssrc_groups.push_back(ssrc_group);
1039     video_desc_->AddStream(video_stream);
1040     rtc::SocketAddress video_addr("74.125.224.39", 3457);
1041     video_desc_->set_connection_address(video_addr);
1042     desc_.AddContent(kVideoContentName, MediaProtocolType::kRtp,
1043                      absl::WrapUnique(video_desc_));
1044 
1045     // TransportInfo
1046     desc_.AddTransportInfo(TransportInfo(
1047         kAudioContentName, TransportDescription(kUfragVoice, kPwdVoice)));
1048     desc_.AddTransportInfo(TransportInfo(
1049         kVideoContentName, TransportDescription(kUfragVideo, kPwdVideo)));
1050 
1051     // v4 host
1052     int port = 1234;
1053     rtc::SocketAddress address("192.168.1.5", port++);
1054     Candidate candidate1(ICE_CANDIDATE_COMPONENT_RTP, "udp", address,
1055                          kCandidatePriority, "", "", LOCAL_PORT_TYPE,
1056                          kCandidateGeneration, kCandidateFoundation1);
1057     address.SetPort(port++);
1058     Candidate candidate2(ICE_CANDIDATE_COMPONENT_RTCP, "udp", address,
1059                          kCandidatePriority, "", "", LOCAL_PORT_TYPE,
1060                          kCandidateGeneration, kCandidateFoundation1);
1061     address.SetPort(port++);
1062     Candidate candidate3(ICE_CANDIDATE_COMPONENT_RTCP, "udp", address,
1063                          kCandidatePriority, "", "", LOCAL_PORT_TYPE,
1064                          kCandidateGeneration, kCandidateFoundation1);
1065     address.SetPort(port++);
1066     Candidate candidate4(ICE_CANDIDATE_COMPONENT_RTP, "udp", address,
1067                          kCandidatePriority, "", "", LOCAL_PORT_TYPE,
1068                          kCandidateGeneration, kCandidateFoundation1);
1069 
1070     // v6 host
1071     rtc::SocketAddress v6_address("::1", port++);
1072     cricket::Candidate candidate5(cricket::ICE_CANDIDATE_COMPONENT_RTP, "udp",
1073                                   v6_address, kCandidatePriority, "", "",
1074                                   cricket::LOCAL_PORT_TYPE,
1075                                   kCandidateGeneration, kCandidateFoundation2);
1076     v6_address.SetPort(port++);
1077     cricket::Candidate candidate6(cricket::ICE_CANDIDATE_COMPONENT_RTCP, "udp",
1078                                   v6_address, kCandidatePriority, "", "",
1079                                   cricket::LOCAL_PORT_TYPE,
1080                                   kCandidateGeneration, kCandidateFoundation2);
1081     v6_address.SetPort(port++);
1082     cricket::Candidate candidate7(cricket::ICE_CANDIDATE_COMPONENT_RTCP, "udp",
1083                                   v6_address, kCandidatePriority, "", "",
1084                                   cricket::LOCAL_PORT_TYPE,
1085                                   kCandidateGeneration, kCandidateFoundation2);
1086     v6_address.SetPort(port++);
1087     cricket::Candidate candidate8(cricket::ICE_CANDIDATE_COMPONENT_RTP, "udp",
1088                                   v6_address, kCandidatePriority, "", "",
1089                                   cricket::LOCAL_PORT_TYPE,
1090                                   kCandidateGeneration, kCandidateFoundation2);
1091 
1092     // stun
1093     int port_stun = 2345;
1094     rtc::SocketAddress address_stun("74.125.127.126", port_stun++);
1095     rtc::SocketAddress rel_address_stun("192.168.1.5", port_stun++);
1096     cricket::Candidate candidate9(cricket::ICE_CANDIDATE_COMPONENT_RTP, "udp",
1097                                   address_stun, kCandidatePriority, "", "",
1098                                   STUN_PORT_TYPE, kCandidateGeneration,
1099                                   kCandidateFoundation3);
1100     candidate9.set_related_address(rel_address_stun);
1101 
1102     address_stun.SetPort(port_stun++);
1103     rel_address_stun.SetPort(port_stun++);
1104     cricket::Candidate candidate10(cricket::ICE_CANDIDATE_COMPONENT_RTCP, "udp",
1105                                    address_stun, kCandidatePriority, "", "",
1106                                    STUN_PORT_TYPE, kCandidateGeneration,
1107                                    kCandidateFoundation3);
1108     candidate10.set_related_address(rel_address_stun);
1109 
1110     // relay
1111     int port_relay = 3456;
1112     rtc::SocketAddress address_relay("74.125.224.39", port_relay++);
1113     cricket::Candidate candidate11(cricket::ICE_CANDIDATE_COMPONENT_RTCP, "udp",
1114                                    address_relay, kCandidatePriority, "", "",
1115                                    cricket::RELAY_PORT_TYPE,
1116                                    kCandidateGeneration, kCandidateFoundation4);
1117     address_relay.SetPort(port_relay++);
1118     cricket::Candidate candidate12(cricket::ICE_CANDIDATE_COMPONENT_RTP, "udp",
1119                                    address_relay, kCandidatePriority, "", "",
1120                                    RELAY_PORT_TYPE, kCandidateGeneration,
1121                                    kCandidateFoundation4);
1122 
1123     // voice
1124     candidates_.push_back(candidate1);
1125     candidates_.push_back(candidate2);
1126     candidates_.push_back(candidate5);
1127     candidates_.push_back(candidate6);
1128     candidates_.push_back(candidate9);
1129     candidates_.push_back(candidate10);
1130 
1131     // video
1132     candidates_.push_back(candidate3);
1133     candidates_.push_back(candidate4);
1134     candidates_.push_back(candidate7);
1135     candidates_.push_back(candidate8);
1136     candidates_.push_back(candidate11);
1137     candidates_.push_back(candidate12);
1138 
1139     jcandidate_.reset(
1140         new JsepIceCandidate(std::string("audio_content_name"), 0, candidate1));
1141 
1142     // Set up JsepSessionDescription.
1143     jdesc_.Initialize(desc_.Clone(), kSessionId, kSessionVersion);
1144     std::string mline_id;
1145     int mline_index = 0;
1146     for (size_t i = 0; i < candidates_.size(); ++i) {
1147       // In this test, the audio m line index will be 0, and the video m line
1148       // will be 1.
1149       bool is_video = (i > 5);
1150       mline_id = is_video ? "video_content_name" : "audio_content_name";
1151       mline_index = is_video ? 1 : 0;
1152       JsepIceCandidate jice(mline_id, mline_index, candidates_.at(i));
1153       jdesc_.AddCandidate(&jice);
1154     }
1155   }
1156 
RemoveVideoCandidates()1157   void RemoveVideoCandidates() {
1158     const IceCandidateCollection* video_candidates_collection =
1159         jdesc_.candidates(1);
1160     ASSERT_NE(nullptr, video_candidates_collection);
1161     std::vector<cricket::Candidate> video_candidates;
1162     for (size_t i = 0; i < video_candidates_collection->count(); ++i) {
1163       cricket::Candidate c = video_candidates_collection->at(i)->candidate();
1164       c.set_transport_name("video_content_name");
1165       video_candidates.push_back(c);
1166     }
1167     jdesc_.RemoveCandidates(video_candidates);
1168   }
1169 
1170   // Turns the existing reference description into a description using
1171   // a=bundle-only. This means no transport attributes and a 0 port value on
1172   // the m= sections not associated with the BUNDLE-tag.
MakeBundleOnlyDescription()1173   void MakeBundleOnlyDescription() {
1174     RemoveVideoCandidates();
1175 
1176     // And the rest of the transport attributes.
1177     desc_.transport_infos()[1].description.ice_ufrag.clear();
1178     desc_.transport_infos()[1].description.ice_pwd.clear();
1179     desc_.transport_infos()[1].description.connection_role =
1180         cricket::CONNECTIONROLE_NONE;
1181 
1182     // Set bundle-only flag.
1183     desc_.contents()[1].bundle_only = true;
1184 
1185     // Add BUNDLE group.
1186     ContentGroup group(cricket::GROUP_TYPE_BUNDLE);
1187     group.AddContentName(kAudioContentName);
1188     group.AddContentName(kVideoContentName);
1189     desc_.AddGroup(group);
1190 
1191     ASSERT_TRUE(jdesc_.Initialize(desc_.Clone(), jdesc_.session_id(),
1192                                   jdesc_.session_version()));
1193   }
1194 
1195   // Turns the existing reference description into a plan B description,
1196   // with 2 audio tracks and 3 video tracks.
MakePlanBDescription()1197   void MakePlanBDescription() {
1198     audio_desc_ = new AudioContentDescription(*audio_desc_);
1199     video_desc_ = new VideoContentDescription(*video_desc_);
1200 
1201     StreamParams audio_track_2;
1202     audio_track_2.id = kAudioTrackId2;
1203     audio_track_2.cname = kStream2Cname;
1204     audio_track_2.set_stream_ids({kStreamId2});
1205     audio_track_2.ssrcs.push_back(kAudioTrack2Ssrc);
1206     audio_desc_->AddStream(audio_track_2);
1207 
1208     StreamParams video_track_2;
1209     video_track_2.id = kVideoTrackId2;
1210     video_track_2.cname = kStream2Cname;
1211     video_track_2.set_stream_ids({kStreamId2});
1212     video_track_2.ssrcs.push_back(kVideoTrack2Ssrc);
1213     video_desc_->AddStream(video_track_2);
1214 
1215     StreamParams video_track_3;
1216     video_track_3.id = kVideoTrackId3;
1217     video_track_3.cname = kStream2Cname;
1218     video_track_3.set_stream_ids({kStreamId2});
1219     video_track_3.ssrcs.push_back(kVideoTrack3Ssrc);
1220     video_desc_->AddStream(video_track_3);
1221 
1222     desc_.RemoveContentByName(kAudioContentName);
1223     desc_.RemoveContentByName(kVideoContentName);
1224     desc_.AddContent(kAudioContentName, MediaProtocolType::kRtp,
1225                      absl::WrapUnique(audio_desc_));
1226     desc_.AddContent(kVideoContentName, MediaProtocolType::kRtp,
1227                      absl::WrapUnique(video_desc_));
1228 
1229     ASSERT_TRUE(jdesc_.Initialize(desc_.Clone(), jdesc_.session_id(),
1230                                   jdesc_.session_version()));
1231   }
1232 
1233   // Turns the existing reference description into a unified plan description,
1234   // with 2 audio tracks and 3 video tracks.
MakeUnifiedPlanDescription(bool use_ssrcs=true)1235   void MakeUnifiedPlanDescription(bool use_ssrcs = true) {
1236     // Audio track 2.
1237     AudioContentDescription* audio_desc_2 = CreateAudioContentDescription();
1238     StreamParams audio_track_2;
1239     audio_track_2.id = kAudioTrackId2;
1240     audio_track_2.set_stream_ids({kStreamId2});
1241     if (use_ssrcs) {
1242       audio_track_2.cname = kStream2Cname;
1243       audio_track_2.ssrcs.push_back(kAudioTrack2Ssrc);
1244     }
1245     audio_desc_2->AddStream(audio_track_2);
1246     desc_.AddContent(kAudioContentName2, MediaProtocolType::kRtp,
1247                      absl::WrapUnique(audio_desc_2));
1248     desc_.AddTransportInfo(TransportInfo(
1249         kAudioContentName2, TransportDescription(kUfragVoice2, kPwdVoice2)));
1250     // Video track 2, in stream 2.
1251     VideoContentDescription* video_desc_2 = CreateVideoContentDescription();
1252     StreamParams video_track_2;
1253     video_track_2.id = kVideoTrackId2;
1254     video_track_2.set_stream_ids({kStreamId2});
1255     if (use_ssrcs) {
1256       video_track_2.cname = kStream2Cname;
1257       video_track_2.ssrcs.push_back(kVideoTrack2Ssrc);
1258     }
1259     video_desc_2->AddStream(video_track_2);
1260     desc_.AddContent(kVideoContentName2, MediaProtocolType::kRtp,
1261                      absl::WrapUnique(video_desc_2));
1262     desc_.AddTransportInfo(TransportInfo(
1263         kVideoContentName2, TransportDescription(kUfragVideo2, kPwdVideo2)));
1264 
1265     // Video track 3, in stream 2.
1266     VideoContentDescription* video_desc_3 = CreateVideoContentDescription();
1267     StreamParams video_track_3;
1268     video_track_3.id = kVideoTrackId3;
1269     video_track_3.set_stream_ids({kStreamId2});
1270     if (use_ssrcs) {
1271       video_track_3.cname = kStream2Cname;
1272       video_track_3.ssrcs.push_back(kVideoTrack3Ssrc);
1273     }
1274     video_desc_3->AddStream(video_track_3);
1275     desc_.AddContent(kVideoContentName3, MediaProtocolType::kRtp,
1276                      absl::WrapUnique(video_desc_3));
1277     desc_.AddTransportInfo(TransportInfo(
1278         kVideoContentName3, TransportDescription(kUfragVideo3, kPwdVideo3)));
1279     desc_.set_msid_signaling(cricket::kMsidSignalingMediaSection);
1280 
1281     ASSERT_TRUE(jdesc_.Initialize(desc_.Clone(), jdesc_.session_id(),
1282                                   jdesc_.session_version()));
1283   }
1284 
1285   // Creates an audio content description with no streams, and some default
1286   // configuration.
CreateAudioContentDescription()1287   AudioContentDescription* CreateAudioContentDescription() {
1288     AudioContentDescription* audio = new AudioContentDescription();
1289     audio->set_rtcp_mux(true);
1290     audio->set_rtcp_reduced_size(true);
1291     audio->AddCrypto(CryptoParams(
1292         1, "AES_CM_128_HMAC_SHA1_32",
1293         "inline:NzB4d1BINUAvLEw6UzF3WSJ+PSdFcGdUJShpX1Zj|2^20|1:32",
1294         "dummy_session_params"));
1295     audio->set_protocol(cricket::kMediaProtocolSavpf);
1296     audio->AddCodec(AudioCodec(111, "opus", 48000, 0, 2));
1297     audio->AddCodec(AudioCodec(103, "ISAC", 16000, 0, 1));
1298     audio->AddCodec(AudioCodec(104, "ISAC", 32000, 0, 1));
1299     return audio;
1300   }
1301 
1302   // Turns the existing reference description into a unified plan description,
1303   // with 3 audio MediaContentDescriptions with special StreamParams that
1304   // contain 0 or multiple stream ids: - audio track 1 has 1 media stream id -
1305   // audio track 2 has 2 media stream ids - audio track 3 has 0 media stream ids
MakeUnifiedPlanDescriptionMultipleStreamIds(const int msid_signaling)1306   void MakeUnifiedPlanDescriptionMultipleStreamIds(const int msid_signaling) {
1307     desc_.RemoveContentByName(kVideoContentName);
1308     desc_.RemoveTransportInfoByName(kVideoContentName);
1309     RemoveVideoCandidates();
1310 
1311     // Audio track 2 has 2 media stream ids.
1312     AudioContentDescription* audio_desc_2 = CreateAudioContentDescription();
1313     StreamParams audio_track_2;
1314     audio_track_2.id = kAudioTrackId2;
1315     audio_track_2.cname = kStream1Cname;
1316     audio_track_2.set_stream_ids({kStreamId1, kStreamId2});
1317     audio_track_2.ssrcs.push_back(kAudioTrack2Ssrc);
1318     audio_desc_2->AddStream(audio_track_2);
1319     desc_.AddContent(kAudioContentName2, MediaProtocolType::kRtp,
1320                      absl::WrapUnique(audio_desc_2));
1321     desc_.AddTransportInfo(TransportInfo(
1322         kAudioContentName2, TransportDescription(kUfragVoice2, kPwdVoice2)));
1323 
1324     // Audio track 3 has no stream ids.
1325     AudioContentDescription* audio_desc_3 = CreateAudioContentDescription();
1326     StreamParams audio_track_3;
1327     audio_track_3.id = kAudioTrackId3;
1328     audio_track_3.cname = kStream2Cname;
1329     audio_track_3.set_stream_ids({});
1330     audio_track_3.ssrcs.push_back(kAudioTrack3Ssrc);
1331     audio_desc_3->AddStream(audio_track_3);
1332     desc_.AddContent(kAudioContentName3, MediaProtocolType::kRtp,
1333                      absl::WrapUnique(audio_desc_3));
1334     desc_.AddTransportInfo(TransportInfo(
1335         kAudioContentName3, TransportDescription(kUfragVoice3, kPwdVoice3)));
1336     desc_.set_msid_signaling(msid_signaling);
1337     ASSERT_TRUE(jdesc_.Initialize(desc_.Clone(), jdesc_.session_id(),
1338                                   jdesc_.session_version()));
1339   }
1340 
1341   // Turns the existing reference description into a unified plan description
1342   // with one audio MediaContentDescription that contains one StreamParams with
1343   // 0 ssrcs.
MakeUnifiedPlanDescriptionNoSsrcSignaling()1344   void MakeUnifiedPlanDescriptionNoSsrcSignaling() {
1345     desc_.RemoveContentByName(kVideoContentName);
1346     desc_.RemoveContentByName(kAudioContentName);
1347     desc_.RemoveTransportInfoByName(kVideoContentName);
1348     RemoveVideoCandidates();
1349 
1350     AudioContentDescription* audio_desc = CreateAudioContentDescription();
1351     StreamParams audio_track;
1352     audio_track.id = kAudioTrackId1;
1353     audio_track.set_stream_ids({kStreamId1});
1354     audio_desc->AddStream(audio_track);
1355     desc_.AddContent(kAudioContentName, MediaProtocolType::kRtp,
1356                      absl::WrapUnique(audio_desc));
1357 
1358     // Enable signaling a=msid lines.
1359     desc_.set_msid_signaling(cricket::kMsidSignalingMediaSection);
1360     ASSERT_TRUE(jdesc_.Initialize(desc_.Clone(), jdesc_.session_id(),
1361                                   jdesc_.session_version()));
1362   }
1363 
1364   // Creates a video content description with no streams, and some default
1365   // configuration.
CreateVideoContentDescription()1366   VideoContentDescription* CreateVideoContentDescription() {
1367     VideoContentDescription* video = new VideoContentDescription();
1368     video->AddCrypto(CryptoParams(
1369         1, "AES_CM_128_HMAC_SHA1_80",
1370         "inline:d0RmdmcmVCspeEc3QGZiNWpVLFJhQX1cfHAwJSoj|2^20|1:32", ""));
1371     video->set_protocol(cricket::kMediaProtocolSavpf);
1372     video->AddCodec(
1373         VideoCodec(120, JsepSessionDescription::kDefaultVideoCodecName));
1374     return video;
1375   }
1376 
1377   template <class MCD>
CompareMediaContentDescription(const MCD * cd1,const MCD * cd2)1378   void CompareMediaContentDescription(const MCD* cd1, const MCD* cd2) {
1379     // type
1380     EXPECT_EQ(cd1->type(), cd2->type());
1381 
1382     // content direction
1383     EXPECT_EQ(cd1->direction(), cd2->direction());
1384 
1385     // rtcp_mux
1386     EXPECT_EQ(cd1->rtcp_mux(), cd2->rtcp_mux());
1387 
1388     // rtcp_reduced_size
1389     EXPECT_EQ(cd1->rtcp_reduced_size(), cd2->rtcp_reduced_size());
1390 
1391     // cryptos
1392     EXPECT_EQ(cd1->cryptos().size(), cd2->cryptos().size());
1393     if (cd1->cryptos().size() != cd2->cryptos().size()) {
1394       ADD_FAILURE();
1395       return;
1396     }
1397     for (size_t i = 0; i < cd1->cryptos().size(); ++i) {
1398       const CryptoParams c1 = cd1->cryptos().at(i);
1399       const CryptoParams c2 = cd2->cryptos().at(i);
1400       EXPECT_TRUE(c1.Matches(c2));
1401       EXPECT_EQ(c1.key_params, c2.key_params);
1402       EXPECT_EQ(c1.session_params, c2.session_params);
1403     }
1404 
1405     // protocol
1406     // Use an equivalence class here, for old and new versions of the
1407     // protocol description.
1408     if (cd1->protocol() == cricket::kMediaProtocolDtlsSctp ||
1409         cd1->protocol() == cricket::kMediaProtocolUdpDtlsSctp ||
1410         cd1->protocol() == cricket::kMediaProtocolTcpDtlsSctp) {
1411       const bool cd2_is_also_dtls_sctp =
1412           cd2->protocol() == cricket::kMediaProtocolDtlsSctp ||
1413           cd2->protocol() == cricket::kMediaProtocolUdpDtlsSctp ||
1414           cd2->protocol() == cricket::kMediaProtocolTcpDtlsSctp;
1415       EXPECT_TRUE(cd2_is_also_dtls_sctp);
1416     } else {
1417       EXPECT_EQ(cd1->protocol(), cd2->protocol());
1418     }
1419 
1420     // codecs
1421     EXPECT_EQ(cd1->codecs(), cd2->codecs());
1422 
1423     // bandwidth
1424     EXPECT_EQ(cd1->bandwidth(), cd2->bandwidth());
1425 
1426     // streams
1427     EXPECT_EQ(cd1->streams(), cd2->streams());
1428 
1429     // extmap-allow-mixed
1430     EXPECT_EQ(cd1->extmap_allow_mixed_enum(), cd2->extmap_allow_mixed_enum());
1431 
1432     // extmap
1433     ASSERT_EQ(cd1->rtp_header_extensions().size(),
1434               cd2->rtp_header_extensions().size());
1435     for (size_t i = 0; i < cd1->rtp_header_extensions().size(); ++i) {
1436       const RtpExtension ext1 = cd1->rtp_header_extensions().at(i);
1437       const RtpExtension ext2 = cd2->rtp_header_extensions().at(i);
1438       EXPECT_EQ(ext1.uri, ext2.uri);
1439       EXPECT_EQ(ext1.id, ext2.id);
1440       EXPECT_EQ(ext1.encrypt, ext2.encrypt);
1441     }
1442   }
1443 
CompareRidDescriptionIds(const std::vector<RidDescription> & rids,const std::vector<std::string> & ids)1444   void CompareRidDescriptionIds(const std::vector<RidDescription>& rids,
1445                                 const std::vector<std::string>& ids) {
1446     // Order of elements does not matter, only equivalence of sets.
1447     EXPECT_EQ(rids.size(), ids.size());
1448     for (const std::string& id : ids) {
1449       EXPECT_EQ(1l, absl::c_count_if(rids, [id](const RidDescription& rid) {
1450                   return rid.rid == id;
1451                 }));
1452     }
1453   }
1454 
CompareSimulcastDescription(const SimulcastDescription & simulcast1,const SimulcastDescription & simulcast2)1455   void CompareSimulcastDescription(const SimulcastDescription& simulcast1,
1456                                    const SimulcastDescription& simulcast2) {
1457     EXPECT_EQ(simulcast1.send_layers().size(), simulcast2.send_layers().size());
1458     EXPECT_EQ(simulcast1.receive_layers().size(),
1459               simulcast2.receive_layers().size());
1460   }
1461 
CompareRtpDataContentDescription(const RtpDataContentDescription * dcd1,const RtpDataContentDescription * dcd2)1462   void CompareRtpDataContentDescription(const RtpDataContentDescription* dcd1,
1463                                         const RtpDataContentDescription* dcd2) {
1464     CompareMediaContentDescription<RtpDataContentDescription>(dcd1, dcd2);
1465   }
1466 
CompareSctpDataContentDescription(const SctpDataContentDescription * dcd1,const SctpDataContentDescription * dcd2)1467   void CompareSctpDataContentDescription(
1468       const SctpDataContentDescription* dcd1,
1469       const SctpDataContentDescription* dcd2) {
1470     EXPECT_EQ(dcd1->use_sctpmap(), dcd2->use_sctpmap());
1471     EXPECT_EQ(dcd1->port(), dcd2->port());
1472     EXPECT_EQ(dcd1->max_message_size(), dcd2->max_message_size());
1473   }
1474 
CompareSessionDescription(const SessionDescription & desc1,const SessionDescription & desc2)1475   void CompareSessionDescription(const SessionDescription& desc1,
1476                                  const SessionDescription& desc2) {
1477     // Compare content descriptions.
1478     if (desc1.contents().size() != desc2.contents().size()) {
1479       ADD_FAILURE();
1480       return;
1481     }
1482     for (size_t i = 0; i < desc1.contents().size(); ++i) {
1483       const cricket::ContentInfo& c1 = desc1.contents().at(i);
1484       const cricket::ContentInfo& c2 = desc2.contents().at(i);
1485       // ContentInfo properties.
1486       EXPECT_EQ(c1.name, c2.name);
1487       EXPECT_EQ(c1.type, c2.type);
1488       EXPECT_EQ(c1.rejected, c2.rejected);
1489       EXPECT_EQ(c1.bundle_only, c2.bundle_only);
1490 
1491       ASSERT_EQ(IsAudioContent(&c1), IsAudioContent(&c2));
1492       if (IsAudioContent(&c1)) {
1493         const AudioContentDescription* acd1 =
1494             c1.media_description()->as_audio();
1495         const AudioContentDescription* acd2 =
1496             c2.media_description()->as_audio();
1497         CompareMediaContentDescription<AudioContentDescription>(acd1, acd2);
1498       }
1499 
1500       ASSERT_EQ(IsVideoContent(&c1), IsVideoContent(&c2));
1501       if (IsVideoContent(&c1)) {
1502         const VideoContentDescription* vcd1 =
1503             c1.media_description()->as_video();
1504         const VideoContentDescription* vcd2 =
1505             c2.media_description()->as_video();
1506         CompareMediaContentDescription<VideoContentDescription>(vcd1, vcd2);
1507       }
1508 
1509       ASSERT_EQ(IsDataContent(&c1), IsDataContent(&c2));
1510       if (c1.media_description()->as_sctp()) {
1511         ASSERT_TRUE(c2.media_description()->as_sctp());
1512         const SctpDataContentDescription* scd1 =
1513             c1.media_description()->as_sctp();
1514         const SctpDataContentDescription* scd2 =
1515             c2.media_description()->as_sctp();
1516         CompareSctpDataContentDescription(scd1, scd2);
1517       } else {
1518         if (IsDataContent(&c1)) {
1519           const RtpDataContentDescription* dcd1 =
1520               c1.media_description()->as_rtp_data();
1521           const RtpDataContentDescription* dcd2 =
1522               c2.media_description()->as_rtp_data();
1523           CompareRtpDataContentDescription(dcd1, dcd2);
1524         }
1525       }
1526 
1527       CompareSimulcastDescription(
1528           c1.media_description()->simulcast_description(),
1529           c2.media_description()->simulcast_description());
1530     }
1531 
1532     // group
1533     const cricket::ContentGroups groups1 = desc1.groups();
1534     const cricket::ContentGroups groups2 = desc2.groups();
1535     EXPECT_EQ(groups1.size(), groups1.size());
1536     if (groups1.size() != groups2.size()) {
1537       ADD_FAILURE();
1538       return;
1539     }
1540     for (size_t i = 0; i < groups1.size(); ++i) {
1541       const cricket::ContentGroup group1 = groups1.at(i);
1542       const cricket::ContentGroup group2 = groups2.at(i);
1543       EXPECT_EQ(group1.semantics(), group2.semantics());
1544       const cricket::ContentNames names1 = group1.content_names();
1545       const cricket::ContentNames names2 = group2.content_names();
1546       EXPECT_EQ(names1.size(), names2.size());
1547       if (names1.size() != names2.size()) {
1548         ADD_FAILURE();
1549         return;
1550       }
1551       cricket::ContentNames::const_iterator iter1 = names1.begin();
1552       cricket::ContentNames::const_iterator iter2 = names2.begin();
1553       while (iter1 != names1.end()) {
1554         EXPECT_EQ(*iter1++, *iter2++);
1555       }
1556     }
1557 
1558     // transport info
1559     const cricket::TransportInfos transports1 = desc1.transport_infos();
1560     const cricket::TransportInfos transports2 = desc2.transport_infos();
1561     EXPECT_EQ(transports1.size(), transports2.size());
1562     if (transports1.size() != transports2.size()) {
1563       ADD_FAILURE();
1564       return;
1565     }
1566     for (size_t i = 0; i < transports1.size(); ++i) {
1567       const cricket::TransportInfo transport1 = transports1.at(i);
1568       const cricket::TransportInfo transport2 = transports2.at(i);
1569       EXPECT_EQ(transport1.content_name, transport2.content_name);
1570       EXPECT_EQ(transport1.description.ice_ufrag,
1571                 transport2.description.ice_ufrag);
1572       EXPECT_EQ(transport1.description.ice_pwd, transport2.description.ice_pwd);
1573       EXPECT_EQ(transport1.description.ice_mode,
1574                 transport2.description.ice_mode);
1575       if (transport1.description.identity_fingerprint) {
1576         EXPECT_EQ(*transport1.description.identity_fingerprint,
1577                   *transport2.description.identity_fingerprint);
1578       } else {
1579         EXPECT_EQ(transport1.description.identity_fingerprint.get(),
1580                   transport2.description.identity_fingerprint.get());
1581       }
1582       EXPECT_EQ(transport1.description.transport_options,
1583                 transport2.description.transport_options);
1584     }
1585 
1586     // global attributes
1587     EXPECT_EQ(desc1.msid_supported(), desc2.msid_supported());
1588     EXPECT_EQ(desc1.extmap_allow_mixed(), desc2.extmap_allow_mixed());
1589   }
1590 
CompareSessionDescription(const JsepSessionDescription & desc1,const JsepSessionDescription & desc2)1591   bool CompareSessionDescription(const JsepSessionDescription& desc1,
1592                                  const JsepSessionDescription& desc2) {
1593     EXPECT_EQ(desc1.session_id(), desc2.session_id());
1594     EXPECT_EQ(desc1.session_version(), desc2.session_version());
1595     CompareSessionDescription(*desc1.description(), *desc2.description());
1596     if (desc1.number_of_mediasections() != desc2.number_of_mediasections())
1597       return false;
1598     for (size_t i = 0; i < desc1.number_of_mediasections(); ++i) {
1599       const IceCandidateCollection* cc1 = desc1.candidates(i);
1600       const IceCandidateCollection* cc2 = desc2.candidates(i);
1601       if (cc1->count() != cc2->count()) {
1602         ADD_FAILURE();
1603         return false;
1604       }
1605       for (size_t j = 0; j < cc1->count(); ++j) {
1606         const IceCandidateInterface* c1 = cc1->at(j);
1607         const IceCandidateInterface* c2 = cc2->at(j);
1608         EXPECT_EQ(c1->sdp_mid(), c2->sdp_mid());
1609         EXPECT_EQ(c1->sdp_mline_index(), c2->sdp_mline_index());
1610         EXPECT_TRUE(c1->candidate().IsEquivalent(c2->candidate()));
1611       }
1612     }
1613     return true;
1614   }
1615 
1616   // Disable the ice-ufrag and ice-pwd in given |sdp| message by replacing
1617   // them with invalid keywords so that the parser will just ignore them.
RemoveCandidateUfragPwd(std::string * sdp)1618   bool RemoveCandidateUfragPwd(std::string* sdp) {
1619     absl::StrReplaceAll(
1620         {{"a=ice-ufrag", "a=xice-ufrag"}, {"a=ice-pwd", "a=xice-pwd"}}, sdp);
1621     return true;
1622   }
1623 
1624   // Update the candidates in |jdesc| to use the given |ufrag| and |pwd|.
UpdateCandidateUfragPwd(JsepSessionDescription * jdesc,int mline_index,const std::string & ufrag,const std::string & pwd)1625   bool UpdateCandidateUfragPwd(JsepSessionDescription* jdesc,
1626                                int mline_index,
1627                                const std::string& ufrag,
1628                                const std::string& pwd) {
1629     std::string content_name;
1630     if (mline_index == 0) {
1631       content_name = kAudioContentName;
1632     } else if (mline_index == 1) {
1633       content_name = kVideoContentName;
1634     } else {
1635       RTC_NOTREACHED();
1636     }
1637     TransportInfo transport_info(content_name,
1638                                  TransportDescription(ufrag, pwd));
1639     SessionDescription* desc =
1640         const_cast<SessionDescription*>(jdesc->description());
1641     desc->RemoveTransportInfoByName(content_name);
1642     desc->AddTransportInfo(transport_info);
1643     for (size_t i = 0; i < jdesc_.number_of_mediasections(); ++i) {
1644       const IceCandidateCollection* cc = jdesc_.candidates(i);
1645       for (size_t j = 0; j < cc->count(); ++j) {
1646         if (cc->at(j)->sdp_mline_index() == mline_index) {
1647           const_cast<Candidate&>(cc->at(j)->candidate()).set_username(ufrag);
1648           const_cast<Candidate&>(cc->at(j)->candidate()).set_password(pwd);
1649         }
1650       }
1651     }
1652     return true;
1653   }
1654 
AddIceOptions(const std::string & content_name,const std::vector<std::string> & transport_options)1655   void AddIceOptions(const std::string& content_name,
1656                      const std::vector<std::string>& transport_options) {
1657     ASSERT_TRUE(desc_.GetTransportInfoByName(content_name) != NULL);
1658     cricket::TransportInfo transport_info =
1659         *(desc_.GetTransportInfoByName(content_name));
1660     desc_.RemoveTransportInfoByName(content_name);
1661     transport_info.description.transport_options = transport_options;
1662     desc_.AddTransportInfo(transport_info);
1663   }
1664 
SetIceUfragPwd(const std::string & content_name,const std::string & ice_ufrag,const std::string & ice_pwd)1665   void SetIceUfragPwd(const std::string& content_name,
1666                       const std::string& ice_ufrag,
1667                       const std::string& ice_pwd) {
1668     ASSERT_TRUE(desc_.GetTransportInfoByName(content_name) != NULL);
1669     cricket::TransportInfo transport_info =
1670         *(desc_.GetTransportInfoByName(content_name));
1671     desc_.RemoveTransportInfoByName(content_name);
1672     transport_info.description.ice_ufrag = ice_ufrag;
1673     transport_info.description.ice_pwd = ice_pwd;
1674     desc_.AddTransportInfo(transport_info);
1675   }
1676 
AddFingerprint()1677   void AddFingerprint() {
1678     desc_.RemoveTransportInfoByName(kAudioContentName);
1679     desc_.RemoveTransportInfoByName(kVideoContentName);
1680     rtc::SSLFingerprint fingerprint(rtc::DIGEST_SHA_1, kIdentityDigest);
1681     desc_.AddTransportInfo(TransportInfo(
1682         kAudioContentName,
1683         TransportDescription(std::vector<std::string>(), kUfragVoice, kPwdVoice,
1684                              cricket::ICEMODE_FULL,
1685                              cricket::CONNECTIONROLE_NONE, &fingerprint)));
1686     desc_.AddTransportInfo(TransportInfo(
1687         kVideoContentName,
1688         TransportDescription(std::vector<std::string>(), kUfragVideo, kPwdVideo,
1689                              cricket::ICEMODE_FULL,
1690                              cricket::CONNECTIONROLE_NONE, &fingerprint)));
1691   }
1692 
AddExtmap(bool encrypted)1693   void AddExtmap(bool encrypted) {
1694     audio_desc_ = new AudioContentDescription(*audio_desc_);
1695     video_desc_ = new VideoContentDescription(*video_desc_);
1696     audio_desc_->AddRtpHeaderExtension(
1697         RtpExtension(kExtmapUri, kExtmapId, encrypted));
1698     video_desc_->AddRtpHeaderExtension(
1699         RtpExtension(kExtmapUri, kExtmapId, encrypted));
1700     desc_.RemoveContentByName(kAudioContentName);
1701     desc_.RemoveContentByName(kVideoContentName);
1702     desc_.AddContent(kAudioContentName, MediaProtocolType::kRtp,
1703                      absl::WrapUnique(audio_desc_));
1704     desc_.AddContent(kVideoContentName, MediaProtocolType::kRtp,
1705                      absl::WrapUnique(video_desc_));
1706   }
1707 
RemoveCryptos()1708   void RemoveCryptos() {
1709     audio_desc_->set_cryptos(std::vector<CryptoParams>());
1710     video_desc_->set_cryptos(std::vector<CryptoParams>());
1711   }
1712 
1713   // Removes everything in StreamParams from the session description that is
1714   // used for a=ssrc lines.
RemoveSsrcSignalingFromStreamParams()1715   void RemoveSsrcSignalingFromStreamParams() {
1716     for (cricket::ContentInfo& content_info :
1717          jdesc_.description()->contents()) {
1718       // With Unified Plan there should be one StreamParams per m= section.
1719       StreamParams& stream =
1720           content_info.media_description()->mutable_streams()[0];
1721       stream.ssrcs.clear();
1722       stream.ssrc_groups.clear();
1723       stream.cname.clear();
1724     }
1725   }
1726 
1727   // Removes all a=ssrc lines from the SDP string, except for the
1728   // "a=ssrc:... cname:..." lines.
RemoveSsrcMsidLinesFromSdpString(std::string * sdp_string)1729   void RemoveSsrcMsidLinesFromSdpString(std::string* sdp_string) {
1730     const char kAttributeSsrc[] = "a=ssrc";
1731     const char kAttributeCname[] = "cname";
1732     size_t ssrc_line_pos = sdp_string->find(kAttributeSsrc);
1733     while (ssrc_line_pos != std::string::npos) {
1734       size_t beg_line_pos = sdp_string->rfind('\n', ssrc_line_pos);
1735       size_t end_line_pos = sdp_string->find('\n', ssrc_line_pos);
1736       size_t cname_pos = sdp_string->find(kAttributeCname, ssrc_line_pos);
1737       if (cname_pos == std::string::npos || cname_pos > end_line_pos) {
1738         // Only erase a=ssrc lines that don't contain "cname".
1739         sdp_string->erase(beg_line_pos, end_line_pos - beg_line_pos);
1740         ssrc_line_pos = sdp_string->find(kAttributeSsrc, beg_line_pos);
1741       } else {
1742         // Skip the "a=ssrc:... cname" line and find the next "a=ssrc" line.
1743         ssrc_line_pos = sdp_string->find(kAttributeSsrc, end_line_pos);
1744       }
1745     }
1746   }
1747 
1748   // Removes all a=ssrc lines from the SDP string.
RemoveSsrcLinesFromSdpString(std::string * sdp_string)1749   void RemoveSsrcLinesFromSdpString(std::string* sdp_string) {
1750     const char kAttributeSsrc[] = "a=ssrc";
1751     while (sdp_string->find(kAttributeSsrc) != std::string::npos) {
1752       size_t pos_ssrc_attribute = sdp_string->find(kAttributeSsrc);
1753       size_t beg_line_pos = sdp_string->rfind('\n', pos_ssrc_attribute);
1754       size_t end_line_pos = sdp_string->find('\n', pos_ssrc_attribute);
1755       sdp_string->erase(beg_line_pos, end_line_pos - beg_line_pos);
1756     }
1757   }
1758 
TestSerializeDirection(RtpTransceiverDirection direction)1759   bool TestSerializeDirection(RtpTransceiverDirection direction) {
1760     audio_desc_->set_direction(direction);
1761     video_desc_->set_direction(direction);
1762     std::string new_sdp = kSdpFullString;
1763     ReplaceDirection(direction, &new_sdp);
1764 
1765     if (!jdesc_.Initialize(desc_.Clone(), jdesc_.session_id(),
1766                            jdesc_.session_version())) {
1767       return false;
1768     }
1769     std::string message = webrtc::SdpSerialize(jdesc_);
1770     EXPECT_EQ(new_sdp, message);
1771     return true;
1772   }
1773 
TestSerializeRejected(bool audio_rejected,bool video_rejected)1774   bool TestSerializeRejected(bool audio_rejected, bool video_rejected) {
1775     audio_desc_ = new AudioContentDescription(*audio_desc_);
1776     video_desc_ = new VideoContentDescription(*video_desc_);
1777 
1778     desc_.RemoveContentByName(kAudioContentName);
1779     desc_.RemoveContentByName(kVideoContentName);
1780     desc_.AddContent(kAudioContentName, MediaProtocolType::kRtp, audio_rejected,
1781                      absl::WrapUnique(audio_desc_));
1782     desc_.AddContent(kVideoContentName, MediaProtocolType::kRtp, video_rejected,
1783                      absl::WrapUnique(video_desc_));
1784     SetIceUfragPwd(kAudioContentName, audio_rejected ? "" : kUfragVoice,
1785                    audio_rejected ? "" : kPwdVoice);
1786     SetIceUfragPwd(kVideoContentName, video_rejected ? "" : kUfragVideo,
1787                    video_rejected ? "" : kPwdVideo);
1788 
1789     std::string new_sdp = kSdpString;
1790     ReplaceRejected(audio_rejected, video_rejected, &new_sdp);
1791 
1792     JsepSessionDescription jdesc_no_candidates(kDummyType);
1793     MakeDescriptionWithoutCandidates(&jdesc_no_candidates);
1794     std::string message = webrtc::SdpSerialize(jdesc_no_candidates);
1795     EXPECT_EQ(new_sdp, message);
1796     return true;
1797   }
1798 
AddSctpDataChannel(bool use_sctpmap)1799   void AddSctpDataChannel(bool use_sctpmap) {
1800     std::unique_ptr<SctpDataContentDescription> data(
1801         new SctpDataContentDescription());
1802     sctp_desc_ = data.get();
1803     sctp_desc_->set_use_sctpmap(use_sctpmap);
1804     sctp_desc_->set_protocol(cricket::kMediaProtocolUdpDtlsSctp);
1805     sctp_desc_->set_port(kDefaultSctpPort);
1806     desc_.AddContent(kDataContentName, MediaProtocolType::kSctp,
1807                      std::move(data));
1808     desc_.AddTransportInfo(TransportInfo(
1809         kDataContentName, TransportDescription(kUfragData, kPwdData)));
1810   }
1811 
AddRtpDataChannel()1812   void AddRtpDataChannel() {
1813     std::unique_ptr<RtpDataContentDescription> data(
1814         new RtpDataContentDescription());
1815     data_desc_ = data.get();
1816 
1817     data_desc_->AddCodec(DataCodec(101, "google-data"));
1818     StreamParams data_stream;
1819     data_stream.id = kDataChannelMsid;
1820     data_stream.cname = kDataChannelCname;
1821     data_stream.set_stream_ids({kDataChannelLabel});
1822     data_stream.ssrcs.push_back(kDataChannelSsrc);
1823     data_desc_->AddStream(data_stream);
1824     data_desc_->AddCrypto(
1825         CryptoParams(1, "AES_CM_128_HMAC_SHA1_80",
1826                      "inline:FvLcvU2P3ZWmQxgPAgcDu7Zl9vftYElFOjEzhWs5", ""));
1827     data_desc_->set_protocol(cricket::kMediaProtocolSavpf);
1828     desc_.AddContent(kDataContentName, MediaProtocolType::kRtp,
1829                      std::move(data));
1830     desc_.AddTransportInfo(TransportInfo(
1831         kDataContentName, TransportDescription(kUfragData, kPwdData)));
1832   }
1833 
TestDeserializeDirection(RtpTransceiverDirection direction)1834   bool TestDeserializeDirection(RtpTransceiverDirection direction) {
1835     std::string new_sdp = kSdpFullString;
1836     ReplaceDirection(direction, &new_sdp);
1837     JsepSessionDescription new_jdesc(kDummyType);
1838 
1839     EXPECT_TRUE(SdpDeserialize(new_sdp, &new_jdesc));
1840 
1841     audio_desc_->set_direction(direction);
1842     video_desc_->set_direction(direction);
1843     if (!jdesc_.Initialize(desc_.Clone(), jdesc_.session_id(),
1844                            jdesc_.session_version())) {
1845       return false;
1846     }
1847     EXPECT_TRUE(CompareSessionDescription(jdesc_, new_jdesc));
1848     return true;
1849   }
1850 
TestDeserializeRejected(bool audio_rejected,bool video_rejected)1851   bool TestDeserializeRejected(bool audio_rejected, bool video_rejected) {
1852     std::string new_sdp = kSdpString;
1853     ReplaceRejected(audio_rejected, video_rejected, &new_sdp);
1854     JsepSessionDescription new_jdesc(SdpType::kOffer);
1855     EXPECT_TRUE(SdpDeserialize(new_sdp, &new_jdesc));
1856 
1857     audio_desc_ = new AudioContentDescription(*audio_desc_);
1858     video_desc_ = new VideoContentDescription(*video_desc_);
1859     desc_.RemoveContentByName(kAudioContentName);
1860     desc_.RemoveContentByName(kVideoContentName);
1861     desc_.AddContent(kAudioContentName, MediaProtocolType::kRtp, audio_rejected,
1862                      absl::WrapUnique(audio_desc_));
1863     desc_.AddContent(kVideoContentName, MediaProtocolType::kRtp, video_rejected,
1864                      absl::WrapUnique(video_desc_));
1865     SetIceUfragPwd(kAudioContentName, audio_rejected ? "" : kUfragVoice,
1866                    audio_rejected ? "" : kPwdVoice);
1867     SetIceUfragPwd(kVideoContentName, video_rejected ? "" : kUfragVideo,
1868                    video_rejected ? "" : kPwdVideo);
1869     JsepSessionDescription jdesc_no_candidates(kDummyType);
1870     if (!jdesc_no_candidates.Initialize(desc_.Clone(), jdesc_.session_id(),
1871                                         jdesc_.session_version())) {
1872       return false;
1873     }
1874     EXPECT_TRUE(CompareSessionDescription(jdesc_no_candidates, new_jdesc));
1875     return true;
1876   }
1877 
TestDeserializeExtmap(bool session_level,bool media_level,bool encrypted)1878   void TestDeserializeExtmap(bool session_level,
1879                              bool media_level,
1880                              bool encrypted) {
1881     AddExtmap(encrypted);
1882     JsepSessionDescription new_jdesc(SdpType::kOffer);
1883     ASSERT_TRUE(new_jdesc.Initialize(desc_.Clone(), jdesc_.session_id(),
1884                                      jdesc_.session_version()));
1885     JsepSessionDescription jdesc_with_extmap(SdpType::kOffer);
1886     std::string sdp_with_extmap = kSdpString;
1887     if (session_level) {
1888       InjectAfter(kSessionTime,
1889                   encrypted ? kExtmapWithDirectionAndAttributeEncrypted
1890                             : kExtmapWithDirectionAndAttribute,
1891                   &sdp_with_extmap);
1892     }
1893     if (media_level) {
1894       InjectAfter(kAttributeIcePwdVoice,
1895                   encrypted ? kExtmapWithDirectionAndAttributeEncrypted
1896                             : kExtmapWithDirectionAndAttribute,
1897                   &sdp_with_extmap);
1898       InjectAfter(kAttributeIcePwdVideo,
1899                   encrypted ? kExtmapWithDirectionAndAttributeEncrypted
1900                             : kExtmapWithDirectionAndAttribute,
1901                   &sdp_with_extmap);
1902     }
1903     // The extmap can't be present at the same time in both session level and
1904     // media level.
1905     if (session_level && media_level) {
1906       SdpParseError error;
1907       EXPECT_FALSE(
1908           webrtc::SdpDeserialize(sdp_with_extmap, &jdesc_with_extmap, &error));
1909       EXPECT_NE(std::string::npos, error.description.find("a=extmap"));
1910     } else {
1911       EXPECT_TRUE(SdpDeserialize(sdp_with_extmap, &jdesc_with_extmap));
1912       EXPECT_TRUE(CompareSessionDescription(jdesc_with_extmap, new_jdesc));
1913     }
1914   }
1915 
VerifyCodecParameter(const cricket::CodecParameterMap & params,const std::string & name,int expected_value)1916   void VerifyCodecParameter(const cricket::CodecParameterMap& params,
1917                             const std::string& name,
1918                             int expected_value) {
1919     cricket::CodecParameterMap::const_iterator found = params.find(name);
1920     ASSERT_TRUE(found != params.end());
1921     EXPECT_EQ(found->second, rtc::ToString(expected_value));
1922   }
1923 
TestDeserializeCodecParams(const CodecParams & params,JsepSessionDescription * jdesc_output)1924   void TestDeserializeCodecParams(const CodecParams& params,
1925                                   JsepSessionDescription* jdesc_output) {
1926     std::string sdp =
1927         "v=0\r\n"
1928         "o=- 18446744069414584320 18446462598732840960 IN IP4 127.0.0.1\r\n"
1929         "s=-\r\n"
1930         "t=0 0\r\n"
1931         // Include semantics for WebRTC Media Streams since it is supported by
1932         // this parser, and will be added to the SDP when serializing a session
1933         // description.
1934         "a=msid-semantic: WMS\r\n"
1935         // Pl type 111 preferred.
1936         "m=audio 9 RTP/SAVPF 111 104 103 105\r\n"
1937         // Pltype 111 listed before 103 and 104 in the map.
1938         "a=rtpmap:111 opus/48000/2\r\n"
1939         // Pltype 103 listed before 104.
1940         "a=rtpmap:103 ISAC/16000\r\n"
1941         "a=rtpmap:104 ISAC/32000\r\n"
1942         "a=rtpmap:105 telephone-event/8000\r\n"
1943         "a=fmtp:105 0-15,66,70\r\n"
1944         "a=fmtp:111 ";
1945     std::ostringstream os;
1946     os << "minptime=" << params.min_ptime << "; stereo=" << params.stereo
1947        << "; sprop-stereo=" << params.sprop_stereo
1948        << "; useinbandfec=" << params.useinband
1949        << "; maxaveragebitrate=" << params.maxaveragebitrate
1950        << "\r\n"
1951           "a=ptime:"
1952        << params.ptime
1953        << "\r\n"
1954           "a=maxptime:"
1955        << params.max_ptime << "\r\n";
1956     sdp += os.str();
1957 
1958     os.clear();
1959     os.str("");
1960     // Pl type 100 preferred.
1961     os << "m=video 9 RTP/SAVPF 99 95\r\n"
1962           "a=rtpmap:99 VP8/90000\r\n"
1963           "a=rtpmap:95 RTX/90000\r\n"
1964           "a=fmtp:95 apt=99;\r\n";
1965     sdp += os.str();
1966 
1967     // Deserialize
1968     SdpParseError error;
1969     EXPECT_TRUE(webrtc::SdpDeserialize(sdp, jdesc_output, &error));
1970 
1971     const AudioContentDescription* acd =
1972         GetFirstAudioContentDescription(jdesc_output->description());
1973     ASSERT_TRUE(acd);
1974     ASSERT_FALSE(acd->codecs().empty());
1975     cricket::AudioCodec opus = acd->codecs()[0];
1976     EXPECT_EQ("opus", opus.name);
1977     EXPECT_EQ(111, opus.id);
1978     VerifyCodecParameter(opus.params, "minptime", params.min_ptime);
1979     VerifyCodecParameter(opus.params, "stereo", params.stereo);
1980     VerifyCodecParameter(opus.params, "sprop-stereo", params.sprop_stereo);
1981     VerifyCodecParameter(opus.params, "useinbandfec", params.useinband);
1982     VerifyCodecParameter(opus.params, "maxaveragebitrate",
1983                          params.maxaveragebitrate);
1984     for (size_t i = 0; i < acd->codecs().size(); ++i) {
1985       cricket::AudioCodec codec = acd->codecs()[i];
1986       VerifyCodecParameter(codec.params, "ptime", params.ptime);
1987       VerifyCodecParameter(codec.params, "maxptime", params.max_ptime);
1988     }
1989 
1990     cricket::AudioCodec dtmf = acd->codecs()[3];
1991     EXPECT_EQ("telephone-event", dtmf.name);
1992     EXPECT_EQ(105, dtmf.id);
1993     EXPECT_EQ(3u,
1994               dtmf.params.size());  // ptime and max_ptime count as parameters.
1995     EXPECT_EQ(dtmf.params.begin()->first, "");
1996     EXPECT_EQ(dtmf.params.begin()->second, "0-15,66,70");
1997 
1998     const VideoContentDescription* vcd =
1999         GetFirstVideoContentDescription(jdesc_output->description());
2000     ASSERT_TRUE(vcd);
2001     ASSERT_FALSE(vcd->codecs().empty());
2002     cricket::VideoCodec vp8 = vcd->codecs()[0];
2003     EXPECT_EQ("VP8", vp8.name);
2004     EXPECT_EQ(99, vp8.id);
2005     cricket::VideoCodec rtx = vcd->codecs()[1];
2006     EXPECT_EQ("RTX", rtx.name);
2007     EXPECT_EQ(95, rtx.id);
2008     VerifyCodecParameter(rtx.params, "apt", vp8.id);
2009   }
2010 
TestDeserializeRtcpFb(JsepSessionDescription * jdesc_output,bool use_wildcard)2011   void TestDeserializeRtcpFb(JsepSessionDescription* jdesc_output,
2012                              bool use_wildcard) {
2013     std::string sdp_session_and_audio =
2014         "v=0\r\n"
2015         "o=- 18446744069414584320 18446462598732840960 IN IP4 127.0.0.1\r\n"
2016         "s=-\r\n"
2017         "t=0 0\r\n"
2018         // Include semantics for WebRTC Media Streams since it is supported by
2019         // this parser, and will be added to the SDP when serializing a session
2020         // description.
2021         "a=msid-semantic: WMS\r\n"
2022         "m=audio 9 RTP/SAVPF 111\r\n"
2023         "a=rtpmap:111 opus/48000/2\r\n";
2024     std::string sdp_video =
2025         "m=video 3457 RTP/SAVPF 101\r\n"
2026         "a=rtpmap:101 VP8/90000\r\n"
2027         "a=rtcp-fb:101 goog-lntf\r\n"
2028         "a=rtcp-fb:101 nack\r\n"
2029         "a=rtcp-fb:101 nack pli\r\n"
2030         "a=rtcp-fb:101 goog-remb\r\n";
2031     std::ostringstream os;
2032     os << sdp_session_and_audio;
2033     os << "a=rtcp-fb:" << (use_wildcard ? "*" : "111") << " nack\r\n";
2034     os << sdp_video;
2035     os << "a=rtcp-fb:" << (use_wildcard ? "*" : "101") << " ccm fir\r\n";
2036     std::string sdp = os.str();
2037     // Deserialize
2038     SdpParseError error;
2039     EXPECT_TRUE(webrtc::SdpDeserialize(sdp, jdesc_output, &error));
2040     const AudioContentDescription* acd =
2041         GetFirstAudioContentDescription(jdesc_output->description());
2042     ASSERT_TRUE(acd);
2043     ASSERT_FALSE(acd->codecs().empty());
2044     cricket::AudioCodec opus = acd->codecs()[0];
2045     EXPECT_EQ(111, opus.id);
2046     EXPECT_TRUE(opus.HasFeedbackParam(cricket::FeedbackParam(
2047         cricket::kRtcpFbParamNack, cricket::kParamValueEmpty)));
2048 
2049     const VideoContentDescription* vcd =
2050         GetFirstVideoContentDescription(jdesc_output->description());
2051     ASSERT_TRUE(vcd);
2052     ASSERT_FALSE(vcd->codecs().empty());
2053     cricket::VideoCodec vp8 = vcd->codecs()[0];
2054     EXPECT_STREQ(webrtc::JsepSessionDescription::kDefaultVideoCodecName,
2055                  vp8.name.c_str());
2056     EXPECT_EQ(101, vp8.id);
2057     EXPECT_TRUE(vp8.HasFeedbackParam(cricket::FeedbackParam(
2058         cricket::kRtcpFbParamLntf, cricket::kParamValueEmpty)));
2059     EXPECT_TRUE(vp8.HasFeedbackParam(cricket::FeedbackParam(
2060         cricket::kRtcpFbParamNack, cricket::kParamValueEmpty)));
2061     EXPECT_TRUE(vp8.HasFeedbackParam(cricket::FeedbackParam(
2062         cricket::kRtcpFbParamNack, cricket::kRtcpFbNackParamPli)));
2063     EXPECT_TRUE(vp8.HasFeedbackParam(cricket::FeedbackParam(
2064         cricket::kRtcpFbParamRemb, cricket::kParamValueEmpty)));
2065     EXPECT_TRUE(vp8.HasFeedbackParam(cricket::FeedbackParam(
2066         cricket::kRtcpFbParamCcm, cricket::kRtcpFbCcmParamFir)));
2067   }
2068 
2069   // Two SDP messages can mean the same thing but be different strings, e.g.
2070   // some of the lines can be serialized in different order.
2071   // However, a deserialized description can be compared field by field and has
2072   // no order. If deserializer has already been tested, serializing then
2073   // deserializing and comparing JsepSessionDescription will test
2074   // the serializer sufficiently.
TestSerialize(const JsepSessionDescription & jdesc)2075   void TestSerialize(const JsepSessionDescription& jdesc) {
2076     std::string message = webrtc::SdpSerialize(jdesc);
2077     JsepSessionDescription jdesc_output_des(kDummyType);
2078     SdpParseError error;
2079     EXPECT_TRUE(webrtc::SdpDeserialize(message, &jdesc_output_des, &error));
2080     EXPECT_TRUE(CompareSessionDescription(jdesc, jdesc_output_des));
2081   }
2082 
2083   // Calling 'Initialize' with a copy of the inner SessionDescription will
2084   // create a copy of the JsepSessionDescription without candidates. The
2085   // 'connection address' field, previously set from the candidates, must also
2086   // be reset.
MakeDescriptionWithoutCandidates(JsepSessionDescription * jdesc)2087   void MakeDescriptionWithoutCandidates(JsepSessionDescription* jdesc) {
2088     rtc::SocketAddress audio_addr("0.0.0.0", 9);
2089     rtc::SocketAddress video_addr("0.0.0.0", 9);
2090     audio_desc_->set_connection_address(audio_addr);
2091     video_desc_->set_connection_address(video_addr);
2092     ASSERT_TRUE(jdesc->Initialize(desc_.Clone(), kSessionId, kSessionVersion));
2093   }
2094 
2095  protected:
2096   SessionDescription desc_;
2097   AudioContentDescription* audio_desc_;
2098   VideoContentDescription* video_desc_;
2099   RtpDataContentDescription* data_desc_;
2100   SctpDataContentDescription* sctp_desc_;
2101   Candidates candidates_;
2102   std::unique_ptr<IceCandidateInterface> jcandidate_;
2103   JsepSessionDescription jdesc_;
2104 };
2105 
TestMismatch(const std::string & string1,const std::string & string2)2106 void TestMismatch(const std::string& string1, const std::string& string2) {
2107   int position = 0;
2108   for (size_t i = 0; i < string1.length() && i < string2.length(); ++i) {
2109     if (string1.c_str()[i] != string2.c_str()[i]) {
2110       position = static_cast<int>(i);
2111       break;
2112     }
2113   }
2114   EXPECT_EQ(0, position) << "Strings mismatch at the " << position
2115                          << " character\n"
2116                             " 1: "
2117                          << string1.substr(position, 20)
2118                          << "\n"
2119                             " 2: "
2120                          << string2.substr(position, 20) << "\n";
2121 }
2122 
TEST_F(WebRtcSdpTest,SerializeSessionDescription)2123 TEST_F(WebRtcSdpTest, SerializeSessionDescription) {
2124   // SessionDescription with desc and candidates.
2125   std::string message = webrtc::SdpSerialize(jdesc_);
2126   TestMismatch(std::string(kSdpFullString), message);
2127 }
2128 
TEST_F(WebRtcSdpTest,SerializeSessionDescriptionEmpty)2129 TEST_F(WebRtcSdpTest, SerializeSessionDescriptionEmpty) {
2130   JsepSessionDescription jdesc_empty(kDummyType);
2131   EXPECT_EQ("", webrtc::SdpSerialize(jdesc_empty));
2132 }
2133 
2134 // This tests serialization of SDP with a=crypto and a=fingerprint, as would be
2135 // the case in a DTLS offer.
TEST_F(WebRtcSdpTest,SerializeSessionDescriptionWithFingerprint)2136 TEST_F(WebRtcSdpTest, SerializeSessionDescriptionWithFingerprint) {
2137   AddFingerprint();
2138   JsepSessionDescription jdesc_with_fingerprint(kDummyType);
2139   MakeDescriptionWithoutCandidates(&jdesc_with_fingerprint);
2140   std::string message = webrtc::SdpSerialize(jdesc_with_fingerprint);
2141 
2142   std::string sdp_with_fingerprint = kSdpString;
2143   InjectAfter(kAttributeIcePwdVoice, kFingerprint, &sdp_with_fingerprint);
2144   InjectAfter(kAttributeIcePwdVideo, kFingerprint, &sdp_with_fingerprint);
2145 
2146   EXPECT_EQ(sdp_with_fingerprint, message);
2147 }
2148 
2149 // This tests serialization of SDP with a=fingerprint with no a=crypto, as would
2150 // be the case in a DTLS answer.
TEST_F(WebRtcSdpTest,SerializeSessionDescriptionWithFingerprintNoCryptos)2151 TEST_F(WebRtcSdpTest, SerializeSessionDescriptionWithFingerprintNoCryptos) {
2152   AddFingerprint();
2153   RemoveCryptos();
2154   JsepSessionDescription jdesc_with_fingerprint(kDummyType);
2155   MakeDescriptionWithoutCandidates(&jdesc_with_fingerprint);
2156   std::string message = webrtc::SdpSerialize(jdesc_with_fingerprint);
2157 
2158   std::string sdp_with_fingerprint = kSdpString;
2159   Replace(kAttributeCryptoVoice, "", &sdp_with_fingerprint);
2160   Replace(kAttributeCryptoVideo, "", &sdp_with_fingerprint);
2161   InjectAfter(kAttributeIcePwdVoice, kFingerprint, &sdp_with_fingerprint);
2162   InjectAfter(kAttributeIcePwdVideo, kFingerprint, &sdp_with_fingerprint);
2163 
2164   EXPECT_EQ(sdp_with_fingerprint, message);
2165 }
2166 
TEST_F(WebRtcSdpTest,SerializeSessionDescriptionWithoutCandidates)2167 TEST_F(WebRtcSdpTest, SerializeSessionDescriptionWithoutCandidates) {
2168   // JsepSessionDescription with desc but without candidates.
2169   JsepSessionDescription jdesc_no_candidates(kDummyType);
2170   MakeDescriptionWithoutCandidates(&jdesc_no_candidates);
2171   std::string message = webrtc::SdpSerialize(jdesc_no_candidates);
2172   EXPECT_EQ(std::string(kSdpString), message);
2173 }
2174 
TEST_F(WebRtcSdpTest,SerializeSessionDescriptionWithBundle)2175 TEST_F(WebRtcSdpTest, SerializeSessionDescriptionWithBundle) {
2176   ContentGroup group(cricket::GROUP_TYPE_BUNDLE);
2177   group.AddContentName(kAudioContentName);
2178   group.AddContentName(kVideoContentName);
2179   desc_.AddGroup(group);
2180   ASSERT_TRUE(jdesc_.Initialize(desc_.Clone(), jdesc_.session_id(),
2181                                 jdesc_.session_version()));
2182   std::string message = webrtc::SdpSerialize(jdesc_);
2183   std::string sdp_with_bundle = kSdpFullString;
2184   InjectAfter(kSessionTime,
2185               "a=group:BUNDLE audio_content_name video_content_name\r\n",
2186               &sdp_with_bundle);
2187   EXPECT_EQ(sdp_with_bundle, message);
2188 }
2189 
TEST_F(WebRtcSdpTest,SerializeSessionDescriptionWithBandwidth)2190 TEST_F(WebRtcSdpTest, SerializeSessionDescriptionWithBandwidth) {
2191   VideoContentDescription* vcd = GetFirstVideoContentDescription(&desc_);
2192   vcd->set_bandwidth(100 * 1000 + 755);  // Integer division will drop the 755.
2193   vcd->set_bandwidth_type("AS");
2194   AudioContentDescription* acd = GetFirstAudioContentDescription(&desc_);
2195   acd->set_bandwidth(555);
2196   acd->set_bandwidth_type("TIAS");
2197   ASSERT_TRUE(jdesc_.Initialize(desc_.Clone(), jdesc_.session_id(),
2198                                 jdesc_.session_version()));
2199   std::string message = webrtc::SdpSerialize(jdesc_);
2200   std::string sdp_with_bandwidth = kSdpFullString;
2201   InjectAfter("c=IN IP4 74.125.224.39\r\n", "b=AS:100\r\n",
2202               &sdp_with_bandwidth);
2203   InjectAfter("c=IN IP4 74.125.127.126\r\n", "b=TIAS:555\r\n",
2204               &sdp_with_bandwidth);
2205   EXPECT_EQ(sdp_with_bandwidth, message);
2206 }
2207 
2208 // Should default to b=AS if bandwidth_type isn't set.
TEST_F(WebRtcSdpTest,SerializeSessionDescriptionWithMissingBandwidthType)2209 TEST_F(WebRtcSdpTest, SerializeSessionDescriptionWithMissingBandwidthType) {
2210   VideoContentDescription* vcd = GetFirstVideoContentDescription(&desc_);
2211   vcd->set_bandwidth(100 * 1000);
2212   ASSERT_TRUE(jdesc_.Initialize(desc_.Clone(), jdesc_.session_id(),
2213                                 jdesc_.session_version()));
2214   std::string message = webrtc::SdpSerialize(jdesc_);
2215   std::string sdp_with_bandwidth = kSdpFullString;
2216   InjectAfter("c=IN IP4 74.125.224.39\r\n", "b=AS:100\r\n",
2217               &sdp_with_bandwidth);
2218   EXPECT_EQ(sdp_with_bandwidth, message);
2219 }
2220 
TEST_F(WebRtcSdpTest,SerializeSessionDescriptionWithIceOptions)2221 TEST_F(WebRtcSdpTest, SerializeSessionDescriptionWithIceOptions) {
2222   std::vector<std::string> transport_options;
2223   transport_options.push_back(kIceOption1);
2224   transport_options.push_back(kIceOption3);
2225   AddIceOptions(kAudioContentName, transport_options);
2226   transport_options.clear();
2227   transport_options.push_back(kIceOption2);
2228   transport_options.push_back(kIceOption3);
2229   AddIceOptions(kVideoContentName, transport_options);
2230   ASSERT_TRUE(jdesc_.Initialize(desc_.Clone(), jdesc_.session_id(),
2231                                 jdesc_.session_version()));
2232   std::string message = webrtc::SdpSerialize(jdesc_);
2233   std::string sdp_with_ice_options = kSdpFullString;
2234   InjectAfter(kAttributeIcePwdVoice, "a=ice-options:iceoption1 iceoption3\r\n",
2235               &sdp_with_ice_options);
2236   InjectAfter(kAttributeIcePwdVideo, "a=ice-options:iceoption2 iceoption3\r\n",
2237               &sdp_with_ice_options);
2238   EXPECT_EQ(sdp_with_ice_options, message);
2239 }
2240 
TEST_F(WebRtcSdpTest,SerializeSessionDescriptionWithRecvOnlyContent)2241 TEST_F(WebRtcSdpTest, SerializeSessionDescriptionWithRecvOnlyContent) {
2242   EXPECT_TRUE(TestSerializeDirection(RtpTransceiverDirection::kRecvOnly));
2243 }
2244 
TEST_F(WebRtcSdpTest,SerializeSessionDescriptionWithSendOnlyContent)2245 TEST_F(WebRtcSdpTest, SerializeSessionDescriptionWithSendOnlyContent) {
2246   EXPECT_TRUE(TestSerializeDirection(RtpTransceiverDirection::kSendOnly));
2247 }
2248 
TEST_F(WebRtcSdpTest,SerializeSessionDescriptionWithInactiveContent)2249 TEST_F(WebRtcSdpTest, SerializeSessionDescriptionWithInactiveContent) {
2250   EXPECT_TRUE(TestSerializeDirection(RtpTransceiverDirection::kInactive));
2251 }
2252 
TEST_F(WebRtcSdpTest,SerializeSessionDescriptionWithAudioRejected)2253 TEST_F(WebRtcSdpTest, SerializeSessionDescriptionWithAudioRejected) {
2254   EXPECT_TRUE(TestSerializeRejected(true, false));
2255 }
2256 
TEST_F(WebRtcSdpTest,SerializeSessionDescriptionWithVideoRejected)2257 TEST_F(WebRtcSdpTest, SerializeSessionDescriptionWithVideoRejected) {
2258   EXPECT_TRUE(TestSerializeRejected(false, true));
2259 }
2260 
TEST_F(WebRtcSdpTest,SerializeSessionDescriptionWithAudioVideoRejected)2261 TEST_F(WebRtcSdpTest, SerializeSessionDescriptionWithAudioVideoRejected) {
2262   EXPECT_TRUE(TestSerializeRejected(true, true));
2263 }
2264 
TEST_F(WebRtcSdpTest,SerializeSessionDescriptionWithRtpDataChannel)2265 TEST_F(WebRtcSdpTest, SerializeSessionDescriptionWithRtpDataChannel) {
2266   AddRtpDataChannel();
2267   JsepSessionDescription jsep_desc(kDummyType);
2268 
2269   MakeDescriptionWithoutCandidates(&jsep_desc);
2270   std::string message = webrtc::SdpSerialize(jsep_desc);
2271 
2272   std::string expected_sdp = kSdpString;
2273   expected_sdp.append(kSdpRtpDataChannelString);
2274   EXPECT_EQ(expected_sdp, message);
2275 }
2276 
TEST_F(WebRtcSdpTest,SerializeSessionDescriptionWithSctpDataChannel)2277 TEST_F(WebRtcSdpTest, SerializeSessionDescriptionWithSctpDataChannel) {
2278   bool use_sctpmap = true;
2279   AddSctpDataChannel(use_sctpmap);
2280   JsepSessionDescription jsep_desc(kDummyType);
2281 
2282   MakeDescriptionWithoutCandidates(&jsep_desc);
2283   std::string message = webrtc::SdpSerialize(jsep_desc);
2284 
2285   std::string expected_sdp = kSdpString;
2286   expected_sdp.append(kSdpSctpDataChannelString);
2287   EXPECT_EQ(message, expected_sdp);
2288 }
2289 
MutateJsepSctpPort(JsepSessionDescription * jdesc,const SessionDescription & desc,int port)2290 void MutateJsepSctpPort(JsepSessionDescription* jdesc,
2291                         const SessionDescription& desc,
2292                         int port) {
2293   // Take our pre-built session description and change the SCTP port.
2294   std::unique_ptr<cricket::SessionDescription> mutant = desc.Clone();
2295   SctpDataContentDescription* dcdesc =
2296       mutant->GetContentDescriptionByName(kDataContentName)->as_sctp();
2297   dcdesc->set_port(port);
2298   ASSERT_TRUE(
2299       jdesc->Initialize(std::move(mutant), kSessionId, kSessionVersion));
2300 }
2301 
TEST_F(WebRtcSdpTest,SerializeWithSctpDataChannelAndNewPort)2302 TEST_F(WebRtcSdpTest, SerializeWithSctpDataChannelAndNewPort) {
2303   bool use_sctpmap = true;
2304   AddSctpDataChannel(use_sctpmap);
2305   JsepSessionDescription jsep_desc(kDummyType);
2306   MakeDescriptionWithoutCandidates(&jsep_desc);
2307 
2308   const int kNewPort = 1234;
2309   MutateJsepSctpPort(&jsep_desc, desc_, kNewPort);
2310 
2311   std::string message = webrtc::SdpSerialize(jsep_desc);
2312 
2313   std::string expected_sdp = kSdpString;
2314   expected_sdp.append(kSdpSctpDataChannelString);
2315 
2316   absl::StrReplaceAll(
2317       {{rtc::ToString(kDefaultSctpPort), rtc::ToString(kNewPort)}},
2318       &expected_sdp);
2319 
2320   EXPECT_EQ(expected_sdp, message);
2321 }
2322 
TEST_F(WebRtcSdpTest,SerializeSessionDescriptionWithDataChannelAndBandwidth)2323 TEST_F(WebRtcSdpTest, SerializeSessionDescriptionWithDataChannelAndBandwidth) {
2324   JsepSessionDescription jsep_desc(kDummyType);
2325   AddRtpDataChannel();
2326   data_desc_->set_bandwidth(100 * 1000);
2327   data_desc_->set_bandwidth_type("AS");
2328   MakeDescriptionWithoutCandidates(&jsep_desc);
2329   std::string message = webrtc::SdpSerialize(jsep_desc);
2330 
2331   std::string expected_sdp = kSdpString;
2332   expected_sdp.append(kSdpRtpDataChannelString);
2333   // Serializing data content shouldn't ignore bandwidth settings.
2334   InjectAfter("m=application 9 RTP/SAVPF 101\r\nc=IN IP4 0.0.0.0\r\n",
2335               "b=AS:100\r\n", &expected_sdp);
2336   EXPECT_EQ(expected_sdp, message);
2337 }
2338 
TEST_F(WebRtcSdpTest,SerializeSessionDescriptionWithExtmapAllowMixed)2339 TEST_F(WebRtcSdpTest, SerializeSessionDescriptionWithExtmapAllowMixed) {
2340   jdesc_.description()->set_extmap_allow_mixed(true);
2341   TestSerialize(jdesc_);
2342 }
2343 
TEST_F(WebRtcSdpTest,SerializeMediaContentDescriptionWithExtmapAllowMixed)2344 TEST_F(WebRtcSdpTest, SerializeMediaContentDescriptionWithExtmapAllowMixed) {
2345   cricket::MediaContentDescription* video_desc =
2346       jdesc_.description()->GetContentDescriptionByName(kVideoContentName);
2347   ASSERT_TRUE(video_desc);
2348   cricket::MediaContentDescription* audio_desc =
2349       jdesc_.description()->GetContentDescriptionByName(kAudioContentName);
2350   ASSERT_TRUE(audio_desc);
2351   video_desc->set_extmap_allow_mixed_enum(
2352       cricket::MediaContentDescription::kMedia);
2353   audio_desc->set_extmap_allow_mixed_enum(
2354       cricket::MediaContentDescription::kMedia);
2355   TestSerialize(jdesc_);
2356 }
2357 
TEST_F(WebRtcSdpTest,SerializeSessionDescriptionWithExtmap)2358 TEST_F(WebRtcSdpTest, SerializeSessionDescriptionWithExtmap) {
2359   bool encrypted = false;
2360   AddExtmap(encrypted);
2361   JsepSessionDescription desc_with_extmap(kDummyType);
2362   MakeDescriptionWithoutCandidates(&desc_with_extmap);
2363   std::string message = webrtc::SdpSerialize(desc_with_extmap);
2364 
2365   std::string sdp_with_extmap = kSdpString;
2366   InjectAfter("a=mid:audio_content_name\r\n", kExtmap, &sdp_with_extmap);
2367   InjectAfter("a=mid:video_content_name\r\n", kExtmap, &sdp_with_extmap);
2368 
2369   EXPECT_EQ(sdp_with_extmap, message);
2370 }
2371 
TEST_F(WebRtcSdpTest,SerializeSessionDescriptionWithExtmapEncrypted)2372 TEST_F(WebRtcSdpTest, SerializeSessionDescriptionWithExtmapEncrypted) {
2373   bool encrypted = true;
2374   AddExtmap(encrypted);
2375   JsepSessionDescription desc_with_extmap(kDummyType);
2376   ASSERT_TRUE(
2377       desc_with_extmap.Initialize(desc_.Clone(), kSessionId, kSessionVersion));
2378   TestSerialize(desc_with_extmap);
2379 }
2380 
TEST_F(WebRtcSdpTest,SerializeCandidates)2381 TEST_F(WebRtcSdpTest, SerializeCandidates) {
2382   std::string message = webrtc::SdpSerializeCandidate(*jcandidate_);
2383   EXPECT_EQ(std::string(kRawCandidate), message);
2384 
2385   Candidate candidate_with_ufrag(candidates_.front());
2386   candidate_with_ufrag.set_username("ABC");
2387   jcandidate_.reset(new JsepIceCandidate(std::string("audio_content_name"), 0,
2388                                          candidate_with_ufrag));
2389   message = webrtc::SdpSerializeCandidate(*jcandidate_);
2390   EXPECT_EQ(std::string(kRawCandidate) + " ufrag ABC", message);
2391 
2392   Candidate candidate_with_network_info(candidates_.front());
2393   candidate_with_network_info.set_network_id(1);
2394   jcandidate_.reset(new JsepIceCandidate(std::string("audio"), 0,
2395                                          candidate_with_network_info));
2396   message = webrtc::SdpSerializeCandidate(*jcandidate_);
2397   EXPECT_EQ(std::string(kRawCandidate) + " network-id 1", message);
2398   candidate_with_network_info.set_network_cost(999);
2399   jcandidate_.reset(new JsepIceCandidate(std::string("audio"), 0,
2400                                          candidate_with_network_info));
2401   message = webrtc::SdpSerializeCandidate(*jcandidate_);
2402   EXPECT_EQ(std::string(kRawCandidate) + " network-id 1 network-cost 999",
2403             message);
2404 }
2405 
TEST_F(WebRtcSdpTest,SerializeHostnameCandidate)2406 TEST_F(WebRtcSdpTest, SerializeHostnameCandidate) {
2407   rtc::SocketAddress address("a.test", 1234);
2408   cricket::Candidate candidate(
2409       cricket::ICE_CANDIDATE_COMPONENT_RTP, "udp", address, kCandidatePriority,
2410       "", "", LOCAL_PORT_TYPE, kCandidateGeneration, kCandidateFoundation1);
2411   JsepIceCandidate jcandidate(std::string("audio_content_name"), 0, candidate);
2412   std::string message = webrtc::SdpSerializeCandidate(jcandidate);
2413   EXPECT_EQ(std::string(kRawHostnameCandidate), message);
2414 }
2415 
TEST_F(WebRtcSdpTest,SerializeTcpCandidates)2416 TEST_F(WebRtcSdpTest, SerializeTcpCandidates) {
2417   Candidate candidate(ICE_CANDIDATE_COMPONENT_RTP, "tcp",
2418                       rtc::SocketAddress("192.168.1.5", 9), kCandidatePriority,
2419                       "", "", LOCAL_PORT_TYPE, kCandidateGeneration,
2420                       kCandidateFoundation1);
2421   candidate.set_tcptype(cricket::TCPTYPE_ACTIVE_STR);
2422   std::unique_ptr<IceCandidateInterface> jcandidate(
2423       new JsepIceCandidate(std::string("audio_content_name"), 0, candidate));
2424 
2425   std::string message = webrtc::SdpSerializeCandidate(*jcandidate);
2426   EXPECT_EQ(std::string(kSdpTcpActiveCandidate), message);
2427 }
2428 
2429 // Test serializing a TCP candidate that came in with a missing tcptype. This
2430 // shouldn't happen according to the spec, but our implementation has been
2431 // accepting this for quite some time, treating it as a passive candidate.
2432 //
2433 // So, we should be able to at least convert such candidates to and from SDP.
2434 // See: bugs.webrtc.org/11423
TEST_F(WebRtcSdpTest,ParseTcpCandidateWithoutTcptype)2435 TEST_F(WebRtcSdpTest, ParseTcpCandidateWithoutTcptype) {
2436   std::string missing_tcptype =
2437       "candidate:a0+B/1 1 tcp 2130706432 192.168.1.5 9999 typ host";
2438   JsepIceCandidate jcandidate(kDummyMid, kDummyIndex);
2439   EXPECT_TRUE(SdpDeserializeCandidate(missing_tcptype, &jcandidate));
2440 
2441   EXPECT_EQ(std::string(cricket::TCPTYPE_PASSIVE_STR),
2442             jcandidate.candidate().tcptype());
2443 }
2444 
TEST_F(WebRtcSdpTest,ParseSslTcpCandidate)2445 TEST_F(WebRtcSdpTest, ParseSslTcpCandidate) {
2446   std::string ssltcp =
2447       "candidate:a0+B/1 1 ssltcp 2130706432 192.168.1.5 9999 typ host tcptype "
2448       "passive";
2449   JsepIceCandidate jcandidate(kDummyMid, kDummyIndex);
2450   EXPECT_TRUE(SdpDeserializeCandidate(ssltcp, &jcandidate));
2451 
2452   EXPECT_EQ(std::string("ssltcp"), jcandidate.candidate().protocol());
2453 }
2454 
TEST_F(WebRtcSdpTest,SerializeSessionDescriptionWithH264)2455 TEST_F(WebRtcSdpTest, SerializeSessionDescriptionWithH264) {
2456   cricket::VideoCodec h264_codec("H264");
2457   h264_codec.SetParam("profile-level-id", "42e01f");
2458   h264_codec.SetParam("level-asymmetry-allowed", "1");
2459   h264_codec.SetParam("packetization-mode", "1");
2460   video_desc_->AddCodec(h264_codec);
2461 
2462   jdesc_.Initialize(desc_.Clone(), kSessionId, kSessionVersion);
2463 
2464   std::string message = webrtc::SdpSerialize(jdesc_);
2465   size_t after_pt = message.find(" H264/90000");
2466   ASSERT_NE(after_pt, std::string::npos);
2467   size_t before_pt = message.rfind("a=rtpmap:", after_pt);
2468   ASSERT_NE(before_pt, std::string::npos);
2469   before_pt += strlen("a=rtpmap:");
2470   std::string pt = message.substr(before_pt, after_pt - before_pt);
2471   // TODO(hta): Check if payload type |pt| occurs in the m=video line.
2472   std::string to_find = "a=fmtp:" + pt + " ";
2473   size_t fmtp_pos = message.find(to_find);
2474   ASSERT_NE(std::string::npos, fmtp_pos) << "Failed to find " << to_find;
2475   size_t fmtp_endpos = message.find('\n', fmtp_pos);
2476   ASSERT_NE(std::string::npos, fmtp_endpos);
2477   std::string fmtp_value = message.substr(fmtp_pos, fmtp_endpos);
2478   EXPECT_NE(std::string::npos, fmtp_value.find("level-asymmetry-allowed=1"));
2479   EXPECT_NE(std::string::npos, fmtp_value.find("packetization-mode=1"));
2480   EXPECT_NE(std::string::npos, fmtp_value.find("profile-level-id=42e01f"));
2481   // Check that there are no spaces after semicolons.
2482   // https://bugs.webrtc.org/5793
2483   EXPECT_EQ(std::string::npos, fmtp_value.find("; "));
2484 }
2485 
TEST_F(WebRtcSdpTest,DeserializeSessionDescription)2486 TEST_F(WebRtcSdpTest, DeserializeSessionDescription) {
2487   JsepSessionDescription jdesc(kDummyType);
2488   // Deserialize
2489   EXPECT_TRUE(SdpDeserialize(kSdpFullString, &jdesc));
2490   // Verify
2491   EXPECT_TRUE(CompareSessionDescription(jdesc_, jdesc));
2492 }
2493 
TEST_F(WebRtcSdpTest,DeserializeSessionDescriptionWithoutMline)2494 TEST_F(WebRtcSdpTest, DeserializeSessionDescriptionWithoutMline) {
2495   JsepSessionDescription jdesc(kDummyType);
2496   const char kSdpWithoutMline[] =
2497       "v=0\r\n"
2498       "o=- 18446744069414584320 18446462598732840960 IN IP4 127.0.0.1\r\n"
2499       "s=-\r\n"
2500       "t=0 0\r\n"
2501       "a=msid-semantic: WMS local_stream_1 local_stream_2\r\n";
2502   // Deserialize
2503   EXPECT_TRUE(SdpDeserialize(kSdpWithoutMline, &jdesc));
2504   EXPECT_EQ(0u, jdesc.description()->contents().size());
2505 }
2506 
TEST_F(WebRtcSdpTest,DeserializeSessionDescriptionWithoutCarriageReturn)2507 TEST_F(WebRtcSdpTest, DeserializeSessionDescriptionWithoutCarriageReturn) {
2508   JsepSessionDescription jdesc(kDummyType);
2509   std::string sdp_without_carriage_return = kSdpFullString;
2510   Replace("\r\n", "\n", &sdp_without_carriage_return);
2511   // Deserialize
2512   EXPECT_TRUE(SdpDeserialize(sdp_without_carriage_return, &jdesc));
2513   // Verify
2514   EXPECT_TRUE(CompareSessionDescription(jdesc_, jdesc));
2515 }
2516 
TEST_F(WebRtcSdpTest,DeserializeSessionDescriptionWithoutCandidates)2517 TEST_F(WebRtcSdpTest, DeserializeSessionDescriptionWithoutCandidates) {
2518   // SessionDescription with desc but without candidates.
2519   JsepSessionDescription jdesc_no_candidates(kDummyType);
2520   ASSERT_TRUE(jdesc_no_candidates.Initialize(desc_.Clone(), kSessionId,
2521                                              kSessionVersion));
2522   JsepSessionDescription new_jdesc(kDummyType);
2523   EXPECT_TRUE(SdpDeserialize(kSdpString, &new_jdesc));
2524   EXPECT_TRUE(CompareSessionDescription(jdesc_no_candidates, new_jdesc));
2525 }
2526 
TEST_F(WebRtcSdpTest,DeserializeSessionDescriptionWithoutRtpmap)2527 TEST_F(WebRtcSdpTest, DeserializeSessionDescriptionWithoutRtpmap) {
2528   static const char kSdpNoRtpmapString[] =
2529       "v=0\r\n"
2530       "o=- 11 22 IN IP4 127.0.0.1\r\n"
2531       "s=-\r\n"
2532       "t=0 0\r\n"
2533       "m=audio 49232 RTP/AVP 0 18 103\r\n"
2534       // Codec that doesn't appear in the m= line will be ignored.
2535       "a=rtpmap:104 ISAC/32000\r\n"
2536       // The rtpmap line for static payload codec is optional.
2537       "a=rtpmap:18 G729/16000\r\n"
2538       "a=rtpmap:103 ISAC/16000\r\n";
2539 
2540   JsepSessionDescription jdesc(kDummyType);
2541   EXPECT_TRUE(SdpDeserialize(kSdpNoRtpmapString, &jdesc));
2542   cricket::AudioContentDescription* audio =
2543       cricket::GetFirstAudioContentDescription(jdesc.description());
2544   AudioCodecs ref_codecs;
2545   // The codecs in the AudioContentDescription should be in the same order as
2546   // the payload types (<fmt>s) on the m= line.
2547   ref_codecs.push_back(AudioCodec(0, "PCMU", 8000, 0, 1));
2548   ref_codecs.push_back(AudioCodec(18, "G729", 16000, 0, 1));
2549   ref_codecs.push_back(AudioCodec(103, "ISAC", 16000, 0, 1));
2550   EXPECT_EQ(ref_codecs, audio->codecs());
2551 }
2552 
TEST_F(WebRtcSdpTest,DeserializeSessionDescriptionWithoutRtpmapButWithFmtp)2553 TEST_F(WebRtcSdpTest, DeserializeSessionDescriptionWithoutRtpmapButWithFmtp) {
2554   static const char kSdpNoRtpmapString[] =
2555       "v=0\r\n"
2556       "o=- 11 22 IN IP4 127.0.0.1\r\n"
2557       "s=-\r\n"
2558       "t=0 0\r\n"
2559       "m=audio 49232 RTP/AVP 18 103\r\n"
2560       "a=fmtp:18 annexb=yes\r\n"
2561       "a=rtpmap:103 ISAC/16000\r\n";
2562 
2563   JsepSessionDescription jdesc(kDummyType);
2564   EXPECT_TRUE(SdpDeserialize(kSdpNoRtpmapString, &jdesc));
2565   cricket::AudioContentDescription* audio =
2566       cricket::GetFirstAudioContentDescription(jdesc.description());
2567 
2568   cricket::AudioCodec g729 = audio->codecs()[0];
2569   EXPECT_EQ("G729", g729.name);
2570   EXPECT_EQ(8000, g729.clockrate);
2571   EXPECT_EQ(18, g729.id);
2572   cricket::CodecParameterMap::iterator found = g729.params.find("annexb");
2573   ASSERT_TRUE(found != g729.params.end());
2574   EXPECT_EQ(found->second, "yes");
2575 
2576   cricket::AudioCodec isac = audio->codecs()[1];
2577   EXPECT_EQ("ISAC", isac.name);
2578   EXPECT_EQ(103, isac.id);
2579   EXPECT_EQ(16000, isac.clockrate);
2580 }
2581 
2582 // Ensure that we can deserialize SDP with a=fingerprint properly.
TEST_F(WebRtcSdpTest,DeserializeJsepSessionDescriptionWithFingerprint)2583 TEST_F(WebRtcSdpTest, DeserializeJsepSessionDescriptionWithFingerprint) {
2584   // Add a DTLS a=fingerprint attribute to our session description.
2585   AddFingerprint();
2586   JsepSessionDescription new_jdesc(kDummyType);
2587   ASSERT_TRUE(new_jdesc.Initialize(desc_.Clone(), jdesc_.session_id(),
2588                                    jdesc_.session_version()));
2589 
2590   JsepSessionDescription jdesc_with_fingerprint(kDummyType);
2591   std::string sdp_with_fingerprint = kSdpString;
2592   InjectAfter(kAttributeIcePwdVoice, kFingerprint, &sdp_with_fingerprint);
2593   InjectAfter(kAttributeIcePwdVideo, kFingerprint, &sdp_with_fingerprint);
2594   EXPECT_TRUE(SdpDeserialize(sdp_with_fingerprint, &jdesc_with_fingerprint));
2595   EXPECT_TRUE(CompareSessionDescription(jdesc_with_fingerprint, new_jdesc));
2596 }
2597 
TEST_F(WebRtcSdpTest,DeserializeSessionDescriptionWithBundle)2598 TEST_F(WebRtcSdpTest, DeserializeSessionDescriptionWithBundle) {
2599   JsepSessionDescription jdesc_with_bundle(kDummyType);
2600   std::string sdp_with_bundle = kSdpFullString;
2601   InjectAfter(kSessionTime,
2602               "a=group:BUNDLE audio_content_name video_content_name\r\n",
2603               &sdp_with_bundle);
2604   EXPECT_TRUE(SdpDeserialize(sdp_with_bundle, &jdesc_with_bundle));
2605   ContentGroup group(cricket::GROUP_TYPE_BUNDLE);
2606   group.AddContentName(kAudioContentName);
2607   group.AddContentName(kVideoContentName);
2608   desc_.AddGroup(group);
2609   ASSERT_TRUE(jdesc_.Initialize(desc_.Clone(), jdesc_.session_id(),
2610                                 jdesc_.session_version()));
2611   EXPECT_TRUE(CompareSessionDescription(jdesc_, jdesc_with_bundle));
2612 }
2613 
TEST_F(WebRtcSdpTest,DeserializeSessionDescriptionWithBandwidth)2614 TEST_F(WebRtcSdpTest, DeserializeSessionDescriptionWithBandwidth) {
2615   JsepSessionDescription jdesc_with_bandwidth(kDummyType);
2616   std::string sdp_with_bandwidth = kSdpFullString;
2617   InjectAfter("a=mid:video_content_name\r\na=sendrecv\r\n", "b=AS:100\r\n",
2618               &sdp_with_bandwidth);
2619   InjectAfter("a=mid:audio_content_name\r\na=sendrecv\r\n", "b=AS:50\r\n",
2620               &sdp_with_bandwidth);
2621   EXPECT_TRUE(SdpDeserialize(sdp_with_bandwidth, &jdesc_with_bandwidth));
2622   VideoContentDescription* vcd = GetFirstVideoContentDescription(&desc_);
2623   vcd->set_bandwidth(100 * 1000);
2624   AudioContentDescription* acd = GetFirstAudioContentDescription(&desc_);
2625   acd->set_bandwidth(50 * 1000);
2626   ASSERT_TRUE(jdesc_.Initialize(desc_.Clone(), jdesc_.session_id(),
2627                                 jdesc_.session_version()));
2628   EXPECT_TRUE(CompareSessionDescription(jdesc_, jdesc_with_bandwidth));
2629 }
2630 
TEST_F(WebRtcSdpTest,DeserializeSessionDescriptionWithTiasBandwidth)2631 TEST_F(WebRtcSdpTest, DeserializeSessionDescriptionWithTiasBandwidth) {
2632   JsepSessionDescription jdesc_with_bandwidth(kDummyType);
2633   std::string sdp_with_bandwidth = kSdpFullString;
2634   InjectAfter("a=mid:video_content_name\r\na=sendrecv\r\n", "b=TIAS:100000\r\n",
2635               &sdp_with_bandwidth);
2636   InjectAfter("a=mid:audio_content_name\r\na=sendrecv\r\n", "b=TIAS:50000\r\n",
2637               &sdp_with_bandwidth);
2638   EXPECT_TRUE(SdpDeserialize(sdp_with_bandwidth, &jdesc_with_bandwidth));
2639   VideoContentDescription* vcd = GetFirstVideoContentDescription(&desc_);
2640   vcd->set_bandwidth(100 * 1000);
2641   AudioContentDescription* acd = GetFirstAudioContentDescription(&desc_);
2642   acd->set_bandwidth(50 * 1000);
2643   ASSERT_TRUE(jdesc_.Initialize(desc_.Clone(), jdesc_.session_id(),
2644                                 jdesc_.session_version()));
2645   EXPECT_TRUE(CompareSessionDescription(jdesc_, jdesc_with_bandwidth));
2646 }
2647 
TEST_F(WebRtcSdpTest,DeserializeSessionDescriptionWithUnknownBandwidthModifier)2648 TEST_F(WebRtcSdpTest,
2649        DeserializeSessionDescriptionWithUnknownBandwidthModifier) {
2650   JsepSessionDescription jdesc_with_bandwidth(kDummyType);
2651   std::string sdp_with_bandwidth = kSdpFullString;
2652   InjectAfter("a=mid:video_content_name\r\na=sendrecv\r\n",
2653               "b=unknown:100000\r\n", &sdp_with_bandwidth);
2654   InjectAfter("a=mid:audio_content_name\r\na=sendrecv\r\n",
2655               "b=unknown:50000\r\n", &sdp_with_bandwidth);
2656   EXPECT_TRUE(SdpDeserialize(sdp_with_bandwidth, &jdesc_with_bandwidth));
2657   VideoContentDescription* vcd = GetFirstVideoContentDescription(&desc_);
2658   vcd->set_bandwidth(-1);
2659   AudioContentDescription* acd = GetFirstAudioContentDescription(&desc_);
2660   acd->set_bandwidth(-1);
2661   ASSERT_TRUE(jdesc_.Initialize(desc_.Clone(), jdesc_.session_id(),
2662                                 jdesc_.session_version()));
2663   EXPECT_TRUE(CompareSessionDescription(jdesc_, jdesc_with_bandwidth));
2664 }
2665 
TEST_F(WebRtcSdpTest,DeserializeSessionDescriptionWithIceOptions)2666 TEST_F(WebRtcSdpTest, DeserializeSessionDescriptionWithIceOptions) {
2667   JsepSessionDescription jdesc_with_ice_options(kDummyType);
2668   std::string sdp_with_ice_options = kSdpFullString;
2669   InjectAfter(kSessionTime, "a=ice-options:iceoption3\r\n",
2670               &sdp_with_ice_options);
2671   InjectAfter(kAttributeIcePwdVoice, "a=ice-options:iceoption1\r\n",
2672               &sdp_with_ice_options);
2673   InjectAfter(kAttributeIcePwdVideo, "a=ice-options:iceoption2\r\n",
2674               &sdp_with_ice_options);
2675   EXPECT_TRUE(SdpDeserialize(sdp_with_ice_options, &jdesc_with_ice_options));
2676   std::vector<std::string> transport_options;
2677   transport_options.push_back(kIceOption3);
2678   transport_options.push_back(kIceOption1);
2679   AddIceOptions(kAudioContentName, transport_options);
2680   transport_options.clear();
2681   transport_options.push_back(kIceOption3);
2682   transport_options.push_back(kIceOption2);
2683   AddIceOptions(kVideoContentName, transport_options);
2684   ASSERT_TRUE(jdesc_.Initialize(desc_.Clone(), jdesc_.session_id(),
2685                                 jdesc_.session_version()));
2686   EXPECT_TRUE(CompareSessionDescription(jdesc_, jdesc_with_ice_options));
2687 }
2688 
TEST_F(WebRtcSdpTest,DeserializeSessionDescriptionWithUfragPwd)2689 TEST_F(WebRtcSdpTest, DeserializeSessionDescriptionWithUfragPwd) {
2690   // Remove the original ice-ufrag and ice-pwd
2691   JsepSessionDescription jdesc_with_ufrag_pwd(kDummyType);
2692   std::string sdp_with_ufrag_pwd = kSdpFullString;
2693   EXPECT_TRUE(RemoveCandidateUfragPwd(&sdp_with_ufrag_pwd));
2694   // Add session level ufrag and pwd
2695   InjectAfter(kSessionTime,
2696               "a=ice-pwd:session+level+icepwd\r\n"
2697               "a=ice-ufrag:session+level+iceufrag\r\n",
2698               &sdp_with_ufrag_pwd);
2699   // Add media level ufrag and pwd for audio
2700   InjectAfter(
2701       "a=mid:audio_content_name\r\n",
2702       "a=ice-pwd:media+level+icepwd\r\na=ice-ufrag:media+level+iceufrag\r\n",
2703       &sdp_with_ufrag_pwd);
2704   // Update the candidate ufrag and pwd to the expected ones.
2705   EXPECT_TRUE(UpdateCandidateUfragPwd(&jdesc_, 0, "media+level+iceufrag",
2706                                       "media+level+icepwd"));
2707   EXPECT_TRUE(UpdateCandidateUfragPwd(&jdesc_, 1, "session+level+iceufrag",
2708                                       "session+level+icepwd"));
2709   EXPECT_TRUE(SdpDeserialize(sdp_with_ufrag_pwd, &jdesc_with_ufrag_pwd));
2710   EXPECT_TRUE(CompareSessionDescription(jdesc_, jdesc_with_ufrag_pwd));
2711 }
2712 
TEST_F(WebRtcSdpTest,DeserializeSessionDescriptionWithRecvOnlyContent)2713 TEST_F(WebRtcSdpTest, DeserializeSessionDescriptionWithRecvOnlyContent) {
2714   EXPECT_TRUE(TestDeserializeDirection(RtpTransceiverDirection::kRecvOnly));
2715 }
2716 
TEST_F(WebRtcSdpTest,DeserializeSessionDescriptionWithSendOnlyContent)2717 TEST_F(WebRtcSdpTest, DeserializeSessionDescriptionWithSendOnlyContent) {
2718   EXPECT_TRUE(TestDeserializeDirection(RtpTransceiverDirection::kSendOnly));
2719 }
2720 
TEST_F(WebRtcSdpTest,DeserializeSessionDescriptionWithInactiveContent)2721 TEST_F(WebRtcSdpTest, DeserializeSessionDescriptionWithInactiveContent) {
2722   EXPECT_TRUE(TestDeserializeDirection(RtpTransceiverDirection::kInactive));
2723 }
2724 
TEST_F(WebRtcSdpTest,DeserializeSessionDescriptionWithRejectedAudio)2725 TEST_F(WebRtcSdpTest, DeserializeSessionDescriptionWithRejectedAudio) {
2726   EXPECT_TRUE(TestDeserializeRejected(true, false));
2727 }
2728 
TEST_F(WebRtcSdpTest,DeserializeSessionDescriptionWithRejectedVideo)2729 TEST_F(WebRtcSdpTest, DeserializeSessionDescriptionWithRejectedVideo) {
2730   EXPECT_TRUE(TestDeserializeRejected(false, true));
2731 }
2732 
TEST_F(WebRtcSdpTest,DeserializeSessionDescriptionWithRejectedAudioVideo)2733 TEST_F(WebRtcSdpTest, DeserializeSessionDescriptionWithRejectedAudioVideo) {
2734   EXPECT_TRUE(TestDeserializeRejected(true, true));
2735 }
2736 
2737 // Tests that we can still handle the sdp uses mslabel and label instead of
2738 // msid for backward compatibility.
TEST_F(WebRtcSdpTest,DeserializeSessionDescriptionWithoutMsid)2739 TEST_F(WebRtcSdpTest, DeserializeSessionDescriptionWithoutMsid) {
2740   jdesc_.description()->set_msid_supported(false);
2741   JsepSessionDescription jdesc(kDummyType);
2742   std::string sdp_without_msid = kSdpFullString;
2743   Replace("msid", "xmsid", &sdp_without_msid);
2744   // Deserialize
2745   EXPECT_TRUE(SdpDeserialize(sdp_without_msid, &jdesc));
2746   // Verify
2747   EXPECT_TRUE(CompareSessionDescription(jdesc_, jdesc));
2748   EXPECT_FALSE(jdesc.description()->msid_signaling() &
2749                ~cricket::kMsidSignalingSsrcAttribute);
2750 }
2751 
TEST_F(WebRtcSdpTest,DeserializeSessionDescriptionWithExtmapAllowMixed)2752 TEST_F(WebRtcSdpTest, DeserializeSessionDescriptionWithExtmapAllowMixed) {
2753   jdesc_.description()->set_extmap_allow_mixed(true);
2754   std::string sdp_with_extmap_allow_mixed = kSdpFullString;
2755   InjectAfter("t=0 0\r\n", kExtmapAllowMixed, &sdp_with_extmap_allow_mixed);
2756   // Deserialize
2757   JsepSessionDescription jdesc_deserialized(kDummyType);
2758   EXPECT_TRUE(SdpDeserialize(sdp_with_extmap_allow_mixed, &jdesc_deserialized));
2759   // Verify
2760   EXPECT_TRUE(CompareSessionDescription(jdesc_, jdesc_deserialized));
2761 }
2762 
TEST_F(WebRtcSdpTest,DeserializeSessionDescriptionWithoutExtmapAllowMixed)2763 TEST_F(WebRtcSdpTest, DeserializeSessionDescriptionWithoutExtmapAllowMixed) {
2764   jdesc_.description()->set_extmap_allow_mixed(false);
2765   std::string sdp_without_extmap_allow_mixed = kSdpFullString;
2766   // Deserialize
2767   JsepSessionDescription jdesc_deserialized(kDummyType);
2768   EXPECT_TRUE(
2769       SdpDeserialize(sdp_without_extmap_allow_mixed, &jdesc_deserialized));
2770   // Verify
2771   EXPECT_TRUE(CompareSessionDescription(jdesc_, jdesc_deserialized));
2772 }
2773 
TEST_F(WebRtcSdpTest,DeserializeMediaContentDescriptionWithExtmapAllowMixed)2774 TEST_F(WebRtcSdpTest, DeserializeMediaContentDescriptionWithExtmapAllowMixed) {
2775   cricket::MediaContentDescription* video_desc =
2776       jdesc_.description()->GetContentDescriptionByName(kVideoContentName);
2777   ASSERT_TRUE(video_desc);
2778   cricket::MediaContentDescription* audio_desc =
2779       jdesc_.description()->GetContentDescriptionByName(kAudioContentName);
2780   ASSERT_TRUE(audio_desc);
2781   video_desc->set_extmap_allow_mixed_enum(
2782       cricket::MediaContentDescription::kMedia);
2783   audio_desc->set_extmap_allow_mixed_enum(
2784       cricket::MediaContentDescription::kMedia);
2785 
2786   std::string sdp_with_extmap_allow_mixed = kSdpFullString;
2787   InjectAfter("a=mid:audio_content_name\r\n", kExtmapAllowMixed,
2788               &sdp_with_extmap_allow_mixed);
2789   InjectAfter("a=mid:video_content_name\r\n", kExtmapAllowMixed,
2790               &sdp_with_extmap_allow_mixed);
2791 
2792   // Deserialize
2793   JsepSessionDescription jdesc_deserialized(kDummyType);
2794   EXPECT_TRUE(SdpDeserialize(sdp_with_extmap_allow_mixed, &jdesc_deserialized));
2795   // Verify
2796   EXPECT_TRUE(CompareSessionDescription(jdesc_, jdesc_deserialized));
2797 }
2798 
TEST_F(WebRtcSdpTest,DeserializeCandidate)2799 TEST_F(WebRtcSdpTest, DeserializeCandidate) {
2800   JsepIceCandidate jcandidate(kDummyMid, kDummyIndex);
2801 
2802   std::string sdp = kSdpOneCandidate;
2803   EXPECT_TRUE(SdpDeserializeCandidate(sdp, &jcandidate));
2804   EXPECT_EQ(kDummyMid, jcandidate.sdp_mid());
2805   EXPECT_EQ(kDummyIndex, jcandidate.sdp_mline_index());
2806   EXPECT_TRUE(jcandidate.candidate().IsEquivalent(jcandidate_->candidate()));
2807   EXPECT_EQ(0, jcandidate.candidate().network_cost());
2808 
2809   // Candidate line without generation extension.
2810   sdp = kSdpOneCandidate;
2811   Replace(" generation 2", "", &sdp);
2812   EXPECT_TRUE(SdpDeserializeCandidate(sdp, &jcandidate));
2813   EXPECT_EQ(kDummyMid, jcandidate.sdp_mid());
2814   EXPECT_EQ(kDummyIndex, jcandidate.sdp_mline_index());
2815   Candidate expected = jcandidate_->candidate();
2816   expected.set_generation(0);
2817   EXPECT_TRUE(jcandidate.candidate().IsEquivalent(expected));
2818 
2819   // Candidate with network id and/or cost.
2820   sdp = kSdpOneCandidate;
2821   Replace(" generation 2", " generation 2 network-id 2", &sdp);
2822   EXPECT_TRUE(SdpDeserializeCandidate(sdp, &jcandidate));
2823   EXPECT_EQ(kDummyMid, jcandidate.sdp_mid());
2824   EXPECT_EQ(kDummyIndex, jcandidate.sdp_mline_index());
2825   expected = jcandidate_->candidate();
2826   expected.set_network_id(2);
2827   EXPECT_TRUE(jcandidate.candidate().IsEquivalent(expected));
2828   EXPECT_EQ(0, jcandidate.candidate().network_cost());
2829   // Add network cost
2830   Replace(" network-id 2", " network-id 2 network-cost 9", &sdp);
2831   EXPECT_TRUE(SdpDeserializeCandidate(sdp, &jcandidate));
2832   EXPECT_TRUE(jcandidate.candidate().IsEquivalent(expected));
2833   EXPECT_EQ(9, jcandidate.candidate().network_cost());
2834 
2835   sdp = kSdpTcpActiveCandidate;
2836   EXPECT_TRUE(SdpDeserializeCandidate(sdp, &jcandidate));
2837   // Make a cricket::Candidate equivalent to kSdpTcpCandidate string.
2838   Candidate candidate(ICE_CANDIDATE_COMPONENT_RTP, "tcp",
2839                       rtc::SocketAddress("192.168.1.5", 9), kCandidatePriority,
2840                       "", "", LOCAL_PORT_TYPE, kCandidateGeneration,
2841                       kCandidateFoundation1);
2842   std::unique_ptr<IceCandidateInterface> jcandidate_template(
2843       new JsepIceCandidate(std::string("audio_content_name"), 0, candidate));
2844   EXPECT_TRUE(
2845       jcandidate.candidate().IsEquivalent(jcandidate_template->candidate()));
2846   sdp = kSdpTcpPassiveCandidate;
2847   EXPECT_TRUE(SdpDeserializeCandidate(sdp, &jcandidate));
2848   sdp = kSdpTcpSOCandidate;
2849   EXPECT_TRUE(SdpDeserializeCandidate(sdp, &jcandidate));
2850 }
2851 
2852 // This test verifies the deserialization of candidate-attribute
2853 // as per RFC 5245. Candiate-attribute will be of the format
2854 // candidate:<blah>. This format will be used when candidates
2855 // are trickled.
TEST_F(WebRtcSdpTest,DeserializeRawCandidateAttribute)2856 TEST_F(WebRtcSdpTest, DeserializeRawCandidateAttribute) {
2857   JsepIceCandidate jcandidate(kDummyMid, kDummyIndex);
2858 
2859   std::string candidate_attribute = kRawCandidate;
2860   EXPECT_TRUE(SdpDeserializeCandidate(candidate_attribute, &jcandidate));
2861   EXPECT_EQ(kDummyMid, jcandidate.sdp_mid());
2862   EXPECT_EQ(kDummyIndex, jcandidate.sdp_mline_index());
2863   EXPECT_TRUE(jcandidate.candidate().IsEquivalent(jcandidate_->candidate()));
2864   EXPECT_EQ(2u, jcandidate.candidate().generation());
2865 
2866   // Candidate line without generation extension.
2867   candidate_attribute = kRawCandidate;
2868   Replace(" generation 2", "", &candidate_attribute);
2869   EXPECT_TRUE(SdpDeserializeCandidate(candidate_attribute, &jcandidate));
2870   EXPECT_EQ(kDummyMid, jcandidate.sdp_mid());
2871   EXPECT_EQ(kDummyIndex, jcandidate.sdp_mline_index());
2872   Candidate expected = jcandidate_->candidate();
2873   expected.set_generation(0);
2874   EXPECT_TRUE(jcandidate.candidate().IsEquivalent(expected));
2875 
2876   // Candidate line without candidate:
2877   candidate_attribute = kRawCandidate;
2878   Replace("candidate:", "", &candidate_attribute);
2879   EXPECT_FALSE(SdpDeserializeCandidate(candidate_attribute, &jcandidate));
2880 
2881   // Candidate line with IPV6 address.
2882   EXPECT_TRUE(SdpDeserializeCandidate(kRawIPV6Candidate, &jcandidate));
2883 
2884   // Candidate line with hostname address.
2885   EXPECT_TRUE(SdpDeserializeCandidate(kRawHostnameCandidate, &jcandidate));
2886 }
2887 
2888 // This test verifies that the deserialization of an invalid candidate string
2889 // fails.
TEST_F(WebRtcSdpTest,DeserializeInvalidCandidiate)2890 TEST_F(WebRtcSdpTest, DeserializeInvalidCandidiate) {
2891   JsepIceCandidate jcandidate(kDummyMid, kDummyIndex);
2892 
2893   std::string candidate_attribute = kRawCandidate;
2894   candidate_attribute.replace(0, 1, "x");
2895   EXPECT_FALSE(SdpDeserializeCandidate(candidate_attribute, &jcandidate));
2896 
2897   candidate_attribute = kSdpOneCandidate;
2898   candidate_attribute.replace(0, 1, "x");
2899   EXPECT_FALSE(SdpDeserializeCandidate(candidate_attribute, &jcandidate));
2900 
2901   candidate_attribute = kRawCandidate;
2902   candidate_attribute.append("\r\n");
2903   candidate_attribute.append(kRawCandidate);
2904   EXPECT_FALSE(SdpDeserializeCandidate(candidate_attribute, &jcandidate));
2905 
2906   EXPECT_FALSE(SdpDeserializeCandidate(kSdpTcpInvalidCandidate, &jcandidate));
2907 }
2908 
TEST_F(WebRtcSdpTest,DeserializeSdpWithRtpDataChannels)2909 TEST_F(WebRtcSdpTest, DeserializeSdpWithRtpDataChannels) {
2910   AddRtpDataChannel();
2911   JsepSessionDescription jdesc(kDummyType);
2912   ASSERT_TRUE(jdesc.Initialize(desc_.Clone(), kSessionId, kSessionVersion));
2913 
2914   std::string sdp_with_data = kSdpString;
2915   sdp_with_data.append(kSdpRtpDataChannelString);
2916   JsepSessionDescription jdesc_output(kDummyType);
2917 
2918   // Deserialize
2919   EXPECT_TRUE(SdpDeserialize(sdp_with_data, &jdesc_output));
2920   // Verify
2921   EXPECT_TRUE(CompareSessionDescription(jdesc, jdesc_output));
2922 }
2923 
TEST_F(WebRtcSdpTest,DeserializeSdpWithSctpDataChannels)2924 TEST_F(WebRtcSdpTest, DeserializeSdpWithSctpDataChannels) {
2925   bool use_sctpmap = true;
2926   AddSctpDataChannel(use_sctpmap);
2927   JsepSessionDescription jdesc(kDummyType);
2928   ASSERT_TRUE(jdesc.Initialize(desc_.Clone(), kSessionId, kSessionVersion));
2929 
2930   std::string sdp_with_data = kSdpString;
2931   sdp_with_data.append(kSdpSctpDataChannelString);
2932   JsepSessionDescription jdesc_output(kDummyType);
2933 
2934   // Verify with UDP/DTLS/SCTP (already in kSdpSctpDataChannelString).
2935   EXPECT_TRUE(SdpDeserialize(sdp_with_data, &jdesc_output));
2936   EXPECT_TRUE(CompareSessionDescription(jdesc, jdesc_output));
2937 
2938   // Verify with DTLS/SCTP.
2939   sdp_with_data.replace(sdp_with_data.find(kUdpDtlsSctp), strlen(kUdpDtlsSctp),
2940                         kDtlsSctp);
2941   EXPECT_TRUE(SdpDeserialize(sdp_with_data, &jdesc_output));
2942   EXPECT_TRUE(CompareSessionDescription(jdesc, jdesc_output));
2943 
2944   // Verify with TCP/DTLS/SCTP.
2945   sdp_with_data.replace(sdp_with_data.find(kDtlsSctp), strlen(kDtlsSctp),
2946                         kTcpDtlsSctp);
2947   EXPECT_TRUE(SdpDeserialize(sdp_with_data, &jdesc_output));
2948   EXPECT_TRUE(CompareSessionDescription(jdesc, jdesc_output));
2949 }
2950 
TEST_F(WebRtcSdpTest,DeserializeSdpWithSctpDataChannelsWithSctpPort)2951 TEST_F(WebRtcSdpTest, DeserializeSdpWithSctpDataChannelsWithSctpPort) {
2952   bool use_sctpmap = false;
2953   AddSctpDataChannel(use_sctpmap);
2954   JsepSessionDescription jdesc(kDummyType);
2955   ASSERT_TRUE(jdesc.Initialize(desc_.Clone(), kSessionId, kSessionVersion));
2956 
2957   std::string sdp_with_data = kSdpString;
2958   sdp_with_data.append(kSdpSctpDataChannelStringWithSctpPort);
2959   JsepSessionDescription jdesc_output(kDummyType);
2960 
2961   EXPECT_TRUE(SdpDeserialize(sdp_with_data, &jdesc_output));
2962   EXPECT_TRUE(CompareSessionDescription(jdesc, jdesc_output));
2963 }
2964 
TEST_F(WebRtcSdpTest,DeserializeSdpWithSctpDataChannelsWithSctpColonPort)2965 TEST_F(WebRtcSdpTest, DeserializeSdpWithSctpDataChannelsWithSctpColonPort) {
2966   bool use_sctpmap = false;
2967   AddSctpDataChannel(use_sctpmap);
2968   JsepSessionDescription jdesc(kDummyType);
2969   ASSERT_TRUE(jdesc.Initialize(desc_.Clone(), kSessionId, kSessionVersion));
2970 
2971   std::string sdp_with_data = kSdpString;
2972   sdp_with_data.append(kSdpSctpDataChannelStringWithSctpColonPort);
2973   JsepSessionDescription jdesc_output(kDummyType);
2974 
2975   EXPECT_TRUE(SdpDeserialize(sdp_with_data, &jdesc_output));
2976   EXPECT_TRUE(CompareSessionDescription(jdesc, jdesc_output));
2977 }
2978 
TEST_F(WebRtcSdpTest,DeserializeSdpWithSctpDataChannelsButWrongMediaType)2979 TEST_F(WebRtcSdpTest, DeserializeSdpWithSctpDataChannelsButWrongMediaType) {
2980   bool use_sctpmap = true;
2981   AddSctpDataChannel(use_sctpmap);
2982   JsepSessionDescription jdesc(kDummyType);
2983   ASSERT_TRUE(jdesc.Initialize(desc_.Clone(), kSessionId, kSessionVersion));
2984 
2985   std::string sdp = kSdpSessionString;
2986   sdp += kSdpSctpDataChannelString;
2987 
2988   const char needle[] = "m=application ";
2989   sdp.replace(sdp.find(needle), strlen(needle), "m=application:bogus ");
2990 
2991   JsepSessionDescription jdesc_output(kDummyType);
2992   EXPECT_TRUE(SdpDeserialize(sdp, &jdesc_output));
2993 
2994   EXPECT_EQ(1u, jdesc_output.description()->contents().size());
2995   EXPECT_TRUE(jdesc_output.description()->contents()[0].rejected);
2996 }
2997 
2998 // Helper function to set the max-message-size parameter in the
2999 // SCTP data codec.
MutateJsepSctpMaxMessageSize(const SessionDescription & desc,int new_value,JsepSessionDescription * jdesc)3000 void MutateJsepSctpMaxMessageSize(const SessionDescription& desc,
3001                                   int new_value,
3002                                   JsepSessionDescription* jdesc) {
3003   std::unique_ptr<cricket::SessionDescription> mutant = desc.Clone();
3004   SctpDataContentDescription* dcdesc =
3005       mutant->GetContentDescriptionByName(kDataContentName)->as_sctp();
3006   dcdesc->set_max_message_size(new_value);
3007   jdesc->Initialize(std::move(mutant), kSessionId, kSessionVersion);
3008 }
3009 
TEST_F(WebRtcSdpTest,DeserializeSdpWithSctpDataChannelsWithMaxMessageSize)3010 TEST_F(WebRtcSdpTest, DeserializeSdpWithSctpDataChannelsWithMaxMessageSize) {
3011   bool use_sctpmap = false;
3012   AddSctpDataChannel(use_sctpmap);
3013   JsepSessionDescription jdesc(kDummyType);
3014   std::string sdp_with_data = kSdpString;
3015 
3016   sdp_with_data.append(kSdpSctpDataChannelStringWithSctpColonPort);
3017   sdp_with_data.append("a=max-message-size:12345\r\n");
3018   MutateJsepSctpMaxMessageSize(desc_, 12345, &jdesc);
3019   JsepSessionDescription jdesc_output(kDummyType);
3020 
3021   EXPECT_TRUE(SdpDeserialize(sdp_with_data, &jdesc_output));
3022   EXPECT_TRUE(CompareSessionDescription(jdesc, jdesc_output));
3023 }
3024 
TEST_F(WebRtcSdpTest,SerializeSdpWithSctpDataChannelWithMaxMessageSize)3025 TEST_F(WebRtcSdpTest, SerializeSdpWithSctpDataChannelWithMaxMessageSize) {
3026   bool use_sctpmap = false;
3027   AddSctpDataChannel(use_sctpmap);
3028   JsepSessionDescription jdesc(kDummyType);
3029   MutateJsepSctpMaxMessageSize(desc_, 12345, &jdesc);
3030   std::string message = webrtc::SdpSerialize(jdesc);
3031   EXPECT_NE(std::string::npos,
3032             message.find("\r\na=max-message-size:12345\r\n"));
3033   JsepSessionDescription jdesc_output(kDummyType);
3034   EXPECT_TRUE(SdpDeserialize(message, &jdesc_output));
3035   EXPECT_TRUE(CompareSessionDescription(jdesc, jdesc_output));
3036 }
3037 
TEST_F(WebRtcSdpTest,SerializeSdpWithSctpDataChannelWithDefaultMaxMessageSize)3038 TEST_F(WebRtcSdpTest,
3039        SerializeSdpWithSctpDataChannelWithDefaultMaxMessageSize) {
3040   // https://tools.ietf.org/html/draft-ietf-mmusic-sctp-sdp-26#section-6
3041   // The default max message size is 64K.
3042   bool use_sctpmap = false;
3043   AddSctpDataChannel(use_sctpmap);
3044   JsepSessionDescription jdesc(kDummyType);
3045   MutateJsepSctpMaxMessageSize(desc_, 65536, &jdesc);
3046   std::string message = webrtc::SdpSerialize(jdesc);
3047   EXPECT_EQ(std::string::npos, message.find("\r\na=max-message-size:"));
3048   JsepSessionDescription jdesc_output(kDummyType);
3049   EXPECT_TRUE(SdpDeserialize(message, &jdesc_output));
3050   EXPECT_TRUE(CompareSessionDescription(jdesc, jdesc_output));
3051 }
3052 
3053 // Test to check the behaviour if sctp-port is specified
3054 // on the m= line and in a=sctp-port.
TEST_F(WebRtcSdpTest,DeserializeSdpWithMultiSctpPort)3055 TEST_F(WebRtcSdpTest, DeserializeSdpWithMultiSctpPort) {
3056   bool use_sctpmap = true;
3057   AddSctpDataChannel(use_sctpmap);
3058   JsepSessionDescription jdesc(kDummyType);
3059   ASSERT_TRUE(jdesc.Initialize(desc_.Clone(), kSessionId, kSessionVersion));
3060 
3061   std::string sdp_with_data = kSdpString;
3062   // Append m= attributes
3063   sdp_with_data.append(kSdpSctpDataChannelString);
3064   // Append a=sctp-port attribute
3065   sdp_with_data.append("a=sctp-port 5000\r\n");
3066   JsepSessionDescription jdesc_output(kDummyType);
3067 
3068   EXPECT_FALSE(SdpDeserialize(sdp_with_data, &jdesc_output));
3069 }
3070 
3071 // Test behavior if a=rtpmap occurs in an SCTP section.
TEST_F(WebRtcSdpTest,DeserializeSdpWithRtpmapAttribute)3072 TEST_F(WebRtcSdpTest, DeserializeSdpWithRtpmapAttribute) {
3073   std::string sdp_with_data = kSdpString;
3074   // Append m= attributes
3075   sdp_with_data.append(kSdpSctpDataChannelString);
3076   // Append a=rtpmap attribute
3077   sdp_with_data.append("a=rtpmap:111 opus/48000/2\r\n");
3078   JsepSessionDescription jdesc_output(kDummyType);
3079   // Correct behavior is to ignore the extra attribute.
3080   EXPECT_TRUE(SdpDeserialize(sdp_with_data, &jdesc_output));
3081 }
3082 
TEST_F(WebRtcSdpTest,DeserializeSdpWithStrangeApplicationProtocolNames)3083 TEST_F(WebRtcSdpTest, DeserializeSdpWithStrangeApplicationProtocolNames) {
3084   static const char* bad_strings[] = {"DTLS/SCTPRTP/", "obviously-bogus",
3085                                       "UDP/TL/RTSP/SAVPF", "UDP/TL/RTSP/S"};
3086   for (auto proto : bad_strings) {
3087     std::string sdp_with_data = kSdpString;
3088     sdp_with_data.append("m=application 9 ");
3089     sdp_with_data.append(proto);
3090     sdp_with_data.append(" 47\r\n");
3091     JsepSessionDescription jdesc_output(kDummyType);
3092     EXPECT_FALSE(SdpDeserialize(sdp_with_data, &jdesc_output))
3093         << "Parsing should have failed on " << proto;
3094   }
3095   // The following strings are strange, but acceptable as RTP.
3096   static const char* weird_strings[] = {"DTLS/SCTP/RTP/FOO",
3097                                         "obviously-bogus/RTP/"};
3098   for (auto proto : weird_strings) {
3099     std::string sdp_with_data = kSdpString;
3100     sdp_with_data.append("m=application 9 ");
3101     sdp_with_data.append(proto);
3102     sdp_with_data.append(" 47\r\n");
3103     JsepSessionDescription jdesc_output(kDummyType);
3104     EXPECT_TRUE(SdpDeserialize(sdp_with_data, &jdesc_output))
3105         << "Parsing should have succeeded on " << proto;
3106   }
3107 }
3108 
3109 // For crbug/344475.
TEST_F(WebRtcSdpTest,DeserializeSdpWithCorruptedSctpDataChannels)3110 TEST_F(WebRtcSdpTest, DeserializeSdpWithCorruptedSctpDataChannels) {
3111   std::string sdp_with_data = kSdpString;
3112   sdp_with_data.append(kSdpSctpDataChannelString);
3113   // Remove the "\n" at the end.
3114   sdp_with_data = sdp_with_data.substr(0, sdp_with_data.size() - 1);
3115   JsepSessionDescription jdesc_output(kDummyType);
3116 
3117   EXPECT_FALSE(SdpDeserialize(sdp_with_data, &jdesc_output));
3118   // No crash is a pass.
3119 }
3120 
TEST_F(WebRtcSdpTest,DeserializeSdpWithSctpDataChannelAndUnusualPort)3121 TEST_F(WebRtcSdpTest, DeserializeSdpWithSctpDataChannelAndUnusualPort) {
3122   bool use_sctpmap = true;
3123   AddSctpDataChannel(use_sctpmap);
3124 
3125   // First setup the expected JsepSessionDescription.
3126   JsepSessionDescription jdesc(kDummyType);
3127   MutateJsepSctpPort(&jdesc, desc_, kUnusualSctpPort);
3128 
3129   // Then get the deserialized JsepSessionDescription.
3130   std::string sdp_with_data = kSdpString;
3131   sdp_with_data.append(kSdpSctpDataChannelString);
3132   absl::StrReplaceAll(
3133       {{rtc::ToString(kDefaultSctpPort), rtc::ToString(kUnusualSctpPort)}},
3134       &sdp_with_data);
3135   JsepSessionDescription jdesc_output(kDummyType);
3136 
3137   EXPECT_TRUE(SdpDeserialize(sdp_with_data, &jdesc_output));
3138   EXPECT_TRUE(CompareSessionDescription(jdesc, jdesc_output));
3139 }
3140 
TEST_F(WebRtcSdpTest,DeserializeSdpWithSctpDataChannelAndUnusualPortInAttribute)3141 TEST_F(WebRtcSdpTest,
3142        DeserializeSdpWithSctpDataChannelAndUnusualPortInAttribute) {
3143   bool use_sctpmap = false;
3144   AddSctpDataChannel(use_sctpmap);
3145 
3146   JsepSessionDescription jdesc(kDummyType);
3147   MutateJsepSctpPort(&jdesc, desc_, kUnusualSctpPort);
3148 
3149   // We need to test the deserialized JsepSessionDescription from
3150   // kSdpSctpDataChannelStringWithSctpPort for
3151   // draft-ietf-mmusic-sctp-sdp-07
3152   // a=sctp-port
3153   std::string sdp_with_data = kSdpString;
3154   sdp_with_data.append(kSdpSctpDataChannelStringWithSctpPort);
3155   absl::StrReplaceAll(
3156       {{rtc::ToString(kDefaultSctpPort), rtc::ToString(kUnusualSctpPort)}},
3157       &sdp_with_data);
3158   JsepSessionDescription jdesc_output(kDummyType);
3159 
3160   EXPECT_TRUE(SdpDeserialize(sdp_with_data, &jdesc_output));
3161   EXPECT_TRUE(CompareSessionDescription(jdesc, jdesc_output));
3162 }
3163 
TEST_F(WebRtcSdpTest,DeserializeSdpWithRtpDataChannelsAndBandwidth)3164 TEST_F(WebRtcSdpTest, DeserializeSdpWithRtpDataChannelsAndBandwidth) {
3165   // We want to test that deserializing data content limits bandwidth
3166   // settings (it should never be greater than the default).
3167   // This should prevent someone from using unlimited data bandwidth through
3168   // JS and "breaking the Internet".
3169   // See: https://code.google.com/p/chromium/issues/detail?id=280726
3170   std::string sdp_with_bandwidth = kSdpString;
3171   sdp_with_bandwidth.append(kSdpRtpDataChannelString);
3172   InjectAfter("a=mid:data_content_name\r\n", "b=AS:100\r\n",
3173               &sdp_with_bandwidth);
3174   JsepSessionDescription jdesc_with_bandwidth(kDummyType);
3175 
3176   EXPECT_FALSE(SdpDeserialize(sdp_with_bandwidth, &jdesc_with_bandwidth));
3177 }
3178 
TEST_F(WebRtcSdpTest,DeserializeSdpWithSctpDataChannelsAndBandwidth)3179 TEST_F(WebRtcSdpTest, DeserializeSdpWithSctpDataChannelsAndBandwidth) {
3180   bool use_sctpmap = true;
3181   AddSctpDataChannel(use_sctpmap);
3182   JsepSessionDescription jdesc(kDummyType);
3183   SctpDataContentDescription* dcd = GetFirstSctpDataContentDescription(&desc_);
3184   dcd->set_bandwidth(100 * 1000);
3185   ASSERT_TRUE(jdesc.Initialize(desc_.Clone(), kSessionId, kSessionVersion));
3186 
3187   std::string sdp_with_bandwidth = kSdpString;
3188   sdp_with_bandwidth.append(kSdpSctpDataChannelString);
3189   InjectAfter("a=mid:data_content_name\r\n", "b=AS:100\r\n",
3190               &sdp_with_bandwidth);
3191   JsepSessionDescription jdesc_with_bandwidth(kDummyType);
3192 
3193   // SCTP has congestion control, so we shouldn't limit the bandwidth
3194   // as we do for RTP.
3195   EXPECT_TRUE(SdpDeserialize(sdp_with_bandwidth, &jdesc_with_bandwidth));
3196   EXPECT_TRUE(CompareSessionDescription(jdesc, jdesc_with_bandwidth));
3197 }
3198 
3199 class WebRtcSdpExtmapTest : public WebRtcSdpTest,
3200                             public ::testing::WithParamInterface<bool> {};
3201 
TEST_P(WebRtcSdpExtmapTest,DeserializeSessionDescriptionWithSessionLevelExtmap)3202 TEST_P(WebRtcSdpExtmapTest,
3203        DeserializeSessionDescriptionWithSessionLevelExtmap) {
3204   bool encrypted = GetParam();
3205   TestDeserializeExtmap(true, false, encrypted);
3206 }
3207 
TEST_P(WebRtcSdpExtmapTest,DeserializeSessionDescriptionWithMediaLevelExtmap)3208 TEST_P(WebRtcSdpExtmapTest, DeserializeSessionDescriptionWithMediaLevelExtmap) {
3209   bool encrypted = GetParam();
3210   TestDeserializeExtmap(false, true, encrypted);
3211 }
3212 
TEST_P(WebRtcSdpExtmapTest,DeserializeSessionDescriptionWithInvalidExtmap)3213 TEST_P(WebRtcSdpExtmapTest, DeserializeSessionDescriptionWithInvalidExtmap) {
3214   bool encrypted = GetParam();
3215   TestDeserializeExtmap(true, true, encrypted);
3216 }
3217 
3218 INSTANTIATE_TEST_SUITE_P(Encrypted,
3219                          WebRtcSdpExtmapTest,
3220                          ::testing::Values(false, true));
3221 
TEST_F(WebRtcSdpTest,DeserializeSessionDescriptionWithoutEndLineBreak)3222 TEST_F(WebRtcSdpTest, DeserializeSessionDescriptionWithoutEndLineBreak) {
3223   JsepSessionDescription jdesc(kDummyType);
3224   std::string sdp = kSdpFullString;
3225   sdp = sdp.substr(0, sdp.size() - 2);  // Remove \r\n at the end.
3226   // Deserialize
3227   SdpParseError error;
3228   EXPECT_FALSE(webrtc::SdpDeserialize(sdp, &jdesc, &error));
3229   const std::string lastline = "a=ssrc:3 label:video_track_id_1";
3230   EXPECT_EQ(lastline, error.line);
3231   EXPECT_EQ("Invalid SDP line.", error.description);
3232 }
3233 
TEST_F(WebRtcSdpTest,DeserializeCandidateWithDifferentTransport)3234 TEST_F(WebRtcSdpTest, DeserializeCandidateWithDifferentTransport) {
3235   JsepIceCandidate jcandidate(kDummyMid, kDummyIndex);
3236   std::string new_sdp = kSdpOneCandidate;
3237   Replace("udp", "unsupported_transport", &new_sdp);
3238   EXPECT_FALSE(SdpDeserializeCandidate(new_sdp, &jcandidate));
3239   new_sdp = kSdpOneCandidate;
3240   Replace("udp", "uDP", &new_sdp);
3241   EXPECT_TRUE(SdpDeserializeCandidate(new_sdp, &jcandidate));
3242   EXPECT_EQ(kDummyMid, jcandidate.sdp_mid());
3243   EXPECT_EQ(kDummyIndex, jcandidate.sdp_mline_index());
3244   EXPECT_TRUE(jcandidate.candidate().IsEquivalent(jcandidate_->candidate()));
3245 }
3246 
TEST_F(WebRtcSdpTest,DeserializeCandidateWithUfragPwd)3247 TEST_F(WebRtcSdpTest, DeserializeCandidateWithUfragPwd) {
3248   JsepIceCandidate jcandidate(kDummyMid, kDummyIndex);
3249   EXPECT_TRUE(
3250       SdpDeserializeCandidate(kSdpOneCandidateWithUfragPwd, &jcandidate));
3251   EXPECT_EQ(kDummyMid, jcandidate.sdp_mid());
3252   EXPECT_EQ(kDummyIndex, jcandidate.sdp_mline_index());
3253   Candidate ref_candidate = jcandidate_->candidate();
3254   ref_candidate.set_username("user_rtp");
3255   ref_candidate.set_password("password_rtp");
3256   EXPECT_TRUE(jcandidate.candidate().IsEquivalent(ref_candidate));
3257 }
3258 
TEST_F(WebRtcSdpTest,DeserializeSdpWithConferenceFlag)3259 TEST_F(WebRtcSdpTest, DeserializeSdpWithConferenceFlag) {
3260   JsepSessionDescription jdesc(kDummyType);
3261 
3262   // Deserialize
3263   EXPECT_TRUE(SdpDeserialize(kSdpConferenceString, &jdesc));
3264 
3265   // Verify
3266   cricket::AudioContentDescription* audio =
3267       cricket::GetFirstAudioContentDescription(jdesc.description());
3268   EXPECT_TRUE(audio->conference_mode());
3269 
3270   cricket::VideoContentDescription* video =
3271       cricket::GetFirstVideoContentDescription(jdesc.description());
3272   EXPECT_TRUE(video->conference_mode());
3273 }
3274 
TEST_F(WebRtcSdpTest,SerializeSdpWithConferenceFlag)3275 TEST_F(WebRtcSdpTest, SerializeSdpWithConferenceFlag) {
3276   JsepSessionDescription jdesc(kDummyType);
3277 
3278   // We tested deserialization already above, so just test that if we serialize
3279   // and deserialize the flag doesn't disappear.
3280   EXPECT_TRUE(SdpDeserialize(kSdpConferenceString, &jdesc));
3281   std::string reserialized = webrtc::SdpSerialize(jdesc);
3282   EXPECT_TRUE(SdpDeserialize(reserialized, &jdesc));
3283 
3284   // Verify.
3285   cricket::AudioContentDescription* audio =
3286       cricket::GetFirstAudioContentDescription(jdesc.description());
3287   EXPECT_TRUE(audio->conference_mode());
3288 
3289   cricket::VideoContentDescription* video =
3290       cricket::GetFirstVideoContentDescription(jdesc.description());
3291   EXPECT_TRUE(video->conference_mode());
3292 }
3293 
TEST_F(WebRtcSdpTest,SerializeAndDeserializeRemoteNetEstimate)3294 TEST_F(WebRtcSdpTest, SerializeAndDeserializeRemoteNetEstimate) {
3295   {
3296     // By default remote estimates are disabled.
3297     JsepSessionDescription dst(kDummyType);
3298     SdpDeserialize(webrtc::SdpSerialize(jdesc_), &dst);
3299     EXPECT_FALSE(cricket::GetFirstVideoContentDescription(dst.description())
3300                      ->remote_estimate());
3301   }
3302   {
3303     // When remote estimate is enabled, the setting is propagated via SDP.
3304     cricket::GetFirstVideoContentDescription(jdesc_.description())
3305         ->set_remote_estimate(true);
3306     JsepSessionDescription dst(kDummyType);
3307     SdpDeserialize(webrtc::SdpSerialize(jdesc_), &dst);
3308     EXPECT_TRUE(cricket::GetFirstVideoContentDescription(dst.description())
3309                     ->remote_estimate());
3310   }
3311 }
3312 
TEST_F(WebRtcSdpTest,DeserializeBrokenSdp)3313 TEST_F(WebRtcSdpTest, DeserializeBrokenSdp) {
3314   const char kSdpDestroyer[] = "!@#$%^&";
3315   const char kSdpEmptyType[] = " =candidate";
3316   const char kSdpEqualAsPlus[] = "a+candidate";
3317   const char kSdpSpaceAfterEqual[] = "a= candidate";
3318   const char kSdpUpperType[] = "A=candidate";
3319   const char kSdpEmptyLine[] = "";
3320   const char kSdpMissingValue[] = "a=";
3321 
3322   const char kSdpBrokenFingerprint[] =
3323       "a=fingerprint:sha-1 "
3324       "4AAD:B9:B1:3F:82:18:3B:54:02:12:DF:3E:5D:49:6B:19:E5:7C:AB";
3325   const char kSdpExtraField[] =
3326       "a=fingerprint:sha-1 "
3327       "4A:AD:B9:B1:3F:82:18:3B:54:02:12:DF:3E:5D:49:6B:19:E5:7C:AB XXX";
3328   const char kSdpMissingSpace[] =
3329       "a=fingerprint:sha-1"
3330       "4A:AD:B9:B1:3F:82:18:3B:54:02:12:DF:3E:5D:49:6B:19:E5:7C:AB";
3331   // MD5 is not allowed in fingerprints.
3332   const char kSdpMd5[] =
3333       "a=fingerprint:md5 "
3334       "4A:AD:B9:B1:3F:82:18:3B:54:02:12:DF:3E:5D:49:6B";
3335 
3336   // Broken session description
3337   ExpectParseFailure("v=", kSdpDestroyer);
3338   ExpectParseFailure("o=", kSdpDestroyer);
3339   ExpectParseFailure("s=-", kSdpDestroyer);
3340   // Broken time description
3341   ExpectParseFailure("t=", kSdpDestroyer);
3342 
3343   // Broken media description
3344   ExpectParseFailure("m=audio", "c=IN IP4 74.125.224.39");
3345   ExpectParseFailure("m=video", kSdpDestroyer);
3346   ExpectParseFailure("m=", "c=IN IP4 74.125.224.39");
3347 
3348   // Invalid lines
3349   ExpectParseFailure("a=candidate", kSdpEmptyType);
3350   ExpectParseFailure("a=candidate", kSdpEqualAsPlus);
3351   ExpectParseFailure("a=candidate", kSdpSpaceAfterEqual);
3352   ExpectParseFailure("a=candidate", kSdpUpperType);
3353 
3354   // Bogus fingerprint replacing a=sendrev. We selected this attribute
3355   // because it's orthogonal to what we are replacing and hence
3356   // safe.
3357   ExpectParseFailure("a=sendrecv", kSdpBrokenFingerprint);
3358   ExpectParseFailure("a=sendrecv", kSdpExtraField);
3359   ExpectParseFailure("a=sendrecv", kSdpMissingSpace);
3360   ExpectParseFailure("a=sendrecv", kSdpMd5);
3361 
3362   // Empty Line
3363   ExpectParseFailure("a=rtcp:2347 IN IP4 74.125.127.126", kSdpEmptyLine);
3364   ExpectParseFailure("a=rtcp:2347 IN IP4 74.125.127.126", kSdpMissingValue);
3365 }
3366 
TEST_F(WebRtcSdpTest,DeserializeSdpWithInvalidAttributeValue)3367 TEST_F(WebRtcSdpTest, DeserializeSdpWithInvalidAttributeValue) {
3368   // ssrc
3369   ExpectParseFailure("a=ssrc:1", "a=ssrc:badvalue");
3370   ExpectParseFailure("a=ssrc-group:FEC 2 3", "a=ssrc-group:FEC badvalue 3");
3371   // crypto
3372   ExpectParseFailure("a=crypto:1 ", "a=crypto:badvalue ");
3373   // rtpmap
3374   ExpectParseFailure("a=rtpmap:111 ", "a=rtpmap:badvalue ");
3375   ExpectParseFailure("opus/48000/2", "opus/badvalue/2");
3376   ExpectParseFailure("opus/48000/2", "opus/48000/badvalue");
3377   // candidate
3378   ExpectParseFailure("1 udp 2130706432", "badvalue udp 2130706432");
3379   ExpectParseFailure("1 udp 2130706432", "1 udp badvalue");
3380   ExpectParseFailure("192.168.1.5 1234", "192.168.1.5 badvalue");
3381   ExpectParseFailure("rport 2346", "rport badvalue");
3382   ExpectParseFailure("rport 2346 generation 2",
3383                      "rport 2346 generation badvalue");
3384   // m line
3385   ExpectParseFailure("m=audio 2345 RTP/SAVPF 111 103 104",
3386                      "m=audio 2345 RTP/SAVPF 111 badvalue 104");
3387 
3388   // bandwidth
3389   ExpectParseFailureWithNewLines("a=mid:video_content_name\r\n",
3390                                  "b=AS:badvalue\r\n", "b=AS:badvalue");
3391   ExpectParseFailureWithNewLines("a=mid:video_content_name\r\n", "b=AS\r\n",
3392                                  "b=AS");
3393   ExpectParseFailureWithNewLines("a=mid:video_content_name\r\n", "b=AS:\r\n",
3394                                  "b=AS:");
3395   ExpectParseFailureWithNewLines("a=mid:video_content_name\r\n",
3396                                  "b=AS:12:34\r\n", "b=AS:12:34");
3397 
3398   // rtcp-fb
3399   ExpectParseFailureWithNewLines("a=mid:video_content_name\r\n",
3400                                  "a=rtcp-fb:badvalue nack\r\n",
3401                                  "a=rtcp-fb:badvalue nack");
3402   // extmap
3403   ExpectParseFailureWithNewLines("a=mid:video_content_name\r\n",
3404                                  "a=extmap:badvalue http://example.com\r\n",
3405                                  "a=extmap:badvalue http://example.com");
3406 }
3407 
TEST_F(WebRtcSdpTest,DeserializeSdpWithReorderedPltypes)3408 TEST_F(WebRtcSdpTest, DeserializeSdpWithReorderedPltypes) {
3409   JsepSessionDescription jdesc_output(kDummyType);
3410 
3411   const char kSdpWithReorderedPlTypesString[] =
3412       "v=0\r\n"
3413       "o=- 18446744069414584320 18446462598732840960 IN IP4 127.0.0.1\r\n"
3414       "s=-\r\n"
3415       "t=0 0\r\n"
3416       "m=audio 9 RTP/SAVPF 104 103\r\n"  // Pl type 104 preferred.
3417       "a=rtpmap:111 opus/48000/2\r\n"    // Pltype 111 listed before 103 and 104
3418                                          // in the map.
3419       "a=rtpmap:103 ISAC/16000\r\n"  // Pltype 103 listed before 104 in the map.
3420       "a=rtpmap:104 ISAC/32000\r\n";
3421 
3422   // Deserialize
3423   EXPECT_TRUE(SdpDeserialize(kSdpWithReorderedPlTypesString, &jdesc_output));
3424 
3425   const AudioContentDescription* acd =
3426       GetFirstAudioContentDescription(jdesc_output.description());
3427   ASSERT_TRUE(acd);
3428   ASSERT_FALSE(acd->codecs().empty());
3429   EXPECT_EQ("ISAC", acd->codecs()[0].name);
3430   EXPECT_EQ(32000, acd->codecs()[0].clockrate);
3431   EXPECT_EQ(104, acd->codecs()[0].id);
3432 }
3433 
TEST_F(WebRtcSdpTest,DeserializeSerializeCodecParams)3434 TEST_F(WebRtcSdpTest, DeserializeSerializeCodecParams) {
3435   JsepSessionDescription jdesc_output(kDummyType);
3436   CodecParams params;
3437   params.max_ptime = 40;
3438   params.ptime = 30;
3439   params.min_ptime = 10;
3440   params.sprop_stereo = 1;
3441   params.stereo = 1;
3442   params.useinband = 1;
3443   params.maxaveragebitrate = 128000;
3444   TestDeserializeCodecParams(params, &jdesc_output);
3445   TestSerialize(jdesc_output);
3446 }
3447 
TEST_F(WebRtcSdpTest,DeserializeSerializeRtcpFb)3448 TEST_F(WebRtcSdpTest, DeserializeSerializeRtcpFb) {
3449   const bool kUseWildcard = false;
3450   JsepSessionDescription jdesc_output(kDummyType);
3451   TestDeserializeRtcpFb(&jdesc_output, kUseWildcard);
3452   TestSerialize(jdesc_output);
3453 }
3454 
TEST_F(WebRtcSdpTest,DeserializeSerializeRtcpFbWildcard)3455 TEST_F(WebRtcSdpTest, DeserializeSerializeRtcpFbWildcard) {
3456   const bool kUseWildcard = true;
3457   JsepSessionDescription jdesc_output(kDummyType);
3458   TestDeserializeRtcpFb(&jdesc_output, kUseWildcard);
3459   TestSerialize(jdesc_output);
3460 }
3461 
TEST_F(WebRtcSdpTest,DeserializeVideoFmtp)3462 TEST_F(WebRtcSdpTest, DeserializeVideoFmtp) {
3463   JsepSessionDescription jdesc_output(kDummyType);
3464 
3465   const char kSdpWithFmtpString[] =
3466       "v=0\r\n"
3467       "o=- 18446744069414584320 18446462598732840960 IN IP4 127.0.0.1\r\n"
3468       "s=-\r\n"
3469       "t=0 0\r\n"
3470       "m=video 3457 RTP/SAVPF 120\r\n"
3471       "a=rtpmap:120 VP8/90000\r\n"
3472       "a=fmtp:120 x-google-min-bitrate=10;x-google-max-quantization=40\r\n";
3473 
3474   // Deserialize
3475   SdpParseError error;
3476   EXPECT_TRUE(
3477       webrtc::SdpDeserialize(kSdpWithFmtpString, &jdesc_output, &error));
3478 
3479   const VideoContentDescription* vcd =
3480       GetFirstVideoContentDescription(jdesc_output.description());
3481   ASSERT_TRUE(vcd);
3482   ASSERT_FALSE(vcd->codecs().empty());
3483   cricket::VideoCodec vp8 = vcd->codecs()[0];
3484   EXPECT_EQ("VP8", vp8.name);
3485   EXPECT_EQ(120, vp8.id);
3486   cricket::CodecParameterMap::iterator found =
3487       vp8.params.find("x-google-min-bitrate");
3488   ASSERT_TRUE(found != vp8.params.end());
3489   EXPECT_EQ(found->second, "10");
3490   found = vp8.params.find("x-google-max-quantization");
3491   ASSERT_TRUE(found != vp8.params.end());
3492   EXPECT_EQ(found->second, "40");
3493 }
3494 
TEST_F(WebRtcSdpTest,DeserializeVideoFmtpWithSprops)3495 TEST_F(WebRtcSdpTest, DeserializeVideoFmtpWithSprops) {
3496   JsepSessionDescription jdesc_output(kDummyType);
3497 
3498   const char kSdpWithFmtpString[] =
3499       "v=0\r\n"
3500       "o=- 18446744069414584320 18446462598732840960 IN IP4 127.0.0.1\r\n"
3501       "s=-\r\n"
3502       "t=0 0\r\n"
3503       "m=video 49170 RTP/AVP 98\r\n"
3504       "a=rtpmap:98 H264/90000\r\n"
3505       "a=fmtp:98 profile-level-id=42A01E; "
3506       "sprop-parameter-sets=Z0IACpZTBYmI,aMljiA==\r\n";
3507 
3508   // Deserialize.
3509   SdpParseError error;
3510   EXPECT_TRUE(
3511       webrtc::SdpDeserialize(kSdpWithFmtpString, &jdesc_output, &error));
3512 
3513   const VideoContentDescription* vcd =
3514       GetFirstVideoContentDescription(jdesc_output.description());
3515   ASSERT_TRUE(vcd);
3516   ASSERT_FALSE(vcd->codecs().empty());
3517   cricket::VideoCodec h264 = vcd->codecs()[0];
3518   EXPECT_EQ("H264", h264.name);
3519   EXPECT_EQ(98, h264.id);
3520   cricket::CodecParameterMap::const_iterator found =
3521       h264.params.find("profile-level-id");
3522   ASSERT_TRUE(found != h264.params.end());
3523   EXPECT_EQ(found->second, "42A01E");
3524   found = h264.params.find("sprop-parameter-sets");
3525   ASSERT_TRUE(found != h264.params.end());
3526   EXPECT_EQ(found->second, "Z0IACpZTBYmI,aMljiA==");
3527 }
3528 
TEST_F(WebRtcSdpTest,DeserializeVideoFmtpWithSpace)3529 TEST_F(WebRtcSdpTest, DeserializeVideoFmtpWithSpace) {
3530   JsepSessionDescription jdesc_output(kDummyType);
3531 
3532   const char kSdpWithFmtpString[] =
3533       "v=0\r\n"
3534       "o=- 18446744069414584320 18446462598732840960 IN IP4 127.0.0.1\r\n"
3535       "s=-\r\n"
3536       "t=0 0\r\n"
3537       "m=video 3457 RTP/SAVPF 120\r\n"
3538       "a=rtpmap:120 VP8/90000\r\n"
3539       "a=fmtp:120   x-google-min-bitrate=10;  x-google-max-quantization=40\r\n";
3540 
3541   // Deserialize
3542   SdpParseError error;
3543   EXPECT_TRUE(
3544       webrtc::SdpDeserialize(kSdpWithFmtpString, &jdesc_output, &error));
3545 
3546   const VideoContentDescription* vcd =
3547       GetFirstVideoContentDescription(jdesc_output.description());
3548   ASSERT_TRUE(vcd);
3549   ASSERT_FALSE(vcd->codecs().empty());
3550   cricket::VideoCodec vp8 = vcd->codecs()[0];
3551   EXPECT_EQ("VP8", vp8.name);
3552   EXPECT_EQ(120, vp8.id);
3553   cricket::CodecParameterMap::iterator found =
3554       vp8.params.find("x-google-min-bitrate");
3555   ASSERT_TRUE(found != vp8.params.end());
3556   EXPECT_EQ(found->second, "10");
3557   found = vp8.params.find("x-google-max-quantization");
3558   ASSERT_TRUE(found != vp8.params.end());
3559   EXPECT_EQ(found->second, "40");
3560 }
3561 
TEST_F(WebRtcSdpTest,DeserializePacketizationAttributeWithIllegalValue)3562 TEST_F(WebRtcSdpTest, DeserializePacketizationAttributeWithIllegalValue) {
3563   JsepSessionDescription jdesc_output(kDummyType);
3564 
3565   const char kSdpWithPacketizationString[] =
3566       "v=0\r\n"
3567       "o=- 18446744069414584320 18446462598732840960 IN IP4 127.0.0.1\r\n"
3568       "s=-\r\n"
3569       "t=0 0\r\n"
3570       "m=audio 9 RTP/SAVPF 111\r\n"
3571       "a=rtpmap:111 opus/48000/2\r\n"
3572       "a=packetization:111 unknownpacketizationattributeforaudio\r\n"
3573       "m=video 3457 RTP/SAVPF 120 121 122\r\n"
3574       "a=rtpmap:120 VP8/90000\r\n"
3575       "a=packetization:120 raw\r\n"
3576       "a=rtpmap:121 VP9/90000\r\n"
3577       "a=rtpmap:122 H264/90000\r\n"
3578       "a=packetization:122 unknownpacketizationattributevalue\r\n";
3579 
3580   SdpParseError error;
3581   EXPECT_TRUE(webrtc::SdpDeserialize(kSdpWithPacketizationString, &jdesc_output,
3582                                      &error));
3583 
3584   AudioContentDescription* acd =
3585       GetFirstAudioContentDescription(jdesc_output.description());
3586   ASSERT_TRUE(acd);
3587   ASSERT_THAT(acd->codecs(), testing::SizeIs(1));
3588   cricket::AudioCodec opus = acd->codecs()[0];
3589   EXPECT_EQ(opus.name, "opus");
3590   EXPECT_EQ(opus.id, 111);
3591 
3592   const VideoContentDescription* vcd =
3593       GetFirstVideoContentDescription(jdesc_output.description());
3594   ASSERT_TRUE(vcd);
3595   ASSERT_THAT(vcd->codecs(), testing::SizeIs(3));
3596   cricket::VideoCodec vp8 = vcd->codecs()[0];
3597   EXPECT_EQ(vp8.name, "VP8");
3598   EXPECT_EQ(vp8.id, 120);
3599   EXPECT_EQ(vp8.packetization, "raw");
3600   cricket::VideoCodec vp9 = vcd->codecs()[1];
3601   EXPECT_EQ(vp9.name, "VP9");
3602   EXPECT_EQ(vp9.id, 121);
3603   EXPECT_EQ(vp9.packetization, absl::nullopt);
3604   cricket::VideoCodec h264 = vcd->codecs()[2];
3605   EXPECT_EQ(h264.name, "H264");
3606   EXPECT_EQ(h264.id, 122);
3607   EXPECT_EQ(h264.packetization, absl::nullopt);
3608 }
3609 
TEST_F(WebRtcSdpTest,SerializeAudioFmtpWithUnknownParameter)3610 TEST_F(WebRtcSdpTest, SerializeAudioFmtpWithUnknownParameter) {
3611   AudioContentDescription* acd = GetFirstAudioContentDescription(&desc_);
3612 
3613   cricket::AudioCodecs codecs = acd->codecs();
3614   codecs[0].params["unknown-future-parameter"] = "SomeFutureValue";
3615   acd->set_codecs(codecs);
3616 
3617   ASSERT_TRUE(jdesc_.Initialize(desc_.Clone(), jdesc_.session_id(),
3618                                 jdesc_.session_version()));
3619   std::string message = webrtc::SdpSerialize(jdesc_);
3620   std::string sdp_with_fmtp = kSdpFullString;
3621   InjectAfter("a=rtpmap:111 opus/48000/2\r\n",
3622               "a=fmtp:111 unknown-future-parameter=SomeFutureValue\r\n",
3623               &sdp_with_fmtp);
3624   EXPECT_EQ(sdp_with_fmtp, message);
3625 }
3626 
TEST_F(WebRtcSdpTest,SerializeAudioFmtpWithKnownFmtpParameter)3627 TEST_F(WebRtcSdpTest, SerializeAudioFmtpWithKnownFmtpParameter) {
3628   AudioContentDescription* acd = GetFirstAudioContentDescription(&desc_);
3629 
3630   cricket::AudioCodecs codecs = acd->codecs();
3631   codecs[0].params["stereo"] = "1";
3632   acd->set_codecs(codecs);
3633 
3634   ASSERT_TRUE(jdesc_.Initialize(desc_.Clone(), jdesc_.session_id(),
3635                                 jdesc_.session_version()));
3636   std::string message = webrtc::SdpSerialize(jdesc_);
3637   std::string sdp_with_fmtp = kSdpFullString;
3638   InjectAfter("a=rtpmap:111 opus/48000/2\r\n", "a=fmtp:111 stereo=1\r\n",
3639               &sdp_with_fmtp);
3640   EXPECT_EQ(sdp_with_fmtp, message);
3641 }
3642 
TEST_F(WebRtcSdpTest,SerializeAudioFmtpWithPTimeAndMaxPTime)3643 TEST_F(WebRtcSdpTest, SerializeAudioFmtpWithPTimeAndMaxPTime) {
3644   AudioContentDescription* acd = GetFirstAudioContentDescription(&desc_);
3645 
3646   cricket::AudioCodecs codecs = acd->codecs();
3647   codecs[0].params["ptime"] = "20";
3648   codecs[0].params["maxptime"] = "120";
3649   acd->set_codecs(codecs);
3650 
3651   ASSERT_TRUE(jdesc_.Initialize(desc_.Clone(), jdesc_.session_id(),
3652                                 jdesc_.session_version()));
3653   std::string message = webrtc::SdpSerialize(jdesc_);
3654   std::string sdp_with_fmtp = kSdpFullString;
3655   InjectAfter("a=rtpmap:104 ISAC/32000\r\n",
3656               "a=maxptime:120\r\n"  // No comma here. String merging!
3657               "a=ptime:20\r\n",
3658               &sdp_with_fmtp);
3659   EXPECT_EQ(sdp_with_fmtp, message);
3660 }
3661 
TEST_F(WebRtcSdpTest,SerializeAudioFmtpWithTelephoneEvent)3662 TEST_F(WebRtcSdpTest, SerializeAudioFmtpWithTelephoneEvent) {
3663   AudioContentDescription* acd = GetFirstAudioContentDescription(&desc_);
3664 
3665   cricket::AudioCodecs codecs = acd->codecs();
3666   cricket::AudioCodec dtmf(105, "telephone-event", 8000, 0, 1);
3667   dtmf.params[""] = "0-15";
3668   codecs.push_back(dtmf);
3669   acd->set_codecs(codecs);
3670 
3671   ASSERT_TRUE(jdesc_.Initialize(desc_.Clone(), jdesc_.session_id(),
3672                                 jdesc_.session_version()));
3673   std::string message = webrtc::SdpSerialize(jdesc_);
3674   std::string sdp_with_fmtp = kSdpFullString;
3675   InjectAfter("m=audio 2345 RTP/SAVPF 111 103 104", " 105", &sdp_with_fmtp);
3676   InjectAfter(
3677       "a=rtpmap:104 ISAC/32000\r\n",
3678       "a=rtpmap:105 telephone-event/8000\r\n"  // No comma here. String merging!
3679       "a=fmtp:105 0-15\r\n",
3680       &sdp_with_fmtp);
3681   EXPECT_EQ(sdp_with_fmtp, message);
3682 }
3683 
TEST_F(WebRtcSdpTest,SerializeVideoFmtp)3684 TEST_F(WebRtcSdpTest, SerializeVideoFmtp) {
3685   VideoContentDescription* vcd = GetFirstVideoContentDescription(&desc_);
3686 
3687   cricket::VideoCodecs codecs = vcd->codecs();
3688   codecs[0].params["x-google-min-bitrate"] = "10";
3689   vcd->set_codecs(codecs);
3690 
3691   ASSERT_TRUE(jdesc_.Initialize(desc_.Clone(), jdesc_.session_id(),
3692                                 jdesc_.session_version()));
3693   std::string message = webrtc::SdpSerialize(jdesc_);
3694   std::string sdp_with_fmtp = kSdpFullString;
3695   InjectAfter("a=rtpmap:120 VP8/90000\r\n",
3696               "a=fmtp:120 x-google-min-bitrate=10\r\n", &sdp_with_fmtp);
3697   EXPECT_EQ(sdp_with_fmtp, message);
3698 }
3699 
TEST_F(WebRtcSdpTest,SerializeVideoPacketizationAttribute)3700 TEST_F(WebRtcSdpTest, SerializeVideoPacketizationAttribute) {
3701   VideoContentDescription* vcd = GetFirstVideoContentDescription(&desc_);
3702 
3703   cricket::VideoCodecs codecs = vcd->codecs();
3704   codecs[0].packetization = "raw";
3705   vcd->set_codecs(codecs);
3706 
3707   ASSERT_TRUE(jdesc_.Initialize(desc_.Clone(), jdesc_.session_id(),
3708                                 jdesc_.session_version()));
3709   std::string message = webrtc::SdpSerialize(jdesc_);
3710   std::string sdp_with_packetization = kSdpFullString;
3711   InjectAfter("a=rtpmap:120 VP8/90000\r\n", "a=packetization:120 raw\r\n",
3712               &sdp_with_packetization);
3713   EXPECT_EQ(sdp_with_packetization, message);
3714 }
3715 
TEST_F(WebRtcSdpTest,DeserializeAndSerializeSdpWithIceLite)3716 TEST_F(WebRtcSdpTest, DeserializeAndSerializeSdpWithIceLite) {
3717   // Deserialize the baseline description, making sure it's ICE full.
3718   JsepSessionDescription jdesc_with_icelite(kDummyType);
3719   std::string sdp_with_icelite = kSdpFullString;
3720   EXPECT_TRUE(SdpDeserialize(sdp_with_icelite, &jdesc_with_icelite));
3721   cricket::SessionDescription* desc = jdesc_with_icelite.description();
3722   const cricket::TransportInfo* tinfo1 =
3723       desc->GetTransportInfoByName("audio_content_name");
3724   EXPECT_EQ(cricket::ICEMODE_FULL, tinfo1->description.ice_mode);
3725   const cricket::TransportInfo* tinfo2 =
3726       desc->GetTransportInfoByName("video_content_name");
3727   EXPECT_EQ(cricket::ICEMODE_FULL, tinfo2->description.ice_mode);
3728 
3729   // Add "a=ice-lite" and deserialize, making sure it's ICE lite.
3730   InjectAfter(kSessionTime, "a=ice-lite\r\n", &sdp_with_icelite);
3731   EXPECT_TRUE(SdpDeserialize(sdp_with_icelite, &jdesc_with_icelite));
3732   desc = jdesc_with_icelite.description();
3733   const cricket::TransportInfo* atinfo =
3734       desc->GetTransportInfoByName("audio_content_name");
3735   EXPECT_EQ(cricket::ICEMODE_LITE, atinfo->description.ice_mode);
3736   const cricket::TransportInfo* vtinfo =
3737       desc->GetTransportInfoByName("video_content_name");
3738   EXPECT_EQ(cricket::ICEMODE_LITE, vtinfo->description.ice_mode);
3739 
3740   // Now that we know deserialization works, we can use TestSerialize to test
3741   // serialization.
3742   TestSerialize(jdesc_with_icelite);
3743 }
3744 
3745 // Verifies that the candidates in the input SDP are parsed and serialized
3746 // correctly in the output SDP.
TEST_F(WebRtcSdpTest,RoundTripSdpWithSctpDataChannelsWithCandidates)3747 TEST_F(WebRtcSdpTest, RoundTripSdpWithSctpDataChannelsWithCandidates) {
3748   std::string sdp_with_data = kSdpString;
3749   sdp_with_data.append(kSdpSctpDataChannelWithCandidatesString);
3750   JsepSessionDescription jdesc_output(kDummyType);
3751 
3752   EXPECT_TRUE(SdpDeserialize(sdp_with_data, &jdesc_output));
3753   EXPECT_EQ(sdp_with_data, webrtc::SdpSerialize(jdesc_output));
3754 }
3755 
TEST_F(WebRtcSdpTest,SerializeDtlsSetupAttribute)3756 TEST_F(WebRtcSdpTest, SerializeDtlsSetupAttribute) {
3757   AddFingerprint();
3758   TransportInfo audio_transport_info =
3759       *(desc_.GetTransportInfoByName(kAudioContentName));
3760   EXPECT_EQ(cricket::CONNECTIONROLE_NONE,
3761             audio_transport_info.description.connection_role);
3762   audio_transport_info.description.connection_role =
3763       cricket::CONNECTIONROLE_ACTIVE;
3764 
3765   TransportInfo video_transport_info =
3766       *(desc_.GetTransportInfoByName(kVideoContentName));
3767   EXPECT_EQ(cricket::CONNECTIONROLE_NONE,
3768             video_transport_info.description.connection_role);
3769   video_transport_info.description.connection_role =
3770       cricket::CONNECTIONROLE_ACTIVE;
3771 
3772   desc_.RemoveTransportInfoByName(kAudioContentName);
3773   desc_.RemoveTransportInfoByName(kVideoContentName);
3774 
3775   desc_.AddTransportInfo(audio_transport_info);
3776   desc_.AddTransportInfo(video_transport_info);
3777 
3778   ASSERT_TRUE(jdesc_.Initialize(desc_.Clone(), jdesc_.session_id(),
3779                                 jdesc_.session_version()));
3780   std::string message = webrtc::SdpSerialize(jdesc_);
3781   std::string sdp_with_dtlssetup = kSdpFullString;
3782 
3783   // Fingerprint attribute is necessary to add DTLS setup attribute.
3784   InjectAfter(kAttributeIcePwdVoice, kFingerprint, &sdp_with_dtlssetup);
3785   InjectAfter(kAttributeIcePwdVideo, kFingerprint, &sdp_with_dtlssetup);
3786   // Now adding |setup| attribute.
3787   InjectAfter(kFingerprint, "a=setup:active\r\n", &sdp_with_dtlssetup);
3788   EXPECT_EQ(sdp_with_dtlssetup, message);
3789 }
3790 
TEST_F(WebRtcSdpTest,DeserializeDtlsSetupAttribute)3791 TEST_F(WebRtcSdpTest, DeserializeDtlsSetupAttribute) {
3792   JsepSessionDescription jdesc_with_dtlssetup(kDummyType);
3793   std::string sdp_with_dtlssetup = kSdpFullString;
3794   InjectAfter(kSessionTime, "a=setup:actpass\r\n", &sdp_with_dtlssetup);
3795   EXPECT_TRUE(SdpDeserialize(sdp_with_dtlssetup, &jdesc_with_dtlssetup));
3796   cricket::SessionDescription* desc = jdesc_with_dtlssetup.description();
3797   const cricket::TransportInfo* atinfo =
3798       desc->GetTransportInfoByName("audio_content_name");
3799   EXPECT_EQ(cricket::CONNECTIONROLE_ACTPASS,
3800             atinfo->description.connection_role);
3801   const cricket::TransportInfo* vtinfo =
3802       desc->GetTransportInfoByName("video_content_name");
3803   EXPECT_EQ(cricket::CONNECTIONROLE_ACTPASS,
3804             vtinfo->description.connection_role);
3805 }
3806 
3807 // Verifies that the order of the serialized m-lines follows the order of the
3808 // ContentInfo in SessionDescription, and vise versa for deserialization.
TEST_F(WebRtcSdpTest,MediaContentOrderMaintainedRoundTrip)3809 TEST_F(WebRtcSdpTest, MediaContentOrderMaintainedRoundTrip) {
3810   JsepSessionDescription jdesc(kDummyType);
3811   const std::string media_content_sdps[3] = {kSdpAudioString, kSdpVideoString,
3812                                              kSdpSctpDataChannelString};
3813   const cricket::MediaType media_types[3] = {cricket::MEDIA_TYPE_AUDIO,
3814                                              cricket::MEDIA_TYPE_VIDEO,
3815                                              cricket::MEDIA_TYPE_DATA};
3816 
3817   // Verifies all 6 permutations.
3818   for (size_t i = 0; i < 6; ++i) {
3819     size_t media_content_in_sdp[3];
3820     // The index of the first media content.
3821     media_content_in_sdp[0] = i / 2;
3822     // The index of the second media content.
3823     media_content_in_sdp[1] = (media_content_in_sdp[0] + i % 2 + 1) % 3;
3824     // The index of the third media content.
3825     media_content_in_sdp[2] = (media_content_in_sdp[0] + (i + 1) % 2 + 1) % 3;
3826 
3827     std::string sdp_string = kSdpSessionString;
3828     for (size_t i = 0; i < 3; ++i)
3829       sdp_string += media_content_sdps[media_content_in_sdp[i]];
3830 
3831     EXPECT_TRUE(SdpDeserialize(sdp_string, &jdesc));
3832     cricket::SessionDescription* desc = jdesc.description();
3833     EXPECT_EQ(3u, desc->contents().size());
3834 
3835     for (size_t i = 0; i < 3; ++i) {
3836       const cricket::MediaContentDescription* mdesc =
3837           desc->contents()[i].media_description();
3838       EXPECT_EQ(media_types[media_content_in_sdp[i]], mdesc->type());
3839     }
3840 
3841     std::string serialized_sdp = webrtc::SdpSerialize(jdesc);
3842     EXPECT_EQ(sdp_string, serialized_sdp);
3843   }
3844 }
3845 
TEST_F(WebRtcSdpTest,DeserializeBundleOnlyAttribute)3846 TEST_F(WebRtcSdpTest, DeserializeBundleOnlyAttribute) {
3847   MakeBundleOnlyDescription();
3848   JsepSessionDescription deserialized_description(kDummyType);
3849   ASSERT_TRUE(
3850       SdpDeserialize(kBundleOnlySdpFullString, &deserialized_description));
3851   EXPECT_TRUE(CompareSessionDescription(jdesc_, deserialized_description));
3852 }
3853 
3854 // The semantics of "a=bundle-only" are only defined when it's used in
3855 // combination with a 0 port on the m= line. We should ignore it if used with a
3856 // nonzero port.
TEST_F(WebRtcSdpTest,IgnoreBundleOnlyWithNonzeroPort)3857 TEST_F(WebRtcSdpTest, IgnoreBundleOnlyWithNonzeroPort) {
3858   // Make the base bundle-only description but unset the bundle-only flag.
3859   MakeBundleOnlyDescription();
3860   jdesc_.description()->contents()[1].bundle_only = false;
3861 
3862   std::string modified_sdp = kBundleOnlySdpFullString;
3863   Replace("m=video 0", "m=video 9", &modified_sdp);
3864   JsepSessionDescription deserialized_description(kDummyType);
3865   ASSERT_TRUE(SdpDeserialize(modified_sdp, &deserialized_description));
3866   EXPECT_TRUE(CompareSessionDescription(jdesc_, deserialized_description));
3867 }
3868 
TEST_F(WebRtcSdpTest,SerializeBundleOnlyAttribute)3869 TEST_F(WebRtcSdpTest, SerializeBundleOnlyAttribute) {
3870   MakeBundleOnlyDescription();
3871   TestSerialize(jdesc_);
3872 }
3873 
TEST_F(WebRtcSdpTest,DeserializePlanBSessionDescription)3874 TEST_F(WebRtcSdpTest, DeserializePlanBSessionDescription) {
3875   MakePlanBDescription();
3876 
3877   JsepSessionDescription deserialized_description(kDummyType);
3878   EXPECT_TRUE(SdpDeserialize(kPlanBSdpFullString, &deserialized_description));
3879 
3880   EXPECT_TRUE(CompareSessionDescription(jdesc_, deserialized_description));
3881 }
3882 
TEST_F(WebRtcSdpTest,SerializePlanBSessionDescription)3883 TEST_F(WebRtcSdpTest, SerializePlanBSessionDescription) {
3884   MakePlanBDescription();
3885   TestSerialize(jdesc_);
3886 }
3887 
TEST_F(WebRtcSdpTest,DeserializeUnifiedPlanSessionDescription)3888 TEST_F(WebRtcSdpTest, DeserializeUnifiedPlanSessionDescription) {
3889   MakeUnifiedPlanDescription();
3890 
3891   JsepSessionDescription deserialized_description(kDummyType);
3892   EXPECT_TRUE(
3893       SdpDeserialize(kUnifiedPlanSdpFullString, &deserialized_description));
3894 
3895   EXPECT_TRUE(CompareSessionDescription(jdesc_, deserialized_description));
3896 }
3897 
TEST_F(WebRtcSdpTest,SerializeUnifiedPlanSessionDescription)3898 TEST_F(WebRtcSdpTest, SerializeUnifiedPlanSessionDescription) {
3899   MakeUnifiedPlanDescription();
3900   TestSerialize(jdesc_);
3901 }
3902 
3903 // This tests deserializing a Unified Plan SDP that is compatible with both
3904 // Unified Plan and Plan B style SDP, meaning that it contains both "a=ssrc
3905 // msid" lines and "a=msid " lines. It tests the case for audio/video tracks
3906 // with no stream ids and multiple stream ids. For parsing this, the Unified
3907 // Plan a=msid lines should take priority, because the Plan B style a=ssrc msid
3908 // lines do not support multiple stream ids and no stream ids.
TEST_F(WebRtcSdpTest,DeserializeSessionDescriptionSpecialMsid)3909 TEST_F(WebRtcSdpTest, DeserializeSessionDescriptionSpecialMsid) {
3910   // Create both msid lines for Plan B and Unified Plan support.
3911   MakeUnifiedPlanDescriptionMultipleStreamIds(
3912       cricket::kMsidSignalingMediaSection |
3913       cricket::kMsidSignalingSsrcAttribute);
3914 
3915   JsepSessionDescription deserialized_description(kDummyType);
3916   EXPECT_TRUE(SdpDeserialize(kUnifiedPlanSdpFullStringWithSpecialMsid,
3917                              &deserialized_description));
3918 
3919   EXPECT_TRUE(CompareSessionDescription(jdesc_, deserialized_description));
3920   EXPECT_EQ(cricket::kMsidSignalingMediaSection |
3921                 cricket::kMsidSignalingSsrcAttribute,
3922             deserialized_description.description()->msid_signaling());
3923 }
3924 
3925 // Tests the serialization of a Unified Plan SDP that is compatible for both
3926 // Unified Plan and Plan B style SDPs, meaning that it contains both "a=ssrc
3927 // msid" lines and "a=msid " lines. It tests the case for no stream ids and
3928 // multiple stream ids.
TEST_F(WebRtcSdpTest,SerializeSessionDescriptionSpecialMsid)3929 TEST_F(WebRtcSdpTest, SerializeSessionDescriptionSpecialMsid) {
3930   // Create both msid lines for Plan B and Unified Plan support.
3931   MakeUnifiedPlanDescriptionMultipleStreamIds(
3932       cricket::kMsidSignalingMediaSection |
3933       cricket::kMsidSignalingSsrcAttribute);
3934   std::string serialized_sdp = webrtc::SdpSerialize(jdesc_);
3935   // We explicitly test that the serialized SDP string is equal to the hard
3936   // coded SDP string. This is necessary, because in the parser "a=msid" lines
3937   // take priority over "a=ssrc msid" lines. This means if we just used
3938   // TestSerialize(), it could serialize an SDP that omits "a=ssrc msid" lines,
3939   // and still pass, because the deserialized version would be the same.
3940   EXPECT_EQ(kUnifiedPlanSdpFullStringWithSpecialMsid, serialized_sdp);
3941 }
3942 
3943 // Tests that a Unified Plan style SDP (does not contain "a=ssrc msid" lines
3944 // that signal stream IDs) is deserialized appropriately. It tests the case for
3945 // no stream ids and multiple stream ids.
TEST_F(WebRtcSdpTest,UnifiedPlanDeserializeSessionDescriptionSpecialMsid)3946 TEST_F(WebRtcSdpTest, UnifiedPlanDeserializeSessionDescriptionSpecialMsid) {
3947   // Only create a=msid lines for strictly Unified Plan stream ID support.
3948   MakeUnifiedPlanDescriptionMultipleStreamIds(
3949       cricket::kMsidSignalingMediaSection);
3950 
3951   JsepSessionDescription deserialized_description(kDummyType);
3952   std::string unified_plan_sdp_string =
3953       kUnifiedPlanSdpFullStringWithSpecialMsid;
3954   RemoveSsrcMsidLinesFromSdpString(&unified_plan_sdp_string);
3955   EXPECT_TRUE(
3956       SdpDeserialize(unified_plan_sdp_string, &deserialized_description));
3957 
3958   EXPECT_TRUE(CompareSessionDescription(jdesc_, deserialized_description));
3959 }
3960 
3961 // Tests that a Unified Plan style SDP (does not contain "a=ssrc msid" lines
3962 // that signal stream IDs) is serialized appropriately. It tests the case for no
3963 // stream ids and multiple stream ids.
TEST_F(WebRtcSdpTest,UnifiedPlanSerializeSessionDescriptionSpecialMsid)3964 TEST_F(WebRtcSdpTest, UnifiedPlanSerializeSessionDescriptionSpecialMsid) {
3965   // Only create a=msid lines for strictly Unified Plan stream ID support.
3966   MakeUnifiedPlanDescriptionMultipleStreamIds(
3967       cricket::kMsidSignalingMediaSection);
3968 
3969   TestSerialize(jdesc_);
3970 }
3971 
3972 // This tests that a Unified Plan SDP with no a=ssrc lines is
3973 // serialized/deserialized appropriately. In this case the
3974 // MediaContentDescription will contain a StreamParams object that doesn't have
3975 // any SSRCs. Vice versa, this will be created upon deserializing an SDP with no
3976 // SSRC lines.
TEST_F(WebRtcSdpTest,DeserializeUnifiedPlanSessionDescriptionNoSsrcSignaling)3977 TEST_F(WebRtcSdpTest, DeserializeUnifiedPlanSessionDescriptionNoSsrcSignaling) {
3978   MakeUnifiedPlanDescription();
3979   RemoveSsrcSignalingFromStreamParams();
3980   std::string unified_plan_sdp_string = kUnifiedPlanSdpFullString;
3981   RemoveSsrcLinesFromSdpString(&unified_plan_sdp_string);
3982 
3983   JsepSessionDescription deserialized_description(kDummyType);
3984   EXPECT_TRUE(
3985       SdpDeserialize(unified_plan_sdp_string, &deserialized_description));
3986   EXPECT_TRUE(CompareSessionDescription(jdesc_, deserialized_description));
3987 }
3988 
TEST_F(WebRtcSdpTest,SerializeUnifiedPlanSessionDescriptionNoSsrcSignaling)3989 TEST_F(WebRtcSdpTest, SerializeUnifiedPlanSessionDescriptionNoSsrcSignaling) {
3990   MakeUnifiedPlanDescription();
3991   RemoveSsrcSignalingFromStreamParams();
3992 
3993   TestSerialize(jdesc_);
3994 }
3995 
TEST_F(WebRtcSdpTest,EmptyDescriptionHasNoMsidSignaling)3996 TEST_F(WebRtcSdpTest, EmptyDescriptionHasNoMsidSignaling) {
3997   JsepSessionDescription jsep_desc(kDummyType);
3998   ASSERT_TRUE(SdpDeserialize(kSdpSessionString, &jsep_desc));
3999   EXPECT_EQ(0, jsep_desc.description()->msid_signaling());
4000 }
4001 
TEST_F(WebRtcSdpTest,DataChannelOnlyHasNoMsidSignaling)4002 TEST_F(WebRtcSdpTest, DataChannelOnlyHasNoMsidSignaling) {
4003   JsepSessionDescription jsep_desc(kDummyType);
4004   std::string sdp = kSdpSessionString;
4005   sdp += kSdpSctpDataChannelString;
4006   ASSERT_TRUE(SdpDeserialize(sdp, &jsep_desc));
4007   EXPECT_EQ(0, jsep_desc.description()->msid_signaling());
4008 }
4009 
TEST_F(WebRtcSdpTest,PlanBHasSsrcAttributeMsidSignaling)4010 TEST_F(WebRtcSdpTest, PlanBHasSsrcAttributeMsidSignaling) {
4011   JsepSessionDescription jsep_desc(kDummyType);
4012   ASSERT_TRUE(SdpDeserialize(kPlanBSdpFullString, &jsep_desc));
4013   EXPECT_EQ(cricket::kMsidSignalingSsrcAttribute,
4014             jsep_desc.description()->msid_signaling());
4015 }
4016 
TEST_F(WebRtcSdpTest,UnifiedPlanHasMediaSectionMsidSignaling)4017 TEST_F(WebRtcSdpTest, UnifiedPlanHasMediaSectionMsidSignaling) {
4018   JsepSessionDescription jsep_desc(kDummyType);
4019   ASSERT_TRUE(SdpDeserialize(kUnifiedPlanSdpFullString, &jsep_desc));
4020   EXPECT_EQ(cricket::kMsidSignalingMediaSection,
4021             jsep_desc.description()->msid_signaling());
4022 }
4023 
4024 const char kMediaSectionMsidLine[] = "a=msid:local_stream_1 audio_track_id_1";
4025 const char kSsrcAttributeMsidLine[] =
4026     "a=ssrc:1 msid:local_stream_1 audio_track_id_1";
4027 
TEST_F(WebRtcSdpTest,SerializeOnlyMediaSectionMsid)4028 TEST_F(WebRtcSdpTest, SerializeOnlyMediaSectionMsid) {
4029   jdesc_.description()->set_msid_signaling(cricket::kMsidSignalingMediaSection);
4030   std::string sdp = webrtc::SdpSerialize(jdesc_);
4031 
4032   EXPECT_NE(std::string::npos, sdp.find(kMediaSectionMsidLine));
4033   EXPECT_EQ(std::string::npos, sdp.find(kSsrcAttributeMsidLine));
4034 }
4035 
TEST_F(WebRtcSdpTest,SerializeOnlySsrcAttributeMsid)4036 TEST_F(WebRtcSdpTest, SerializeOnlySsrcAttributeMsid) {
4037   jdesc_.description()->set_msid_signaling(
4038       cricket::kMsidSignalingSsrcAttribute);
4039   std::string sdp = webrtc::SdpSerialize(jdesc_);
4040 
4041   EXPECT_EQ(std::string::npos, sdp.find(kMediaSectionMsidLine));
4042   EXPECT_NE(std::string::npos, sdp.find(kSsrcAttributeMsidLine));
4043 }
4044 
TEST_F(WebRtcSdpTest,SerializeBothMediaSectionAndSsrcAttributeMsid)4045 TEST_F(WebRtcSdpTest, SerializeBothMediaSectionAndSsrcAttributeMsid) {
4046   jdesc_.description()->set_msid_signaling(
4047       cricket::kMsidSignalingMediaSection |
4048       cricket::kMsidSignalingSsrcAttribute);
4049   std::string sdp = webrtc::SdpSerialize(jdesc_);
4050 
4051   EXPECT_NE(std::string::npos, sdp.find(kMediaSectionMsidLine));
4052   EXPECT_NE(std::string::npos, sdp.find(kSsrcAttributeMsidLine));
4053 }
4054 
4055 // Regression test for heap overflow bug:
4056 // https://bugs.chromium.org/p/chromium/issues/detail?id=647916
TEST_F(WebRtcSdpTest,DeserializeSctpPortInVideoDescription)4057 TEST_F(WebRtcSdpTest, DeserializeSctpPortInVideoDescription) {
4058   // The issue occurs when the sctp-port attribute is found in a video
4059   // description. The actual heap overflow occurs when parsing the fmtp line.
4060   static const char kSdpWithSctpPortInVideoDescription[] =
4061       "v=0\r\n"
4062       "o=- 18446744069414584320 18446462598732840960 IN IP4 127.0.0.1\r\n"
4063       "s=-\r\n"
4064       "t=0 0\r\n"
4065       "m=video 9 UDP/DTLS/SCTP 120\r\n"
4066       "a=sctp-port 5000\r\n"
4067       "a=fmtp:108 foo=10\r\n";
4068 
4069   ExpectParseFailure(std::string(kSdpWithSctpPortInVideoDescription),
4070                      "sctp-port");
4071 }
4072 
4073 // Regression test for integer overflow bug:
4074 // https://bugs.chromium.org/p/chromium/issues/detail?id=648071
TEST_F(WebRtcSdpTest,DeserializeLargeBandwidthLimit)4075 TEST_F(WebRtcSdpTest, DeserializeLargeBandwidthLimit) {
4076   // Bandwidth attribute is the max signed 32-bit int, which will get
4077   // multiplied by 1000 and cause int overflow if not careful.
4078   static const char kSdpWithLargeBandwidth[] =
4079       "v=0\r\n"
4080       "o=- 18446744069414584320 18446462598732840960 IN IP4 127.0.0.1\r\n"
4081       "s=-\r\n"
4082       "t=0 0\r\n"
4083       "m=video 3457 RTP/SAVPF 120\r\n"
4084       "b=AS:2147483647\r\n"
4085       "foo=fail\r\n";
4086 
4087   ExpectParseFailure(std::string(kSdpWithLargeBandwidth), "foo=fail");
4088 }
4089 
4090 // Similar to the above, except that negative values are illegal, not just
4091 // error-prone as large values are.
4092 // https://bugs.chromium.org/p/chromium/issues/detail?id=675361
TEST_F(WebRtcSdpTest,DeserializingNegativeBandwidthLimitFails)4093 TEST_F(WebRtcSdpTest, DeserializingNegativeBandwidthLimitFails) {
4094   static const char kSdpWithNegativeBandwidth[] =
4095       "v=0\r\n"
4096       "o=- 18446744069414584320 18446462598732840960 IN IP4 127.0.0.1\r\n"
4097       "s=-\r\n"
4098       "t=0 0\r\n"
4099       "m=video 3457 RTP/SAVPF 120\r\n"
4100       "b=AS:-1000\r\n";
4101 
4102   ExpectParseFailure(std::string(kSdpWithNegativeBandwidth), "b=AS:-1000");
4103 }
4104 
4105 // An exception to the above rule: a value of -1 for b=AS should just be
4106 // ignored, resulting in "kAutoBandwidth" in the deserialized object.
4107 // Applications historically may be using "b=AS:-1" to mean "no bandwidth
4108 // limit", but this is now what ommitting the attribute entirely will do, so
4109 // ignoring it will have the intended effect.
TEST_F(WebRtcSdpTest,BandwidthLimitOfNegativeOneIgnored)4110 TEST_F(WebRtcSdpTest, BandwidthLimitOfNegativeOneIgnored) {
4111   static const char kSdpWithBandwidthOfNegativeOne[] =
4112       "v=0\r\n"
4113       "o=- 18446744069414584320 18446462598732840960 IN IP4 127.0.0.1\r\n"
4114       "s=-\r\n"
4115       "t=0 0\r\n"
4116       "m=video 3457 RTP/SAVPF 120\r\n"
4117       "b=AS:-1\r\n";
4118 
4119   JsepSessionDescription jdesc_output(kDummyType);
4120   EXPECT_TRUE(SdpDeserialize(kSdpWithBandwidthOfNegativeOne, &jdesc_output));
4121   const VideoContentDescription* vcd =
4122       GetFirstVideoContentDescription(jdesc_output.description());
4123   ASSERT_TRUE(vcd);
4124   EXPECT_EQ(cricket::kAutoBandwidth, vcd->bandwidth());
4125 }
4126 
4127 // Test that "ufrag"/"pwd" in the candidate line itself are ignored, and only
4128 // the "a=ice-ufrag"/"a=ice-pwd" attributes are used.
4129 // Regression test for:
4130 // https://bugs.chromium.org/p/chromium/issues/detail?id=681286
TEST_F(WebRtcSdpTest,IceCredentialsInCandidateStringIgnored)4131 TEST_F(WebRtcSdpTest, IceCredentialsInCandidateStringIgnored) {
4132   // Important piece is "ufrag foo pwd bar".
4133   static const char kSdpWithIceCredentialsInCandidateString[] =
4134       "v=0\r\n"
4135       "o=- 18446744069414584320 18446462598732840960 IN IP4 127.0.0.1\r\n"
4136       "s=-\r\n"
4137       "t=0 0\r\n"
4138       "m=audio 9 RTP/SAVPF 111\r\n"
4139       "c=IN IP4 0.0.0.0\r\n"
4140       "a=rtcp:9 IN IP4 0.0.0.0\r\n"
4141       "a=ice-ufrag:ufrag_voice\r\na=ice-pwd:pwd_voice\r\n"
4142       "a=rtpmap:111 opus/48000/2\r\n"
4143       "a=candidate:a0+B/1 1 udp 2130706432 192.168.1.5 1234 typ host "
4144       "generation 2 ufrag foo pwd bar\r\n";
4145 
4146   JsepSessionDescription jdesc_output(kDummyType);
4147   EXPECT_TRUE(
4148       SdpDeserialize(kSdpWithIceCredentialsInCandidateString, &jdesc_output));
4149   const IceCandidateCollection* candidates = jdesc_output.candidates(0);
4150   ASSERT_NE(nullptr, candidates);
4151   ASSERT_EQ(1U, candidates->count());
4152   cricket::Candidate c = candidates->at(0)->candidate();
4153   EXPECT_EQ("ufrag_voice", c.username());
4154   EXPECT_EQ("pwd_voice", c.password());
4155 }
4156 
4157 // Test that attribute lines "a=ice-ufrag-something"/"a=ice-pwd-something" are
4158 // ignored, and only the "a=ice-ufrag"/"a=ice-pwd" attributes are used.
4159 // Regression test for:
4160 // https://bugs.chromium.org/p/webrtc/issues/detail?id=9712
TEST_F(WebRtcSdpTest,AttributeWithPartialMatchingNameIsIgnored)4161 TEST_F(WebRtcSdpTest, AttributeWithPartialMatchingNameIsIgnored) {
4162   static const char kSdpWithFooIceCredentials[] =
4163       "v=0\r\n"
4164       "o=- 18446744069414584320 18446462598732840960 IN IP4 127.0.0.1\r\n"
4165       "s=-\r\n"
4166       "t=0 0\r\n"
4167       "m=audio 9 RTP/SAVPF 111\r\n"
4168       "c=IN IP4 0.0.0.0\r\n"
4169       "a=rtcp:9 IN IP4 0.0.0.0\r\n"
4170       "a=ice-ufrag-something:foo\r\na=ice-pwd-something:bar\r\n"
4171       "a=ice-ufrag:ufrag_voice\r\na=ice-pwd:pwd_voice\r\n"
4172       "a=rtpmap:111 opus/48000/2\r\n"
4173       "a=candidate:a0+B/1 1 udp 2130706432 192.168.1.5 1234 typ host "
4174       "generation 2\r\n";
4175 
4176   JsepSessionDescription jdesc_output(kDummyType);
4177   EXPECT_TRUE(SdpDeserialize(kSdpWithFooIceCredentials, &jdesc_output));
4178   const IceCandidateCollection* candidates = jdesc_output.candidates(0);
4179   ASSERT_NE(nullptr, candidates);
4180   ASSERT_EQ(1U, candidates->count());
4181   cricket::Candidate c = candidates->at(0)->candidate();
4182   EXPECT_EQ("ufrag_voice", c.username());
4183   EXPECT_EQ("pwd_voice", c.password());
4184 }
4185 
4186 // Test that SDP with an invalid port number in "a=candidate" lines is
4187 // rejected, without crashing.
4188 // Regression test for:
4189 // https://bugs.chromium.org/p/chromium/issues/detail?id=677029
TEST_F(WebRtcSdpTest,DeserializeInvalidPortInCandidateAttribute)4190 TEST_F(WebRtcSdpTest, DeserializeInvalidPortInCandidateAttribute) {
4191   static const char kSdpWithInvalidCandidatePort[] =
4192       "v=0\r\n"
4193       "o=- 18446744069414584320 18446462598732840960 IN IP4 127.0.0.1\r\n"
4194       "s=-\r\n"
4195       "t=0 0\r\n"
4196       "m=audio 9 RTP/SAVPF 111\r\n"
4197       "c=IN IP4 0.0.0.0\r\n"
4198       "a=rtcp:9 IN IP4 0.0.0.0\r\n"
4199       "a=ice-ufrag:ufrag_voice\r\na=ice-pwd:pwd_voice\r\n"
4200       "a=rtpmap:111 opus/48000/2\r\n"
4201       "a=candidate:a0+B/1 1 udp 2130706432 192.168.1.5 12345678 typ host "
4202       "generation 2 raddr 192.168.1.1 rport 87654321\r\n";
4203 
4204   JsepSessionDescription jdesc_output(kDummyType);
4205   EXPECT_FALSE(SdpDeserialize(kSdpWithInvalidCandidatePort, &jdesc_output));
4206 }
4207 
4208 // Test that "a=msid" with a missing track ID is rejected and doesn't crash.
4209 // Regression test for:
4210 // https://bugs.chromium.org/p/chromium/issues/detail?id=686405
TEST_F(WebRtcSdpTest,DeserializeMsidAttributeWithMissingTrackId)4211 TEST_F(WebRtcSdpTest, DeserializeMsidAttributeWithMissingTrackId) {
4212   static const char kSdpWithMissingTrackId[] =
4213       "v=0\r\n"
4214       "o=- 18446744069414584320 18446462598732840960 IN IP4 127.0.0.1\r\n"
4215       "s=-\r\n"
4216       "t=0 0\r\n"
4217       "m=audio 9 RTP/SAVPF 111\r\n"
4218       "c=IN IP4 0.0.0.0\r\n"
4219       "a=rtpmap:111 opus/48000/2\r\n"
4220       "a=msid:stream_id \r\n";
4221 
4222   JsepSessionDescription jdesc_output(kDummyType);
4223   EXPECT_FALSE(SdpDeserialize(kSdpWithMissingTrackId, &jdesc_output));
4224 }
4225 
TEST_F(WebRtcSdpTest,DeserializeMsidAttributeWithMissingStreamId)4226 TEST_F(WebRtcSdpTest, DeserializeMsidAttributeWithMissingStreamId) {
4227   static const char kSdpWithMissingStreamId[] =
4228       "v=0\r\n"
4229       "o=- 18446744069414584320 18446462598732840960 IN IP4 127.0.0.1\r\n"
4230       "s=-\r\n"
4231       "t=0 0\r\n"
4232       "m=audio 9 RTP/SAVPF 111\r\n"
4233       "c=IN IP4 0.0.0.0\r\n"
4234       "a=rtpmap:111 opus/48000/2\r\n"
4235       "a=msid: track_id\r\n";
4236 
4237   JsepSessionDescription jdesc_output(kDummyType);
4238   EXPECT_FALSE(SdpDeserialize(kSdpWithMissingStreamId, &jdesc_output));
4239 }
4240 
4241 // Tests that if both session-level address and media-level address exist, use
4242 // the media-level address.
TEST_F(WebRtcSdpTest,ParseConnectionData)4243 TEST_F(WebRtcSdpTest, ParseConnectionData) {
4244   JsepSessionDescription jsep_desc(kDummyType);
4245 
4246   // Sesssion-level address.
4247   std::string sdp = kSdpFullString;
4248   InjectAfter("s=-\r\n", "c=IN IP4 192.168.0.3\r\n", &sdp);
4249   EXPECT_TRUE(SdpDeserialize(sdp, &jsep_desc));
4250 
4251   const auto& content1 = jsep_desc.description()->contents()[0];
4252   EXPECT_EQ("74.125.127.126:2345",
4253             content1.media_description()->connection_address().ToString());
4254   const auto& content2 = jsep_desc.description()->contents()[1];
4255   EXPECT_EQ("74.125.224.39:3457",
4256             content2.media_description()->connection_address().ToString());
4257 }
4258 
4259 // Tests that the session-level connection address will be used if the media
4260 // level-addresses are not specified.
TEST_F(WebRtcSdpTest,ParseConnectionDataSessionLevelOnly)4261 TEST_F(WebRtcSdpTest, ParseConnectionDataSessionLevelOnly) {
4262   JsepSessionDescription jsep_desc(kDummyType);
4263 
4264   // Sesssion-level address.
4265   std::string sdp = kSdpString;
4266   InjectAfter("s=-\r\n", "c=IN IP4 192.168.0.3\r\n", &sdp);
4267   // Remove the media level addresses.
4268   Replace("c=IN IP4 0.0.0.0\r\n", "", &sdp);
4269   Replace("c=IN IP4 0.0.0.0\r\n", "", &sdp);
4270   EXPECT_TRUE(SdpDeserialize(sdp, &jsep_desc));
4271 
4272   const auto& content1 = jsep_desc.description()->contents()[0];
4273   EXPECT_EQ("192.168.0.3:9",
4274             content1.media_description()->connection_address().ToString());
4275   const auto& content2 = jsep_desc.description()->contents()[1];
4276   EXPECT_EQ("192.168.0.3:9",
4277             content2.media_description()->connection_address().ToString());
4278 }
4279 
TEST_F(WebRtcSdpTest,ParseConnectionDataIPv6)4280 TEST_F(WebRtcSdpTest, ParseConnectionDataIPv6) {
4281   JsepSessionDescription jsep_desc(kDummyType);
4282 
4283   std::string sdp = kSdpString;
4284   EXPECT_TRUE(SdpDeserialize(sdp, &jsep_desc));
4285   Replace("m=audio 9 RTP/SAVPF 111 103 104\r\nc=IN IP4 0.0.0.0\r\n",
4286           "m=audio 9 RTP/SAVPF 111 103 104\r\nc=IN IP6 "
4287           "2001:0db8:85a3:0000:0000:8a2e:0370:7335\r\n",
4288           &sdp);
4289   Replace("m=video 9 RTP/SAVPF 120\r\nc=IN IP4 0.0.0.0\r\n",
4290           "m=video 9 RTP/SAVPF 120\r\nc=IN IP6 "
4291           "2001:0db8:85a3:0000:0000:8a2e:0370:7336\r\n",
4292           &sdp);
4293   EXPECT_TRUE(SdpDeserialize(sdp, &jsep_desc));
4294   const auto& content1 = jsep_desc.description()->contents()[0];
4295   EXPECT_EQ("[2001:db8:85a3::8a2e:370:7335]:9",
4296             content1.media_description()->connection_address().ToString());
4297   const auto& content2 = jsep_desc.description()->contents()[1];
4298   EXPECT_EQ("[2001:db8:85a3::8a2e:370:7336]:9",
4299             content2.media_description()->connection_address().ToString());
4300 }
4301 
4302 // Test that a c= line that contains a hostname connection address can be
4303 // parsed.
TEST_F(WebRtcSdpTest,ParseConnectionDataWithHostnameConnectionAddress)4304 TEST_F(WebRtcSdpTest, ParseConnectionDataWithHostnameConnectionAddress) {
4305   JsepSessionDescription jsep_desc(kDummyType);
4306   std::string sdp = kSdpString;
4307   EXPECT_TRUE(SdpDeserialize(sdp, &jsep_desc));
4308 
4309   sdp = kSdpString;
4310   Replace("c=IN IP4 0.0.0.0\r\n", "c=IN IP4 example.local\r\n", &sdp);
4311   Replace("c=IN IP4 0.0.0.0\r\n", "c=IN IP4 example.local\r\n", &sdp);
4312   ASSERT_TRUE(SdpDeserialize(sdp, &jsep_desc));
4313 
4314   ASSERT_NE(nullptr, jsep_desc.description());
4315   const auto& content1 = jsep_desc.description()->contents()[0];
4316   EXPECT_EQ("example.local:9",
4317             content1.media_description()->connection_address().ToString());
4318   const auto& content2 = jsep_desc.description()->contents()[1];
4319   EXPECT_EQ("example.local:9",
4320             content2.media_description()->connection_address().ToString());
4321 }
4322 
4323 // Test that the invalid or unsupported connection data cannot be parsed.
TEST_F(WebRtcSdpTest,ParseConnectionDataFailure)4324 TEST_F(WebRtcSdpTest, ParseConnectionDataFailure) {
4325   JsepSessionDescription jsep_desc(kDummyType);
4326   std::string sdp = kSdpString;
4327   EXPECT_TRUE(SdpDeserialize(sdp, &jsep_desc));
4328 
4329   // Unsupported multicast IPv4 address.
4330   sdp = kSdpFullString;
4331   Replace("c=IN IP4 74.125.224.39\r\n", "c=IN IP4 74.125.224.39/127\r\n", &sdp);
4332   EXPECT_FALSE(SdpDeserialize(sdp, &jsep_desc));
4333 
4334   // Unsupported multicast IPv6 address.
4335   sdp = kSdpFullString;
4336   Replace("c=IN IP4 74.125.224.39\r\n", "c=IN IP6 ::1/3\r\n", &sdp);
4337   EXPECT_FALSE(SdpDeserialize(sdp, &jsep_desc));
4338 
4339   // Mismatched address type.
4340   sdp = kSdpFullString;
4341   Replace("c=IN IP4 74.125.224.39\r\n", "c=IN IP6 74.125.224.39\r\n", &sdp);
4342   EXPECT_FALSE(SdpDeserialize(sdp, &jsep_desc));
4343 
4344   sdp = kSdpFullString;
4345   Replace("c=IN IP4 74.125.224.39\r\n",
4346           "c=IN IP4 2001:0db8:85a3:0000:0000:8a2e:0370:7334\r\n", &sdp);
4347   EXPECT_FALSE(SdpDeserialize(sdp, &jsep_desc));
4348 }
4349 
TEST_F(WebRtcSdpTest,SerializeAndDeserializeWithConnectionAddress)4350 TEST_F(WebRtcSdpTest, SerializeAndDeserializeWithConnectionAddress) {
4351   JsepSessionDescription expected_jsep(kDummyType);
4352   MakeDescriptionWithoutCandidates(&expected_jsep);
4353   // Serialization.
4354   std::string message = webrtc::SdpSerialize(expected_jsep);
4355   // Deserialization.
4356   JsepSessionDescription jdesc(kDummyType);
4357   EXPECT_TRUE(SdpDeserialize(message, &jdesc));
4358   auto audio_desc = jdesc.description()
4359                         ->GetContentByName(kAudioContentName)
4360                         ->media_description();
4361   auto video_desc = jdesc.description()
4362                         ->GetContentByName(kVideoContentName)
4363                         ->media_description();
4364   EXPECT_EQ(audio_desc_->connection_address().ToString(),
4365             audio_desc->connection_address().ToString());
4366   EXPECT_EQ(video_desc_->connection_address().ToString(),
4367             video_desc->connection_address().ToString());
4368 }
4369 
4370 // RFC4566 says "If a session has no meaningful name, the value "s= " SHOULD be
4371 // used (i.e., a single space as the session name)." So we should accept that.
TEST_F(WebRtcSdpTest,DeserializeEmptySessionName)4372 TEST_F(WebRtcSdpTest, DeserializeEmptySessionName) {
4373   JsepSessionDescription jsep_desc(kDummyType);
4374   std::string sdp = kSdpString;
4375   Replace("s=-\r\n", "s= \r\n", &sdp);
4376   EXPECT_TRUE(SdpDeserialize(sdp, &jsep_desc));
4377 }
4378 
4379 // Simulcast malformed input test for invalid format.
TEST_F(WebRtcSdpTest,DeserializeSimulcastNegative_EmptyAttribute)4380 TEST_F(WebRtcSdpTest, DeserializeSimulcastNegative_EmptyAttribute) {
4381   ExpectParseFailureWithNewLines("a=ssrc:3 label:video_track_id_1\r\n",
4382                                  "a=simulcast:\r\n", "a=simulcast:");
4383 }
4384 
4385 // Tests that duplicate simulcast entries in the SDP triggers a parse failure.
TEST_F(WebRtcSdpTest,DeserializeSimulcastNegative_DuplicateAttribute)4386 TEST_F(WebRtcSdpTest, DeserializeSimulcastNegative_DuplicateAttribute) {
4387   ExpectParseFailureWithNewLines("a=ssrc:3 label:video_track_id_1\r\n",
4388                                  "a=simulcast:send 1\r\na=simulcast:recv 2\r\n",
4389                                  "a=simulcast:");
4390 }
4391 
4392 // Validates that deserialization uses the a=simulcast: attribute
TEST_F(WebRtcSdpTest,TestDeserializeSimulcastAttribute)4393 TEST_F(WebRtcSdpTest, TestDeserializeSimulcastAttribute) {
4394   std::string sdp = kUnifiedPlanSdpFullStringNoSsrc;
4395   sdp += "a=rid:1 send\r\n";
4396   sdp += "a=rid:2 send\r\n";
4397   sdp += "a=rid:3 send\r\n";
4398   sdp += "a=rid:4 recv\r\n";
4399   sdp += "a=rid:5 recv\r\n";
4400   sdp += "a=rid:6 recv\r\n";
4401   sdp += "a=simulcast:send 1,2;3 recv 4;5;6\r\n";
4402   JsepSessionDescription output(kDummyType);
4403   SdpParseError error;
4404   EXPECT_TRUE(webrtc::SdpDeserialize(sdp, &output, &error));
4405   const cricket::ContentInfos& contents = output.description()->contents();
4406   const cricket::MediaContentDescription* media =
4407       contents.back().media_description();
4408   EXPECT_TRUE(media->HasSimulcast());
4409   EXPECT_EQ(2ul, media->simulcast_description().send_layers().size());
4410   EXPECT_EQ(3ul, media->simulcast_description().receive_layers().size());
4411   EXPECT_FALSE(media->streams().empty());
4412   const std::vector<RidDescription>& rids = media->streams()[0].rids();
4413   CompareRidDescriptionIds(rids, {"1", "2", "3"});
4414 }
4415 
4416 // Validates that deserialization removes rids that do not appear in SDP
TEST_F(WebRtcSdpTest,TestDeserializeSimulcastAttributeRemovesUnknownRids)4417 TEST_F(WebRtcSdpTest, TestDeserializeSimulcastAttributeRemovesUnknownRids) {
4418   std::string sdp = kUnifiedPlanSdpFullStringNoSsrc;
4419   sdp += "a=rid:1 send\r\n";
4420   sdp += "a=rid:3 send\r\n";
4421   sdp += "a=rid:4 recv\r\n";
4422   sdp += "a=simulcast:send 1,2;3 recv 4;5,6\r\n";
4423   JsepSessionDescription output(kDummyType);
4424   SdpParseError error;
4425   EXPECT_TRUE(webrtc::SdpDeserialize(sdp, &output, &error));
4426   const cricket::ContentInfos& contents = output.description()->contents();
4427   const cricket::MediaContentDescription* media =
4428       contents.back().media_description();
4429   EXPECT_TRUE(media->HasSimulcast());
4430   const SimulcastDescription& simulcast = media->simulcast_description();
4431   EXPECT_EQ(2ul, simulcast.send_layers().size());
4432   EXPECT_EQ(1ul, simulcast.receive_layers().size());
4433 
4434   std::vector<SimulcastLayer> all_send_layers =
4435       simulcast.send_layers().GetAllLayers();
4436   EXPECT_EQ(2ul, all_send_layers.size());
4437   EXPECT_EQ(0,
4438             absl::c_count_if(all_send_layers, [](const SimulcastLayer& layer) {
4439               return layer.rid == "2";
4440             }));
4441 
4442   std::vector<SimulcastLayer> all_receive_layers =
4443       simulcast.receive_layers().GetAllLayers();
4444   ASSERT_EQ(1ul, all_receive_layers.size());
4445   EXPECT_EQ("4", all_receive_layers[0].rid);
4446 
4447   EXPECT_FALSE(media->streams().empty());
4448   const std::vector<RidDescription>& rids = media->streams()[0].rids();
4449   CompareRidDescriptionIds(rids, {"1", "3"});
4450 }
4451 
4452 // Validates that Simulcast removes rids that appear in both send and receive.
TEST_F(WebRtcSdpTest,TestDeserializeSimulcastAttributeRemovesDuplicateSendReceive)4453 TEST_F(WebRtcSdpTest,
4454        TestDeserializeSimulcastAttributeRemovesDuplicateSendReceive) {
4455   std::string sdp = kUnifiedPlanSdpFullStringNoSsrc;
4456   sdp += "a=rid:1 send\r\n";
4457   sdp += "a=rid:2 send\r\n";
4458   sdp += "a=rid:3 send\r\n";
4459   sdp += "a=rid:4 recv\r\n";
4460   sdp += "a=simulcast:send 1;2;3 recv 2;4\r\n";
4461   JsepSessionDescription output(kDummyType);
4462   SdpParseError error;
4463   EXPECT_TRUE(webrtc::SdpDeserialize(sdp, &output, &error));
4464   const cricket::ContentInfos& contents = output.description()->contents();
4465   const cricket::MediaContentDescription* media =
4466       contents.back().media_description();
4467   EXPECT_TRUE(media->HasSimulcast());
4468   const SimulcastDescription& simulcast = media->simulcast_description();
4469   EXPECT_EQ(2ul, simulcast.send_layers().size());
4470   EXPECT_EQ(1ul, simulcast.receive_layers().size());
4471   EXPECT_EQ(2ul, simulcast.send_layers().GetAllLayers().size());
4472   EXPECT_EQ(1ul, simulcast.receive_layers().GetAllLayers().size());
4473 
4474   EXPECT_FALSE(media->streams().empty());
4475   const std::vector<RidDescription>& rids = media->streams()[0].rids();
4476   CompareRidDescriptionIds(rids, {"1", "3"});
4477 }
4478 
4479 // Ignores empty rid line.
TEST_F(WebRtcSdpTest,TestDeserializeIgnoresEmptyRidLines)4480 TEST_F(WebRtcSdpTest, TestDeserializeIgnoresEmptyRidLines) {
4481   std::string sdp = kUnifiedPlanSdpFullStringNoSsrc;
4482   sdp += "a=rid:1 send\r\n";
4483   sdp += "a=rid:2 send\r\n";
4484   sdp += "a=rid\r\n";   // Should ignore this line.
4485   sdp += "a=rid:\r\n";  // Should ignore this line.
4486   sdp += "a=simulcast:send 1;2\r\n";
4487   JsepSessionDescription output(kDummyType);
4488   SdpParseError error;
4489   EXPECT_TRUE(webrtc::SdpDeserialize(sdp, &output, &error));
4490   const cricket::ContentInfos& contents = output.description()->contents();
4491   const cricket::MediaContentDescription* media =
4492       contents.back().media_description();
4493   EXPECT_TRUE(media->HasSimulcast());
4494   const SimulcastDescription& simulcast = media->simulcast_description();
4495   EXPECT_TRUE(simulcast.receive_layers().empty());
4496   EXPECT_EQ(2ul, simulcast.send_layers().size());
4497   EXPECT_EQ(2ul, simulcast.send_layers().GetAllLayers().size());
4498 
4499   EXPECT_FALSE(media->streams().empty());
4500   const std::vector<RidDescription>& rids = media->streams()[0].rids();
4501   CompareRidDescriptionIds(rids, {"1", "2"});
4502 }
4503 
4504 // Ignores malformed rid lines.
TEST_F(WebRtcSdpTest,TestDeserializeIgnoresMalformedRidLines)4505 TEST_F(WebRtcSdpTest, TestDeserializeIgnoresMalformedRidLines) {
4506   std::string sdp = kUnifiedPlanSdpFullStringNoSsrc;
4507   sdp += "a=rid:1 send pt=\r\n";              // Should ignore this line.
4508   sdp += "a=rid:2 receive\r\n";               // Should ignore this line.
4509   sdp += "a=rid:3 max-width=720;pt=120\r\n";  // Should ignore this line.
4510   sdp += "a=rid:4\r\n";                       // Should ignore this line.
4511   sdp += "a=rid:5 send\r\n";
4512   sdp += "a=simulcast:send 1,2,3;4,5\r\n";
4513   JsepSessionDescription output(kDummyType);
4514   SdpParseError error;
4515   EXPECT_TRUE(webrtc::SdpDeserialize(sdp, &output, &error));
4516   const cricket::ContentInfos& contents = output.description()->contents();
4517   const cricket::MediaContentDescription* media =
4518       contents.back().media_description();
4519   EXPECT_TRUE(media->HasSimulcast());
4520   const SimulcastDescription& simulcast = media->simulcast_description();
4521   EXPECT_TRUE(simulcast.receive_layers().empty());
4522   EXPECT_EQ(1ul, simulcast.send_layers().size());
4523   EXPECT_EQ(1ul, simulcast.send_layers().GetAllLayers().size());
4524 
4525   EXPECT_FALSE(media->streams().empty());
4526   const std::vector<RidDescription>& rids = media->streams()[0].rids();
4527   CompareRidDescriptionIds(rids, {"5"});
4528 }
4529 
4530 // Removes RIDs that specify a different format than the m= section.
TEST_F(WebRtcSdpTest,TestDeserializeRemovesRidsWithInvalidCodec)4531 TEST_F(WebRtcSdpTest, TestDeserializeRemovesRidsWithInvalidCodec) {
4532   std::string sdp = kUnifiedPlanSdpFullStringNoSsrc;
4533   sdp += "a=rid:1 send pt=121,120\r\n";  // Should remove 121 and keep RID.
4534   sdp += "a=rid:2 send pt=121\r\n";      // Should remove RID altogether.
4535   sdp += "a=simulcast:send 1;2\r\n";
4536   JsepSessionDescription output(kDummyType);
4537   SdpParseError error;
4538   EXPECT_TRUE(webrtc::SdpDeserialize(sdp, &output, &error));
4539   const cricket::ContentInfos& contents = output.description()->contents();
4540   const cricket::MediaContentDescription* media =
4541       contents.back().media_description();
4542   EXPECT_TRUE(media->HasSimulcast());
4543   const SimulcastDescription& simulcast = media->simulcast_description();
4544   EXPECT_TRUE(simulcast.receive_layers().empty());
4545   EXPECT_EQ(1ul, simulcast.send_layers().size());
4546   EXPECT_EQ(1ul, simulcast.send_layers().GetAllLayers().size());
4547   EXPECT_EQ("1", simulcast.send_layers()[0][0].rid);
4548   EXPECT_EQ(1ul, media->streams().size());
4549   const std::vector<RidDescription>& rids = media->streams()[0].rids();
4550   EXPECT_EQ(1ul, rids.size());
4551   EXPECT_EQ("1", rids[0].rid);
4552   EXPECT_EQ(1ul, rids[0].payload_types.size());
4553   EXPECT_EQ(120, rids[0].payload_types[0]);
4554 }
4555 
4556 // Ignores duplicate rid lines
TEST_F(WebRtcSdpTest,TestDeserializeIgnoresDuplicateRidLines)4557 TEST_F(WebRtcSdpTest, TestDeserializeIgnoresDuplicateRidLines) {
4558   std::string sdp = kUnifiedPlanSdpFullStringNoSsrc;
4559   sdp += "a=rid:1 send\r\n";
4560   sdp += "a=rid:2 send\r\n";
4561   sdp += "a=rid:2 send\r\n";
4562   sdp += "a=rid:3 send\r\n";
4563   sdp += "a=rid:4 recv\r\n";
4564   sdp += "a=simulcast:send 1,2;3 recv 4\r\n";
4565   JsepSessionDescription output(kDummyType);
4566   SdpParseError error;
4567   EXPECT_TRUE(webrtc::SdpDeserialize(sdp, &output, &error));
4568   const cricket::ContentInfos& contents = output.description()->contents();
4569   const cricket::MediaContentDescription* media =
4570       contents.back().media_description();
4571   EXPECT_TRUE(media->HasSimulcast());
4572   const SimulcastDescription& simulcast = media->simulcast_description();
4573   EXPECT_EQ(2ul, simulcast.send_layers().size());
4574   EXPECT_EQ(1ul, simulcast.receive_layers().size());
4575   EXPECT_EQ(2ul, simulcast.send_layers().GetAllLayers().size());
4576   EXPECT_EQ(1ul, simulcast.receive_layers().GetAllLayers().size());
4577 
4578   EXPECT_FALSE(media->streams().empty());
4579   const std::vector<RidDescription>& rids = media->streams()[0].rids();
4580   CompareRidDescriptionIds(rids, {"1", "3"});
4581 }
4582 
TEST_F(WebRtcSdpTest,TestDeserializeRidSendDirection)4583 TEST_F(WebRtcSdpTest, TestDeserializeRidSendDirection) {
4584   std::string sdp = kUnifiedPlanSdpFullStringNoSsrc;
4585   sdp += "a=rid:1 recv\r\n";
4586   sdp += "a=rid:2 recv\r\n";
4587   sdp += "a=simulcast:send 1;2\r\n";
4588   JsepSessionDescription output(kDummyType);
4589   SdpParseError error;
4590   EXPECT_TRUE(webrtc::SdpDeserialize(sdp, &output, &error));
4591   const cricket::ContentInfos& contents = output.description()->contents();
4592   const cricket::MediaContentDescription* media =
4593       contents.back().media_description();
4594   EXPECT_FALSE(media->HasSimulcast());
4595 }
4596 
TEST_F(WebRtcSdpTest,TestDeserializeRidRecvDirection)4597 TEST_F(WebRtcSdpTest, TestDeserializeRidRecvDirection) {
4598   std::string sdp = kUnifiedPlanSdpFullStringNoSsrc;
4599   sdp += "a=rid:1 send\r\n";
4600   sdp += "a=rid:2 send\r\n";
4601   sdp += "a=simulcast:recv 1;2\r\n";
4602   JsepSessionDescription output(kDummyType);
4603   SdpParseError error;
4604   EXPECT_TRUE(webrtc::SdpDeserialize(sdp, &output, &error));
4605   const cricket::ContentInfos& contents = output.description()->contents();
4606   const cricket::MediaContentDescription* media =
4607       contents.back().media_description();
4608   EXPECT_FALSE(media->HasSimulcast());
4609 }
4610 
TEST_F(WebRtcSdpTest,TestDeserializeIgnoresWrongRidDirectionLines)4611 TEST_F(WebRtcSdpTest, TestDeserializeIgnoresWrongRidDirectionLines) {
4612   std::string sdp = kUnifiedPlanSdpFullStringNoSsrc;
4613   sdp += "a=rid:1 send\r\n";
4614   sdp += "a=rid:2 send\r\n";
4615   sdp += "a=rid:3 send\r\n";
4616   sdp += "a=rid:4 recv\r\n";
4617   sdp += "a=rid:5 recv\r\n";
4618   sdp += "a=rid:6 recv\r\n";
4619   sdp += "a=simulcast:send 1;5;3 recv 4;2;6\r\n";
4620   JsepSessionDescription output(kDummyType);
4621   SdpParseError error;
4622   EXPECT_TRUE(webrtc::SdpDeserialize(sdp, &output, &error));
4623   const cricket::ContentInfos& contents = output.description()->contents();
4624   const cricket::MediaContentDescription* media =
4625       contents.back().media_description();
4626   EXPECT_TRUE(media->HasSimulcast());
4627   const SimulcastDescription& simulcast = media->simulcast_description();
4628   EXPECT_EQ(2ul, simulcast.send_layers().size());
4629   EXPECT_EQ(2ul, simulcast.receive_layers().size());
4630   EXPECT_EQ(2ul, simulcast.send_layers().GetAllLayers().size());
4631   EXPECT_EQ(2ul, simulcast.receive_layers().GetAllLayers().size());
4632 
4633   EXPECT_FALSE(media->streams().empty());
4634   const std::vector<RidDescription>& rids = media->streams()[0].rids();
4635   CompareRidDescriptionIds(rids, {"1", "3"});
4636 }
4637 
4638 // Simulcast serialization integration test.
4639 // This test will serialize and deserialize the description and compare.
4640 // More detailed tests for parsing simulcast can be found in
4641 // unit tests for SdpSerializer.
TEST_F(WebRtcSdpTest,SerializeSimulcast_ComplexSerialization)4642 TEST_F(WebRtcSdpTest, SerializeSimulcast_ComplexSerialization) {
4643   MakeUnifiedPlanDescription(/* use_ssrcs = */ false);
4644   auto description = jdesc_.description();
4645   auto media = description->GetContentDescriptionByName(kVideoContentName3);
4646   ASSERT_EQ(media->streams().size(), 1ul);
4647   StreamParams& send_stream = media->mutable_streams()[0];
4648   std::vector<RidDescription> send_rids;
4649   send_rids.push_back(RidDescription("1", RidDirection::kSend));
4650   send_rids.push_back(RidDescription("2", RidDirection::kSend));
4651   send_rids.push_back(RidDescription("3", RidDirection::kSend));
4652   send_rids.push_back(RidDescription("4", RidDirection::kSend));
4653   send_stream.set_rids(send_rids);
4654   std::vector<RidDescription> receive_rids;
4655   receive_rids.push_back(RidDescription("5", RidDirection::kReceive));
4656   receive_rids.push_back(RidDescription("6", RidDirection::kReceive));
4657   receive_rids.push_back(RidDescription("7", RidDirection::kReceive));
4658   media->set_receive_rids(receive_rids);
4659 
4660   SimulcastDescription& simulcast = media->simulcast_description();
4661   simulcast.send_layers().AddLayerWithAlternatives(
4662       {SimulcastLayer("2", false), SimulcastLayer("1", true)});
4663   simulcast.send_layers().AddLayerWithAlternatives(
4664       {SimulcastLayer("4", false), SimulcastLayer("3", false)});
4665   simulcast.receive_layers().AddLayer({SimulcastLayer("5", false)});
4666   simulcast.receive_layers().AddLayer({SimulcastLayer("6", false)});
4667   simulcast.receive_layers().AddLayer({SimulcastLayer("7", false)});
4668 
4669   TestSerialize(jdesc_);
4670 }
4671 
4672 // Test that the content name is empty if the media section does not have an
4673 // a=mid line.
TEST_F(WebRtcSdpTest,ParseNoMid)4674 TEST_F(WebRtcSdpTest, ParseNoMid) {
4675   std::string sdp = kSdpString;
4676   Replace("a=mid:audio_content_name\r\n", "", &sdp);
4677   Replace("a=mid:video_content_name\r\n", "", &sdp);
4678 
4679   JsepSessionDescription output(kDummyType);
4680   SdpParseError error;
4681   ASSERT_TRUE(webrtc::SdpDeserialize(sdp, &output, &error));
4682 
4683   EXPECT_THAT(output.description()->contents(),
4684               ElementsAre(Field("name", &cricket::ContentInfo::name, ""),
4685                           Field("name", &cricket::ContentInfo::name, "")));
4686 }
4687 
TEST_F(WebRtcSdpTest,SerializeWithDefaultSctpProtocol)4688 TEST_F(WebRtcSdpTest, SerializeWithDefaultSctpProtocol) {
4689   AddSctpDataChannel(false);  // Don't use sctpmap
4690   JsepSessionDescription jsep_desc(kDummyType);
4691   MakeDescriptionWithoutCandidates(&jsep_desc);
4692   std::string message = webrtc::SdpSerialize(jsep_desc);
4693   EXPECT_NE(std::string::npos,
4694             message.find(cricket::kMediaProtocolUdpDtlsSctp));
4695 }
4696 
TEST_F(WebRtcSdpTest,DeserializeWithAllSctpProtocols)4697 TEST_F(WebRtcSdpTest, DeserializeWithAllSctpProtocols) {
4698   AddSctpDataChannel(false);
4699   std::string protocols[] = {cricket::kMediaProtocolDtlsSctp,
4700                              cricket::kMediaProtocolUdpDtlsSctp,
4701                              cricket::kMediaProtocolTcpDtlsSctp};
4702   for (const auto& protocol : protocols) {
4703     sctp_desc_->set_protocol(protocol);
4704     JsepSessionDescription jsep_desc(kDummyType);
4705     MakeDescriptionWithoutCandidates(&jsep_desc);
4706     std::string message = webrtc::SdpSerialize(jsep_desc);
4707     EXPECT_NE(std::string::npos, message.find(protocol));
4708     JsepSessionDescription jsep_output(kDummyType);
4709     SdpParseError error;
4710     EXPECT_TRUE(webrtc::SdpDeserialize(message, &jsep_output, &error));
4711   }
4712 }
4713 
4714 // According to https://tools.ietf.org/html/rfc5576#section-6.1, the CNAME
4715 // attribute is mandatory, but we relax that restriction.
TEST_F(WebRtcSdpTest,DeserializeSessionDescriptionWithoutCname)4716 TEST_F(WebRtcSdpTest, DeserializeSessionDescriptionWithoutCname) {
4717   std::string sdp_without_cname = kSdpFullString;
4718   Replace("a=ssrc:1 cname:stream_1_cname\r\n", "", &sdp_without_cname);
4719   JsepSessionDescription new_jdesc(kDummyType);
4720   EXPECT_TRUE(SdpDeserialize(sdp_without_cname, &new_jdesc));
4721 
4722   audio_desc_->mutable_streams()[0].cname = "";
4723   ASSERT_TRUE(jdesc_.Initialize(desc_.Clone(), jdesc_.session_id(),
4724                                 jdesc_.session_version()));
4725   EXPECT_TRUE(CompareSessionDescription(jdesc_, new_jdesc));
4726 }
4727 
TEST_F(WebRtcSdpTest,DeserializeSdpWithUnsupportedMediaType)4728 TEST_F(WebRtcSdpTest, DeserializeSdpWithUnsupportedMediaType) {
4729   std::string sdp = kSdpSessionString;
4730   sdp +=
4731       "m=bogus 9 RTP/SAVPF 0 8\r\n"
4732       "c=IN IP4 0.0.0.0\r\n"
4733       "a=mid:bogusmid\r\n";
4734   sdp +=
4735       "m=audio/something 9 RTP/SAVPF 0 8\r\n"
4736       "c=IN IP4 0.0.0.0\r\n"
4737       "a=mid:somethingmid\r\n";
4738 
4739   JsepSessionDescription jdesc_output(kDummyType);
4740   EXPECT_TRUE(SdpDeserialize(sdp, &jdesc_output));
4741 
4742   ASSERT_EQ(2u, jdesc_output.description()->contents().size());
4743   ASSERT_NE(nullptr, jdesc_output.description()
4744                          ->contents()[0]
4745                          .media_description()
4746                          ->as_unsupported());
4747   ASSERT_NE(nullptr, jdesc_output.description()
4748                          ->contents()[1]
4749                          .media_description()
4750                          ->as_unsupported());
4751 
4752   EXPECT_TRUE(jdesc_output.description()->contents()[0].rejected);
4753   EXPECT_TRUE(jdesc_output.description()->contents()[1].rejected);
4754 
4755   EXPECT_EQ(jdesc_output.description()->contents()[0].name, "bogusmid");
4756   EXPECT_EQ(jdesc_output.description()->contents()[1].name, "somethingmid");
4757 }
4758