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