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/base/gunit.h" 35 #include "talk/base/logging.h" 36 #include "talk/base/messagedigest.h" 37 #include "talk/base/scoped_ptr.h" 38 #include "talk/base/sslfingerprint.h" 39 #include "talk/base/stringencode.h" 40 #include "talk/base/stringutils.h" 41 #include "talk/media/base/constants.h" 42 #include "talk/p2p/base/constants.h" 43 #include "talk/session/media/mediasession.h" 44 45 using cricket::AudioCodec; 46 using cricket::AudioContentDescription; 47 using cricket::Candidate; 48 using cricket::ContentInfo; 49 using cricket::CryptoParams; 50 using cricket::ContentGroup; 51 using cricket::DataCodec; 52 using cricket::DataContentDescription; 53 using cricket::ICE_CANDIDATE_COMPONENT_RTCP; 54 using cricket::ICE_CANDIDATE_COMPONENT_RTP; 55 using cricket::kFecSsrcGroupSemantics; 56 using cricket::LOCAL_PORT_TYPE; 57 using cricket::NS_JINGLE_DRAFT_SCTP; 58 using cricket::NS_JINGLE_ICE_UDP; 59 using cricket::NS_JINGLE_RTP; 60 using cricket::RtpHeaderExtension; 61 using cricket::RELAY_PORT_TYPE; 62 using cricket::SessionDescription; 63 using cricket::StreamParams; 64 using cricket::STUN_PORT_TYPE; 65 using cricket::TransportDescription; 66 using cricket::TransportInfo; 67 using cricket::VideoCodec; 68 using cricket::VideoContentDescription; 69 using webrtc::IceCandidateCollection; 70 using webrtc::IceCandidateInterface; 71 using webrtc::JsepIceCandidate; 72 using webrtc::JsepSessionDescription; 73 using webrtc::SdpParseError; 74 using webrtc::SessionDescriptionInterface; 75 76 typedef std::vector<AudioCodec> AudioCodecs; 77 typedef std::vector<Candidate> Candidates; 78 79 static const uint32 kDefaultSctpPort = 5000; 80 static const char kSessionTime[] = "t=0 0\r\n"; 81 static const uint32 kCandidatePriority = 2130706432U; // pref = 1.0 82 static const char kCandidateUfragVoice[] = "ufrag_voice"; 83 static const char kCandidatePwdVoice[] = "pwd_voice"; 84 static const char kAttributeIcePwdVoice[] = "a=ice-pwd:pwd_voice\r\n"; 85 static const char kCandidateUfragVideo[] = "ufrag_video"; 86 static const char kCandidatePwdVideo[] = "pwd_video"; 87 static const char kCandidateUfragData[] = "ufrag_data"; 88 static const char kCandidatePwdData[] = "pwd_data"; 89 static const char kAttributeIcePwdVideo[] = "a=ice-pwd:pwd_video\r\n"; 90 static const uint32 kCandidateGeneration = 2; 91 static const char kCandidateFoundation1[] = "a0+B/1"; 92 static const char kCandidateFoundation2[] = "a0+B/2"; 93 static const char kCandidateFoundation3[] = "a0+B/3"; 94 static const char kCandidateFoundation4[] = "a0+B/4"; 95 static 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"; 99 static const char kAttributeCryptoVideo[] = 100 "a=crypto:1 AES_CM_128_HMAC_SHA1_80 " 101 "inline:d0RmdmcmVCspeEc3QGZiNWpVLFJhQX1cfHAwJSoj|2^20|1:32\r\n"; 102 static 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"; 104 static const int kExtmapId = 1; 105 static const char kExtmapUri[] = "http://example.com/082005/ext.htm#ttime"; 106 static const char kExtmap[] = 107 "a=extmap:1 http://example.com/082005/ext.htm#ttime\r\n"; 108 static const char kExtmapWithDirectionAndAttribute[] = 109 "a=extmap:1/sendrecv http://example.com/082005/ext.htm#ttime a1 a2\r\n"; 110 111 static 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 117 struct 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 128 static 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. 209 static 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 263 static 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 279 static 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 static const char kSdpSctpDataChannelWithCandidatesString[] = 288 "m=application 2345 DTLS/SCTP 5000\r\n" 289 "c=IN IP4 74.125.127.126\r\n" 290 "a=candidate:a0+B/1 1 udp 2130706432 192.168.1.5 1234 typ host " 291 "generation 2\r\n" 292 "a=candidate:a0+B/2 1 udp 2130706432 ::1 1238 typ host " 293 "generation 2\r\n" 294 "a=candidate:a0+B/3 1 udp 2130706432 74.125.127.126 2345 typ srflx " 295 "raddr 192.168.1.5 rport 2346 " 296 "generation 2\r\n" 297 "a=ice-ufrag:ufrag_data\r\n" 298 "a=ice-pwd:pwd_data\r\n" 299 "a=mid:data_content_name\r\n" 300 "a=sctpmap:5000 webrtc-datachannel 1024\r\n"; 301 302 303 // One candidate reference string as per W3c spec. 304 // candidate:<blah> not a=candidate:<blah>CRLF 305 static const char kRawCandidate[] = 306 "candidate:a0+B/1 1 udp 2130706432 192.168.1.5 1234 typ host generation 2"; 307 // One candidate reference string. 308 static const char kSdpOneCandidate[] = 309 "a=candidate:a0+B/1 1 udp 2130706432 192.168.1.5 1234 typ host " 310 "generation 2\r\n"; 311 312 // One candidate reference string. 313 static const char kSdpOneCandidateOldFormat[] = 314 "a=candidate:a0+B/1 1 udp 2130706432 192.168.1.5 1234 typ host network_name" 315 " eth0 username user_rtp password password_rtp generation 2\r\n"; 316 317 // Session id and version 318 static const char kSessionId[] = "18446744069414584320"; 319 static const char kSessionVersion[] = "18446462598732840960"; 320 321 // Ice options 322 static const char kIceOption1[] = "iceoption1"; 323 static const char kIceOption2[] = "iceoption2"; 324 static const char kIceOption3[] = "iceoption3"; 325 326 // Content name 327 static const char kAudioContentName[] = "audio_content_name"; 328 static const char kVideoContentName[] = "video_content_name"; 329 static const char kDataContentName[] = "data_content_name"; 330 331 // MediaStream 1 332 static const char kStreamLabel1[] = "local_stream_1"; 333 static const char kStream1Cname[] = "stream_1_cname"; 334 static const char kAudioTrackId1[] = "audio_track_id_1"; 335 static const uint32 kAudioTrack1Ssrc = 1; 336 static const char kVideoTrackId1[] = "video_track_id_1"; 337 static const uint32 kVideoTrack1Ssrc = 2; 338 static const char kVideoTrackId2[] = "video_track_id_2"; 339 static const uint32 kVideoTrack2Ssrc = 3; 340 341 // MediaStream 2 342 static const char kStreamLabel2[] = "local_stream_2"; 343 static const char kStream2Cname[] = "stream_2_cname"; 344 static const char kAudioTrackId2[] = "audio_track_id_2"; 345 static const uint32 kAudioTrack2Ssrc = 4; 346 static const char kVideoTrackId3[] = "video_track_id_3"; 347 static const uint32 kVideoTrack3Ssrc = 5; 348 static const uint32 kVideoTrack4Ssrc = 6; 349 350 // DataChannel 351 static const char kDataChannelLabel[] = "data_channel"; 352 static const char kDataChannelMsid[] = "data_channeld0"; 353 static const char kDataChannelCname[] = "data_channel_cname"; 354 static const uint32 kDataChannelSsrc = 10; 355 356 // Candidate 357 static const char kDummyMid[] = "dummy_mid"; 358 static const int kDummyIndex = 123; 359 360 // Misc 361 static const char kDummyString[] = "dummy"; 362 363 // Helper functions 364 365 static bool SdpDeserialize(const std::string& message, 366 JsepSessionDescription* jdesc) { 367 return webrtc::SdpDeserialize(message, jdesc, NULL); 368 } 369 370 static bool SdpDeserializeCandidate(const std::string& message, 371 JsepIceCandidate* candidate) { 372 return webrtc::SdpDeserializeCandidate(message, candidate, NULL); 373 } 374 375 // Add some extra |newlines| to the |message| after |line|. 376 static void InjectAfter(const std::string& line, 377 const std::string& newlines, 378 std::string* message) { 379 const std::string tmp = line + newlines; 380 talk_base::replace_substrs(line.c_str(), line.length(), 381 tmp.c_str(), tmp.length(), message); 382 } 383 384 static void Replace(const std::string& line, 385 const std::string& newlines, 386 std::string* message) { 387 talk_base::replace_substrs(line.c_str(), line.length(), 388 newlines.c_str(), newlines.length(), message); 389 } 390 391 static void ReplaceAndTryToParse(const char* search, const char* replace) { 392 JsepSessionDescription desc(kDummyString); 393 std::string sdp = kSdpFullString; 394 Replace(search, replace, &sdp); 395 SdpParseError error; 396 bool ret = webrtc::SdpDeserialize(sdp, &desc, &error); 397 EXPECT_FALSE(ret); 398 EXPECT_NE(std::string::npos, error.line.find(replace)); 399 } 400 401 static void ReplaceDirection(cricket::MediaContentDirection direction, 402 std::string* message) { 403 std::string new_direction; 404 switch (direction) { 405 case cricket::MD_INACTIVE: 406 new_direction = "a=inactive"; 407 break; 408 case cricket::MD_SENDONLY: 409 new_direction = "a=sendonly"; 410 break; 411 case cricket::MD_RECVONLY: 412 new_direction = "a=recvonly"; 413 break; 414 case cricket::MD_SENDRECV: 415 default: 416 new_direction = "a=sendrecv"; 417 break; 418 } 419 Replace("a=sendrecv", new_direction, message); 420 } 421 422 static void ReplaceRejected(bool audio_rejected, bool video_rejected, 423 std::string* message) { 424 if (audio_rejected) { 425 Replace("m=audio 2345", "m=audio 0", message); 426 } 427 if (video_rejected) { 428 Replace("m=video 3457", "m=video 0", message); 429 } 430 } 431 432 // WebRtcSdpTest 433 434 class WebRtcSdpTest : public testing::Test { 435 public: 436 WebRtcSdpTest() 437 : jdesc_(kDummyString) { 438 // AudioContentDescription 439 audio_desc_ = CreateAudioContentDescription(); 440 AudioCodec opus(111, "opus", 48000, 0, 2, 3); 441 audio_desc_->AddCodec(opus); 442 audio_desc_->AddCodec(AudioCodec(103, "ISAC", 16000, 32000, 1, 2)); 443 audio_desc_->AddCodec(AudioCodec(104, "CELT", 32000, 0, 2, 1)); 444 desc_.AddContent(kAudioContentName, NS_JINGLE_RTP, audio_desc_); 445 446 // VideoContentDescription 447 talk_base::scoped_ptr<VideoContentDescription> video( 448 new VideoContentDescription()); 449 video_desc_ = video.get(); 450 StreamParams video_stream1; 451 video_stream1.id = kVideoTrackId1; 452 video_stream1.cname = kStream1Cname; 453 video_stream1.sync_label = kStreamLabel1; 454 video_stream1.ssrcs.push_back(kVideoTrack1Ssrc); 455 video->AddStream(video_stream1); 456 StreamParams video_stream2; 457 video_stream2.id = kVideoTrackId2; 458 video_stream2.cname = kStream1Cname; 459 video_stream2.sync_label = kStreamLabel1; 460 video_stream2.ssrcs.push_back(kVideoTrack2Ssrc); 461 video->AddStream(video_stream2); 462 StreamParams video_stream3; 463 video_stream3.id = kVideoTrackId3; 464 video_stream3.cname = kStream2Cname; 465 video_stream3.sync_label = kStreamLabel2; 466 video_stream3.ssrcs.push_back(kVideoTrack3Ssrc); 467 video_stream3.ssrcs.push_back(kVideoTrack4Ssrc); 468 cricket::SsrcGroup ssrc_group(kFecSsrcGroupSemantics, video_stream3.ssrcs); 469 video_stream3.ssrc_groups.push_back(ssrc_group); 470 video->AddStream(video_stream3); 471 video->AddCrypto(CryptoParams(1, "AES_CM_128_HMAC_SHA1_80", 472 "inline:d0RmdmcmVCspeEc3QGZiNWpVLFJhQX1cfHAwJSoj|2^20|1:32", "")); 473 video->set_protocol(cricket::kMediaProtocolSavpf); 474 video->AddCodec(VideoCodec( 475 120, 476 JsepSessionDescription::kDefaultVideoCodecName, 477 JsepSessionDescription::kMaxVideoCodecWidth, 478 JsepSessionDescription::kMaxVideoCodecHeight, 479 JsepSessionDescription::kDefaultVideoCodecFramerate, 480 JsepSessionDescription::kDefaultVideoCodecPreference)); 481 482 desc_.AddContent(kVideoContentName, NS_JINGLE_RTP, 483 video.release()); 484 485 // TransportInfo 486 EXPECT_TRUE(desc_.AddTransportInfo( 487 TransportInfo(kAudioContentName, 488 TransportDescription(NS_JINGLE_ICE_UDP, 489 kCandidateUfragVoice, 490 kCandidatePwdVoice)))); 491 EXPECT_TRUE(desc_.AddTransportInfo( 492 TransportInfo(kVideoContentName, 493 TransportDescription(NS_JINGLE_ICE_UDP, 494 kCandidateUfragVideo, 495 kCandidatePwdVideo)))); 496 497 // v4 host 498 int port = 1234; 499 talk_base::SocketAddress address("192.168.1.5", port++); 500 Candidate candidate1( 501 "", ICE_CANDIDATE_COMPONENT_RTP, "udp", address, kCandidatePriority, 502 "", "", LOCAL_PORT_TYPE, 503 "", kCandidateGeneration, kCandidateFoundation1); 504 address.SetPort(port++); 505 Candidate candidate2( 506 "", ICE_CANDIDATE_COMPONENT_RTCP, "udp", address, kCandidatePriority, 507 "", "", LOCAL_PORT_TYPE, 508 "", kCandidateGeneration, kCandidateFoundation1); 509 address.SetPort(port++); 510 Candidate candidate3( 511 "", ICE_CANDIDATE_COMPONENT_RTCP, "udp", address, kCandidatePriority, 512 "", "", LOCAL_PORT_TYPE, 513 "", kCandidateGeneration, kCandidateFoundation1); 514 address.SetPort(port++); 515 Candidate candidate4( 516 "", ICE_CANDIDATE_COMPONENT_RTP, "udp", address, kCandidatePriority, 517 "", "", LOCAL_PORT_TYPE, 518 "", kCandidateGeneration, kCandidateFoundation1); 519 520 // v6 host 521 talk_base::SocketAddress v6_address("::1", port++); 522 cricket::Candidate candidate5( 523 "", cricket::ICE_CANDIDATE_COMPONENT_RTP, 524 "udp", v6_address, kCandidatePriority, 525 "", "", cricket::LOCAL_PORT_TYPE, 526 "", kCandidateGeneration, kCandidateFoundation2); 527 v6_address.SetPort(port++); 528 cricket::Candidate candidate6( 529 "", cricket::ICE_CANDIDATE_COMPONENT_RTCP, 530 "udp", v6_address, kCandidatePriority, 531 "", "", cricket::LOCAL_PORT_TYPE, 532 "", kCandidateGeneration, kCandidateFoundation2); 533 v6_address.SetPort(port++); 534 cricket::Candidate candidate7( 535 "", cricket::ICE_CANDIDATE_COMPONENT_RTCP, 536 "udp", v6_address, kCandidatePriority, 537 "", "", cricket::LOCAL_PORT_TYPE, 538 "", kCandidateGeneration, kCandidateFoundation2); 539 v6_address.SetPort(port++); 540 cricket::Candidate candidate8( 541 "", cricket::ICE_CANDIDATE_COMPONENT_RTP, 542 "udp", v6_address, kCandidatePriority, 543 "", "", cricket::LOCAL_PORT_TYPE, 544 "", kCandidateGeneration, kCandidateFoundation2); 545 546 // stun 547 int port_stun = 2345; 548 talk_base::SocketAddress address_stun("74.125.127.126", port_stun++); 549 talk_base::SocketAddress rel_address_stun("192.168.1.5", port_stun++); 550 cricket::Candidate candidate9 551 ("", cricket::ICE_CANDIDATE_COMPONENT_RTP, 552 "udp", address_stun, kCandidatePriority, 553 "", "", STUN_PORT_TYPE, 554 "", kCandidateGeneration, kCandidateFoundation3); 555 candidate9.set_related_address(rel_address_stun); 556 557 address_stun.SetPort(port_stun++); 558 rel_address_stun.SetPort(port_stun++); 559 cricket::Candidate candidate10( 560 "", cricket::ICE_CANDIDATE_COMPONENT_RTCP, 561 "udp", address_stun, kCandidatePriority, 562 "", "", STUN_PORT_TYPE, 563 "", kCandidateGeneration, kCandidateFoundation3); 564 candidate10.set_related_address(rel_address_stun); 565 566 // relay 567 int port_relay = 3456; 568 talk_base::SocketAddress address_relay("74.125.224.39", port_relay++); 569 cricket::Candidate candidate11( 570 "", cricket::ICE_CANDIDATE_COMPONENT_RTCP, 571 "udp", address_relay, kCandidatePriority, 572 "", "", 573 cricket::RELAY_PORT_TYPE, "", 574 kCandidateGeneration, kCandidateFoundation4); 575 address_relay.SetPort(port_relay++); 576 cricket::Candidate candidate12( 577 "", cricket::ICE_CANDIDATE_COMPONENT_RTP, 578 "udp", address_relay, kCandidatePriority, 579 "", "", 580 RELAY_PORT_TYPE, "", 581 kCandidateGeneration, kCandidateFoundation4); 582 583 // voice 584 candidates_.push_back(candidate1); 585 candidates_.push_back(candidate2); 586 candidates_.push_back(candidate5); 587 candidates_.push_back(candidate6); 588 candidates_.push_back(candidate9); 589 candidates_.push_back(candidate10); 590 591 // video 592 candidates_.push_back(candidate3); 593 candidates_.push_back(candidate4); 594 candidates_.push_back(candidate7); 595 candidates_.push_back(candidate8); 596 candidates_.push_back(candidate11); 597 candidates_.push_back(candidate12); 598 599 jcandidate_.reset(new JsepIceCandidate(std::string("audio_content_name"), 600 0, candidate1)); 601 602 // Set up JsepSessionDescription. 603 jdesc_.Initialize(desc_.Copy(), kSessionId, kSessionVersion); 604 std::string mline_id; 605 int mline_index = 0; 606 for (size_t i = 0; i< candidates_.size(); ++i) { 607 // In this test, the audio m line index will be 0, and the video m line 608 // will be 1. 609 bool is_video = (i > 5); 610 mline_id = is_video ? "video_content_name" : "audio_content_name"; 611 mline_index = is_video ? 1 : 0; 612 JsepIceCandidate jice(mline_id, 613 mline_index, 614 candidates_.at(i)); 615 jdesc_.AddCandidate(&jice); 616 } 617 } 618 619 AudioContentDescription* CreateAudioContentDescription() { 620 AudioContentDescription* audio = new AudioContentDescription(); 621 audio->set_rtcp_mux(true); 622 StreamParams audio_stream1; 623 audio_stream1.id = kAudioTrackId1; 624 audio_stream1.cname = kStream1Cname; 625 audio_stream1.sync_label = kStreamLabel1; 626 audio_stream1.ssrcs.push_back(kAudioTrack1Ssrc); 627 audio->AddStream(audio_stream1); 628 StreamParams audio_stream2; 629 audio_stream2.id = kAudioTrackId2; 630 audio_stream2.cname = kStream2Cname; 631 audio_stream2.sync_label = kStreamLabel2; 632 audio_stream2.ssrcs.push_back(kAudioTrack2Ssrc); 633 audio->AddStream(audio_stream2); 634 audio->AddCrypto(CryptoParams(1, "AES_CM_128_HMAC_SHA1_32", 635 "inline:NzB4d1BINUAvLEw6UzF3WSJ+PSdFcGdUJShpX1Zj|2^20|1:32", 636 "dummy_session_params")); 637 audio->set_protocol(cricket::kMediaProtocolSavpf); 638 return audio; 639 } 640 641 template <class MCD> 642 void CompareMediaContentDescription(const MCD* cd1, 643 const MCD* cd2) { 644 // type 645 EXPECT_EQ(cd1->type(), cd1->type()); 646 647 // content direction 648 EXPECT_EQ(cd1->direction(), cd2->direction()); 649 650 // rtcp_mux 651 EXPECT_EQ(cd1->rtcp_mux(), cd2->rtcp_mux()); 652 653 // cryptos 654 EXPECT_EQ(cd1->cryptos().size(), cd2->cryptos().size()); 655 if (cd1->cryptos().size() != cd2->cryptos().size()) { 656 ADD_FAILURE(); 657 return; 658 } 659 for (size_t i = 0; i< cd1->cryptos().size(); ++i) { 660 const CryptoParams c1 = cd1->cryptos().at(i); 661 const CryptoParams c2 = cd2->cryptos().at(i); 662 EXPECT_TRUE(c1.Matches(c2)); 663 EXPECT_EQ(c1.key_params, c2.key_params); 664 EXPECT_EQ(c1.session_params, c2.session_params); 665 } 666 // protocol 667 EXPECT_EQ(cd1->protocol(), cd2->protocol()); 668 669 // codecs 670 EXPECT_EQ(cd1->codecs(), cd2->codecs()); 671 672 // bandwidth 673 EXPECT_EQ(cd1->bandwidth(), cd2->bandwidth()); 674 675 // streams 676 EXPECT_EQ(cd1->streams(), cd2->streams()); 677 678 // extmap 679 ASSERT_EQ(cd1->rtp_header_extensions().size(), 680 cd2->rtp_header_extensions().size()); 681 for (size_t i = 0; i< cd1->rtp_header_extensions().size(); ++i) { 682 const RtpHeaderExtension ext1 = cd1->rtp_header_extensions().at(i); 683 const RtpHeaderExtension ext2 = cd2->rtp_header_extensions().at(i); 684 EXPECT_EQ(ext1.uri, ext2.uri); 685 EXPECT_EQ(ext1.id, ext2.id); 686 } 687 688 // buffered mode latency 689 EXPECT_EQ(cd1->buffered_mode_latency(), cd2->buffered_mode_latency()); 690 } 691 692 693 void CompareSessionDescription(const SessionDescription& desc1, 694 const SessionDescription& desc2) { 695 // Compare content descriptions. 696 if (desc1.contents().size() != desc2.contents().size()) { 697 ADD_FAILURE(); 698 return; 699 } 700 for (size_t i = 0 ; i < desc1.contents().size(); ++i) { 701 const cricket::ContentInfo& c1 = desc1.contents().at(i); 702 const cricket::ContentInfo& c2 = desc2.contents().at(i); 703 // content name 704 EXPECT_EQ(c1.name, c2.name); 705 // content type 706 // Note, ASSERT will return from the function, but will not stop the test. 707 ASSERT_EQ(c1.type, c2.type); 708 709 ASSERT_EQ(IsAudioContent(&c1), IsAudioContent(&c2)); 710 if (IsAudioContent(&c1)) { 711 const AudioContentDescription* acd1 = 712 static_cast<const AudioContentDescription*>(c1.description); 713 const AudioContentDescription* acd2 = 714 static_cast<const AudioContentDescription*>(c2.description); 715 CompareMediaContentDescription<AudioContentDescription>(acd1, acd2); 716 } 717 718 ASSERT_EQ(IsVideoContent(&c1), IsVideoContent(&c2)); 719 if (IsVideoContent(&c1)) { 720 const VideoContentDescription* vcd1 = 721 static_cast<const VideoContentDescription*>(c1.description); 722 const VideoContentDescription* vcd2 = 723 static_cast<const VideoContentDescription*>(c2.description); 724 CompareMediaContentDescription<VideoContentDescription>(vcd1, vcd2); 725 } 726 727 ASSERT_EQ(IsDataContent(&c1), IsDataContent(&c2)); 728 if (IsDataContent(&c1)) { 729 const DataContentDescription* dcd1 = 730 static_cast<const DataContentDescription*>(c1.description); 731 const DataContentDescription* dcd2 = 732 static_cast<const DataContentDescription*>(c2.description); 733 CompareMediaContentDescription<DataContentDescription>(dcd1, dcd2); 734 } 735 } 736 737 // group 738 const cricket::ContentGroups groups1 = desc1.groups(); 739 const cricket::ContentGroups groups2 = desc2.groups(); 740 EXPECT_EQ(groups1.size(), groups1.size()); 741 if (groups1.size() != groups2.size()) { 742 ADD_FAILURE(); 743 return; 744 } 745 for (size_t i = 0; i < groups1.size(); ++i) { 746 const cricket::ContentGroup group1 = groups1.at(i); 747 const cricket::ContentGroup group2 = groups2.at(i); 748 EXPECT_EQ(group1.semantics(), group2.semantics()); 749 const cricket::ContentNames names1 = group1.content_names(); 750 const cricket::ContentNames names2 = group2.content_names(); 751 EXPECT_EQ(names1.size(), names2.size()); 752 if (names1.size() != names2.size()) { 753 ADD_FAILURE(); 754 return; 755 } 756 cricket::ContentNames::const_iterator iter1 = names1.begin(); 757 cricket::ContentNames::const_iterator iter2 = names2.begin(); 758 while (iter1 != names1.end()) { 759 EXPECT_EQ(*iter1++, *iter2++); 760 } 761 } 762 763 // transport info 764 const cricket::TransportInfos transports1 = desc1.transport_infos(); 765 const cricket::TransportInfos transports2 = desc2.transport_infos(); 766 EXPECT_EQ(transports1.size(), transports2.size()); 767 if (transports1.size() != transports2.size()) { 768 ADD_FAILURE(); 769 return; 770 } 771 for (size_t i = 0; i < transports1.size(); ++i) { 772 const cricket::TransportInfo transport1 = transports1.at(i); 773 const cricket::TransportInfo transport2 = transports2.at(i); 774 EXPECT_EQ(transport1.content_name, transport2.content_name); 775 EXPECT_EQ(transport1.description.transport_type, 776 transport2.description.transport_type); 777 EXPECT_EQ(transport1.description.ice_ufrag, 778 transport2.description.ice_ufrag); 779 EXPECT_EQ(transport1.description.ice_pwd, 780 transport2.description.ice_pwd); 781 if (transport1.description.identity_fingerprint) { 782 EXPECT_EQ(*transport1.description.identity_fingerprint, 783 *transport2.description.identity_fingerprint); 784 } else { 785 EXPECT_EQ(transport1.description.identity_fingerprint.get(), 786 transport2.description.identity_fingerprint.get()); 787 } 788 EXPECT_EQ(transport1.description.transport_options, 789 transport2.description.transport_options); 790 EXPECT_TRUE(CompareCandidates(transport1.description.candidates, 791 transport2.description.candidates)); 792 } 793 } 794 795 bool CompareCandidates(const Candidates& cs1, const Candidates& cs2) { 796 EXPECT_EQ(cs1.size(), cs2.size()); 797 if (cs1.size() != cs2.size()) 798 return false; 799 for (size_t i = 0; i< cs1.size(); ++i) { 800 const Candidate c1 = cs1.at(i); 801 const Candidate c2 = cs2.at(i); 802 EXPECT_TRUE(c1.IsEquivalent(c2)); 803 } 804 return true; 805 } 806 807 bool CompareSessionDescription( 808 const JsepSessionDescription& desc1, 809 const JsepSessionDescription& desc2) { 810 EXPECT_EQ(desc1.session_id(), desc2.session_id()); 811 EXPECT_EQ(desc1.session_version(), desc2.session_version()); 812 CompareSessionDescription(*desc1.description(), *desc2.description()); 813 if (desc1.number_of_mediasections() != desc2.number_of_mediasections()) 814 return false; 815 for (size_t i = 0; i < desc1.number_of_mediasections(); ++i) { 816 const IceCandidateCollection* cc1 = desc1.candidates(i); 817 const IceCandidateCollection* cc2 = desc2.candidates(i); 818 if (cc1->count() != cc2->count()) 819 return false; 820 for (size_t j = 0; j < cc1->count(); ++j) { 821 const IceCandidateInterface* c1 = cc1->at(j); 822 const IceCandidateInterface* c2 = cc2->at(j); 823 EXPECT_EQ(c1->sdp_mid(), c2->sdp_mid()); 824 EXPECT_EQ(c1->sdp_mline_index(), c2->sdp_mline_index()); 825 EXPECT_TRUE(c1->candidate().IsEquivalent(c2->candidate())); 826 } 827 } 828 return true; 829 } 830 831 // Disable the ice-ufrag and ice-pwd in given |sdp| message by replacing 832 // them with invalid keywords so that the parser will just ignore them. 833 bool RemoveCandidateUfragPwd(std::string* sdp) { 834 const char ice_ufrag[] = "a=ice-ufrag"; 835 const char ice_ufragx[] = "a=xice-ufrag"; 836 const char ice_pwd[] = "a=ice-pwd"; 837 const char ice_pwdx[] = "a=xice-pwd"; 838 talk_base::replace_substrs(ice_ufrag, strlen(ice_ufrag), 839 ice_ufragx, strlen(ice_ufragx), sdp); 840 talk_base::replace_substrs(ice_pwd, strlen(ice_pwd), 841 ice_pwdx, strlen(ice_pwdx), sdp); 842 return true; 843 } 844 845 // Update the candidates in |jdesc| to use the given |ufrag| and |pwd|. 846 bool UpdateCandidateUfragPwd(JsepSessionDescription* jdesc, int mline_index, 847 const std::string& ufrag, const std::string& pwd) { 848 std::string content_name; 849 if (mline_index == 0) { 850 content_name = kAudioContentName; 851 } else if (mline_index == 1) { 852 content_name = kVideoContentName; 853 } else { 854 ASSERT(false); 855 } 856 TransportInfo transport_info( 857 content_name, TransportDescription(NS_JINGLE_ICE_UDP, 858 ufrag, pwd)); 859 SessionDescription* desc = 860 const_cast<SessionDescription*>(jdesc->description()); 861 desc->RemoveTransportInfoByName(content_name); 862 EXPECT_TRUE(desc->AddTransportInfo(transport_info)); 863 for (size_t i = 0; i < jdesc_.number_of_mediasections(); ++i) { 864 const IceCandidateCollection* cc = jdesc_.candidates(i); 865 for (size_t j = 0; j < cc->count(); ++j) { 866 if (cc->at(j)->sdp_mline_index() == mline_index) { 867 const_cast<Candidate&>(cc->at(j)->candidate()).set_username( 868 ufrag); 869 const_cast<Candidate&>(cc->at(j)->candidate()).set_password( 870 pwd); 871 } 872 } 873 } 874 return true; 875 } 876 877 void AddIceOptions(const std::string& content_name, 878 const std::vector<std::string>& transport_options) { 879 ASSERT_TRUE(desc_.GetTransportInfoByName(content_name) != NULL); 880 cricket::TransportInfo transport_info = 881 *(desc_.GetTransportInfoByName(content_name)); 882 desc_.RemoveTransportInfoByName(content_name); 883 transport_info.description.transport_options = transport_options; 884 desc_.AddTransportInfo(transport_info); 885 } 886 887 void AddFingerprint() { 888 desc_.RemoveTransportInfoByName(kAudioContentName); 889 desc_.RemoveTransportInfoByName(kVideoContentName); 890 talk_base::SSLFingerprint fingerprint(talk_base::DIGEST_SHA_1, 891 kIdentityDigest, 892 sizeof(kIdentityDigest)); 893 EXPECT_TRUE(desc_.AddTransportInfo( 894 TransportInfo(kAudioContentName, 895 TransportDescription(NS_JINGLE_ICE_UDP, 896 std::vector<std::string>(), 897 kCandidateUfragVoice, 898 kCandidatePwdVoice, 899 cricket::ICEMODE_FULL, 900 cricket::CONNECTIONROLE_NONE, 901 &fingerprint, Candidates())))); 902 EXPECT_TRUE(desc_.AddTransportInfo( 903 TransportInfo(kVideoContentName, 904 TransportDescription(NS_JINGLE_ICE_UDP, 905 std::vector<std::string>(), 906 kCandidateUfragVideo, 907 kCandidatePwdVideo, 908 cricket::ICEMODE_FULL, 909 cricket::CONNECTIONROLE_NONE, 910 &fingerprint, Candidates())))); 911 } 912 913 void AddExtmap() { 914 audio_desc_ = static_cast<AudioContentDescription*>( 915 audio_desc_->Copy()); 916 video_desc_ = static_cast<VideoContentDescription*>( 917 video_desc_->Copy()); 918 audio_desc_->AddRtpHeaderExtension( 919 RtpHeaderExtension(kExtmapUri, kExtmapId)); 920 video_desc_->AddRtpHeaderExtension( 921 RtpHeaderExtension(kExtmapUri, kExtmapId)); 922 desc_.RemoveContentByName(kAudioContentName); 923 desc_.RemoveContentByName(kVideoContentName); 924 desc_.AddContent(kAudioContentName, NS_JINGLE_RTP, audio_desc_); 925 desc_.AddContent(kVideoContentName, NS_JINGLE_RTP, video_desc_); 926 } 927 928 void RemoveCryptos() { 929 audio_desc_->set_cryptos(std::vector<CryptoParams>()); 930 video_desc_->set_cryptos(std::vector<CryptoParams>()); 931 } 932 933 bool TestSerializeDirection(cricket::MediaContentDirection direction) { 934 audio_desc_->set_direction(direction); 935 video_desc_->set_direction(direction); 936 std::string new_sdp = kSdpFullString; 937 ReplaceDirection(direction, &new_sdp); 938 939 if (!jdesc_.Initialize(desc_.Copy(), 940 jdesc_.session_id(), 941 jdesc_.session_version())) { 942 return false; 943 } 944 std::string message = webrtc::SdpSerialize(jdesc_); 945 EXPECT_EQ(new_sdp, message); 946 return true; 947 } 948 949 bool TestSerializeRejected(bool audio_rejected, bool video_rejected) { 950 audio_desc_ = static_cast<AudioContentDescription*>( 951 audio_desc_->Copy()); 952 video_desc_ = static_cast<VideoContentDescription*>( 953 video_desc_->Copy()); 954 desc_.RemoveContentByName(kAudioContentName); 955 desc_.RemoveContentByName(kVideoContentName); 956 desc_.AddContent(kAudioContentName, NS_JINGLE_RTP, audio_rejected, 957 audio_desc_); 958 desc_.AddContent(kVideoContentName, NS_JINGLE_RTP, video_rejected, 959 video_desc_); 960 std::string new_sdp = kSdpFullString; 961 ReplaceRejected(audio_rejected, video_rejected, &new_sdp); 962 963 if (!jdesc_.Initialize(desc_.Copy(), 964 jdesc_.session_id(), 965 jdesc_.session_version())) { 966 return false; 967 } 968 std::string message = webrtc::SdpSerialize(jdesc_); 969 EXPECT_EQ(new_sdp, message); 970 return true; 971 } 972 973 void AddSctpDataChannel() { 974 talk_base::scoped_ptr<DataContentDescription> data( 975 new DataContentDescription()); 976 data_desc_ = data.get(); 977 data_desc_->set_protocol(cricket::kMediaProtocolDtlsSctp); 978 DataCodec codec(cricket::kGoogleSctpDataCodecId, 979 cricket::kGoogleSctpDataCodecName, 0); 980 codec.SetParam(cricket::kCodecParamPort, kDefaultSctpPort); 981 data_desc_->AddCodec(codec); 982 desc_.AddContent(kDataContentName, NS_JINGLE_DRAFT_SCTP, data.release()); 983 EXPECT_TRUE(desc_.AddTransportInfo( 984 TransportInfo(kDataContentName, 985 TransportDescription(NS_JINGLE_ICE_UDP, 986 kCandidateUfragData, 987 kCandidatePwdData)))); 988 } 989 990 void AddRtpDataChannel() { 991 talk_base::scoped_ptr<DataContentDescription> data( 992 new DataContentDescription()); 993 data_desc_ = data.get(); 994 995 data_desc_->AddCodec(DataCodec(101, "google-data", 1)); 996 StreamParams data_stream; 997 data_stream.id = kDataChannelMsid; 998 data_stream.cname = kDataChannelCname; 999 data_stream.sync_label = kDataChannelLabel; 1000 data_stream.ssrcs.push_back(kDataChannelSsrc); 1001 data_desc_->AddStream(data_stream); 1002 data_desc_->AddCrypto(CryptoParams( 1003 1, "AES_CM_128_HMAC_SHA1_80", 1004 "inline:FvLcvU2P3ZWmQxgPAgcDu7Zl9vftYElFOjEzhWs5", "")); 1005 data_desc_->set_protocol(cricket::kMediaProtocolSavpf); 1006 desc_.AddContent(kDataContentName, NS_JINGLE_RTP, data.release()); 1007 EXPECT_TRUE(desc_.AddTransportInfo( 1008 TransportInfo(kDataContentName, 1009 TransportDescription(NS_JINGLE_ICE_UDP, 1010 kCandidateUfragData, 1011 kCandidatePwdData)))); 1012 } 1013 1014 bool TestDeserializeDirection(cricket::MediaContentDirection direction) { 1015 std::string new_sdp = kSdpFullString; 1016 ReplaceDirection(direction, &new_sdp); 1017 JsepSessionDescription new_jdesc(kDummyString); 1018 1019 EXPECT_TRUE(SdpDeserialize(new_sdp, &new_jdesc)); 1020 1021 audio_desc_->set_direction(direction); 1022 video_desc_->set_direction(direction); 1023 if (!jdesc_.Initialize(desc_.Copy(), 1024 jdesc_.session_id(), 1025 jdesc_.session_version())) { 1026 return false; 1027 } 1028 EXPECT_TRUE(CompareSessionDescription(jdesc_, new_jdesc)); 1029 return true; 1030 } 1031 1032 bool TestDeserializeRejected(bool audio_rejected, bool video_rejected) { 1033 std::string new_sdp = kSdpFullString; 1034 ReplaceRejected(audio_rejected, video_rejected, &new_sdp); 1035 JsepSessionDescription new_jdesc(JsepSessionDescription::kOffer); 1036 1037 EXPECT_TRUE(SdpDeserialize(new_sdp, &new_jdesc)); 1038 audio_desc_ = static_cast<AudioContentDescription*>( 1039 audio_desc_->Copy()); 1040 video_desc_ = static_cast<VideoContentDescription*>( 1041 video_desc_->Copy()); 1042 desc_.RemoveContentByName(kAudioContentName); 1043 desc_.RemoveContentByName(kVideoContentName); 1044 desc_.AddContent(kAudioContentName, NS_JINGLE_RTP, audio_rejected, 1045 audio_desc_); 1046 desc_.AddContent(kVideoContentName, NS_JINGLE_RTP, video_rejected, 1047 video_desc_); 1048 if (!jdesc_.Initialize(desc_.Copy(), 1049 jdesc_.session_id(), 1050 jdesc_.session_version())) { 1051 return false; 1052 } 1053 EXPECT_TRUE(CompareSessionDescription(jdesc_, new_jdesc)); 1054 return true; 1055 } 1056 1057 void TestDeserializeExtmap(bool session_level, bool media_level) { 1058 AddExtmap(); 1059 JsepSessionDescription new_jdesc("dummy"); 1060 ASSERT_TRUE(new_jdesc.Initialize(desc_.Copy(), 1061 jdesc_.session_id(), 1062 jdesc_.session_version())); 1063 JsepSessionDescription jdesc_with_extmap("dummy"); 1064 std::string sdp_with_extmap = kSdpString; 1065 if (session_level) { 1066 InjectAfter(kSessionTime, kExtmapWithDirectionAndAttribute, 1067 &sdp_with_extmap); 1068 } 1069 if (media_level) { 1070 InjectAfter(kAttributeIcePwdVoice, kExtmapWithDirectionAndAttribute, 1071 &sdp_with_extmap); 1072 InjectAfter(kAttributeIcePwdVideo, kExtmapWithDirectionAndAttribute, 1073 &sdp_with_extmap); 1074 } 1075 // The extmap can't be present at the same time in both session level and 1076 // media level. 1077 if (session_level && media_level) { 1078 SdpParseError error; 1079 EXPECT_FALSE(webrtc::SdpDeserialize(sdp_with_extmap, 1080 &jdesc_with_extmap, &error)); 1081 EXPECT_NE(std::string::npos, error.description.find("a=extmap")); 1082 } else { 1083 EXPECT_TRUE(SdpDeserialize(sdp_with_extmap, &jdesc_with_extmap)); 1084 EXPECT_TRUE(CompareSessionDescription(jdesc_with_extmap, new_jdesc)); 1085 } 1086 } 1087 1088 void VerifyCodecParameter(const cricket::CodecParameterMap& params, 1089 const std::string& name, int expected_value) { 1090 cricket::CodecParameterMap::const_iterator found = params.find(name); 1091 ASSERT_TRUE(found != params.end()); 1092 EXPECT_EQ(found->second, talk_base::ToString<int>(expected_value)); 1093 } 1094 1095 void TestDeserializeCodecParams(const CodecParams& params, 1096 JsepSessionDescription* jdesc_output) { 1097 std::string sdp = 1098 "v=0\r\n" 1099 "o=- 18446744069414584320 18446462598732840960 IN IP4 127.0.0.1\r\n" 1100 "s=-\r\n" 1101 "t=0 0\r\n" 1102 // Include semantics for WebRTC Media Streams since it is supported by 1103 // this parser, and will be added to the SDP when serializing a session 1104 // description. 1105 "a=msid-semantic: WMS\r\n" 1106 // Pl type 111 preferred. 1107 "m=audio 1 RTP/SAVPF 111 104 103 102\r\n" 1108 // Pltype 111 listed before 103 and 104 in the map. 1109 "a=rtpmap:111 opus/48000/2\r\n" 1110 // Pltype 103 listed before 104. 1111 "a=rtpmap:103 ISAC/16000\r\n" 1112 "a=rtpmap:104 CELT/32000/2\r\n" 1113 "a=rtpmap:102 ISAC/32000/1\r\n" 1114 "a=fmtp:111 0-15,66,70\r\n" 1115 "a=fmtp:111 "; 1116 std::ostringstream os; 1117 os << "minptime=" << params.min_ptime 1118 << "; stereo=" << params.stereo 1119 << "; sprop-stereo=" << params.sprop_stereo 1120 << "; useinbandfec=" << params.useinband 1121 << " maxaveragebitrate=" << params.maxaveragebitrate << "\r\n" 1122 << "a=ptime:" << params.ptime << "\r\n" 1123 << "a=maxptime:" << params.max_ptime << "\r\n"; 1124 sdp += os.str(); 1125 1126 // Deserialize 1127 SdpParseError error; 1128 EXPECT_TRUE(webrtc::SdpDeserialize(sdp, jdesc_output, &error)); 1129 1130 const ContentInfo* ac = GetFirstAudioContent(jdesc_output->description()); 1131 ASSERT_TRUE(ac != NULL); 1132 const AudioContentDescription* acd = 1133 static_cast<const AudioContentDescription*>(ac->description); 1134 ASSERT_FALSE(acd->codecs().empty()); 1135 cricket::AudioCodec opus = acd->codecs()[0]; 1136 EXPECT_EQ("opus", opus.name); 1137 EXPECT_EQ(111, opus.id); 1138 VerifyCodecParameter(opus.params, "minptime", params.min_ptime); 1139 VerifyCodecParameter(opus.params, "stereo", params.stereo); 1140 VerifyCodecParameter(opus.params, "sprop-stereo", params.sprop_stereo); 1141 VerifyCodecParameter(opus.params, "useinbandfec", params.useinband); 1142 VerifyCodecParameter(opus.params, "maxaveragebitrate", 1143 params.maxaveragebitrate); 1144 for (size_t i = 0; i < acd->codecs().size(); ++i) { 1145 cricket::AudioCodec codec = acd->codecs()[i]; 1146 VerifyCodecParameter(codec.params, "ptime", params.ptime); 1147 VerifyCodecParameter(codec.params, "maxptime", params.max_ptime); 1148 if (codec.name == "ISAC") { 1149 if (codec.clockrate == 16000) { 1150 EXPECT_EQ(32000, codec.bitrate); 1151 } else { 1152 EXPECT_EQ(56000, codec.bitrate); 1153 } 1154 } 1155 } 1156 } 1157 1158 void TestDeserializeRtcpFb(JsepSessionDescription* jdesc_output, 1159 bool use_wildcard) { 1160 std::string sdp = 1161 "v=0\r\n" 1162 "o=- 18446744069414584320 18446462598732840960 IN IP4 127.0.0.1\r\n" 1163 "s=-\r\n" 1164 "t=0 0\r\n" 1165 // Include semantics for WebRTC Media Streams since it is supported by 1166 // this parser, and will be added to the SDP when serializing a session 1167 // description. 1168 "a=msid-semantic: WMS\r\n" 1169 "m=audio 1 RTP/SAVPF 111\r\n" 1170 "a=rtpmap:111 opus/48000/2\r\n" 1171 "a=rtcp-fb:111 nack\r\n" 1172 "m=video 3457 RTP/SAVPF 101\r\n" 1173 "a=rtpmap:101 VP8/90000\r\n" 1174 "a=rtcp-fb:101 nack\r\n" 1175 "a=rtcp-fb:101 goog-remb\r\n" 1176 "a=rtcp-fb:101 ccm fir\r\n"; 1177 std::ostringstream os; 1178 os << "a=rtcp-fb:" << (use_wildcard ? "*" : "101") << " ccm fir\r\n"; 1179 sdp += os.str(); 1180 // Deserialize 1181 SdpParseError error; 1182 EXPECT_TRUE(webrtc::SdpDeserialize(sdp, jdesc_output, &error)); 1183 const ContentInfo* ac = GetFirstAudioContent(jdesc_output->description()); 1184 ASSERT_TRUE(ac != NULL); 1185 const AudioContentDescription* acd = 1186 static_cast<const AudioContentDescription*>(ac->description); 1187 ASSERT_FALSE(acd->codecs().empty()); 1188 cricket::AudioCodec opus = acd->codecs()[0]; 1189 EXPECT_EQ(111, opus.id); 1190 EXPECT_TRUE(opus.HasFeedbackParam( 1191 cricket::FeedbackParam(cricket::kRtcpFbParamNack, 1192 cricket::kParamValueEmpty))); 1193 1194 const ContentInfo* vc = GetFirstVideoContent(jdesc_output->description()); 1195 ASSERT_TRUE(vc != NULL); 1196 const VideoContentDescription* vcd = 1197 static_cast<const VideoContentDescription*>(vc->description); 1198 ASSERT_FALSE(vcd->codecs().empty()); 1199 cricket::VideoCodec vp8 = vcd->codecs()[0]; 1200 EXPECT_STREQ(webrtc::JsepSessionDescription::kDefaultVideoCodecName, 1201 vp8.name.c_str()); 1202 EXPECT_EQ(101, vp8.id); 1203 EXPECT_TRUE(vp8.HasFeedbackParam( 1204 cricket::FeedbackParam(cricket::kRtcpFbParamNack, 1205 cricket::kParamValueEmpty))); 1206 EXPECT_TRUE(vp8.HasFeedbackParam( 1207 cricket::FeedbackParam(cricket::kRtcpFbParamRemb, 1208 cricket::kParamValueEmpty))); 1209 EXPECT_TRUE(vp8.HasFeedbackParam( 1210 cricket::FeedbackParam(cricket::kRtcpFbParamCcm, 1211 cricket::kRtcpFbCcmParamFir))); 1212 } 1213 1214 // Two SDP messages can mean the same thing but be different strings, e.g. 1215 // some of the lines can be serialized in different order. 1216 // However, a deserialized description can be compared field by field and has 1217 // no order. If deserializer has already been tested, serializing then 1218 // deserializing and comparing JsepSessionDescription will test 1219 // the serializer sufficiently. 1220 void TestSerialize(const JsepSessionDescription& jdesc) { 1221 std::string message = webrtc::SdpSerialize(jdesc); 1222 JsepSessionDescription jdesc_output_des(kDummyString); 1223 SdpParseError error; 1224 EXPECT_TRUE(webrtc::SdpDeserialize(message, &jdesc_output_des, &error)); 1225 EXPECT_TRUE(CompareSessionDescription(jdesc, jdesc_output_des)); 1226 } 1227 1228 protected: 1229 SessionDescription desc_; 1230 AudioContentDescription* audio_desc_; 1231 VideoContentDescription* video_desc_; 1232 DataContentDescription* data_desc_; 1233 Candidates candidates_; 1234 talk_base::scoped_ptr<IceCandidateInterface> jcandidate_; 1235 JsepSessionDescription jdesc_; 1236 }; 1237 1238 void TestMismatch(const std::string& string1, const std::string& string2) { 1239 int position = 0; 1240 for (size_t i = 0; i < string1.length() && i < string2.length(); ++i) { 1241 if (string1.c_str()[i] != string2.c_str()[i]) { 1242 position = static_cast<int>(i); 1243 break; 1244 } 1245 } 1246 EXPECT_EQ(0, position) << "Strings mismatch at the " << position 1247 << " character\n" 1248 << " 1: " << string1.substr(position, 20) << "\n" 1249 << " 2: " << string2.substr(position, 20) << "\n"; 1250 } 1251 1252 std::string GetLine(const std::string& message, 1253 const std::string& session_description_name) { 1254 size_t start = message.find(session_description_name); 1255 if (std::string::npos == start) { 1256 return ""; 1257 } 1258 size_t stop = message.find("\r\n", start); 1259 if (std::string::npos == stop) { 1260 return ""; 1261 } 1262 if (stop <= start) { 1263 return ""; 1264 } 1265 return message.substr(start, stop - start); 1266 } 1267 1268 TEST_F(WebRtcSdpTest, SerializeSessionDescription) { 1269 // SessionDescription with desc and candidates. 1270 std::string message = webrtc::SdpSerialize(jdesc_); 1271 TestMismatch(std::string(kSdpFullString), message); 1272 } 1273 1274 TEST_F(WebRtcSdpTest, SerializeSessionDescriptionEmpty) { 1275 JsepSessionDescription jdesc_empty(kDummyString); 1276 EXPECT_EQ("", webrtc::SdpSerialize(jdesc_empty)); 1277 } 1278 1279 // This tests serialization of SDP with a=crypto and a=fingerprint, as would be 1280 // the case in a DTLS offer. 1281 TEST_F(WebRtcSdpTest, SerializeSessionDescriptionWithFingerprint) { 1282 AddFingerprint(); 1283 JsepSessionDescription jdesc_with_fingerprint(kDummyString); 1284 ASSERT_TRUE(jdesc_with_fingerprint.Initialize(desc_.Copy(), 1285 kSessionId, kSessionVersion)); 1286 std::string message = webrtc::SdpSerialize(jdesc_with_fingerprint); 1287 1288 std::string sdp_with_fingerprint = kSdpString; 1289 InjectAfter(kAttributeIcePwdVoice, 1290 kFingerprint, &sdp_with_fingerprint); 1291 InjectAfter(kAttributeIcePwdVideo, 1292 kFingerprint, &sdp_with_fingerprint); 1293 1294 EXPECT_EQ(sdp_with_fingerprint, message); 1295 } 1296 1297 // This tests serialization of SDP with a=fingerprint with no a=crypto, as would 1298 // be the case in a DTLS answer. 1299 TEST_F(WebRtcSdpTest, SerializeSessionDescriptionWithFingerprintNoCryptos) { 1300 AddFingerprint(); 1301 RemoveCryptos(); 1302 JsepSessionDescription jdesc_with_fingerprint(kDummyString); 1303 ASSERT_TRUE(jdesc_with_fingerprint.Initialize(desc_.Copy(), 1304 kSessionId, kSessionVersion)); 1305 std::string message = webrtc::SdpSerialize(jdesc_with_fingerprint); 1306 1307 std::string sdp_with_fingerprint = kSdpString; 1308 Replace(kAttributeCryptoVoice, "", &sdp_with_fingerprint); 1309 Replace(kAttributeCryptoVideo, "", &sdp_with_fingerprint); 1310 InjectAfter(kAttributeIcePwdVoice, 1311 kFingerprint, &sdp_with_fingerprint); 1312 InjectAfter(kAttributeIcePwdVideo, 1313 kFingerprint, &sdp_with_fingerprint); 1314 1315 EXPECT_EQ(sdp_with_fingerprint, message); 1316 } 1317 1318 TEST_F(WebRtcSdpTest, SerializeSessionDescriptionWithoutCandidates) { 1319 // JsepSessionDescription with desc but without candidates. 1320 JsepSessionDescription jdesc_no_candidates(kDummyString); 1321 ASSERT_TRUE(jdesc_no_candidates.Initialize(desc_.Copy(), 1322 kSessionId, kSessionVersion)); 1323 std::string message = webrtc::SdpSerialize(jdesc_no_candidates); 1324 EXPECT_EQ(std::string(kSdpString), message); 1325 } 1326 1327 TEST_F(WebRtcSdpTest, SerializeSessionDescriptionWithBundle) { 1328 ContentGroup group(cricket::GROUP_TYPE_BUNDLE); 1329 group.AddContentName(kAudioContentName); 1330 group.AddContentName(kVideoContentName); 1331 desc_.AddGroup(group); 1332 ASSERT_TRUE(jdesc_.Initialize(desc_.Copy(), 1333 jdesc_.session_id(), 1334 jdesc_.session_version())); 1335 std::string message = webrtc::SdpSerialize(jdesc_); 1336 std::string sdp_with_bundle = kSdpFullString; 1337 InjectAfter(kSessionTime, 1338 "a=group:BUNDLE audio_content_name video_content_name\r\n", 1339 &sdp_with_bundle); 1340 EXPECT_EQ(sdp_with_bundle, message); 1341 } 1342 1343 TEST_F(WebRtcSdpTest, SerializeSessionDescriptionWithBandwidth) { 1344 VideoContentDescription* vcd = static_cast<VideoContentDescription*>( 1345 GetFirstVideoContent(&desc_)->description); 1346 vcd->set_bandwidth(100 * 1000); 1347 AudioContentDescription* acd = static_cast<AudioContentDescription*>( 1348 GetFirstAudioContent(&desc_)->description); 1349 acd->set_bandwidth(50 * 1000); 1350 ASSERT_TRUE(jdesc_.Initialize(desc_.Copy(), 1351 jdesc_.session_id(), 1352 jdesc_.session_version())); 1353 std::string message = webrtc::SdpSerialize(jdesc_); 1354 std::string sdp_with_bandwidth = kSdpFullString; 1355 InjectAfter("a=mid:video_content_name\r\na=sendrecv\r\n", 1356 "b=AS:100\r\n", 1357 &sdp_with_bandwidth); 1358 InjectAfter("a=mid:audio_content_name\r\na=sendrecv\r\n", 1359 "b=AS:50\r\n", 1360 &sdp_with_bandwidth); 1361 EXPECT_EQ(sdp_with_bandwidth, message); 1362 } 1363 1364 TEST_F(WebRtcSdpTest, SerializeSessionDescriptionWithIceOptions) { 1365 std::vector<std::string> transport_options; 1366 transport_options.push_back(kIceOption1); 1367 transport_options.push_back(kIceOption3); 1368 AddIceOptions(kAudioContentName, transport_options); 1369 transport_options.clear(); 1370 transport_options.push_back(kIceOption2); 1371 transport_options.push_back(kIceOption3); 1372 AddIceOptions(kVideoContentName, transport_options); 1373 ASSERT_TRUE(jdesc_.Initialize(desc_.Copy(), 1374 jdesc_.session_id(), 1375 jdesc_.session_version())); 1376 std::string message = webrtc::SdpSerialize(jdesc_); 1377 std::string sdp_with_ice_options = kSdpFullString; 1378 InjectAfter(kAttributeIcePwdVoice, 1379 "a=ice-options:iceoption1 iceoption3\r\n", 1380 &sdp_with_ice_options); 1381 InjectAfter(kAttributeIcePwdVideo, 1382 "a=ice-options:iceoption2 iceoption3\r\n", 1383 &sdp_with_ice_options); 1384 EXPECT_EQ(sdp_with_ice_options, message); 1385 } 1386 1387 TEST_F(WebRtcSdpTest, SerializeSessionDescriptionWithRecvOnlyContent) { 1388 EXPECT_TRUE(TestSerializeDirection(cricket::MD_RECVONLY)); 1389 } 1390 1391 TEST_F(WebRtcSdpTest, SerializeSessionDescriptionWithSendOnlyContent) { 1392 EXPECT_TRUE(TestSerializeDirection(cricket::MD_SENDONLY)); 1393 } 1394 1395 TEST_F(WebRtcSdpTest, SerializeSessionDescriptionWithInactiveContent) { 1396 EXPECT_TRUE(TestSerializeDirection(cricket::MD_INACTIVE)); 1397 } 1398 1399 TEST_F(WebRtcSdpTest, SerializeSessionDescriptionWithAudioRejected) { 1400 EXPECT_TRUE(TestSerializeRejected(true, false)); 1401 } 1402 1403 TEST_F(WebRtcSdpTest, SerializeSessionDescriptionWithVideoRejected) { 1404 EXPECT_TRUE(TestSerializeRejected(false, true)); 1405 } 1406 1407 TEST_F(WebRtcSdpTest, SerializeSessionDescriptionWithAudioVideoRejected) { 1408 EXPECT_TRUE(TestSerializeRejected(true, true)); 1409 } 1410 1411 TEST_F(WebRtcSdpTest, SerializeSessionDescriptionWithRtpDataChannel) { 1412 AddRtpDataChannel(); 1413 JsepSessionDescription jsep_desc(kDummyString); 1414 1415 ASSERT_TRUE(jsep_desc.Initialize(desc_.Copy(), kSessionId, kSessionVersion)); 1416 std::string message = webrtc::SdpSerialize(jsep_desc); 1417 1418 std::string expected_sdp = kSdpString; 1419 expected_sdp.append(kSdpRtpDataChannelString); 1420 EXPECT_EQ(expected_sdp, message); 1421 } 1422 1423 TEST_F(WebRtcSdpTest, SerializeSessionDescriptionWithSctpDataChannel) { 1424 AddSctpDataChannel(); 1425 JsepSessionDescription jsep_desc(kDummyString); 1426 1427 ASSERT_TRUE(jsep_desc.Initialize(desc_.Copy(), kSessionId, kSessionVersion)); 1428 std::string message = webrtc::SdpSerialize(jsep_desc); 1429 1430 std::string expected_sdp = kSdpString; 1431 expected_sdp.append(kSdpSctpDataChannelString); 1432 EXPECT_EQ(message, expected_sdp); 1433 } 1434 1435 TEST_F(WebRtcSdpTest, SerializeSessionDescriptionWithDataChannelAndBandwidth) { 1436 AddRtpDataChannel(); 1437 data_desc_->set_bandwidth(100*1000); 1438 JsepSessionDescription jsep_desc(kDummyString); 1439 1440 ASSERT_TRUE(jsep_desc.Initialize(desc_.Copy(), kSessionId, kSessionVersion)); 1441 std::string message = webrtc::SdpSerialize(jsep_desc); 1442 1443 std::string expected_sdp = kSdpString; 1444 expected_sdp.append(kSdpRtpDataChannelString); 1445 // We want to test that serializing data content ignores bandwidth 1446 // settings (it should always be the default). Thus, we don't do 1447 // the following: 1448 // TODO(pthatcher): We need to temporarily allow the SDP to control 1449 // this for backwards-compatibility. Once we don't need that any 1450 // more, remove this. 1451 InjectAfter("a=mid:data_content_name\r\na=sendrecv\r\n", 1452 "b=AS:100\r\n", 1453 &expected_sdp); 1454 EXPECT_EQ(expected_sdp, message); 1455 } 1456 1457 TEST_F(WebRtcSdpTest, SerializeSessionDescriptionWithExtmap) { 1458 AddExtmap(); 1459 JsepSessionDescription desc_with_extmap("dummy"); 1460 ASSERT_TRUE(desc_with_extmap.Initialize(desc_.Copy(), 1461 kSessionId, kSessionVersion)); 1462 std::string message = webrtc::SdpSerialize(desc_with_extmap); 1463 1464 std::string sdp_with_extmap = kSdpString; 1465 InjectAfter("a=mid:audio_content_name\r\n", 1466 kExtmap, &sdp_with_extmap); 1467 InjectAfter("a=mid:video_content_name\r\n", 1468 kExtmap, &sdp_with_extmap); 1469 1470 EXPECT_EQ(sdp_with_extmap, message); 1471 } 1472 1473 1474 TEST_F(WebRtcSdpTest, SerializeCandidates) { 1475 std::string message = webrtc::SdpSerializeCandidate(*jcandidate_); 1476 EXPECT_EQ(std::string(kSdpOneCandidate), message); 1477 } 1478 1479 TEST_F(WebRtcSdpTest, DeserializeSessionDescription) { 1480 JsepSessionDescription jdesc(kDummyString); 1481 // Deserialize 1482 EXPECT_TRUE(SdpDeserialize(kSdpFullString, &jdesc)); 1483 // Verify 1484 EXPECT_TRUE(CompareSessionDescription(jdesc_, jdesc)); 1485 } 1486 1487 TEST_F(WebRtcSdpTest, DeserializeSessionDescriptionWithoutMline) { 1488 JsepSessionDescription jdesc(kDummyString); 1489 const char kSdpWithoutMline[] = 1490 "v=0\r\n" 1491 "o=- 18446744069414584320 18446462598732840960 IN IP4 127.0.0.1\r\n" 1492 "s=-\r\n" 1493 "t=0 0\r\n" 1494 "a=msid-semantic: WMS local_stream_1 local_stream_2\r\n"; 1495 // Deserialize 1496 EXPECT_TRUE(SdpDeserialize(kSdpWithoutMline, &jdesc)); 1497 EXPECT_EQ(0u, jdesc.description()->contents().size()); 1498 } 1499 1500 TEST_F(WebRtcSdpTest, DeserializeSessionDescriptionWithoutCarriageReturn) { 1501 JsepSessionDescription jdesc(kDummyString); 1502 std::string sdp_without_carriage_return = kSdpFullString; 1503 Replace("\r\n", "\n", &sdp_without_carriage_return); 1504 // Deserialize 1505 EXPECT_TRUE(SdpDeserialize(sdp_without_carriage_return, &jdesc)); 1506 // Verify 1507 EXPECT_TRUE(CompareSessionDescription(jdesc_, jdesc)); 1508 } 1509 1510 TEST_F(WebRtcSdpTest, DeserializeSessionDescriptionWithoutCandidates) { 1511 // SessionDescription with desc but without candidates. 1512 JsepSessionDescription jdesc_no_candidates(kDummyString); 1513 ASSERT_TRUE(jdesc_no_candidates.Initialize(desc_.Copy(), 1514 kSessionId, kSessionVersion)); 1515 JsepSessionDescription new_jdesc(kDummyString); 1516 EXPECT_TRUE(SdpDeserialize(kSdpString, &new_jdesc)); 1517 EXPECT_TRUE(CompareSessionDescription(jdesc_no_candidates, new_jdesc)); 1518 } 1519 1520 TEST_F(WebRtcSdpTest, DeserializeSessionDescriptionWithoutRtpmap) { 1521 static const char kSdpNoRtpmapString[] = 1522 "v=0\r\n" 1523 "o=- 11 22 IN IP4 127.0.0.1\r\n" 1524 "s=-\r\n" 1525 "t=0 0\r\n" 1526 "m=audio 49232 RTP/AVP 0 18 103\r\n" 1527 // Codec that doesn't appear in the m= line will be ignored. 1528 "a=rtpmap:104 CELT/32000/2\r\n" 1529 // The rtpmap line for static payload codec is optional. 1530 "a=rtpmap:18 G729/16000\r\n" 1531 "a=rtpmap:103 ISAC/16000\r\n"; 1532 1533 JsepSessionDescription jdesc(kDummyString); 1534 EXPECT_TRUE(SdpDeserialize(kSdpNoRtpmapString, &jdesc)); 1535 cricket::AudioContentDescription* audio = 1536 static_cast<AudioContentDescription*>( 1537 jdesc.description()->GetContentDescriptionByName(cricket::CN_AUDIO)); 1538 AudioCodecs ref_codecs; 1539 // The codecs in the AudioContentDescription will be sorted by preference. 1540 ref_codecs.push_back(AudioCodec(0, "PCMU", 8000, 0, 1, 3)); 1541 ref_codecs.push_back(AudioCodec(18, "G729", 16000, 0, 1, 2)); 1542 ref_codecs.push_back(AudioCodec(103, "ISAC", 16000, 32000, 1, 1)); 1543 EXPECT_EQ(ref_codecs, audio->codecs()); 1544 } 1545 1546 // Ensure that we can deserialize SDP with a=fingerprint properly. 1547 TEST_F(WebRtcSdpTest, DeserializeJsepSessionDescriptionWithFingerprint) { 1548 // Add a DTLS a=fingerprint attribute to our session description. 1549 AddFingerprint(); 1550 JsepSessionDescription new_jdesc(kDummyString); 1551 ASSERT_TRUE(new_jdesc.Initialize(desc_.Copy(), 1552 jdesc_.session_id(), 1553 jdesc_.session_version())); 1554 1555 JsepSessionDescription jdesc_with_fingerprint(kDummyString); 1556 std::string sdp_with_fingerprint = kSdpString; 1557 InjectAfter(kAttributeIcePwdVoice, kFingerprint, &sdp_with_fingerprint); 1558 InjectAfter(kAttributeIcePwdVideo, kFingerprint, &sdp_with_fingerprint); 1559 EXPECT_TRUE(SdpDeserialize(sdp_with_fingerprint, &jdesc_with_fingerprint)); 1560 EXPECT_TRUE(CompareSessionDescription(jdesc_with_fingerprint, new_jdesc)); 1561 } 1562 1563 TEST_F(WebRtcSdpTest, DeserializeSessionDescriptionWithBundle) { 1564 JsepSessionDescription jdesc_with_bundle(kDummyString); 1565 std::string sdp_with_bundle = kSdpFullString; 1566 InjectAfter(kSessionTime, 1567 "a=group:BUNDLE audio_content_name video_content_name\r\n", 1568 &sdp_with_bundle); 1569 EXPECT_TRUE(SdpDeserialize(sdp_with_bundle, &jdesc_with_bundle)); 1570 ContentGroup group(cricket::GROUP_TYPE_BUNDLE); 1571 group.AddContentName(kAudioContentName); 1572 group.AddContentName(kVideoContentName); 1573 desc_.AddGroup(group); 1574 ASSERT_TRUE(jdesc_.Initialize(desc_.Copy(), 1575 jdesc_.session_id(), 1576 jdesc_.session_version())); 1577 EXPECT_TRUE(CompareSessionDescription(jdesc_, jdesc_with_bundle)); 1578 } 1579 1580 TEST_F(WebRtcSdpTest, DeserializeSessionDescriptionWithBandwidth) { 1581 JsepSessionDescription jdesc_with_bandwidth(kDummyString); 1582 std::string sdp_with_bandwidth = kSdpFullString; 1583 InjectAfter("a=mid:video_content_name\r\na=sendrecv\r\n", 1584 "b=AS:100\r\n", 1585 &sdp_with_bandwidth); 1586 InjectAfter("a=mid:audio_content_name\r\na=sendrecv\r\n", 1587 "b=AS:50\r\n", 1588 &sdp_with_bandwidth); 1589 EXPECT_TRUE( 1590 SdpDeserialize(sdp_with_bandwidth, &jdesc_with_bandwidth)); 1591 VideoContentDescription* vcd = static_cast<VideoContentDescription*>( 1592 GetFirstVideoContent(&desc_)->description); 1593 vcd->set_bandwidth(100 * 1000); 1594 AudioContentDescription* acd = static_cast<AudioContentDescription*>( 1595 GetFirstAudioContent(&desc_)->description); 1596 acd->set_bandwidth(50 * 1000); 1597 ASSERT_TRUE(jdesc_.Initialize(desc_.Copy(), 1598 jdesc_.session_id(), 1599 jdesc_.session_version())); 1600 EXPECT_TRUE(CompareSessionDescription(jdesc_, jdesc_with_bandwidth)); 1601 } 1602 1603 TEST_F(WebRtcSdpTest, DeserializeSessionDescriptionWithIceOptions) { 1604 JsepSessionDescription jdesc_with_ice_options(kDummyString); 1605 std::string sdp_with_ice_options = kSdpFullString; 1606 InjectAfter(kSessionTime, 1607 "a=ice-options:iceoption3\r\n", 1608 &sdp_with_ice_options); 1609 InjectAfter(kAttributeIcePwdVoice, 1610 "a=ice-options:iceoption1\r\n", 1611 &sdp_with_ice_options); 1612 InjectAfter(kAttributeIcePwdVideo, 1613 "a=ice-options:iceoption2\r\n", 1614 &sdp_with_ice_options); 1615 EXPECT_TRUE(SdpDeserialize(sdp_with_ice_options, &jdesc_with_ice_options)); 1616 std::vector<std::string> transport_options; 1617 transport_options.push_back(kIceOption3); 1618 transport_options.push_back(kIceOption1); 1619 AddIceOptions(kAudioContentName, transport_options); 1620 transport_options.clear(); 1621 transport_options.push_back(kIceOption3); 1622 transport_options.push_back(kIceOption2); 1623 AddIceOptions(kVideoContentName, transport_options); 1624 ASSERT_TRUE(jdesc_.Initialize(desc_.Copy(), 1625 jdesc_.session_id(), 1626 jdesc_.session_version())); 1627 EXPECT_TRUE(CompareSessionDescription(jdesc_, jdesc_with_ice_options)); 1628 } 1629 1630 TEST_F(WebRtcSdpTest, DeserializeSessionDescriptionWithUfragPwd) { 1631 // Remove the original ice-ufrag and ice-pwd 1632 JsepSessionDescription jdesc_with_ufrag_pwd(kDummyString); 1633 std::string sdp_with_ufrag_pwd = kSdpFullString; 1634 EXPECT_TRUE(RemoveCandidateUfragPwd(&sdp_with_ufrag_pwd)); 1635 // Add session level ufrag and pwd 1636 InjectAfter(kSessionTime, 1637 "a=ice-pwd:session+level+icepwd\r\n" 1638 "a=ice-ufrag:session+level+iceufrag\r\n", 1639 &sdp_with_ufrag_pwd); 1640 // Add media level ufrag and pwd for audio 1641 InjectAfter("a=mid:audio_content_name\r\n", 1642 "a=ice-pwd:media+level+icepwd\r\na=ice-ufrag:media+level+iceufrag\r\n", 1643 &sdp_with_ufrag_pwd); 1644 // Update the candidate ufrag and pwd to the expected ones. 1645 EXPECT_TRUE(UpdateCandidateUfragPwd(&jdesc_, 0, 1646 "media+level+iceufrag", "media+level+icepwd")); 1647 EXPECT_TRUE(UpdateCandidateUfragPwd(&jdesc_, 1, 1648 "session+level+iceufrag", "session+level+icepwd")); 1649 EXPECT_TRUE(SdpDeserialize(sdp_with_ufrag_pwd, &jdesc_with_ufrag_pwd)); 1650 EXPECT_TRUE(CompareSessionDescription(jdesc_, jdesc_with_ufrag_pwd)); 1651 } 1652 1653 1654 TEST_F(WebRtcSdpTest, DeserializeSessionDescriptionWithRecvOnlyContent) { 1655 EXPECT_TRUE(TestDeserializeDirection(cricket::MD_RECVONLY)); 1656 } 1657 1658 TEST_F(WebRtcSdpTest, DeserializeSessionDescriptionWithSendOnlyContent) { 1659 EXPECT_TRUE(TestDeserializeDirection(cricket::MD_SENDONLY)); 1660 } 1661 1662 TEST_F(WebRtcSdpTest, DeserializeSessionDescriptionWithInactiveContent) { 1663 EXPECT_TRUE(TestDeserializeDirection(cricket::MD_INACTIVE)); 1664 } 1665 1666 TEST_F(WebRtcSdpTest, DeserializeSessionDescriptionWithRejectedAudio) { 1667 EXPECT_TRUE(TestDeserializeRejected(true, false)); 1668 } 1669 1670 TEST_F(WebRtcSdpTest, DeserializeSessionDescriptionWithRejectedVideo) { 1671 EXPECT_TRUE(TestDeserializeRejected(false, true)); 1672 } 1673 1674 TEST_F(WebRtcSdpTest, DeserializeSessionDescriptionWithRejectedAudioVideo) { 1675 EXPECT_TRUE(TestDeserializeRejected(true, true)); 1676 } 1677 1678 // Tests that we can still handle the sdp uses mslabel and label instead of 1679 // msid for backward compatibility. 1680 TEST_F(WebRtcSdpTest, DeserializeSessionDescriptionWithoutMsid) { 1681 JsepSessionDescription jdesc(kDummyString); 1682 std::string sdp_without_msid = kSdpFullString; 1683 Replace("msid", "xmsid", &sdp_without_msid); 1684 // Deserialize 1685 EXPECT_TRUE(SdpDeserialize(sdp_without_msid, &jdesc)); 1686 // Verify 1687 EXPECT_TRUE(CompareSessionDescription(jdesc_, jdesc)); 1688 } 1689 1690 TEST_F(WebRtcSdpTest, DeserializeCandidate) { 1691 JsepIceCandidate jcandidate(kDummyMid, kDummyIndex); 1692 1693 std::string sdp = kSdpOneCandidate; 1694 EXPECT_TRUE(SdpDeserializeCandidate(sdp, &jcandidate)); 1695 EXPECT_EQ(kDummyMid, jcandidate.sdp_mid()); 1696 EXPECT_EQ(kDummyIndex, jcandidate.sdp_mline_index()); 1697 EXPECT_TRUE(jcandidate.candidate().IsEquivalent(jcandidate_->candidate())); 1698 1699 // Candidate line without generation extension. 1700 sdp = kSdpOneCandidate; 1701 Replace(" generation 2", "", &sdp); 1702 EXPECT_TRUE(SdpDeserializeCandidate(sdp, &jcandidate)); 1703 EXPECT_EQ(kDummyMid, jcandidate.sdp_mid()); 1704 EXPECT_EQ(kDummyIndex, jcandidate.sdp_mline_index()); 1705 Candidate expected = jcandidate_->candidate(); 1706 expected.set_generation(0); 1707 EXPECT_TRUE(jcandidate.candidate().IsEquivalent(expected)); 1708 1709 // Multiple candidate lines. 1710 // Only the first line will be deserialized. The rest will be ignored. 1711 sdp = kSdpOneCandidate; 1712 sdp.append("a=candidate:1 2 tcp 1234 192.168.1.100 5678 typ host\r\n"); 1713 EXPECT_TRUE(SdpDeserializeCandidate(sdp, &jcandidate)); 1714 EXPECT_EQ(kDummyMid, jcandidate.sdp_mid()); 1715 EXPECT_EQ(kDummyIndex, jcandidate.sdp_mline_index()); 1716 EXPECT_TRUE(jcandidate.candidate().IsEquivalent(jcandidate_->candidate())); 1717 } 1718 1719 // This test verifies the deserialization of candidate-attribute 1720 // as per RFC 5245. Candiate-attribute will be of the format 1721 // candidate:<blah>. This format will be used when candidates 1722 // are trickled. 1723 TEST_F(WebRtcSdpTest, DeserializeRawCandidateAttribute) { 1724 JsepIceCandidate jcandidate(kDummyMid, kDummyIndex); 1725 1726 std::string candidate_attribute = kRawCandidate; 1727 EXPECT_TRUE(SdpDeserializeCandidate(candidate_attribute, &jcandidate)); 1728 EXPECT_EQ(kDummyMid, jcandidate.sdp_mid()); 1729 EXPECT_EQ(kDummyIndex, jcandidate.sdp_mline_index()); 1730 EXPECT_TRUE(jcandidate.candidate().IsEquivalent(jcandidate_->candidate())); 1731 EXPECT_EQ(2u, jcandidate.candidate().generation()); 1732 1733 // Candidate line without generation extension. 1734 candidate_attribute = kRawCandidate; 1735 Replace(" generation 2", "", &candidate_attribute); 1736 EXPECT_TRUE(SdpDeserializeCandidate(candidate_attribute, &jcandidate)); 1737 EXPECT_EQ(kDummyMid, jcandidate.sdp_mid()); 1738 EXPECT_EQ(kDummyIndex, jcandidate.sdp_mline_index()); 1739 Candidate expected = jcandidate_->candidate(); 1740 expected.set_generation(0); 1741 EXPECT_TRUE(jcandidate.candidate().IsEquivalent(expected)); 1742 1743 // Candidate line without candidate: 1744 candidate_attribute = kRawCandidate; 1745 Replace("candidate:", "", &candidate_attribute); 1746 EXPECT_FALSE(SdpDeserializeCandidate(candidate_attribute, &jcandidate)); 1747 1748 // Concatenating additional candidate. Expecting deserialization to fail. 1749 candidate_attribute = kRawCandidate; 1750 candidate_attribute.append("candidate:1 2 udp 1234 192.168.1.1 typ host"); 1751 EXPECT_FALSE(SdpDeserializeCandidate(candidate_attribute, &jcandidate)); 1752 } 1753 1754 TEST_F(WebRtcSdpTest, DeserializeSdpWithRtpDataChannels) { 1755 AddRtpDataChannel(); 1756 JsepSessionDescription jdesc(kDummyString); 1757 ASSERT_TRUE(jdesc.Initialize(desc_.Copy(), kSessionId, kSessionVersion)); 1758 1759 std::string sdp_with_data = kSdpString; 1760 sdp_with_data.append(kSdpRtpDataChannelString); 1761 JsepSessionDescription jdesc_output(kDummyString); 1762 1763 // Deserialize 1764 EXPECT_TRUE(SdpDeserialize(sdp_with_data, &jdesc_output)); 1765 // Verify 1766 EXPECT_TRUE(CompareSessionDescription(jdesc, jdesc_output)); 1767 } 1768 1769 TEST_F(WebRtcSdpTest, DeserializeSdpWithSctpDataChannels) { 1770 AddSctpDataChannel(); 1771 JsepSessionDescription jdesc(kDummyString); 1772 ASSERT_TRUE(jdesc.Initialize(desc_.Copy(), kSessionId, kSessionVersion)); 1773 1774 std::string sdp_with_data = kSdpString; 1775 sdp_with_data.append(kSdpSctpDataChannelString); 1776 JsepSessionDescription jdesc_output(kDummyString); 1777 1778 EXPECT_TRUE(SdpDeserialize(sdp_with_data, &jdesc_output)); 1779 EXPECT_TRUE(CompareSessionDescription(jdesc, jdesc_output)); 1780 } 1781 1782 TEST_F(WebRtcSdpTest, DeserializeSdpWithSctpDataChannelAndNewPort) { 1783 AddSctpDataChannel(); 1784 const uint16 kUnusualSctpPort = 9556; 1785 char default_portstr[16]; 1786 char unusual_portstr[16]; 1787 talk_base::sprintfn(default_portstr, sizeof(default_portstr), "%d", 1788 kDefaultSctpPort); 1789 talk_base::sprintfn(unusual_portstr, sizeof(unusual_portstr), "%d", 1790 kUnusualSctpPort); 1791 1792 JsepSessionDescription jdesc(kDummyString); 1793 // take our pre-built session description and change the SCTP port. 1794 cricket::SessionDescription* mutant = desc_.Copy(); 1795 DataContentDescription* dcdesc = static_cast<DataContentDescription*>( 1796 mutant->GetContentDescriptionByName(kDataContentName)); 1797 std::vector<cricket::DataCodec> codecs(dcdesc->codecs()); 1798 EXPECT_EQ(codecs.size(), 1UL); 1799 EXPECT_EQ(codecs[0].id, cricket::kGoogleSctpDataCodecId); 1800 codecs[0].SetParam(cricket::kCodecParamPort, kUnusualSctpPort); 1801 1802 // note: mutant's owned by jdesc now. 1803 ASSERT_TRUE(jdesc.Initialize(mutant, kSessionId, kSessionVersion)); 1804 mutant = NULL; 1805 1806 std::string sdp_with_data = kSdpString; 1807 sdp_with_data.append(kSdpSctpDataChannelString); 1808 talk_base::replace_substrs(default_portstr, strlen(default_portstr), 1809 unusual_portstr, strlen(unusual_portstr), 1810 &sdp_with_data); 1811 JsepSessionDescription jdesc_output(kDummyString); 1812 1813 EXPECT_TRUE(SdpDeserialize(sdp_with_data, &jdesc_output)); 1814 EXPECT_TRUE(CompareSessionDescription(jdesc, jdesc_output)); 1815 } 1816 1817 TEST_F(WebRtcSdpTest, DeserializeSdpWithRtpDataChannelsAndBandwidth) { 1818 AddRtpDataChannel(); 1819 JsepSessionDescription jdesc(kDummyString); 1820 // We want to test that deserializing data content ignores bandwidth 1821 // settings (it should always be the default). Thus, we don't do 1822 // the following: 1823 // TODO(pthatcher): We need to temporarily allow the SDP to control 1824 // this for backwards-compatibility. Once we don't need that any 1825 // more, remove this. 1826 DataContentDescription* dcd = static_cast<DataContentDescription*>( 1827 GetFirstDataContent(&desc_)->description); 1828 dcd->set_bandwidth(100 * 1000); 1829 ASSERT_TRUE(jdesc.Initialize(desc_.Copy(), kSessionId, kSessionVersion)); 1830 1831 std::string sdp_with_bandwidth = kSdpString; 1832 sdp_with_bandwidth.append(kSdpRtpDataChannelString); 1833 InjectAfter("a=mid:data_content_name\r\n", 1834 "b=AS:100\r\n", 1835 &sdp_with_bandwidth); 1836 JsepSessionDescription jdesc_with_bandwidth(kDummyString); 1837 1838 EXPECT_TRUE( 1839 SdpDeserialize(sdp_with_bandwidth, &jdesc_with_bandwidth)); 1840 EXPECT_TRUE(CompareSessionDescription(jdesc, jdesc_with_bandwidth)); 1841 } 1842 1843 TEST_F(WebRtcSdpTest, DeserializeSessionDescriptionWithSessionLevelExtmap) { 1844 TestDeserializeExtmap(true, false); 1845 } 1846 1847 TEST_F(WebRtcSdpTest, DeserializeSessionDescriptionWithMediaLevelExtmap) { 1848 TestDeserializeExtmap(false, true); 1849 } 1850 1851 TEST_F(WebRtcSdpTest, DeserializeSessionDescriptionWithInvalidExtmap) { 1852 TestDeserializeExtmap(true, true); 1853 } 1854 1855 TEST_F(WebRtcSdpTest, DeserializeSessionDescriptionWithoutEndLineBreak) { 1856 JsepSessionDescription jdesc(kDummyString); 1857 std::string sdp = kSdpFullString; 1858 sdp = sdp.substr(0, sdp.size() - 2); // Remove \r\n at the end. 1859 // Deserialize 1860 SdpParseError error; 1861 EXPECT_FALSE(webrtc::SdpDeserialize(sdp, &jdesc, &error)); 1862 const std::string lastline = "a=ssrc:6 label:video_track_id_3"; 1863 EXPECT_EQ(lastline, error.line); 1864 EXPECT_EQ("Invalid SDP line.", error.description); 1865 } 1866 1867 TEST_F(WebRtcSdpTest, DeserializeCandidateWithDifferentTransport) { 1868 JsepIceCandidate jcandidate(kDummyMid, kDummyIndex); 1869 std::string new_sdp = kSdpOneCandidate; 1870 Replace("udp", "unsupported_transport", &new_sdp); 1871 EXPECT_FALSE(SdpDeserializeCandidate(new_sdp, &jcandidate)); 1872 new_sdp = kSdpOneCandidate; 1873 Replace("udp", "uDP", &new_sdp); 1874 EXPECT_TRUE(SdpDeserializeCandidate(new_sdp, &jcandidate)); 1875 EXPECT_EQ(kDummyMid, jcandidate.sdp_mid()); 1876 EXPECT_EQ(kDummyIndex, jcandidate.sdp_mline_index()); 1877 EXPECT_TRUE(jcandidate.candidate().IsEquivalent(jcandidate_->candidate())); 1878 } 1879 1880 TEST_F(WebRtcSdpTest, DeserializeCandidateOldFormat) { 1881 JsepIceCandidate jcandidate(kDummyMid, kDummyIndex); 1882 EXPECT_TRUE(SdpDeserializeCandidate(kSdpOneCandidateOldFormat,&jcandidate)); 1883 EXPECT_EQ(kDummyMid, jcandidate.sdp_mid()); 1884 EXPECT_EQ(kDummyIndex, jcandidate.sdp_mline_index()); 1885 Candidate ref_candidate = jcandidate_->candidate(); 1886 ref_candidate.set_username("user_rtp"); 1887 ref_candidate.set_password("password_rtp"); 1888 EXPECT_TRUE(jcandidate.candidate().IsEquivalent(ref_candidate)); 1889 } 1890 1891 TEST_F(WebRtcSdpTest, DeserializeBrokenSdp) { 1892 const char kSdpDestroyer[] = "!@#$%^&"; 1893 const char kSdpInvalidLine1[] = " =candidate"; 1894 const char kSdpInvalidLine2[] = "a+candidate"; 1895 const char kSdpInvalidLine3[] = "a= candidate"; 1896 // Broken fingerprint. 1897 const char kSdpInvalidLine4[] = "a=fingerprint:sha-1 " 1898 "4AAD:B9:B1:3F:82:18:3B:54:02:12:DF:3E:5D:49:6B:19:E5:7C:AB"; 1899 // Extra field. 1900 const char kSdpInvalidLine5[] = "a=fingerprint:sha-1 " 1901 "4A:AD:B9:B1:3F:82:18:3B:54:02:12:DF:3E:5D:49:6B:19:E5:7C:AB XXX"; 1902 // Missing space. 1903 const char kSdpInvalidLine6[] = "a=fingerprint:sha-1" 1904 "4A:AD:B9:B1:3F:82:18:3B:54:02:12:DF:3E:5D:49:6B:19:E5:7C:AB"; 1905 1906 // Broken session description 1907 ReplaceAndTryToParse("v=", kSdpDestroyer); 1908 ReplaceAndTryToParse("o=", kSdpDestroyer); 1909 ReplaceAndTryToParse("s=-", kSdpDestroyer); 1910 // Broken time description 1911 ReplaceAndTryToParse("t=", kSdpDestroyer); 1912 1913 // Broken media description 1914 ReplaceAndTryToParse("m=audio", "c=IN IP4 74.125.224.39"); 1915 ReplaceAndTryToParse("m=video", kSdpDestroyer); 1916 1917 // Invalid lines 1918 ReplaceAndTryToParse("a=candidate", kSdpInvalidLine1); 1919 ReplaceAndTryToParse("a=candidate", kSdpInvalidLine2); 1920 ReplaceAndTryToParse("a=candidate", kSdpInvalidLine3); 1921 1922 // Bogus fingerprint replacing a=sendrev. We selected this attribute 1923 // because it's orthogonal to what we are replacing and hence 1924 // safe. 1925 ReplaceAndTryToParse("a=sendrecv", kSdpInvalidLine4); 1926 ReplaceAndTryToParse("a=sendrecv", kSdpInvalidLine5); 1927 ReplaceAndTryToParse("a=sendrecv", kSdpInvalidLine6); 1928 } 1929 1930 TEST_F(WebRtcSdpTest, DeserializeSdpWithReorderedPltypes) { 1931 JsepSessionDescription jdesc_output(kDummyString); 1932 1933 const char kSdpWithReorderedPlTypesString[] = 1934 "v=0\r\n" 1935 "o=- 18446744069414584320 18446462598732840960 IN IP4 127.0.0.1\r\n" 1936 "s=-\r\n" 1937 "t=0 0\r\n" 1938 "m=audio 1 RTP/SAVPF 104 103\r\n" // Pl type 104 preferred. 1939 "a=rtpmap:111 opus/48000/2\r\n" // Pltype 111 listed before 103 and 104 1940 // in the map. 1941 "a=rtpmap:103 ISAC/16000\r\n" // Pltype 103 listed before 104 in the map. 1942 "a=rtpmap:104 CELT/32000/2\r\n"; 1943 1944 // Deserialize 1945 EXPECT_TRUE(SdpDeserialize(kSdpWithReorderedPlTypesString, &jdesc_output)); 1946 1947 const ContentInfo* ac = GetFirstAudioContent(jdesc_output.description()); 1948 ASSERT_TRUE(ac != NULL); 1949 const AudioContentDescription* acd = 1950 static_cast<const AudioContentDescription*>(ac->description); 1951 ASSERT_FALSE(acd->codecs().empty()); 1952 EXPECT_EQ("CELT", acd->codecs()[0].name); 1953 EXPECT_EQ(104, acd->codecs()[0].id); 1954 } 1955 1956 TEST_F(WebRtcSdpTest, DeserializeSerializeCodecParams) { 1957 JsepSessionDescription jdesc_output(kDummyString); 1958 CodecParams params; 1959 params.max_ptime = 40; 1960 params.ptime = 30; 1961 params.min_ptime = 10; 1962 params.sprop_stereo = 1; 1963 params.stereo = 1; 1964 params.useinband = 1; 1965 params.maxaveragebitrate = 128000; 1966 TestDeserializeCodecParams(params, &jdesc_output); 1967 TestSerialize(jdesc_output); 1968 } 1969 1970 TEST_F(WebRtcSdpTest, DeserializeSerializeRtcpFb) { 1971 const bool kUseWildcard = false; 1972 JsepSessionDescription jdesc_output(kDummyString); 1973 TestDeserializeRtcpFb(&jdesc_output, kUseWildcard); 1974 TestSerialize(jdesc_output); 1975 } 1976 1977 TEST_F(WebRtcSdpTest, DeserializeSerializeRtcpFbWildcard) { 1978 const bool kUseWildcard = true; 1979 JsepSessionDescription jdesc_output(kDummyString); 1980 TestDeserializeRtcpFb(&jdesc_output, kUseWildcard); 1981 TestSerialize(jdesc_output); 1982 } 1983 1984 TEST_F(WebRtcSdpTest, DeserializeVideoFmtp) { 1985 JsepSessionDescription jdesc_output(kDummyString); 1986 1987 const char kSdpWithFmtpString[] = 1988 "v=0\r\n" 1989 "o=- 18446744069414584320 18446462598732840960 IN IP4 127.0.0.1\r\n" 1990 "s=-\r\n" 1991 "t=0 0\r\n" 1992 "m=video 3457 RTP/SAVPF 120\r\n" 1993 "a=rtpmap:120 VP8/90000\r\n" 1994 "a=fmtp:120 x-google-min-bitrate=10; x-google-max-quantization=40\r\n"; 1995 1996 // Deserialize 1997 SdpParseError error; 1998 EXPECT_TRUE(webrtc::SdpDeserialize(kSdpWithFmtpString, &jdesc_output, 1999 &error)); 2000 2001 const ContentInfo* vc = GetFirstVideoContent(jdesc_output.description()); 2002 ASSERT_TRUE(vc != NULL); 2003 const VideoContentDescription* vcd = 2004 static_cast<const VideoContentDescription*>(vc->description); 2005 ASSERT_FALSE(vcd->codecs().empty()); 2006 cricket::VideoCodec vp8 = vcd->codecs()[0]; 2007 EXPECT_EQ("VP8", vp8.name); 2008 EXPECT_EQ(120, vp8.id); 2009 cricket::CodecParameterMap::iterator found = 2010 vp8.params.find("x-google-min-bitrate"); 2011 ASSERT_TRUE(found != vp8.params.end()); 2012 EXPECT_EQ(found->second, "10"); 2013 found = vp8.params.find("x-google-max-quantization"); 2014 ASSERT_TRUE(found != vp8.params.end()); 2015 EXPECT_EQ(found->second, "40"); 2016 } 2017 2018 TEST_F(WebRtcSdpTest, SerializeVideoFmtp) { 2019 VideoContentDescription* vcd = static_cast<VideoContentDescription*>( 2020 GetFirstVideoContent(&desc_)->description); 2021 2022 cricket::VideoCodecs codecs = vcd->codecs(); 2023 codecs[0].params["x-google-min-bitrate"] = "10"; 2024 vcd->set_codecs(codecs); 2025 2026 ASSERT_TRUE(jdesc_.Initialize(desc_.Copy(), 2027 jdesc_.session_id(), 2028 jdesc_.session_version())); 2029 std::string message = webrtc::SdpSerialize(jdesc_); 2030 std::string sdp_with_fmtp = kSdpFullString; 2031 InjectAfter("a=rtpmap:120 VP8/90000\r\n", 2032 "a=fmtp:120 x-google-min-bitrate=10\r\n", 2033 &sdp_with_fmtp); 2034 EXPECT_EQ(sdp_with_fmtp, message); 2035 } 2036 2037 TEST_F(WebRtcSdpTest, DeserializeSdpWithIceLite) { 2038 JsepSessionDescription jdesc_with_icelite(kDummyString); 2039 std::string sdp_with_icelite = kSdpFullString; 2040 EXPECT_TRUE(SdpDeserialize(sdp_with_icelite, &jdesc_with_icelite)); 2041 cricket::SessionDescription* desc = jdesc_with_icelite.description(); 2042 const cricket::TransportInfo* tinfo1 = 2043 desc->GetTransportInfoByName("audio_content_name"); 2044 EXPECT_EQ(cricket::ICEMODE_FULL, tinfo1->description.ice_mode); 2045 const cricket::TransportInfo* tinfo2 = 2046 desc->GetTransportInfoByName("video_content_name"); 2047 EXPECT_EQ(cricket::ICEMODE_FULL, tinfo2->description.ice_mode); 2048 InjectAfter(kSessionTime, 2049 "a=ice-lite\r\n", 2050 &sdp_with_icelite); 2051 EXPECT_TRUE(SdpDeserialize(sdp_with_icelite, &jdesc_with_icelite)); 2052 desc = jdesc_with_icelite.description(); 2053 const cricket::TransportInfo* atinfo = 2054 desc->GetTransportInfoByName("audio_content_name"); 2055 EXPECT_EQ(cricket::ICEMODE_LITE, atinfo->description.ice_mode); 2056 const cricket::TransportInfo* vtinfo = 2057 desc->GetTransportInfoByName("video_content_name"); 2058 EXPECT_EQ(cricket::ICEMODE_LITE, vtinfo->description.ice_mode); 2059 } 2060 2061 // Verifies that the candidates in the input SDP are parsed and serialized 2062 // correctly in the output SDP. 2063 TEST_F(WebRtcSdpTest, RoundTripSdpWithSctpDataChannelsWithCandidates) { 2064 std::string sdp_with_data = kSdpString; 2065 sdp_with_data.append(kSdpSctpDataChannelWithCandidatesString); 2066 JsepSessionDescription jdesc_output(kDummyString); 2067 2068 EXPECT_TRUE(SdpDeserialize(sdp_with_data, &jdesc_output)); 2069 EXPECT_EQ(sdp_with_data, webrtc::SdpSerialize(jdesc_output)); 2070 } 2071 2072 TEST_F(WebRtcSdpTest, SerializeDtlsSetupAttribute) { 2073 AddFingerprint(); 2074 TransportInfo audio_transport_info = 2075 *(desc_.GetTransportInfoByName(kAudioContentName)); 2076 EXPECT_EQ(cricket::CONNECTIONROLE_NONE, 2077 audio_transport_info.description.connection_role); 2078 audio_transport_info.description.connection_role = 2079 cricket::CONNECTIONROLE_ACTIVE; 2080 2081 TransportInfo video_transport_info = 2082 *(desc_.GetTransportInfoByName(kVideoContentName)); 2083 EXPECT_EQ(cricket::CONNECTIONROLE_NONE, 2084 video_transport_info.description.connection_role); 2085 video_transport_info.description.connection_role = 2086 cricket::CONNECTIONROLE_ACTIVE; 2087 2088 desc_.RemoveTransportInfoByName(kAudioContentName); 2089 desc_.RemoveTransportInfoByName(kVideoContentName); 2090 2091 desc_.AddTransportInfo(audio_transport_info); 2092 desc_.AddTransportInfo(video_transport_info); 2093 2094 ASSERT_TRUE(jdesc_.Initialize(desc_.Copy(), 2095 jdesc_.session_id(), 2096 jdesc_.session_version())); 2097 std::string message = webrtc::SdpSerialize(jdesc_); 2098 std::string sdp_with_dtlssetup = kSdpFullString; 2099 2100 // Fingerprint attribute is necessary to add DTLS setup attribute. 2101 InjectAfter(kAttributeIcePwdVoice, 2102 kFingerprint, &sdp_with_dtlssetup); 2103 InjectAfter(kAttributeIcePwdVideo, 2104 kFingerprint, &sdp_with_dtlssetup); 2105 // Now adding |setup| attribute. 2106 InjectAfter(kFingerprint, 2107 "a=setup:active\r\n", &sdp_with_dtlssetup); 2108 EXPECT_EQ(sdp_with_dtlssetup, message); 2109 } 2110 2111 TEST_F(WebRtcSdpTest, DeserializeDtlsSetupAttribute) { 2112 JsepSessionDescription jdesc_with_dtlssetup(kDummyString); 2113 std::string sdp_with_dtlssetup = kSdpFullString; 2114 InjectAfter(kSessionTime, 2115 "a=setup:actpass\r\n", 2116 &sdp_with_dtlssetup); 2117 EXPECT_TRUE(SdpDeserialize(sdp_with_dtlssetup, &jdesc_with_dtlssetup)); 2118 cricket::SessionDescription* desc = jdesc_with_dtlssetup.description(); 2119 const cricket::TransportInfo* atinfo = 2120 desc->GetTransportInfoByName("audio_content_name"); 2121 EXPECT_EQ(cricket::CONNECTIONROLE_ACTPASS, 2122 atinfo->description.connection_role); 2123 const cricket::TransportInfo* vtinfo = 2124 desc->GetTransportInfoByName("video_content_name"); 2125 EXPECT_EQ(cricket::CONNECTIONROLE_ACTPASS, 2126 vtinfo->description.connection_role); 2127 } 2128