1/*
2 * libjingle
3 * Copyright 2011, Google Inc.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 *
8 *  1. Redistributions of source code must retain the above copyright notice,
9 *     this list of conditions and the following disclaimer.
10 *  2. Redistributions in binary form must reproduce the above copyright notice,
11 *     this list of conditions and the following disclaimer in the documentation
12 *     and/or other materials provided with the distribution.
13 *  3. The name of the author may not be used to endorse or promote products
14 *     derived from this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
17 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
18 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
19 * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
20 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
21 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
22 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
23 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
24 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
25 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27
28#include <set>
29#include <string>
30#include <vector>
31
32#include "talk/app/webrtc/jsepsessiondescription.h"
33#include "talk/app/webrtc/webrtcsdp.h"
34#include "talk/media/base/constants.h"
35#include "talk/p2p/base/constants.h"
36#include "talk/session/media/mediasession.h"
37#include "webrtc/base/gunit.h"
38#include "webrtc/base/logging.h"
39#include "webrtc/base/messagedigest.h"
40#include "webrtc/base/scoped_ptr.h"
41#include "webrtc/base/sslfingerprint.h"
42#include "webrtc/base/stringencode.h"
43#include "webrtc/base/stringutils.h"
44
45using cricket::AudioCodec;
46using cricket::AudioContentDescription;
47using cricket::Candidate;
48using cricket::ContentInfo;
49using cricket::CryptoParams;
50using cricket::ContentGroup;
51using cricket::DataCodec;
52using cricket::DataContentDescription;
53using cricket::ICE_CANDIDATE_COMPONENT_RTCP;
54using cricket::ICE_CANDIDATE_COMPONENT_RTP;
55using cricket::kFecSsrcGroupSemantics;
56using cricket::LOCAL_PORT_TYPE;
57using cricket::NS_JINGLE_DRAFT_SCTP;
58using cricket::NS_JINGLE_ICE_UDP;
59using cricket::NS_JINGLE_RTP;
60using cricket::RtpHeaderExtension;
61using cricket::RELAY_PORT_TYPE;
62using cricket::SessionDescription;
63using cricket::StreamParams;
64using cricket::STUN_PORT_TYPE;
65using cricket::TransportDescription;
66using cricket::TransportInfo;
67using cricket::VideoCodec;
68using cricket::VideoContentDescription;
69using webrtc::IceCandidateCollection;
70using webrtc::IceCandidateInterface;
71using webrtc::JsepIceCandidate;
72using webrtc::JsepSessionDescription;
73using webrtc::SdpParseError;
74using webrtc::SessionDescriptionInterface;
75
76typedef std::vector<AudioCodec> AudioCodecs;
77typedef std::vector<Candidate> Candidates;
78
79static const uint32 kDefaultSctpPort = 5000;
80static const char kSessionTime[] = "t=0 0\r\n";
81static const uint32 kCandidatePriority = 2130706432U;  // pref = 1.0
82static const char kCandidateUfragVoice[] = "ufrag_voice";
83static const char kCandidatePwdVoice[] = "pwd_voice";
84static const char kAttributeIcePwdVoice[] = "a=ice-pwd:pwd_voice\r\n";
85static const char kCandidateUfragVideo[] = "ufrag_video";
86static const char kCandidatePwdVideo[] = "pwd_video";
87static const char kCandidateUfragData[] = "ufrag_data";
88static const char kCandidatePwdData[] = "pwd_data";
89static const char kAttributeIcePwdVideo[] = "a=ice-pwd:pwd_video\r\n";
90static const uint32 kCandidateGeneration = 2;
91static const char kCandidateFoundation1[] = "a0+B/1";
92static const char kCandidateFoundation2[] = "a0+B/2";
93static const char kCandidateFoundation3[] = "a0+B/3";
94static const char kCandidateFoundation4[] = "a0+B/4";
95static const char kAttributeCryptoVoice[] =
96    "a=crypto:1 AES_CM_128_HMAC_SHA1_32 "
97    "inline:NzB4d1BINUAvLEw6UzF3WSJ+PSdFcGdUJShpX1Zj|2^20|1:32 "
98    "dummy_session_params\r\n";
99static const char kAttributeCryptoVideo[] =
100    "a=crypto:1 AES_CM_128_HMAC_SHA1_80 "
101    "inline:d0RmdmcmVCspeEc3QGZiNWpVLFJhQX1cfHAwJSoj|2^20|1:32\r\n";
102static const char kFingerprint[] = "a=fingerprint:sha-1 "
103    "4A:AD:B9:B1:3F:82:18:3B:54:02:12:DF:3E:5D:49:6B:19:E5:7C:AB\r\n";
104static const int kExtmapId = 1;
105static const char kExtmapUri[] = "http://example.com/082005/ext.htm#ttime";
106static const char kExtmap[] =
107    "a=extmap:1 http://example.com/082005/ext.htm#ttime\r\n";
108static const char kExtmapWithDirectionAndAttribute[] =
109    "a=extmap:1/sendrecv http://example.com/082005/ext.htm#ttime a1 a2\r\n";
110
111static const uint8 kIdentityDigest[] = {0x4A, 0xAD, 0xB9, 0xB1,
112                                        0x3F, 0x82, 0x18, 0x3B,
113                                        0x54, 0x02, 0x12, 0xDF,
114                                        0x3E, 0x5D, 0x49, 0x6B,
115                                        0x19, 0xE5, 0x7C, 0xAB};
116
117struct CodecParams {
118  int max_ptime;
119  int ptime;
120  int min_ptime;
121  int sprop_stereo;
122  int stereo;
123  int useinband;
124  int maxaveragebitrate;
125};
126
127// Reference sdp string
128static const char kSdpFullString[] =
129    "v=0\r\n"
130    "o=- 18446744069414584320 18446462598732840960 IN IP4 127.0.0.1\r\n"
131    "s=-\r\n"
132    "t=0 0\r\n"
133    "a=msid-semantic: WMS local_stream_1 local_stream_2\r\n"
134    "m=audio 2345 RTP/SAVPF 111 103 104\r\n"
135    "c=IN IP4 74.125.127.126\r\n"
136    "a=rtcp:2347 IN IP4 74.125.127.126\r\n"
137    "a=candidate:a0+B/1 1 udp 2130706432 192.168.1.5 1234 typ host "
138    "generation 2\r\n"
139    "a=candidate:a0+B/1 2 udp 2130706432 192.168.1.5 1235 typ host "
140    "generation 2\r\n"
141    "a=candidate:a0+B/2 1 udp 2130706432 ::1 1238 typ host "
142    "generation 2\r\n"
143    "a=candidate:a0+B/2 2 udp 2130706432 ::1 1239 typ host "
144    "generation 2\r\n"
145    "a=candidate:a0+B/3 1 udp 2130706432 74.125.127.126 2345 typ srflx "
146    "raddr 192.168.1.5 rport 2346 "
147    "generation 2\r\n"
148    "a=candidate:a0+B/3 2 udp 2130706432 74.125.127.126 2347 typ srflx "
149    "raddr 192.168.1.5 rport 2348 "
150    "generation 2\r\n"
151    "a=ice-ufrag:ufrag_voice\r\na=ice-pwd:pwd_voice\r\n"
152    "a=mid:audio_content_name\r\n"
153    "a=sendrecv\r\n"
154    "a=rtcp-mux\r\n"
155    "a=crypto:1 AES_CM_128_HMAC_SHA1_32 "
156    "inline:NzB4d1BINUAvLEw6UzF3WSJ+PSdFcGdUJShpX1Zj|2^20|1:32 "
157    "dummy_session_params\r\n"
158    "a=rtpmap:111 opus/48000/2\r\n"
159    "a=rtpmap:103 ISAC/16000\r\n"
160    "a=rtpmap:104 CELT/32000/2\r\n"
161    "a=ssrc:1 cname:stream_1_cname\r\n"
162    "a=ssrc:1 msid:local_stream_1 audio_track_id_1\r\n"
163    "a=ssrc:1 mslabel:local_stream_1\r\n"
164    "a=ssrc:1 label:audio_track_id_1\r\n"
165    "a=ssrc:4 cname:stream_2_cname\r\n"
166    "a=ssrc:4 msid:local_stream_2 audio_track_id_2\r\n"
167    "a=ssrc:4 mslabel:local_stream_2\r\n"
168    "a=ssrc:4 label:audio_track_id_2\r\n"
169    "m=video 3457 RTP/SAVPF 120\r\n"
170    "c=IN IP4 74.125.224.39\r\n"
171    "a=rtcp:3456 IN IP4 74.125.224.39\r\n"
172    "a=candidate:a0+B/1 2 udp 2130706432 192.168.1.5 1236 typ host "
173    "generation 2\r\n"
174    "a=candidate:a0+B/1 1 udp 2130706432 192.168.1.5 1237 typ host "
175    "generation 2\r\n"
176    "a=candidate:a0+B/2 2 udp 2130706432 ::1 1240 typ host "
177    "generation 2\r\n"
178    "a=candidate:a0+B/2 1 udp 2130706432 ::1 1241 typ host "
179    "generation 2\r\n"
180    "a=candidate:a0+B/4 2 udp 2130706432 74.125.224.39 3456 typ relay "
181    "generation 2\r\n"
182    "a=candidate:a0+B/4 1 udp 2130706432 74.125.224.39 3457 typ relay "
183    "generation 2\r\n"
184    "a=ice-ufrag:ufrag_video\r\na=ice-pwd:pwd_video\r\n"
185    "a=mid:video_content_name\r\n"
186    "a=sendrecv\r\n"
187    "a=crypto:1 AES_CM_128_HMAC_SHA1_80 "
188    "inline:d0RmdmcmVCspeEc3QGZiNWpVLFJhQX1cfHAwJSoj|2^20|1:32\r\n"
189    "a=rtpmap:120 VP8/90000\r\n"
190    "a=ssrc:2 cname:stream_1_cname\r\n"
191    "a=ssrc:2 msid:local_stream_1 video_track_id_1\r\n"
192    "a=ssrc:2 mslabel:local_stream_1\r\n"
193    "a=ssrc:2 label:video_track_id_1\r\n"
194    "a=ssrc:3 cname:stream_1_cname\r\n"
195    "a=ssrc:3 msid:local_stream_1 video_track_id_2\r\n"
196    "a=ssrc:3 mslabel:local_stream_1\r\n"
197    "a=ssrc:3 label:video_track_id_2\r\n"
198    "a=ssrc-group:FEC 5 6\r\n"
199    "a=ssrc:5 cname:stream_2_cname\r\n"
200    "a=ssrc:5 msid:local_stream_2 video_track_id_3\r\n"
201    "a=ssrc:5 mslabel:local_stream_2\r\n"
202    "a=ssrc:5 label:video_track_id_3\r\n"
203    "a=ssrc:6 cname:stream_2_cname\r\n"
204    "a=ssrc:6 msid:local_stream_2 video_track_id_3\r\n"
205    "a=ssrc:6 mslabel:local_stream_2\r\n"
206    "a=ssrc:6 label:video_track_id_3\r\n";
207
208// SDP reference string without the candidates.
209static const char kSdpString[] =
210    "v=0\r\n"
211    "o=- 18446744069414584320 18446462598732840960 IN IP4 127.0.0.1\r\n"
212    "s=-\r\n"
213    "t=0 0\r\n"
214    "a=msid-semantic: WMS local_stream_1 local_stream_2\r\n"
215    "m=audio 1 RTP/SAVPF 111 103 104\r\n"
216    "c=IN IP4 0.0.0.0\r\n"
217    "a=rtcp:1 IN IP4 0.0.0.0\r\n"
218    "a=ice-ufrag:ufrag_voice\r\na=ice-pwd:pwd_voice\r\n"
219    "a=mid:audio_content_name\r\n"
220    "a=sendrecv\r\n"
221    "a=rtcp-mux\r\n"
222    "a=crypto:1 AES_CM_128_HMAC_SHA1_32 "
223    "inline:NzB4d1BINUAvLEw6UzF3WSJ+PSdFcGdUJShpX1Zj|2^20|1:32 "
224    "dummy_session_params\r\n"
225    "a=rtpmap:111 opus/48000/2\r\n"
226    "a=rtpmap:103 ISAC/16000\r\n"
227    "a=rtpmap:104 CELT/32000/2\r\n"
228    "a=ssrc:1 cname:stream_1_cname\r\n"
229    "a=ssrc:1 msid:local_stream_1 audio_track_id_1\r\n"
230    "a=ssrc:1 mslabel:local_stream_1\r\n"
231    "a=ssrc:1 label:audio_track_id_1\r\n"
232    "a=ssrc:4 cname:stream_2_cname\r\n"
233    "a=ssrc:4 msid:local_stream_2 audio_track_id_2\r\n"
234    "a=ssrc:4 mslabel:local_stream_2\r\n"
235    "a=ssrc:4 label:audio_track_id_2\r\n"
236    "m=video 1 RTP/SAVPF 120\r\n"
237    "c=IN IP4 0.0.0.0\r\n"
238    "a=rtcp:1 IN IP4 0.0.0.0\r\n"
239    "a=ice-ufrag:ufrag_video\r\na=ice-pwd:pwd_video\r\n"
240    "a=mid:video_content_name\r\n"
241    "a=sendrecv\r\n"
242    "a=crypto:1 AES_CM_128_HMAC_SHA1_80 "
243    "inline:d0RmdmcmVCspeEc3QGZiNWpVLFJhQX1cfHAwJSoj|2^20|1:32\r\n"
244    "a=rtpmap:120 VP8/90000\r\n"
245    "a=ssrc:2 cname:stream_1_cname\r\n"
246    "a=ssrc:2 msid:local_stream_1 video_track_id_1\r\n"
247    "a=ssrc:2 mslabel:local_stream_1\r\n"
248    "a=ssrc:2 label:video_track_id_1\r\n"
249    "a=ssrc:3 cname:stream_1_cname\r\n"
250    "a=ssrc:3 msid:local_stream_1 video_track_id_2\r\n"
251    "a=ssrc:3 mslabel:local_stream_1\r\n"
252    "a=ssrc:3 label:video_track_id_2\r\n"
253    "a=ssrc-group:FEC 5 6\r\n"
254    "a=ssrc:5 cname:stream_2_cname\r\n"
255    "a=ssrc:5 msid:local_stream_2 video_track_id_3\r\n"
256    "a=ssrc:5 mslabel:local_stream_2\r\n"
257    "a=ssrc:5 label:video_track_id_3\r\n"
258    "a=ssrc:6 cname:stream_2_cname\r\n"
259    "a=ssrc:6 msid:local_stream_2 video_track_id_3\r\n"
260    "a=ssrc:6 mslabel:local_stream_2\r\n"
261    "a=ssrc:6 label:video_track_id_3\r\n";
262
263static const char kSdpRtpDataChannelString[] =
264    "m=application 1 RTP/SAVPF 101\r\n"
265    "c=IN IP4 0.0.0.0\r\n"
266    "a=rtcp:1 IN IP4 0.0.0.0\r\n"
267    "a=ice-ufrag:ufrag_data\r\n"
268    "a=ice-pwd:pwd_data\r\n"
269    "a=mid:data_content_name\r\n"
270    "a=sendrecv\r\n"
271    "a=crypto:1 AES_CM_128_HMAC_SHA1_80 "
272    "inline:FvLcvU2P3ZWmQxgPAgcDu7Zl9vftYElFOjEzhWs5\r\n"
273    "a=rtpmap:101 google-data/90000\r\n"
274    "a=ssrc:10 cname:data_channel_cname\r\n"
275    "a=ssrc:10 msid:data_channel data_channeld0\r\n"
276    "a=ssrc:10 mslabel:data_channel\r\n"
277    "a=ssrc:10 label:data_channeld0\r\n";
278
279static const char kSdpSctpDataChannelString[] =
280    "m=application 1 DTLS/SCTP 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    "a=sctpmap:5000 webrtc-datachannel 1024\r\n";
286
287// draft-ietf-mmusic-sctp-sdp-07
288static const char kSdpSctpDataChannelStringWithSctpPort[] =
289    "m=application 1 DTLS/SCTP webrtc-datachannel\r\n"
290    "a=fmtp:webrtc-datachannel max-message-size=100000\r\n"
291    "a=sctp-port 5000\r\n"
292    "c=IN IP4 0.0.0.0\r\n"
293    "a=ice-ufrag:ufrag_data\r\n"
294    "a=ice-pwd:pwd_data\r\n"
295    "a=mid:data_content_name\r\n";
296
297static const char kSdpSctpDataChannelWithCandidatesString[] =
298    "m=application 2345 DTLS/SCTP 5000\r\n"
299    "c=IN IP4 74.125.127.126\r\n"
300    "a=candidate:a0+B/1 1 udp 2130706432 192.168.1.5 1234 typ host "
301    "generation 2\r\n"
302    "a=candidate:a0+B/2 1 udp 2130706432 ::1 1238 typ host "
303    "generation 2\r\n"
304    "a=candidate:a0+B/3 1 udp 2130706432 74.125.127.126 2345 typ srflx "
305    "raddr 192.168.1.5 rport 2346 "
306    "generation 2\r\n"
307    "a=ice-ufrag:ufrag_data\r\n"
308    "a=ice-pwd:pwd_data\r\n"
309    "a=mid:data_content_name\r\n"
310    "a=sctpmap:5000 webrtc-datachannel 1024\r\n";
311
312static const char kSdpConferenceString[] =
313    "v=0\r\n"
314    "o=- 18446744069414584320 18446462598732840960 IN IP4 127.0.0.1\r\n"
315    "s=-\r\n"
316    "t=0 0\r\n"
317    "a=msid-semantic: WMS\r\n"
318    "m=audio 1 RTP/SAVPF 111 103 104\r\n"
319    "c=IN IP4 0.0.0.0\r\n"
320    "a=x-google-flag:conference\r\n"
321    "m=video 1 RTP/SAVPF 120\r\n"
322    "c=IN IP4 0.0.0.0\r\n"
323    "a=x-google-flag:conference\r\n";
324
325static const char kSdpSessionString[] =
326    "v=0\r\n"
327    "o=- 18446744069414584320 18446462598732840960 IN IP4 127.0.0.1\r\n"
328    "s=-\r\n"
329    "t=0 0\r\n"
330    "a=msid-semantic: WMS local_stream\r\n";
331
332static const char kSdpAudioString[] =
333    "m=audio 1 RTP/SAVPF 111\r\n"
334    "c=IN IP4 0.0.0.0\r\n"
335    "a=rtcp:1 IN IP4 0.0.0.0\r\n"
336    "a=ice-ufrag:ufrag_voice\r\na=ice-pwd:pwd_voice\r\n"
337    "a=mid:audio_content_name\r\n"
338    "a=sendrecv\r\n"
339    "a=rtpmap:111 opus/48000/2\r\n"
340    "a=ssrc:1 cname:stream_1_cname\r\n"
341    "a=ssrc:1 msid:local_stream audio_track_id_1\r\n"
342    "a=ssrc:1 mslabel:local_stream\r\n"
343    "a=ssrc:1 label:audio_track_id_1\r\n";
344
345static const char kSdpVideoString[] =
346    "m=video 1 RTP/SAVPF 120\r\n"
347    "c=IN IP4 0.0.0.0\r\n"
348    "a=rtcp:1 IN IP4 0.0.0.0\r\n"
349    "a=ice-ufrag:ufrag_video\r\na=ice-pwd:pwd_video\r\n"
350    "a=mid:video_content_name\r\n"
351    "a=sendrecv\r\n"
352    "a=rtpmap:120 VP8/90000\r\n"
353    "a=ssrc:2 cname:stream_1_cname\r\n"
354    "a=ssrc:2 msid:local_stream video_track_id_1\r\n"
355    "a=ssrc:2 mslabel:local_stream\r\n"
356    "a=ssrc:2 label:video_track_id_1\r\n";
357
358
359// One candidate reference string as per W3c spec.
360// candidate:<blah> not a=candidate:<blah>CRLF
361static const char kRawCandidate[] =
362    "candidate:a0+B/1 1 udp 2130706432 192.168.1.5 1234 typ host generation 2";
363// One candidate reference string.
364static const char kSdpOneCandidate[] =
365    "a=candidate:a0+B/1 1 udp 2130706432 192.168.1.5 1234 typ host "
366    "generation 2\r\n";
367
368static const char kSdpTcpActiveCandidate[] =
369    "candidate:a0+B/1 1 tcp 2130706432 192.168.1.5 9 typ host "
370    "tcptype active generation 2";
371static const char kSdpTcpPassiveCandidate[] =
372    "candidate:a0+B/1 1 tcp 2130706432 192.168.1.5 9 typ host "
373    "tcptype passive generation 2";
374static const char kSdpTcpSOCandidate[] =
375    "candidate:a0+B/1 1 tcp 2130706432 192.168.1.5 9 typ host "
376    "tcptype so generation 2";
377static const char kSdpTcpInvalidCandidate[] =
378    "candidate:a0+B/1 1 tcp 2130706432 192.168.1.5 9 typ host "
379    "tcptype invalid generation 2";
380
381// One candidate reference string with IPV6 address.
382static const char kRawIPV6Candidate[] =
383    "candidate:a0+B/1 1 udp 2130706432 "
384    "abcd::abcd::abcd::abcd::abcd::abcd::abcd::abcd 1234 typ host generation 2";
385
386// One candidate reference string.
387static const char kSdpOneCandidateOldFormat[] =
388    "a=candidate:a0+B/1 1 udp 2130706432 192.168.1.5 1234 typ host network_name"
389    " eth0 username user_rtp password password_rtp generation 2\r\n";
390
391// Session id and version
392static const char kSessionId[] = "18446744069414584320";
393static const char kSessionVersion[] = "18446462598732840960";
394
395// Ice options
396static const char kIceOption1[] = "iceoption1";
397static const char kIceOption2[] = "iceoption2";
398static const char kIceOption3[] = "iceoption3";
399
400// Content name
401static const char kAudioContentName[] = "audio_content_name";
402static const char kVideoContentName[] = "video_content_name";
403static const char kDataContentName[] = "data_content_name";
404
405// MediaStream 1
406static const char kStreamLabel1[] = "local_stream_1";
407static const char kStream1Cname[] = "stream_1_cname";
408static const char kAudioTrackId1[] = "audio_track_id_1";
409static const uint32 kAudioTrack1Ssrc = 1;
410static const char kVideoTrackId1[] = "video_track_id_1";
411static const uint32 kVideoTrack1Ssrc = 2;
412static const char kVideoTrackId2[] = "video_track_id_2";
413static const uint32 kVideoTrack2Ssrc = 3;
414
415// MediaStream 2
416static const char kStreamLabel2[] = "local_stream_2";
417static const char kStream2Cname[] = "stream_2_cname";
418static const char kAudioTrackId2[] = "audio_track_id_2";
419static const uint32 kAudioTrack2Ssrc = 4;
420static const char kVideoTrackId3[] = "video_track_id_3";
421static const uint32 kVideoTrack3Ssrc = 5;
422static const uint32 kVideoTrack4Ssrc = 6;
423
424// DataChannel
425static const char kDataChannelLabel[] = "data_channel";
426static const char kDataChannelMsid[] = "data_channeld0";
427static const char kDataChannelCname[] = "data_channel_cname";
428static const uint32 kDataChannelSsrc = 10;
429
430// Candidate
431static const char kDummyMid[] = "dummy_mid";
432static const int kDummyIndex = 123;
433
434// Misc
435static const char kDummyString[] = "dummy";
436
437// Helper functions
438
439static bool SdpDeserialize(const std::string& message,
440                           JsepSessionDescription* jdesc) {
441  return webrtc::SdpDeserialize(message, jdesc, NULL);
442}
443
444static bool SdpDeserializeCandidate(const std::string& message,
445                                    JsepIceCandidate* candidate) {
446  return webrtc::SdpDeserializeCandidate(message, candidate, NULL);
447}
448
449// Add some extra |newlines| to the |message| after |line|.
450static void InjectAfter(const std::string& line,
451                        const std::string& newlines,
452                        std::string* message) {
453  const std::string tmp = line + newlines;
454  rtc::replace_substrs(line.c_str(), line.length(),
455                             tmp.c_str(), tmp.length(), message);
456}
457
458static void Replace(const std::string& line,
459                    const std::string& newlines,
460                    std::string* message) {
461  rtc::replace_substrs(line.c_str(), line.length(),
462                             newlines.c_str(), newlines.length(), message);
463}
464
465// Expect fail to parase |bad_sdp| and expect |bad_part| be part of the error
466// message.
467static void ExpectParseFailure(const std::string& bad_sdp,
468                               const std::string& bad_part) {
469  JsepSessionDescription desc(kDummyString);
470  SdpParseError error;
471  bool ret = webrtc::SdpDeserialize(bad_sdp, &desc, &error);
472  EXPECT_FALSE(ret);
473  EXPECT_NE(std::string::npos, error.line.find(bad_part.c_str()));
474}
475
476// Expect fail to parse kSdpFullString if replace |good_part| with |bad_part|.
477static void ExpectParseFailure(const char* good_part, const char* bad_part) {
478  std::string bad_sdp = kSdpFullString;
479  Replace(good_part, bad_part, &bad_sdp);
480  ExpectParseFailure(bad_sdp, bad_part);
481}
482
483// Expect fail to parse kSdpFullString if add |newlines| after |injectpoint|.
484static void ExpectParseFailureWithNewLines(const std::string& injectpoint,
485                                           const std::string& newlines,
486                                           const std::string& bad_part) {
487  std::string bad_sdp = kSdpFullString;
488  InjectAfter(injectpoint, newlines, &bad_sdp);
489  ExpectParseFailure(bad_sdp, bad_part);
490}
491
492static void ReplaceDirection(cricket::MediaContentDirection direction,
493                             std::string* message) {
494  std::string new_direction;
495  switch (direction) {
496    case cricket::MD_INACTIVE:
497      new_direction = "a=inactive";
498      break;
499    case cricket::MD_SENDONLY:
500      new_direction = "a=sendonly";
501      break;
502    case cricket::MD_RECVONLY:
503      new_direction = "a=recvonly";
504      break;
505    case cricket::MD_SENDRECV:
506    default:
507      new_direction = "a=sendrecv";
508      break;
509  }
510  Replace("a=sendrecv", new_direction, message);
511}
512
513static void ReplaceRejected(bool audio_rejected, bool video_rejected,
514                            std::string* message) {
515  if (audio_rejected) {
516    Replace("m=audio 2345", "m=audio 0", message);
517  }
518  if (video_rejected) {
519    Replace("m=video 3457", "m=video 0", message);
520  }
521}
522
523// WebRtcSdpTest
524
525class WebRtcSdpTest : public testing::Test {
526 public:
527  WebRtcSdpTest()
528     : jdesc_(kDummyString) {
529    // AudioContentDescription
530    audio_desc_ = CreateAudioContentDescription();
531    AudioCodec opus(111, "opus", 48000, 0, 2, 3);
532    audio_desc_->AddCodec(opus);
533    audio_desc_->AddCodec(AudioCodec(103, "ISAC", 16000, 32000, 1, 2));
534    audio_desc_->AddCodec(AudioCodec(104, "CELT", 32000, 0, 2, 1));
535    desc_.AddContent(kAudioContentName, NS_JINGLE_RTP, audio_desc_);
536
537    // VideoContentDescription
538    rtc::scoped_ptr<VideoContentDescription> video(
539        new VideoContentDescription());
540    video_desc_ = video.get();
541    StreamParams video_stream1;
542    video_stream1.id = kVideoTrackId1;
543    video_stream1.cname = kStream1Cname;
544    video_stream1.sync_label = kStreamLabel1;
545    video_stream1.ssrcs.push_back(kVideoTrack1Ssrc);
546    video->AddStream(video_stream1);
547    StreamParams video_stream2;
548    video_stream2.id = kVideoTrackId2;
549    video_stream2.cname = kStream1Cname;
550    video_stream2.sync_label = kStreamLabel1;
551    video_stream2.ssrcs.push_back(kVideoTrack2Ssrc);
552    video->AddStream(video_stream2);
553    StreamParams video_stream3;
554    video_stream3.id = kVideoTrackId3;
555    video_stream3.cname = kStream2Cname;
556    video_stream3.sync_label = kStreamLabel2;
557    video_stream3.ssrcs.push_back(kVideoTrack3Ssrc);
558    video_stream3.ssrcs.push_back(kVideoTrack4Ssrc);
559    cricket::SsrcGroup ssrc_group(kFecSsrcGroupSemantics, video_stream3.ssrcs);
560    video_stream3.ssrc_groups.push_back(ssrc_group);
561    video->AddStream(video_stream3);
562    video->AddCrypto(CryptoParams(1, "AES_CM_128_HMAC_SHA1_80",
563        "inline:d0RmdmcmVCspeEc3QGZiNWpVLFJhQX1cfHAwJSoj|2^20|1:32", ""));
564    video->set_protocol(cricket::kMediaProtocolSavpf);
565    video->AddCodec(VideoCodec(
566        120,
567        JsepSessionDescription::kDefaultVideoCodecName,
568        JsepSessionDescription::kMaxVideoCodecWidth,
569        JsepSessionDescription::kMaxVideoCodecHeight,
570        JsepSessionDescription::kDefaultVideoCodecFramerate,
571        JsepSessionDescription::kDefaultVideoCodecPreference));
572
573    desc_.AddContent(kVideoContentName, NS_JINGLE_RTP,
574                     video.release());
575
576    // TransportInfo
577    EXPECT_TRUE(desc_.AddTransportInfo(
578        TransportInfo(kAudioContentName,
579                      TransportDescription(NS_JINGLE_ICE_UDP,
580                                           kCandidateUfragVoice,
581                                           kCandidatePwdVoice))));
582    EXPECT_TRUE(desc_.AddTransportInfo(
583        TransportInfo(kVideoContentName,
584                      TransportDescription(NS_JINGLE_ICE_UDP,
585                                           kCandidateUfragVideo,
586                                           kCandidatePwdVideo))));
587
588    // v4 host
589    int port = 1234;
590    rtc::SocketAddress address("192.168.1.5", port++);
591    Candidate candidate1(
592        "", ICE_CANDIDATE_COMPONENT_RTP, "udp", address, kCandidatePriority,
593        "", "", LOCAL_PORT_TYPE,
594        "", kCandidateGeneration, kCandidateFoundation1);
595    address.SetPort(port++);
596    Candidate candidate2(
597        "", ICE_CANDIDATE_COMPONENT_RTCP, "udp", address, kCandidatePriority,
598        "", "", LOCAL_PORT_TYPE,
599        "", kCandidateGeneration, kCandidateFoundation1);
600    address.SetPort(port++);
601    Candidate candidate3(
602        "", ICE_CANDIDATE_COMPONENT_RTCP, "udp", address, kCandidatePriority,
603        "", "", LOCAL_PORT_TYPE,
604        "", kCandidateGeneration, kCandidateFoundation1);
605    address.SetPort(port++);
606    Candidate candidate4(
607        "", ICE_CANDIDATE_COMPONENT_RTP, "udp", address, kCandidatePriority,
608        "", "", LOCAL_PORT_TYPE,
609        "", kCandidateGeneration, kCandidateFoundation1);
610
611    // v6 host
612    rtc::SocketAddress v6_address("::1", port++);
613    cricket::Candidate candidate5(
614        "", cricket::ICE_CANDIDATE_COMPONENT_RTP,
615        "udp", v6_address, kCandidatePriority,
616        "", "", cricket::LOCAL_PORT_TYPE,
617        "", kCandidateGeneration, kCandidateFoundation2);
618    v6_address.SetPort(port++);
619    cricket::Candidate candidate6(
620        "", cricket::ICE_CANDIDATE_COMPONENT_RTCP,
621        "udp", v6_address, kCandidatePriority,
622        "", "", cricket::LOCAL_PORT_TYPE,
623        "", kCandidateGeneration, kCandidateFoundation2);
624    v6_address.SetPort(port++);
625    cricket::Candidate candidate7(
626        "", cricket::ICE_CANDIDATE_COMPONENT_RTCP,
627        "udp", v6_address, kCandidatePriority,
628        "", "", cricket::LOCAL_PORT_TYPE,
629        "", kCandidateGeneration, kCandidateFoundation2);
630    v6_address.SetPort(port++);
631    cricket::Candidate candidate8(
632        "", cricket::ICE_CANDIDATE_COMPONENT_RTP,
633        "udp", v6_address, kCandidatePriority,
634        "", "", cricket::LOCAL_PORT_TYPE,
635        "", kCandidateGeneration, kCandidateFoundation2);
636
637    // stun
638    int port_stun = 2345;
639    rtc::SocketAddress address_stun("74.125.127.126", port_stun++);
640    rtc::SocketAddress rel_address_stun("192.168.1.5", port_stun++);
641    cricket::Candidate candidate9
642        ("", cricket::ICE_CANDIDATE_COMPONENT_RTP,
643         "udp", address_stun, kCandidatePriority,
644         "", "", STUN_PORT_TYPE,
645         "", kCandidateGeneration, kCandidateFoundation3);
646    candidate9.set_related_address(rel_address_stun);
647
648    address_stun.SetPort(port_stun++);
649    rel_address_stun.SetPort(port_stun++);
650    cricket::Candidate candidate10(
651        "", cricket::ICE_CANDIDATE_COMPONENT_RTCP,
652        "udp", address_stun, kCandidatePriority,
653        "", "", STUN_PORT_TYPE,
654        "", kCandidateGeneration, kCandidateFoundation3);
655    candidate10.set_related_address(rel_address_stun);
656
657    // relay
658    int port_relay = 3456;
659    rtc::SocketAddress address_relay("74.125.224.39", port_relay++);
660    cricket::Candidate candidate11(
661        "", cricket::ICE_CANDIDATE_COMPONENT_RTCP,
662        "udp", address_relay, kCandidatePriority,
663        "", "",
664        cricket::RELAY_PORT_TYPE, "",
665        kCandidateGeneration, kCandidateFoundation4);
666    address_relay.SetPort(port_relay++);
667    cricket::Candidate candidate12(
668        "", cricket::ICE_CANDIDATE_COMPONENT_RTP,
669        "udp", address_relay, kCandidatePriority,
670        "", "",
671        RELAY_PORT_TYPE, "",
672        kCandidateGeneration, kCandidateFoundation4);
673
674    // voice
675    candidates_.push_back(candidate1);
676    candidates_.push_back(candidate2);
677    candidates_.push_back(candidate5);
678    candidates_.push_back(candidate6);
679    candidates_.push_back(candidate9);
680    candidates_.push_back(candidate10);
681
682    // video
683    candidates_.push_back(candidate3);
684    candidates_.push_back(candidate4);
685    candidates_.push_back(candidate7);
686    candidates_.push_back(candidate8);
687    candidates_.push_back(candidate11);
688    candidates_.push_back(candidate12);
689
690    jcandidate_.reset(new JsepIceCandidate(std::string("audio_content_name"),
691                                           0, candidate1));
692
693    // Set up JsepSessionDescription.
694    jdesc_.Initialize(desc_.Copy(), kSessionId, kSessionVersion);
695    std::string mline_id;
696    int mline_index = 0;
697    for (size_t i = 0; i< candidates_.size(); ++i) {
698      // In this test, the audio m line index will be 0, and the video m line
699      // will be 1.
700      bool is_video = (i > 5);
701      mline_id = is_video ? "video_content_name" : "audio_content_name";
702      mline_index = is_video ? 1 : 0;
703      JsepIceCandidate jice(mline_id,
704                            mline_index,
705                            candidates_.at(i));
706      jdesc_.AddCandidate(&jice);
707    }
708  }
709
710  AudioContentDescription* CreateAudioContentDescription() {
711    AudioContentDescription* audio = new AudioContentDescription();
712    audio->set_rtcp_mux(true);
713    StreamParams audio_stream1;
714    audio_stream1.id = kAudioTrackId1;
715    audio_stream1.cname = kStream1Cname;
716    audio_stream1.sync_label = kStreamLabel1;
717    audio_stream1.ssrcs.push_back(kAudioTrack1Ssrc);
718    audio->AddStream(audio_stream1);
719    StreamParams audio_stream2;
720    audio_stream2.id = kAudioTrackId2;
721    audio_stream2.cname = kStream2Cname;
722    audio_stream2.sync_label = kStreamLabel2;
723    audio_stream2.ssrcs.push_back(kAudioTrack2Ssrc);
724    audio->AddStream(audio_stream2);
725    audio->AddCrypto(CryptoParams(1, "AES_CM_128_HMAC_SHA1_32",
726        "inline:NzB4d1BINUAvLEw6UzF3WSJ+PSdFcGdUJShpX1Zj|2^20|1:32",
727        "dummy_session_params"));
728    audio->set_protocol(cricket::kMediaProtocolSavpf);
729    return audio;
730  }
731
732  template <class MCD>
733  void CompareMediaContentDescription(const MCD* cd1,
734                                      const MCD* cd2) {
735    // type
736    EXPECT_EQ(cd1->type(), cd1->type());
737
738    // content direction
739    EXPECT_EQ(cd1->direction(), cd2->direction());
740
741    // rtcp_mux
742    EXPECT_EQ(cd1->rtcp_mux(), cd2->rtcp_mux());
743
744    // cryptos
745    EXPECT_EQ(cd1->cryptos().size(), cd2->cryptos().size());
746    if (cd1->cryptos().size() != cd2->cryptos().size()) {
747      ADD_FAILURE();
748      return;
749    }
750    for (size_t i = 0; i< cd1->cryptos().size(); ++i) {
751      const CryptoParams c1 = cd1->cryptos().at(i);
752      const CryptoParams c2 = cd2->cryptos().at(i);
753      EXPECT_TRUE(c1.Matches(c2));
754      EXPECT_EQ(c1.key_params, c2.key_params);
755      EXPECT_EQ(c1.session_params, c2.session_params);
756    }
757    // protocol
758    EXPECT_EQ(cd1->protocol(), cd2->protocol());
759
760    // codecs
761    EXPECT_EQ(cd1->codecs(), cd2->codecs());
762
763    // bandwidth
764    EXPECT_EQ(cd1->bandwidth(), cd2->bandwidth());
765
766    // streams
767    EXPECT_EQ(cd1->streams(), cd2->streams());
768
769    // extmap
770    ASSERT_EQ(cd1->rtp_header_extensions().size(),
771              cd2->rtp_header_extensions().size());
772    for (size_t i = 0; i< cd1->rtp_header_extensions().size(); ++i) {
773      const RtpHeaderExtension ext1 = cd1->rtp_header_extensions().at(i);
774      const RtpHeaderExtension ext2 = cd2->rtp_header_extensions().at(i);
775      EXPECT_EQ(ext1.uri, ext2.uri);
776      EXPECT_EQ(ext1.id, ext2.id);
777    }
778
779    // buffered mode latency
780    EXPECT_EQ(cd1->buffered_mode_latency(), cd2->buffered_mode_latency());
781  }
782
783
784  void CompareSessionDescription(const SessionDescription& desc1,
785                                 const SessionDescription& desc2) {
786    // Compare content descriptions.
787    if (desc1.contents().size() != desc2.contents().size()) {
788      ADD_FAILURE();
789      return;
790    }
791    for (size_t i = 0 ; i < desc1.contents().size(); ++i) {
792      const cricket::ContentInfo& c1 = desc1.contents().at(i);
793      const cricket::ContentInfo& c2 = desc2.contents().at(i);
794      // content name
795      EXPECT_EQ(c1.name, c2.name);
796      // content type
797      // Note, ASSERT will return from the function, but will not stop the test.
798      ASSERT_EQ(c1.type, c2.type);
799
800      ASSERT_EQ(IsAudioContent(&c1), IsAudioContent(&c2));
801      if (IsAudioContent(&c1)) {
802        const AudioContentDescription* acd1 =
803            static_cast<const AudioContentDescription*>(c1.description);
804        const AudioContentDescription* acd2 =
805            static_cast<const AudioContentDescription*>(c2.description);
806        CompareMediaContentDescription<AudioContentDescription>(acd1, acd2);
807      }
808
809      ASSERT_EQ(IsVideoContent(&c1), IsVideoContent(&c2));
810      if (IsVideoContent(&c1)) {
811        const VideoContentDescription* vcd1 =
812            static_cast<const VideoContentDescription*>(c1.description);
813        const VideoContentDescription* vcd2 =
814            static_cast<const VideoContentDescription*>(c2.description);
815        CompareMediaContentDescription<VideoContentDescription>(vcd1, vcd2);
816      }
817
818      ASSERT_EQ(IsDataContent(&c1), IsDataContent(&c2));
819      if (IsDataContent(&c1)) {
820        const DataContentDescription* dcd1 =
821            static_cast<const DataContentDescription*>(c1.description);
822        const DataContentDescription* dcd2 =
823            static_cast<const DataContentDescription*>(c2.description);
824        CompareMediaContentDescription<DataContentDescription>(dcd1, dcd2);
825      }
826    }
827
828    // group
829    const cricket::ContentGroups groups1 = desc1.groups();
830    const cricket::ContentGroups groups2 = desc2.groups();
831    EXPECT_EQ(groups1.size(), groups1.size());
832    if (groups1.size() != groups2.size()) {
833      ADD_FAILURE();
834      return;
835    }
836    for (size_t i = 0; i < groups1.size(); ++i) {
837      const cricket::ContentGroup group1 = groups1.at(i);
838      const cricket::ContentGroup group2 = groups2.at(i);
839      EXPECT_EQ(group1.semantics(), group2.semantics());
840      const cricket::ContentNames names1 = group1.content_names();
841      const cricket::ContentNames names2 = group2.content_names();
842      EXPECT_EQ(names1.size(), names2.size());
843      if (names1.size() != names2.size()) {
844        ADD_FAILURE();
845        return;
846      }
847      cricket::ContentNames::const_iterator iter1 = names1.begin();
848      cricket::ContentNames::const_iterator iter2 = names2.begin();
849      while (iter1 != names1.end()) {
850        EXPECT_EQ(*iter1++, *iter2++);
851      }
852    }
853
854    // transport info
855    const cricket::TransportInfos transports1 = desc1.transport_infos();
856    const cricket::TransportInfos transports2 = desc2.transport_infos();
857    EXPECT_EQ(transports1.size(), transports2.size());
858    if (transports1.size() != transports2.size()) {
859      ADD_FAILURE();
860      return;
861    }
862    for (size_t i = 0; i < transports1.size(); ++i) {
863      const cricket::TransportInfo transport1 = transports1.at(i);
864      const cricket::TransportInfo transport2 = transports2.at(i);
865      EXPECT_EQ(transport1.content_name, transport2.content_name);
866      EXPECT_EQ(transport1.description.transport_type,
867                transport2.description.transport_type);
868      EXPECT_EQ(transport1.description.ice_ufrag,
869                transport2.description.ice_ufrag);
870      EXPECT_EQ(transport1.description.ice_pwd,
871                transport2.description.ice_pwd);
872      if (transport1.description.identity_fingerprint) {
873        EXPECT_EQ(*transport1.description.identity_fingerprint,
874                  *transport2.description.identity_fingerprint);
875      } else {
876        EXPECT_EQ(transport1.description.identity_fingerprint.get(),
877                  transport2.description.identity_fingerprint.get());
878      }
879      EXPECT_EQ(transport1.description.transport_options,
880                transport2.description.transport_options);
881      EXPECT_TRUE(CompareCandidates(transport1.description.candidates,
882                                    transport2.description.candidates));
883    }
884  }
885
886  bool CompareCandidates(const Candidates& cs1, const Candidates& cs2) {
887    EXPECT_EQ(cs1.size(), cs2.size());
888    if (cs1.size() != cs2.size())
889      return false;
890    for (size_t i = 0; i< cs1.size(); ++i) {
891      const Candidate c1 = cs1.at(i);
892      const Candidate c2 = cs2.at(i);
893      EXPECT_TRUE(c1.IsEquivalent(c2));
894    }
895    return true;
896  }
897
898  bool CompareSessionDescription(
899      const JsepSessionDescription& desc1,
900      const JsepSessionDescription& desc2) {
901    EXPECT_EQ(desc1.session_id(), desc2.session_id());
902    EXPECT_EQ(desc1.session_version(), desc2.session_version());
903    CompareSessionDescription(*desc1.description(), *desc2.description());
904    if (desc1.number_of_mediasections() != desc2.number_of_mediasections())
905      return false;
906    for (size_t i = 0; i < desc1.number_of_mediasections(); ++i) {
907      const IceCandidateCollection* cc1 = desc1.candidates(i);
908      const IceCandidateCollection* cc2 = desc2.candidates(i);
909      if (cc1->count() != cc2->count())
910        return false;
911      for (size_t j = 0; j < cc1->count(); ++j) {
912        const IceCandidateInterface* c1 = cc1->at(j);
913        const IceCandidateInterface* c2 = cc2->at(j);
914        EXPECT_EQ(c1->sdp_mid(), c2->sdp_mid());
915        EXPECT_EQ(c1->sdp_mline_index(), c2->sdp_mline_index());
916        EXPECT_TRUE(c1->candidate().IsEquivalent(c2->candidate()));
917      }
918    }
919    return true;
920  }
921
922  // Disable the ice-ufrag and ice-pwd in given |sdp| message by replacing
923  // them with invalid keywords so that the parser will just ignore them.
924  bool RemoveCandidateUfragPwd(std::string* sdp) {
925    const char ice_ufrag[] = "a=ice-ufrag";
926    const char ice_ufragx[] = "a=xice-ufrag";
927    const char ice_pwd[] = "a=ice-pwd";
928    const char ice_pwdx[] = "a=xice-pwd";
929    rtc::replace_substrs(ice_ufrag, strlen(ice_ufrag),
930        ice_ufragx, strlen(ice_ufragx), sdp);
931    rtc::replace_substrs(ice_pwd, strlen(ice_pwd),
932        ice_pwdx, strlen(ice_pwdx), sdp);
933    return true;
934  }
935
936  // Update the candidates in |jdesc| to use the given |ufrag| and |pwd|.
937  bool UpdateCandidateUfragPwd(JsepSessionDescription* jdesc, int mline_index,
938      const std::string& ufrag, const std::string& pwd) {
939    std::string content_name;
940    if (mline_index == 0) {
941      content_name = kAudioContentName;
942    } else if (mline_index == 1) {
943      content_name = kVideoContentName;
944    } else {
945      ASSERT(false);
946    }
947    TransportInfo transport_info(
948        content_name, TransportDescription(NS_JINGLE_ICE_UDP,
949                                           ufrag, pwd));
950    SessionDescription* desc =
951        const_cast<SessionDescription*>(jdesc->description());
952    desc->RemoveTransportInfoByName(content_name);
953    EXPECT_TRUE(desc->AddTransportInfo(transport_info));
954    for (size_t i = 0; i < jdesc_.number_of_mediasections(); ++i) {
955      const IceCandidateCollection* cc = jdesc_.candidates(i);
956      for (size_t j = 0; j < cc->count(); ++j) {
957        if (cc->at(j)->sdp_mline_index() == mline_index) {
958          const_cast<Candidate&>(cc->at(j)->candidate()).set_username(
959              ufrag);
960          const_cast<Candidate&>(cc->at(j)->candidate()).set_password(
961              pwd);
962        }
963      }
964    }
965    return true;
966  }
967
968  void AddIceOptions(const std::string& content_name,
969                     const std::vector<std::string>& transport_options) {
970    ASSERT_TRUE(desc_.GetTransportInfoByName(content_name) != NULL);
971    cricket::TransportInfo transport_info =
972        *(desc_.GetTransportInfoByName(content_name));
973    desc_.RemoveTransportInfoByName(content_name);
974    transport_info.description.transport_options = transport_options;
975    desc_.AddTransportInfo(transport_info);
976  }
977
978  void AddFingerprint() {
979    desc_.RemoveTransportInfoByName(kAudioContentName);
980    desc_.RemoveTransportInfoByName(kVideoContentName);
981    rtc::SSLFingerprint fingerprint(rtc::DIGEST_SHA_1,
982                                          kIdentityDigest,
983                                          sizeof(kIdentityDigest));
984    EXPECT_TRUE(desc_.AddTransportInfo(
985        TransportInfo(kAudioContentName,
986                      TransportDescription(NS_JINGLE_ICE_UDP,
987                                           std::vector<std::string>(),
988                                           kCandidateUfragVoice,
989                                           kCandidatePwdVoice,
990                                           cricket::ICEMODE_FULL,
991                                           cricket::CONNECTIONROLE_NONE,
992                                           &fingerprint, Candidates()))));
993    EXPECT_TRUE(desc_.AddTransportInfo(
994        TransportInfo(kVideoContentName,
995                      TransportDescription(NS_JINGLE_ICE_UDP,
996                                           std::vector<std::string>(),
997                                           kCandidateUfragVideo,
998                                           kCandidatePwdVideo,
999                                           cricket::ICEMODE_FULL,
1000                                           cricket::CONNECTIONROLE_NONE,
1001                                           &fingerprint, Candidates()))));
1002  }
1003
1004  void AddExtmap() {
1005    audio_desc_ = static_cast<AudioContentDescription*>(
1006        audio_desc_->Copy());
1007    video_desc_ = static_cast<VideoContentDescription*>(
1008        video_desc_->Copy());
1009    audio_desc_->AddRtpHeaderExtension(
1010        RtpHeaderExtension(kExtmapUri, kExtmapId));
1011    video_desc_->AddRtpHeaderExtension(
1012        RtpHeaderExtension(kExtmapUri, kExtmapId));
1013    desc_.RemoveContentByName(kAudioContentName);
1014    desc_.RemoveContentByName(kVideoContentName);
1015    desc_.AddContent(kAudioContentName, NS_JINGLE_RTP, audio_desc_);
1016    desc_.AddContent(kVideoContentName, NS_JINGLE_RTP, video_desc_);
1017  }
1018
1019  void RemoveCryptos() {
1020    audio_desc_->set_cryptos(std::vector<CryptoParams>());
1021    video_desc_->set_cryptos(std::vector<CryptoParams>());
1022  }
1023
1024  bool TestSerializeDirection(cricket::MediaContentDirection direction) {
1025    audio_desc_->set_direction(direction);
1026    video_desc_->set_direction(direction);
1027    std::string new_sdp = kSdpFullString;
1028    ReplaceDirection(direction, &new_sdp);
1029
1030    if (!jdesc_.Initialize(desc_.Copy(),
1031                           jdesc_.session_id(),
1032                           jdesc_.session_version())) {
1033      return false;
1034    }
1035    std::string message = webrtc::SdpSerialize(jdesc_);
1036    EXPECT_EQ(new_sdp, message);
1037    return true;
1038  }
1039
1040  bool TestSerializeRejected(bool audio_rejected, bool video_rejected) {
1041    audio_desc_ = static_cast<AudioContentDescription*>(
1042        audio_desc_->Copy());
1043    video_desc_ = static_cast<VideoContentDescription*>(
1044        video_desc_->Copy());
1045    desc_.RemoveContentByName(kAudioContentName);
1046    desc_.RemoveContentByName(kVideoContentName);
1047    desc_.AddContent(kAudioContentName, NS_JINGLE_RTP, audio_rejected,
1048                     audio_desc_);
1049    desc_.AddContent(kVideoContentName, NS_JINGLE_RTP, video_rejected,
1050                     video_desc_);
1051    std::string new_sdp = kSdpFullString;
1052    ReplaceRejected(audio_rejected, video_rejected, &new_sdp);
1053
1054    if (!jdesc_.Initialize(desc_.Copy(),
1055                           jdesc_.session_id(),
1056                           jdesc_.session_version())) {
1057      return false;
1058    }
1059    std::string message = webrtc::SdpSerialize(jdesc_);
1060    EXPECT_EQ(new_sdp, message);
1061    return true;
1062  }
1063
1064  void AddSctpDataChannel() {
1065    rtc::scoped_ptr<DataContentDescription> data(
1066        new DataContentDescription());
1067    data_desc_ = data.get();
1068    data_desc_->set_protocol(cricket::kMediaProtocolDtlsSctp);
1069    DataCodec codec(cricket::kGoogleSctpDataCodecId,
1070                    cricket::kGoogleSctpDataCodecName, 0);
1071    codec.SetParam(cricket::kCodecParamPort, kDefaultSctpPort);
1072    data_desc_->AddCodec(codec);
1073    desc_.AddContent(kDataContentName, NS_JINGLE_DRAFT_SCTP, data.release());
1074    EXPECT_TRUE(desc_.AddTransportInfo(
1075           TransportInfo(kDataContentName,
1076                         TransportDescription(NS_JINGLE_ICE_UDP,
1077                                              kCandidateUfragData,
1078                                              kCandidatePwdData))));
1079  }
1080
1081  void AddRtpDataChannel() {
1082    rtc::scoped_ptr<DataContentDescription> data(
1083        new DataContentDescription());
1084    data_desc_ = data.get();
1085
1086    data_desc_->AddCodec(DataCodec(101, "google-data", 1));
1087    StreamParams data_stream;
1088    data_stream.id = kDataChannelMsid;
1089    data_stream.cname = kDataChannelCname;
1090    data_stream.sync_label = kDataChannelLabel;
1091    data_stream.ssrcs.push_back(kDataChannelSsrc);
1092    data_desc_->AddStream(data_stream);
1093    data_desc_->AddCrypto(CryptoParams(
1094        1, "AES_CM_128_HMAC_SHA1_80",
1095        "inline:FvLcvU2P3ZWmQxgPAgcDu7Zl9vftYElFOjEzhWs5", ""));
1096    data_desc_->set_protocol(cricket::kMediaProtocolSavpf);
1097    desc_.AddContent(kDataContentName, NS_JINGLE_RTP, data.release());
1098    EXPECT_TRUE(desc_.AddTransportInfo(
1099           TransportInfo(kDataContentName,
1100                         TransportDescription(NS_JINGLE_ICE_UDP,
1101                                              kCandidateUfragData,
1102                                              kCandidatePwdData))));
1103  }
1104
1105  bool TestDeserializeDirection(cricket::MediaContentDirection direction) {
1106    std::string new_sdp = kSdpFullString;
1107    ReplaceDirection(direction, &new_sdp);
1108    JsepSessionDescription new_jdesc(kDummyString);
1109
1110    EXPECT_TRUE(SdpDeserialize(new_sdp, &new_jdesc));
1111
1112    audio_desc_->set_direction(direction);
1113    video_desc_->set_direction(direction);
1114    if (!jdesc_.Initialize(desc_.Copy(),
1115                           jdesc_.session_id(),
1116                           jdesc_.session_version())) {
1117      return false;
1118    }
1119    EXPECT_TRUE(CompareSessionDescription(jdesc_, new_jdesc));
1120    return true;
1121  }
1122
1123  bool TestDeserializeRejected(bool audio_rejected, bool video_rejected) {
1124    std::string new_sdp = kSdpFullString;
1125    ReplaceRejected(audio_rejected, video_rejected, &new_sdp);
1126    JsepSessionDescription new_jdesc(JsepSessionDescription::kOffer);
1127
1128    EXPECT_TRUE(SdpDeserialize(new_sdp, &new_jdesc));
1129    audio_desc_ = static_cast<AudioContentDescription*>(
1130        audio_desc_->Copy());
1131    video_desc_ = static_cast<VideoContentDescription*>(
1132        video_desc_->Copy());
1133    desc_.RemoveContentByName(kAudioContentName);
1134    desc_.RemoveContentByName(kVideoContentName);
1135    desc_.AddContent(kAudioContentName, NS_JINGLE_RTP, audio_rejected,
1136                     audio_desc_);
1137    desc_.AddContent(kVideoContentName, NS_JINGLE_RTP, video_rejected,
1138                     video_desc_);
1139    if (!jdesc_.Initialize(desc_.Copy(),
1140                           jdesc_.session_id(),
1141                           jdesc_.session_version())) {
1142      return false;
1143    }
1144    EXPECT_TRUE(CompareSessionDescription(jdesc_, new_jdesc));
1145    return true;
1146  }
1147
1148  void TestDeserializeExtmap(bool session_level, bool media_level) {
1149    AddExtmap();
1150    JsepSessionDescription new_jdesc("dummy");
1151    ASSERT_TRUE(new_jdesc.Initialize(desc_.Copy(),
1152                                     jdesc_.session_id(),
1153                                     jdesc_.session_version()));
1154    JsepSessionDescription jdesc_with_extmap("dummy");
1155    std::string sdp_with_extmap = kSdpString;
1156    if (session_level) {
1157      InjectAfter(kSessionTime, kExtmapWithDirectionAndAttribute,
1158                  &sdp_with_extmap);
1159    }
1160    if (media_level) {
1161      InjectAfter(kAttributeIcePwdVoice, kExtmapWithDirectionAndAttribute,
1162                  &sdp_with_extmap);
1163      InjectAfter(kAttributeIcePwdVideo, kExtmapWithDirectionAndAttribute,
1164                  &sdp_with_extmap);
1165    }
1166    // The extmap can't be present at the same time in both session level and
1167    // media level.
1168    if (session_level && media_level) {
1169      SdpParseError error;
1170      EXPECT_FALSE(webrtc::SdpDeserialize(sdp_with_extmap,
1171                   &jdesc_with_extmap, &error));
1172      EXPECT_NE(std::string::npos, error.description.find("a=extmap"));
1173    } else {
1174      EXPECT_TRUE(SdpDeserialize(sdp_with_extmap, &jdesc_with_extmap));
1175      EXPECT_TRUE(CompareSessionDescription(jdesc_with_extmap, new_jdesc));
1176    }
1177  }
1178
1179  void VerifyCodecParameter(const cricket::CodecParameterMap& params,
1180      const std::string& name, int expected_value) {
1181    cricket::CodecParameterMap::const_iterator found = params.find(name);
1182    ASSERT_TRUE(found != params.end());
1183    EXPECT_EQ(found->second, rtc::ToString<int>(expected_value));
1184  }
1185
1186  void TestDeserializeCodecParams(const CodecParams& params,
1187                                  JsepSessionDescription* jdesc_output) {
1188    std::string sdp =
1189        "v=0\r\n"
1190        "o=- 18446744069414584320 18446462598732840960 IN IP4 127.0.0.1\r\n"
1191        "s=-\r\n"
1192        "t=0 0\r\n"
1193        // Include semantics for WebRTC Media Streams since it is supported by
1194        // this parser, and will be added to the SDP when serializing a session
1195        // description.
1196        "a=msid-semantic: WMS\r\n"
1197        // Pl type 111 preferred.
1198        "m=audio 1 RTP/SAVPF 111 104 103 102\r\n"
1199        // Pltype 111 listed before 103 and 104 in the map.
1200        "a=rtpmap:111 opus/48000/2\r\n"
1201        // Pltype 103 listed before 104.
1202        "a=rtpmap:103 ISAC/16000\r\n"
1203        "a=rtpmap:104 CELT/32000/2\r\n"
1204        "a=rtpmap:102 ISAC/32000/1\r\n"
1205        "a=fmtp:111 0-15,66,70\r\n"
1206        "a=fmtp:111 ";
1207    std::ostringstream os;
1208    os << "minptime=" << params.min_ptime
1209       << "; stereo=" << params.stereo
1210       << "; sprop-stereo=" << params.sprop_stereo
1211       << "; useinbandfec=" << params.useinband
1212       << " maxaveragebitrate=" << params.maxaveragebitrate << "\r\n"
1213       << "a=ptime:" << params.ptime << "\r\n"
1214       << "a=maxptime:" << params.max_ptime << "\r\n";
1215    sdp += os.str();
1216
1217    os.clear();
1218    os.str("");
1219    // Pl type 100 preferred.
1220    os << "m=video 1 RTP/SAVPF 99 95\r\n"
1221       << "a=rtpmap:99 VP8/90000\r\n"
1222       << "a=rtpmap:95 RTX/90000\r\n"
1223       << "a=fmtp:95 apt=99;rtx-time=1000\r\n";
1224    sdp += os.str();
1225
1226    // Deserialize
1227    SdpParseError error;
1228    EXPECT_TRUE(webrtc::SdpDeserialize(sdp, jdesc_output, &error));
1229
1230    const ContentInfo* ac = GetFirstAudioContent(jdesc_output->description());
1231    ASSERT_TRUE(ac != NULL);
1232    const AudioContentDescription* acd =
1233        static_cast<const AudioContentDescription*>(ac->description);
1234    ASSERT_FALSE(acd->codecs().empty());
1235    cricket::AudioCodec opus = acd->codecs()[0];
1236    EXPECT_EQ("opus", opus.name);
1237    EXPECT_EQ(111, opus.id);
1238    VerifyCodecParameter(opus.params, "minptime", params.min_ptime);
1239    VerifyCodecParameter(opus.params, "stereo", params.stereo);
1240    VerifyCodecParameter(opus.params, "sprop-stereo", params.sprop_stereo);
1241    VerifyCodecParameter(opus.params, "useinbandfec", params.useinband);
1242    VerifyCodecParameter(opus.params, "maxaveragebitrate",
1243                         params.maxaveragebitrate);
1244    for (size_t i = 0; i < acd->codecs().size(); ++i) {
1245      cricket::AudioCodec codec = acd->codecs()[i];
1246      VerifyCodecParameter(codec.params, "ptime", params.ptime);
1247      VerifyCodecParameter(codec.params, "maxptime", params.max_ptime);
1248      if (codec.name == "ISAC") {
1249        if (codec.clockrate == 16000) {
1250          EXPECT_EQ(32000, codec.bitrate);
1251        } else {
1252          EXPECT_EQ(56000, codec.bitrate);
1253        }
1254      }
1255    }
1256
1257    const ContentInfo* vc = GetFirstVideoContent(jdesc_output->description());
1258    ASSERT_TRUE(vc != NULL);
1259    const VideoContentDescription* vcd =
1260        static_cast<const VideoContentDescription*>(vc->description);
1261    ASSERT_FALSE(vcd->codecs().empty());
1262    cricket::VideoCodec vp8 = vcd->codecs()[0];
1263    EXPECT_EQ("VP8", vp8.name);
1264    EXPECT_EQ(99, vp8.id);
1265    cricket::VideoCodec rtx = vcd->codecs()[1];
1266    EXPECT_EQ("RTX", rtx.name);
1267    EXPECT_EQ(95, rtx.id);
1268    VerifyCodecParameter(rtx.params, "apt", vp8.id);
1269  }
1270
1271  void TestDeserializeRtcpFb(JsepSessionDescription* jdesc_output,
1272                             bool use_wildcard) {
1273    std::string sdp =
1274        "v=0\r\n"
1275        "o=- 18446744069414584320 18446462598732840960 IN IP4 127.0.0.1\r\n"
1276        "s=-\r\n"
1277        "t=0 0\r\n"
1278        // Include semantics for WebRTC Media Streams since it is supported by
1279        // this parser, and will be added to the SDP when serializing a session
1280        // description.
1281        "a=msid-semantic: WMS\r\n"
1282        "m=audio 1 RTP/SAVPF 111\r\n"
1283        "a=rtpmap:111 opus/48000/2\r\n"
1284        "a=rtcp-fb:111 nack\r\n"
1285        "m=video 3457 RTP/SAVPF 101\r\n"
1286        "a=rtpmap:101 VP8/90000\r\n"
1287        "a=rtcp-fb:101 nack\r\n"
1288        "a=rtcp-fb:101 nack pli\r\n"
1289        "a=rtcp-fb:101 goog-remb\r\n"
1290        "a=rtcp-fb:101 ccm fir\r\n";
1291    std::ostringstream os;
1292    os << "a=rtcp-fb:" << (use_wildcard ? "*" : "101") <<  " ccm fir\r\n";
1293    sdp += os.str();
1294    // Deserialize
1295    SdpParseError error;
1296    EXPECT_TRUE(webrtc::SdpDeserialize(sdp, jdesc_output, &error));
1297    const ContentInfo* ac = GetFirstAudioContent(jdesc_output->description());
1298    ASSERT_TRUE(ac != NULL);
1299    const AudioContentDescription* acd =
1300        static_cast<const AudioContentDescription*>(ac->description);
1301    ASSERT_FALSE(acd->codecs().empty());
1302    cricket::AudioCodec opus = acd->codecs()[0];
1303    EXPECT_EQ(111, opus.id);
1304    EXPECT_TRUE(opus.HasFeedbackParam(
1305        cricket::FeedbackParam(cricket::kRtcpFbParamNack,
1306                               cricket::kParamValueEmpty)));
1307
1308    const ContentInfo* vc = GetFirstVideoContent(jdesc_output->description());
1309    ASSERT_TRUE(vc != NULL);
1310    const VideoContentDescription* vcd =
1311        static_cast<const VideoContentDescription*>(vc->description);
1312    ASSERT_FALSE(vcd->codecs().empty());
1313    cricket::VideoCodec vp8 = vcd->codecs()[0];
1314    EXPECT_STREQ(webrtc::JsepSessionDescription::kDefaultVideoCodecName,
1315                 vp8.name.c_str());
1316    EXPECT_EQ(101, vp8.id);
1317    EXPECT_TRUE(vp8.HasFeedbackParam(
1318        cricket::FeedbackParam(cricket::kRtcpFbParamNack,
1319                               cricket::kParamValueEmpty)));
1320    EXPECT_TRUE(vp8.HasFeedbackParam(
1321        cricket::FeedbackParam(cricket::kRtcpFbParamNack,
1322                               cricket::kRtcpFbNackParamPli)));
1323    EXPECT_TRUE(vp8.HasFeedbackParam(
1324        cricket::FeedbackParam(cricket::kRtcpFbParamRemb,
1325                               cricket::kParamValueEmpty)));
1326    EXPECT_TRUE(vp8.HasFeedbackParam(
1327        cricket::FeedbackParam(cricket::kRtcpFbParamCcm,
1328                               cricket::kRtcpFbCcmParamFir)));
1329  }
1330
1331  // Two SDP messages can mean the same thing but be different strings, e.g.
1332  // some of the lines can be serialized in different order.
1333  // However, a deserialized description can be compared field by field and has
1334  // no order. If deserializer has already been tested, serializing then
1335  // deserializing and comparing JsepSessionDescription will test
1336  // the serializer sufficiently.
1337  void TestSerialize(const JsepSessionDescription& jdesc) {
1338    std::string message = webrtc::SdpSerialize(jdesc);
1339    JsepSessionDescription jdesc_output_des(kDummyString);
1340    SdpParseError error;
1341    EXPECT_TRUE(webrtc::SdpDeserialize(message, &jdesc_output_des, &error));
1342    EXPECT_TRUE(CompareSessionDescription(jdesc, jdesc_output_des));
1343  }
1344
1345 protected:
1346  SessionDescription desc_;
1347  AudioContentDescription* audio_desc_;
1348  VideoContentDescription* video_desc_;
1349  DataContentDescription* data_desc_;
1350  Candidates candidates_;
1351  rtc::scoped_ptr<IceCandidateInterface> jcandidate_;
1352  JsepSessionDescription jdesc_;
1353};
1354
1355void TestMismatch(const std::string& string1, const std::string& string2) {
1356  int position = 0;
1357  for (size_t i = 0; i < string1.length() && i < string2.length(); ++i) {
1358    if (string1.c_str()[i] != string2.c_str()[i]) {
1359      position = static_cast<int>(i);
1360      break;
1361    }
1362  }
1363  EXPECT_EQ(0, position) << "Strings mismatch at the " << position
1364                         << " character\n"
1365                         << " 1: " << string1.substr(position, 20) << "\n"
1366                         << " 2: " << string2.substr(position, 20) << "\n";
1367}
1368
1369std::string GetLine(const std::string& message,
1370                    const std::string& session_description_name) {
1371  size_t start = message.find(session_description_name);
1372  if (std::string::npos == start) {
1373    return "";
1374  }
1375  size_t stop = message.find("\r\n", start);
1376  if (std::string::npos == stop) {
1377    return "";
1378  }
1379  if (stop <= start) {
1380    return "";
1381  }
1382  return message.substr(start, stop - start);
1383}
1384
1385TEST_F(WebRtcSdpTest, SerializeSessionDescription) {
1386  // SessionDescription with desc and candidates.
1387  std::string message = webrtc::SdpSerialize(jdesc_);
1388  TestMismatch(std::string(kSdpFullString), message);
1389}
1390
1391TEST_F(WebRtcSdpTest, SerializeSessionDescriptionEmpty) {
1392  JsepSessionDescription jdesc_empty(kDummyString);
1393  EXPECT_EQ("", webrtc::SdpSerialize(jdesc_empty));
1394}
1395
1396// This tests serialization of SDP with a=crypto and a=fingerprint, as would be
1397// the case in a DTLS offer.
1398TEST_F(WebRtcSdpTest, SerializeSessionDescriptionWithFingerprint) {
1399  AddFingerprint();
1400  JsepSessionDescription jdesc_with_fingerprint(kDummyString);
1401  ASSERT_TRUE(jdesc_with_fingerprint.Initialize(desc_.Copy(),
1402                                                kSessionId, kSessionVersion));
1403  std::string message = webrtc::SdpSerialize(jdesc_with_fingerprint);
1404
1405  std::string sdp_with_fingerprint = kSdpString;
1406  InjectAfter(kAttributeIcePwdVoice,
1407              kFingerprint, &sdp_with_fingerprint);
1408  InjectAfter(kAttributeIcePwdVideo,
1409              kFingerprint, &sdp_with_fingerprint);
1410
1411  EXPECT_EQ(sdp_with_fingerprint, message);
1412}
1413
1414// This tests serialization of SDP with a=fingerprint with no a=crypto, as would
1415// be the case in a DTLS answer.
1416TEST_F(WebRtcSdpTest, SerializeSessionDescriptionWithFingerprintNoCryptos) {
1417  AddFingerprint();
1418  RemoveCryptos();
1419  JsepSessionDescription jdesc_with_fingerprint(kDummyString);
1420  ASSERT_TRUE(jdesc_with_fingerprint.Initialize(desc_.Copy(),
1421                                                kSessionId, kSessionVersion));
1422  std::string message = webrtc::SdpSerialize(jdesc_with_fingerprint);
1423
1424  std::string sdp_with_fingerprint = kSdpString;
1425  Replace(kAttributeCryptoVoice, "", &sdp_with_fingerprint);
1426  Replace(kAttributeCryptoVideo, "", &sdp_with_fingerprint);
1427  InjectAfter(kAttributeIcePwdVoice,
1428              kFingerprint, &sdp_with_fingerprint);
1429  InjectAfter(kAttributeIcePwdVideo,
1430              kFingerprint, &sdp_with_fingerprint);
1431
1432  EXPECT_EQ(sdp_with_fingerprint, message);
1433}
1434
1435TEST_F(WebRtcSdpTest, SerializeSessionDescriptionWithoutCandidates) {
1436  // JsepSessionDescription with desc but without candidates.
1437  JsepSessionDescription jdesc_no_candidates(kDummyString);
1438  ASSERT_TRUE(jdesc_no_candidates.Initialize(desc_.Copy(),
1439                                             kSessionId, kSessionVersion));
1440  std::string message = webrtc::SdpSerialize(jdesc_no_candidates);
1441  EXPECT_EQ(std::string(kSdpString), message);
1442}
1443
1444TEST_F(WebRtcSdpTest, SerializeSessionDescriptionWithBundle) {
1445  ContentGroup group(cricket::GROUP_TYPE_BUNDLE);
1446  group.AddContentName(kAudioContentName);
1447  group.AddContentName(kVideoContentName);
1448  desc_.AddGroup(group);
1449  ASSERT_TRUE(jdesc_.Initialize(desc_.Copy(),
1450                                jdesc_.session_id(),
1451                                jdesc_.session_version()));
1452  std::string message = webrtc::SdpSerialize(jdesc_);
1453  std::string sdp_with_bundle = kSdpFullString;
1454  InjectAfter(kSessionTime,
1455              "a=group:BUNDLE audio_content_name video_content_name\r\n",
1456              &sdp_with_bundle);
1457  EXPECT_EQ(sdp_with_bundle, message);
1458}
1459
1460TEST_F(WebRtcSdpTest, SerializeSessionDescriptionWithBandwidth) {
1461  VideoContentDescription* vcd = static_cast<VideoContentDescription*>(
1462      GetFirstVideoContent(&desc_)->description);
1463  vcd->set_bandwidth(100 * 1000);
1464  AudioContentDescription* acd = static_cast<AudioContentDescription*>(
1465      GetFirstAudioContent(&desc_)->description);
1466  acd->set_bandwidth(50 * 1000);
1467  ASSERT_TRUE(jdesc_.Initialize(desc_.Copy(),
1468                                jdesc_.session_id(),
1469                                jdesc_.session_version()));
1470  std::string message = webrtc::SdpSerialize(jdesc_);
1471  std::string sdp_with_bandwidth = kSdpFullString;
1472  InjectAfter("c=IN IP4 74.125.224.39\r\n",
1473              "b=AS:100\r\n",
1474              &sdp_with_bandwidth);
1475  InjectAfter("c=IN IP4 74.125.127.126\r\n",
1476              "b=AS:50\r\n",
1477              &sdp_with_bandwidth);
1478  EXPECT_EQ(sdp_with_bandwidth, message);
1479}
1480
1481TEST_F(WebRtcSdpTest, SerializeSessionDescriptionWithIceOptions) {
1482  std::vector<std::string> transport_options;
1483  transport_options.push_back(kIceOption1);
1484  transport_options.push_back(kIceOption3);
1485  AddIceOptions(kAudioContentName, transport_options);
1486  transport_options.clear();
1487  transport_options.push_back(kIceOption2);
1488  transport_options.push_back(kIceOption3);
1489  AddIceOptions(kVideoContentName, transport_options);
1490  ASSERT_TRUE(jdesc_.Initialize(desc_.Copy(),
1491                                jdesc_.session_id(),
1492                                jdesc_.session_version()));
1493  std::string message = webrtc::SdpSerialize(jdesc_);
1494  std::string sdp_with_ice_options = kSdpFullString;
1495  InjectAfter(kAttributeIcePwdVoice,
1496              "a=ice-options:iceoption1 iceoption3\r\n",
1497              &sdp_with_ice_options);
1498  InjectAfter(kAttributeIcePwdVideo,
1499              "a=ice-options:iceoption2 iceoption3\r\n",
1500              &sdp_with_ice_options);
1501  EXPECT_EQ(sdp_with_ice_options, message);
1502}
1503
1504TEST_F(WebRtcSdpTest, SerializeSessionDescriptionWithRecvOnlyContent) {
1505  EXPECT_TRUE(TestSerializeDirection(cricket::MD_RECVONLY));
1506}
1507
1508TEST_F(WebRtcSdpTest, SerializeSessionDescriptionWithSendOnlyContent) {
1509  EXPECT_TRUE(TestSerializeDirection(cricket::MD_SENDONLY));
1510}
1511
1512TEST_F(WebRtcSdpTest, SerializeSessionDescriptionWithInactiveContent) {
1513  EXPECT_TRUE(TestSerializeDirection(cricket::MD_INACTIVE));
1514}
1515
1516TEST_F(WebRtcSdpTest, SerializeSessionDescriptionWithAudioRejected) {
1517  EXPECT_TRUE(TestSerializeRejected(true, false));
1518}
1519
1520TEST_F(WebRtcSdpTest, SerializeSessionDescriptionWithVideoRejected) {
1521  EXPECT_TRUE(TestSerializeRejected(false, true));
1522}
1523
1524TEST_F(WebRtcSdpTest, SerializeSessionDescriptionWithAudioVideoRejected) {
1525  EXPECT_TRUE(TestSerializeRejected(true, true));
1526}
1527
1528TEST_F(WebRtcSdpTest, SerializeSessionDescriptionWithRtpDataChannel) {
1529  AddRtpDataChannel();
1530  JsepSessionDescription jsep_desc(kDummyString);
1531
1532  ASSERT_TRUE(jsep_desc.Initialize(desc_.Copy(), kSessionId, kSessionVersion));
1533  std::string message = webrtc::SdpSerialize(jsep_desc);
1534
1535  std::string expected_sdp = kSdpString;
1536  expected_sdp.append(kSdpRtpDataChannelString);
1537  EXPECT_EQ(expected_sdp, message);
1538}
1539
1540TEST_F(WebRtcSdpTest, SerializeSessionDescriptionWithSctpDataChannel) {
1541  AddSctpDataChannel();
1542  JsepSessionDescription jsep_desc(kDummyString);
1543
1544  ASSERT_TRUE(jsep_desc.Initialize(desc_.Copy(), kSessionId, kSessionVersion));
1545  std::string message = webrtc::SdpSerialize(jsep_desc);
1546
1547  std::string expected_sdp = kSdpString;
1548  expected_sdp.append(kSdpSctpDataChannelString);
1549  EXPECT_EQ(message, expected_sdp);
1550}
1551
1552TEST_F(WebRtcSdpTest, SerializeWithSctpDataChannelAndNewPort) {
1553  AddSctpDataChannel();
1554  JsepSessionDescription jsep_desc(kDummyString);
1555
1556  ASSERT_TRUE(jsep_desc.Initialize(desc_.Copy(), kSessionId, kSessionVersion));
1557  DataContentDescription* dcdesc = static_cast<DataContentDescription*>(
1558      jsep_desc.description()->GetContentDescriptionByName(kDataContentName));
1559
1560  const int kNewPort = 1234;
1561  cricket::DataCodec codec(
1562        cricket::kGoogleSctpDataCodecId, cricket::kGoogleSctpDataCodecName, 0);
1563  codec.SetParam(cricket::kCodecParamPort, kNewPort);
1564  dcdesc->AddOrReplaceCodec(codec);
1565
1566  std::string message = webrtc::SdpSerialize(jsep_desc);
1567
1568  std::string expected_sdp = kSdpString;
1569  expected_sdp.append(kSdpSctpDataChannelString);
1570
1571  char default_portstr[16];
1572  char new_portstr[16];
1573  rtc::sprintfn(default_portstr, sizeof(default_portstr), "%d",
1574                      kDefaultSctpPort);
1575  rtc::sprintfn(new_portstr, sizeof(new_portstr), "%d", kNewPort);
1576  rtc::replace_substrs(default_portstr, strlen(default_portstr),
1577                             new_portstr, strlen(new_portstr),
1578                             &expected_sdp);
1579
1580  EXPECT_EQ(expected_sdp, message);
1581}
1582
1583TEST_F(WebRtcSdpTest, SerializeSessionDescriptionWithDataChannelAndBandwidth) {
1584  AddRtpDataChannel();
1585  data_desc_->set_bandwidth(100*1000);
1586  JsepSessionDescription jsep_desc(kDummyString);
1587
1588  ASSERT_TRUE(jsep_desc.Initialize(desc_.Copy(), kSessionId, kSessionVersion));
1589  std::string message = webrtc::SdpSerialize(jsep_desc);
1590
1591  std::string expected_sdp = kSdpString;
1592  expected_sdp.append(kSdpRtpDataChannelString);
1593  // We want to test that serializing data content ignores bandwidth
1594  // settings (it should always be the default).  Thus, we don't do
1595  // the following:
1596  // TODO(pthatcher): We need to temporarily allow the SDP to control
1597  // this for backwards-compatibility.  Once we don't need that any
1598  // more, remove this.
1599  InjectAfter("m=application 1 RTP/SAVPF 101\r\nc=IN IP4 0.0.0.0\r\n",
1600              "b=AS:100\r\n",
1601              &expected_sdp);
1602  EXPECT_EQ(expected_sdp, message);
1603}
1604
1605TEST_F(WebRtcSdpTest, SerializeSessionDescriptionWithExtmap) {
1606  AddExtmap();
1607  JsepSessionDescription desc_with_extmap("dummy");
1608  ASSERT_TRUE(desc_with_extmap.Initialize(desc_.Copy(),
1609                                          kSessionId, kSessionVersion));
1610  std::string message = webrtc::SdpSerialize(desc_with_extmap);
1611
1612  std::string sdp_with_extmap = kSdpString;
1613  InjectAfter("a=mid:audio_content_name\r\n",
1614              kExtmap, &sdp_with_extmap);
1615  InjectAfter("a=mid:video_content_name\r\n",
1616              kExtmap, &sdp_with_extmap);
1617
1618  EXPECT_EQ(sdp_with_extmap, message);
1619}
1620
1621TEST_F(WebRtcSdpTest, SerializeSessionDescriptionWithBufferLatency) {
1622  VideoContentDescription* vcd = static_cast<VideoContentDescription*>(
1623      GetFirstVideoContent(&desc_)->description);
1624  vcd->set_buffered_mode_latency(128);
1625
1626  ASSERT_TRUE(jdesc_.Initialize(desc_.Copy(),
1627                                jdesc_.session_id(),
1628                                jdesc_.session_version()));
1629  std::string message = webrtc::SdpSerialize(jdesc_);
1630  std::string sdp_with_buffer_latency = kSdpFullString;
1631  InjectAfter("a=rtpmap:120 VP8/90000\r\n",
1632              "a=x-google-buffer-latency:128\r\n",
1633              &sdp_with_buffer_latency);
1634  EXPECT_EQ(sdp_with_buffer_latency, message);
1635}
1636
1637TEST_F(WebRtcSdpTest, SerializeCandidates) {
1638  std::string message = webrtc::SdpSerializeCandidate(*jcandidate_);
1639  EXPECT_EQ(std::string(kRawCandidate), message);
1640}
1641
1642// TODO(mallinath) : Enable this test once WebRTCSdp capable of parsing
1643// RFC 6544.
1644TEST_F(WebRtcSdpTest, SerializeTcpCandidates) {
1645  Candidate candidate(
1646      "", ICE_CANDIDATE_COMPONENT_RTP, "tcp",
1647      rtc::SocketAddress("192.168.1.5", 9), kCandidatePriority,
1648      "", "", LOCAL_PORT_TYPE,
1649      "", kCandidateGeneration, kCandidateFoundation1);
1650  candidate.set_tcptype(cricket::TCPTYPE_ACTIVE_STR);
1651  rtc::scoped_ptr<IceCandidateInterface> jcandidate(
1652    new JsepIceCandidate(std::string("audio_content_name"), 0, candidate));
1653
1654  std::string message = webrtc::SdpSerializeCandidate(*jcandidate);
1655  EXPECT_EQ(std::string(kSdpTcpActiveCandidate), message);
1656}
1657
1658TEST_F(WebRtcSdpTest, DeserializeSessionDescription) {
1659  JsepSessionDescription jdesc(kDummyString);
1660  // Deserialize
1661  EXPECT_TRUE(SdpDeserialize(kSdpFullString, &jdesc));
1662  // Verify
1663  EXPECT_TRUE(CompareSessionDescription(jdesc_, jdesc));
1664}
1665
1666TEST_F(WebRtcSdpTest, DeserializeSessionDescriptionWithoutMline) {
1667  JsepSessionDescription jdesc(kDummyString);
1668  const char kSdpWithoutMline[] =
1669    "v=0\r\n"
1670    "o=- 18446744069414584320 18446462598732840960 IN IP4 127.0.0.1\r\n"
1671    "s=-\r\n"
1672    "t=0 0\r\n"
1673    "a=msid-semantic: WMS local_stream_1 local_stream_2\r\n";
1674  // Deserialize
1675  EXPECT_TRUE(SdpDeserialize(kSdpWithoutMline, &jdesc));
1676  EXPECT_EQ(0u, jdesc.description()->contents().size());
1677}
1678
1679TEST_F(WebRtcSdpTest, DeserializeSessionDescriptionWithoutCarriageReturn) {
1680  JsepSessionDescription jdesc(kDummyString);
1681  std::string sdp_without_carriage_return = kSdpFullString;
1682  Replace("\r\n", "\n", &sdp_without_carriage_return);
1683  // Deserialize
1684  EXPECT_TRUE(SdpDeserialize(sdp_without_carriage_return, &jdesc));
1685  // Verify
1686  EXPECT_TRUE(CompareSessionDescription(jdesc_, jdesc));
1687}
1688
1689TEST_F(WebRtcSdpTest, DeserializeSessionDescriptionWithoutCandidates) {
1690  // SessionDescription with desc but without candidates.
1691  JsepSessionDescription jdesc_no_candidates(kDummyString);
1692  ASSERT_TRUE(jdesc_no_candidates.Initialize(desc_.Copy(),
1693                                             kSessionId, kSessionVersion));
1694  JsepSessionDescription new_jdesc(kDummyString);
1695  EXPECT_TRUE(SdpDeserialize(kSdpString, &new_jdesc));
1696  EXPECT_TRUE(CompareSessionDescription(jdesc_no_candidates, new_jdesc));
1697}
1698
1699TEST_F(WebRtcSdpTest, DeserializeSessionDescriptionWithoutRtpmap) {
1700  static const char kSdpNoRtpmapString[] =
1701      "v=0\r\n"
1702      "o=- 11 22 IN IP4 127.0.0.1\r\n"
1703      "s=-\r\n"
1704      "t=0 0\r\n"
1705      "m=audio 49232 RTP/AVP 0 18 103\r\n"
1706      // Codec that doesn't appear in the m= line will be ignored.
1707      "a=rtpmap:104 CELT/32000/2\r\n"
1708      // The rtpmap line for static payload codec is optional.
1709      "a=rtpmap:18 G729/16000\r\n"
1710      "a=rtpmap:103 ISAC/16000\r\n";
1711
1712  JsepSessionDescription jdesc(kDummyString);
1713  EXPECT_TRUE(SdpDeserialize(kSdpNoRtpmapString, &jdesc));
1714  cricket::AudioContentDescription* audio =
1715    static_cast<AudioContentDescription*>(
1716        jdesc.description()->GetContentDescriptionByName(cricket::CN_AUDIO));
1717  AudioCodecs ref_codecs;
1718  // The codecs in the AudioContentDescription will be sorted by preference.
1719  ref_codecs.push_back(AudioCodec(0, "PCMU", 8000, 0, 1, 3));
1720  ref_codecs.push_back(AudioCodec(18, "G729", 16000, 0, 1, 2));
1721  ref_codecs.push_back(AudioCodec(103, "ISAC", 16000, 32000, 1, 1));
1722  EXPECT_EQ(ref_codecs, audio->codecs());
1723}
1724
1725TEST_F(WebRtcSdpTest, DeserializeSessionDescriptionWithoutRtpmapButWithFmtp) {
1726  static const char kSdpNoRtpmapString[] =
1727      "v=0\r\n"
1728      "o=- 11 22 IN IP4 127.0.0.1\r\n"
1729      "s=-\r\n"
1730      "t=0 0\r\n"
1731      "m=audio 49232 RTP/AVP 18 103\r\n"
1732      "a=fmtp:18 annexb=yes\r\n"
1733      "a=rtpmap:103 ISAC/16000\r\n";
1734
1735  JsepSessionDescription jdesc(kDummyString);
1736  EXPECT_TRUE(SdpDeserialize(kSdpNoRtpmapString, &jdesc));
1737  cricket::AudioContentDescription* audio =
1738    static_cast<AudioContentDescription*>(
1739        jdesc.description()->GetContentDescriptionByName(cricket::CN_AUDIO));
1740
1741  cricket::AudioCodec g729 = audio->codecs()[0];
1742  EXPECT_EQ("G729", g729.name);
1743  EXPECT_EQ(8000, g729.clockrate);
1744  EXPECT_EQ(18, g729.id);
1745  cricket::CodecParameterMap::iterator found =
1746      g729.params.find("annexb");
1747  ASSERT_TRUE(found != g729.params.end());
1748  EXPECT_EQ(found->second, "yes");
1749
1750  cricket::AudioCodec isac = audio->codecs()[1];
1751  EXPECT_EQ("ISAC", isac.name);
1752  EXPECT_EQ(103, isac.id);
1753  EXPECT_EQ(16000, isac.clockrate);
1754}
1755
1756// Ensure that we can deserialize SDP with a=fingerprint properly.
1757TEST_F(WebRtcSdpTest, DeserializeJsepSessionDescriptionWithFingerprint) {
1758  // Add a DTLS a=fingerprint attribute to our session description.
1759  AddFingerprint();
1760  JsepSessionDescription new_jdesc(kDummyString);
1761  ASSERT_TRUE(new_jdesc.Initialize(desc_.Copy(),
1762                                   jdesc_.session_id(),
1763                                   jdesc_.session_version()));
1764
1765  JsepSessionDescription jdesc_with_fingerprint(kDummyString);
1766  std::string sdp_with_fingerprint = kSdpString;
1767  InjectAfter(kAttributeIcePwdVoice, kFingerprint, &sdp_with_fingerprint);
1768  InjectAfter(kAttributeIcePwdVideo, kFingerprint, &sdp_with_fingerprint);
1769  EXPECT_TRUE(SdpDeserialize(sdp_with_fingerprint, &jdesc_with_fingerprint));
1770  EXPECT_TRUE(CompareSessionDescription(jdesc_with_fingerprint, new_jdesc));
1771}
1772
1773TEST_F(WebRtcSdpTest, DeserializeSessionDescriptionWithBundle) {
1774  JsepSessionDescription jdesc_with_bundle(kDummyString);
1775  std::string sdp_with_bundle = kSdpFullString;
1776  InjectAfter(kSessionTime,
1777              "a=group:BUNDLE audio_content_name video_content_name\r\n",
1778              &sdp_with_bundle);
1779  EXPECT_TRUE(SdpDeserialize(sdp_with_bundle, &jdesc_with_bundle));
1780  ContentGroup group(cricket::GROUP_TYPE_BUNDLE);
1781  group.AddContentName(kAudioContentName);
1782  group.AddContentName(kVideoContentName);
1783  desc_.AddGroup(group);
1784  ASSERT_TRUE(jdesc_.Initialize(desc_.Copy(),
1785                                jdesc_.session_id(),
1786                                jdesc_.session_version()));
1787  EXPECT_TRUE(CompareSessionDescription(jdesc_, jdesc_with_bundle));
1788}
1789
1790TEST_F(WebRtcSdpTest, DeserializeSessionDescriptionWithBandwidth) {
1791  JsepSessionDescription jdesc_with_bandwidth(kDummyString);
1792  std::string sdp_with_bandwidth = kSdpFullString;
1793  InjectAfter("a=mid:video_content_name\r\na=sendrecv\r\n",
1794              "b=AS:100\r\n",
1795              &sdp_with_bandwidth);
1796  InjectAfter("a=mid:audio_content_name\r\na=sendrecv\r\n",
1797              "b=AS:50\r\n",
1798              &sdp_with_bandwidth);
1799  EXPECT_TRUE(
1800      SdpDeserialize(sdp_with_bandwidth, &jdesc_with_bandwidth));
1801  VideoContentDescription* vcd = static_cast<VideoContentDescription*>(
1802      GetFirstVideoContent(&desc_)->description);
1803  vcd->set_bandwidth(100 * 1000);
1804  AudioContentDescription* acd = static_cast<AudioContentDescription*>(
1805      GetFirstAudioContent(&desc_)->description);
1806  acd->set_bandwidth(50 * 1000);
1807  ASSERT_TRUE(jdesc_.Initialize(desc_.Copy(),
1808                                jdesc_.session_id(),
1809                                jdesc_.session_version()));
1810  EXPECT_TRUE(CompareSessionDescription(jdesc_, jdesc_with_bandwidth));
1811}
1812
1813TEST_F(WebRtcSdpTest, DeserializeSessionDescriptionWithIceOptions) {
1814  JsepSessionDescription jdesc_with_ice_options(kDummyString);
1815  std::string sdp_with_ice_options = kSdpFullString;
1816  InjectAfter(kSessionTime,
1817              "a=ice-options:iceoption3\r\n",
1818              &sdp_with_ice_options);
1819  InjectAfter(kAttributeIcePwdVoice,
1820              "a=ice-options:iceoption1\r\n",
1821              &sdp_with_ice_options);
1822  InjectAfter(kAttributeIcePwdVideo,
1823              "a=ice-options:iceoption2\r\n",
1824              &sdp_with_ice_options);
1825  EXPECT_TRUE(SdpDeserialize(sdp_with_ice_options, &jdesc_with_ice_options));
1826  std::vector<std::string> transport_options;
1827  transport_options.push_back(kIceOption3);
1828  transport_options.push_back(kIceOption1);
1829  AddIceOptions(kAudioContentName, transport_options);
1830  transport_options.clear();
1831  transport_options.push_back(kIceOption3);
1832  transport_options.push_back(kIceOption2);
1833  AddIceOptions(kVideoContentName, transport_options);
1834  ASSERT_TRUE(jdesc_.Initialize(desc_.Copy(),
1835                                jdesc_.session_id(),
1836                                jdesc_.session_version()));
1837  EXPECT_TRUE(CompareSessionDescription(jdesc_, jdesc_with_ice_options));
1838}
1839
1840TEST_F(WebRtcSdpTest, DeserializeSessionDescriptionWithUfragPwd) {
1841  // Remove the original ice-ufrag and ice-pwd
1842  JsepSessionDescription jdesc_with_ufrag_pwd(kDummyString);
1843  std::string sdp_with_ufrag_pwd = kSdpFullString;
1844  EXPECT_TRUE(RemoveCandidateUfragPwd(&sdp_with_ufrag_pwd));
1845  // Add session level ufrag and pwd
1846  InjectAfter(kSessionTime,
1847      "a=ice-pwd:session+level+icepwd\r\n"
1848      "a=ice-ufrag:session+level+iceufrag\r\n",
1849      &sdp_with_ufrag_pwd);
1850  // Add media level ufrag and pwd for audio
1851  InjectAfter("a=mid:audio_content_name\r\n",
1852      "a=ice-pwd:media+level+icepwd\r\na=ice-ufrag:media+level+iceufrag\r\n",
1853      &sdp_with_ufrag_pwd);
1854  // Update the candidate ufrag and pwd to the expected ones.
1855  EXPECT_TRUE(UpdateCandidateUfragPwd(&jdesc_, 0,
1856      "media+level+iceufrag", "media+level+icepwd"));
1857  EXPECT_TRUE(UpdateCandidateUfragPwd(&jdesc_, 1,
1858      "session+level+iceufrag", "session+level+icepwd"));
1859  EXPECT_TRUE(SdpDeserialize(sdp_with_ufrag_pwd, &jdesc_with_ufrag_pwd));
1860  EXPECT_TRUE(CompareSessionDescription(jdesc_, jdesc_with_ufrag_pwd));
1861}
1862
1863TEST_F(WebRtcSdpTest, DeserializeSessionDescriptionWithBufferLatency) {
1864  JsepSessionDescription jdesc_with_buffer_latency(kDummyString);
1865  std::string sdp_with_buffer_latency = kSdpFullString;
1866  InjectAfter("a=rtpmap:120 VP8/90000\r\n",
1867              "a=x-google-buffer-latency:128\r\n",
1868              &sdp_with_buffer_latency);
1869
1870  EXPECT_TRUE(
1871      SdpDeserialize(sdp_with_buffer_latency, &jdesc_with_buffer_latency));
1872  VideoContentDescription* vcd = static_cast<VideoContentDescription*>(
1873      GetFirstVideoContent(&desc_)->description);
1874  vcd->set_buffered_mode_latency(128);
1875  ASSERT_TRUE(jdesc_.Initialize(desc_.Copy(),
1876                                jdesc_.session_id(),
1877                                jdesc_.session_version()));
1878  EXPECT_TRUE(CompareSessionDescription(jdesc_, jdesc_with_buffer_latency));
1879}
1880
1881TEST_F(WebRtcSdpTest, DeserializeSessionDescriptionWithRecvOnlyContent) {
1882  EXPECT_TRUE(TestDeserializeDirection(cricket::MD_RECVONLY));
1883}
1884
1885TEST_F(WebRtcSdpTest, DeserializeSessionDescriptionWithSendOnlyContent) {
1886  EXPECT_TRUE(TestDeserializeDirection(cricket::MD_SENDONLY));
1887}
1888
1889TEST_F(WebRtcSdpTest, DeserializeSessionDescriptionWithInactiveContent) {
1890  EXPECT_TRUE(TestDeserializeDirection(cricket::MD_INACTIVE));
1891}
1892
1893TEST_F(WebRtcSdpTest, DeserializeSessionDescriptionWithRejectedAudio) {
1894  EXPECT_TRUE(TestDeserializeRejected(true, false));
1895}
1896
1897TEST_F(WebRtcSdpTest, DeserializeSessionDescriptionWithRejectedVideo) {
1898  EXPECT_TRUE(TestDeserializeRejected(false, true));
1899}
1900
1901TEST_F(WebRtcSdpTest, DeserializeSessionDescriptionWithRejectedAudioVideo) {
1902  EXPECT_TRUE(TestDeserializeRejected(true, true));
1903}
1904
1905// Tests that we can still handle the sdp uses mslabel and label instead of
1906// msid for backward compatibility.
1907TEST_F(WebRtcSdpTest, DeserializeSessionDescriptionWithoutMsid) {
1908  JsepSessionDescription jdesc(kDummyString);
1909  std::string sdp_without_msid = kSdpFullString;
1910  Replace("msid", "xmsid", &sdp_without_msid);
1911  // Deserialize
1912  EXPECT_TRUE(SdpDeserialize(sdp_without_msid, &jdesc));
1913  // Verify
1914  EXPECT_TRUE(CompareSessionDescription(jdesc_, jdesc));
1915}
1916
1917TEST_F(WebRtcSdpTest, DeserializeCandidate) {
1918  JsepIceCandidate jcandidate(kDummyMid, kDummyIndex);
1919
1920  std::string sdp = kSdpOneCandidate;
1921  EXPECT_TRUE(SdpDeserializeCandidate(sdp, &jcandidate));
1922  EXPECT_EQ(kDummyMid, jcandidate.sdp_mid());
1923  EXPECT_EQ(kDummyIndex, jcandidate.sdp_mline_index());
1924  EXPECT_TRUE(jcandidate.candidate().IsEquivalent(jcandidate_->candidate()));
1925
1926  // Candidate line without generation extension.
1927  sdp = kSdpOneCandidate;
1928  Replace(" generation 2", "", &sdp);
1929  EXPECT_TRUE(SdpDeserializeCandidate(sdp, &jcandidate));
1930  EXPECT_EQ(kDummyMid, jcandidate.sdp_mid());
1931  EXPECT_EQ(kDummyIndex, jcandidate.sdp_mline_index());
1932  Candidate expected = jcandidate_->candidate();
1933  expected.set_generation(0);
1934  EXPECT_TRUE(jcandidate.candidate().IsEquivalent(expected));
1935
1936  sdp = kSdpTcpActiveCandidate;
1937  EXPECT_TRUE(SdpDeserializeCandidate(sdp, &jcandidate));
1938  // Make a cricket::Candidate equivalent to kSdpTcpCandidate string.
1939  Candidate candidate(
1940      "", ICE_CANDIDATE_COMPONENT_RTP, "tcp",
1941      rtc::SocketAddress("192.168.1.5", 9), kCandidatePriority,
1942      "", "", LOCAL_PORT_TYPE,
1943      "", kCandidateGeneration, kCandidateFoundation1);
1944  rtc::scoped_ptr<IceCandidateInterface> jcandidate_template(
1945    new JsepIceCandidate(std::string("audio_content_name"), 0, candidate));
1946  EXPECT_TRUE(jcandidate.candidate().IsEquivalent(
1947                    jcandidate_template->candidate()));
1948  sdp = kSdpTcpPassiveCandidate;
1949  EXPECT_TRUE(SdpDeserializeCandidate(sdp, &jcandidate));
1950  sdp = kSdpTcpSOCandidate;
1951  EXPECT_TRUE(SdpDeserializeCandidate(sdp, &jcandidate));
1952}
1953
1954// This test verifies the deserialization of candidate-attribute
1955// as per RFC 5245. Candiate-attribute will be of the format
1956// candidate:<blah>. This format will be used when candidates
1957// are trickled.
1958TEST_F(WebRtcSdpTest, DeserializeRawCandidateAttribute) {
1959  JsepIceCandidate jcandidate(kDummyMid, kDummyIndex);
1960
1961  std::string candidate_attribute = kRawCandidate;
1962  EXPECT_TRUE(SdpDeserializeCandidate(candidate_attribute, &jcandidate));
1963  EXPECT_EQ(kDummyMid, jcandidate.sdp_mid());
1964  EXPECT_EQ(kDummyIndex, jcandidate.sdp_mline_index());
1965  EXPECT_TRUE(jcandidate.candidate().IsEquivalent(jcandidate_->candidate()));
1966  EXPECT_EQ(2u, jcandidate.candidate().generation());
1967
1968  // Candidate line without generation extension.
1969  candidate_attribute = kRawCandidate;
1970  Replace(" generation 2", "", &candidate_attribute);
1971  EXPECT_TRUE(SdpDeserializeCandidate(candidate_attribute, &jcandidate));
1972  EXPECT_EQ(kDummyMid, jcandidate.sdp_mid());
1973  EXPECT_EQ(kDummyIndex, jcandidate.sdp_mline_index());
1974  Candidate expected = jcandidate_->candidate();
1975  expected.set_generation(0);
1976  EXPECT_TRUE(jcandidate.candidate().IsEquivalent(expected));
1977
1978  // Candidate line without candidate:
1979  candidate_attribute = kRawCandidate;
1980  Replace("candidate:", "", &candidate_attribute);
1981  EXPECT_FALSE(SdpDeserializeCandidate(candidate_attribute, &jcandidate));
1982
1983  // Candidate line with IPV6 address.
1984  EXPECT_TRUE(SdpDeserializeCandidate(kRawIPV6Candidate, &jcandidate));
1985}
1986
1987// This test verifies that the deserialization of an invalid candidate string
1988// fails.
1989TEST_F(WebRtcSdpTest, DeserializeInvalidCandidiate) {
1990    JsepIceCandidate jcandidate(kDummyMid, kDummyIndex);
1991
1992  std::string candidate_attribute = kRawCandidate;
1993  candidate_attribute.replace(0, 1, "x");
1994  EXPECT_FALSE(SdpDeserializeCandidate(candidate_attribute, &jcandidate));
1995
1996  candidate_attribute = kSdpOneCandidate;
1997  candidate_attribute.replace(0, 1, "x");
1998  EXPECT_FALSE(SdpDeserializeCandidate(candidate_attribute, &jcandidate));
1999
2000  candidate_attribute = kRawCandidate;
2001  candidate_attribute.append("\r\n");
2002  candidate_attribute.append(kRawCandidate);
2003  EXPECT_FALSE(SdpDeserializeCandidate(candidate_attribute, &jcandidate));
2004
2005  EXPECT_FALSE(SdpDeserializeCandidate(kSdpTcpInvalidCandidate, &jcandidate));
2006}
2007
2008TEST_F(WebRtcSdpTest, DeserializeSdpWithRtpDataChannels) {
2009  AddRtpDataChannel();
2010  JsepSessionDescription jdesc(kDummyString);
2011  ASSERT_TRUE(jdesc.Initialize(desc_.Copy(), kSessionId, kSessionVersion));
2012
2013  std::string sdp_with_data = kSdpString;
2014  sdp_with_data.append(kSdpRtpDataChannelString);
2015  JsepSessionDescription jdesc_output(kDummyString);
2016
2017  // Deserialize
2018  EXPECT_TRUE(SdpDeserialize(sdp_with_data, &jdesc_output));
2019  // Verify
2020  EXPECT_TRUE(CompareSessionDescription(jdesc, jdesc_output));
2021}
2022
2023TEST_F(WebRtcSdpTest, DeserializeSdpWithSctpDataChannels) {
2024  AddSctpDataChannel();
2025  JsepSessionDescription jdesc(kDummyString);
2026  ASSERT_TRUE(jdesc.Initialize(desc_.Copy(), kSessionId, kSessionVersion));
2027
2028  std::string sdp_with_data = kSdpString;
2029  sdp_with_data.append(kSdpSctpDataChannelString);
2030  JsepSessionDescription jdesc_output(kDummyString);
2031
2032  EXPECT_TRUE(SdpDeserialize(sdp_with_data, &jdesc_output));
2033  EXPECT_TRUE(CompareSessionDescription(jdesc, jdesc_output));
2034}
2035
2036TEST_F(WebRtcSdpTest, DeserializeSdpWithSctpDataChannelsWithSctpPort) {
2037  AddSctpDataChannel();
2038  JsepSessionDescription jdesc(kDummyString);
2039  ASSERT_TRUE(jdesc.Initialize(desc_.Copy(), kSessionId, kSessionVersion));
2040
2041  std::string sdp_with_data = kSdpString;
2042  sdp_with_data.append(kSdpSctpDataChannelStringWithSctpPort);
2043  JsepSessionDescription jdesc_output(kDummyString);
2044
2045  EXPECT_TRUE(SdpDeserialize(sdp_with_data, &jdesc_output));
2046  EXPECT_TRUE(CompareSessionDescription(jdesc, jdesc_output));
2047}
2048
2049// Test to check the behaviour if sctp-port is specified
2050// on the m= line and in a=sctp-port.
2051TEST_F(WebRtcSdpTest, DeserializeSdpWithMultiSctpPort) {
2052  AddSctpDataChannel();
2053  JsepSessionDescription jdesc(kDummyString);
2054  ASSERT_TRUE(jdesc.Initialize(desc_.Copy(), kSessionId, kSessionVersion));
2055
2056  std::string sdp_with_data = kSdpString;
2057  // Append m= attributes
2058  sdp_with_data.append(kSdpSctpDataChannelString);
2059  // Append a=sctp-port attribute
2060  sdp_with_data.append("a=sctp-port 5000\r\n");
2061  JsepSessionDescription jdesc_output(kDummyString);
2062
2063  EXPECT_FALSE(SdpDeserialize(sdp_with_data, &jdesc_output));
2064}
2065
2066// For crbug/344475.
2067TEST_F(WebRtcSdpTest, DeserializeSdpWithCorruptedSctpDataChannels) {
2068  std::string sdp_with_data = kSdpString;
2069  sdp_with_data.append(kSdpSctpDataChannelString);
2070  // Remove the "\n" at the end.
2071  sdp_with_data = sdp_with_data.substr(0, sdp_with_data.size() - 1);
2072  JsepSessionDescription jdesc_output(kDummyString);
2073
2074  EXPECT_FALSE(SdpDeserialize(sdp_with_data, &jdesc_output));
2075  // No crash is a pass.
2076}
2077
2078TEST_F(WebRtcSdpTest, DeserializeSdpWithSctpDataChannelAndNewPort) {
2079  AddSctpDataChannel();
2080  const uint16 kUnusualSctpPort = 9556;
2081  char default_portstr[16];
2082  char unusual_portstr[16];
2083  rtc::sprintfn(default_portstr, sizeof(default_portstr), "%d",
2084                      kDefaultSctpPort);
2085  rtc::sprintfn(unusual_portstr, sizeof(unusual_portstr), "%d",
2086                      kUnusualSctpPort);
2087
2088  // First setup the expected JsepSessionDescription.
2089  JsepSessionDescription jdesc(kDummyString);
2090  // take our pre-built session description and change the SCTP port.
2091  cricket::SessionDescription* mutant = desc_.Copy();
2092  DataContentDescription* dcdesc = static_cast<DataContentDescription*>(
2093      mutant->GetContentDescriptionByName(kDataContentName));
2094  std::vector<cricket::DataCodec> codecs(dcdesc->codecs());
2095  EXPECT_EQ(codecs.size(), 1UL);
2096  EXPECT_EQ(codecs[0].id, cricket::kGoogleSctpDataCodecId);
2097  codecs[0].SetParam(cricket::kCodecParamPort, kUnusualSctpPort);
2098  dcdesc->set_codecs(codecs);
2099
2100  // note: mutant's owned by jdesc now.
2101  ASSERT_TRUE(jdesc.Initialize(mutant, kSessionId, kSessionVersion));
2102  mutant = NULL;
2103
2104  // Then get the deserialized JsepSessionDescription.
2105  std::string sdp_with_data = kSdpString;
2106  sdp_with_data.append(kSdpSctpDataChannelString);
2107  rtc::replace_substrs(default_portstr, strlen(default_portstr),
2108                             unusual_portstr, strlen(unusual_portstr),
2109                             &sdp_with_data);
2110  JsepSessionDescription jdesc_output(kDummyString);
2111
2112  EXPECT_TRUE(SdpDeserialize(sdp_with_data, &jdesc_output));
2113  EXPECT_TRUE(CompareSessionDescription(jdesc, jdesc_output));
2114
2115  // We need to test the deserialized JsepSessionDescription from
2116  // kSdpSctpDataChannelStringWithSctpPort for
2117  // draft-ietf-mmusic-sctp-sdp-07
2118  // a=sctp-port
2119  sdp_with_data = kSdpString;
2120  sdp_with_data.append(kSdpSctpDataChannelStringWithSctpPort);
2121  rtc::replace_substrs(default_portstr, strlen(default_portstr),
2122                             unusual_portstr, strlen(unusual_portstr),
2123                             &sdp_with_data);
2124
2125  EXPECT_TRUE(SdpDeserialize(sdp_with_data, &jdesc_output));
2126  EXPECT_TRUE(CompareSessionDescription(jdesc, jdesc_output));
2127}
2128
2129TEST_F(WebRtcSdpTest, DeserializeSdpWithRtpDataChannelsAndBandwidth) {
2130  AddRtpDataChannel();
2131  JsepSessionDescription jdesc(kDummyString);
2132  // We want to test that deserializing data content ignores bandwidth
2133  // settings (it should always be the default).  Thus, we don't do
2134  // the following:
2135  // TODO(pthatcher): We need to temporarily allow the SDP to control
2136  // this for backwards-compatibility.  Once we don't need that any
2137  // more, remove this.
2138  DataContentDescription* dcd = static_cast<DataContentDescription*>(
2139     GetFirstDataContent(&desc_)->description);
2140  dcd->set_bandwidth(100 * 1000);
2141  ASSERT_TRUE(jdesc.Initialize(desc_.Copy(), kSessionId, kSessionVersion));
2142
2143  std::string sdp_with_bandwidth = kSdpString;
2144  sdp_with_bandwidth.append(kSdpRtpDataChannelString);
2145  InjectAfter("a=mid:data_content_name\r\n",
2146              "b=AS:100\r\n",
2147              &sdp_with_bandwidth);
2148  JsepSessionDescription jdesc_with_bandwidth(kDummyString);
2149
2150  EXPECT_TRUE(
2151      SdpDeserialize(sdp_with_bandwidth, &jdesc_with_bandwidth));
2152  EXPECT_TRUE(CompareSessionDescription(jdesc, jdesc_with_bandwidth));
2153}
2154
2155TEST_F(WebRtcSdpTest, DeserializeSessionDescriptionWithSessionLevelExtmap) {
2156  TestDeserializeExtmap(true, false);
2157}
2158
2159TEST_F(WebRtcSdpTest, DeserializeSessionDescriptionWithMediaLevelExtmap) {
2160  TestDeserializeExtmap(false, true);
2161}
2162
2163TEST_F(WebRtcSdpTest, DeserializeSessionDescriptionWithInvalidExtmap) {
2164  TestDeserializeExtmap(true, true);
2165}
2166
2167TEST_F(WebRtcSdpTest, DeserializeSessionDescriptionWithoutEndLineBreak) {
2168  JsepSessionDescription jdesc(kDummyString);
2169  std::string sdp = kSdpFullString;
2170  sdp = sdp.substr(0, sdp.size() - 2);  // Remove \r\n at the end.
2171  // Deserialize
2172  SdpParseError error;
2173  EXPECT_FALSE(webrtc::SdpDeserialize(sdp, &jdesc, &error));
2174  const std::string lastline = "a=ssrc:6 label:video_track_id_3";
2175  EXPECT_EQ(lastline, error.line);
2176  EXPECT_EQ("Invalid SDP line.", error.description);
2177}
2178
2179TEST_F(WebRtcSdpTest, DeserializeCandidateWithDifferentTransport) {
2180  JsepIceCandidate jcandidate(kDummyMid, kDummyIndex);
2181  std::string new_sdp = kSdpOneCandidate;
2182  Replace("udp", "unsupported_transport", &new_sdp);
2183  EXPECT_FALSE(SdpDeserializeCandidate(new_sdp, &jcandidate));
2184  new_sdp = kSdpOneCandidate;
2185  Replace("udp", "uDP", &new_sdp);
2186  EXPECT_TRUE(SdpDeserializeCandidate(new_sdp, &jcandidate));
2187  EXPECT_EQ(kDummyMid, jcandidate.sdp_mid());
2188  EXPECT_EQ(kDummyIndex, jcandidate.sdp_mline_index());
2189  EXPECT_TRUE(jcandidate.candidate().IsEquivalent(jcandidate_->candidate()));
2190}
2191
2192TEST_F(WebRtcSdpTest, DeserializeCandidateOldFormat) {
2193  JsepIceCandidate jcandidate(kDummyMid, kDummyIndex);
2194  EXPECT_TRUE(SdpDeserializeCandidate(kSdpOneCandidateOldFormat,&jcandidate));
2195  EXPECT_EQ(kDummyMid, jcandidate.sdp_mid());
2196  EXPECT_EQ(kDummyIndex, jcandidate.sdp_mline_index());
2197  Candidate ref_candidate = jcandidate_->candidate();
2198  ref_candidate.set_username("user_rtp");
2199  ref_candidate.set_password("password_rtp");
2200  EXPECT_TRUE(jcandidate.candidate().IsEquivalent(ref_candidate));
2201}
2202
2203TEST_F(WebRtcSdpTest, DeserializeSdpWithConferenceFlag) {
2204  JsepSessionDescription jdesc(kDummyString);
2205
2206  // Deserialize
2207  EXPECT_TRUE(SdpDeserialize(kSdpConferenceString, &jdesc));
2208
2209  // Verify
2210  cricket::AudioContentDescription* audio =
2211    static_cast<AudioContentDescription*>(
2212      jdesc.description()->GetContentDescriptionByName(cricket::CN_AUDIO));
2213  EXPECT_TRUE(audio->conference_mode());
2214
2215  cricket::VideoContentDescription* video =
2216    static_cast<VideoContentDescription*>(
2217      jdesc.description()->GetContentDescriptionByName(cricket::CN_VIDEO));
2218  EXPECT_TRUE(video->conference_mode());
2219}
2220
2221TEST_F(WebRtcSdpTest, DeserializeBrokenSdp) {
2222  const char kSdpDestroyer[] = "!@#$%^&";
2223  const char kSdpInvalidLine1[] = " =candidate";
2224  const char kSdpInvalidLine2[] = "a+candidate";
2225  const char kSdpInvalidLine3[] = "a= candidate";
2226  // Broken fingerprint.
2227  const char kSdpInvalidLine4[] = "a=fingerprint:sha-1 "
2228      "4AAD:B9:B1:3F:82:18:3B:54:02:12:DF:3E:5D:49:6B:19:E5:7C:AB";
2229  // Extra field.
2230  const char kSdpInvalidLine5[] = "a=fingerprint:sha-1 "
2231      "4A:AD:B9:B1:3F:82:18:3B:54:02:12:DF:3E:5D:49:6B:19:E5:7C:AB XXX";
2232  // Missing space.
2233  const char kSdpInvalidLine6[] = "a=fingerprint:sha-1"
2234      "4A:AD:B9:B1:3F:82:18:3B:54:02:12:DF:3E:5D:49:6B:19:E5:7C:AB";
2235  // MD5 is not allowed in fingerprints.
2236  const char kSdpInvalidLine7[] = "a=fingerprint:md5 "
2237      "4A:AD:B9:B1:3F:82:18:3B:54:02:12:DF:3E:5D:49:6B";
2238
2239  // Broken session description
2240  ExpectParseFailure("v=", kSdpDestroyer);
2241  ExpectParseFailure("o=", kSdpDestroyer);
2242  ExpectParseFailure("s=-", kSdpDestroyer);
2243  // Broken time description
2244  ExpectParseFailure("t=", kSdpDestroyer);
2245
2246  // Broken media description
2247  ExpectParseFailure("m=audio", "c=IN IP4 74.125.224.39");
2248  ExpectParseFailure("m=video", kSdpDestroyer);
2249
2250  // Invalid lines
2251  ExpectParseFailure("a=candidate", kSdpInvalidLine1);
2252  ExpectParseFailure("a=candidate", kSdpInvalidLine2);
2253  ExpectParseFailure("a=candidate", kSdpInvalidLine3);
2254
2255  // Bogus fingerprint replacing a=sendrev. We selected this attribute
2256  // because it's orthogonal to what we are replacing and hence
2257  // safe.
2258  ExpectParseFailure("a=sendrecv", kSdpInvalidLine4);
2259  ExpectParseFailure("a=sendrecv", kSdpInvalidLine5);
2260  ExpectParseFailure("a=sendrecv", kSdpInvalidLine6);
2261  ExpectParseFailure("a=sendrecv", kSdpInvalidLine7);
2262}
2263
2264TEST_F(WebRtcSdpTest, DeserializeSdpWithInvalidAttributeValue) {
2265  // ssrc
2266  ExpectParseFailure("a=ssrc:1", "a=ssrc:badvalue");
2267  ExpectParseFailure("a=ssrc-group:FEC 5 6", "a=ssrc-group:FEC badvalue 6");
2268  // crypto
2269  ExpectParseFailure("a=crypto:1 ", "a=crypto:badvalue ");
2270  // rtpmap
2271  ExpectParseFailure("a=rtpmap:111 ", "a=rtpmap:badvalue ");
2272  ExpectParseFailure("opus/48000/2", "opus/badvalue/2");
2273  ExpectParseFailure("opus/48000/2", "opus/48000/badvalue");
2274  // candidate
2275  ExpectParseFailure("1 udp 2130706432", "badvalue udp 2130706432");
2276  ExpectParseFailure("1 udp 2130706432", "1 udp badvalue");
2277  ExpectParseFailure("192.168.1.5 1234", "192.168.1.5 badvalue");
2278  ExpectParseFailure("rport 2346", "rport badvalue");
2279  ExpectParseFailure("rport 2346 generation 2",
2280                     "rport 2346 generation badvalue");
2281  // m line
2282  ExpectParseFailure("m=audio 2345 RTP/SAVPF 111 103 104",
2283                     "m=audio 2345 RTP/SAVPF 111 badvalue 104");
2284
2285  // bandwidth
2286  ExpectParseFailureWithNewLines("a=mid:video_content_name\r\n",
2287                                 "b=AS:badvalue\r\n",
2288                                 "b=AS:badvalue");
2289  // rtcp-fb
2290  ExpectParseFailureWithNewLines("a=mid:video_content_name\r\n",
2291                                 "a=rtcp-fb:badvalue nack\r\n",
2292                                 "a=rtcp-fb:badvalue nack");
2293  // extmap
2294  ExpectParseFailureWithNewLines("a=mid:video_content_name\r\n",
2295                                 "a=extmap:badvalue http://example.com\r\n",
2296                                 "a=extmap:badvalue http://example.com");
2297  // x-google-buffer-latency
2298  ExpectParseFailureWithNewLines("a=mid:video_content_name\r\n",
2299                                 "a=x-google-buffer-latency:badvalue\r\n",
2300                                 "a=x-google-buffer-latency:badvalue");
2301}
2302
2303TEST_F(WebRtcSdpTest, DeserializeSdpWithReorderedPltypes) {
2304  JsepSessionDescription jdesc_output(kDummyString);
2305
2306  const char kSdpWithReorderedPlTypesString[] =
2307      "v=0\r\n"
2308      "o=- 18446744069414584320 18446462598732840960 IN IP4 127.0.0.1\r\n"
2309      "s=-\r\n"
2310      "t=0 0\r\n"
2311      "m=audio 1 RTP/SAVPF 104 103\r\n"  // Pl type 104 preferred.
2312      "a=rtpmap:111 opus/48000/2\r\n"  // Pltype 111 listed before 103 and 104
2313                                       // in the map.
2314      "a=rtpmap:103 ISAC/16000\r\n"  // Pltype 103 listed before 104 in the map.
2315      "a=rtpmap:104 CELT/32000/2\r\n";
2316
2317  // Deserialize
2318  EXPECT_TRUE(SdpDeserialize(kSdpWithReorderedPlTypesString, &jdesc_output));
2319
2320  const ContentInfo* ac = GetFirstAudioContent(jdesc_output.description());
2321  ASSERT_TRUE(ac != NULL);
2322  const AudioContentDescription* acd =
2323      static_cast<const AudioContentDescription*>(ac->description);
2324  ASSERT_FALSE(acd->codecs().empty());
2325  EXPECT_EQ("CELT", acd->codecs()[0].name);
2326  EXPECT_EQ(104, acd->codecs()[0].id);
2327}
2328
2329TEST_F(WebRtcSdpTest, DeserializeSerializeCodecParams) {
2330  JsepSessionDescription jdesc_output(kDummyString);
2331  CodecParams params;
2332  params.max_ptime = 40;
2333  params.ptime = 30;
2334  params.min_ptime = 10;
2335  params.sprop_stereo = 1;
2336  params.stereo = 1;
2337  params.useinband = 1;
2338  params.maxaveragebitrate = 128000;
2339  TestDeserializeCodecParams(params, &jdesc_output);
2340  TestSerialize(jdesc_output);
2341}
2342
2343TEST_F(WebRtcSdpTest, DeserializeSerializeRtcpFb) {
2344  const bool kUseWildcard = false;
2345  JsepSessionDescription jdesc_output(kDummyString);
2346  TestDeserializeRtcpFb(&jdesc_output, kUseWildcard);
2347  TestSerialize(jdesc_output);
2348}
2349
2350TEST_F(WebRtcSdpTest, DeserializeSerializeRtcpFbWildcard) {
2351  const bool kUseWildcard = true;
2352  JsepSessionDescription jdesc_output(kDummyString);
2353  TestDeserializeRtcpFb(&jdesc_output, kUseWildcard);
2354  TestSerialize(jdesc_output);
2355}
2356
2357TEST_F(WebRtcSdpTest, DeserializeVideoFmtp) {
2358  JsepSessionDescription jdesc_output(kDummyString);
2359
2360  const char kSdpWithFmtpString[] =
2361      "v=0\r\n"
2362      "o=- 18446744069414584320 18446462598732840960 IN IP4 127.0.0.1\r\n"
2363      "s=-\r\n"
2364      "t=0 0\r\n"
2365      "m=video 3457 RTP/SAVPF 120\r\n"
2366      "a=rtpmap:120 VP8/90000\r\n"
2367      "a=fmtp:120 x-google-min-bitrate=10; x-google-max-quantization=40\r\n";
2368
2369  // Deserialize
2370  SdpParseError error;
2371  EXPECT_TRUE(webrtc::SdpDeserialize(kSdpWithFmtpString, &jdesc_output,
2372                                     &error));
2373
2374  const ContentInfo* vc = GetFirstVideoContent(jdesc_output.description());
2375  ASSERT_TRUE(vc != NULL);
2376  const VideoContentDescription* vcd =
2377      static_cast<const VideoContentDescription*>(vc->description);
2378  ASSERT_FALSE(vcd->codecs().empty());
2379  cricket::VideoCodec vp8 = vcd->codecs()[0];
2380  EXPECT_EQ("VP8", vp8.name);
2381  EXPECT_EQ(120, vp8.id);
2382  cricket::CodecParameterMap::iterator found =
2383      vp8.params.find("x-google-min-bitrate");
2384  ASSERT_TRUE(found != vp8.params.end());
2385  EXPECT_EQ(found->second, "10");
2386  found = vp8.params.find("x-google-max-quantization");
2387  ASSERT_TRUE(found != vp8.params.end());
2388  EXPECT_EQ(found->second, "40");
2389}
2390
2391TEST_F(WebRtcSdpTest, SerializeVideoFmtp) {
2392  VideoContentDescription* vcd = static_cast<VideoContentDescription*>(
2393      GetFirstVideoContent(&desc_)->description);
2394
2395  cricket::VideoCodecs codecs = vcd->codecs();
2396  codecs[0].params["x-google-min-bitrate"] = "10";
2397  vcd->set_codecs(codecs);
2398
2399  ASSERT_TRUE(jdesc_.Initialize(desc_.Copy(),
2400                                jdesc_.session_id(),
2401                                jdesc_.session_version()));
2402  std::string message = webrtc::SdpSerialize(jdesc_);
2403  std::string sdp_with_fmtp = kSdpFullString;
2404  InjectAfter("a=rtpmap:120 VP8/90000\r\n",
2405              "a=fmtp:120 x-google-min-bitrate=10\r\n",
2406              &sdp_with_fmtp);
2407  EXPECT_EQ(sdp_with_fmtp, message);
2408}
2409
2410TEST_F(WebRtcSdpTest, DeserializeSdpWithIceLite) {
2411  JsepSessionDescription jdesc_with_icelite(kDummyString);
2412  std::string sdp_with_icelite = kSdpFullString;
2413  EXPECT_TRUE(SdpDeserialize(sdp_with_icelite, &jdesc_with_icelite));
2414  cricket::SessionDescription* desc = jdesc_with_icelite.description();
2415  const cricket::TransportInfo* tinfo1 =
2416      desc->GetTransportInfoByName("audio_content_name");
2417  EXPECT_EQ(cricket::ICEMODE_FULL, tinfo1->description.ice_mode);
2418  const cricket::TransportInfo* tinfo2 =
2419      desc->GetTransportInfoByName("video_content_name");
2420  EXPECT_EQ(cricket::ICEMODE_FULL, tinfo2->description.ice_mode);
2421  InjectAfter(kSessionTime,
2422              "a=ice-lite\r\n",
2423              &sdp_with_icelite);
2424  EXPECT_TRUE(SdpDeserialize(sdp_with_icelite, &jdesc_with_icelite));
2425  desc = jdesc_with_icelite.description();
2426  const cricket::TransportInfo* atinfo =
2427      desc->GetTransportInfoByName("audio_content_name");
2428  EXPECT_EQ(cricket::ICEMODE_LITE, atinfo->description.ice_mode);
2429  const cricket::TransportInfo* vtinfo =
2430        desc->GetTransportInfoByName("video_content_name");
2431  EXPECT_EQ(cricket::ICEMODE_LITE, vtinfo->description.ice_mode);
2432}
2433
2434// Verifies that the candidates in the input SDP are parsed and serialized
2435// correctly in the output SDP.
2436TEST_F(WebRtcSdpTest, RoundTripSdpWithSctpDataChannelsWithCandidates) {
2437  std::string sdp_with_data = kSdpString;
2438  sdp_with_data.append(kSdpSctpDataChannelWithCandidatesString);
2439  JsepSessionDescription jdesc_output(kDummyString);
2440
2441  EXPECT_TRUE(SdpDeserialize(sdp_with_data, &jdesc_output));
2442  EXPECT_EQ(sdp_with_data, webrtc::SdpSerialize(jdesc_output));
2443}
2444
2445TEST_F(WebRtcSdpTest, SerializeDtlsSetupAttribute) {
2446  AddFingerprint();
2447  TransportInfo audio_transport_info =
2448      *(desc_.GetTransportInfoByName(kAudioContentName));
2449  EXPECT_EQ(cricket::CONNECTIONROLE_NONE,
2450            audio_transport_info.description.connection_role);
2451  audio_transport_info.description.connection_role =
2452        cricket::CONNECTIONROLE_ACTIVE;
2453
2454  TransportInfo video_transport_info =
2455      *(desc_.GetTransportInfoByName(kVideoContentName));
2456  EXPECT_EQ(cricket::CONNECTIONROLE_NONE,
2457            video_transport_info.description.connection_role);
2458  video_transport_info.description.connection_role =
2459        cricket::CONNECTIONROLE_ACTIVE;
2460
2461  desc_.RemoveTransportInfoByName(kAudioContentName);
2462  desc_.RemoveTransportInfoByName(kVideoContentName);
2463
2464  desc_.AddTransportInfo(audio_transport_info);
2465  desc_.AddTransportInfo(video_transport_info);
2466
2467  ASSERT_TRUE(jdesc_.Initialize(desc_.Copy(),
2468                                jdesc_.session_id(),
2469                                jdesc_.session_version()));
2470  std::string message = webrtc::SdpSerialize(jdesc_);
2471  std::string sdp_with_dtlssetup = kSdpFullString;
2472
2473  // Fingerprint attribute is necessary to add DTLS setup attribute.
2474  InjectAfter(kAttributeIcePwdVoice,
2475              kFingerprint, &sdp_with_dtlssetup);
2476  InjectAfter(kAttributeIcePwdVideo,
2477              kFingerprint, &sdp_with_dtlssetup);
2478  // Now adding |setup| attribute.
2479  InjectAfter(kFingerprint,
2480              "a=setup:active\r\n", &sdp_with_dtlssetup);
2481  EXPECT_EQ(sdp_with_dtlssetup, message);
2482}
2483
2484TEST_F(WebRtcSdpTest, DeserializeDtlsSetupAttribute) {
2485  JsepSessionDescription jdesc_with_dtlssetup(kDummyString);
2486  std::string sdp_with_dtlssetup = kSdpFullString;
2487  InjectAfter(kSessionTime,
2488              "a=setup:actpass\r\n",
2489              &sdp_with_dtlssetup);
2490  EXPECT_TRUE(SdpDeserialize(sdp_with_dtlssetup, &jdesc_with_dtlssetup));
2491  cricket::SessionDescription* desc = jdesc_with_dtlssetup.description();
2492  const cricket::TransportInfo* atinfo =
2493      desc->GetTransportInfoByName("audio_content_name");
2494  EXPECT_EQ(cricket::CONNECTIONROLE_ACTPASS,
2495            atinfo->description.connection_role);
2496  const cricket::TransportInfo* vtinfo =
2497        desc->GetTransportInfoByName("video_content_name");
2498  EXPECT_EQ(cricket::CONNECTIONROLE_ACTPASS,
2499            vtinfo->description.connection_role);
2500}
2501
2502// Verifies that the order of the serialized m-lines follows the order of the
2503// ContentInfo in SessionDescription, and vise versa for deserialization.
2504TEST_F(WebRtcSdpTest, MediaContentOrderMaintainedRoundTrip) {
2505  JsepSessionDescription jdesc(kDummyString);
2506  const std::string media_content_sdps[3] = {
2507    kSdpAudioString,
2508    kSdpVideoString,
2509    kSdpSctpDataChannelString
2510  };
2511  const cricket::MediaType media_types[3] = {
2512    cricket::MEDIA_TYPE_AUDIO,
2513    cricket::MEDIA_TYPE_VIDEO,
2514    cricket::MEDIA_TYPE_DATA
2515  };
2516
2517  // Verifies all 6 permutations.
2518  for (size_t i = 0; i < 6; ++i) {
2519    size_t media_content_in_sdp[3];
2520    // The index of the first media content.
2521    media_content_in_sdp[0] = i / 2;
2522    // The index of the second media content.
2523    media_content_in_sdp[1] = (media_content_in_sdp[0] + i % 2 + 1) % 3;
2524    // The index of the third media content.
2525    media_content_in_sdp[2] = (media_content_in_sdp[0] + (i + 1) % 2 + 1) % 3;
2526
2527    std::string sdp_string = kSdpSessionString;
2528    for (size_t i = 0; i < 3; ++i)
2529      sdp_string += media_content_sdps[media_content_in_sdp[i]];
2530
2531    EXPECT_TRUE(SdpDeserialize(sdp_string, &jdesc));
2532    cricket::SessionDescription* desc = jdesc.description();
2533    EXPECT_EQ(3u, desc->contents().size());
2534
2535    for (size_t i = 0; i < 3; ++i) {
2536      const cricket::MediaContentDescription* mdesc =
2537          static_cast<const cricket::MediaContentDescription*>(
2538              desc->contents()[i].description);
2539      EXPECT_EQ(media_types[media_content_in_sdp[i]], mdesc->type());
2540    }
2541
2542    std::string serialized_sdp = webrtc::SdpSerialize(jdesc);
2543    EXPECT_EQ(sdp_string, serialized_sdp);
2544  }
2545}
2546