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