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