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