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     AudioCodec opus(111, "opus", 48000, 0, 2);
1297     audio->AddCodec(opus);
1298     audio->AddCodec(AudioCodec(103, "ISAC", 16000, 0, 1));
1299     audio->AddCodec(AudioCodec(104, "ISAC", 32000, 0, 1));
1300     return audio;
1301   }
1302 
1303   // Turns the existing reference description into a unified plan description,
1304   // with 3 audio MediaContentDescriptions with special StreamParams that
1305   // contain 0 or multiple stream ids: - audio track 1 has 1 media stream id -
1306   // audio track 2 has 2 media stream ids - audio track 3 has 0 media stream ids
MakeUnifiedPlanDescriptionMultipleStreamIds(const int msid_signaling)1307   void MakeUnifiedPlanDescriptionMultipleStreamIds(const int msid_signaling) {
1308     desc_.RemoveContentByName(kVideoContentName);
1309     desc_.RemoveTransportInfoByName(kVideoContentName);
1310     RemoveVideoCandidates();
1311 
1312     // Audio track 2 has 2 media stream ids.
1313     AudioContentDescription* audio_desc_2 = CreateAudioContentDescription();
1314     StreamParams audio_track_2;
1315     audio_track_2.id = kAudioTrackId2;
1316     audio_track_2.cname = kStream1Cname;
1317     audio_track_2.set_stream_ids({kStreamId1, kStreamId2});
1318     audio_track_2.ssrcs.push_back(kAudioTrack2Ssrc);
1319     audio_desc_2->AddStream(audio_track_2);
1320     desc_.AddContent(kAudioContentName2, MediaProtocolType::kRtp,
1321                      absl::WrapUnique(audio_desc_2));
1322     desc_.AddTransportInfo(TransportInfo(
1323         kAudioContentName2, TransportDescription(kUfragVoice2, kPwdVoice2)));
1324 
1325     // Audio track 3 has no stream ids.
1326     AudioContentDescription* audio_desc_3 = CreateAudioContentDescription();
1327     StreamParams audio_track_3;
1328     audio_track_3.id = kAudioTrackId3;
1329     audio_track_3.cname = kStream2Cname;
1330     audio_track_3.set_stream_ids({});
1331     audio_track_3.ssrcs.push_back(kAudioTrack3Ssrc);
1332     audio_desc_3->AddStream(audio_track_3);
1333     desc_.AddContent(kAudioContentName3, MediaProtocolType::kRtp,
1334                      absl::WrapUnique(audio_desc_3));
1335     desc_.AddTransportInfo(TransportInfo(
1336         kAudioContentName3, TransportDescription(kUfragVoice3, kPwdVoice3)));
1337     desc_.set_msid_signaling(msid_signaling);
1338     ASSERT_TRUE(jdesc_.Initialize(desc_.Clone(), jdesc_.session_id(),
1339                                   jdesc_.session_version()));
1340   }
1341 
1342   // Turns the existing reference description into a unified plan description
1343   // with one audio MediaContentDescription that contains one StreamParams with
1344   // 0 ssrcs.
MakeUnifiedPlanDescriptionNoSsrcSignaling()1345   void MakeUnifiedPlanDescriptionNoSsrcSignaling() {
1346     desc_.RemoveContentByName(kVideoContentName);
1347     desc_.RemoveContentByName(kAudioContentName);
1348     desc_.RemoveTransportInfoByName(kVideoContentName);
1349     RemoveVideoCandidates();
1350 
1351     AudioContentDescription* audio_desc = CreateAudioContentDescription();
1352     StreamParams audio_track;
1353     audio_track.id = kAudioTrackId1;
1354     audio_track.set_stream_ids({kStreamId1});
1355     audio_desc->AddStream(audio_track);
1356     desc_.AddContent(kAudioContentName, MediaProtocolType::kRtp,
1357                      absl::WrapUnique(audio_desc));
1358 
1359     // Enable signaling a=msid lines.
1360     desc_.set_msid_signaling(cricket::kMsidSignalingMediaSection);
1361     ASSERT_TRUE(jdesc_.Initialize(desc_.Clone(), jdesc_.session_id(),
1362                                   jdesc_.session_version()));
1363   }
1364 
1365   // Creates a video content description with no streams, and some default
1366   // configuration.
CreateVideoContentDescription()1367   VideoContentDescription* CreateVideoContentDescription() {
1368     VideoContentDescription* video = new VideoContentDescription();
1369     video->AddCrypto(CryptoParams(
1370         1, "AES_CM_128_HMAC_SHA1_80",
1371         "inline:d0RmdmcmVCspeEc3QGZiNWpVLFJhQX1cfHAwJSoj|2^20|1:32", ""));
1372     video->set_protocol(cricket::kMediaProtocolSavpf);
1373     video->AddCodec(
1374         VideoCodec(120, JsepSessionDescription::kDefaultVideoCodecName));
1375     return video;
1376   }
1377 
1378   template <class MCD>
CompareMediaContentDescription(const MCD * cd1,const MCD * cd2)1379   void CompareMediaContentDescription(const MCD* cd1, const MCD* cd2) {
1380     // type
1381     EXPECT_EQ(cd1->type(), cd2->type());
1382 
1383     // content direction
1384     EXPECT_EQ(cd1->direction(), cd2->direction());
1385 
1386     // rtcp_mux
1387     EXPECT_EQ(cd1->rtcp_mux(), cd2->rtcp_mux());
1388 
1389     // rtcp_reduced_size
1390     EXPECT_EQ(cd1->rtcp_reduced_size(), cd2->rtcp_reduced_size());
1391 
1392     // cryptos
1393     EXPECT_EQ(cd1->cryptos().size(), cd2->cryptos().size());
1394     if (cd1->cryptos().size() != cd2->cryptos().size()) {
1395       ADD_FAILURE();
1396       return;
1397     }
1398     for (size_t i = 0; i < cd1->cryptos().size(); ++i) {
1399       const CryptoParams c1 = cd1->cryptos().at(i);
1400       const CryptoParams c2 = cd2->cryptos().at(i);
1401       EXPECT_TRUE(c1.Matches(c2));
1402       EXPECT_EQ(c1.key_params, c2.key_params);
1403       EXPECT_EQ(c1.session_params, c2.session_params);
1404     }
1405 
1406     // protocol
1407     // Use an equivalence class here, for old and new versions of the
1408     // protocol description.
1409     if (cd1->protocol() == cricket::kMediaProtocolDtlsSctp ||
1410         cd1->protocol() == cricket::kMediaProtocolUdpDtlsSctp ||
1411         cd1->protocol() == cricket::kMediaProtocolTcpDtlsSctp) {
1412       const bool cd2_is_also_dtls_sctp =
1413           cd2->protocol() == cricket::kMediaProtocolDtlsSctp ||
1414           cd2->protocol() == cricket::kMediaProtocolUdpDtlsSctp ||
1415           cd2->protocol() == cricket::kMediaProtocolTcpDtlsSctp;
1416       EXPECT_TRUE(cd2_is_also_dtls_sctp);
1417     } else {
1418       EXPECT_EQ(cd1->protocol(), cd2->protocol());
1419     }
1420 
1421     // codecs
1422     EXPECT_EQ(cd1->codecs(), cd2->codecs());
1423 
1424     // bandwidth
1425     EXPECT_EQ(cd1->bandwidth(), cd2->bandwidth());
1426 
1427     // streams
1428     EXPECT_EQ(cd1->streams(), cd2->streams());
1429 
1430     // extmap-allow-mixed
1431     EXPECT_EQ(cd1->extmap_allow_mixed_enum(), cd2->extmap_allow_mixed_enum());
1432 
1433     // extmap
1434     ASSERT_EQ(cd1->rtp_header_extensions().size(),
1435               cd2->rtp_header_extensions().size());
1436     for (size_t i = 0; i < cd1->rtp_header_extensions().size(); ++i) {
1437       const RtpExtension ext1 = cd1->rtp_header_extensions().at(i);
1438       const RtpExtension ext2 = cd2->rtp_header_extensions().at(i);
1439       EXPECT_EQ(ext1.uri, ext2.uri);
1440       EXPECT_EQ(ext1.id, ext2.id);
1441       EXPECT_EQ(ext1.encrypt, ext2.encrypt);
1442     }
1443   }
1444 
CompareRidDescriptionIds(const std::vector<RidDescription> & rids,const std::vector<std::string> & ids)1445   void CompareRidDescriptionIds(const std::vector<RidDescription>& rids,
1446                                 const std::vector<std::string>& ids) {
1447     // Order of elements does not matter, only equivalence of sets.
1448     EXPECT_EQ(rids.size(), ids.size());
1449     for (const std::string& id : ids) {
1450       EXPECT_EQ(1l, absl::c_count_if(rids, [id](const RidDescription& rid) {
1451                   return rid.rid == id;
1452                 }));
1453     }
1454   }
1455 
CompareSimulcastDescription(const SimulcastDescription & simulcast1,const SimulcastDescription & simulcast2)1456   void CompareSimulcastDescription(const SimulcastDescription& simulcast1,
1457                                    const SimulcastDescription& simulcast2) {
1458     EXPECT_EQ(simulcast1.send_layers().size(), simulcast2.send_layers().size());
1459     EXPECT_EQ(simulcast1.receive_layers().size(),
1460               simulcast2.receive_layers().size());
1461   }
1462 
CompareRtpDataContentDescription(const RtpDataContentDescription * dcd1,const RtpDataContentDescription * dcd2)1463   void CompareRtpDataContentDescription(const RtpDataContentDescription* dcd1,
1464                                         const RtpDataContentDescription* dcd2) {
1465     CompareMediaContentDescription<RtpDataContentDescription>(dcd1, dcd2);
1466   }
1467 
CompareSctpDataContentDescription(const SctpDataContentDescription * dcd1,const SctpDataContentDescription * dcd2)1468   void CompareSctpDataContentDescription(
1469       const SctpDataContentDescription* dcd1,
1470       const SctpDataContentDescription* dcd2) {
1471     EXPECT_EQ(dcd1->use_sctpmap(), dcd2->use_sctpmap());
1472     EXPECT_EQ(dcd1->port(), dcd2->port());
1473     EXPECT_EQ(dcd1->max_message_size(), dcd2->max_message_size());
1474   }
1475 
CompareSessionDescription(const SessionDescription & desc1,const SessionDescription & desc2)1476   void CompareSessionDescription(const SessionDescription& desc1,
1477                                  const SessionDescription& desc2) {
1478     // Compare content descriptions.
1479     if (desc1.contents().size() != desc2.contents().size()) {
1480       ADD_FAILURE();
1481       return;
1482     }
1483     for (size_t i = 0; i < desc1.contents().size(); ++i) {
1484       const cricket::ContentInfo& c1 = desc1.contents().at(i);
1485       const cricket::ContentInfo& c2 = desc2.contents().at(i);
1486       // ContentInfo properties.
1487       EXPECT_EQ(c1.name, c2.name);
1488       EXPECT_EQ(c1.type, c2.type);
1489       EXPECT_EQ(c1.rejected, c2.rejected);
1490       EXPECT_EQ(c1.bundle_only, c2.bundle_only);
1491 
1492       ASSERT_EQ(IsAudioContent(&c1), IsAudioContent(&c2));
1493       if (IsAudioContent(&c1)) {
1494         const AudioContentDescription* acd1 =
1495             c1.media_description()->as_audio();
1496         const AudioContentDescription* acd2 =
1497             c2.media_description()->as_audio();
1498         CompareMediaContentDescription<AudioContentDescription>(acd1, acd2);
1499       }
1500 
1501       ASSERT_EQ(IsVideoContent(&c1), IsVideoContent(&c2));
1502       if (IsVideoContent(&c1)) {
1503         const VideoContentDescription* vcd1 =
1504             c1.media_description()->as_video();
1505         const VideoContentDescription* vcd2 =
1506             c2.media_description()->as_video();
1507         CompareMediaContentDescription<VideoContentDescription>(vcd1, vcd2);
1508       }
1509 
1510       ASSERT_EQ(IsDataContent(&c1), IsDataContent(&c2));
1511       if (c1.media_description()->as_sctp()) {
1512         ASSERT_TRUE(c2.media_description()->as_sctp());
1513         const SctpDataContentDescription* scd1 =
1514             c1.media_description()->as_sctp();
1515         const SctpDataContentDescription* scd2 =
1516             c2.media_description()->as_sctp();
1517         CompareSctpDataContentDescription(scd1, scd2);
1518       } else {
1519         if (IsDataContent(&c1)) {
1520           const RtpDataContentDescription* dcd1 =
1521               c1.media_description()->as_rtp_data();
1522           const RtpDataContentDescription* dcd2 =
1523               c2.media_description()->as_rtp_data();
1524           CompareRtpDataContentDescription(dcd1, dcd2);
1525         }
1526       }
1527 
1528       CompareSimulcastDescription(
1529           c1.media_description()->simulcast_description(),
1530           c2.media_description()->simulcast_description());
1531       EXPECT_EQ(c1.media_description()->alt_protocol(),
1532                 c2.media_description()->alt_protocol());
1533     }
1534 
1535     // group
1536     const cricket::ContentGroups groups1 = desc1.groups();
1537     const cricket::ContentGroups groups2 = desc2.groups();
1538     EXPECT_EQ(groups1.size(), groups1.size());
1539     if (groups1.size() != groups2.size()) {
1540       ADD_FAILURE();
1541       return;
1542     }
1543     for (size_t i = 0; i < groups1.size(); ++i) {
1544       const cricket::ContentGroup group1 = groups1.at(i);
1545       const cricket::ContentGroup group2 = groups2.at(i);
1546       EXPECT_EQ(group1.semantics(), group2.semantics());
1547       const cricket::ContentNames names1 = group1.content_names();
1548       const cricket::ContentNames names2 = group2.content_names();
1549       EXPECT_EQ(names1.size(), names2.size());
1550       if (names1.size() != names2.size()) {
1551         ADD_FAILURE();
1552         return;
1553       }
1554       cricket::ContentNames::const_iterator iter1 = names1.begin();
1555       cricket::ContentNames::const_iterator iter2 = names2.begin();
1556       while (iter1 != names1.end()) {
1557         EXPECT_EQ(*iter1++, *iter2++);
1558       }
1559     }
1560 
1561     // transport info
1562     const cricket::TransportInfos transports1 = desc1.transport_infos();
1563     const cricket::TransportInfos transports2 = desc2.transport_infos();
1564     EXPECT_EQ(transports1.size(), transports2.size());
1565     if (transports1.size() != transports2.size()) {
1566       ADD_FAILURE();
1567       return;
1568     }
1569     for (size_t i = 0; i < transports1.size(); ++i) {
1570       const cricket::TransportInfo transport1 = transports1.at(i);
1571       const cricket::TransportInfo transport2 = transports2.at(i);
1572       EXPECT_EQ(transport1.content_name, transport2.content_name);
1573       EXPECT_EQ(transport1.description.ice_ufrag,
1574                 transport2.description.ice_ufrag);
1575       EXPECT_EQ(transport1.description.ice_pwd, transport2.description.ice_pwd);
1576       EXPECT_EQ(transport1.description.ice_mode,
1577                 transport2.description.ice_mode);
1578       if (transport1.description.identity_fingerprint) {
1579         EXPECT_EQ(*transport1.description.identity_fingerprint,
1580                   *transport2.description.identity_fingerprint);
1581       } else {
1582         EXPECT_EQ(transport1.description.identity_fingerprint.get(),
1583                   transport2.description.identity_fingerprint.get());
1584       }
1585       EXPECT_EQ(transport1.description.transport_options,
1586                 transport2.description.transport_options);
1587       EXPECT_EQ(transport1.description.opaque_parameters,
1588                 transport2.description.opaque_parameters);
1589     }
1590 
1591     // global attributes
1592     EXPECT_EQ(desc1.msid_supported(), desc2.msid_supported());
1593     EXPECT_EQ(desc1.extmap_allow_mixed(), desc2.extmap_allow_mixed());
1594   }
1595 
CompareSessionDescription(const JsepSessionDescription & desc1,const JsepSessionDescription & desc2)1596   bool CompareSessionDescription(const JsepSessionDescription& desc1,
1597                                  const JsepSessionDescription& desc2) {
1598     EXPECT_EQ(desc1.session_id(), desc2.session_id());
1599     EXPECT_EQ(desc1.session_version(), desc2.session_version());
1600     CompareSessionDescription(*desc1.description(), *desc2.description());
1601     if (desc1.number_of_mediasections() != desc2.number_of_mediasections())
1602       return false;
1603     for (size_t i = 0; i < desc1.number_of_mediasections(); ++i) {
1604       const IceCandidateCollection* cc1 = desc1.candidates(i);
1605       const IceCandidateCollection* cc2 = desc2.candidates(i);
1606       if (cc1->count() != cc2->count()) {
1607         ADD_FAILURE();
1608         return false;
1609       }
1610       for (size_t j = 0; j < cc1->count(); ++j) {
1611         const IceCandidateInterface* c1 = cc1->at(j);
1612         const IceCandidateInterface* c2 = cc2->at(j);
1613         EXPECT_EQ(c1->sdp_mid(), c2->sdp_mid());
1614         EXPECT_EQ(c1->sdp_mline_index(), c2->sdp_mline_index());
1615         EXPECT_TRUE(c1->candidate().IsEquivalent(c2->candidate()));
1616       }
1617     }
1618     return true;
1619   }
1620 
1621   // Disable the ice-ufrag and ice-pwd in given |sdp| message by replacing
1622   // them with invalid keywords so that the parser will just ignore them.
RemoveCandidateUfragPwd(std::string * sdp)1623   bool RemoveCandidateUfragPwd(std::string* sdp) {
1624     absl::StrReplaceAll(
1625         {{"a=ice-ufrag", "a=xice-ufrag"}, {"a=ice-pwd", "a=xice-pwd"}}, sdp);
1626     return true;
1627   }
1628 
1629   // 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)1630   bool UpdateCandidateUfragPwd(JsepSessionDescription* jdesc,
1631                                int mline_index,
1632                                const std::string& ufrag,
1633                                const std::string& pwd) {
1634     std::string content_name;
1635     if (mline_index == 0) {
1636       content_name = kAudioContentName;
1637     } else if (mline_index == 1) {
1638       content_name = kVideoContentName;
1639     } else {
1640       RTC_NOTREACHED();
1641     }
1642     TransportInfo transport_info(content_name,
1643                                  TransportDescription(ufrag, pwd));
1644     SessionDescription* desc =
1645         const_cast<SessionDescription*>(jdesc->description());
1646     desc->RemoveTransportInfoByName(content_name);
1647     desc->AddTransportInfo(transport_info);
1648     for (size_t i = 0; i < jdesc_.number_of_mediasections(); ++i) {
1649       const IceCandidateCollection* cc = jdesc_.candidates(i);
1650       for (size_t j = 0; j < cc->count(); ++j) {
1651         if (cc->at(j)->sdp_mline_index() == mline_index) {
1652           const_cast<Candidate&>(cc->at(j)->candidate()).set_username(ufrag);
1653           const_cast<Candidate&>(cc->at(j)->candidate()).set_password(pwd);
1654         }
1655       }
1656     }
1657     return true;
1658   }
1659 
AddIceOptions(const std::string & content_name,const std::vector<std::string> & transport_options)1660   void AddIceOptions(const std::string& content_name,
1661                      const std::vector<std::string>& transport_options) {
1662     ASSERT_TRUE(desc_.GetTransportInfoByName(content_name) != NULL);
1663     cricket::TransportInfo transport_info =
1664         *(desc_.GetTransportInfoByName(content_name));
1665     desc_.RemoveTransportInfoByName(content_name);
1666     transport_info.description.transport_options = transport_options;
1667     desc_.AddTransportInfo(transport_info);
1668   }
1669 
SetIceUfragPwd(const std::string & content_name,const std::string & ice_ufrag,const std::string & ice_pwd)1670   void SetIceUfragPwd(const std::string& content_name,
1671                       const std::string& ice_ufrag,
1672                       const std::string& ice_pwd) {
1673     ASSERT_TRUE(desc_.GetTransportInfoByName(content_name) != NULL);
1674     cricket::TransportInfo transport_info =
1675         *(desc_.GetTransportInfoByName(content_name));
1676     desc_.RemoveTransportInfoByName(content_name);
1677     transport_info.description.ice_ufrag = ice_ufrag;
1678     transport_info.description.ice_pwd = ice_pwd;
1679     desc_.AddTransportInfo(transport_info);
1680   }
1681 
AddOpaqueTransportParameters(const std::string & content_name,cricket::OpaqueTransportParameters params)1682   void AddOpaqueTransportParameters(const std::string& content_name,
1683                                     cricket::OpaqueTransportParameters params) {
1684     ASSERT_TRUE(desc_.GetTransportInfoByName(content_name) != NULL);
1685     cricket::TransportInfo info = *(desc_.GetTransportInfoByName(content_name));
1686     desc_.RemoveTransportInfoByName(content_name);
1687     info.description.opaque_parameters = params;
1688     desc_.AddTransportInfo(info);
1689   }
1690 
AddAltProtocol(const std::string & content_name,const std::string & alt_protocol)1691   void AddAltProtocol(const std::string& content_name,
1692                       const std::string& alt_protocol) {
1693     ASSERT_TRUE(desc_.GetTransportInfoByName(content_name) != NULL);
1694     cricket::MediaContentDescription* description =
1695         desc_.GetContentDescriptionByName(content_name);
1696     description->set_alt_protocol(alt_protocol);
1697   }
1698 
AddFingerprint()1699   void AddFingerprint() {
1700     desc_.RemoveTransportInfoByName(kAudioContentName);
1701     desc_.RemoveTransportInfoByName(kVideoContentName);
1702     rtc::SSLFingerprint fingerprint(rtc::DIGEST_SHA_1, kIdentityDigest);
1703     desc_.AddTransportInfo(TransportInfo(
1704         kAudioContentName,
1705         TransportDescription(std::vector<std::string>(), kUfragVoice, kPwdVoice,
1706                              cricket::ICEMODE_FULL,
1707                              cricket::CONNECTIONROLE_NONE, &fingerprint)));
1708     desc_.AddTransportInfo(TransportInfo(
1709         kVideoContentName,
1710         TransportDescription(std::vector<std::string>(), kUfragVideo, kPwdVideo,
1711                              cricket::ICEMODE_FULL,
1712                              cricket::CONNECTIONROLE_NONE, &fingerprint)));
1713   }
1714 
AddExtmap(bool encrypted)1715   void AddExtmap(bool encrypted) {
1716     audio_desc_ = new AudioContentDescription(*audio_desc_);
1717     video_desc_ = new VideoContentDescription(*video_desc_);
1718     audio_desc_->AddRtpHeaderExtension(
1719         RtpExtension(kExtmapUri, kExtmapId, encrypted));
1720     video_desc_->AddRtpHeaderExtension(
1721         RtpExtension(kExtmapUri, kExtmapId, encrypted));
1722     desc_.RemoveContentByName(kAudioContentName);
1723     desc_.RemoveContentByName(kVideoContentName);
1724     desc_.AddContent(kAudioContentName, MediaProtocolType::kRtp,
1725                      absl::WrapUnique(audio_desc_));
1726     desc_.AddContent(kVideoContentName, MediaProtocolType::kRtp,
1727                      absl::WrapUnique(video_desc_));
1728   }
1729 
RemoveCryptos()1730   void RemoveCryptos() {
1731     audio_desc_->set_cryptos(std::vector<CryptoParams>());
1732     video_desc_->set_cryptos(std::vector<CryptoParams>());
1733   }
1734 
1735   // Removes everything in StreamParams from the session description that is
1736   // used for a=ssrc lines.
RemoveSsrcSignalingFromStreamParams()1737   void RemoveSsrcSignalingFromStreamParams() {
1738     for (cricket::ContentInfo& content_info :
1739          jdesc_.description()->contents()) {
1740       // With Unified Plan there should be one StreamParams per m= section.
1741       StreamParams& stream =
1742           content_info.media_description()->mutable_streams()[0];
1743       stream.ssrcs.clear();
1744       stream.ssrc_groups.clear();
1745       stream.cname.clear();
1746     }
1747   }
1748 
1749   // Removes all a=ssrc lines from the SDP string, except for the
1750   // "a=ssrc:... cname:..." lines.
RemoveSsrcMsidLinesFromSdpString(std::string * sdp_string)1751   void RemoveSsrcMsidLinesFromSdpString(std::string* sdp_string) {
1752     const char kAttributeSsrc[] = "a=ssrc";
1753     const char kAttributeCname[] = "cname";
1754     size_t ssrc_line_pos = sdp_string->find(kAttributeSsrc);
1755     while (ssrc_line_pos != std::string::npos) {
1756       size_t beg_line_pos = sdp_string->rfind('\n', ssrc_line_pos);
1757       size_t end_line_pos = sdp_string->find('\n', ssrc_line_pos);
1758       size_t cname_pos = sdp_string->find(kAttributeCname, ssrc_line_pos);
1759       if (cname_pos == std::string::npos || cname_pos > end_line_pos) {
1760         // Only erase a=ssrc lines that don't contain "cname".
1761         sdp_string->erase(beg_line_pos, end_line_pos - beg_line_pos);
1762         ssrc_line_pos = sdp_string->find(kAttributeSsrc, beg_line_pos);
1763       } else {
1764         // Skip the "a=ssrc:... cname" line and find the next "a=ssrc" line.
1765         ssrc_line_pos = sdp_string->find(kAttributeSsrc, end_line_pos);
1766       }
1767     }
1768   }
1769 
1770   // Removes all a=ssrc lines from the SDP string.
RemoveSsrcLinesFromSdpString(std::string * sdp_string)1771   void RemoveSsrcLinesFromSdpString(std::string* sdp_string) {
1772     const char kAttributeSsrc[] = "a=ssrc";
1773     while (sdp_string->find(kAttributeSsrc) != std::string::npos) {
1774       size_t pos_ssrc_attribute = sdp_string->find(kAttributeSsrc);
1775       size_t beg_line_pos = sdp_string->rfind('\n', pos_ssrc_attribute);
1776       size_t end_line_pos = sdp_string->find('\n', pos_ssrc_attribute);
1777       sdp_string->erase(beg_line_pos, end_line_pos - beg_line_pos);
1778     }
1779   }
1780 
TestSerializeDirection(RtpTransceiverDirection direction)1781   bool TestSerializeDirection(RtpTransceiverDirection direction) {
1782     audio_desc_->set_direction(direction);
1783     video_desc_->set_direction(direction);
1784     std::string new_sdp = kSdpFullString;
1785     ReplaceDirection(direction, &new_sdp);
1786 
1787     if (!jdesc_.Initialize(desc_.Clone(), jdesc_.session_id(),
1788                            jdesc_.session_version())) {
1789       return false;
1790     }
1791     std::string message = webrtc::SdpSerialize(jdesc_);
1792     EXPECT_EQ(new_sdp, message);
1793     return true;
1794   }
1795 
TestSerializeRejected(bool audio_rejected,bool video_rejected)1796   bool TestSerializeRejected(bool audio_rejected, bool video_rejected) {
1797     audio_desc_ = new AudioContentDescription(*audio_desc_);
1798     video_desc_ = new VideoContentDescription(*video_desc_);
1799 
1800     desc_.RemoveContentByName(kAudioContentName);
1801     desc_.RemoveContentByName(kVideoContentName);
1802     desc_.AddContent(kAudioContentName, MediaProtocolType::kRtp, audio_rejected,
1803                      absl::WrapUnique(audio_desc_));
1804     desc_.AddContent(kVideoContentName, MediaProtocolType::kRtp, video_rejected,
1805                      absl::WrapUnique(video_desc_));
1806     SetIceUfragPwd(kAudioContentName, audio_rejected ? "" : kUfragVoice,
1807                    audio_rejected ? "" : kPwdVoice);
1808     SetIceUfragPwd(kVideoContentName, video_rejected ? "" : kUfragVideo,
1809                    video_rejected ? "" : kPwdVideo);
1810 
1811     std::string new_sdp = kSdpString;
1812     ReplaceRejected(audio_rejected, video_rejected, &new_sdp);
1813 
1814     JsepSessionDescription jdesc_no_candidates(kDummyType);
1815     MakeDescriptionWithoutCandidates(&jdesc_no_candidates);
1816     std::string message = webrtc::SdpSerialize(jdesc_no_candidates);
1817     EXPECT_EQ(new_sdp, message);
1818     return true;
1819   }
1820 
AddSctpDataChannel(bool use_sctpmap)1821   void AddSctpDataChannel(bool use_sctpmap) {
1822     std::unique_ptr<SctpDataContentDescription> data(
1823         new SctpDataContentDescription());
1824     sctp_desc_ = data.get();
1825     sctp_desc_->set_use_sctpmap(use_sctpmap);
1826     sctp_desc_->set_protocol(cricket::kMediaProtocolUdpDtlsSctp);
1827     sctp_desc_->set_port(kDefaultSctpPort);
1828     desc_.AddContent(kDataContentName, MediaProtocolType::kSctp,
1829                      std::move(data));
1830     desc_.AddTransportInfo(TransportInfo(
1831         kDataContentName, TransportDescription(kUfragData, kPwdData)));
1832   }
1833 
AddRtpDataChannel()1834   void AddRtpDataChannel() {
1835     std::unique_ptr<RtpDataContentDescription> data(
1836         new RtpDataContentDescription());
1837     data_desc_ = data.get();
1838 
1839     data_desc_->AddCodec(DataCodec(101, "google-data"));
1840     StreamParams data_stream;
1841     data_stream.id = kDataChannelMsid;
1842     data_stream.cname = kDataChannelCname;
1843     data_stream.set_stream_ids({kDataChannelLabel});
1844     data_stream.ssrcs.push_back(kDataChannelSsrc);
1845     data_desc_->AddStream(data_stream);
1846     data_desc_->AddCrypto(
1847         CryptoParams(1, "AES_CM_128_HMAC_SHA1_80",
1848                      "inline:FvLcvU2P3ZWmQxgPAgcDu7Zl9vftYElFOjEzhWs5", ""));
1849     data_desc_->set_protocol(cricket::kMediaProtocolSavpf);
1850     desc_.AddContent(kDataContentName, MediaProtocolType::kRtp,
1851                      std::move(data));
1852     desc_.AddTransportInfo(TransportInfo(
1853         kDataContentName, TransportDescription(kUfragData, kPwdData)));
1854   }
1855 
TestDeserializeDirection(RtpTransceiverDirection direction)1856   bool TestDeserializeDirection(RtpTransceiverDirection direction) {
1857     std::string new_sdp = kSdpFullString;
1858     ReplaceDirection(direction, &new_sdp);
1859     JsepSessionDescription new_jdesc(kDummyType);
1860 
1861     EXPECT_TRUE(SdpDeserialize(new_sdp, &new_jdesc));
1862 
1863     audio_desc_->set_direction(direction);
1864     video_desc_->set_direction(direction);
1865     if (!jdesc_.Initialize(desc_.Clone(), jdesc_.session_id(),
1866                            jdesc_.session_version())) {
1867       return false;
1868     }
1869     EXPECT_TRUE(CompareSessionDescription(jdesc_, new_jdesc));
1870     return true;
1871   }
1872 
TestDeserializeRejected(bool audio_rejected,bool video_rejected)1873   bool TestDeserializeRejected(bool audio_rejected, bool video_rejected) {
1874     std::string new_sdp = kSdpString;
1875     ReplaceRejected(audio_rejected, video_rejected, &new_sdp);
1876     JsepSessionDescription new_jdesc(SdpType::kOffer);
1877     EXPECT_TRUE(SdpDeserialize(new_sdp, &new_jdesc));
1878 
1879     audio_desc_ = new AudioContentDescription(*audio_desc_);
1880     video_desc_ = new VideoContentDescription(*video_desc_);
1881     desc_.RemoveContentByName(kAudioContentName);
1882     desc_.RemoveContentByName(kVideoContentName);
1883     desc_.AddContent(kAudioContentName, MediaProtocolType::kRtp, audio_rejected,
1884                      absl::WrapUnique(audio_desc_));
1885     desc_.AddContent(kVideoContentName, MediaProtocolType::kRtp, video_rejected,
1886                      absl::WrapUnique(video_desc_));
1887     SetIceUfragPwd(kAudioContentName, audio_rejected ? "" : kUfragVoice,
1888                    audio_rejected ? "" : kPwdVoice);
1889     SetIceUfragPwd(kVideoContentName, video_rejected ? "" : kUfragVideo,
1890                    video_rejected ? "" : kPwdVideo);
1891     JsepSessionDescription jdesc_no_candidates(kDummyType);
1892     if (!jdesc_no_candidates.Initialize(desc_.Clone(), jdesc_.session_id(),
1893                                         jdesc_.session_version())) {
1894       return false;
1895     }
1896     EXPECT_TRUE(CompareSessionDescription(jdesc_no_candidates, new_jdesc));
1897     return true;
1898   }
1899 
TestDeserializeExtmap(bool session_level,bool media_level,bool encrypted)1900   void TestDeserializeExtmap(bool session_level,
1901                              bool media_level,
1902                              bool encrypted) {
1903     AddExtmap(encrypted);
1904     JsepSessionDescription new_jdesc(SdpType::kOffer);
1905     ASSERT_TRUE(new_jdesc.Initialize(desc_.Clone(), jdesc_.session_id(),
1906                                      jdesc_.session_version()));
1907     JsepSessionDescription jdesc_with_extmap(SdpType::kOffer);
1908     std::string sdp_with_extmap = kSdpString;
1909     if (session_level) {
1910       InjectAfter(kSessionTime,
1911                   encrypted ? kExtmapWithDirectionAndAttributeEncrypted
1912                             : kExtmapWithDirectionAndAttribute,
1913                   &sdp_with_extmap);
1914     }
1915     if (media_level) {
1916       InjectAfter(kAttributeIcePwdVoice,
1917                   encrypted ? kExtmapWithDirectionAndAttributeEncrypted
1918                             : kExtmapWithDirectionAndAttribute,
1919                   &sdp_with_extmap);
1920       InjectAfter(kAttributeIcePwdVideo,
1921                   encrypted ? kExtmapWithDirectionAndAttributeEncrypted
1922                             : kExtmapWithDirectionAndAttribute,
1923                   &sdp_with_extmap);
1924     }
1925     // The extmap can't be present at the same time in both session level and
1926     // media level.
1927     if (session_level && media_level) {
1928       SdpParseError error;
1929       EXPECT_FALSE(
1930           webrtc::SdpDeserialize(sdp_with_extmap, &jdesc_with_extmap, &error));
1931       EXPECT_NE(std::string::npos, error.description.find("a=extmap"));
1932     } else {
1933       EXPECT_TRUE(SdpDeserialize(sdp_with_extmap, &jdesc_with_extmap));
1934       EXPECT_TRUE(CompareSessionDescription(jdesc_with_extmap, new_jdesc));
1935     }
1936   }
1937 
VerifyCodecParameter(const cricket::CodecParameterMap & params,const std::string & name,int expected_value)1938   void VerifyCodecParameter(const cricket::CodecParameterMap& params,
1939                             const std::string& name,
1940                             int expected_value) {
1941     cricket::CodecParameterMap::const_iterator found = params.find(name);
1942     ASSERT_TRUE(found != params.end());
1943     EXPECT_EQ(found->second, rtc::ToString(expected_value));
1944   }
1945 
TestDeserializeCodecParams(const CodecParams & params,JsepSessionDescription * jdesc_output)1946   void TestDeserializeCodecParams(const CodecParams& params,
1947                                   JsepSessionDescription* jdesc_output) {
1948     std::string sdp =
1949         "v=0\r\n"
1950         "o=- 18446744069414584320 18446462598732840960 IN IP4 127.0.0.1\r\n"
1951         "s=-\r\n"
1952         "t=0 0\r\n"
1953         // Include semantics for WebRTC Media Streams since it is supported by
1954         // this parser, and will be added to the SDP when serializing a session
1955         // description.
1956         "a=msid-semantic: WMS\r\n"
1957         // Pl type 111 preferred.
1958         "m=audio 9 RTP/SAVPF 111 104 103\r\n"
1959         // Pltype 111 listed before 103 and 104 in the map.
1960         "a=rtpmap:111 opus/48000/2\r\n"
1961         // Pltype 103 listed before 104.
1962         "a=rtpmap:103 ISAC/16000\r\n"
1963         "a=rtpmap:104 ISAC/32000\r\n"
1964         "a=fmtp:111 0-15,66,70\r\n"
1965         "a=fmtp:111 ";
1966     std::ostringstream os;
1967     os << "minptime=" << params.min_ptime << "; stereo=" << params.stereo
1968        << "; sprop-stereo=" << params.sprop_stereo
1969        << "; useinbandfec=" << params.useinband
1970        << "; maxaveragebitrate=" << params.maxaveragebitrate
1971        << "\r\n"
1972           "a=ptime:"
1973        << params.ptime
1974        << "\r\n"
1975           "a=maxptime:"
1976        << params.max_ptime << "\r\n";
1977     sdp += os.str();
1978 
1979     os.clear();
1980     os.str("");
1981     // Pl type 100 preferred.
1982     os << "m=video 9 RTP/SAVPF 99 95\r\n"
1983           "a=rtpmap:99 VP8/90000\r\n"
1984           "a=rtpmap:95 RTX/90000\r\n"
1985           "a=fmtp:95 apt=99;\r\n";
1986     sdp += os.str();
1987 
1988     // Deserialize
1989     SdpParseError error;
1990     EXPECT_TRUE(webrtc::SdpDeserialize(sdp, jdesc_output, &error));
1991 
1992     const AudioContentDescription* acd =
1993         GetFirstAudioContentDescription(jdesc_output->description());
1994     ASSERT_TRUE(acd);
1995     ASSERT_FALSE(acd->codecs().empty());
1996     cricket::AudioCodec opus = acd->codecs()[0];
1997     EXPECT_EQ("opus", opus.name);
1998     EXPECT_EQ(111, opus.id);
1999     VerifyCodecParameter(opus.params, "minptime", params.min_ptime);
2000     VerifyCodecParameter(opus.params, "stereo", params.stereo);
2001     VerifyCodecParameter(opus.params, "sprop-stereo", params.sprop_stereo);
2002     VerifyCodecParameter(opus.params, "useinbandfec", params.useinband);
2003     VerifyCodecParameter(opus.params, "maxaveragebitrate",
2004                          params.maxaveragebitrate);
2005     for (size_t i = 0; i < acd->codecs().size(); ++i) {
2006       cricket::AudioCodec codec = acd->codecs()[i];
2007       VerifyCodecParameter(codec.params, "ptime", params.ptime);
2008       VerifyCodecParameter(codec.params, "maxptime", params.max_ptime);
2009     }
2010 
2011     const VideoContentDescription* vcd =
2012         GetFirstVideoContentDescription(jdesc_output->description());
2013     ASSERT_TRUE(vcd);
2014     ASSERT_FALSE(vcd->codecs().empty());
2015     cricket::VideoCodec vp8 = vcd->codecs()[0];
2016     EXPECT_EQ("VP8", vp8.name);
2017     EXPECT_EQ(99, vp8.id);
2018     cricket::VideoCodec rtx = vcd->codecs()[1];
2019     EXPECT_EQ("RTX", rtx.name);
2020     EXPECT_EQ(95, rtx.id);
2021     VerifyCodecParameter(rtx.params, "apt", vp8.id);
2022   }
2023 
TestDeserializeRtcpFb(JsepSessionDescription * jdesc_output,bool use_wildcard)2024   void TestDeserializeRtcpFb(JsepSessionDescription* jdesc_output,
2025                              bool use_wildcard) {
2026     std::string sdp_session_and_audio =
2027         "v=0\r\n"
2028         "o=- 18446744069414584320 18446462598732840960 IN IP4 127.0.0.1\r\n"
2029         "s=-\r\n"
2030         "t=0 0\r\n"
2031         // Include semantics for WebRTC Media Streams since it is supported by
2032         // this parser, and will be added to the SDP when serializing a session
2033         // description.
2034         "a=msid-semantic: WMS\r\n"
2035         "m=audio 9 RTP/SAVPF 111\r\n"
2036         "a=rtpmap:111 opus/48000/2\r\n";
2037     std::string sdp_video =
2038         "m=video 3457 RTP/SAVPF 101\r\n"
2039         "a=rtpmap:101 VP8/90000\r\n"
2040         "a=rtcp-fb:101 goog-lntf\r\n"
2041         "a=rtcp-fb:101 nack\r\n"
2042         "a=rtcp-fb:101 nack pli\r\n"
2043         "a=rtcp-fb:101 goog-remb\r\n";
2044     std::ostringstream os;
2045     os << sdp_session_and_audio;
2046     os << "a=rtcp-fb:" << (use_wildcard ? "*" : "111") << " nack\r\n";
2047     os << sdp_video;
2048     os << "a=rtcp-fb:" << (use_wildcard ? "*" : "101") << " ccm fir\r\n";
2049     std::string sdp = os.str();
2050     // Deserialize
2051     SdpParseError error;
2052     EXPECT_TRUE(webrtc::SdpDeserialize(sdp, jdesc_output, &error));
2053     const AudioContentDescription* acd =
2054         GetFirstAudioContentDescription(jdesc_output->description());
2055     ASSERT_TRUE(acd);
2056     ASSERT_FALSE(acd->codecs().empty());
2057     cricket::AudioCodec opus = acd->codecs()[0];
2058     EXPECT_EQ(111, opus.id);
2059     EXPECT_TRUE(opus.HasFeedbackParam(cricket::FeedbackParam(
2060         cricket::kRtcpFbParamNack, cricket::kParamValueEmpty)));
2061 
2062     const VideoContentDescription* vcd =
2063         GetFirstVideoContentDescription(jdesc_output->description());
2064     ASSERT_TRUE(vcd);
2065     ASSERT_FALSE(vcd->codecs().empty());
2066     cricket::VideoCodec vp8 = vcd->codecs()[0];
2067     EXPECT_STREQ(webrtc::JsepSessionDescription::kDefaultVideoCodecName,
2068                  vp8.name.c_str());
2069     EXPECT_EQ(101, vp8.id);
2070     EXPECT_TRUE(vp8.HasFeedbackParam(cricket::FeedbackParam(
2071         cricket::kRtcpFbParamLntf, cricket::kParamValueEmpty)));
2072     EXPECT_TRUE(vp8.HasFeedbackParam(cricket::FeedbackParam(
2073         cricket::kRtcpFbParamNack, cricket::kParamValueEmpty)));
2074     EXPECT_TRUE(vp8.HasFeedbackParam(cricket::FeedbackParam(
2075         cricket::kRtcpFbParamNack, cricket::kRtcpFbNackParamPli)));
2076     EXPECT_TRUE(vp8.HasFeedbackParam(cricket::FeedbackParam(
2077         cricket::kRtcpFbParamRemb, cricket::kParamValueEmpty)));
2078     EXPECT_TRUE(vp8.HasFeedbackParam(cricket::FeedbackParam(
2079         cricket::kRtcpFbParamCcm, cricket::kRtcpFbCcmParamFir)));
2080   }
2081 
2082   // Two SDP messages can mean the same thing but be different strings, e.g.
2083   // some of the lines can be serialized in different order.
2084   // However, a deserialized description can be compared field by field and has
2085   // no order. If deserializer has already been tested, serializing then
2086   // deserializing and comparing JsepSessionDescription will test
2087   // the serializer sufficiently.
TestSerialize(const JsepSessionDescription & jdesc)2088   void TestSerialize(const JsepSessionDescription& jdesc) {
2089     std::string message = webrtc::SdpSerialize(jdesc);
2090     JsepSessionDescription jdesc_output_des(kDummyType);
2091     SdpParseError error;
2092     EXPECT_TRUE(webrtc::SdpDeserialize(message, &jdesc_output_des, &error));
2093     EXPECT_TRUE(CompareSessionDescription(jdesc, jdesc_output_des));
2094   }
2095 
2096   // Calling 'Initialize' with a copy of the inner SessionDescription will
2097   // create a copy of the JsepSessionDescription without candidates. The
2098   // 'connection address' field, previously set from the candidates, must also
2099   // be reset.
MakeDescriptionWithoutCandidates(JsepSessionDescription * jdesc)2100   void MakeDescriptionWithoutCandidates(JsepSessionDescription* jdesc) {
2101     rtc::SocketAddress audio_addr("0.0.0.0", 9);
2102     rtc::SocketAddress video_addr("0.0.0.0", 9);
2103     audio_desc_->set_connection_address(audio_addr);
2104     video_desc_->set_connection_address(video_addr);
2105     ASSERT_TRUE(jdesc->Initialize(desc_.Clone(), kSessionId, kSessionVersion));
2106   }
2107 
2108  protected:
2109   SessionDescription desc_;
2110   AudioContentDescription* audio_desc_;
2111   VideoContentDescription* video_desc_;
2112   RtpDataContentDescription* data_desc_;
2113   SctpDataContentDescription* sctp_desc_;
2114   Candidates candidates_;
2115   std::unique_ptr<IceCandidateInterface> jcandidate_;
2116   JsepSessionDescription jdesc_;
2117 };
2118 
TestMismatch(const std::string & string1,const std::string & string2)2119 void TestMismatch(const std::string& string1, const std::string& string2) {
2120   int position = 0;
2121   for (size_t i = 0; i < string1.length() && i < string2.length(); ++i) {
2122     if (string1.c_str()[i] != string2.c_str()[i]) {
2123       position = static_cast<int>(i);
2124       break;
2125     }
2126   }
2127   EXPECT_EQ(0, position) << "Strings mismatch at the " << position
2128                          << " character\n"
2129                             " 1: "
2130                          << string1.substr(position, 20)
2131                          << "\n"
2132                             " 2: "
2133                          << string2.substr(position, 20) << "\n";
2134 }
2135 
TEST_F(WebRtcSdpTest,SerializeSessionDescription)2136 TEST_F(WebRtcSdpTest, SerializeSessionDescription) {
2137   // SessionDescription with desc and candidates.
2138   std::string message = webrtc::SdpSerialize(jdesc_);
2139   TestMismatch(std::string(kSdpFullString), message);
2140 }
2141 
TEST_F(WebRtcSdpTest,SerializeSessionDescriptionEmpty)2142 TEST_F(WebRtcSdpTest, SerializeSessionDescriptionEmpty) {
2143   JsepSessionDescription jdesc_empty(kDummyType);
2144   EXPECT_EQ("", webrtc::SdpSerialize(jdesc_empty));
2145 }
2146 
2147 // This tests serialization of SDP with a=crypto and a=fingerprint, as would be
2148 // the case in a DTLS offer.
TEST_F(WebRtcSdpTest,SerializeSessionDescriptionWithFingerprint)2149 TEST_F(WebRtcSdpTest, SerializeSessionDescriptionWithFingerprint) {
2150   AddFingerprint();
2151   JsepSessionDescription jdesc_with_fingerprint(kDummyType);
2152   MakeDescriptionWithoutCandidates(&jdesc_with_fingerprint);
2153   std::string message = webrtc::SdpSerialize(jdesc_with_fingerprint);
2154 
2155   std::string sdp_with_fingerprint = kSdpString;
2156   InjectAfter(kAttributeIcePwdVoice, kFingerprint, &sdp_with_fingerprint);
2157   InjectAfter(kAttributeIcePwdVideo, kFingerprint, &sdp_with_fingerprint);
2158 
2159   EXPECT_EQ(sdp_with_fingerprint, message);
2160 }
2161 
2162 // This tests serialization of SDP with a=fingerprint with no a=crypto, as would
2163 // be the case in a DTLS answer.
TEST_F(WebRtcSdpTest,SerializeSessionDescriptionWithFingerprintNoCryptos)2164 TEST_F(WebRtcSdpTest, SerializeSessionDescriptionWithFingerprintNoCryptos) {
2165   AddFingerprint();
2166   RemoveCryptos();
2167   JsepSessionDescription jdesc_with_fingerprint(kDummyType);
2168   MakeDescriptionWithoutCandidates(&jdesc_with_fingerprint);
2169   std::string message = webrtc::SdpSerialize(jdesc_with_fingerprint);
2170 
2171   std::string sdp_with_fingerprint = kSdpString;
2172   Replace(kAttributeCryptoVoice, "", &sdp_with_fingerprint);
2173   Replace(kAttributeCryptoVideo, "", &sdp_with_fingerprint);
2174   InjectAfter(kAttributeIcePwdVoice, kFingerprint, &sdp_with_fingerprint);
2175   InjectAfter(kAttributeIcePwdVideo, kFingerprint, &sdp_with_fingerprint);
2176 
2177   EXPECT_EQ(sdp_with_fingerprint, message);
2178 }
2179 
TEST_F(WebRtcSdpTest,SerializeSessionDescriptionWithoutCandidates)2180 TEST_F(WebRtcSdpTest, SerializeSessionDescriptionWithoutCandidates) {
2181   // JsepSessionDescription with desc but without candidates.
2182   JsepSessionDescription jdesc_no_candidates(kDummyType);
2183   MakeDescriptionWithoutCandidates(&jdesc_no_candidates);
2184   std::string message = webrtc::SdpSerialize(jdesc_no_candidates);
2185   EXPECT_EQ(std::string(kSdpString), message);
2186 }
2187 
TEST_F(WebRtcSdpTest,SerializeSessionDescriptionWithBundle)2188 TEST_F(WebRtcSdpTest, SerializeSessionDescriptionWithBundle) {
2189   ContentGroup group(cricket::GROUP_TYPE_BUNDLE);
2190   group.AddContentName(kAudioContentName);
2191   group.AddContentName(kVideoContentName);
2192   desc_.AddGroup(group);
2193   ASSERT_TRUE(jdesc_.Initialize(desc_.Clone(), jdesc_.session_id(),
2194                                 jdesc_.session_version()));
2195   std::string message = webrtc::SdpSerialize(jdesc_);
2196   std::string sdp_with_bundle = kSdpFullString;
2197   InjectAfter(kSessionTime,
2198               "a=group:BUNDLE audio_content_name video_content_name\r\n",
2199               &sdp_with_bundle);
2200   EXPECT_EQ(sdp_with_bundle, message);
2201 }
2202 
TEST_F(WebRtcSdpTest,SerializeSessionDescriptionWithBandwidth)2203 TEST_F(WebRtcSdpTest, SerializeSessionDescriptionWithBandwidth) {
2204   VideoContentDescription* vcd = GetFirstVideoContentDescription(&desc_);
2205   vcd->set_bandwidth(100 * 1000);
2206   AudioContentDescription* acd = GetFirstAudioContentDescription(&desc_);
2207   acd->set_bandwidth(50 * 1000);
2208   ASSERT_TRUE(jdesc_.Initialize(desc_.Clone(), jdesc_.session_id(),
2209                                 jdesc_.session_version()));
2210   std::string message = webrtc::SdpSerialize(jdesc_);
2211   std::string sdp_with_bandwidth = kSdpFullString;
2212   InjectAfter("c=IN IP4 74.125.224.39\r\n", "b=AS:100\r\n",
2213               &sdp_with_bandwidth);
2214   InjectAfter("c=IN IP4 74.125.127.126\r\n", "b=AS:50\r\n",
2215               &sdp_with_bandwidth);
2216   EXPECT_EQ(sdp_with_bandwidth, message);
2217 }
2218 
TEST_F(WebRtcSdpTest,SerializeSessionDescriptionWithIceOptions)2219 TEST_F(WebRtcSdpTest, SerializeSessionDescriptionWithIceOptions) {
2220   std::vector<std::string> transport_options;
2221   transport_options.push_back(kIceOption1);
2222   transport_options.push_back(kIceOption3);
2223   AddIceOptions(kAudioContentName, transport_options);
2224   transport_options.clear();
2225   transport_options.push_back(kIceOption2);
2226   transport_options.push_back(kIceOption3);
2227   AddIceOptions(kVideoContentName, transport_options);
2228   ASSERT_TRUE(jdesc_.Initialize(desc_.Clone(), jdesc_.session_id(),
2229                                 jdesc_.session_version()));
2230   std::string message = webrtc::SdpSerialize(jdesc_);
2231   std::string sdp_with_ice_options = kSdpFullString;
2232   InjectAfter(kAttributeIcePwdVoice, "a=ice-options:iceoption1 iceoption3\r\n",
2233               &sdp_with_ice_options);
2234   InjectAfter(kAttributeIcePwdVideo, "a=ice-options:iceoption2 iceoption3\r\n",
2235               &sdp_with_ice_options);
2236   EXPECT_EQ(sdp_with_ice_options, message);
2237 }
2238 
TEST_F(WebRtcSdpTest,SerializeSessionDescriptionWithOpaqueTransportParams)2239 TEST_F(WebRtcSdpTest, SerializeSessionDescriptionWithOpaqueTransportParams) {
2240   cricket::OpaqueTransportParameters params;
2241   params.protocol = "foo";
2242   params.parameters = "test64";
2243   AddOpaqueTransportParameters(kAudioContentName, params);
2244   AddOpaqueTransportParameters(kVideoContentName, params);
2245 
2246   ASSERT_TRUE(jdesc_.Initialize(desc_.Clone(), jdesc_.session_id(),
2247                                 jdesc_.session_version()));
2248   std::string message = webrtc::SdpSerialize(jdesc_);
2249 
2250   std::string sdp_with_transport_parameters = kSdpFullString;
2251   InjectAfter(kAttributeIcePwdVoice, "a=x-opaque:foo:dGVzdDY0\r\n",
2252               &sdp_with_transport_parameters);
2253   InjectAfter(kAttributeIcePwdVideo, "a=x-opaque:foo:dGVzdDY0\r\n",
2254               &sdp_with_transport_parameters);
2255   EXPECT_EQ(message, sdp_with_transport_parameters);
2256 }
2257 
TEST_F(WebRtcSdpTest,SerializeSessionDescriptionWithAltProtocol)2258 TEST_F(WebRtcSdpTest, SerializeSessionDescriptionWithAltProtocol) {
2259   AddAltProtocol(kAudioContentName, "foo");
2260   AddAltProtocol(kVideoContentName, "bar");
2261 
2262   ASSERT_TRUE(jdesc_.Initialize(desc_.Clone(), jdesc_.session_id(),
2263                                 jdesc_.session_version()));
2264   std::string message = webrtc::SdpSerialize(jdesc_);
2265 
2266   std::string sdp_with_alt_protocol = kSdpFullString;
2267   InjectAfter(kAttributeIcePwdVoice, "a=x-alt-protocol:foo\r\n",
2268               &sdp_with_alt_protocol);
2269   InjectAfter(kAttributeIcePwdVideo, "a=x-alt-protocol:bar\r\n",
2270               &sdp_with_alt_protocol);
2271   EXPECT_EQ(message, sdp_with_alt_protocol);
2272 }
2273 
TEST_F(WebRtcSdpTest,SerializeSessionDescriptionWithRecvOnlyContent)2274 TEST_F(WebRtcSdpTest, SerializeSessionDescriptionWithRecvOnlyContent) {
2275   EXPECT_TRUE(TestSerializeDirection(RtpTransceiverDirection::kRecvOnly));
2276 }
2277 
TEST_F(WebRtcSdpTest,SerializeSessionDescriptionWithSendOnlyContent)2278 TEST_F(WebRtcSdpTest, SerializeSessionDescriptionWithSendOnlyContent) {
2279   EXPECT_TRUE(TestSerializeDirection(RtpTransceiverDirection::kSendOnly));
2280 }
2281 
TEST_F(WebRtcSdpTest,SerializeSessionDescriptionWithInactiveContent)2282 TEST_F(WebRtcSdpTest, SerializeSessionDescriptionWithInactiveContent) {
2283   EXPECT_TRUE(TestSerializeDirection(RtpTransceiverDirection::kInactive));
2284 }
2285 
TEST_F(WebRtcSdpTest,SerializeSessionDescriptionWithAudioRejected)2286 TEST_F(WebRtcSdpTest, SerializeSessionDescriptionWithAudioRejected) {
2287   EXPECT_TRUE(TestSerializeRejected(true, false));
2288 }
2289 
TEST_F(WebRtcSdpTest,SerializeSessionDescriptionWithVideoRejected)2290 TEST_F(WebRtcSdpTest, SerializeSessionDescriptionWithVideoRejected) {
2291   EXPECT_TRUE(TestSerializeRejected(false, true));
2292 }
2293 
TEST_F(WebRtcSdpTest,SerializeSessionDescriptionWithAudioVideoRejected)2294 TEST_F(WebRtcSdpTest, SerializeSessionDescriptionWithAudioVideoRejected) {
2295   EXPECT_TRUE(TestSerializeRejected(true, true));
2296 }
2297 
TEST_F(WebRtcSdpTest,SerializeSessionDescriptionWithRtpDataChannel)2298 TEST_F(WebRtcSdpTest, SerializeSessionDescriptionWithRtpDataChannel) {
2299   AddRtpDataChannel();
2300   JsepSessionDescription jsep_desc(kDummyType);
2301 
2302   MakeDescriptionWithoutCandidates(&jsep_desc);
2303   std::string message = webrtc::SdpSerialize(jsep_desc);
2304 
2305   std::string expected_sdp = kSdpString;
2306   expected_sdp.append(kSdpRtpDataChannelString);
2307   EXPECT_EQ(expected_sdp, message);
2308 }
2309 
TEST_F(WebRtcSdpTest,SerializeSessionDescriptionWithSctpDataChannel)2310 TEST_F(WebRtcSdpTest, SerializeSessionDescriptionWithSctpDataChannel) {
2311   bool use_sctpmap = true;
2312   AddSctpDataChannel(use_sctpmap);
2313   JsepSessionDescription jsep_desc(kDummyType);
2314 
2315   MakeDescriptionWithoutCandidates(&jsep_desc);
2316   std::string message = webrtc::SdpSerialize(jsep_desc);
2317 
2318   std::string expected_sdp = kSdpString;
2319   expected_sdp.append(kSdpSctpDataChannelString);
2320   EXPECT_EQ(message, expected_sdp);
2321 }
2322 
MutateJsepSctpPort(JsepSessionDescription * jdesc,const SessionDescription & desc,int port)2323 void MutateJsepSctpPort(JsepSessionDescription* jdesc,
2324                         const SessionDescription& desc,
2325                         int port) {
2326   // Take our pre-built session description and change the SCTP port.
2327   std::unique_ptr<cricket::SessionDescription> mutant = desc.Clone();
2328   SctpDataContentDescription* dcdesc =
2329       mutant->GetContentDescriptionByName(kDataContentName)->as_sctp();
2330   dcdesc->set_port(port);
2331   ASSERT_TRUE(
2332       jdesc->Initialize(std::move(mutant), kSessionId, kSessionVersion));
2333 }
2334 
TEST_F(WebRtcSdpTest,SerializeWithSctpDataChannelAndNewPort)2335 TEST_F(WebRtcSdpTest, SerializeWithSctpDataChannelAndNewPort) {
2336   bool use_sctpmap = true;
2337   AddSctpDataChannel(use_sctpmap);
2338   JsepSessionDescription jsep_desc(kDummyType);
2339   MakeDescriptionWithoutCandidates(&jsep_desc);
2340 
2341   const int kNewPort = 1234;
2342   MutateJsepSctpPort(&jsep_desc, desc_, kNewPort);
2343 
2344   std::string message = webrtc::SdpSerialize(jsep_desc);
2345 
2346   std::string expected_sdp = kSdpString;
2347   expected_sdp.append(kSdpSctpDataChannelString);
2348 
2349   absl::StrReplaceAll(
2350       {{rtc::ToString(kDefaultSctpPort), rtc::ToString(kNewPort)}},
2351       &expected_sdp);
2352 
2353   EXPECT_EQ(expected_sdp, message);
2354 }
2355 
TEST_F(WebRtcSdpTest,SerializeSessionDescriptionWithDataChannelAndBandwidth)2356 TEST_F(WebRtcSdpTest, SerializeSessionDescriptionWithDataChannelAndBandwidth) {
2357   JsepSessionDescription jsep_desc(kDummyType);
2358   AddRtpDataChannel();
2359   data_desc_->set_bandwidth(100 * 1000);
2360   MakeDescriptionWithoutCandidates(&jsep_desc);
2361   std::string message = webrtc::SdpSerialize(jsep_desc);
2362 
2363   std::string expected_sdp = kSdpString;
2364   expected_sdp.append(kSdpRtpDataChannelString);
2365   // Serializing data content shouldn't ignore bandwidth settings.
2366   InjectAfter("m=application 9 RTP/SAVPF 101\r\nc=IN IP4 0.0.0.0\r\n",
2367               "b=AS:100\r\n", &expected_sdp);
2368   EXPECT_EQ(expected_sdp, message);
2369 }
2370 
TEST_F(WebRtcSdpTest,SerializeSessionDescriptionWithExtmapAllowMixed)2371 TEST_F(WebRtcSdpTest, SerializeSessionDescriptionWithExtmapAllowMixed) {
2372   jdesc_.description()->set_extmap_allow_mixed(true);
2373   TestSerialize(jdesc_);
2374 }
2375 
TEST_F(WebRtcSdpTest,SerializeMediaContentDescriptionWithExtmapAllowMixed)2376 TEST_F(WebRtcSdpTest, SerializeMediaContentDescriptionWithExtmapAllowMixed) {
2377   cricket::MediaContentDescription* video_desc =
2378       jdesc_.description()->GetContentDescriptionByName(kVideoContentName);
2379   ASSERT_TRUE(video_desc);
2380   cricket::MediaContentDescription* audio_desc =
2381       jdesc_.description()->GetContentDescriptionByName(kAudioContentName);
2382   ASSERT_TRUE(audio_desc);
2383   video_desc->set_extmap_allow_mixed_enum(
2384       cricket::MediaContentDescription::kMedia);
2385   audio_desc->set_extmap_allow_mixed_enum(
2386       cricket::MediaContentDescription::kMedia);
2387   TestSerialize(jdesc_);
2388 }
2389 
TEST_F(WebRtcSdpTest,SerializeSessionDescriptionWithExtmap)2390 TEST_F(WebRtcSdpTest, SerializeSessionDescriptionWithExtmap) {
2391   bool encrypted = false;
2392   AddExtmap(encrypted);
2393   JsepSessionDescription desc_with_extmap(kDummyType);
2394   MakeDescriptionWithoutCandidates(&desc_with_extmap);
2395   std::string message = webrtc::SdpSerialize(desc_with_extmap);
2396 
2397   std::string sdp_with_extmap = kSdpString;
2398   InjectAfter("a=mid:audio_content_name\r\n", kExtmap, &sdp_with_extmap);
2399   InjectAfter("a=mid:video_content_name\r\n", kExtmap, &sdp_with_extmap);
2400 
2401   EXPECT_EQ(sdp_with_extmap, message);
2402 }
2403 
TEST_F(WebRtcSdpTest,SerializeSessionDescriptionWithExtmapEncrypted)2404 TEST_F(WebRtcSdpTest, SerializeSessionDescriptionWithExtmapEncrypted) {
2405   bool encrypted = true;
2406   AddExtmap(encrypted);
2407   JsepSessionDescription desc_with_extmap(kDummyType);
2408   ASSERT_TRUE(
2409       desc_with_extmap.Initialize(desc_.Clone(), kSessionId, kSessionVersion));
2410   TestSerialize(desc_with_extmap);
2411 }
2412 
TEST_F(WebRtcSdpTest,SerializeCandidates)2413 TEST_F(WebRtcSdpTest, SerializeCandidates) {
2414   std::string message = webrtc::SdpSerializeCandidate(*jcandidate_);
2415   EXPECT_EQ(std::string(kRawCandidate), message);
2416 
2417   Candidate candidate_with_ufrag(candidates_.front());
2418   candidate_with_ufrag.set_username("ABC");
2419   jcandidate_.reset(new JsepIceCandidate(std::string("audio_content_name"), 0,
2420                                          candidate_with_ufrag));
2421   message = webrtc::SdpSerializeCandidate(*jcandidate_);
2422   EXPECT_EQ(std::string(kRawCandidate) + " ufrag ABC", message);
2423 
2424   Candidate candidate_with_network_info(candidates_.front());
2425   candidate_with_network_info.set_network_id(1);
2426   jcandidate_.reset(new JsepIceCandidate(std::string("audio"), 0,
2427                                          candidate_with_network_info));
2428   message = webrtc::SdpSerializeCandidate(*jcandidate_);
2429   EXPECT_EQ(std::string(kRawCandidate) + " network-id 1", message);
2430   candidate_with_network_info.set_network_cost(999);
2431   jcandidate_.reset(new JsepIceCandidate(std::string("audio"), 0,
2432                                          candidate_with_network_info));
2433   message = webrtc::SdpSerializeCandidate(*jcandidate_);
2434   EXPECT_EQ(std::string(kRawCandidate) + " network-id 1 network-cost 999",
2435             message);
2436 }
2437 
TEST_F(WebRtcSdpTest,SerializeHostnameCandidate)2438 TEST_F(WebRtcSdpTest, SerializeHostnameCandidate) {
2439   rtc::SocketAddress address("a.test", 1234);
2440   cricket::Candidate candidate(
2441       cricket::ICE_CANDIDATE_COMPONENT_RTP, "udp", address, kCandidatePriority,
2442       "", "", LOCAL_PORT_TYPE, kCandidateGeneration, kCandidateFoundation1);
2443   JsepIceCandidate jcandidate(std::string("audio_content_name"), 0, candidate);
2444   std::string message = webrtc::SdpSerializeCandidate(jcandidate);
2445   EXPECT_EQ(std::string(kRawHostnameCandidate), message);
2446 }
2447 
2448 // TODO(mallinath) : Enable this test once WebRTCSdp capable of parsing
2449 // RFC 6544.
TEST_F(WebRtcSdpTest,SerializeTcpCandidates)2450 TEST_F(WebRtcSdpTest, SerializeTcpCandidates) {
2451   Candidate candidate(ICE_CANDIDATE_COMPONENT_RTP, "tcp",
2452                       rtc::SocketAddress("192.168.1.5", 9), kCandidatePriority,
2453                       "", "", LOCAL_PORT_TYPE, kCandidateGeneration,
2454                       kCandidateFoundation1);
2455   candidate.set_tcptype(cricket::TCPTYPE_ACTIVE_STR);
2456   std::unique_ptr<IceCandidateInterface> jcandidate(
2457       new JsepIceCandidate(std::string("audio_content_name"), 0, candidate));
2458 
2459   std::string message = webrtc::SdpSerializeCandidate(*jcandidate);
2460   EXPECT_EQ(std::string(kSdpTcpActiveCandidate), message);
2461 }
2462 
TEST_F(WebRtcSdpTest,SerializeSessionDescriptionWithH264)2463 TEST_F(WebRtcSdpTest, SerializeSessionDescriptionWithH264) {
2464   cricket::VideoCodec h264_codec("H264");
2465   h264_codec.SetParam("profile-level-id", "42e01f");
2466   h264_codec.SetParam("level-asymmetry-allowed", "1");
2467   h264_codec.SetParam("packetization-mode", "1");
2468   video_desc_->AddCodec(h264_codec);
2469 
2470   jdesc_.Initialize(desc_.Clone(), kSessionId, kSessionVersion);
2471 
2472   std::string message = webrtc::SdpSerialize(jdesc_);
2473   size_t after_pt = message.find(" H264/90000");
2474   ASSERT_NE(after_pt, std::string::npos);
2475   size_t before_pt = message.rfind("a=rtpmap:", after_pt);
2476   ASSERT_NE(before_pt, std::string::npos);
2477   before_pt += strlen("a=rtpmap:");
2478   std::string pt = message.substr(before_pt, after_pt - before_pt);
2479   // TODO(hta): Check if payload type |pt| occurs in the m=video line.
2480   std::string to_find = "a=fmtp:" + pt + " ";
2481   size_t fmtp_pos = message.find(to_find);
2482   ASSERT_NE(std::string::npos, fmtp_pos) << "Failed to find " << to_find;
2483   size_t fmtp_endpos = message.find('\n', fmtp_pos);
2484   ASSERT_NE(std::string::npos, fmtp_endpos);
2485   std::string fmtp_value = message.substr(fmtp_pos, fmtp_endpos);
2486   EXPECT_NE(std::string::npos, fmtp_value.find("level-asymmetry-allowed=1"));
2487   EXPECT_NE(std::string::npos, fmtp_value.find("packetization-mode=1"));
2488   EXPECT_NE(std::string::npos, fmtp_value.find("profile-level-id=42e01f"));
2489   // Check that there are no spaces after semicolons.
2490   // https://bugs.webrtc.org/5793
2491   EXPECT_EQ(std::string::npos, fmtp_value.find("; "));
2492 }
2493 
TEST_F(WebRtcSdpTest,DeserializeSessionDescription)2494 TEST_F(WebRtcSdpTest, DeserializeSessionDescription) {
2495   JsepSessionDescription jdesc(kDummyType);
2496   // Deserialize
2497   EXPECT_TRUE(SdpDeserialize(kSdpFullString, &jdesc));
2498   // Verify
2499   EXPECT_TRUE(CompareSessionDescription(jdesc_, jdesc));
2500 }
2501 
TEST_F(WebRtcSdpTest,DeserializeSessionDescriptionWithoutMline)2502 TEST_F(WebRtcSdpTest, DeserializeSessionDescriptionWithoutMline) {
2503   JsepSessionDescription jdesc(kDummyType);
2504   const char kSdpWithoutMline[] =
2505       "v=0\r\n"
2506       "o=- 18446744069414584320 18446462598732840960 IN IP4 127.0.0.1\r\n"
2507       "s=-\r\n"
2508       "t=0 0\r\n"
2509       "a=msid-semantic: WMS local_stream_1 local_stream_2\r\n";
2510   // Deserialize
2511   EXPECT_TRUE(SdpDeserialize(kSdpWithoutMline, &jdesc));
2512   EXPECT_EQ(0u, jdesc.description()->contents().size());
2513 }
2514 
TEST_F(WebRtcSdpTest,DeserializeSessionDescriptionWithoutCarriageReturn)2515 TEST_F(WebRtcSdpTest, DeserializeSessionDescriptionWithoutCarriageReturn) {
2516   JsepSessionDescription jdesc(kDummyType);
2517   std::string sdp_without_carriage_return = kSdpFullString;
2518   Replace("\r\n", "\n", &sdp_without_carriage_return);
2519   // Deserialize
2520   EXPECT_TRUE(SdpDeserialize(sdp_without_carriage_return, &jdesc));
2521   // Verify
2522   EXPECT_TRUE(CompareSessionDescription(jdesc_, jdesc));
2523 }
2524 
TEST_F(WebRtcSdpTest,DeserializeSessionDescriptionWithoutCandidates)2525 TEST_F(WebRtcSdpTest, DeserializeSessionDescriptionWithoutCandidates) {
2526   // SessionDescription with desc but without candidates.
2527   JsepSessionDescription jdesc_no_candidates(kDummyType);
2528   ASSERT_TRUE(jdesc_no_candidates.Initialize(desc_.Clone(), kSessionId,
2529                                              kSessionVersion));
2530   JsepSessionDescription new_jdesc(kDummyType);
2531   EXPECT_TRUE(SdpDeserialize(kSdpString, &new_jdesc));
2532   EXPECT_TRUE(CompareSessionDescription(jdesc_no_candidates, new_jdesc));
2533 }
2534 
TEST_F(WebRtcSdpTest,DeserializeSessionDescriptionWithoutRtpmap)2535 TEST_F(WebRtcSdpTest, DeserializeSessionDescriptionWithoutRtpmap) {
2536   static const char kSdpNoRtpmapString[] =
2537       "v=0\r\n"
2538       "o=- 11 22 IN IP4 127.0.0.1\r\n"
2539       "s=-\r\n"
2540       "t=0 0\r\n"
2541       "m=audio 49232 RTP/AVP 0 18 103\r\n"
2542       // Codec that doesn't appear in the m= line will be ignored.
2543       "a=rtpmap:104 ISAC/32000\r\n"
2544       // The rtpmap line for static payload codec is optional.
2545       "a=rtpmap:18 G729/16000\r\n"
2546       "a=rtpmap:103 ISAC/16000\r\n";
2547 
2548   JsepSessionDescription jdesc(kDummyType);
2549   EXPECT_TRUE(SdpDeserialize(kSdpNoRtpmapString, &jdesc));
2550   cricket::AudioContentDescription* audio =
2551       cricket::GetFirstAudioContentDescription(jdesc.description());
2552   AudioCodecs ref_codecs;
2553   // The codecs in the AudioContentDescription should be in the same order as
2554   // the payload types (<fmt>s) on the m= line.
2555   ref_codecs.push_back(AudioCodec(0, "PCMU", 8000, 0, 1));
2556   ref_codecs.push_back(AudioCodec(18, "G729", 16000, 0, 1));
2557   ref_codecs.push_back(AudioCodec(103, "ISAC", 16000, 0, 1));
2558   EXPECT_EQ(ref_codecs, audio->codecs());
2559 }
2560 
TEST_F(WebRtcSdpTest,DeserializeSessionDescriptionWithoutRtpmapButWithFmtp)2561 TEST_F(WebRtcSdpTest, DeserializeSessionDescriptionWithoutRtpmapButWithFmtp) {
2562   static const char kSdpNoRtpmapString[] =
2563       "v=0\r\n"
2564       "o=- 11 22 IN IP4 127.0.0.1\r\n"
2565       "s=-\r\n"
2566       "t=0 0\r\n"
2567       "m=audio 49232 RTP/AVP 18 103\r\n"
2568       "a=fmtp:18 annexb=yes\r\n"
2569       "a=rtpmap:103 ISAC/16000\r\n";
2570 
2571   JsepSessionDescription jdesc(kDummyType);
2572   EXPECT_TRUE(SdpDeserialize(kSdpNoRtpmapString, &jdesc));
2573   cricket::AudioContentDescription* audio =
2574       cricket::GetFirstAudioContentDescription(jdesc.description());
2575 
2576   cricket::AudioCodec g729 = audio->codecs()[0];
2577   EXPECT_EQ("G729", g729.name);
2578   EXPECT_EQ(8000, g729.clockrate);
2579   EXPECT_EQ(18, g729.id);
2580   cricket::CodecParameterMap::iterator found = g729.params.find("annexb");
2581   ASSERT_TRUE(found != g729.params.end());
2582   EXPECT_EQ(found->second, "yes");
2583 
2584   cricket::AudioCodec isac = audio->codecs()[1];
2585   EXPECT_EQ("ISAC", isac.name);
2586   EXPECT_EQ(103, isac.id);
2587   EXPECT_EQ(16000, isac.clockrate);
2588 }
2589 
2590 // Ensure that we can deserialize SDP with a=fingerprint properly.
TEST_F(WebRtcSdpTest,DeserializeJsepSessionDescriptionWithFingerprint)2591 TEST_F(WebRtcSdpTest, DeserializeJsepSessionDescriptionWithFingerprint) {
2592   // Add a DTLS a=fingerprint attribute to our session description.
2593   AddFingerprint();
2594   JsepSessionDescription new_jdesc(kDummyType);
2595   ASSERT_TRUE(new_jdesc.Initialize(desc_.Clone(), jdesc_.session_id(),
2596                                    jdesc_.session_version()));
2597 
2598   JsepSessionDescription jdesc_with_fingerprint(kDummyType);
2599   std::string sdp_with_fingerprint = kSdpString;
2600   InjectAfter(kAttributeIcePwdVoice, kFingerprint, &sdp_with_fingerprint);
2601   InjectAfter(kAttributeIcePwdVideo, kFingerprint, &sdp_with_fingerprint);
2602   EXPECT_TRUE(SdpDeserialize(sdp_with_fingerprint, &jdesc_with_fingerprint));
2603   EXPECT_TRUE(CompareSessionDescription(jdesc_with_fingerprint, new_jdesc));
2604 }
2605 
TEST_F(WebRtcSdpTest,DeserializeSessionDescriptionWithBundle)2606 TEST_F(WebRtcSdpTest, DeserializeSessionDescriptionWithBundle) {
2607   JsepSessionDescription jdesc_with_bundle(kDummyType);
2608   std::string sdp_with_bundle = kSdpFullString;
2609   InjectAfter(kSessionTime,
2610               "a=group:BUNDLE audio_content_name video_content_name\r\n",
2611               &sdp_with_bundle);
2612   EXPECT_TRUE(SdpDeserialize(sdp_with_bundle, &jdesc_with_bundle));
2613   ContentGroup group(cricket::GROUP_TYPE_BUNDLE);
2614   group.AddContentName(kAudioContentName);
2615   group.AddContentName(kVideoContentName);
2616   desc_.AddGroup(group);
2617   ASSERT_TRUE(jdesc_.Initialize(desc_.Clone(), jdesc_.session_id(),
2618                                 jdesc_.session_version()));
2619   EXPECT_TRUE(CompareSessionDescription(jdesc_, jdesc_with_bundle));
2620 }
2621 
TEST_F(WebRtcSdpTest,DeserializeSessionDescriptionWithBandwidth)2622 TEST_F(WebRtcSdpTest, DeserializeSessionDescriptionWithBandwidth) {
2623   JsepSessionDescription jdesc_with_bandwidth(kDummyType);
2624   std::string sdp_with_bandwidth = kSdpFullString;
2625   InjectAfter("a=mid:video_content_name\r\na=sendrecv\r\n", "b=AS:100\r\n",
2626               &sdp_with_bandwidth);
2627   InjectAfter("a=mid:audio_content_name\r\na=sendrecv\r\n", "b=AS:50\r\n",
2628               &sdp_with_bandwidth);
2629   EXPECT_TRUE(SdpDeserialize(sdp_with_bandwidth, &jdesc_with_bandwidth));
2630   VideoContentDescription* vcd = GetFirstVideoContentDescription(&desc_);
2631   vcd->set_bandwidth(100 * 1000);
2632   AudioContentDescription* acd = GetFirstAudioContentDescription(&desc_);
2633   acd->set_bandwidth(50 * 1000);
2634   ASSERT_TRUE(jdesc_.Initialize(desc_.Clone(), jdesc_.session_id(),
2635                                 jdesc_.session_version()));
2636   EXPECT_TRUE(CompareSessionDescription(jdesc_, jdesc_with_bandwidth));
2637 }
2638 
TEST_F(WebRtcSdpTest,DeserializeSessionDescriptionWithIceOptions)2639 TEST_F(WebRtcSdpTest, DeserializeSessionDescriptionWithIceOptions) {
2640   JsepSessionDescription jdesc_with_ice_options(kDummyType);
2641   std::string sdp_with_ice_options = kSdpFullString;
2642   InjectAfter(kSessionTime, "a=ice-options:iceoption3\r\n",
2643               &sdp_with_ice_options);
2644   InjectAfter(kAttributeIcePwdVoice, "a=ice-options:iceoption1\r\n",
2645               &sdp_with_ice_options);
2646   InjectAfter(kAttributeIcePwdVideo, "a=ice-options:iceoption2\r\n",
2647               &sdp_with_ice_options);
2648   EXPECT_TRUE(SdpDeserialize(sdp_with_ice_options, &jdesc_with_ice_options));
2649   std::vector<std::string> transport_options;
2650   transport_options.push_back(kIceOption3);
2651   transport_options.push_back(kIceOption1);
2652   AddIceOptions(kAudioContentName, transport_options);
2653   transport_options.clear();
2654   transport_options.push_back(kIceOption3);
2655   transport_options.push_back(kIceOption2);
2656   AddIceOptions(kVideoContentName, transport_options);
2657   ASSERT_TRUE(jdesc_.Initialize(desc_.Clone(), jdesc_.session_id(),
2658                                 jdesc_.session_version()));
2659   EXPECT_TRUE(CompareSessionDescription(jdesc_, jdesc_with_ice_options));
2660 }
2661 
TEST_F(WebRtcSdpTest,DeserializeSessionDescriptionWithOpaqueTransportParams)2662 TEST_F(WebRtcSdpTest, DeserializeSessionDescriptionWithOpaqueTransportParams) {
2663   std::string sdp_with_transport_parameters = kSdpFullString;
2664   InjectAfter(kAttributeIcePwdVoice, "a=x-opaque:foo:dGVzdDY0\r\n",
2665               &sdp_with_transport_parameters);
2666   InjectAfter(kAttributeIcePwdVideo, "a=x-opaque:foo:dGVzdDY0\r\n",
2667               &sdp_with_transport_parameters);
2668 
2669   JsepSessionDescription jdesc_with_transport_parameters(kDummyType);
2670   EXPECT_TRUE(SdpDeserialize(sdp_with_transport_parameters,
2671                              &jdesc_with_transport_parameters));
2672 
2673   cricket::OpaqueTransportParameters params;
2674   params.protocol = "foo";
2675   params.parameters = "test64";
2676 
2677   AddOpaqueTransportParameters(kAudioContentName, params);
2678   AddOpaqueTransportParameters(kVideoContentName, params);
2679 
2680   ASSERT_TRUE(jdesc_.Initialize(desc_.Clone(), jdesc_.session_id(),
2681                                 jdesc_.session_version()));
2682   EXPECT_TRUE(
2683       CompareSessionDescription(jdesc_, jdesc_with_transport_parameters));
2684 }
2685 
TEST_F(WebRtcSdpTest,DeserializeSessionDescriptionWithAltProtocol)2686 TEST_F(WebRtcSdpTest, DeserializeSessionDescriptionWithAltProtocol) {
2687   std::string sdp_with_alt_protocol = kSdpFullString;
2688   InjectAfter(kAttributeIcePwdVoice, "a=x-alt-protocol:foo\r\n",
2689               &sdp_with_alt_protocol);
2690   InjectAfter(kAttributeIcePwdVideo, "a=x-alt-protocol:bar\r\n",
2691               &sdp_with_alt_protocol);
2692 
2693   JsepSessionDescription jdesc_with_alt_protocol(kDummyType);
2694   EXPECT_TRUE(SdpDeserialize(sdp_with_alt_protocol, &jdesc_with_alt_protocol));
2695 
2696   AddAltProtocol(kAudioContentName, "foo");
2697   AddAltProtocol(kVideoContentName, "bar");
2698 
2699   ASSERT_TRUE(jdesc_.Initialize(desc_.Clone(), jdesc_.session_id(),
2700                                 jdesc_.session_version()));
2701   EXPECT_TRUE(CompareSessionDescription(jdesc_, jdesc_with_alt_protocol));
2702 }
2703 
TEST_F(WebRtcSdpTest,DeserializeSessionDescriptionWithUfragPwd)2704 TEST_F(WebRtcSdpTest, DeserializeSessionDescriptionWithUfragPwd) {
2705   // Remove the original ice-ufrag and ice-pwd
2706   JsepSessionDescription jdesc_with_ufrag_pwd(kDummyType);
2707   std::string sdp_with_ufrag_pwd = kSdpFullString;
2708   EXPECT_TRUE(RemoveCandidateUfragPwd(&sdp_with_ufrag_pwd));
2709   // Add session level ufrag and pwd
2710   InjectAfter(kSessionTime,
2711               "a=ice-pwd:session+level+icepwd\r\n"
2712               "a=ice-ufrag:session+level+iceufrag\r\n",
2713               &sdp_with_ufrag_pwd);
2714   // Add media level ufrag and pwd for audio
2715   InjectAfter(
2716       "a=mid:audio_content_name\r\n",
2717       "a=ice-pwd:media+level+icepwd\r\na=ice-ufrag:media+level+iceufrag\r\n",
2718       &sdp_with_ufrag_pwd);
2719   // Update the candidate ufrag and pwd to the expected ones.
2720   EXPECT_TRUE(UpdateCandidateUfragPwd(&jdesc_, 0, "media+level+iceufrag",
2721                                       "media+level+icepwd"));
2722   EXPECT_TRUE(UpdateCandidateUfragPwd(&jdesc_, 1, "session+level+iceufrag",
2723                                       "session+level+icepwd"));
2724   EXPECT_TRUE(SdpDeserialize(sdp_with_ufrag_pwd, &jdesc_with_ufrag_pwd));
2725   EXPECT_TRUE(CompareSessionDescription(jdesc_, jdesc_with_ufrag_pwd));
2726 }
2727 
TEST_F(WebRtcSdpTest,DeserializeSessionDescriptionWithRecvOnlyContent)2728 TEST_F(WebRtcSdpTest, DeserializeSessionDescriptionWithRecvOnlyContent) {
2729   EXPECT_TRUE(TestDeserializeDirection(RtpTransceiverDirection::kRecvOnly));
2730 }
2731 
TEST_F(WebRtcSdpTest,DeserializeSessionDescriptionWithSendOnlyContent)2732 TEST_F(WebRtcSdpTest, DeserializeSessionDescriptionWithSendOnlyContent) {
2733   EXPECT_TRUE(TestDeserializeDirection(RtpTransceiverDirection::kSendOnly));
2734 }
2735 
TEST_F(WebRtcSdpTest,DeserializeSessionDescriptionWithInactiveContent)2736 TEST_F(WebRtcSdpTest, DeserializeSessionDescriptionWithInactiveContent) {
2737   EXPECT_TRUE(TestDeserializeDirection(RtpTransceiverDirection::kInactive));
2738 }
2739 
TEST_F(WebRtcSdpTest,DeserializeSessionDescriptionWithRejectedAudio)2740 TEST_F(WebRtcSdpTest, DeserializeSessionDescriptionWithRejectedAudio) {
2741   EXPECT_TRUE(TestDeserializeRejected(true, false));
2742 }
2743 
TEST_F(WebRtcSdpTest,DeserializeSessionDescriptionWithRejectedVideo)2744 TEST_F(WebRtcSdpTest, DeserializeSessionDescriptionWithRejectedVideo) {
2745   EXPECT_TRUE(TestDeserializeRejected(false, true));
2746 }
2747 
TEST_F(WebRtcSdpTest,DeserializeSessionDescriptionWithRejectedAudioVideo)2748 TEST_F(WebRtcSdpTest, DeserializeSessionDescriptionWithRejectedAudioVideo) {
2749   EXPECT_TRUE(TestDeserializeRejected(true, true));
2750 }
2751 
2752 // Tests that we can still handle the sdp uses mslabel and label instead of
2753 // msid for backward compatibility.
TEST_F(WebRtcSdpTest,DeserializeSessionDescriptionWithoutMsid)2754 TEST_F(WebRtcSdpTest, DeserializeSessionDescriptionWithoutMsid) {
2755   jdesc_.description()->set_msid_supported(false);
2756   JsepSessionDescription jdesc(kDummyType);
2757   std::string sdp_without_msid = kSdpFullString;
2758   Replace("msid", "xmsid", &sdp_without_msid);
2759   // Deserialize
2760   EXPECT_TRUE(SdpDeserialize(sdp_without_msid, &jdesc));
2761   // Verify
2762   EXPECT_TRUE(CompareSessionDescription(jdesc_, jdesc));
2763   EXPECT_FALSE(jdesc.description()->msid_signaling() &
2764                ~cricket::kMsidSignalingSsrcAttribute);
2765 }
2766 
TEST_F(WebRtcSdpTest,DeserializeSessionDescriptionWithExtmapAllowMixed)2767 TEST_F(WebRtcSdpTest, DeserializeSessionDescriptionWithExtmapAllowMixed) {
2768   jdesc_.description()->set_extmap_allow_mixed(true);
2769   std::string sdp_with_extmap_allow_mixed = kSdpFullString;
2770   InjectAfter("t=0 0\r\n", kExtmapAllowMixed, &sdp_with_extmap_allow_mixed);
2771   // Deserialize
2772   JsepSessionDescription jdesc_deserialized(kDummyType);
2773   EXPECT_TRUE(SdpDeserialize(sdp_with_extmap_allow_mixed, &jdesc_deserialized));
2774   // Verify
2775   EXPECT_TRUE(CompareSessionDescription(jdesc_, jdesc_deserialized));
2776 }
2777 
TEST_F(WebRtcSdpTest,DeserializeSessionDescriptionWithoutExtmapAllowMixed)2778 TEST_F(WebRtcSdpTest, DeserializeSessionDescriptionWithoutExtmapAllowMixed) {
2779   jdesc_.description()->set_extmap_allow_mixed(false);
2780   std::string sdp_without_extmap_allow_mixed = kSdpFullString;
2781   // Deserialize
2782   JsepSessionDescription jdesc_deserialized(kDummyType);
2783   EXPECT_TRUE(
2784       SdpDeserialize(sdp_without_extmap_allow_mixed, &jdesc_deserialized));
2785   // Verify
2786   EXPECT_TRUE(CompareSessionDescription(jdesc_, jdesc_deserialized));
2787 }
2788 
TEST_F(WebRtcSdpTest,DeserializeMediaContentDescriptionWithExtmapAllowMixed)2789 TEST_F(WebRtcSdpTest, DeserializeMediaContentDescriptionWithExtmapAllowMixed) {
2790   cricket::MediaContentDescription* video_desc =
2791       jdesc_.description()->GetContentDescriptionByName(kVideoContentName);
2792   ASSERT_TRUE(video_desc);
2793   cricket::MediaContentDescription* audio_desc =
2794       jdesc_.description()->GetContentDescriptionByName(kAudioContentName);
2795   ASSERT_TRUE(audio_desc);
2796   video_desc->set_extmap_allow_mixed_enum(
2797       cricket::MediaContentDescription::kMedia);
2798   audio_desc->set_extmap_allow_mixed_enum(
2799       cricket::MediaContentDescription::kMedia);
2800 
2801   std::string sdp_with_extmap_allow_mixed = kSdpFullString;
2802   InjectAfter("a=mid:audio_content_name\r\n", kExtmapAllowMixed,
2803               &sdp_with_extmap_allow_mixed);
2804   InjectAfter("a=mid:video_content_name\r\n", kExtmapAllowMixed,
2805               &sdp_with_extmap_allow_mixed);
2806 
2807   // Deserialize
2808   JsepSessionDescription jdesc_deserialized(kDummyType);
2809   EXPECT_TRUE(SdpDeserialize(sdp_with_extmap_allow_mixed, &jdesc_deserialized));
2810   // Verify
2811   EXPECT_TRUE(CompareSessionDescription(jdesc_, jdesc_deserialized));
2812 }
2813 
TEST_F(WebRtcSdpTest,DeserializeCandidate)2814 TEST_F(WebRtcSdpTest, DeserializeCandidate) {
2815   JsepIceCandidate jcandidate(kDummyMid, kDummyIndex);
2816 
2817   std::string sdp = kSdpOneCandidate;
2818   EXPECT_TRUE(SdpDeserializeCandidate(sdp, &jcandidate));
2819   EXPECT_EQ(kDummyMid, jcandidate.sdp_mid());
2820   EXPECT_EQ(kDummyIndex, jcandidate.sdp_mline_index());
2821   EXPECT_TRUE(jcandidate.candidate().IsEquivalent(jcandidate_->candidate()));
2822   EXPECT_EQ(0, jcandidate.candidate().network_cost());
2823 
2824   // Candidate line without generation extension.
2825   sdp = kSdpOneCandidate;
2826   Replace(" generation 2", "", &sdp);
2827   EXPECT_TRUE(SdpDeserializeCandidate(sdp, &jcandidate));
2828   EXPECT_EQ(kDummyMid, jcandidate.sdp_mid());
2829   EXPECT_EQ(kDummyIndex, jcandidate.sdp_mline_index());
2830   Candidate expected = jcandidate_->candidate();
2831   expected.set_generation(0);
2832   EXPECT_TRUE(jcandidate.candidate().IsEquivalent(expected));
2833 
2834   // Candidate with network id and/or cost.
2835   sdp = kSdpOneCandidate;
2836   Replace(" generation 2", " generation 2 network-id 2", &sdp);
2837   EXPECT_TRUE(SdpDeserializeCandidate(sdp, &jcandidate));
2838   EXPECT_EQ(kDummyMid, jcandidate.sdp_mid());
2839   EXPECT_EQ(kDummyIndex, jcandidate.sdp_mline_index());
2840   expected = jcandidate_->candidate();
2841   expected.set_network_id(2);
2842   EXPECT_TRUE(jcandidate.candidate().IsEquivalent(expected));
2843   EXPECT_EQ(0, jcandidate.candidate().network_cost());
2844   // Add network cost
2845   Replace(" network-id 2", " network-id 2 network-cost 9", &sdp);
2846   EXPECT_TRUE(SdpDeserializeCandidate(sdp, &jcandidate));
2847   EXPECT_TRUE(jcandidate.candidate().IsEquivalent(expected));
2848   EXPECT_EQ(9, jcandidate.candidate().network_cost());
2849 
2850   sdp = kSdpTcpActiveCandidate;
2851   EXPECT_TRUE(SdpDeserializeCandidate(sdp, &jcandidate));
2852   // Make a cricket::Candidate equivalent to kSdpTcpCandidate string.
2853   Candidate candidate(ICE_CANDIDATE_COMPONENT_RTP, "tcp",
2854                       rtc::SocketAddress("192.168.1.5", 9), kCandidatePriority,
2855                       "", "", LOCAL_PORT_TYPE, kCandidateGeneration,
2856                       kCandidateFoundation1);
2857   std::unique_ptr<IceCandidateInterface> jcandidate_template(
2858       new JsepIceCandidate(std::string("audio_content_name"), 0, candidate));
2859   EXPECT_TRUE(
2860       jcandidate.candidate().IsEquivalent(jcandidate_template->candidate()));
2861   sdp = kSdpTcpPassiveCandidate;
2862   EXPECT_TRUE(SdpDeserializeCandidate(sdp, &jcandidate));
2863   sdp = kSdpTcpSOCandidate;
2864   EXPECT_TRUE(SdpDeserializeCandidate(sdp, &jcandidate));
2865 }
2866 
2867 // This test verifies the deserialization of candidate-attribute
2868 // as per RFC 5245. Candiate-attribute will be of the format
2869 // candidate:<blah>. This format will be used when candidates
2870 // are trickled.
TEST_F(WebRtcSdpTest,DeserializeRawCandidateAttribute)2871 TEST_F(WebRtcSdpTest, DeserializeRawCandidateAttribute) {
2872   JsepIceCandidate jcandidate(kDummyMid, kDummyIndex);
2873 
2874   std::string candidate_attribute = kRawCandidate;
2875   EXPECT_TRUE(SdpDeserializeCandidate(candidate_attribute, &jcandidate));
2876   EXPECT_EQ(kDummyMid, jcandidate.sdp_mid());
2877   EXPECT_EQ(kDummyIndex, jcandidate.sdp_mline_index());
2878   EXPECT_TRUE(jcandidate.candidate().IsEquivalent(jcandidate_->candidate()));
2879   EXPECT_EQ(2u, jcandidate.candidate().generation());
2880 
2881   // Candidate line without generation extension.
2882   candidate_attribute = kRawCandidate;
2883   Replace(" generation 2", "", &candidate_attribute);
2884   EXPECT_TRUE(SdpDeserializeCandidate(candidate_attribute, &jcandidate));
2885   EXPECT_EQ(kDummyMid, jcandidate.sdp_mid());
2886   EXPECT_EQ(kDummyIndex, jcandidate.sdp_mline_index());
2887   Candidate expected = jcandidate_->candidate();
2888   expected.set_generation(0);
2889   EXPECT_TRUE(jcandidate.candidate().IsEquivalent(expected));
2890 
2891   // Candidate line without candidate:
2892   candidate_attribute = kRawCandidate;
2893   Replace("candidate:", "", &candidate_attribute);
2894   EXPECT_FALSE(SdpDeserializeCandidate(candidate_attribute, &jcandidate));
2895 
2896   // Candidate line with IPV6 address.
2897   EXPECT_TRUE(SdpDeserializeCandidate(kRawIPV6Candidate, &jcandidate));
2898 
2899   // Candidate line with hostname address.
2900   EXPECT_TRUE(SdpDeserializeCandidate(kRawHostnameCandidate, &jcandidate));
2901 }
2902 
2903 // This test verifies that the deserialization of an invalid candidate string
2904 // fails.
TEST_F(WebRtcSdpTest,DeserializeInvalidCandidiate)2905 TEST_F(WebRtcSdpTest, DeserializeInvalidCandidiate) {
2906   JsepIceCandidate jcandidate(kDummyMid, kDummyIndex);
2907 
2908   std::string candidate_attribute = kRawCandidate;
2909   candidate_attribute.replace(0, 1, "x");
2910   EXPECT_FALSE(SdpDeserializeCandidate(candidate_attribute, &jcandidate));
2911 
2912   candidate_attribute = kSdpOneCandidate;
2913   candidate_attribute.replace(0, 1, "x");
2914   EXPECT_FALSE(SdpDeserializeCandidate(candidate_attribute, &jcandidate));
2915 
2916   candidate_attribute = kRawCandidate;
2917   candidate_attribute.append("\r\n");
2918   candidate_attribute.append(kRawCandidate);
2919   EXPECT_FALSE(SdpDeserializeCandidate(candidate_attribute, &jcandidate));
2920 
2921   EXPECT_FALSE(SdpDeserializeCandidate(kSdpTcpInvalidCandidate, &jcandidate));
2922 }
2923 
TEST_F(WebRtcSdpTest,DeserializeSdpWithRtpDataChannels)2924 TEST_F(WebRtcSdpTest, DeserializeSdpWithRtpDataChannels) {
2925   AddRtpDataChannel();
2926   JsepSessionDescription jdesc(kDummyType);
2927   ASSERT_TRUE(jdesc.Initialize(desc_.Clone(), kSessionId, kSessionVersion));
2928 
2929   std::string sdp_with_data = kSdpString;
2930   sdp_with_data.append(kSdpRtpDataChannelString);
2931   JsepSessionDescription jdesc_output(kDummyType);
2932 
2933   // Deserialize
2934   EXPECT_TRUE(SdpDeserialize(sdp_with_data, &jdesc_output));
2935   // Verify
2936   EXPECT_TRUE(CompareSessionDescription(jdesc, jdesc_output));
2937 }
2938 
TEST_F(WebRtcSdpTest,DeserializeSdpWithSctpDataChannels)2939 TEST_F(WebRtcSdpTest, DeserializeSdpWithSctpDataChannels) {
2940   bool use_sctpmap = true;
2941   AddSctpDataChannel(use_sctpmap);
2942   JsepSessionDescription jdesc(kDummyType);
2943   ASSERT_TRUE(jdesc.Initialize(desc_.Clone(), kSessionId, kSessionVersion));
2944 
2945   std::string sdp_with_data = kSdpString;
2946   sdp_with_data.append(kSdpSctpDataChannelString);
2947   JsepSessionDescription jdesc_output(kDummyType);
2948 
2949   // Verify with UDP/DTLS/SCTP (already in kSdpSctpDataChannelString).
2950   EXPECT_TRUE(SdpDeserialize(sdp_with_data, &jdesc_output));
2951   EXPECT_TRUE(CompareSessionDescription(jdesc, jdesc_output));
2952 
2953   // Verify with DTLS/SCTP.
2954   sdp_with_data.replace(sdp_with_data.find(kUdpDtlsSctp), strlen(kUdpDtlsSctp),
2955                         kDtlsSctp);
2956   EXPECT_TRUE(SdpDeserialize(sdp_with_data, &jdesc_output));
2957   EXPECT_TRUE(CompareSessionDescription(jdesc, jdesc_output));
2958 
2959   // Verify with TCP/DTLS/SCTP.
2960   sdp_with_data.replace(sdp_with_data.find(kDtlsSctp), strlen(kDtlsSctp),
2961                         kTcpDtlsSctp);
2962   EXPECT_TRUE(SdpDeserialize(sdp_with_data, &jdesc_output));
2963   EXPECT_TRUE(CompareSessionDescription(jdesc, jdesc_output));
2964 }
2965 
TEST_F(WebRtcSdpTest,DeserializeSdpWithSctpDataChannelsWithSctpPort)2966 TEST_F(WebRtcSdpTest, DeserializeSdpWithSctpDataChannelsWithSctpPort) {
2967   bool use_sctpmap = false;
2968   AddSctpDataChannel(use_sctpmap);
2969   JsepSessionDescription jdesc(kDummyType);
2970   ASSERT_TRUE(jdesc.Initialize(desc_.Clone(), kSessionId, kSessionVersion));
2971 
2972   std::string sdp_with_data = kSdpString;
2973   sdp_with_data.append(kSdpSctpDataChannelStringWithSctpPort);
2974   JsepSessionDescription jdesc_output(kDummyType);
2975 
2976   EXPECT_TRUE(SdpDeserialize(sdp_with_data, &jdesc_output));
2977   EXPECT_TRUE(CompareSessionDescription(jdesc, jdesc_output));
2978 }
2979 
TEST_F(WebRtcSdpTest,DeserializeSdpWithSctpDataChannelsWithSctpColonPort)2980 TEST_F(WebRtcSdpTest, DeserializeSdpWithSctpDataChannelsWithSctpColonPort) {
2981   bool use_sctpmap = false;
2982   AddSctpDataChannel(use_sctpmap);
2983   JsepSessionDescription jdesc(kDummyType);
2984   ASSERT_TRUE(jdesc.Initialize(desc_.Clone(), kSessionId, kSessionVersion));
2985 
2986   std::string sdp_with_data = kSdpString;
2987   sdp_with_data.append(kSdpSctpDataChannelStringWithSctpColonPort);
2988   JsepSessionDescription jdesc_output(kDummyType);
2989 
2990   EXPECT_TRUE(SdpDeserialize(sdp_with_data, &jdesc_output));
2991   EXPECT_TRUE(CompareSessionDescription(jdesc, jdesc_output));
2992 }
2993 
2994 // Helper function to set the max-message-size parameter in the
2995 // SCTP data codec.
MutateJsepSctpMaxMessageSize(const SessionDescription & desc,int new_value,JsepSessionDescription * jdesc)2996 void MutateJsepSctpMaxMessageSize(const SessionDescription& desc,
2997                                   int new_value,
2998                                   JsepSessionDescription* jdesc) {
2999   std::unique_ptr<cricket::SessionDescription> mutant = desc.Clone();
3000   SctpDataContentDescription* dcdesc =
3001       mutant->GetContentDescriptionByName(kDataContentName)->as_sctp();
3002   dcdesc->set_max_message_size(new_value);
3003   jdesc->Initialize(std::move(mutant), kSessionId, kSessionVersion);
3004 }
3005 
TEST_F(WebRtcSdpTest,DeserializeSdpWithSctpDataChannelsWithMaxMessageSize)3006 TEST_F(WebRtcSdpTest, DeserializeSdpWithSctpDataChannelsWithMaxMessageSize) {
3007   bool use_sctpmap = false;
3008   AddSctpDataChannel(use_sctpmap);
3009   JsepSessionDescription jdesc(kDummyType);
3010   std::string sdp_with_data = kSdpString;
3011 
3012   sdp_with_data.append(kSdpSctpDataChannelStringWithSctpColonPort);
3013   sdp_with_data.append("a=max-message-size:12345\r\n");
3014   MutateJsepSctpMaxMessageSize(desc_, 12345, &jdesc);
3015   JsepSessionDescription jdesc_output(kDummyType);
3016 
3017   EXPECT_TRUE(SdpDeserialize(sdp_with_data, &jdesc_output));
3018   EXPECT_TRUE(CompareSessionDescription(jdesc, jdesc_output));
3019 }
3020 
TEST_F(WebRtcSdpTest,SerializeSdpWithSctpDataChannelWithMaxMessageSize)3021 TEST_F(WebRtcSdpTest, SerializeSdpWithSctpDataChannelWithMaxMessageSize) {
3022   bool use_sctpmap = false;
3023   AddSctpDataChannel(use_sctpmap);
3024   JsepSessionDescription jdesc(kDummyType);
3025   MutateJsepSctpMaxMessageSize(desc_, 12345, &jdesc);
3026   std::string message = webrtc::SdpSerialize(jdesc);
3027   EXPECT_NE(std::string::npos,
3028             message.find("\r\na=max-message-size:12345\r\n"));
3029   JsepSessionDescription jdesc_output(kDummyType);
3030   EXPECT_TRUE(SdpDeserialize(message, &jdesc_output));
3031   EXPECT_TRUE(CompareSessionDescription(jdesc, jdesc_output));
3032 }
3033 
TEST_F(WebRtcSdpTest,SerializeSdpWithSctpDataChannelWithDefaultMaxMessageSize)3034 TEST_F(WebRtcSdpTest,
3035        SerializeSdpWithSctpDataChannelWithDefaultMaxMessageSize) {
3036   // https://tools.ietf.org/html/draft-ietf-mmusic-sctp-sdp-26#section-6
3037   // The default max message size is 64K.
3038   bool use_sctpmap = false;
3039   AddSctpDataChannel(use_sctpmap);
3040   JsepSessionDescription jdesc(kDummyType);
3041   MutateJsepSctpMaxMessageSize(desc_, 65536, &jdesc);
3042   std::string message = webrtc::SdpSerialize(jdesc);
3043   EXPECT_EQ(std::string::npos, message.find("\r\na=max-message-size:"));
3044   JsepSessionDescription jdesc_output(kDummyType);
3045   EXPECT_TRUE(SdpDeserialize(message, &jdesc_output));
3046   EXPECT_TRUE(CompareSessionDescription(jdesc, jdesc_output));
3047 }
3048 
3049 // Test to check the behaviour if sctp-port is specified
3050 // on the m= line and in a=sctp-port.
TEST_F(WebRtcSdpTest,DeserializeSdpWithMultiSctpPort)3051 TEST_F(WebRtcSdpTest, DeserializeSdpWithMultiSctpPort) {
3052   bool use_sctpmap = true;
3053   AddSctpDataChannel(use_sctpmap);
3054   JsepSessionDescription jdesc(kDummyType);
3055   ASSERT_TRUE(jdesc.Initialize(desc_.Clone(), kSessionId, kSessionVersion));
3056 
3057   std::string sdp_with_data = kSdpString;
3058   // Append m= attributes
3059   sdp_with_data.append(kSdpSctpDataChannelString);
3060   // Append a=sctp-port attribute
3061   sdp_with_data.append("a=sctp-port 5000\r\n");
3062   JsepSessionDescription jdesc_output(kDummyType);
3063 
3064   EXPECT_FALSE(SdpDeserialize(sdp_with_data, &jdesc_output));
3065 }
3066 
3067 // Test behavior if a=rtpmap occurs in an SCTP section.
TEST_F(WebRtcSdpTest,DeserializeSdpWithRtpmapAttribute)3068 TEST_F(WebRtcSdpTest, DeserializeSdpWithRtpmapAttribute) {
3069   std::string sdp_with_data = kSdpString;
3070   // Append m= attributes
3071   sdp_with_data.append(kSdpSctpDataChannelString);
3072   // Append a=rtpmap attribute
3073   sdp_with_data.append("a=rtpmap:111 opus/48000/2\r\n");
3074   JsepSessionDescription jdesc_output(kDummyType);
3075   // Correct behavior is to ignore the extra attribute.
3076   EXPECT_TRUE(SdpDeserialize(sdp_with_data, &jdesc_output));
3077 }
3078 
TEST_F(WebRtcSdpTest,DeserializeSdpWithStrangeApplicationProtocolNames)3079 TEST_F(WebRtcSdpTest, DeserializeSdpWithStrangeApplicationProtocolNames) {
3080   static const char* bad_strings[] = {"DTLS/SCTPRTP/", "obviously-bogus",
3081                                       "UDP/TL/RTSP/SAVPF", "UDP/TL/RTSP/S"};
3082   for (auto proto : bad_strings) {
3083     std::string sdp_with_data = kSdpString;
3084     sdp_with_data.append("m=application 9 ");
3085     sdp_with_data.append(proto);
3086     sdp_with_data.append(" 47\r\n");
3087     JsepSessionDescription jdesc_output(kDummyType);
3088     EXPECT_FALSE(SdpDeserialize(sdp_with_data, &jdesc_output))
3089         << "Parsing should have failed on " << proto;
3090   }
3091   // The following strings are strange, but acceptable as RTP.
3092   static const char* weird_strings[] = {"DTLS/SCTP/RTP/FOO",
3093                                         "obviously-bogus/RTP/"};
3094   for (auto proto : weird_strings) {
3095     std::string sdp_with_data = kSdpString;
3096     sdp_with_data.append("m=application 9 ");
3097     sdp_with_data.append(proto);
3098     sdp_with_data.append(" 47\r\n");
3099     JsepSessionDescription jdesc_output(kDummyType);
3100     EXPECT_TRUE(SdpDeserialize(sdp_with_data, &jdesc_output))
3101         << "Parsing should have succeeded on " << proto;
3102   }
3103 }
3104 
3105 // For crbug/344475.
TEST_F(WebRtcSdpTest,DeserializeSdpWithCorruptedSctpDataChannels)3106 TEST_F(WebRtcSdpTest, DeserializeSdpWithCorruptedSctpDataChannels) {
3107   std::string sdp_with_data = kSdpString;
3108   sdp_with_data.append(kSdpSctpDataChannelString);
3109   // Remove the "\n" at the end.
3110   sdp_with_data = sdp_with_data.substr(0, sdp_with_data.size() - 1);
3111   JsepSessionDescription jdesc_output(kDummyType);
3112 
3113   EXPECT_FALSE(SdpDeserialize(sdp_with_data, &jdesc_output));
3114   // No crash is a pass.
3115 }
3116 
TEST_F(WebRtcSdpTest,DeserializeSdpWithSctpDataChannelAndUnusualPort)3117 TEST_F(WebRtcSdpTest, DeserializeSdpWithSctpDataChannelAndUnusualPort) {
3118   bool use_sctpmap = true;
3119   AddSctpDataChannel(use_sctpmap);
3120 
3121   // First setup the expected JsepSessionDescription.
3122   JsepSessionDescription jdesc(kDummyType);
3123   MutateJsepSctpPort(&jdesc, desc_, kUnusualSctpPort);
3124 
3125   // Then get the deserialized JsepSessionDescription.
3126   std::string sdp_with_data = kSdpString;
3127   sdp_with_data.append(kSdpSctpDataChannelString);
3128   absl::StrReplaceAll(
3129       {{rtc::ToString(kDefaultSctpPort), rtc::ToString(kUnusualSctpPort)}},
3130       &sdp_with_data);
3131   JsepSessionDescription jdesc_output(kDummyType);
3132 
3133   EXPECT_TRUE(SdpDeserialize(sdp_with_data, &jdesc_output));
3134   EXPECT_TRUE(CompareSessionDescription(jdesc, jdesc_output));
3135 }
3136 
TEST_F(WebRtcSdpTest,DeserializeSdpWithSctpDataChannelAndUnusualPortInAttribute)3137 TEST_F(WebRtcSdpTest,
3138        DeserializeSdpWithSctpDataChannelAndUnusualPortInAttribute) {
3139   bool use_sctpmap = false;
3140   AddSctpDataChannel(use_sctpmap);
3141 
3142   JsepSessionDescription jdesc(kDummyType);
3143   MutateJsepSctpPort(&jdesc, desc_, kUnusualSctpPort);
3144 
3145   // We need to test the deserialized JsepSessionDescription from
3146   // kSdpSctpDataChannelStringWithSctpPort for
3147   // draft-ietf-mmusic-sctp-sdp-07
3148   // a=sctp-port
3149   std::string sdp_with_data = kSdpString;
3150   sdp_with_data.append(kSdpSctpDataChannelStringWithSctpPort);
3151   absl::StrReplaceAll(
3152       {{rtc::ToString(kDefaultSctpPort), rtc::ToString(kUnusualSctpPort)}},
3153       &sdp_with_data);
3154   JsepSessionDescription jdesc_output(kDummyType);
3155 
3156   EXPECT_TRUE(SdpDeserialize(sdp_with_data, &jdesc_output));
3157   EXPECT_TRUE(CompareSessionDescription(jdesc, jdesc_output));
3158 }
3159 
TEST_F(WebRtcSdpTest,DeserializeSdpWithRtpDataChannelsAndBandwidth)3160 TEST_F(WebRtcSdpTest, DeserializeSdpWithRtpDataChannelsAndBandwidth) {
3161   // We want to test that deserializing data content limits bandwidth
3162   // settings (it should never be greater than the default).
3163   // This should prevent someone from using unlimited data bandwidth through
3164   // JS and "breaking the Internet".
3165   // See: https://code.google.com/p/chromium/issues/detail?id=280726
3166   std::string sdp_with_bandwidth = kSdpString;
3167   sdp_with_bandwidth.append(kSdpRtpDataChannelString);
3168   InjectAfter("a=mid:data_content_name\r\n", "b=AS:100\r\n",
3169               &sdp_with_bandwidth);
3170   JsepSessionDescription jdesc_with_bandwidth(kDummyType);
3171 
3172   EXPECT_FALSE(SdpDeserialize(sdp_with_bandwidth, &jdesc_with_bandwidth));
3173 }
3174 
TEST_F(WebRtcSdpTest,DeserializeSdpWithSctpDataChannelsAndBandwidth)3175 TEST_F(WebRtcSdpTest, DeserializeSdpWithSctpDataChannelsAndBandwidth) {
3176   bool use_sctpmap = true;
3177   AddSctpDataChannel(use_sctpmap);
3178   JsepSessionDescription jdesc(kDummyType);
3179   SctpDataContentDescription* dcd = GetFirstSctpDataContentDescription(&desc_);
3180   dcd->set_bandwidth(100 * 1000);
3181   ASSERT_TRUE(jdesc.Initialize(desc_.Clone(), kSessionId, kSessionVersion));
3182 
3183   std::string sdp_with_bandwidth = kSdpString;
3184   sdp_with_bandwidth.append(kSdpSctpDataChannelString);
3185   InjectAfter("a=mid:data_content_name\r\n", "b=AS:100\r\n",
3186               &sdp_with_bandwidth);
3187   JsepSessionDescription jdesc_with_bandwidth(kDummyType);
3188 
3189   // SCTP has congestion control, so we shouldn't limit the bandwidth
3190   // as we do for RTP.
3191   EXPECT_TRUE(SdpDeserialize(sdp_with_bandwidth, &jdesc_with_bandwidth));
3192   EXPECT_TRUE(CompareSessionDescription(jdesc, jdesc_with_bandwidth));
3193 }
3194 
3195 class WebRtcSdpExtmapTest : public WebRtcSdpTest,
3196                             public ::testing::WithParamInterface<bool> {};
3197 
TEST_P(WebRtcSdpExtmapTest,DeserializeSessionDescriptionWithSessionLevelExtmap)3198 TEST_P(WebRtcSdpExtmapTest,
3199        DeserializeSessionDescriptionWithSessionLevelExtmap) {
3200   bool encrypted = GetParam();
3201   TestDeserializeExtmap(true, false, encrypted);
3202 }
3203 
TEST_P(WebRtcSdpExtmapTest,DeserializeSessionDescriptionWithMediaLevelExtmap)3204 TEST_P(WebRtcSdpExtmapTest, DeserializeSessionDescriptionWithMediaLevelExtmap) {
3205   bool encrypted = GetParam();
3206   TestDeserializeExtmap(false, true, encrypted);
3207 }
3208 
TEST_P(WebRtcSdpExtmapTest,DeserializeSessionDescriptionWithInvalidExtmap)3209 TEST_P(WebRtcSdpExtmapTest, DeserializeSessionDescriptionWithInvalidExtmap) {
3210   bool encrypted = GetParam();
3211   TestDeserializeExtmap(true, true, encrypted);
3212 }
3213 
3214 INSTANTIATE_TEST_SUITE_P(Encrypted,
3215                          WebRtcSdpExtmapTest,
3216                          ::testing::Values(false, true));
3217 
TEST_F(WebRtcSdpTest,DeserializeSessionDescriptionWithoutEndLineBreak)3218 TEST_F(WebRtcSdpTest, DeserializeSessionDescriptionWithoutEndLineBreak) {
3219   JsepSessionDescription jdesc(kDummyType);
3220   std::string sdp = kSdpFullString;
3221   sdp = sdp.substr(0, sdp.size() - 2);  // Remove \r\n at the end.
3222   // Deserialize
3223   SdpParseError error;
3224   EXPECT_FALSE(webrtc::SdpDeserialize(sdp, &jdesc, &error));
3225   const std::string lastline = "a=ssrc:3 label:video_track_id_1";
3226   EXPECT_EQ(lastline, error.line);
3227   EXPECT_EQ("Invalid SDP line.", error.description);
3228 }
3229 
TEST_F(WebRtcSdpTest,DeserializeCandidateWithDifferentTransport)3230 TEST_F(WebRtcSdpTest, DeserializeCandidateWithDifferentTransport) {
3231   JsepIceCandidate jcandidate(kDummyMid, kDummyIndex);
3232   std::string new_sdp = kSdpOneCandidate;
3233   Replace("udp", "unsupported_transport", &new_sdp);
3234   EXPECT_FALSE(SdpDeserializeCandidate(new_sdp, &jcandidate));
3235   new_sdp = kSdpOneCandidate;
3236   Replace("udp", "uDP", &new_sdp);
3237   EXPECT_TRUE(SdpDeserializeCandidate(new_sdp, &jcandidate));
3238   EXPECT_EQ(kDummyMid, jcandidate.sdp_mid());
3239   EXPECT_EQ(kDummyIndex, jcandidate.sdp_mline_index());
3240   EXPECT_TRUE(jcandidate.candidate().IsEquivalent(jcandidate_->candidate()));
3241 }
3242 
TEST_F(WebRtcSdpTest,DeserializeCandidateWithUfragPwd)3243 TEST_F(WebRtcSdpTest, DeserializeCandidateWithUfragPwd) {
3244   JsepIceCandidate jcandidate(kDummyMid, kDummyIndex);
3245   EXPECT_TRUE(
3246       SdpDeserializeCandidate(kSdpOneCandidateWithUfragPwd, &jcandidate));
3247   EXPECT_EQ(kDummyMid, jcandidate.sdp_mid());
3248   EXPECT_EQ(kDummyIndex, jcandidate.sdp_mline_index());
3249   Candidate ref_candidate = jcandidate_->candidate();
3250   ref_candidate.set_username("user_rtp");
3251   ref_candidate.set_password("password_rtp");
3252   EXPECT_TRUE(jcandidate.candidate().IsEquivalent(ref_candidate));
3253 }
3254 
TEST_F(WebRtcSdpTest,DeserializeSdpWithConferenceFlag)3255 TEST_F(WebRtcSdpTest, DeserializeSdpWithConferenceFlag) {
3256   JsepSessionDescription jdesc(kDummyType);
3257 
3258   // Deserialize
3259   EXPECT_TRUE(SdpDeserialize(kSdpConferenceString, &jdesc));
3260 
3261   // Verify
3262   cricket::AudioContentDescription* audio =
3263       cricket::GetFirstAudioContentDescription(jdesc.description());
3264   EXPECT_TRUE(audio->conference_mode());
3265 
3266   cricket::VideoContentDescription* video =
3267       cricket::GetFirstVideoContentDescription(jdesc.description());
3268   EXPECT_TRUE(video->conference_mode());
3269 }
3270 
TEST_F(WebRtcSdpTest,SerializeSdpWithConferenceFlag)3271 TEST_F(WebRtcSdpTest, SerializeSdpWithConferenceFlag) {
3272   JsepSessionDescription jdesc(kDummyType);
3273 
3274   // We tested deserialization already above, so just test that if we serialize
3275   // and deserialize the flag doesn't disappear.
3276   EXPECT_TRUE(SdpDeserialize(kSdpConferenceString, &jdesc));
3277   std::string reserialized = webrtc::SdpSerialize(jdesc);
3278   EXPECT_TRUE(SdpDeserialize(reserialized, &jdesc));
3279 
3280   // Verify.
3281   cricket::AudioContentDescription* audio =
3282       cricket::GetFirstAudioContentDescription(jdesc.description());
3283   EXPECT_TRUE(audio->conference_mode());
3284 
3285   cricket::VideoContentDescription* video =
3286       cricket::GetFirstVideoContentDescription(jdesc.description());
3287   EXPECT_TRUE(video->conference_mode());
3288 }
3289 
TEST_F(WebRtcSdpTest,SerializeAndDeserializeRemoteNetEstimate)3290 TEST_F(WebRtcSdpTest, SerializeAndDeserializeRemoteNetEstimate) {
3291   {
3292     // By default remote estimates are disabled.
3293     JsepSessionDescription dst(kDummyType);
3294     SdpDeserialize(webrtc::SdpSerialize(jdesc_), &dst);
3295     EXPECT_FALSE(cricket::GetFirstVideoContentDescription(dst.description())
3296                      ->remote_estimate());
3297   }
3298   {
3299     // When remote estimate is enabled, the setting is propagated via SDP.
3300     cricket::GetFirstVideoContentDescription(jdesc_.description())
3301         ->set_remote_estimate(true);
3302     JsepSessionDescription dst(kDummyType);
3303     SdpDeserialize(webrtc::SdpSerialize(jdesc_), &dst);
3304     EXPECT_TRUE(cricket::GetFirstVideoContentDescription(dst.description())
3305                     ->remote_estimate());
3306   }
3307 }
3308 
TEST_F(WebRtcSdpTest,DeserializeBrokenSdp)3309 TEST_F(WebRtcSdpTest, DeserializeBrokenSdp) {
3310   const char kSdpDestroyer[] = "!@#$%^&";
3311   const char kSdpEmptyType[] = " =candidate";
3312   const char kSdpEqualAsPlus[] = "a+candidate";
3313   const char kSdpSpaceAfterEqual[] = "a= candidate";
3314   const char kSdpUpperType[] = "A=candidate";
3315   const char kSdpEmptyLine[] = "";
3316   const char kSdpMissingValue[] = "a=";
3317 
3318   const char kSdpBrokenFingerprint[] =
3319       "a=fingerprint:sha-1 "
3320       "4AAD:B9:B1:3F:82:18:3B:54:02:12:DF:3E:5D:49:6B:19:E5:7C:AB";
3321   const char kSdpExtraField[] =
3322       "a=fingerprint:sha-1 "
3323       "4A:AD:B9:B1:3F:82:18:3B:54:02:12:DF:3E:5D:49:6B:19:E5:7C:AB XXX";
3324   const char kSdpMissingSpace[] =
3325       "a=fingerprint:sha-1"
3326       "4A:AD:B9:B1:3F:82:18:3B:54:02:12:DF:3E:5D:49:6B:19:E5:7C:AB";
3327   // MD5 is not allowed in fingerprints.
3328   const char kSdpMd5[] =
3329       "a=fingerprint:md5 "
3330       "4A:AD:B9:B1:3F:82:18:3B:54:02:12:DF:3E:5D:49:6B";
3331 
3332   // Broken session description
3333   ExpectParseFailure("v=", kSdpDestroyer);
3334   ExpectParseFailure("o=", kSdpDestroyer);
3335   ExpectParseFailure("s=-", kSdpDestroyer);
3336   // Broken time description
3337   ExpectParseFailure("t=", kSdpDestroyer);
3338 
3339   // Broken media description
3340   ExpectParseFailure("m=audio", "c=IN IP4 74.125.224.39");
3341   ExpectParseFailure("m=video", kSdpDestroyer);
3342 
3343   // Invalid lines
3344   ExpectParseFailure("a=candidate", kSdpEmptyType);
3345   ExpectParseFailure("a=candidate", kSdpEqualAsPlus);
3346   ExpectParseFailure("a=candidate", kSdpSpaceAfterEqual);
3347   ExpectParseFailure("a=candidate", kSdpUpperType);
3348 
3349   // Bogus fingerprint replacing a=sendrev. We selected this attribute
3350   // because it's orthogonal to what we are replacing and hence
3351   // safe.
3352   ExpectParseFailure("a=sendrecv", kSdpBrokenFingerprint);
3353   ExpectParseFailure("a=sendrecv", kSdpExtraField);
3354   ExpectParseFailure("a=sendrecv", kSdpMissingSpace);
3355   ExpectParseFailure("a=sendrecv", kSdpMd5);
3356 
3357   // Empty Line
3358   ExpectParseFailure("a=rtcp:2347 IN IP4 74.125.127.126", kSdpEmptyLine);
3359   ExpectParseFailure("a=rtcp:2347 IN IP4 74.125.127.126", kSdpMissingValue);
3360 }
3361 
TEST_F(WebRtcSdpTest,DeserializeSdpWithInvalidAttributeValue)3362 TEST_F(WebRtcSdpTest, DeserializeSdpWithInvalidAttributeValue) {
3363   // ssrc
3364   ExpectParseFailure("a=ssrc:1", "a=ssrc:badvalue");
3365   ExpectParseFailure("a=ssrc-group:FEC 2 3", "a=ssrc-group:FEC badvalue 3");
3366   // crypto
3367   ExpectParseFailure("a=crypto:1 ", "a=crypto:badvalue ");
3368   // rtpmap
3369   ExpectParseFailure("a=rtpmap:111 ", "a=rtpmap:badvalue ");
3370   ExpectParseFailure("opus/48000/2", "opus/badvalue/2");
3371   ExpectParseFailure("opus/48000/2", "opus/48000/badvalue");
3372   // candidate
3373   ExpectParseFailure("1 udp 2130706432", "badvalue udp 2130706432");
3374   ExpectParseFailure("1 udp 2130706432", "1 udp badvalue");
3375   ExpectParseFailure("192.168.1.5 1234", "192.168.1.5 badvalue");
3376   ExpectParseFailure("rport 2346", "rport badvalue");
3377   ExpectParseFailure("rport 2346 generation 2",
3378                      "rport 2346 generation badvalue");
3379   // m line
3380   ExpectParseFailure("m=audio 2345 RTP/SAVPF 111 103 104",
3381                      "m=audio 2345 RTP/SAVPF 111 badvalue 104");
3382 
3383   // bandwidth
3384   ExpectParseFailureWithNewLines("a=mid:video_content_name\r\n",
3385                                  "b=AS:badvalue\r\n", "b=AS:badvalue");
3386   // rtcp-fb
3387   ExpectParseFailureWithNewLines("a=mid:video_content_name\r\n",
3388                                  "a=rtcp-fb:badvalue nack\r\n",
3389                                  "a=rtcp-fb:badvalue nack");
3390   // extmap
3391   ExpectParseFailureWithNewLines("a=mid:video_content_name\r\n",
3392                                  "a=extmap:badvalue http://example.com\r\n",
3393                                  "a=extmap:badvalue http://example.com");
3394 }
3395 
TEST_F(WebRtcSdpTest,DeserializeSdpWithReorderedPltypes)3396 TEST_F(WebRtcSdpTest, DeserializeSdpWithReorderedPltypes) {
3397   JsepSessionDescription jdesc_output(kDummyType);
3398 
3399   const char kSdpWithReorderedPlTypesString[] =
3400       "v=0\r\n"
3401       "o=- 18446744069414584320 18446462598732840960 IN IP4 127.0.0.1\r\n"
3402       "s=-\r\n"
3403       "t=0 0\r\n"
3404       "m=audio 9 RTP/SAVPF 104 103\r\n"  // Pl type 104 preferred.
3405       "a=rtpmap:111 opus/48000/2\r\n"    // Pltype 111 listed before 103 and 104
3406                                          // in the map.
3407       "a=rtpmap:103 ISAC/16000\r\n"  // Pltype 103 listed before 104 in the map.
3408       "a=rtpmap:104 ISAC/32000\r\n";
3409 
3410   // Deserialize
3411   EXPECT_TRUE(SdpDeserialize(kSdpWithReorderedPlTypesString, &jdesc_output));
3412 
3413   const AudioContentDescription* acd =
3414       GetFirstAudioContentDescription(jdesc_output.description());
3415   ASSERT_TRUE(acd);
3416   ASSERT_FALSE(acd->codecs().empty());
3417   EXPECT_EQ("ISAC", acd->codecs()[0].name);
3418   EXPECT_EQ(32000, acd->codecs()[0].clockrate);
3419   EXPECT_EQ(104, acd->codecs()[0].id);
3420 }
3421 
TEST_F(WebRtcSdpTest,DeserializeSerializeCodecParams)3422 TEST_F(WebRtcSdpTest, DeserializeSerializeCodecParams) {
3423   JsepSessionDescription jdesc_output(kDummyType);
3424   CodecParams params;
3425   params.max_ptime = 40;
3426   params.ptime = 30;
3427   params.min_ptime = 10;
3428   params.sprop_stereo = 1;
3429   params.stereo = 1;
3430   params.useinband = 1;
3431   params.maxaveragebitrate = 128000;
3432   TestDeserializeCodecParams(params, &jdesc_output);
3433   TestSerialize(jdesc_output);
3434 }
3435 
TEST_F(WebRtcSdpTest,DeserializeSerializeRtcpFb)3436 TEST_F(WebRtcSdpTest, DeserializeSerializeRtcpFb) {
3437   const bool kUseWildcard = false;
3438   JsepSessionDescription jdesc_output(kDummyType);
3439   TestDeserializeRtcpFb(&jdesc_output, kUseWildcard);
3440   TestSerialize(jdesc_output);
3441 }
3442 
TEST_F(WebRtcSdpTest,DeserializeSerializeRtcpFbWildcard)3443 TEST_F(WebRtcSdpTest, DeserializeSerializeRtcpFbWildcard) {
3444   const bool kUseWildcard = true;
3445   JsepSessionDescription jdesc_output(kDummyType);
3446   TestDeserializeRtcpFb(&jdesc_output, kUseWildcard);
3447   TestSerialize(jdesc_output);
3448 }
3449 
TEST_F(WebRtcSdpTest,DeserializeVideoFmtp)3450 TEST_F(WebRtcSdpTest, DeserializeVideoFmtp) {
3451   JsepSessionDescription jdesc_output(kDummyType);
3452 
3453   const char kSdpWithFmtpString[] =
3454       "v=0\r\n"
3455       "o=- 18446744069414584320 18446462598732840960 IN IP4 127.0.0.1\r\n"
3456       "s=-\r\n"
3457       "t=0 0\r\n"
3458       "m=video 3457 RTP/SAVPF 120\r\n"
3459       "a=rtpmap:120 VP8/90000\r\n"
3460       "a=fmtp:120 x-google-min-bitrate=10;x-google-max-quantization=40\r\n";
3461 
3462   // Deserialize
3463   SdpParseError error;
3464   EXPECT_TRUE(
3465       webrtc::SdpDeserialize(kSdpWithFmtpString, &jdesc_output, &error));
3466 
3467   const VideoContentDescription* vcd =
3468       GetFirstVideoContentDescription(jdesc_output.description());
3469   ASSERT_TRUE(vcd);
3470   ASSERT_FALSE(vcd->codecs().empty());
3471   cricket::VideoCodec vp8 = vcd->codecs()[0];
3472   EXPECT_EQ("VP8", vp8.name);
3473   EXPECT_EQ(120, vp8.id);
3474   cricket::CodecParameterMap::iterator found =
3475       vp8.params.find("x-google-min-bitrate");
3476   ASSERT_TRUE(found != vp8.params.end());
3477   EXPECT_EQ(found->second, "10");
3478   found = vp8.params.find("x-google-max-quantization");
3479   ASSERT_TRUE(found != vp8.params.end());
3480   EXPECT_EQ(found->second, "40");
3481 }
3482 
TEST_F(WebRtcSdpTest,DeserializeVideoFmtpWithSprops)3483 TEST_F(WebRtcSdpTest, DeserializeVideoFmtpWithSprops) {
3484   JsepSessionDescription jdesc_output(kDummyType);
3485 
3486   const char kSdpWithFmtpString[] =
3487       "v=0\r\n"
3488       "o=- 18446744069414584320 18446462598732840960 IN IP4 127.0.0.1\r\n"
3489       "s=-\r\n"
3490       "t=0 0\r\n"
3491       "m=video 49170 RTP/AVP 98\r\n"
3492       "a=rtpmap:98 H264/90000\r\n"
3493       "a=fmtp:98 profile-level-id=42A01E; "
3494       "sprop-parameter-sets=Z0IACpZTBYmI,aMljiA==\r\n";
3495 
3496   // Deserialize.
3497   SdpParseError error;
3498   EXPECT_TRUE(
3499       webrtc::SdpDeserialize(kSdpWithFmtpString, &jdesc_output, &error));
3500 
3501   const VideoContentDescription* vcd =
3502       GetFirstVideoContentDescription(jdesc_output.description());
3503   ASSERT_TRUE(vcd);
3504   ASSERT_FALSE(vcd->codecs().empty());
3505   cricket::VideoCodec h264 = vcd->codecs()[0];
3506   EXPECT_EQ("H264", h264.name);
3507   EXPECT_EQ(98, h264.id);
3508   cricket::CodecParameterMap::const_iterator found =
3509       h264.params.find("profile-level-id");
3510   ASSERT_TRUE(found != h264.params.end());
3511   EXPECT_EQ(found->second, "42A01E");
3512   found = h264.params.find("sprop-parameter-sets");
3513   ASSERT_TRUE(found != h264.params.end());
3514   EXPECT_EQ(found->second, "Z0IACpZTBYmI,aMljiA==");
3515 }
3516 
TEST_F(WebRtcSdpTest,DeserializeVideoFmtpWithSpace)3517 TEST_F(WebRtcSdpTest, DeserializeVideoFmtpWithSpace) {
3518   JsepSessionDescription jdesc_output(kDummyType);
3519 
3520   const char kSdpWithFmtpString[] =
3521       "v=0\r\n"
3522       "o=- 18446744069414584320 18446462598732840960 IN IP4 127.0.0.1\r\n"
3523       "s=-\r\n"
3524       "t=0 0\r\n"
3525       "m=video 3457 RTP/SAVPF 120\r\n"
3526       "a=rtpmap:120 VP8/90000\r\n"
3527       "a=fmtp:120   x-google-min-bitrate=10;  x-google-max-quantization=40\r\n";
3528 
3529   // Deserialize
3530   SdpParseError error;
3531   EXPECT_TRUE(
3532       webrtc::SdpDeserialize(kSdpWithFmtpString, &jdesc_output, &error));
3533 
3534   const VideoContentDescription* vcd =
3535       GetFirstVideoContentDescription(jdesc_output.description());
3536   ASSERT_TRUE(vcd);
3537   ASSERT_FALSE(vcd->codecs().empty());
3538   cricket::VideoCodec vp8 = vcd->codecs()[0];
3539   EXPECT_EQ("VP8", vp8.name);
3540   EXPECT_EQ(120, vp8.id);
3541   cricket::CodecParameterMap::iterator found =
3542       vp8.params.find("x-google-min-bitrate");
3543   ASSERT_TRUE(found != vp8.params.end());
3544   EXPECT_EQ(found->second, "10");
3545   found = vp8.params.find("x-google-max-quantization");
3546   ASSERT_TRUE(found != vp8.params.end());
3547   EXPECT_EQ(found->second, "40");
3548 }
3549 
TEST_F(WebRtcSdpTest,DeserializePacketizationAttributeWithIllegalValue)3550 TEST_F(WebRtcSdpTest, DeserializePacketizationAttributeWithIllegalValue) {
3551   JsepSessionDescription jdesc_output(kDummyType);
3552 
3553   const char kSdpWithPacketizationString[] =
3554       "v=0\r\n"
3555       "o=- 18446744069414584320 18446462598732840960 IN IP4 127.0.0.1\r\n"
3556       "s=-\r\n"
3557       "t=0 0\r\n"
3558       "m=audio 9 RTP/SAVPF 111\r\n"
3559       "a=rtpmap:111 opus/48000/2\r\n"
3560       "a=packetization:111 unknownpacketizationattributeforaudio\r\n"
3561       "m=video 3457 RTP/SAVPF 120 121 122\r\n"
3562       "a=rtpmap:120 VP8/90000\r\n"
3563       "a=packetization:120 raw\r\n"
3564       "a=rtpmap:121 VP9/90000\r\n"
3565       "a=rtpmap:122 H264/90000\r\n"
3566       "a=packetization:122 unknownpacketizationattributevalue\r\n";
3567 
3568   SdpParseError error;
3569   EXPECT_TRUE(webrtc::SdpDeserialize(kSdpWithPacketizationString, &jdesc_output,
3570                                      &error));
3571 
3572   AudioContentDescription* acd =
3573       GetFirstAudioContentDescription(jdesc_output.description());
3574   ASSERT_TRUE(acd);
3575   ASSERT_THAT(acd->codecs(), testing::SizeIs(1));
3576   cricket::AudioCodec opus = acd->codecs()[0];
3577   EXPECT_EQ(opus.name, "opus");
3578   EXPECT_EQ(opus.id, 111);
3579 
3580   const VideoContentDescription* vcd =
3581       GetFirstVideoContentDescription(jdesc_output.description());
3582   ASSERT_TRUE(vcd);
3583   ASSERT_THAT(vcd->codecs(), testing::SizeIs(3));
3584   cricket::VideoCodec vp8 = vcd->codecs()[0];
3585   EXPECT_EQ(vp8.name, "VP8");
3586   EXPECT_EQ(vp8.id, 120);
3587   EXPECT_EQ(vp8.packetization, "raw");
3588   cricket::VideoCodec vp9 = vcd->codecs()[1];
3589   EXPECT_EQ(vp9.name, "VP9");
3590   EXPECT_EQ(vp9.id, 121);
3591   EXPECT_EQ(vp9.packetization, absl::nullopt);
3592   cricket::VideoCodec h264 = vcd->codecs()[2];
3593   EXPECT_EQ(h264.name, "H264");
3594   EXPECT_EQ(h264.id, 122);
3595   EXPECT_EQ(h264.packetization, absl::nullopt);
3596 }
3597 
TEST_F(WebRtcSdpTest,SerializeAudioFmtpWithUnknownParameter)3598 TEST_F(WebRtcSdpTest, SerializeAudioFmtpWithUnknownParameter) {
3599   AudioContentDescription* acd = GetFirstAudioContentDescription(&desc_);
3600 
3601   cricket::AudioCodecs codecs = acd->codecs();
3602   codecs[0].params["unknown-future-parameter"] = "SomeFutureValue";
3603   acd->set_codecs(codecs);
3604 
3605   ASSERT_TRUE(jdesc_.Initialize(desc_.Clone(), jdesc_.session_id(),
3606                                 jdesc_.session_version()));
3607   std::string message = webrtc::SdpSerialize(jdesc_);
3608   std::string sdp_with_fmtp = kSdpFullString;
3609   InjectAfter("a=rtpmap:111 opus/48000/2\r\n",
3610               "a=fmtp:111 unknown-future-parameter=SomeFutureValue\r\n",
3611               &sdp_with_fmtp);
3612   EXPECT_EQ(sdp_with_fmtp, message);
3613 }
3614 
TEST_F(WebRtcSdpTest,SerializeAudioFmtpWithKnownFmtpParameter)3615 TEST_F(WebRtcSdpTest, SerializeAudioFmtpWithKnownFmtpParameter) {
3616   AudioContentDescription* acd = GetFirstAudioContentDescription(&desc_);
3617 
3618   cricket::AudioCodecs codecs = acd->codecs();
3619   codecs[0].params["stereo"] = "1";
3620   acd->set_codecs(codecs);
3621 
3622   ASSERT_TRUE(jdesc_.Initialize(desc_.Clone(), jdesc_.session_id(),
3623                                 jdesc_.session_version()));
3624   std::string message = webrtc::SdpSerialize(jdesc_);
3625   std::string sdp_with_fmtp = kSdpFullString;
3626   InjectAfter("a=rtpmap:111 opus/48000/2\r\n", "a=fmtp:111 stereo=1\r\n",
3627               &sdp_with_fmtp);
3628   EXPECT_EQ(sdp_with_fmtp, message);
3629 }
3630 
TEST_F(WebRtcSdpTest,SerializeAudioFmtpWithPTimeAndMaxPTime)3631 TEST_F(WebRtcSdpTest, SerializeAudioFmtpWithPTimeAndMaxPTime) {
3632   AudioContentDescription* acd = GetFirstAudioContentDescription(&desc_);
3633 
3634   cricket::AudioCodecs codecs = acd->codecs();
3635   codecs[0].params["ptime"] = "20";
3636   codecs[0].params["maxptime"] = "120";
3637   acd->set_codecs(codecs);
3638 
3639   ASSERT_TRUE(jdesc_.Initialize(desc_.Clone(), jdesc_.session_id(),
3640                                 jdesc_.session_version()));
3641   std::string message = webrtc::SdpSerialize(jdesc_);
3642   std::string sdp_with_fmtp = kSdpFullString;
3643   InjectAfter("a=rtpmap:104 ISAC/32000\r\n",
3644               "a=maxptime:120\r\n"  // No comma here. String merging!
3645               "a=ptime:20\r\n",
3646               &sdp_with_fmtp);
3647   EXPECT_EQ(sdp_with_fmtp, message);
3648 }
3649 
TEST_F(WebRtcSdpTest,SerializeVideoFmtp)3650 TEST_F(WebRtcSdpTest, SerializeVideoFmtp) {
3651   VideoContentDescription* vcd = GetFirstVideoContentDescription(&desc_);
3652 
3653   cricket::VideoCodecs codecs = vcd->codecs();
3654   codecs[0].params["x-google-min-bitrate"] = "10";
3655   vcd->set_codecs(codecs);
3656 
3657   ASSERT_TRUE(jdesc_.Initialize(desc_.Clone(), jdesc_.session_id(),
3658                                 jdesc_.session_version()));
3659   std::string message = webrtc::SdpSerialize(jdesc_);
3660   std::string sdp_with_fmtp = kSdpFullString;
3661   InjectAfter("a=rtpmap:120 VP8/90000\r\n",
3662               "a=fmtp:120 x-google-min-bitrate=10\r\n", &sdp_with_fmtp);
3663   EXPECT_EQ(sdp_with_fmtp, message);
3664 }
3665 
TEST_F(WebRtcSdpTest,SerializeVideoPacketizationAttribute)3666 TEST_F(WebRtcSdpTest, SerializeVideoPacketizationAttribute) {
3667   VideoContentDescription* vcd = GetFirstVideoContentDescription(&desc_);
3668 
3669   cricket::VideoCodecs codecs = vcd->codecs();
3670   codecs[0].packetization = "raw";
3671   vcd->set_codecs(codecs);
3672 
3673   ASSERT_TRUE(jdesc_.Initialize(desc_.Clone(), jdesc_.session_id(),
3674                                 jdesc_.session_version()));
3675   std::string message = webrtc::SdpSerialize(jdesc_);
3676   std::string sdp_with_packetization = kSdpFullString;
3677   InjectAfter("a=rtpmap:120 VP8/90000\r\n", "a=packetization:120 raw\r\n",
3678               &sdp_with_packetization);
3679   EXPECT_EQ(sdp_with_packetization, message);
3680 }
3681 
TEST_F(WebRtcSdpTest,DeserializeAndSerializeSdpWithIceLite)3682 TEST_F(WebRtcSdpTest, DeserializeAndSerializeSdpWithIceLite) {
3683   // Deserialize the baseline description, making sure it's ICE full.
3684   JsepSessionDescription jdesc_with_icelite(kDummyType);
3685   std::string sdp_with_icelite = kSdpFullString;
3686   EXPECT_TRUE(SdpDeserialize(sdp_with_icelite, &jdesc_with_icelite));
3687   cricket::SessionDescription* desc = jdesc_with_icelite.description();
3688   const cricket::TransportInfo* tinfo1 =
3689       desc->GetTransportInfoByName("audio_content_name");
3690   EXPECT_EQ(cricket::ICEMODE_FULL, tinfo1->description.ice_mode);
3691   const cricket::TransportInfo* tinfo2 =
3692       desc->GetTransportInfoByName("video_content_name");
3693   EXPECT_EQ(cricket::ICEMODE_FULL, tinfo2->description.ice_mode);
3694 
3695   // Add "a=ice-lite" and deserialize, making sure it's ICE lite.
3696   InjectAfter(kSessionTime, "a=ice-lite\r\n", &sdp_with_icelite);
3697   EXPECT_TRUE(SdpDeserialize(sdp_with_icelite, &jdesc_with_icelite));
3698   desc = jdesc_with_icelite.description();
3699   const cricket::TransportInfo* atinfo =
3700       desc->GetTransportInfoByName("audio_content_name");
3701   EXPECT_EQ(cricket::ICEMODE_LITE, atinfo->description.ice_mode);
3702   const cricket::TransportInfo* vtinfo =
3703       desc->GetTransportInfoByName("video_content_name");
3704   EXPECT_EQ(cricket::ICEMODE_LITE, vtinfo->description.ice_mode);
3705 
3706   // Now that we know deserialization works, we can use TestSerialize to test
3707   // serialization.
3708   TestSerialize(jdesc_with_icelite);
3709 }
3710 
3711 // Verifies that the candidates in the input SDP are parsed and serialized
3712 // correctly in the output SDP.
TEST_F(WebRtcSdpTest,RoundTripSdpWithSctpDataChannelsWithCandidates)3713 TEST_F(WebRtcSdpTest, RoundTripSdpWithSctpDataChannelsWithCandidates) {
3714   std::string sdp_with_data = kSdpString;
3715   sdp_with_data.append(kSdpSctpDataChannelWithCandidatesString);
3716   JsepSessionDescription jdesc_output(kDummyType);
3717 
3718   EXPECT_TRUE(SdpDeserialize(sdp_with_data, &jdesc_output));
3719   EXPECT_EQ(sdp_with_data, webrtc::SdpSerialize(jdesc_output));
3720 }
3721 
TEST_F(WebRtcSdpTest,SerializeDtlsSetupAttribute)3722 TEST_F(WebRtcSdpTest, SerializeDtlsSetupAttribute) {
3723   AddFingerprint();
3724   TransportInfo audio_transport_info =
3725       *(desc_.GetTransportInfoByName(kAudioContentName));
3726   EXPECT_EQ(cricket::CONNECTIONROLE_NONE,
3727             audio_transport_info.description.connection_role);
3728   audio_transport_info.description.connection_role =
3729       cricket::CONNECTIONROLE_ACTIVE;
3730 
3731   TransportInfo video_transport_info =
3732       *(desc_.GetTransportInfoByName(kVideoContentName));
3733   EXPECT_EQ(cricket::CONNECTIONROLE_NONE,
3734             video_transport_info.description.connection_role);
3735   video_transport_info.description.connection_role =
3736       cricket::CONNECTIONROLE_ACTIVE;
3737 
3738   desc_.RemoveTransportInfoByName(kAudioContentName);
3739   desc_.RemoveTransportInfoByName(kVideoContentName);
3740 
3741   desc_.AddTransportInfo(audio_transport_info);
3742   desc_.AddTransportInfo(video_transport_info);
3743 
3744   ASSERT_TRUE(jdesc_.Initialize(desc_.Clone(), jdesc_.session_id(),
3745                                 jdesc_.session_version()));
3746   std::string message = webrtc::SdpSerialize(jdesc_);
3747   std::string sdp_with_dtlssetup = kSdpFullString;
3748 
3749   // Fingerprint attribute is necessary to add DTLS setup attribute.
3750   InjectAfter(kAttributeIcePwdVoice, kFingerprint, &sdp_with_dtlssetup);
3751   InjectAfter(kAttributeIcePwdVideo, kFingerprint, &sdp_with_dtlssetup);
3752   // Now adding |setup| attribute.
3753   InjectAfter(kFingerprint, "a=setup:active\r\n", &sdp_with_dtlssetup);
3754   EXPECT_EQ(sdp_with_dtlssetup, message);
3755 }
3756 
TEST_F(WebRtcSdpTest,DeserializeDtlsSetupAttribute)3757 TEST_F(WebRtcSdpTest, DeserializeDtlsSetupAttribute) {
3758   JsepSessionDescription jdesc_with_dtlssetup(kDummyType);
3759   std::string sdp_with_dtlssetup = kSdpFullString;
3760   InjectAfter(kSessionTime, "a=setup:actpass\r\n", &sdp_with_dtlssetup);
3761   EXPECT_TRUE(SdpDeserialize(sdp_with_dtlssetup, &jdesc_with_dtlssetup));
3762   cricket::SessionDescription* desc = jdesc_with_dtlssetup.description();
3763   const cricket::TransportInfo* atinfo =
3764       desc->GetTransportInfoByName("audio_content_name");
3765   EXPECT_EQ(cricket::CONNECTIONROLE_ACTPASS,
3766             atinfo->description.connection_role);
3767   const cricket::TransportInfo* vtinfo =
3768       desc->GetTransportInfoByName("video_content_name");
3769   EXPECT_EQ(cricket::CONNECTIONROLE_ACTPASS,
3770             vtinfo->description.connection_role);
3771 }
3772 
3773 // Verifies that the order of the serialized m-lines follows the order of the
3774 // ContentInfo in SessionDescription, and vise versa for deserialization.
TEST_F(WebRtcSdpTest,MediaContentOrderMaintainedRoundTrip)3775 TEST_F(WebRtcSdpTest, MediaContentOrderMaintainedRoundTrip) {
3776   JsepSessionDescription jdesc(kDummyType);
3777   const std::string media_content_sdps[3] = {kSdpAudioString, kSdpVideoString,
3778                                              kSdpSctpDataChannelString};
3779   const cricket::MediaType media_types[3] = {cricket::MEDIA_TYPE_AUDIO,
3780                                              cricket::MEDIA_TYPE_VIDEO,
3781                                              cricket::MEDIA_TYPE_DATA};
3782 
3783   // Verifies all 6 permutations.
3784   for (size_t i = 0; i < 6; ++i) {
3785     size_t media_content_in_sdp[3];
3786     // The index of the first media content.
3787     media_content_in_sdp[0] = i / 2;
3788     // The index of the second media content.
3789     media_content_in_sdp[1] = (media_content_in_sdp[0] + i % 2 + 1) % 3;
3790     // The index of the third media content.
3791     media_content_in_sdp[2] = (media_content_in_sdp[0] + (i + 1) % 2 + 1) % 3;
3792 
3793     std::string sdp_string = kSdpSessionString;
3794     for (size_t i = 0; i < 3; ++i)
3795       sdp_string += media_content_sdps[media_content_in_sdp[i]];
3796 
3797     EXPECT_TRUE(SdpDeserialize(sdp_string, &jdesc));
3798     cricket::SessionDescription* desc = jdesc.description();
3799     EXPECT_EQ(3u, desc->contents().size());
3800 
3801     for (size_t i = 0; i < 3; ++i) {
3802       const cricket::MediaContentDescription* mdesc =
3803           desc->contents()[i].media_description();
3804       EXPECT_EQ(media_types[media_content_in_sdp[i]], mdesc->type());
3805     }
3806 
3807     std::string serialized_sdp = webrtc::SdpSerialize(jdesc);
3808     EXPECT_EQ(sdp_string, serialized_sdp);
3809   }
3810 }
3811 
TEST_F(WebRtcSdpTest,DeserializeBundleOnlyAttribute)3812 TEST_F(WebRtcSdpTest, DeserializeBundleOnlyAttribute) {
3813   MakeBundleOnlyDescription();
3814   JsepSessionDescription deserialized_description(kDummyType);
3815   ASSERT_TRUE(
3816       SdpDeserialize(kBundleOnlySdpFullString, &deserialized_description));
3817   EXPECT_TRUE(CompareSessionDescription(jdesc_, deserialized_description));
3818 }
3819 
3820 // The semantics of "a=bundle-only" are only defined when it's used in
3821 // combination with a 0 port on the m= line. We should ignore it if used with a
3822 // nonzero port.
TEST_F(WebRtcSdpTest,IgnoreBundleOnlyWithNonzeroPort)3823 TEST_F(WebRtcSdpTest, IgnoreBundleOnlyWithNonzeroPort) {
3824   // Make the base bundle-only description but unset the bundle-only flag.
3825   MakeBundleOnlyDescription();
3826   jdesc_.description()->contents()[1].bundle_only = false;
3827 
3828   std::string modified_sdp = kBundleOnlySdpFullString;
3829   Replace("m=video 0", "m=video 9", &modified_sdp);
3830   JsepSessionDescription deserialized_description(kDummyType);
3831   ASSERT_TRUE(SdpDeserialize(modified_sdp, &deserialized_description));
3832   EXPECT_TRUE(CompareSessionDescription(jdesc_, deserialized_description));
3833 }
3834 
TEST_F(WebRtcSdpTest,SerializeBundleOnlyAttribute)3835 TEST_F(WebRtcSdpTest, SerializeBundleOnlyAttribute) {
3836   MakeBundleOnlyDescription();
3837   TestSerialize(jdesc_);
3838 }
3839 
TEST_F(WebRtcSdpTest,DeserializePlanBSessionDescription)3840 TEST_F(WebRtcSdpTest, DeserializePlanBSessionDescription) {
3841   MakePlanBDescription();
3842 
3843   JsepSessionDescription deserialized_description(kDummyType);
3844   EXPECT_TRUE(SdpDeserialize(kPlanBSdpFullString, &deserialized_description));
3845 
3846   EXPECT_TRUE(CompareSessionDescription(jdesc_, deserialized_description));
3847 }
3848 
TEST_F(WebRtcSdpTest,SerializePlanBSessionDescription)3849 TEST_F(WebRtcSdpTest, SerializePlanBSessionDescription) {
3850   MakePlanBDescription();
3851   TestSerialize(jdesc_);
3852 }
3853 
TEST_F(WebRtcSdpTest,DeserializeUnifiedPlanSessionDescription)3854 TEST_F(WebRtcSdpTest, DeserializeUnifiedPlanSessionDescription) {
3855   MakeUnifiedPlanDescription();
3856 
3857   JsepSessionDescription deserialized_description(kDummyType);
3858   EXPECT_TRUE(
3859       SdpDeserialize(kUnifiedPlanSdpFullString, &deserialized_description));
3860 
3861   EXPECT_TRUE(CompareSessionDescription(jdesc_, deserialized_description));
3862 }
3863 
TEST_F(WebRtcSdpTest,SerializeUnifiedPlanSessionDescription)3864 TEST_F(WebRtcSdpTest, SerializeUnifiedPlanSessionDescription) {
3865   MakeUnifiedPlanDescription();
3866   TestSerialize(jdesc_);
3867 }
3868 
3869 // This tests deserializing a Unified Plan SDP that is compatible with both
3870 // Unified Plan and Plan B style SDP, meaning that it contains both "a=ssrc
3871 // msid" lines and "a=msid " lines. It tests the case for audio/video tracks
3872 // with no stream ids and multiple stream ids. For parsing this, the Unified
3873 // Plan a=msid lines should take priority, because the Plan B style a=ssrc msid
3874 // lines do not support multiple stream ids and no stream ids.
TEST_F(WebRtcSdpTest,DeserializeSessionDescriptionSpecialMsid)3875 TEST_F(WebRtcSdpTest, DeserializeSessionDescriptionSpecialMsid) {
3876   // Create both msid lines for Plan B and Unified Plan support.
3877   MakeUnifiedPlanDescriptionMultipleStreamIds(
3878       cricket::kMsidSignalingMediaSection |
3879       cricket::kMsidSignalingSsrcAttribute);
3880 
3881   JsepSessionDescription deserialized_description(kDummyType);
3882   EXPECT_TRUE(SdpDeserialize(kUnifiedPlanSdpFullStringWithSpecialMsid,
3883                              &deserialized_description));
3884 
3885   EXPECT_TRUE(CompareSessionDescription(jdesc_, deserialized_description));
3886   EXPECT_EQ(cricket::kMsidSignalingMediaSection |
3887                 cricket::kMsidSignalingSsrcAttribute,
3888             deserialized_description.description()->msid_signaling());
3889 }
3890 
3891 // Tests the serialization of a Unified Plan SDP that is compatible for both
3892 // Unified Plan and Plan B style SDPs, meaning that it contains both "a=ssrc
3893 // msid" lines and "a=msid " lines. It tests the case for no stream ids and
3894 // multiple stream ids.
TEST_F(WebRtcSdpTest,SerializeSessionDescriptionSpecialMsid)3895 TEST_F(WebRtcSdpTest, SerializeSessionDescriptionSpecialMsid) {
3896   // Create both msid lines for Plan B and Unified Plan support.
3897   MakeUnifiedPlanDescriptionMultipleStreamIds(
3898       cricket::kMsidSignalingMediaSection |
3899       cricket::kMsidSignalingSsrcAttribute);
3900   std::string serialized_sdp = webrtc::SdpSerialize(jdesc_);
3901   // We explicitly test that the serialized SDP string is equal to the hard
3902   // coded SDP string. This is necessary, because in the parser "a=msid" lines
3903   // take priority over "a=ssrc msid" lines. This means if we just used
3904   // TestSerialize(), it could serialize an SDP that omits "a=ssrc msid" lines,
3905   // and still pass, because the deserialized version would be the same.
3906   EXPECT_EQ(kUnifiedPlanSdpFullStringWithSpecialMsid, serialized_sdp);
3907 }
3908 
3909 // Tests that a Unified Plan style SDP (does not contain "a=ssrc msid" lines
3910 // that signal stream IDs) is deserialized appropriately. It tests the case for
3911 // no stream ids and multiple stream ids.
TEST_F(WebRtcSdpTest,UnifiedPlanDeserializeSessionDescriptionSpecialMsid)3912 TEST_F(WebRtcSdpTest, UnifiedPlanDeserializeSessionDescriptionSpecialMsid) {
3913   // Only create a=msid lines for strictly Unified Plan stream ID support.
3914   MakeUnifiedPlanDescriptionMultipleStreamIds(
3915       cricket::kMsidSignalingMediaSection);
3916 
3917   JsepSessionDescription deserialized_description(kDummyType);
3918   std::string unified_plan_sdp_string =
3919       kUnifiedPlanSdpFullStringWithSpecialMsid;
3920   RemoveSsrcMsidLinesFromSdpString(&unified_plan_sdp_string);
3921   EXPECT_TRUE(
3922       SdpDeserialize(unified_plan_sdp_string, &deserialized_description));
3923 
3924   EXPECT_TRUE(CompareSessionDescription(jdesc_, deserialized_description));
3925 }
3926 
3927 // Tests that a Unified Plan style SDP (does not contain "a=ssrc msid" lines
3928 // that signal stream IDs) is serialized appropriately. It tests the case for no
3929 // stream ids and multiple stream ids.
TEST_F(WebRtcSdpTest,UnifiedPlanSerializeSessionDescriptionSpecialMsid)3930 TEST_F(WebRtcSdpTest, UnifiedPlanSerializeSessionDescriptionSpecialMsid) {
3931   // Only create a=msid lines for strictly Unified Plan stream ID support.
3932   MakeUnifiedPlanDescriptionMultipleStreamIds(
3933       cricket::kMsidSignalingMediaSection);
3934 
3935   TestSerialize(jdesc_);
3936 }
3937 
3938 // This tests that a Unified Plan SDP with no a=ssrc lines is
3939 // serialized/deserialized appropriately. In this case the
3940 // MediaContentDescription will contain a StreamParams object that doesn't have
3941 // any SSRCs. Vice versa, this will be created upon deserializing an SDP with no
3942 // SSRC lines.
TEST_F(WebRtcSdpTest,DeserializeUnifiedPlanSessionDescriptionNoSsrcSignaling)3943 TEST_F(WebRtcSdpTest, DeserializeUnifiedPlanSessionDescriptionNoSsrcSignaling) {
3944   MakeUnifiedPlanDescription();
3945   RemoveSsrcSignalingFromStreamParams();
3946   std::string unified_plan_sdp_string = kUnifiedPlanSdpFullString;
3947   RemoveSsrcLinesFromSdpString(&unified_plan_sdp_string);
3948 
3949   JsepSessionDescription deserialized_description(kDummyType);
3950   EXPECT_TRUE(
3951       SdpDeserialize(unified_plan_sdp_string, &deserialized_description));
3952   EXPECT_TRUE(CompareSessionDescription(jdesc_, deserialized_description));
3953 }
3954 
TEST_F(WebRtcSdpTest,SerializeUnifiedPlanSessionDescriptionNoSsrcSignaling)3955 TEST_F(WebRtcSdpTest, SerializeUnifiedPlanSessionDescriptionNoSsrcSignaling) {
3956   MakeUnifiedPlanDescription();
3957   RemoveSsrcSignalingFromStreamParams();
3958 
3959   TestSerialize(jdesc_);
3960 }
3961 
TEST_F(WebRtcSdpTest,EmptyDescriptionHasNoMsidSignaling)3962 TEST_F(WebRtcSdpTest, EmptyDescriptionHasNoMsidSignaling) {
3963   JsepSessionDescription jsep_desc(kDummyType);
3964   ASSERT_TRUE(SdpDeserialize(kSdpSessionString, &jsep_desc));
3965   EXPECT_EQ(0, jsep_desc.description()->msid_signaling());
3966 }
3967 
TEST_F(WebRtcSdpTest,DataChannelOnlyHasNoMsidSignaling)3968 TEST_F(WebRtcSdpTest, DataChannelOnlyHasNoMsidSignaling) {
3969   JsepSessionDescription jsep_desc(kDummyType);
3970   std::string sdp = kSdpSessionString;
3971   sdp += kSdpSctpDataChannelString;
3972   ASSERT_TRUE(SdpDeserialize(sdp, &jsep_desc));
3973   EXPECT_EQ(0, jsep_desc.description()->msid_signaling());
3974 }
3975 
TEST_F(WebRtcSdpTest,PlanBHasSsrcAttributeMsidSignaling)3976 TEST_F(WebRtcSdpTest, PlanBHasSsrcAttributeMsidSignaling) {
3977   JsepSessionDescription jsep_desc(kDummyType);
3978   ASSERT_TRUE(SdpDeserialize(kPlanBSdpFullString, &jsep_desc));
3979   EXPECT_EQ(cricket::kMsidSignalingSsrcAttribute,
3980             jsep_desc.description()->msid_signaling());
3981 }
3982 
TEST_F(WebRtcSdpTest,UnifiedPlanHasMediaSectionMsidSignaling)3983 TEST_F(WebRtcSdpTest, UnifiedPlanHasMediaSectionMsidSignaling) {
3984   JsepSessionDescription jsep_desc(kDummyType);
3985   ASSERT_TRUE(SdpDeserialize(kUnifiedPlanSdpFullString, &jsep_desc));
3986   EXPECT_EQ(cricket::kMsidSignalingMediaSection,
3987             jsep_desc.description()->msid_signaling());
3988 }
3989 
3990 const char kMediaSectionMsidLine[] = "a=msid:local_stream_1 audio_track_id_1";
3991 const char kSsrcAttributeMsidLine[] =
3992     "a=ssrc:1 msid:local_stream_1 audio_track_id_1";
3993 
TEST_F(WebRtcSdpTest,SerializeOnlyMediaSectionMsid)3994 TEST_F(WebRtcSdpTest, SerializeOnlyMediaSectionMsid) {
3995   jdesc_.description()->set_msid_signaling(cricket::kMsidSignalingMediaSection);
3996   std::string sdp = webrtc::SdpSerialize(jdesc_);
3997 
3998   EXPECT_NE(std::string::npos, sdp.find(kMediaSectionMsidLine));
3999   EXPECT_EQ(std::string::npos, sdp.find(kSsrcAttributeMsidLine));
4000 }
4001 
TEST_F(WebRtcSdpTest,SerializeOnlySsrcAttributeMsid)4002 TEST_F(WebRtcSdpTest, SerializeOnlySsrcAttributeMsid) {
4003   jdesc_.description()->set_msid_signaling(
4004       cricket::kMsidSignalingSsrcAttribute);
4005   std::string sdp = webrtc::SdpSerialize(jdesc_);
4006 
4007   EXPECT_EQ(std::string::npos, sdp.find(kMediaSectionMsidLine));
4008   EXPECT_NE(std::string::npos, sdp.find(kSsrcAttributeMsidLine));
4009 }
4010 
TEST_F(WebRtcSdpTest,SerializeBothMediaSectionAndSsrcAttributeMsid)4011 TEST_F(WebRtcSdpTest, SerializeBothMediaSectionAndSsrcAttributeMsid) {
4012   jdesc_.description()->set_msid_signaling(
4013       cricket::kMsidSignalingMediaSection |
4014       cricket::kMsidSignalingSsrcAttribute);
4015   std::string sdp = webrtc::SdpSerialize(jdesc_);
4016 
4017   EXPECT_NE(std::string::npos, sdp.find(kMediaSectionMsidLine));
4018   EXPECT_NE(std::string::npos, sdp.find(kSsrcAttributeMsidLine));
4019 }
4020 
4021 // Regression test for heap overflow bug:
4022 // https://bugs.chromium.org/p/chromium/issues/detail?id=647916
TEST_F(WebRtcSdpTest,DeserializeSctpPortInVideoDescription)4023 TEST_F(WebRtcSdpTest, DeserializeSctpPortInVideoDescription) {
4024   // The issue occurs when the sctp-port attribute is found in a video
4025   // description. The actual heap overflow occurs when parsing the fmtp line.
4026   static const char kSdpWithSctpPortInVideoDescription[] =
4027       "v=0\r\n"
4028       "o=- 18446744069414584320 18446462598732840960 IN IP4 127.0.0.1\r\n"
4029       "s=-\r\n"
4030       "t=0 0\r\n"
4031       "m=video 9 UDP/DTLS/SCTP 120\r\n"
4032       "a=sctp-port 5000\r\n"
4033       "a=fmtp:108 foo=10\r\n";
4034 
4035   ExpectParseFailure(std::string(kSdpWithSctpPortInVideoDescription),
4036                      "sctp-port");
4037 }
4038 
4039 // Regression test for integer overflow bug:
4040 // https://bugs.chromium.org/p/chromium/issues/detail?id=648071
TEST_F(WebRtcSdpTest,DeserializeLargeBandwidthLimit)4041 TEST_F(WebRtcSdpTest, DeserializeLargeBandwidthLimit) {
4042   // Bandwidth attribute is the max signed 32-bit int, which will get
4043   // multiplied by 1000 and cause int overflow if not careful.
4044   static const char kSdpWithLargeBandwidth[] =
4045       "v=0\r\n"
4046       "o=- 18446744069414584320 18446462598732840960 IN IP4 127.0.0.1\r\n"
4047       "s=-\r\n"
4048       "t=0 0\r\n"
4049       "m=video 3457 RTP/SAVPF 120\r\n"
4050       "b=AS:2147483647\r\n"
4051       "foo=fail\r\n";
4052 
4053   ExpectParseFailure(std::string(kSdpWithLargeBandwidth), "foo=fail");
4054 }
4055 
4056 // Similar to the above, except that negative values are illegal, not just
4057 // error-prone as large values are.
4058 // https://bugs.chromium.org/p/chromium/issues/detail?id=675361
TEST_F(WebRtcSdpTest,DeserializingNegativeBandwidthLimitFails)4059 TEST_F(WebRtcSdpTest, DeserializingNegativeBandwidthLimitFails) {
4060   static const char kSdpWithNegativeBandwidth[] =
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 3457 RTP/SAVPF 120\r\n"
4066       "b=AS:-1000\r\n";
4067 
4068   ExpectParseFailure(std::string(kSdpWithNegativeBandwidth), "b=AS:-1000");
4069 }
4070 
4071 // An exception to the above rule: a value of -1 for b=AS should just be
4072 // ignored, resulting in "kAutoBandwidth" in the deserialized object.
4073 // Applications historically may be using "b=AS:-1" to mean "no bandwidth
4074 // limit", but this is now what ommitting the attribute entirely will do, so
4075 // ignoring it will have the intended effect.
TEST_F(WebRtcSdpTest,BandwidthLimitOfNegativeOneIgnored)4076 TEST_F(WebRtcSdpTest, BandwidthLimitOfNegativeOneIgnored) {
4077   static const char kSdpWithBandwidthOfNegativeOne[] =
4078       "v=0\r\n"
4079       "o=- 18446744069414584320 18446462598732840960 IN IP4 127.0.0.1\r\n"
4080       "s=-\r\n"
4081       "t=0 0\r\n"
4082       "m=video 3457 RTP/SAVPF 120\r\n"
4083       "b=AS:-1\r\n";
4084 
4085   JsepSessionDescription jdesc_output(kDummyType);
4086   EXPECT_TRUE(SdpDeserialize(kSdpWithBandwidthOfNegativeOne, &jdesc_output));
4087   const VideoContentDescription* vcd =
4088       GetFirstVideoContentDescription(jdesc_output.description());
4089   ASSERT_TRUE(vcd);
4090   EXPECT_EQ(cricket::kAutoBandwidth, vcd->bandwidth());
4091 }
4092 
4093 // Test that "ufrag"/"pwd" in the candidate line itself are ignored, and only
4094 // the "a=ice-ufrag"/"a=ice-pwd" attributes are used.
4095 // Regression test for:
4096 // https://bugs.chromium.org/p/chromium/issues/detail?id=681286
TEST_F(WebRtcSdpTest,IceCredentialsInCandidateStringIgnored)4097 TEST_F(WebRtcSdpTest, IceCredentialsInCandidateStringIgnored) {
4098   // Important piece is "ufrag foo pwd bar".
4099   static const char kSdpWithIceCredentialsInCandidateString[] =
4100       "v=0\r\n"
4101       "o=- 18446744069414584320 18446462598732840960 IN IP4 127.0.0.1\r\n"
4102       "s=-\r\n"
4103       "t=0 0\r\n"
4104       "m=audio 9 RTP/SAVPF 111\r\n"
4105       "c=IN IP4 0.0.0.0\r\n"
4106       "a=rtcp:9 IN IP4 0.0.0.0\r\n"
4107       "a=ice-ufrag:ufrag_voice\r\na=ice-pwd:pwd_voice\r\n"
4108       "a=rtpmap:111 opus/48000/2\r\n"
4109       "a=candidate:a0+B/1 1 udp 2130706432 192.168.1.5 1234 typ host "
4110       "generation 2 ufrag foo pwd bar\r\n";
4111 
4112   JsepSessionDescription jdesc_output(kDummyType);
4113   EXPECT_TRUE(
4114       SdpDeserialize(kSdpWithIceCredentialsInCandidateString, &jdesc_output));
4115   const IceCandidateCollection* candidates = jdesc_output.candidates(0);
4116   ASSERT_NE(nullptr, candidates);
4117   ASSERT_EQ(1U, candidates->count());
4118   cricket::Candidate c = candidates->at(0)->candidate();
4119   EXPECT_EQ("ufrag_voice", c.username());
4120   EXPECT_EQ("pwd_voice", c.password());
4121 }
4122 
4123 // Test that attribute lines "a=ice-ufrag-something"/"a=ice-pwd-something" are
4124 // ignored, and only the "a=ice-ufrag"/"a=ice-pwd" attributes are used.
4125 // Regression test for:
4126 // https://bugs.chromium.org/p/webrtc/issues/detail?id=9712
TEST_F(WebRtcSdpTest,AttributeWithPartialMatchingNameIsIgnored)4127 TEST_F(WebRtcSdpTest, AttributeWithPartialMatchingNameIsIgnored) {
4128   static const char kSdpWithFooIceCredentials[] =
4129       "v=0\r\n"
4130       "o=- 18446744069414584320 18446462598732840960 IN IP4 127.0.0.1\r\n"
4131       "s=-\r\n"
4132       "t=0 0\r\n"
4133       "m=audio 9 RTP/SAVPF 111\r\n"
4134       "c=IN IP4 0.0.0.0\r\n"
4135       "a=rtcp:9 IN IP4 0.0.0.0\r\n"
4136       "a=ice-ufrag-something:foo\r\na=ice-pwd-something:bar\r\n"
4137       "a=ice-ufrag:ufrag_voice\r\na=ice-pwd:pwd_voice\r\n"
4138       "a=rtpmap:111 opus/48000/2\r\n"
4139       "a=candidate:a0+B/1 1 udp 2130706432 192.168.1.5 1234 typ host "
4140       "generation 2\r\n";
4141 
4142   JsepSessionDescription jdesc_output(kDummyType);
4143   EXPECT_TRUE(SdpDeserialize(kSdpWithFooIceCredentials, &jdesc_output));
4144   const IceCandidateCollection* candidates = jdesc_output.candidates(0);
4145   ASSERT_NE(nullptr, candidates);
4146   ASSERT_EQ(1U, candidates->count());
4147   cricket::Candidate c = candidates->at(0)->candidate();
4148   EXPECT_EQ("ufrag_voice", c.username());
4149   EXPECT_EQ("pwd_voice", c.password());
4150 }
4151 
4152 // Test that SDP with an invalid port number in "a=candidate" lines is
4153 // rejected, without crashing.
4154 // Regression test for:
4155 // https://bugs.chromium.org/p/chromium/issues/detail?id=677029
TEST_F(WebRtcSdpTest,DeserializeInvalidPortInCandidateAttribute)4156 TEST_F(WebRtcSdpTest, DeserializeInvalidPortInCandidateAttribute) {
4157   static const char kSdpWithInvalidCandidatePort[] =
4158       "v=0\r\n"
4159       "o=- 18446744069414584320 18446462598732840960 IN IP4 127.0.0.1\r\n"
4160       "s=-\r\n"
4161       "t=0 0\r\n"
4162       "m=audio 9 RTP/SAVPF 111\r\n"
4163       "c=IN IP4 0.0.0.0\r\n"
4164       "a=rtcp:9 IN IP4 0.0.0.0\r\n"
4165       "a=ice-ufrag:ufrag_voice\r\na=ice-pwd:pwd_voice\r\n"
4166       "a=rtpmap:111 opus/48000/2\r\n"
4167       "a=candidate:a0+B/1 1 udp 2130706432 192.168.1.5 12345678 typ host "
4168       "generation 2 raddr 192.168.1.1 rport 87654321\r\n";
4169 
4170   JsepSessionDescription jdesc_output(kDummyType);
4171   EXPECT_FALSE(SdpDeserialize(kSdpWithInvalidCandidatePort, &jdesc_output));
4172 }
4173 
4174 // Test that "a=msid" with a missing track ID is rejected and doesn't crash.
4175 // Regression test for:
4176 // https://bugs.chromium.org/p/chromium/issues/detail?id=686405
TEST_F(WebRtcSdpTest,DeserializeMsidAttributeWithMissingTrackId)4177 TEST_F(WebRtcSdpTest, DeserializeMsidAttributeWithMissingTrackId) {
4178   static const char kSdpWithMissingTrackId[] =
4179       "v=0\r\n"
4180       "o=- 18446744069414584320 18446462598732840960 IN IP4 127.0.0.1\r\n"
4181       "s=-\r\n"
4182       "t=0 0\r\n"
4183       "m=audio 9 RTP/SAVPF 111\r\n"
4184       "c=IN IP4 0.0.0.0\r\n"
4185       "a=rtpmap:111 opus/48000/2\r\n"
4186       "a=msid:stream_id \r\n";
4187 
4188   JsepSessionDescription jdesc_output(kDummyType);
4189   EXPECT_FALSE(SdpDeserialize(kSdpWithMissingTrackId, &jdesc_output));
4190 }
4191 
TEST_F(WebRtcSdpTest,DeserializeMsidAttributeWithMissingStreamId)4192 TEST_F(WebRtcSdpTest, DeserializeMsidAttributeWithMissingStreamId) {
4193   static const char kSdpWithMissingStreamId[] =
4194       "v=0\r\n"
4195       "o=- 18446744069414584320 18446462598732840960 IN IP4 127.0.0.1\r\n"
4196       "s=-\r\n"
4197       "t=0 0\r\n"
4198       "m=audio 9 RTP/SAVPF 111\r\n"
4199       "c=IN IP4 0.0.0.0\r\n"
4200       "a=rtpmap:111 opus/48000/2\r\n"
4201       "a=msid: track_id\r\n";
4202 
4203   JsepSessionDescription jdesc_output(kDummyType);
4204   EXPECT_FALSE(SdpDeserialize(kSdpWithMissingStreamId, &jdesc_output));
4205 }
4206 
4207 // Tests that if both session-level address and media-level address exist, use
4208 // the media-level address.
TEST_F(WebRtcSdpTest,ParseConnectionData)4209 TEST_F(WebRtcSdpTest, ParseConnectionData) {
4210   JsepSessionDescription jsep_desc(kDummyType);
4211 
4212   // Sesssion-level address.
4213   std::string sdp = kSdpFullString;
4214   InjectAfter("s=-\r\n", "c=IN IP4 192.168.0.3\r\n", &sdp);
4215   EXPECT_TRUE(SdpDeserialize(sdp, &jsep_desc));
4216 
4217   const auto& content1 = jsep_desc.description()->contents()[0];
4218   EXPECT_EQ("74.125.127.126:2345",
4219             content1.media_description()->connection_address().ToString());
4220   const auto& content2 = jsep_desc.description()->contents()[1];
4221   EXPECT_EQ("74.125.224.39:3457",
4222             content2.media_description()->connection_address().ToString());
4223 }
4224 
4225 // Tests that the session-level connection address will be used if the media
4226 // level-addresses are not specified.
TEST_F(WebRtcSdpTest,ParseConnectionDataSessionLevelOnly)4227 TEST_F(WebRtcSdpTest, ParseConnectionDataSessionLevelOnly) {
4228   JsepSessionDescription jsep_desc(kDummyType);
4229 
4230   // Sesssion-level address.
4231   std::string sdp = kSdpString;
4232   InjectAfter("s=-\r\n", "c=IN IP4 192.168.0.3\r\n", &sdp);
4233   // Remove the media level addresses.
4234   Replace("c=IN IP4 0.0.0.0\r\n", "", &sdp);
4235   Replace("c=IN IP4 0.0.0.0\r\n", "", &sdp);
4236   EXPECT_TRUE(SdpDeserialize(sdp, &jsep_desc));
4237 
4238   const auto& content1 = jsep_desc.description()->contents()[0];
4239   EXPECT_EQ("192.168.0.3:9",
4240             content1.media_description()->connection_address().ToString());
4241   const auto& content2 = jsep_desc.description()->contents()[1];
4242   EXPECT_EQ("192.168.0.3:9",
4243             content2.media_description()->connection_address().ToString());
4244 }
4245 
TEST_F(WebRtcSdpTest,ParseConnectionDataIPv6)4246 TEST_F(WebRtcSdpTest, ParseConnectionDataIPv6) {
4247   JsepSessionDescription jsep_desc(kDummyType);
4248 
4249   std::string sdp = kSdpString;
4250   EXPECT_TRUE(SdpDeserialize(sdp, &jsep_desc));
4251   Replace("m=audio 9 RTP/SAVPF 111 103 104\r\nc=IN IP4 0.0.0.0\r\n",
4252           "m=audio 9 RTP/SAVPF 111 103 104\r\nc=IN IP6 "
4253           "2001:0db8:85a3:0000:0000:8a2e:0370:7335\r\n",
4254           &sdp);
4255   Replace("m=video 9 RTP/SAVPF 120\r\nc=IN IP4 0.0.0.0\r\n",
4256           "m=video 9 RTP/SAVPF 120\r\nc=IN IP6 "
4257           "2001:0db8:85a3:0000:0000:8a2e:0370:7336\r\n",
4258           &sdp);
4259   EXPECT_TRUE(SdpDeserialize(sdp, &jsep_desc));
4260   const auto& content1 = jsep_desc.description()->contents()[0];
4261   EXPECT_EQ("[2001:db8:85a3::8a2e:370:7335]:9",
4262             content1.media_description()->connection_address().ToString());
4263   const auto& content2 = jsep_desc.description()->contents()[1];
4264   EXPECT_EQ("[2001:db8:85a3::8a2e:370:7336]:9",
4265             content2.media_description()->connection_address().ToString());
4266 }
4267 
4268 // Test that a c= line that contains a hostname connection address can be
4269 // parsed.
TEST_F(WebRtcSdpTest,ParseConnectionDataWithHostnameConnectionAddress)4270 TEST_F(WebRtcSdpTest, ParseConnectionDataWithHostnameConnectionAddress) {
4271   JsepSessionDescription jsep_desc(kDummyType);
4272   std::string sdp = kSdpString;
4273   EXPECT_TRUE(SdpDeserialize(sdp, &jsep_desc));
4274 
4275   sdp = kSdpString;
4276   Replace("c=IN IP4 0.0.0.0\r\n", "c=IN IP4 example.local\r\n", &sdp);
4277   Replace("c=IN IP4 0.0.0.0\r\n", "c=IN IP4 example.local\r\n", &sdp);
4278   ASSERT_TRUE(SdpDeserialize(sdp, &jsep_desc));
4279 
4280   ASSERT_NE(nullptr, jsep_desc.description());
4281   const auto& content1 = jsep_desc.description()->contents()[0];
4282   EXPECT_EQ("example.local:9",
4283             content1.media_description()->connection_address().ToString());
4284   const auto& content2 = jsep_desc.description()->contents()[1];
4285   EXPECT_EQ("example.local:9",
4286             content2.media_description()->connection_address().ToString());
4287 }
4288 
4289 // Test that the invalid or unsupported connection data cannot be parsed.
TEST_F(WebRtcSdpTest,ParseConnectionDataFailure)4290 TEST_F(WebRtcSdpTest, ParseConnectionDataFailure) {
4291   JsepSessionDescription jsep_desc(kDummyType);
4292   std::string sdp = kSdpString;
4293   EXPECT_TRUE(SdpDeserialize(sdp, &jsep_desc));
4294 
4295   // Unsupported multicast IPv4 address.
4296   sdp = kSdpFullString;
4297   Replace("c=IN IP4 74.125.224.39\r\n", "c=IN IP4 74.125.224.39/127\r\n", &sdp);
4298   EXPECT_FALSE(SdpDeserialize(sdp, &jsep_desc));
4299 
4300   // Unsupported multicast IPv6 address.
4301   sdp = kSdpFullString;
4302   Replace("c=IN IP4 74.125.224.39\r\n", "c=IN IP6 ::1/3\r\n", &sdp);
4303   EXPECT_FALSE(SdpDeserialize(sdp, &jsep_desc));
4304 
4305   // Mismatched address type.
4306   sdp = kSdpFullString;
4307   Replace("c=IN IP4 74.125.224.39\r\n", "c=IN IP6 74.125.224.39\r\n", &sdp);
4308   EXPECT_FALSE(SdpDeserialize(sdp, &jsep_desc));
4309 
4310   sdp = kSdpFullString;
4311   Replace("c=IN IP4 74.125.224.39\r\n",
4312           "c=IN IP4 2001:0db8:85a3:0000:0000:8a2e:0370:7334\r\n", &sdp);
4313   EXPECT_FALSE(SdpDeserialize(sdp, &jsep_desc));
4314 }
4315 
TEST_F(WebRtcSdpTest,SerializeAndDeserializeWithConnectionAddress)4316 TEST_F(WebRtcSdpTest, SerializeAndDeserializeWithConnectionAddress) {
4317   JsepSessionDescription expected_jsep(kDummyType);
4318   MakeDescriptionWithoutCandidates(&expected_jsep);
4319   // Serialization.
4320   std::string message = webrtc::SdpSerialize(expected_jsep);
4321   // Deserialization.
4322   JsepSessionDescription jdesc(kDummyType);
4323   EXPECT_TRUE(SdpDeserialize(message, &jdesc));
4324   auto audio_desc = jdesc.description()
4325                         ->GetContentByName(kAudioContentName)
4326                         ->media_description();
4327   auto video_desc = jdesc.description()
4328                         ->GetContentByName(kVideoContentName)
4329                         ->media_description();
4330   EXPECT_EQ(audio_desc_->connection_address().ToString(),
4331             audio_desc->connection_address().ToString());
4332   EXPECT_EQ(video_desc_->connection_address().ToString(),
4333             video_desc->connection_address().ToString());
4334 }
4335 
4336 // RFC4566 says "If a session has no meaningful name, the value "s= " SHOULD be
4337 // used (i.e., a single space as the session name)." So we should accept that.
TEST_F(WebRtcSdpTest,DeserializeEmptySessionName)4338 TEST_F(WebRtcSdpTest, DeserializeEmptySessionName) {
4339   JsepSessionDescription jsep_desc(kDummyType);
4340   std::string sdp = kSdpString;
4341   Replace("s=-\r\n", "s= \r\n", &sdp);
4342   EXPECT_TRUE(SdpDeserialize(sdp, &jsep_desc));
4343 }
4344 
4345 // Simulcast malformed input test for invalid format.
TEST_F(WebRtcSdpTest,DeserializeSimulcastNegative_EmptyAttribute)4346 TEST_F(WebRtcSdpTest, DeserializeSimulcastNegative_EmptyAttribute) {
4347   ExpectParseFailureWithNewLines("a=ssrc:3 label:video_track_id_1\r\n",
4348                                  "a=simulcast:\r\n", "a=simulcast:");
4349 }
4350 
4351 // Tests that duplicate simulcast entries in the SDP triggers a parse failure.
TEST_F(WebRtcSdpTest,DeserializeSimulcastNegative_DuplicateAttribute)4352 TEST_F(WebRtcSdpTest, DeserializeSimulcastNegative_DuplicateAttribute) {
4353   ExpectParseFailureWithNewLines("a=ssrc:3 label:video_track_id_1\r\n",
4354                                  "a=simulcast:send 1\r\na=simulcast:recv 2\r\n",
4355                                  "a=simulcast:");
4356 }
4357 
4358 // Validates that deserialization uses the a=simulcast: attribute
TEST_F(WebRtcSdpTest,TestDeserializeSimulcastAttribute)4359 TEST_F(WebRtcSdpTest, TestDeserializeSimulcastAttribute) {
4360   std::string sdp = kUnifiedPlanSdpFullStringNoSsrc;
4361   sdp += "a=rid:1 send\r\n";
4362   sdp += "a=rid:2 send\r\n";
4363   sdp += "a=rid:3 send\r\n";
4364   sdp += "a=rid:4 recv\r\n";
4365   sdp += "a=rid:5 recv\r\n";
4366   sdp += "a=rid:6 recv\r\n";
4367   sdp += "a=simulcast:send 1,2;3 recv 4;5;6\r\n";
4368   JsepSessionDescription output(kDummyType);
4369   SdpParseError error;
4370   EXPECT_TRUE(webrtc::SdpDeserialize(sdp, &output, &error));
4371   const cricket::ContentInfos& contents = output.description()->contents();
4372   const cricket::MediaContentDescription* media =
4373       contents.back().media_description();
4374   EXPECT_TRUE(media->HasSimulcast());
4375   EXPECT_EQ(2ul, media->simulcast_description().send_layers().size());
4376   EXPECT_EQ(3ul, media->simulcast_description().receive_layers().size());
4377   EXPECT_FALSE(media->streams().empty());
4378   const std::vector<RidDescription>& rids = media->streams()[0].rids();
4379   CompareRidDescriptionIds(rids, {"1", "2", "3"});
4380 }
4381 
4382 // Validates that deserialization removes rids that do not appear in SDP
TEST_F(WebRtcSdpTest,TestDeserializeSimulcastAttributeRemovesUnknownRids)4383 TEST_F(WebRtcSdpTest, TestDeserializeSimulcastAttributeRemovesUnknownRids) {
4384   std::string sdp = kUnifiedPlanSdpFullStringNoSsrc;
4385   sdp += "a=rid:1 send\r\n";
4386   sdp += "a=rid:3 send\r\n";
4387   sdp += "a=rid:4 recv\r\n";
4388   sdp += "a=simulcast:send 1,2;3 recv 4;5,6\r\n";
4389   JsepSessionDescription output(kDummyType);
4390   SdpParseError error;
4391   EXPECT_TRUE(webrtc::SdpDeserialize(sdp, &output, &error));
4392   const cricket::ContentInfos& contents = output.description()->contents();
4393   const cricket::MediaContentDescription* media =
4394       contents.back().media_description();
4395   EXPECT_TRUE(media->HasSimulcast());
4396   const SimulcastDescription& simulcast = media->simulcast_description();
4397   EXPECT_EQ(2ul, simulcast.send_layers().size());
4398   EXPECT_EQ(1ul, simulcast.receive_layers().size());
4399 
4400   std::vector<SimulcastLayer> all_send_layers =
4401       simulcast.send_layers().GetAllLayers();
4402   EXPECT_EQ(2ul, all_send_layers.size());
4403   EXPECT_EQ(0,
4404             absl::c_count_if(all_send_layers, [](const SimulcastLayer& layer) {
4405               return layer.rid == "2";
4406             }));
4407 
4408   std::vector<SimulcastLayer> all_receive_layers =
4409       simulcast.receive_layers().GetAllLayers();
4410   ASSERT_EQ(1ul, all_receive_layers.size());
4411   EXPECT_EQ("4", all_receive_layers[0].rid);
4412 
4413   EXPECT_FALSE(media->streams().empty());
4414   const std::vector<RidDescription>& rids = media->streams()[0].rids();
4415   CompareRidDescriptionIds(rids, {"1", "3"});
4416 }
4417 
4418 // Validates that Simulcast removes rids that appear in both send and receive.
TEST_F(WebRtcSdpTest,TestDeserializeSimulcastAttributeRemovesDuplicateSendReceive)4419 TEST_F(WebRtcSdpTest,
4420        TestDeserializeSimulcastAttributeRemovesDuplicateSendReceive) {
4421   std::string sdp = kUnifiedPlanSdpFullStringNoSsrc;
4422   sdp += "a=rid:1 send\r\n";
4423   sdp += "a=rid:2 send\r\n";
4424   sdp += "a=rid:3 send\r\n";
4425   sdp += "a=rid:4 recv\r\n";
4426   sdp += "a=simulcast:send 1;2;3 recv 2;4\r\n";
4427   JsepSessionDescription output(kDummyType);
4428   SdpParseError error;
4429   EXPECT_TRUE(webrtc::SdpDeserialize(sdp, &output, &error));
4430   const cricket::ContentInfos& contents = output.description()->contents();
4431   const cricket::MediaContentDescription* media =
4432       contents.back().media_description();
4433   EXPECT_TRUE(media->HasSimulcast());
4434   const SimulcastDescription& simulcast = media->simulcast_description();
4435   EXPECT_EQ(2ul, simulcast.send_layers().size());
4436   EXPECT_EQ(1ul, simulcast.receive_layers().size());
4437   EXPECT_EQ(2ul, simulcast.send_layers().GetAllLayers().size());
4438   EXPECT_EQ(1ul, simulcast.receive_layers().GetAllLayers().size());
4439 
4440   EXPECT_FALSE(media->streams().empty());
4441   const std::vector<RidDescription>& rids = media->streams()[0].rids();
4442   CompareRidDescriptionIds(rids, {"1", "3"});
4443 }
4444 
4445 // Ignores empty rid line.
TEST_F(WebRtcSdpTest,TestDeserializeIgnoresEmptyRidLines)4446 TEST_F(WebRtcSdpTest, TestDeserializeIgnoresEmptyRidLines) {
4447   std::string sdp = kUnifiedPlanSdpFullStringNoSsrc;
4448   sdp += "a=rid:1 send\r\n";
4449   sdp += "a=rid:2 send\r\n";
4450   sdp += "a=rid\r\n";   // Should ignore this line.
4451   sdp += "a=rid:\r\n";  // Should ignore this line.
4452   sdp += "a=simulcast:send 1;2\r\n";
4453   JsepSessionDescription output(kDummyType);
4454   SdpParseError error;
4455   EXPECT_TRUE(webrtc::SdpDeserialize(sdp, &output, &error));
4456   const cricket::ContentInfos& contents = output.description()->contents();
4457   const cricket::MediaContentDescription* media =
4458       contents.back().media_description();
4459   EXPECT_TRUE(media->HasSimulcast());
4460   const SimulcastDescription& simulcast = media->simulcast_description();
4461   EXPECT_TRUE(simulcast.receive_layers().empty());
4462   EXPECT_EQ(2ul, simulcast.send_layers().size());
4463   EXPECT_EQ(2ul, simulcast.send_layers().GetAllLayers().size());
4464 
4465   EXPECT_FALSE(media->streams().empty());
4466   const std::vector<RidDescription>& rids = media->streams()[0].rids();
4467   CompareRidDescriptionIds(rids, {"1", "2"});
4468 }
4469 
4470 // Ignores malformed rid lines.
TEST_F(WebRtcSdpTest,TestDeserializeIgnoresMalformedRidLines)4471 TEST_F(WebRtcSdpTest, TestDeserializeIgnoresMalformedRidLines) {
4472   std::string sdp = kUnifiedPlanSdpFullStringNoSsrc;
4473   sdp += "a=rid:1 send pt=\r\n";              // Should ignore this line.
4474   sdp += "a=rid:2 receive\r\n";               // Should ignore this line.
4475   sdp += "a=rid:3 max-width=720;pt=120\r\n";  // Should ignore this line.
4476   sdp += "a=rid:4\r\n";                       // Should ignore this line.
4477   sdp += "a=rid:5 send\r\n";
4478   sdp += "a=simulcast:send 1,2,3;4,5\r\n";
4479   JsepSessionDescription output(kDummyType);
4480   SdpParseError error;
4481   EXPECT_TRUE(webrtc::SdpDeserialize(sdp, &output, &error));
4482   const cricket::ContentInfos& contents = output.description()->contents();
4483   const cricket::MediaContentDescription* media =
4484       contents.back().media_description();
4485   EXPECT_TRUE(media->HasSimulcast());
4486   const SimulcastDescription& simulcast = media->simulcast_description();
4487   EXPECT_TRUE(simulcast.receive_layers().empty());
4488   EXPECT_EQ(1ul, simulcast.send_layers().size());
4489   EXPECT_EQ(1ul, simulcast.send_layers().GetAllLayers().size());
4490 
4491   EXPECT_FALSE(media->streams().empty());
4492   const std::vector<RidDescription>& rids = media->streams()[0].rids();
4493   CompareRidDescriptionIds(rids, {"5"});
4494 }
4495 
4496 // Removes RIDs that specify a different format than the m= section.
TEST_F(WebRtcSdpTest,TestDeserializeRemovesRidsWithInvalidCodec)4497 TEST_F(WebRtcSdpTest, TestDeserializeRemovesRidsWithInvalidCodec) {
4498   std::string sdp = kUnifiedPlanSdpFullStringNoSsrc;
4499   sdp += "a=rid:1 send pt=121,120\r\n";  // Should remove 121 and keep RID.
4500   sdp += "a=rid:2 send pt=121\r\n";      // Should remove RID altogether.
4501   sdp += "a=simulcast:send 1;2\r\n";
4502   JsepSessionDescription output(kDummyType);
4503   SdpParseError error;
4504   EXPECT_TRUE(webrtc::SdpDeserialize(sdp, &output, &error));
4505   const cricket::ContentInfos& contents = output.description()->contents();
4506   const cricket::MediaContentDescription* media =
4507       contents.back().media_description();
4508   EXPECT_TRUE(media->HasSimulcast());
4509   const SimulcastDescription& simulcast = media->simulcast_description();
4510   EXPECT_TRUE(simulcast.receive_layers().empty());
4511   EXPECT_EQ(1ul, simulcast.send_layers().size());
4512   EXPECT_EQ(1ul, simulcast.send_layers().GetAllLayers().size());
4513   EXPECT_EQ("1", simulcast.send_layers()[0][0].rid);
4514   EXPECT_EQ(1ul, media->streams().size());
4515   const std::vector<RidDescription>& rids = media->streams()[0].rids();
4516   EXPECT_EQ(1ul, rids.size());
4517   EXPECT_EQ("1", rids[0].rid);
4518   EXPECT_EQ(1ul, rids[0].payload_types.size());
4519   EXPECT_EQ(120, rids[0].payload_types[0]);
4520 }
4521 
4522 // Ignores duplicate rid lines
TEST_F(WebRtcSdpTest,TestDeserializeIgnoresDuplicateRidLines)4523 TEST_F(WebRtcSdpTest, TestDeserializeIgnoresDuplicateRidLines) {
4524   std::string sdp = kUnifiedPlanSdpFullStringNoSsrc;
4525   sdp += "a=rid:1 send\r\n";
4526   sdp += "a=rid:2 send\r\n";
4527   sdp += "a=rid:2 send\r\n";
4528   sdp += "a=rid:3 send\r\n";
4529   sdp += "a=rid:4 recv\r\n";
4530   sdp += "a=simulcast:send 1,2;3 recv 4\r\n";
4531   JsepSessionDescription output(kDummyType);
4532   SdpParseError error;
4533   EXPECT_TRUE(webrtc::SdpDeserialize(sdp, &output, &error));
4534   const cricket::ContentInfos& contents = output.description()->contents();
4535   const cricket::MediaContentDescription* media =
4536       contents.back().media_description();
4537   EXPECT_TRUE(media->HasSimulcast());
4538   const SimulcastDescription& simulcast = media->simulcast_description();
4539   EXPECT_EQ(2ul, simulcast.send_layers().size());
4540   EXPECT_EQ(1ul, simulcast.receive_layers().size());
4541   EXPECT_EQ(2ul, simulcast.send_layers().GetAllLayers().size());
4542   EXPECT_EQ(1ul, simulcast.receive_layers().GetAllLayers().size());
4543 
4544   EXPECT_FALSE(media->streams().empty());
4545   const std::vector<RidDescription>& rids = media->streams()[0].rids();
4546   CompareRidDescriptionIds(rids, {"1", "3"});
4547 }
4548 
TEST_F(WebRtcSdpTest,TestDeserializeRidSendDirection)4549 TEST_F(WebRtcSdpTest, TestDeserializeRidSendDirection) {
4550   std::string sdp = kUnifiedPlanSdpFullStringNoSsrc;
4551   sdp += "a=rid:1 recv\r\n";
4552   sdp += "a=rid:2 recv\r\n";
4553   sdp += "a=simulcast:send 1;2\r\n";
4554   JsepSessionDescription output(kDummyType);
4555   SdpParseError error;
4556   EXPECT_TRUE(webrtc::SdpDeserialize(sdp, &output, &error));
4557   const cricket::ContentInfos& contents = output.description()->contents();
4558   const cricket::MediaContentDescription* media =
4559       contents.back().media_description();
4560   EXPECT_FALSE(media->HasSimulcast());
4561 }
4562 
TEST_F(WebRtcSdpTest,TestDeserializeRidRecvDirection)4563 TEST_F(WebRtcSdpTest, TestDeserializeRidRecvDirection) {
4564   std::string sdp = kUnifiedPlanSdpFullStringNoSsrc;
4565   sdp += "a=rid:1 send\r\n";
4566   sdp += "a=rid:2 send\r\n";
4567   sdp += "a=simulcast:recv 1;2\r\n";
4568   JsepSessionDescription output(kDummyType);
4569   SdpParseError error;
4570   EXPECT_TRUE(webrtc::SdpDeserialize(sdp, &output, &error));
4571   const cricket::ContentInfos& contents = output.description()->contents();
4572   const cricket::MediaContentDescription* media =
4573       contents.back().media_description();
4574   EXPECT_FALSE(media->HasSimulcast());
4575 }
4576 
TEST_F(WebRtcSdpTest,TestDeserializeIgnoresWrongRidDirectionLines)4577 TEST_F(WebRtcSdpTest, TestDeserializeIgnoresWrongRidDirectionLines) {
4578   std::string sdp = kUnifiedPlanSdpFullStringNoSsrc;
4579   sdp += "a=rid:1 send\r\n";
4580   sdp += "a=rid:2 send\r\n";
4581   sdp += "a=rid:3 send\r\n";
4582   sdp += "a=rid:4 recv\r\n";
4583   sdp += "a=rid:5 recv\r\n";
4584   sdp += "a=rid:6 recv\r\n";
4585   sdp += "a=simulcast:send 1;5;3 recv 4;2;6\r\n";
4586   JsepSessionDescription output(kDummyType);
4587   SdpParseError error;
4588   EXPECT_TRUE(webrtc::SdpDeserialize(sdp, &output, &error));
4589   const cricket::ContentInfos& contents = output.description()->contents();
4590   const cricket::MediaContentDescription* media =
4591       contents.back().media_description();
4592   EXPECT_TRUE(media->HasSimulcast());
4593   const SimulcastDescription& simulcast = media->simulcast_description();
4594   EXPECT_EQ(2ul, simulcast.send_layers().size());
4595   EXPECT_EQ(2ul, simulcast.receive_layers().size());
4596   EXPECT_EQ(2ul, simulcast.send_layers().GetAllLayers().size());
4597   EXPECT_EQ(2ul, simulcast.receive_layers().GetAllLayers().size());
4598 
4599   EXPECT_FALSE(media->streams().empty());
4600   const std::vector<RidDescription>& rids = media->streams()[0].rids();
4601   CompareRidDescriptionIds(rids, {"1", "3"});
4602 }
4603 
4604 // Simulcast serialization integration test.
4605 // This test will serialize and deserialize the description and compare.
4606 // More detailed tests for parsing simulcast can be found in
4607 // unit tests for SdpSerializer.
TEST_F(WebRtcSdpTest,SerializeSimulcast_ComplexSerialization)4608 TEST_F(WebRtcSdpTest, SerializeSimulcast_ComplexSerialization) {
4609   MakeUnifiedPlanDescription(/* use_ssrcs = */ false);
4610   auto description = jdesc_.description();
4611   auto media = description->GetContentDescriptionByName(kVideoContentName3);
4612   ASSERT_EQ(media->streams().size(), 1ul);
4613   StreamParams& send_stream = media->mutable_streams()[0];
4614   std::vector<RidDescription> send_rids;
4615   send_rids.push_back(RidDescription("1", RidDirection::kSend));
4616   send_rids.push_back(RidDescription("2", RidDirection::kSend));
4617   send_rids.push_back(RidDescription("3", RidDirection::kSend));
4618   send_rids.push_back(RidDescription("4", RidDirection::kSend));
4619   send_stream.set_rids(send_rids);
4620   std::vector<RidDescription> receive_rids;
4621   receive_rids.push_back(RidDescription("5", RidDirection::kReceive));
4622   receive_rids.push_back(RidDescription("6", RidDirection::kReceive));
4623   receive_rids.push_back(RidDescription("7", RidDirection::kReceive));
4624   media->set_receive_rids(receive_rids);
4625 
4626   SimulcastDescription& simulcast = media->simulcast_description();
4627   simulcast.send_layers().AddLayerWithAlternatives(
4628       {SimulcastLayer("2", false), SimulcastLayer("1", true)});
4629   simulcast.send_layers().AddLayerWithAlternatives(
4630       {SimulcastLayer("4", false), SimulcastLayer("3", false)});
4631   simulcast.receive_layers().AddLayer({SimulcastLayer("5", false)});
4632   simulcast.receive_layers().AddLayer({SimulcastLayer("6", false)});
4633   simulcast.receive_layers().AddLayer({SimulcastLayer("7", false)});
4634 
4635   TestSerialize(jdesc_);
4636 }
4637 
4638 // Test that the content name is empty if the media section does not have an
4639 // a=mid line.
TEST_F(WebRtcSdpTest,ParseNoMid)4640 TEST_F(WebRtcSdpTest, ParseNoMid) {
4641   std::string sdp = kSdpString;
4642   Replace("a=mid:audio_content_name\r\n", "", &sdp);
4643   Replace("a=mid:video_content_name\r\n", "", &sdp);
4644 
4645   JsepSessionDescription output(kDummyType);
4646   SdpParseError error;
4647   ASSERT_TRUE(webrtc::SdpDeserialize(sdp, &output, &error));
4648 
4649   EXPECT_THAT(output.description()->contents(),
4650               ElementsAre(Field("name", &cricket::ContentInfo::name, ""),
4651                           Field("name", &cricket::ContentInfo::name, "")));
4652 }
4653 
TEST_F(WebRtcSdpTest,SerializeWithDefaultSctpProtocol)4654 TEST_F(WebRtcSdpTest, SerializeWithDefaultSctpProtocol) {
4655   AddSctpDataChannel(false);  // Don't use sctpmap
4656   JsepSessionDescription jsep_desc(kDummyType);
4657   MakeDescriptionWithoutCandidates(&jsep_desc);
4658   std::string message = webrtc::SdpSerialize(jsep_desc);
4659   EXPECT_NE(std::string::npos,
4660             message.find(cricket::kMediaProtocolUdpDtlsSctp));
4661 }
4662 
TEST_F(WebRtcSdpTest,DeserializeWithAllSctpProtocols)4663 TEST_F(WebRtcSdpTest, DeserializeWithAllSctpProtocols) {
4664   AddSctpDataChannel(false);
4665   std::string protocols[] = {cricket::kMediaProtocolDtlsSctp,
4666                              cricket::kMediaProtocolUdpDtlsSctp,
4667                              cricket::kMediaProtocolTcpDtlsSctp};
4668   for (const auto& protocol : protocols) {
4669     sctp_desc_->set_protocol(protocol);
4670     JsepSessionDescription jsep_desc(kDummyType);
4671     MakeDescriptionWithoutCandidates(&jsep_desc);
4672     std::string message = webrtc::SdpSerialize(jsep_desc);
4673     EXPECT_NE(std::string::npos, message.find(protocol));
4674     JsepSessionDescription jsep_output(kDummyType);
4675     SdpParseError error;
4676     EXPECT_TRUE(webrtc::SdpDeserialize(message, &jsep_output, &error));
4677   }
4678 }
4679 
4680 // According to https://tools.ietf.org/html/rfc5576#section-6.1, the CNAME
4681 // attribute is mandatory, but we relax that restriction.
TEST_F(WebRtcSdpTest,DeserializeSessionDescriptionWithoutCname)4682 TEST_F(WebRtcSdpTest, DeserializeSessionDescriptionWithoutCname) {
4683   std::string sdp_without_cname = kSdpFullString;
4684   Replace("a=ssrc:1 cname:stream_1_cname\r\n", "", &sdp_without_cname);
4685   JsepSessionDescription new_jdesc(kDummyType);
4686   EXPECT_TRUE(SdpDeserialize(sdp_without_cname, &new_jdesc));
4687 
4688   audio_desc_->mutable_streams()[0].cname = "";
4689   ASSERT_TRUE(jdesc_.Initialize(desc_.Clone(), jdesc_.session_id(),
4690                                 jdesc_.session_version()));
4691   EXPECT_TRUE(CompareSessionDescription(jdesc_, new_jdesc));
4692 }
4693