webrtcsdp_unittest.cc revision 571df2dca9357620e69690c562370680ddb67b6f
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 45using cricket::AudioCodec; 46using cricket::AudioContentDescription; 47using cricket::Candidate; 48using cricket::ContentInfo; 49using cricket::CryptoParams; 50using cricket::ContentGroup; 51using cricket::DataCodec; 52using cricket::DataContentDescription; 53using cricket::ICE_CANDIDATE_COMPONENT_RTCP; 54using cricket::ICE_CANDIDATE_COMPONENT_RTP; 55using cricket::kFecSsrcGroupSemantics; 56using cricket::LOCAL_PORT_TYPE; 57using cricket::NS_JINGLE_DRAFT_SCTP; 58using cricket::NS_JINGLE_ICE_UDP; 59using cricket::NS_JINGLE_RTP; 60using cricket::RtpHeaderExtension; 61using cricket::RELAY_PORT_TYPE; 62using cricket::SessionDescription; 63using cricket::StreamParams; 64using cricket::STUN_PORT_TYPE; 65using cricket::TransportDescription; 66using cricket::TransportInfo; 67using cricket::VideoCodec; 68using cricket::VideoContentDescription; 69using webrtc::IceCandidateCollection; 70using webrtc::IceCandidateInterface; 71using webrtc::JsepIceCandidate; 72using webrtc::JsepSessionDescription; 73using webrtc::SdpParseError; 74using webrtc::SessionDescriptionInterface; 75 76typedef std::vector<AudioCodec> AudioCodecs; 77typedef std::vector<Candidate> Candidates; 78 79static const uint32 kDefaultSctpPort = 5000; 80static const char kSessionTime[] = "t=0 0\r\n"; 81static const uint32 kCandidatePriority = 2130706432U; // pref = 1.0 82static const char kCandidateUfragVoice[] = "ufrag_voice"; 83static const char kCandidatePwdVoice[] = "pwd_voice"; 84static const char kAttributeIcePwdVoice[] = "a=ice-pwd:pwd_voice\r\n"; 85static const char kCandidateUfragVideo[] = "ufrag_video"; 86static const char kCandidatePwdVideo[] = "pwd_video"; 87static const char kCandidateUfragData[] = "ufrag_data"; 88static const char kCandidatePwdData[] = "pwd_data"; 89static const char kAttributeIcePwdVideo[] = "a=ice-pwd:pwd_video\r\n"; 90static const uint32 kCandidateGeneration = 2; 91static const char kCandidateFoundation1[] = "a0+B/1"; 92static const char kCandidateFoundation2[] = "a0+B/2"; 93static const char kCandidateFoundation3[] = "a0+B/3"; 94static const char kCandidateFoundation4[] = "a0+B/4"; 95static const char kAttributeCryptoVoice[] = 96 "a=crypto:1 AES_CM_128_HMAC_SHA1_32 " 97 "inline:NzB4d1BINUAvLEw6UzF3WSJ+PSdFcGdUJShpX1Zj|2^20|1:32 " 98 "dummy_session_params\r\n"; 99static const char kAttributeCryptoVideo[] = 100 "a=crypto:1 AES_CM_128_HMAC_SHA1_80 " 101 "inline:d0RmdmcmVCspeEc3QGZiNWpVLFJhQX1cfHAwJSoj|2^20|1:32\r\n"; 102static const char kFingerprint[] = "a=fingerprint:sha-1 " 103 "4A:AD:B9:B1:3F:82:18:3B:54:02:12:DF:3E:5D:49:6B:19:E5:7C:AB\r\n"; 104static const int kExtmapId = 1; 105static const char kExtmapUri[] = "http://example.com/082005/ext.htm#ttime"; 106static const char kExtmap[] = 107 "a=extmap:1 http://example.com/082005/ext.htm#ttime\r\n"; 108static const char kExtmapWithDirectionAndAttribute[] = 109 "a=extmap:1/sendrecv http://example.com/082005/ext.htm#ttime a1 a2\r\n"; 110 111static const uint8 kIdentityDigest[] = {0x4A, 0xAD, 0xB9, 0xB1, 112 0x3F, 0x82, 0x18, 0x3B, 113 0x54, 0x02, 0x12, 0xDF, 114 0x3E, 0x5D, 0x49, 0x6B, 115 0x19, 0xE5, 0x7C, 0xAB}; 116 117struct CodecParams { 118 int max_ptime; 119 int ptime; 120 int min_ptime; 121 int sprop_stereo; 122 int stereo; 123 int useinband; 124 int maxaveragebitrate; 125}; 126 127// Reference sdp string 128static const char kSdpFullString[] = 129 "v=0\r\n" 130 "o=- 18446744069414584320 18446462598732840960 IN IP4 127.0.0.1\r\n" 131 "s=-\r\n" 132 "t=0 0\r\n" 133 "a=msid-semantic: WMS local_stream_1 local_stream_2\r\n" 134 "m=audio 2345 RTP/SAVPF 111 103 104\r\n" 135 "c=IN IP4 74.125.127.126\r\n" 136 "a=rtcp:2347 IN IP4 74.125.127.126\r\n" 137 "a=candidate:a0+B/1 1 udp 2130706432 192.168.1.5 1234 typ host " 138 "generation 2\r\n" 139 "a=candidate:a0+B/1 2 udp 2130706432 192.168.1.5 1235 typ host " 140 "generation 2\r\n" 141 "a=candidate:a0+B/2 1 udp 2130706432 ::1 1238 typ host " 142 "generation 2\r\n" 143 "a=candidate:a0+B/2 2 udp 2130706432 ::1 1239 typ host " 144 "generation 2\r\n" 145 "a=candidate:a0+B/3 1 udp 2130706432 74.125.127.126 2345 typ srflx " 146 "raddr 192.168.1.5 rport 2346 " 147 "generation 2\r\n" 148 "a=candidate:a0+B/3 2 udp 2130706432 74.125.127.126 2347 typ srflx " 149 "raddr 192.168.1.5 rport 2348 " 150 "generation 2\r\n" 151 "a=ice-ufrag:ufrag_voice\r\na=ice-pwd:pwd_voice\r\n" 152 "a=mid:audio_content_name\r\n" 153 "a=sendrecv\r\n" 154 "a=rtcp-mux\r\n" 155 "a=crypto:1 AES_CM_128_HMAC_SHA1_32 " 156 "inline:NzB4d1BINUAvLEw6UzF3WSJ+PSdFcGdUJShpX1Zj|2^20|1:32 " 157 "dummy_session_params\r\n" 158 "a=rtpmap:111 opus/48000/2\r\n" 159 "a=rtpmap:103 ISAC/16000\r\n" 160 "a=rtpmap:104 CELT/32000/2\r\n" 161 "a=ssrc:1 cname:stream_1_cname\r\n" 162 "a=ssrc:1 msid:local_stream_1 audio_track_id_1\r\n" 163 "a=ssrc:1 mslabel:local_stream_1\r\n" 164 "a=ssrc:1 label:audio_track_id_1\r\n" 165 "a=ssrc:4 cname:stream_2_cname\r\n" 166 "a=ssrc:4 msid:local_stream_2 audio_track_id_2\r\n" 167 "a=ssrc:4 mslabel:local_stream_2\r\n" 168 "a=ssrc:4 label:audio_track_id_2\r\n" 169 "m=video 3457 RTP/SAVPF 120\r\n" 170 "c=IN IP4 74.125.224.39\r\n" 171 "a=rtcp:3456 IN IP4 74.125.224.39\r\n" 172 "a=candidate:a0+B/1 2 udp 2130706432 192.168.1.5 1236 typ host " 173 "generation 2\r\n" 174 "a=candidate:a0+B/1 1 udp 2130706432 192.168.1.5 1237 typ host " 175 "generation 2\r\n" 176 "a=candidate:a0+B/2 2 udp 2130706432 ::1 1240 typ host " 177 "generation 2\r\n" 178 "a=candidate:a0+B/2 1 udp 2130706432 ::1 1241 typ host " 179 "generation 2\r\n" 180 "a=candidate:a0+B/4 2 udp 2130706432 74.125.224.39 3456 typ relay " 181 "generation 2\r\n" 182 "a=candidate:a0+B/4 1 udp 2130706432 74.125.224.39 3457 typ relay " 183 "generation 2\r\n" 184 "a=ice-ufrag:ufrag_video\r\na=ice-pwd:pwd_video\r\n" 185 "a=mid:video_content_name\r\n" 186 "a=sendrecv\r\n" 187 "a=crypto:1 AES_CM_128_HMAC_SHA1_80 " 188 "inline:d0RmdmcmVCspeEc3QGZiNWpVLFJhQX1cfHAwJSoj|2^20|1:32\r\n" 189 "a=rtpmap:120 VP8/90000\r\n" 190 "a=ssrc:2 cname:stream_1_cname\r\n" 191 "a=ssrc:2 msid:local_stream_1 video_track_id_1\r\n" 192 "a=ssrc:2 mslabel:local_stream_1\r\n" 193 "a=ssrc:2 label:video_track_id_1\r\n" 194 "a=ssrc:3 cname:stream_1_cname\r\n" 195 "a=ssrc:3 msid:local_stream_1 video_track_id_2\r\n" 196 "a=ssrc:3 mslabel:local_stream_1\r\n" 197 "a=ssrc:3 label:video_track_id_2\r\n" 198 "a=ssrc-group:FEC 5 6\r\n" 199 "a=ssrc:5 cname:stream_2_cname\r\n" 200 "a=ssrc:5 msid:local_stream_2 video_track_id_3\r\n" 201 "a=ssrc:5 mslabel:local_stream_2\r\n" 202 "a=ssrc:5 label:video_track_id_3\r\n" 203 "a=ssrc:6 cname:stream_2_cname\r\n" 204 "a=ssrc:6 msid:local_stream_2 video_track_id_3\r\n" 205 "a=ssrc:6 mslabel:local_stream_2\r\n" 206 "a=ssrc:6 label:video_track_id_3\r\n"; 207 208// SDP reference string without the candidates. 209static const char kSdpString[] = 210 "v=0\r\n" 211 "o=- 18446744069414584320 18446462598732840960 IN IP4 127.0.0.1\r\n" 212 "s=-\r\n" 213 "t=0 0\r\n" 214 "a=msid-semantic: WMS local_stream_1 local_stream_2\r\n" 215 "m=audio 1 RTP/SAVPF 111 103 104\r\n" 216 "c=IN IP4 0.0.0.0\r\n" 217 "a=rtcp:1 IN IP4 0.0.0.0\r\n" 218 "a=ice-ufrag:ufrag_voice\r\na=ice-pwd:pwd_voice\r\n" 219 "a=mid:audio_content_name\r\n" 220 "a=sendrecv\r\n" 221 "a=rtcp-mux\r\n" 222 "a=crypto:1 AES_CM_128_HMAC_SHA1_32 " 223 "inline:NzB4d1BINUAvLEw6UzF3WSJ+PSdFcGdUJShpX1Zj|2^20|1:32 " 224 "dummy_session_params\r\n" 225 "a=rtpmap:111 opus/48000/2\r\n" 226 "a=rtpmap:103 ISAC/16000\r\n" 227 "a=rtpmap:104 CELT/32000/2\r\n" 228 "a=ssrc:1 cname:stream_1_cname\r\n" 229 "a=ssrc:1 msid:local_stream_1 audio_track_id_1\r\n" 230 "a=ssrc:1 mslabel:local_stream_1\r\n" 231 "a=ssrc:1 label:audio_track_id_1\r\n" 232 "a=ssrc:4 cname:stream_2_cname\r\n" 233 "a=ssrc:4 msid:local_stream_2 audio_track_id_2\r\n" 234 "a=ssrc:4 mslabel:local_stream_2\r\n" 235 "a=ssrc:4 label:audio_track_id_2\r\n" 236 "m=video 1 RTP/SAVPF 120\r\n" 237 "c=IN IP4 0.0.0.0\r\n" 238 "a=rtcp:1 IN IP4 0.0.0.0\r\n" 239 "a=ice-ufrag:ufrag_video\r\na=ice-pwd:pwd_video\r\n" 240 "a=mid:video_content_name\r\n" 241 "a=sendrecv\r\n" 242 "a=crypto:1 AES_CM_128_HMAC_SHA1_80 " 243 "inline:d0RmdmcmVCspeEc3QGZiNWpVLFJhQX1cfHAwJSoj|2^20|1:32\r\n" 244 "a=rtpmap:120 VP8/90000\r\n" 245 "a=ssrc:2 cname:stream_1_cname\r\n" 246 "a=ssrc:2 msid:local_stream_1 video_track_id_1\r\n" 247 "a=ssrc:2 mslabel:local_stream_1\r\n" 248 "a=ssrc:2 label:video_track_id_1\r\n" 249 "a=ssrc:3 cname:stream_1_cname\r\n" 250 "a=ssrc:3 msid:local_stream_1 video_track_id_2\r\n" 251 "a=ssrc:3 mslabel:local_stream_1\r\n" 252 "a=ssrc:3 label:video_track_id_2\r\n" 253 "a=ssrc-group:FEC 5 6\r\n" 254 "a=ssrc:5 cname:stream_2_cname\r\n" 255 "a=ssrc:5 msid:local_stream_2 video_track_id_3\r\n" 256 "a=ssrc:5 mslabel:local_stream_2\r\n" 257 "a=ssrc:5 label:video_track_id_3\r\n" 258 "a=ssrc:6 cname:stream_2_cname\r\n" 259 "a=ssrc:6 msid:local_stream_2 video_track_id_3\r\n" 260 "a=ssrc:6 mslabel:local_stream_2\r\n" 261 "a=ssrc:6 label:video_track_id_3\r\n"; 262 263static const char kSdpRtpDataChannelString[] = 264 "m=application 1 RTP/SAVPF 101\r\n" 265 "c=IN IP4 0.0.0.0\r\n" 266 "a=rtcp:1 IN IP4 0.0.0.0\r\n" 267 "a=ice-ufrag:ufrag_data\r\n" 268 "a=ice-pwd:pwd_data\r\n" 269 "a=mid:data_content_name\r\n" 270 "a=sendrecv\r\n" 271 "a=crypto:1 AES_CM_128_HMAC_SHA1_80 " 272 "inline:FvLcvU2P3ZWmQxgPAgcDu7Zl9vftYElFOjEzhWs5\r\n" 273 "a=rtpmap:101 google-data/90000\r\n" 274 "a=ssrc:10 cname:data_channel_cname\r\n" 275 "a=ssrc:10 msid:data_channel data_channeld0\r\n" 276 "a=ssrc:10 mslabel:data_channel\r\n" 277 "a=ssrc:10 label:data_channeld0\r\n"; 278 279static const char kSdpSctpDataChannelString[] = 280 "m=application 1 DTLS/SCTP 5000\r\n" 281 "c=IN IP4 0.0.0.0\r\n" 282 "a=ice-ufrag:ufrag_data\r\n" 283 "a=ice-pwd:pwd_data\r\n" 284 "a=mid:data_content_name\r\n" 285 "a=sctpmap:5000 webrtc-datachannel 1024\r\n"; 286 287static 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 305static 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. 308static 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. 313static 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 318static const char kSessionId[] = "18446744069414584320"; 319static const char kSessionVersion[] = "18446462598732840960"; 320 321// Ice options 322static const char kIceOption1[] = "iceoption1"; 323static const char kIceOption2[] = "iceoption2"; 324static const char kIceOption3[] = "iceoption3"; 325 326// Content name 327static const char kAudioContentName[] = "audio_content_name"; 328static const char kVideoContentName[] = "video_content_name"; 329static const char kDataContentName[] = "data_content_name"; 330 331// MediaStream 1 332static const char kStreamLabel1[] = "local_stream_1"; 333static const char kStream1Cname[] = "stream_1_cname"; 334static const char kAudioTrackId1[] = "audio_track_id_1"; 335static const uint32 kAudioTrack1Ssrc = 1; 336static const char kVideoTrackId1[] = "video_track_id_1"; 337static const uint32 kVideoTrack1Ssrc = 2; 338static const char kVideoTrackId2[] = "video_track_id_2"; 339static const uint32 kVideoTrack2Ssrc = 3; 340 341// MediaStream 2 342static const char kStreamLabel2[] = "local_stream_2"; 343static const char kStream2Cname[] = "stream_2_cname"; 344static const char kAudioTrackId2[] = "audio_track_id_2"; 345static const uint32 kAudioTrack2Ssrc = 4; 346static const char kVideoTrackId3[] = "video_track_id_3"; 347static const uint32 kVideoTrack3Ssrc = 5; 348static const uint32 kVideoTrack4Ssrc = 6; 349 350// DataChannel 351static const char kDataChannelLabel[] = "data_channel"; 352static const char kDataChannelMsid[] = "data_channeld0"; 353static const char kDataChannelCname[] = "data_channel_cname"; 354static const uint32 kDataChannelSsrc = 10; 355 356// Candidate 357static const char kDummyMid[] = "dummy_mid"; 358static const int kDummyIndex = 123; 359 360// Misc 361static const char kDummyString[] = "dummy"; 362 363// Helper functions 364 365static bool SdpDeserialize(const std::string& message, 366 JsepSessionDescription* jdesc) { 367 return webrtc::SdpDeserialize(message, jdesc, NULL); 368} 369 370static 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|. 376static 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 384static 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 391static 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 401static 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 422static 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 434class 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 nack pli\r\n" 1176 "a=rtcp-fb:101 goog-remb\r\n" 1177 "a=rtcp-fb:101 ccm fir\r\n"; 1178 std::ostringstream os; 1179 os << "a=rtcp-fb:" << (use_wildcard ? "*" : "101") << " ccm fir\r\n"; 1180 sdp += os.str(); 1181 // Deserialize 1182 SdpParseError error; 1183 EXPECT_TRUE(webrtc::SdpDeserialize(sdp, jdesc_output, &error)); 1184 const ContentInfo* ac = GetFirstAudioContent(jdesc_output->description()); 1185 ASSERT_TRUE(ac != NULL); 1186 const AudioContentDescription* acd = 1187 static_cast<const AudioContentDescription*>(ac->description); 1188 ASSERT_FALSE(acd->codecs().empty()); 1189 cricket::AudioCodec opus = acd->codecs()[0]; 1190 EXPECT_EQ(111, opus.id); 1191 EXPECT_TRUE(opus.HasFeedbackParam( 1192 cricket::FeedbackParam(cricket::kRtcpFbParamNack, 1193 cricket::kParamValueEmpty))); 1194 1195 const ContentInfo* vc = GetFirstVideoContent(jdesc_output->description()); 1196 ASSERT_TRUE(vc != NULL); 1197 const VideoContentDescription* vcd = 1198 static_cast<const VideoContentDescription*>(vc->description); 1199 ASSERT_FALSE(vcd->codecs().empty()); 1200 cricket::VideoCodec vp8 = vcd->codecs()[0]; 1201 EXPECT_STREQ(webrtc::JsepSessionDescription::kDefaultVideoCodecName, 1202 vp8.name.c_str()); 1203 EXPECT_EQ(101, vp8.id); 1204 EXPECT_TRUE(vp8.HasFeedbackParam( 1205 cricket::FeedbackParam(cricket::kRtcpFbParamNack, 1206 cricket::kParamValueEmpty))); 1207 EXPECT_TRUE(vp8.HasFeedbackParam( 1208 cricket::FeedbackParam(cricket::kRtcpFbParamNack, 1209 cricket::kRtcpFbNackParamPli))); 1210 EXPECT_TRUE(vp8.HasFeedbackParam( 1211 cricket::FeedbackParam(cricket::kRtcpFbParamRemb, 1212 cricket::kParamValueEmpty))); 1213 EXPECT_TRUE(vp8.HasFeedbackParam( 1214 cricket::FeedbackParam(cricket::kRtcpFbParamCcm, 1215 cricket::kRtcpFbCcmParamFir))); 1216 } 1217 1218 // Two SDP messages can mean the same thing but be different strings, e.g. 1219 // some of the lines can be serialized in different order. 1220 // However, a deserialized description can be compared field by field and has 1221 // no order. If deserializer has already been tested, serializing then 1222 // deserializing and comparing JsepSessionDescription will test 1223 // the serializer sufficiently. 1224 void TestSerialize(const JsepSessionDescription& jdesc) { 1225 std::string message = webrtc::SdpSerialize(jdesc); 1226 JsepSessionDescription jdesc_output_des(kDummyString); 1227 SdpParseError error; 1228 EXPECT_TRUE(webrtc::SdpDeserialize(message, &jdesc_output_des, &error)); 1229 EXPECT_TRUE(CompareSessionDescription(jdesc, jdesc_output_des)); 1230 } 1231 1232 protected: 1233 SessionDescription desc_; 1234 AudioContentDescription* audio_desc_; 1235 VideoContentDescription* video_desc_; 1236 DataContentDescription* data_desc_; 1237 Candidates candidates_; 1238 talk_base::scoped_ptr<IceCandidateInterface> jcandidate_; 1239 JsepSessionDescription jdesc_; 1240}; 1241 1242void TestMismatch(const std::string& string1, const std::string& string2) { 1243 int position = 0; 1244 for (size_t i = 0; i < string1.length() && i < string2.length(); ++i) { 1245 if (string1.c_str()[i] != string2.c_str()[i]) { 1246 position = static_cast<int>(i); 1247 break; 1248 } 1249 } 1250 EXPECT_EQ(0, position) << "Strings mismatch at the " << position 1251 << " character\n" 1252 << " 1: " << string1.substr(position, 20) << "\n" 1253 << " 2: " << string2.substr(position, 20) << "\n"; 1254} 1255 1256std::string GetLine(const std::string& message, 1257 const std::string& session_description_name) { 1258 size_t start = message.find(session_description_name); 1259 if (std::string::npos == start) { 1260 return ""; 1261 } 1262 size_t stop = message.find("\r\n", start); 1263 if (std::string::npos == stop) { 1264 return ""; 1265 } 1266 if (stop <= start) { 1267 return ""; 1268 } 1269 return message.substr(start, stop - start); 1270} 1271 1272TEST_F(WebRtcSdpTest, SerializeSessionDescription) { 1273 // SessionDescription with desc and candidates. 1274 std::string message = webrtc::SdpSerialize(jdesc_); 1275 TestMismatch(std::string(kSdpFullString), message); 1276} 1277 1278TEST_F(WebRtcSdpTest, SerializeSessionDescriptionEmpty) { 1279 JsepSessionDescription jdesc_empty(kDummyString); 1280 EXPECT_EQ("", webrtc::SdpSerialize(jdesc_empty)); 1281} 1282 1283// This tests serialization of SDP with a=crypto and a=fingerprint, as would be 1284// the case in a DTLS offer. 1285TEST_F(WebRtcSdpTest, SerializeSessionDescriptionWithFingerprint) { 1286 AddFingerprint(); 1287 JsepSessionDescription jdesc_with_fingerprint(kDummyString); 1288 ASSERT_TRUE(jdesc_with_fingerprint.Initialize(desc_.Copy(), 1289 kSessionId, kSessionVersion)); 1290 std::string message = webrtc::SdpSerialize(jdesc_with_fingerprint); 1291 1292 std::string sdp_with_fingerprint = kSdpString; 1293 InjectAfter(kAttributeIcePwdVoice, 1294 kFingerprint, &sdp_with_fingerprint); 1295 InjectAfter(kAttributeIcePwdVideo, 1296 kFingerprint, &sdp_with_fingerprint); 1297 1298 EXPECT_EQ(sdp_with_fingerprint, message); 1299} 1300 1301// This tests serialization of SDP with a=fingerprint with no a=crypto, as would 1302// be the case in a DTLS answer. 1303TEST_F(WebRtcSdpTest, SerializeSessionDescriptionWithFingerprintNoCryptos) { 1304 AddFingerprint(); 1305 RemoveCryptos(); 1306 JsepSessionDescription jdesc_with_fingerprint(kDummyString); 1307 ASSERT_TRUE(jdesc_with_fingerprint.Initialize(desc_.Copy(), 1308 kSessionId, kSessionVersion)); 1309 std::string message = webrtc::SdpSerialize(jdesc_with_fingerprint); 1310 1311 std::string sdp_with_fingerprint = kSdpString; 1312 Replace(kAttributeCryptoVoice, "", &sdp_with_fingerprint); 1313 Replace(kAttributeCryptoVideo, "", &sdp_with_fingerprint); 1314 InjectAfter(kAttributeIcePwdVoice, 1315 kFingerprint, &sdp_with_fingerprint); 1316 InjectAfter(kAttributeIcePwdVideo, 1317 kFingerprint, &sdp_with_fingerprint); 1318 1319 EXPECT_EQ(sdp_with_fingerprint, message); 1320} 1321 1322TEST_F(WebRtcSdpTest, SerializeSessionDescriptionWithoutCandidates) { 1323 // JsepSessionDescription with desc but without candidates. 1324 JsepSessionDescription jdesc_no_candidates(kDummyString); 1325 ASSERT_TRUE(jdesc_no_candidates.Initialize(desc_.Copy(), 1326 kSessionId, kSessionVersion)); 1327 std::string message = webrtc::SdpSerialize(jdesc_no_candidates); 1328 EXPECT_EQ(std::string(kSdpString), message); 1329} 1330 1331TEST_F(WebRtcSdpTest, SerializeSessionDescriptionWithBundle) { 1332 ContentGroup group(cricket::GROUP_TYPE_BUNDLE); 1333 group.AddContentName(kAudioContentName); 1334 group.AddContentName(kVideoContentName); 1335 desc_.AddGroup(group); 1336 ASSERT_TRUE(jdesc_.Initialize(desc_.Copy(), 1337 jdesc_.session_id(), 1338 jdesc_.session_version())); 1339 std::string message = webrtc::SdpSerialize(jdesc_); 1340 std::string sdp_with_bundle = kSdpFullString; 1341 InjectAfter(kSessionTime, 1342 "a=group:BUNDLE audio_content_name video_content_name\r\n", 1343 &sdp_with_bundle); 1344 EXPECT_EQ(sdp_with_bundle, message); 1345} 1346 1347TEST_F(WebRtcSdpTest, SerializeSessionDescriptionWithBandwidth) { 1348 VideoContentDescription* vcd = static_cast<VideoContentDescription*>( 1349 GetFirstVideoContent(&desc_)->description); 1350 vcd->set_bandwidth(100 * 1000); 1351 AudioContentDescription* acd = static_cast<AudioContentDescription*>( 1352 GetFirstAudioContent(&desc_)->description); 1353 acd->set_bandwidth(50 * 1000); 1354 ASSERT_TRUE(jdesc_.Initialize(desc_.Copy(), 1355 jdesc_.session_id(), 1356 jdesc_.session_version())); 1357 std::string message = webrtc::SdpSerialize(jdesc_); 1358 std::string sdp_with_bandwidth = kSdpFullString; 1359 InjectAfter("a=mid:video_content_name\r\na=sendrecv\r\n", 1360 "b=AS:100\r\n", 1361 &sdp_with_bandwidth); 1362 InjectAfter("a=mid:audio_content_name\r\na=sendrecv\r\n", 1363 "b=AS:50\r\n", 1364 &sdp_with_bandwidth); 1365 EXPECT_EQ(sdp_with_bandwidth, message); 1366} 1367 1368TEST_F(WebRtcSdpTest, SerializeSessionDescriptionWithIceOptions) { 1369 std::vector<std::string> transport_options; 1370 transport_options.push_back(kIceOption1); 1371 transport_options.push_back(kIceOption3); 1372 AddIceOptions(kAudioContentName, transport_options); 1373 transport_options.clear(); 1374 transport_options.push_back(kIceOption2); 1375 transport_options.push_back(kIceOption3); 1376 AddIceOptions(kVideoContentName, transport_options); 1377 ASSERT_TRUE(jdesc_.Initialize(desc_.Copy(), 1378 jdesc_.session_id(), 1379 jdesc_.session_version())); 1380 std::string message = webrtc::SdpSerialize(jdesc_); 1381 std::string sdp_with_ice_options = kSdpFullString; 1382 InjectAfter(kAttributeIcePwdVoice, 1383 "a=ice-options:iceoption1 iceoption3\r\n", 1384 &sdp_with_ice_options); 1385 InjectAfter(kAttributeIcePwdVideo, 1386 "a=ice-options:iceoption2 iceoption3\r\n", 1387 &sdp_with_ice_options); 1388 EXPECT_EQ(sdp_with_ice_options, message); 1389} 1390 1391TEST_F(WebRtcSdpTest, SerializeSessionDescriptionWithRecvOnlyContent) { 1392 EXPECT_TRUE(TestSerializeDirection(cricket::MD_RECVONLY)); 1393} 1394 1395TEST_F(WebRtcSdpTest, SerializeSessionDescriptionWithSendOnlyContent) { 1396 EXPECT_TRUE(TestSerializeDirection(cricket::MD_SENDONLY)); 1397} 1398 1399TEST_F(WebRtcSdpTest, SerializeSessionDescriptionWithInactiveContent) { 1400 EXPECT_TRUE(TestSerializeDirection(cricket::MD_INACTIVE)); 1401} 1402 1403TEST_F(WebRtcSdpTest, SerializeSessionDescriptionWithAudioRejected) { 1404 EXPECT_TRUE(TestSerializeRejected(true, false)); 1405} 1406 1407TEST_F(WebRtcSdpTest, SerializeSessionDescriptionWithVideoRejected) { 1408 EXPECT_TRUE(TestSerializeRejected(false, true)); 1409} 1410 1411TEST_F(WebRtcSdpTest, SerializeSessionDescriptionWithAudioVideoRejected) { 1412 EXPECT_TRUE(TestSerializeRejected(true, true)); 1413} 1414 1415TEST_F(WebRtcSdpTest, SerializeSessionDescriptionWithRtpDataChannel) { 1416 AddRtpDataChannel(); 1417 JsepSessionDescription jsep_desc(kDummyString); 1418 1419 ASSERT_TRUE(jsep_desc.Initialize(desc_.Copy(), kSessionId, kSessionVersion)); 1420 std::string message = webrtc::SdpSerialize(jsep_desc); 1421 1422 std::string expected_sdp = kSdpString; 1423 expected_sdp.append(kSdpRtpDataChannelString); 1424 EXPECT_EQ(expected_sdp, message); 1425} 1426 1427TEST_F(WebRtcSdpTest, SerializeSessionDescriptionWithSctpDataChannel) { 1428 AddSctpDataChannel(); 1429 JsepSessionDescription jsep_desc(kDummyString); 1430 1431 ASSERT_TRUE(jsep_desc.Initialize(desc_.Copy(), kSessionId, kSessionVersion)); 1432 std::string message = webrtc::SdpSerialize(jsep_desc); 1433 1434 std::string expected_sdp = kSdpString; 1435 expected_sdp.append(kSdpSctpDataChannelString); 1436 EXPECT_EQ(message, expected_sdp); 1437} 1438 1439TEST_F(WebRtcSdpTest, SerializeSessionDescriptionWithDataChannelAndBandwidth) { 1440 AddRtpDataChannel(); 1441 data_desc_->set_bandwidth(100*1000); 1442 JsepSessionDescription jsep_desc(kDummyString); 1443 1444 ASSERT_TRUE(jsep_desc.Initialize(desc_.Copy(), kSessionId, kSessionVersion)); 1445 std::string message = webrtc::SdpSerialize(jsep_desc); 1446 1447 std::string expected_sdp = kSdpString; 1448 expected_sdp.append(kSdpRtpDataChannelString); 1449 // We want to test that serializing data content ignores bandwidth 1450 // settings (it should always be the default). Thus, we don't do 1451 // the following: 1452 // TODO(pthatcher): We need to temporarily allow the SDP to control 1453 // this for backwards-compatibility. Once we don't need that any 1454 // more, remove this. 1455 InjectAfter("a=mid:data_content_name\r\na=sendrecv\r\n", 1456 "b=AS:100\r\n", 1457 &expected_sdp); 1458 EXPECT_EQ(expected_sdp, message); 1459} 1460 1461TEST_F(WebRtcSdpTest, SerializeSessionDescriptionWithExtmap) { 1462 AddExtmap(); 1463 JsepSessionDescription desc_with_extmap("dummy"); 1464 ASSERT_TRUE(desc_with_extmap.Initialize(desc_.Copy(), 1465 kSessionId, kSessionVersion)); 1466 std::string message = webrtc::SdpSerialize(desc_with_extmap); 1467 1468 std::string sdp_with_extmap = kSdpString; 1469 InjectAfter("a=mid:audio_content_name\r\n", 1470 kExtmap, &sdp_with_extmap); 1471 InjectAfter("a=mid:video_content_name\r\n", 1472 kExtmap, &sdp_with_extmap); 1473 1474 EXPECT_EQ(sdp_with_extmap, message); 1475} 1476 1477 1478TEST_F(WebRtcSdpTest, SerializeCandidates) { 1479 std::string message = webrtc::SdpSerializeCandidate(*jcandidate_); 1480 EXPECT_EQ(std::string(kSdpOneCandidate), message); 1481} 1482 1483TEST_F(WebRtcSdpTest, DeserializeSessionDescription) { 1484 JsepSessionDescription jdesc(kDummyString); 1485 // Deserialize 1486 EXPECT_TRUE(SdpDeserialize(kSdpFullString, &jdesc)); 1487 // Verify 1488 EXPECT_TRUE(CompareSessionDescription(jdesc_, jdesc)); 1489} 1490 1491TEST_F(WebRtcSdpTest, DeserializeSessionDescriptionWithoutMline) { 1492 JsepSessionDescription jdesc(kDummyString); 1493 const char kSdpWithoutMline[] = 1494 "v=0\r\n" 1495 "o=- 18446744069414584320 18446462598732840960 IN IP4 127.0.0.1\r\n" 1496 "s=-\r\n" 1497 "t=0 0\r\n" 1498 "a=msid-semantic: WMS local_stream_1 local_stream_2\r\n"; 1499 // Deserialize 1500 EXPECT_TRUE(SdpDeserialize(kSdpWithoutMline, &jdesc)); 1501 EXPECT_EQ(0u, jdesc.description()->contents().size()); 1502} 1503 1504TEST_F(WebRtcSdpTest, DeserializeSessionDescriptionWithoutCarriageReturn) { 1505 JsepSessionDescription jdesc(kDummyString); 1506 std::string sdp_without_carriage_return = kSdpFullString; 1507 Replace("\r\n", "\n", &sdp_without_carriage_return); 1508 // Deserialize 1509 EXPECT_TRUE(SdpDeserialize(sdp_without_carriage_return, &jdesc)); 1510 // Verify 1511 EXPECT_TRUE(CompareSessionDescription(jdesc_, jdesc)); 1512} 1513 1514TEST_F(WebRtcSdpTest, DeserializeSessionDescriptionWithoutCandidates) { 1515 // SessionDescription with desc but without candidates. 1516 JsepSessionDescription jdesc_no_candidates(kDummyString); 1517 ASSERT_TRUE(jdesc_no_candidates.Initialize(desc_.Copy(), 1518 kSessionId, kSessionVersion)); 1519 JsepSessionDescription new_jdesc(kDummyString); 1520 EXPECT_TRUE(SdpDeserialize(kSdpString, &new_jdesc)); 1521 EXPECT_TRUE(CompareSessionDescription(jdesc_no_candidates, new_jdesc)); 1522} 1523 1524TEST_F(WebRtcSdpTest, DeserializeSessionDescriptionWithoutRtpmap) { 1525 static const char kSdpNoRtpmapString[] = 1526 "v=0\r\n" 1527 "o=- 11 22 IN IP4 127.0.0.1\r\n" 1528 "s=-\r\n" 1529 "t=0 0\r\n" 1530 "m=audio 49232 RTP/AVP 0 18 103\r\n" 1531 // Codec that doesn't appear in the m= line will be ignored. 1532 "a=rtpmap:104 CELT/32000/2\r\n" 1533 // The rtpmap line for static payload codec is optional. 1534 "a=rtpmap:18 G729/16000\r\n" 1535 "a=rtpmap:103 ISAC/16000\r\n"; 1536 1537 JsepSessionDescription jdesc(kDummyString); 1538 EXPECT_TRUE(SdpDeserialize(kSdpNoRtpmapString, &jdesc)); 1539 cricket::AudioContentDescription* audio = 1540 static_cast<AudioContentDescription*>( 1541 jdesc.description()->GetContentDescriptionByName(cricket::CN_AUDIO)); 1542 AudioCodecs ref_codecs; 1543 // The codecs in the AudioContentDescription will be sorted by preference. 1544 ref_codecs.push_back(AudioCodec(0, "PCMU", 8000, 0, 1, 3)); 1545 ref_codecs.push_back(AudioCodec(18, "G729", 16000, 0, 1, 2)); 1546 ref_codecs.push_back(AudioCodec(103, "ISAC", 16000, 32000, 1, 1)); 1547 EXPECT_EQ(ref_codecs, audio->codecs()); 1548} 1549 1550// Ensure that we can deserialize SDP with a=fingerprint properly. 1551TEST_F(WebRtcSdpTest, DeserializeJsepSessionDescriptionWithFingerprint) { 1552 // Add a DTLS a=fingerprint attribute to our session description. 1553 AddFingerprint(); 1554 JsepSessionDescription new_jdesc(kDummyString); 1555 ASSERT_TRUE(new_jdesc.Initialize(desc_.Copy(), 1556 jdesc_.session_id(), 1557 jdesc_.session_version())); 1558 1559 JsepSessionDescription jdesc_with_fingerprint(kDummyString); 1560 std::string sdp_with_fingerprint = kSdpString; 1561 InjectAfter(kAttributeIcePwdVoice, kFingerprint, &sdp_with_fingerprint); 1562 InjectAfter(kAttributeIcePwdVideo, kFingerprint, &sdp_with_fingerprint); 1563 EXPECT_TRUE(SdpDeserialize(sdp_with_fingerprint, &jdesc_with_fingerprint)); 1564 EXPECT_TRUE(CompareSessionDescription(jdesc_with_fingerprint, new_jdesc)); 1565} 1566 1567TEST_F(WebRtcSdpTest, DeserializeSessionDescriptionWithBundle) { 1568 JsepSessionDescription jdesc_with_bundle(kDummyString); 1569 std::string sdp_with_bundle = kSdpFullString; 1570 InjectAfter(kSessionTime, 1571 "a=group:BUNDLE audio_content_name video_content_name\r\n", 1572 &sdp_with_bundle); 1573 EXPECT_TRUE(SdpDeserialize(sdp_with_bundle, &jdesc_with_bundle)); 1574 ContentGroup group(cricket::GROUP_TYPE_BUNDLE); 1575 group.AddContentName(kAudioContentName); 1576 group.AddContentName(kVideoContentName); 1577 desc_.AddGroup(group); 1578 ASSERT_TRUE(jdesc_.Initialize(desc_.Copy(), 1579 jdesc_.session_id(), 1580 jdesc_.session_version())); 1581 EXPECT_TRUE(CompareSessionDescription(jdesc_, jdesc_with_bundle)); 1582} 1583 1584TEST_F(WebRtcSdpTest, DeserializeSessionDescriptionWithBandwidth) { 1585 JsepSessionDescription jdesc_with_bandwidth(kDummyString); 1586 std::string sdp_with_bandwidth = kSdpFullString; 1587 InjectAfter("a=mid:video_content_name\r\na=sendrecv\r\n", 1588 "b=AS:100\r\n", 1589 &sdp_with_bandwidth); 1590 InjectAfter("a=mid:audio_content_name\r\na=sendrecv\r\n", 1591 "b=AS:50\r\n", 1592 &sdp_with_bandwidth); 1593 EXPECT_TRUE( 1594 SdpDeserialize(sdp_with_bandwidth, &jdesc_with_bandwidth)); 1595 VideoContentDescription* vcd = static_cast<VideoContentDescription*>( 1596 GetFirstVideoContent(&desc_)->description); 1597 vcd->set_bandwidth(100 * 1000); 1598 AudioContentDescription* acd = static_cast<AudioContentDescription*>( 1599 GetFirstAudioContent(&desc_)->description); 1600 acd->set_bandwidth(50 * 1000); 1601 ASSERT_TRUE(jdesc_.Initialize(desc_.Copy(), 1602 jdesc_.session_id(), 1603 jdesc_.session_version())); 1604 EXPECT_TRUE(CompareSessionDescription(jdesc_, jdesc_with_bandwidth)); 1605} 1606 1607TEST_F(WebRtcSdpTest, DeserializeSessionDescriptionWithIceOptions) { 1608 JsepSessionDescription jdesc_with_ice_options(kDummyString); 1609 std::string sdp_with_ice_options = kSdpFullString; 1610 InjectAfter(kSessionTime, 1611 "a=ice-options:iceoption3\r\n", 1612 &sdp_with_ice_options); 1613 InjectAfter(kAttributeIcePwdVoice, 1614 "a=ice-options:iceoption1\r\n", 1615 &sdp_with_ice_options); 1616 InjectAfter(kAttributeIcePwdVideo, 1617 "a=ice-options:iceoption2\r\n", 1618 &sdp_with_ice_options); 1619 EXPECT_TRUE(SdpDeserialize(sdp_with_ice_options, &jdesc_with_ice_options)); 1620 std::vector<std::string> transport_options; 1621 transport_options.push_back(kIceOption3); 1622 transport_options.push_back(kIceOption1); 1623 AddIceOptions(kAudioContentName, transport_options); 1624 transport_options.clear(); 1625 transport_options.push_back(kIceOption3); 1626 transport_options.push_back(kIceOption2); 1627 AddIceOptions(kVideoContentName, transport_options); 1628 ASSERT_TRUE(jdesc_.Initialize(desc_.Copy(), 1629 jdesc_.session_id(), 1630 jdesc_.session_version())); 1631 EXPECT_TRUE(CompareSessionDescription(jdesc_, jdesc_with_ice_options)); 1632} 1633 1634TEST_F(WebRtcSdpTest, DeserializeSessionDescriptionWithUfragPwd) { 1635 // Remove the original ice-ufrag and ice-pwd 1636 JsepSessionDescription jdesc_with_ufrag_pwd(kDummyString); 1637 std::string sdp_with_ufrag_pwd = kSdpFullString; 1638 EXPECT_TRUE(RemoveCandidateUfragPwd(&sdp_with_ufrag_pwd)); 1639 // Add session level ufrag and pwd 1640 InjectAfter(kSessionTime, 1641 "a=ice-pwd:session+level+icepwd\r\n" 1642 "a=ice-ufrag:session+level+iceufrag\r\n", 1643 &sdp_with_ufrag_pwd); 1644 // Add media level ufrag and pwd for audio 1645 InjectAfter("a=mid:audio_content_name\r\n", 1646 "a=ice-pwd:media+level+icepwd\r\na=ice-ufrag:media+level+iceufrag\r\n", 1647 &sdp_with_ufrag_pwd); 1648 // Update the candidate ufrag and pwd to the expected ones. 1649 EXPECT_TRUE(UpdateCandidateUfragPwd(&jdesc_, 0, 1650 "media+level+iceufrag", "media+level+icepwd")); 1651 EXPECT_TRUE(UpdateCandidateUfragPwd(&jdesc_, 1, 1652 "session+level+iceufrag", "session+level+icepwd")); 1653 EXPECT_TRUE(SdpDeserialize(sdp_with_ufrag_pwd, &jdesc_with_ufrag_pwd)); 1654 EXPECT_TRUE(CompareSessionDescription(jdesc_, jdesc_with_ufrag_pwd)); 1655} 1656 1657 1658TEST_F(WebRtcSdpTest, DeserializeSessionDescriptionWithRecvOnlyContent) { 1659 EXPECT_TRUE(TestDeserializeDirection(cricket::MD_RECVONLY)); 1660} 1661 1662TEST_F(WebRtcSdpTest, DeserializeSessionDescriptionWithSendOnlyContent) { 1663 EXPECT_TRUE(TestDeserializeDirection(cricket::MD_SENDONLY)); 1664} 1665 1666TEST_F(WebRtcSdpTest, DeserializeSessionDescriptionWithInactiveContent) { 1667 EXPECT_TRUE(TestDeserializeDirection(cricket::MD_INACTIVE)); 1668} 1669 1670TEST_F(WebRtcSdpTest, DeserializeSessionDescriptionWithRejectedAudio) { 1671 EXPECT_TRUE(TestDeserializeRejected(true, false)); 1672} 1673 1674TEST_F(WebRtcSdpTest, DeserializeSessionDescriptionWithRejectedVideo) { 1675 EXPECT_TRUE(TestDeserializeRejected(false, true)); 1676} 1677 1678TEST_F(WebRtcSdpTest, DeserializeSessionDescriptionWithRejectedAudioVideo) { 1679 EXPECT_TRUE(TestDeserializeRejected(true, true)); 1680} 1681 1682// Tests that we can still handle the sdp uses mslabel and label instead of 1683// msid for backward compatibility. 1684TEST_F(WebRtcSdpTest, DeserializeSessionDescriptionWithoutMsid) { 1685 JsepSessionDescription jdesc(kDummyString); 1686 std::string sdp_without_msid = kSdpFullString; 1687 Replace("msid", "xmsid", &sdp_without_msid); 1688 // Deserialize 1689 EXPECT_TRUE(SdpDeserialize(sdp_without_msid, &jdesc)); 1690 // Verify 1691 EXPECT_TRUE(CompareSessionDescription(jdesc_, jdesc)); 1692} 1693 1694TEST_F(WebRtcSdpTest, DeserializeCandidate) { 1695 JsepIceCandidate jcandidate(kDummyMid, kDummyIndex); 1696 1697 std::string sdp = kSdpOneCandidate; 1698 EXPECT_TRUE(SdpDeserializeCandidate(sdp, &jcandidate)); 1699 EXPECT_EQ(kDummyMid, jcandidate.sdp_mid()); 1700 EXPECT_EQ(kDummyIndex, jcandidate.sdp_mline_index()); 1701 EXPECT_TRUE(jcandidate.candidate().IsEquivalent(jcandidate_->candidate())); 1702 1703 // Candidate line without generation extension. 1704 sdp = kSdpOneCandidate; 1705 Replace(" generation 2", "", &sdp); 1706 EXPECT_TRUE(SdpDeserializeCandidate(sdp, &jcandidate)); 1707 EXPECT_EQ(kDummyMid, jcandidate.sdp_mid()); 1708 EXPECT_EQ(kDummyIndex, jcandidate.sdp_mline_index()); 1709 Candidate expected = jcandidate_->candidate(); 1710 expected.set_generation(0); 1711 EXPECT_TRUE(jcandidate.candidate().IsEquivalent(expected)); 1712 1713 // Multiple candidate lines. 1714 // Only the first line will be deserialized. The rest will be ignored. 1715 sdp = kSdpOneCandidate; 1716 sdp.append("a=candidate:1 2 tcp 1234 192.168.1.100 5678 typ host\r\n"); 1717 EXPECT_TRUE(SdpDeserializeCandidate(sdp, &jcandidate)); 1718 EXPECT_EQ(kDummyMid, jcandidate.sdp_mid()); 1719 EXPECT_EQ(kDummyIndex, jcandidate.sdp_mline_index()); 1720 EXPECT_TRUE(jcandidate.candidate().IsEquivalent(jcandidate_->candidate())); 1721} 1722 1723// This test verifies the deserialization of candidate-attribute 1724// as per RFC 5245. Candiate-attribute will be of the format 1725// candidate:<blah>. This format will be used when candidates 1726// are trickled. 1727TEST_F(WebRtcSdpTest, DeserializeRawCandidateAttribute) { 1728 JsepIceCandidate jcandidate(kDummyMid, kDummyIndex); 1729 1730 std::string candidate_attribute = kRawCandidate; 1731 EXPECT_TRUE(SdpDeserializeCandidate(candidate_attribute, &jcandidate)); 1732 EXPECT_EQ(kDummyMid, jcandidate.sdp_mid()); 1733 EXPECT_EQ(kDummyIndex, jcandidate.sdp_mline_index()); 1734 EXPECT_TRUE(jcandidate.candidate().IsEquivalent(jcandidate_->candidate())); 1735 EXPECT_EQ(2u, jcandidate.candidate().generation()); 1736 1737 // Candidate line without generation extension. 1738 candidate_attribute = kRawCandidate; 1739 Replace(" generation 2", "", &candidate_attribute); 1740 EXPECT_TRUE(SdpDeserializeCandidate(candidate_attribute, &jcandidate)); 1741 EXPECT_EQ(kDummyMid, jcandidate.sdp_mid()); 1742 EXPECT_EQ(kDummyIndex, jcandidate.sdp_mline_index()); 1743 Candidate expected = jcandidate_->candidate(); 1744 expected.set_generation(0); 1745 EXPECT_TRUE(jcandidate.candidate().IsEquivalent(expected)); 1746 1747 // Candidate line without candidate: 1748 candidate_attribute = kRawCandidate; 1749 Replace("candidate:", "", &candidate_attribute); 1750 EXPECT_FALSE(SdpDeserializeCandidate(candidate_attribute, &jcandidate)); 1751 1752 // Concatenating additional candidate. Expecting deserialization to fail. 1753 candidate_attribute = kRawCandidate; 1754 candidate_attribute.append("candidate:1 2 udp 1234 192.168.1.1 typ host"); 1755 EXPECT_FALSE(SdpDeserializeCandidate(candidate_attribute, &jcandidate)); 1756} 1757 1758TEST_F(WebRtcSdpTest, DeserializeSdpWithRtpDataChannels) { 1759 AddRtpDataChannel(); 1760 JsepSessionDescription jdesc(kDummyString); 1761 ASSERT_TRUE(jdesc.Initialize(desc_.Copy(), kSessionId, kSessionVersion)); 1762 1763 std::string sdp_with_data = kSdpString; 1764 sdp_with_data.append(kSdpRtpDataChannelString); 1765 JsepSessionDescription jdesc_output(kDummyString); 1766 1767 // Deserialize 1768 EXPECT_TRUE(SdpDeserialize(sdp_with_data, &jdesc_output)); 1769 // Verify 1770 EXPECT_TRUE(CompareSessionDescription(jdesc, jdesc_output)); 1771} 1772 1773TEST_F(WebRtcSdpTest, DeserializeSdpWithSctpDataChannels) { 1774 AddSctpDataChannel(); 1775 JsepSessionDescription jdesc(kDummyString); 1776 ASSERT_TRUE(jdesc.Initialize(desc_.Copy(), kSessionId, kSessionVersion)); 1777 1778 std::string sdp_with_data = kSdpString; 1779 sdp_with_data.append(kSdpSctpDataChannelString); 1780 JsepSessionDescription jdesc_output(kDummyString); 1781 1782 EXPECT_TRUE(SdpDeserialize(sdp_with_data, &jdesc_output)); 1783 EXPECT_TRUE(CompareSessionDescription(jdesc, jdesc_output)); 1784} 1785 1786// For crbug/344475. 1787TEST_F(WebRtcSdpTest, DeserializeSdpWithCorruptedSctpDataChannels) { 1788 std::string sdp_with_data = kSdpString; 1789 sdp_with_data.append(kSdpSctpDataChannelString); 1790 // Remove the "\n" at the end. 1791 sdp_with_data = sdp_with_data.substr(0, sdp_with_data.size() - 1); 1792 JsepSessionDescription jdesc_output(kDummyString); 1793 1794 EXPECT_FALSE(SdpDeserialize(sdp_with_data, &jdesc_output)); 1795 // No crash is a pass. 1796} 1797 1798TEST_F(WebRtcSdpTest, DeserializeSdpWithSctpDataChannelAndNewPort) { 1799 AddSctpDataChannel(); 1800 const uint16 kUnusualSctpPort = 9556; 1801 char default_portstr[16]; 1802 char unusual_portstr[16]; 1803 talk_base::sprintfn(default_portstr, sizeof(default_portstr), "%d", 1804 kDefaultSctpPort); 1805 talk_base::sprintfn(unusual_portstr, sizeof(unusual_portstr), "%d", 1806 kUnusualSctpPort); 1807 1808 JsepSessionDescription jdesc(kDummyString); 1809 // take our pre-built session description and change the SCTP port. 1810 cricket::SessionDescription* mutant = desc_.Copy(); 1811 DataContentDescription* dcdesc = static_cast<DataContentDescription*>( 1812 mutant->GetContentDescriptionByName(kDataContentName)); 1813 std::vector<cricket::DataCodec> codecs(dcdesc->codecs()); 1814 EXPECT_EQ(codecs.size(), 1UL); 1815 EXPECT_EQ(codecs[0].id, cricket::kGoogleSctpDataCodecId); 1816 codecs[0].SetParam(cricket::kCodecParamPort, kUnusualSctpPort); 1817 1818 // note: mutant's owned by jdesc now. 1819 ASSERT_TRUE(jdesc.Initialize(mutant, kSessionId, kSessionVersion)); 1820 mutant = NULL; 1821 1822 std::string sdp_with_data = kSdpString; 1823 sdp_with_data.append(kSdpSctpDataChannelString); 1824 talk_base::replace_substrs(default_portstr, strlen(default_portstr), 1825 unusual_portstr, strlen(unusual_portstr), 1826 &sdp_with_data); 1827 JsepSessionDescription jdesc_output(kDummyString); 1828 1829 EXPECT_TRUE(SdpDeserialize(sdp_with_data, &jdesc_output)); 1830 EXPECT_TRUE(CompareSessionDescription(jdesc, jdesc_output)); 1831} 1832 1833TEST_F(WebRtcSdpTest, DeserializeSdpWithRtpDataChannelsAndBandwidth) { 1834 AddRtpDataChannel(); 1835 JsepSessionDescription jdesc(kDummyString); 1836 // We want to test that deserializing data content ignores bandwidth 1837 // settings (it should always be the default). Thus, we don't do 1838 // the following: 1839 // TODO(pthatcher): We need to temporarily allow the SDP to control 1840 // this for backwards-compatibility. Once we don't need that any 1841 // more, remove this. 1842 DataContentDescription* dcd = static_cast<DataContentDescription*>( 1843 GetFirstDataContent(&desc_)->description); 1844 dcd->set_bandwidth(100 * 1000); 1845 ASSERT_TRUE(jdesc.Initialize(desc_.Copy(), kSessionId, kSessionVersion)); 1846 1847 std::string sdp_with_bandwidth = kSdpString; 1848 sdp_with_bandwidth.append(kSdpRtpDataChannelString); 1849 InjectAfter("a=mid:data_content_name\r\n", 1850 "b=AS:100\r\n", 1851 &sdp_with_bandwidth); 1852 JsepSessionDescription jdesc_with_bandwidth(kDummyString); 1853 1854 EXPECT_TRUE( 1855 SdpDeserialize(sdp_with_bandwidth, &jdesc_with_bandwidth)); 1856 EXPECT_TRUE(CompareSessionDescription(jdesc, jdesc_with_bandwidth)); 1857} 1858 1859TEST_F(WebRtcSdpTest, DeserializeSessionDescriptionWithSessionLevelExtmap) { 1860 TestDeserializeExtmap(true, false); 1861} 1862 1863TEST_F(WebRtcSdpTest, DeserializeSessionDescriptionWithMediaLevelExtmap) { 1864 TestDeserializeExtmap(false, true); 1865} 1866 1867TEST_F(WebRtcSdpTest, DeserializeSessionDescriptionWithInvalidExtmap) { 1868 TestDeserializeExtmap(true, true); 1869} 1870 1871TEST_F(WebRtcSdpTest, DeserializeSessionDescriptionWithoutEndLineBreak) { 1872 JsepSessionDescription jdesc(kDummyString); 1873 std::string sdp = kSdpFullString; 1874 sdp = sdp.substr(0, sdp.size() - 2); // Remove \r\n at the end. 1875 // Deserialize 1876 SdpParseError error; 1877 EXPECT_FALSE(webrtc::SdpDeserialize(sdp, &jdesc, &error)); 1878 const std::string lastline = "a=ssrc:6 label:video_track_id_3"; 1879 EXPECT_EQ(lastline, error.line); 1880 EXPECT_EQ("Invalid SDP line.", error.description); 1881} 1882 1883TEST_F(WebRtcSdpTest, DeserializeCandidateWithDifferentTransport) { 1884 JsepIceCandidate jcandidate(kDummyMid, kDummyIndex); 1885 std::string new_sdp = kSdpOneCandidate; 1886 Replace("udp", "unsupported_transport", &new_sdp); 1887 EXPECT_FALSE(SdpDeserializeCandidate(new_sdp, &jcandidate)); 1888 new_sdp = kSdpOneCandidate; 1889 Replace("udp", "uDP", &new_sdp); 1890 EXPECT_TRUE(SdpDeserializeCandidate(new_sdp, &jcandidate)); 1891 EXPECT_EQ(kDummyMid, jcandidate.sdp_mid()); 1892 EXPECT_EQ(kDummyIndex, jcandidate.sdp_mline_index()); 1893 EXPECT_TRUE(jcandidate.candidate().IsEquivalent(jcandidate_->candidate())); 1894} 1895 1896TEST_F(WebRtcSdpTest, DeserializeCandidateOldFormat) { 1897 JsepIceCandidate jcandidate(kDummyMid, kDummyIndex); 1898 EXPECT_TRUE(SdpDeserializeCandidate(kSdpOneCandidateOldFormat,&jcandidate)); 1899 EXPECT_EQ(kDummyMid, jcandidate.sdp_mid()); 1900 EXPECT_EQ(kDummyIndex, jcandidate.sdp_mline_index()); 1901 Candidate ref_candidate = jcandidate_->candidate(); 1902 ref_candidate.set_username("user_rtp"); 1903 ref_candidate.set_password("password_rtp"); 1904 EXPECT_TRUE(jcandidate.candidate().IsEquivalent(ref_candidate)); 1905} 1906 1907TEST_F(WebRtcSdpTest, DeserializeBrokenSdp) { 1908 const char kSdpDestroyer[] = "!@#$%^&"; 1909 const char kSdpInvalidLine1[] = " =candidate"; 1910 const char kSdpInvalidLine2[] = "a+candidate"; 1911 const char kSdpInvalidLine3[] = "a= candidate"; 1912 // Broken fingerprint. 1913 const char kSdpInvalidLine4[] = "a=fingerprint:sha-1 " 1914 "4AAD:B9:B1:3F:82:18:3B:54:02:12:DF:3E:5D:49:6B:19:E5:7C:AB"; 1915 // Extra field. 1916 const char kSdpInvalidLine5[] = "a=fingerprint:sha-1 " 1917 "4A:AD:B9:B1:3F:82:18:3B:54:02:12:DF:3E:5D:49:6B:19:E5:7C:AB XXX"; 1918 // Missing space. 1919 const char kSdpInvalidLine6[] = "a=fingerprint:sha-1" 1920 "4A:AD:B9:B1:3F:82:18:3B:54:02:12:DF:3E:5D:49:6B:19:E5:7C:AB"; 1921 // MD5 is not allowed in fingerprints. 1922 const char kSdpInvalidLine7[] = "a=fingerprint:md5 " 1923 "4A:AD:B9:B1:3F:82:18:3B:54:02:12:DF:3E:5D:49:6B"; 1924 1925 // Broken session description 1926 ReplaceAndTryToParse("v=", kSdpDestroyer); 1927 ReplaceAndTryToParse("o=", kSdpDestroyer); 1928 ReplaceAndTryToParse("s=-", kSdpDestroyer); 1929 // Broken time description 1930 ReplaceAndTryToParse("t=", kSdpDestroyer); 1931 1932 // Broken media description 1933 ReplaceAndTryToParse("m=audio", "c=IN IP4 74.125.224.39"); 1934 ReplaceAndTryToParse("m=video", kSdpDestroyer); 1935 1936 // Invalid lines 1937 ReplaceAndTryToParse("a=candidate", kSdpInvalidLine1); 1938 ReplaceAndTryToParse("a=candidate", kSdpInvalidLine2); 1939 ReplaceAndTryToParse("a=candidate", kSdpInvalidLine3); 1940 1941 // Bogus fingerprint replacing a=sendrev. We selected this attribute 1942 // because it's orthogonal to what we are replacing and hence 1943 // safe. 1944 ReplaceAndTryToParse("a=sendrecv", kSdpInvalidLine4); 1945 ReplaceAndTryToParse("a=sendrecv", kSdpInvalidLine5); 1946 ReplaceAndTryToParse("a=sendrecv", kSdpInvalidLine6); 1947 ReplaceAndTryToParse("a=sendrecv", kSdpInvalidLine7); 1948} 1949 1950TEST_F(WebRtcSdpTest, DeserializeSdpWithReorderedPltypes) { 1951 JsepSessionDescription jdesc_output(kDummyString); 1952 1953 const char kSdpWithReorderedPlTypesString[] = 1954 "v=0\r\n" 1955 "o=- 18446744069414584320 18446462598732840960 IN IP4 127.0.0.1\r\n" 1956 "s=-\r\n" 1957 "t=0 0\r\n" 1958 "m=audio 1 RTP/SAVPF 104 103\r\n" // Pl type 104 preferred. 1959 "a=rtpmap:111 opus/48000/2\r\n" // Pltype 111 listed before 103 and 104 1960 // in the map. 1961 "a=rtpmap:103 ISAC/16000\r\n" // Pltype 103 listed before 104 in the map. 1962 "a=rtpmap:104 CELT/32000/2\r\n"; 1963 1964 // Deserialize 1965 EXPECT_TRUE(SdpDeserialize(kSdpWithReorderedPlTypesString, &jdesc_output)); 1966 1967 const ContentInfo* ac = GetFirstAudioContent(jdesc_output.description()); 1968 ASSERT_TRUE(ac != NULL); 1969 const AudioContentDescription* acd = 1970 static_cast<const AudioContentDescription*>(ac->description); 1971 ASSERT_FALSE(acd->codecs().empty()); 1972 EXPECT_EQ("CELT", acd->codecs()[0].name); 1973 EXPECT_EQ(104, acd->codecs()[0].id); 1974} 1975 1976TEST_F(WebRtcSdpTest, DeserializeSerializeCodecParams) { 1977 JsepSessionDescription jdesc_output(kDummyString); 1978 CodecParams params; 1979 params.max_ptime = 40; 1980 params.ptime = 30; 1981 params.min_ptime = 10; 1982 params.sprop_stereo = 1; 1983 params.stereo = 1; 1984 params.useinband = 1; 1985 params.maxaveragebitrate = 128000; 1986 TestDeserializeCodecParams(params, &jdesc_output); 1987 TestSerialize(jdesc_output); 1988} 1989 1990TEST_F(WebRtcSdpTest, DeserializeSerializeRtcpFb) { 1991 const bool kUseWildcard = false; 1992 JsepSessionDescription jdesc_output(kDummyString); 1993 TestDeserializeRtcpFb(&jdesc_output, kUseWildcard); 1994 TestSerialize(jdesc_output); 1995} 1996 1997TEST_F(WebRtcSdpTest, DeserializeSerializeRtcpFbWildcard) { 1998 const bool kUseWildcard = true; 1999 JsepSessionDescription jdesc_output(kDummyString); 2000 TestDeserializeRtcpFb(&jdesc_output, kUseWildcard); 2001 TestSerialize(jdesc_output); 2002} 2003 2004TEST_F(WebRtcSdpTest, DeserializeVideoFmtp) { 2005 JsepSessionDescription jdesc_output(kDummyString); 2006 2007 const char kSdpWithFmtpString[] = 2008 "v=0\r\n" 2009 "o=- 18446744069414584320 18446462598732840960 IN IP4 127.0.0.1\r\n" 2010 "s=-\r\n" 2011 "t=0 0\r\n" 2012 "m=video 3457 RTP/SAVPF 120\r\n" 2013 "a=rtpmap:120 VP8/90000\r\n" 2014 "a=fmtp:120 x-google-min-bitrate=10; x-google-max-quantization=40\r\n"; 2015 2016 // Deserialize 2017 SdpParseError error; 2018 EXPECT_TRUE(webrtc::SdpDeserialize(kSdpWithFmtpString, &jdesc_output, 2019 &error)); 2020 2021 const ContentInfo* vc = GetFirstVideoContent(jdesc_output.description()); 2022 ASSERT_TRUE(vc != NULL); 2023 const VideoContentDescription* vcd = 2024 static_cast<const VideoContentDescription*>(vc->description); 2025 ASSERT_FALSE(vcd->codecs().empty()); 2026 cricket::VideoCodec vp8 = vcd->codecs()[0]; 2027 EXPECT_EQ("VP8", vp8.name); 2028 EXPECT_EQ(120, vp8.id); 2029 cricket::CodecParameterMap::iterator found = 2030 vp8.params.find("x-google-min-bitrate"); 2031 ASSERT_TRUE(found != vp8.params.end()); 2032 EXPECT_EQ(found->second, "10"); 2033 found = vp8.params.find("x-google-max-quantization"); 2034 ASSERT_TRUE(found != vp8.params.end()); 2035 EXPECT_EQ(found->second, "40"); 2036} 2037 2038TEST_F(WebRtcSdpTest, SerializeVideoFmtp) { 2039 VideoContentDescription* vcd = static_cast<VideoContentDescription*>( 2040 GetFirstVideoContent(&desc_)->description); 2041 2042 cricket::VideoCodecs codecs = vcd->codecs(); 2043 codecs[0].params["x-google-min-bitrate"] = "10"; 2044 vcd->set_codecs(codecs); 2045 2046 ASSERT_TRUE(jdesc_.Initialize(desc_.Copy(), 2047 jdesc_.session_id(), 2048 jdesc_.session_version())); 2049 std::string message = webrtc::SdpSerialize(jdesc_); 2050 std::string sdp_with_fmtp = kSdpFullString; 2051 InjectAfter("a=rtpmap:120 VP8/90000\r\n", 2052 "a=fmtp:120 x-google-min-bitrate=10\r\n", 2053 &sdp_with_fmtp); 2054 EXPECT_EQ(sdp_with_fmtp, message); 2055} 2056 2057TEST_F(WebRtcSdpTest, DeserializeSdpWithIceLite) { 2058 JsepSessionDescription jdesc_with_icelite(kDummyString); 2059 std::string sdp_with_icelite = kSdpFullString; 2060 EXPECT_TRUE(SdpDeserialize(sdp_with_icelite, &jdesc_with_icelite)); 2061 cricket::SessionDescription* desc = jdesc_with_icelite.description(); 2062 const cricket::TransportInfo* tinfo1 = 2063 desc->GetTransportInfoByName("audio_content_name"); 2064 EXPECT_EQ(cricket::ICEMODE_FULL, tinfo1->description.ice_mode); 2065 const cricket::TransportInfo* tinfo2 = 2066 desc->GetTransportInfoByName("video_content_name"); 2067 EXPECT_EQ(cricket::ICEMODE_FULL, tinfo2->description.ice_mode); 2068 InjectAfter(kSessionTime, 2069 "a=ice-lite\r\n", 2070 &sdp_with_icelite); 2071 EXPECT_TRUE(SdpDeserialize(sdp_with_icelite, &jdesc_with_icelite)); 2072 desc = jdesc_with_icelite.description(); 2073 const cricket::TransportInfo* atinfo = 2074 desc->GetTransportInfoByName("audio_content_name"); 2075 EXPECT_EQ(cricket::ICEMODE_LITE, atinfo->description.ice_mode); 2076 const cricket::TransportInfo* vtinfo = 2077 desc->GetTransportInfoByName("video_content_name"); 2078 EXPECT_EQ(cricket::ICEMODE_LITE, vtinfo->description.ice_mode); 2079} 2080 2081// Verifies that the candidates in the input SDP are parsed and serialized 2082// correctly in the output SDP. 2083TEST_F(WebRtcSdpTest, RoundTripSdpWithSctpDataChannelsWithCandidates) { 2084 std::string sdp_with_data = kSdpString; 2085 sdp_with_data.append(kSdpSctpDataChannelWithCandidatesString); 2086 JsepSessionDescription jdesc_output(kDummyString); 2087 2088 EXPECT_TRUE(SdpDeserialize(sdp_with_data, &jdesc_output)); 2089 EXPECT_EQ(sdp_with_data, webrtc::SdpSerialize(jdesc_output)); 2090} 2091 2092TEST_F(WebRtcSdpTest, SerializeDtlsSetupAttribute) { 2093 AddFingerprint(); 2094 TransportInfo audio_transport_info = 2095 *(desc_.GetTransportInfoByName(kAudioContentName)); 2096 EXPECT_EQ(cricket::CONNECTIONROLE_NONE, 2097 audio_transport_info.description.connection_role); 2098 audio_transport_info.description.connection_role = 2099 cricket::CONNECTIONROLE_ACTIVE; 2100 2101 TransportInfo video_transport_info = 2102 *(desc_.GetTransportInfoByName(kVideoContentName)); 2103 EXPECT_EQ(cricket::CONNECTIONROLE_NONE, 2104 video_transport_info.description.connection_role); 2105 video_transport_info.description.connection_role = 2106 cricket::CONNECTIONROLE_ACTIVE; 2107 2108 desc_.RemoveTransportInfoByName(kAudioContentName); 2109 desc_.RemoveTransportInfoByName(kVideoContentName); 2110 2111 desc_.AddTransportInfo(audio_transport_info); 2112 desc_.AddTransportInfo(video_transport_info); 2113 2114 ASSERT_TRUE(jdesc_.Initialize(desc_.Copy(), 2115 jdesc_.session_id(), 2116 jdesc_.session_version())); 2117 std::string message = webrtc::SdpSerialize(jdesc_); 2118 std::string sdp_with_dtlssetup = kSdpFullString; 2119 2120 // Fingerprint attribute is necessary to add DTLS setup attribute. 2121 InjectAfter(kAttributeIcePwdVoice, 2122 kFingerprint, &sdp_with_dtlssetup); 2123 InjectAfter(kAttributeIcePwdVideo, 2124 kFingerprint, &sdp_with_dtlssetup); 2125 // Now adding |setup| attribute. 2126 InjectAfter(kFingerprint, 2127 "a=setup:active\r\n", &sdp_with_dtlssetup); 2128 EXPECT_EQ(sdp_with_dtlssetup, message); 2129} 2130 2131TEST_F(WebRtcSdpTest, DeserializeDtlsSetupAttribute) { 2132 JsepSessionDescription jdesc_with_dtlssetup(kDummyString); 2133 std::string sdp_with_dtlssetup = kSdpFullString; 2134 InjectAfter(kSessionTime, 2135 "a=setup:actpass\r\n", 2136 &sdp_with_dtlssetup); 2137 EXPECT_TRUE(SdpDeserialize(sdp_with_dtlssetup, &jdesc_with_dtlssetup)); 2138 cricket::SessionDescription* desc = jdesc_with_dtlssetup.description(); 2139 const cricket::TransportInfo* atinfo = 2140 desc->GetTransportInfoByName("audio_content_name"); 2141 EXPECT_EQ(cricket::CONNECTIONROLE_ACTPASS, 2142 atinfo->description.connection_role); 2143 const cricket::TransportInfo* vtinfo = 2144 desc->GetTransportInfoByName("video_content_name"); 2145 EXPECT_EQ(cricket::CONNECTIONROLE_ACTPASS, 2146 vtinfo->description.connection_role); 2147} 2148