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