1 /* 2 * libjingle 3 * Copyright 2004 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 <string> 29 #include <vector> 30 31 #include "talk/media/base/codec.h" 32 #include "talk/media/base/testutils.h" 33 #include "talk/p2p/base/constants.h" 34 #include "talk/p2p/base/transportdescription.h" 35 #include "talk/p2p/base/transportinfo.h" 36 #include "talk/session/media/mediasession.h" 37 #include "talk/session/media/srtpfilter.h" 38 #include "webrtc/base/fakesslidentity.h" 39 #include "webrtc/base/gunit.h" 40 #include "webrtc/base/messagedigest.h" 41 #include "webrtc/base/ssladapter.h" 42 43 #ifdef HAVE_SRTP 44 #define ASSERT_CRYPTO(cd, s, cs) \ 45 ASSERT_EQ(cricket::CT_NONE, cd->crypto_required()); \ 46 ASSERT_EQ(s, cd->cryptos().size()); \ 47 ASSERT_EQ(std::string(cs), cd->cryptos()[0].cipher_suite) 48 #else 49 #define ASSERT_CRYPTO(cd, s, cs) \ 50 ASSERT_EQ(cricket::CT_NONE, cd->crypto_required()); \ 51 ASSERT_EQ(0U, cd->cryptos().size()); 52 #endif 53 54 typedef std::vector<cricket::Candidate> Candidates; 55 56 using cricket::MediaContentDescription; 57 using cricket::MediaSessionDescriptionFactory; 58 using cricket::MediaSessionOptions; 59 using cricket::MediaType; 60 using cricket::SessionDescription; 61 using cricket::SsrcGroup; 62 using cricket::StreamParams; 63 using cricket::StreamParamsVec; 64 using cricket::TransportDescription; 65 using cricket::TransportDescriptionFactory; 66 using cricket::TransportInfo; 67 using cricket::ContentInfo; 68 using cricket::CryptoParamsVec; 69 using cricket::AudioContentDescription; 70 using cricket::VideoContentDescription; 71 using cricket::DataContentDescription; 72 using cricket::GetFirstAudioContentDescription; 73 using cricket::GetFirstVideoContentDescription; 74 using cricket::GetFirstDataContentDescription; 75 using cricket::kAutoBandwidth; 76 using cricket::AudioCodec; 77 using cricket::VideoCodec; 78 using cricket::DataCodec; 79 using cricket::NS_JINGLE_RTP; 80 using cricket::MEDIA_TYPE_AUDIO; 81 using cricket::MEDIA_TYPE_VIDEO; 82 using cricket::MEDIA_TYPE_DATA; 83 using cricket::RtpHeaderExtension; 84 using cricket::SEC_DISABLED; 85 using cricket::SEC_ENABLED; 86 using cricket::SEC_REQUIRED; 87 using cricket::CS_AES_CM_128_HMAC_SHA1_32; 88 using cricket::CS_AES_CM_128_HMAC_SHA1_80; 89 90 static const AudioCodec kAudioCodecs1[] = { 91 AudioCodec(103, "ISAC", 16000, -1, 1, 6), 92 AudioCodec(102, "iLBC", 8000, 13300, 1, 5), 93 AudioCodec(0, "PCMU", 8000, 64000, 1, 4), 94 AudioCodec(8, "PCMA", 8000, 64000, 1, 3), 95 AudioCodec(117, "red", 8000, 0, 1, 2), 96 AudioCodec(107, "CN", 48000, 0, 1, 1) 97 }; 98 99 static const AudioCodec kAudioCodecs2[] = { 100 AudioCodec(126, "speex", 16000, 22000, 1, 3), 101 AudioCodec(0, "PCMU", 8000, 64000, 1, 2), 102 AudioCodec(127, "iLBC", 8000, 13300, 1, 1), 103 }; 104 105 static const AudioCodec kAudioCodecsAnswer[] = { 106 AudioCodec(102, "iLBC", 8000, 13300, 1, 5), 107 AudioCodec(0, "PCMU", 8000, 64000, 1, 4), 108 }; 109 110 static const VideoCodec kVideoCodecs1[] = { 111 VideoCodec(96, "H264-SVC", 320, 200, 30, 2), 112 VideoCodec(97, "H264", 320, 200, 30, 1) 113 }; 114 115 static const VideoCodec kVideoCodecs2[] = { 116 VideoCodec(126, "H264", 320, 200, 30, 2), 117 VideoCodec(127, "H263", 320, 200, 30, 1) 118 }; 119 120 static const VideoCodec kVideoCodecsAnswer[] = { 121 VideoCodec(97, "H264", 320, 200, 30, 1) 122 }; 123 124 static const DataCodec kDataCodecs1[] = { 125 DataCodec(98, "binary-data", 2), 126 DataCodec(99, "utf8-text", 1) 127 }; 128 129 static const DataCodec kDataCodecs2[] = { 130 DataCodec(126, "binary-data", 2), 131 DataCodec(127, "utf8-text", 1) 132 }; 133 134 static const DataCodec kDataCodecsAnswer[] = { 135 DataCodec(98, "binary-data", 2), 136 DataCodec(99, "utf8-text", 1) 137 }; 138 139 static const RtpHeaderExtension kAudioRtpExtension1[] = { 140 RtpHeaderExtension("urn:ietf:params:rtp-hdrext:ssrc-audio-level", 8), 141 RtpHeaderExtension("http://google.com/testing/audio_something", 10), 142 }; 143 144 static const RtpHeaderExtension kAudioRtpExtension2[] = { 145 RtpHeaderExtension("urn:ietf:params:rtp-hdrext:ssrc-audio-level", 2), 146 RtpHeaderExtension("http://google.com/testing/audio_something_else", 8), 147 RtpHeaderExtension("http://google.com/testing/both_audio_and_video", 7), 148 }; 149 150 static const RtpHeaderExtension kAudioRtpExtensionAnswer[] = { 151 RtpHeaderExtension("urn:ietf:params:rtp-hdrext:ssrc-audio-level", 8), 152 }; 153 154 static const RtpHeaderExtension kVideoRtpExtension1[] = { 155 RtpHeaderExtension("urn:ietf:params:rtp-hdrext:toffset", 14), 156 RtpHeaderExtension("http://google.com/testing/video_something", 13), 157 }; 158 159 static const RtpHeaderExtension kVideoRtpExtension2[] = { 160 RtpHeaderExtension("urn:ietf:params:rtp-hdrext:toffset", 2), 161 RtpHeaderExtension("http://google.com/testing/video_something_else", 14), 162 RtpHeaderExtension("http://google.com/testing/both_audio_and_video", 7), 163 }; 164 165 static const RtpHeaderExtension kVideoRtpExtensionAnswer[] = { 166 RtpHeaderExtension("urn:ietf:params:rtp-hdrext:toffset", 14), 167 }; 168 169 static const uint32 kSimulcastParamsSsrc[] = {10, 11, 20, 21, 30, 31}; 170 static const uint32 kSimSsrc[] = {10, 20, 30}; 171 static const uint32 kFec1Ssrc[] = {10, 11}; 172 static const uint32 kFec2Ssrc[] = {20, 21}; 173 static const uint32 kFec3Ssrc[] = {30, 31}; 174 175 static const char kMediaStream1[] = "stream_1"; 176 static const char kMediaStream2[] = "stream_2"; 177 static const char kVideoTrack1[] = "video_1"; 178 static const char kVideoTrack2[] = "video_2"; 179 static const char kAudioTrack1[] = "audio_1"; 180 static const char kAudioTrack2[] = "audio_2"; 181 static const char kAudioTrack3[] = "audio_3"; 182 static const char kDataTrack1[] = "data_1"; 183 static const char kDataTrack2[] = "data_2"; 184 static const char kDataTrack3[] = "data_3"; 185 186 static bool IsMediaContentOfType(const ContentInfo* content, 187 MediaType media_type) { 188 const MediaContentDescription* mdesc = 189 static_cast<const MediaContentDescription*>(content->description); 190 return mdesc && mdesc->type() == media_type; 191 } 192 193 class MediaSessionDescriptionFactoryTest : public testing::Test { 194 public: 195 MediaSessionDescriptionFactoryTest() 196 : f1_(&tdf1_), f2_(&tdf2_), id1_("id1"), id2_("id2") { 197 f1_.set_audio_codecs(MAKE_VECTOR(kAudioCodecs1)); 198 f1_.set_video_codecs(MAKE_VECTOR(kVideoCodecs1)); 199 f1_.set_data_codecs(MAKE_VECTOR(kDataCodecs1)); 200 f2_.set_audio_codecs(MAKE_VECTOR(kAudioCodecs2)); 201 f2_.set_video_codecs(MAKE_VECTOR(kVideoCodecs2)); 202 f2_.set_data_codecs(MAKE_VECTOR(kDataCodecs2)); 203 tdf1_.set_identity(&id1_); 204 tdf2_.set_identity(&id2_); 205 } 206 207 static void SetUpTestCase() { 208 rtc::InitializeSSL(); 209 } 210 211 static void TearDownTestCase() { 212 rtc::CleanupSSL(); 213 } 214 215 // Create a video StreamParamsVec object with: 216 // - one video stream with 3 simulcast streams and FEC, 217 StreamParamsVec CreateComplexVideoStreamParamsVec() { 218 SsrcGroup sim_group("SIM", MAKE_VECTOR(kSimSsrc)); 219 SsrcGroup fec_group1("FEC", MAKE_VECTOR(kFec1Ssrc)); 220 SsrcGroup fec_group2("FEC", MAKE_VECTOR(kFec2Ssrc)); 221 SsrcGroup fec_group3("FEC", MAKE_VECTOR(kFec3Ssrc)); 222 223 std::vector<SsrcGroup> ssrc_groups; 224 ssrc_groups.push_back(sim_group); 225 ssrc_groups.push_back(fec_group1); 226 ssrc_groups.push_back(fec_group2); 227 ssrc_groups.push_back(fec_group3); 228 229 StreamParams simulcast_params; 230 simulcast_params.id = kVideoTrack1; 231 simulcast_params.ssrcs = MAKE_VECTOR(kSimulcastParamsSsrc); 232 simulcast_params.ssrc_groups = ssrc_groups; 233 simulcast_params.cname = "Video_SIM_FEC"; 234 simulcast_params.sync_label = kMediaStream1; 235 236 StreamParamsVec video_streams; 237 video_streams.push_back(simulcast_params); 238 239 return video_streams; 240 } 241 242 bool CompareCryptoParams(const CryptoParamsVec& c1, 243 const CryptoParamsVec& c2) { 244 if (c1.size() != c2.size()) 245 return false; 246 for (size_t i = 0; i < c1.size(); ++i) 247 if (c1[i].tag != c2[i].tag || c1[i].cipher_suite != c2[i].cipher_suite || 248 c1[i].key_params != c2[i].key_params || 249 c1[i].session_params != c2[i].session_params) 250 return false; 251 return true; 252 } 253 254 void TestTransportInfo(bool offer, const MediaSessionOptions& options, 255 bool has_current_desc) { 256 const std::string current_audio_ufrag = "current_audio_ufrag"; 257 const std::string current_audio_pwd = "current_audio_pwd"; 258 const std::string current_video_ufrag = "current_video_ufrag"; 259 const std::string current_video_pwd = "current_video_pwd"; 260 const std::string current_data_ufrag = "current_data_ufrag"; 261 const std::string current_data_pwd = "current_data_pwd"; 262 rtc::scoped_ptr<SessionDescription> current_desc; 263 rtc::scoped_ptr<SessionDescription> desc; 264 if (has_current_desc) { 265 current_desc.reset(new SessionDescription()); 266 EXPECT_TRUE(current_desc->AddTransportInfo( 267 TransportInfo("audio", 268 TransportDescription("", 269 current_audio_ufrag, 270 current_audio_pwd)))); 271 EXPECT_TRUE(current_desc->AddTransportInfo( 272 TransportInfo("video", 273 TransportDescription("", 274 current_video_ufrag, 275 current_video_pwd)))); 276 EXPECT_TRUE(current_desc->AddTransportInfo( 277 TransportInfo("data", 278 TransportDescription("", 279 current_data_ufrag, 280 current_data_pwd)))); 281 } 282 if (offer) { 283 desc.reset(f1_.CreateOffer(options, current_desc.get())); 284 } else { 285 rtc::scoped_ptr<SessionDescription> offer; 286 offer.reset(f1_.CreateOffer(options, NULL)); 287 desc.reset(f1_.CreateAnswer(offer.get(), options, current_desc.get())); 288 } 289 ASSERT_TRUE(desc.get() != NULL); 290 const TransportInfo* ti_audio = desc->GetTransportInfoByName("audio"); 291 if (options.has_audio) { 292 EXPECT_TRUE(ti_audio != NULL); 293 if (has_current_desc) { 294 EXPECT_EQ(current_audio_ufrag, ti_audio->description.ice_ufrag); 295 EXPECT_EQ(current_audio_pwd, ti_audio->description.ice_pwd); 296 } else { 297 EXPECT_EQ(static_cast<size_t>(cricket::ICE_UFRAG_LENGTH), 298 ti_audio->description.ice_ufrag.size()); 299 EXPECT_EQ(static_cast<size_t>(cricket::ICE_PWD_LENGTH), 300 ti_audio->description.ice_pwd.size()); 301 } 302 303 } else { 304 EXPECT_TRUE(ti_audio == NULL); 305 } 306 const TransportInfo* ti_video = desc->GetTransportInfoByName("video"); 307 if (options.has_video) { 308 EXPECT_TRUE(ti_video != NULL); 309 if (options.bundle_enabled) { 310 EXPECT_EQ(ti_audio->description.ice_ufrag, 311 ti_video->description.ice_ufrag); 312 EXPECT_EQ(ti_audio->description.ice_pwd, 313 ti_video->description.ice_pwd); 314 } else { 315 if (has_current_desc) { 316 EXPECT_EQ(current_video_ufrag, ti_video->description.ice_ufrag); 317 EXPECT_EQ(current_video_pwd, ti_video->description.ice_pwd); 318 } else { 319 EXPECT_EQ(static_cast<size_t>(cricket::ICE_UFRAG_LENGTH), 320 ti_video->description.ice_ufrag.size()); 321 EXPECT_EQ(static_cast<size_t>(cricket::ICE_PWD_LENGTH), 322 ti_video->description.ice_pwd.size()); 323 } 324 } 325 } else { 326 EXPECT_TRUE(ti_video == NULL); 327 } 328 const TransportInfo* ti_data = desc->GetTransportInfoByName("data"); 329 if (options.has_data()) { 330 EXPECT_TRUE(ti_data != NULL); 331 if (options.bundle_enabled) { 332 EXPECT_EQ(ti_audio->description.ice_ufrag, 333 ti_data->description.ice_ufrag); 334 EXPECT_EQ(ti_audio->description.ice_pwd, 335 ti_data->description.ice_pwd); 336 } else { 337 if (has_current_desc) { 338 EXPECT_EQ(current_data_ufrag, ti_data->description.ice_ufrag); 339 EXPECT_EQ(current_data_pwd, ti_data->description.ice_pwd); 340 } else { 341 EXPECT_EQ(static_cast<size_t>(cricket::ICE_UFRAG_LENGTH), 342 ti_data->description.ice_ufrag.size()); 343 EXPECT_EQ(static_cast<size_t>(cricket::ICE_PWD_LENGTH), 344 ti_data->description.ice_pwd.size()); 345 } 346 } 347 } else { 348 EXPECT_TRUE(ti_video == NULL); 349 } 350 } 351 352 void TestCryptoWithBundle(bool offer) { 353 f1_.set_secure(SEC_ENABLED); 354 MediaSessionOptions options; 355 options.has_audio = true; 356 options.has_video = true; 357 options.data_channel_type = cricket::DCT_RTP; 358 rtc::scoped_ptr<SessionDescription> ref_desc; 359 rtc::scoped_ptr<SessionDescription> desc; 360 if (offer) { 361 options.bundle_enabled = false; 362 ref_desc.reset(f1_.CreateOffer(options, NULL)); 363 options.bundle_enabled = true; 364 desc.reset(f1_.CreateOffer(options, ref_desc.get())); 365 } else { 366 options.bundle_enabled = true; 367 ref_desc.reset(f1_.CreateOffer(options, NULL)); 368 desc.reset(f1_.CreateAnswer(ref_desc.get(), options, NULL)); 369 } 370 ASSERT_TRUE(desc.get() != NULL); 371 const cricket::MediaContentDescription* audio_media_desc = 372 static_cast<const cricket::MediaContentDescription*>( 373 desc.get()->GetContentDescriptionByName("audio")); 374 ASSERT_TRUE(audio_media_desc != NULL); 375 const cricket::MediaContentDescription* video_media_desc = 376 static_cast<const cricket::MediaContentDescription*>( 377 desc.get()->GetContentDescriptionByName("video")); 378 ASSERT_TRUE(video_media_desc != NULL); 379 EXPECT_TRUE(CompareCryptoParams(audio_media_desc->cryptos(), 380 video_media_desc->cryptos())); 381 EXPECT_EQ(1u, audio_media_desc->cryptos().size()); 382 EXPECT_EQ(std::string(CS_AES_CM_128_HMAC_SHA1_80), 383 audio_media_desc->cryptos()[0].cipher_suite); 384 385 // Verify the selected crypto is one from the reference audio 386 // media content. 387 const cricket::MediaContentDescription* ref_audio_media_desc = 388 static_cast<const cricket::MediaContentDescription*>( 389 ref_desc.get()->GetContentDescriptionByName("audio")); 390 bool found = false; 391 for (size_t i = 0; i < ref_audio_media_desc->cryptos().size(); ++i) { 392 if (ref_audio_media_desc->cryptos()[i].Matches( 393 audio_media_desc->cryptos()[0])) { 394 found = true; 395 break; 396 } 397 } 398 EXPECT_TRUE(found); 399 } 400 401 // This test that the audio and video media direction is set to 402 // |expected_direction_in_answer| in an answer if the offer direction is set 403 // to |direction_in_offer|. 404 void TestMediaDirectionInAnswer( 405 cricket::MediaContentDirection direction_in_offer, 406 cricket::MediaContentDirection expected_direction_in_answer) { 407 MediaSessionOptions opts; 408 opts.has_video = true; 409 rtc::scoped_ptr<SessionDescription> offer( 410 f1_.CreateOffer(opts, NULL)); 411 ASSERT_TRUE(offer.get() != NULL); 412 ContentInfo* ac_offer= offer->GetContentByName("audio"); 413 ASSERT_TRUE(ac_offer != NULL); 414 AudioContentDescription* acd_offer = 415 static_cast<AudioContentDescription*>(ac_offer->description); 416 acd_offer->set_direction(direction_in_offer); 417 ContentInfo* vc_offer= offer->GetContentByName("video"); 418 ASSERT_TRUE(vc_offer != NULL); 419 VideoContentDescription* vcd_offer = 420 static_cast<VideoContentDescription*>(vc_offer->description); 421 vcd_offer->set_direction(direction_in_offer); 422 423 rtc::scoped_ptr<SessionDescription> answer( 424 f2_.CreateAnswer(offer.get(), opts, NULL)); 425 const AudioContentDescription* acd_answer = 426 GetFirstAudioContentDescription(answer.get()); 427 EXPECT_EQ(expected_direction_in_answer, acd_answer->direction()); 428 const VideoContentDescription* vcd_answer = 429 GetFirstVideoContentDescription(answer.get()); 430 EXPECT_EQ(expected_direction_in_answer, vcd_answer->direction()); 431 } 432 433 bool VerifyNoCNCodecs(const cricket::ContentInfo* content) { 434 const cricket::ContentDescription* description = content->description; 435 ASSERT(description != NULL); 436 const cricket::AudioContentDescription* audio_content_desc = 437 static_cast<const cricket::AudioContentDescription*>(description); 438 ASSERT(audio_content_desc != NULL); 439 for (size_t i = 0; i < audio_content_desc->codecs().size(); ++i) { 440 if (audio_content_desc->codecs()[i].name == "CN") 441 return false; 442 } 443 return true; 444 } 445 446 protected: 447 MediaSessionDescriptionFactory f1_; 448 MediaSessionDescriptionFactory f2_; 449 TransportDescriptionFactory tdf1_; 450 TransportDescriptionFactory tdf2_; 451 rtc::FakeSSLIdentity id1_; 452 rtc::FakeSSLIdentity id2_; 453 }; 454 455 // Create a typical audio offer, and ensure it matches what we expect. 456 TEST_F(MediaSessionDescriptionFactoryTest, TestCreateAudioOffer) { 457 f1_.set_secure(SEC_ENABLED); 458 rtc::scoped_ptr<SessionDescription> offer( 459 f1_.CreateOffer(MediaSessionOptions(), NULL)); 460 ASSERT_TRUE(offer.get() != NULL); 461 const ContentInfo* ac = offer->GetContentByName("audio"); 462 const ContentInfo* vc = offer->GetContentByName("video"); 463 ASSERT_TRUE(ac != NULL); 464 ASSERT_TRUE(vc == NULL); 465 EXPECT_EQ(std::string(NS_JINGLE_RTP), ac->type); 466 const AudioContentDescription* acd = 467 static_cast<const AudioContentDescription*>(ac->description); 468 EXPECT_EQ(MEDIA_TYPE_AUDIO, acd->type()); 469 EXPECT_EQ(f1_.audio_codecs(), acd->codecs()); 470 EXPECT_NE(0U, acd->first_ssrc()); // a random nonzero ssrc 471 EXPECT_EQ(kAutoBandwidth, acd->bandwidth()); // default bandwidth (auto) 472 EXPECT_TRUE(acd->rtcp_mux()); // rtcp-mux defaults on 473 ASSERT_CRYPTO(acd, 2U, CS_AES_CM_128_HMAC_SHA1_32); 474 EXPECT_EQ(std::string(cricket::kMediaProtocolSavpf), acd->protocol()); 475 } 476 477 // Create a typical video offer, and ensure it matches what we expect. 478 TEST_F(MediaSessionDescriptionFactoryTest, TestCreateVideoOffer) { 479 MediaSessionOptions opts; 480 opts.has_video = true; 481 f1_.set_secure(SEC_ENABLED); 482 rtc::scoped_ptr<SessionDescription> 483 offer(f1_.CreateOffer(opts, NULL)); 484 ASSERT_TRUE(offer.get() != NULL); 485 const ContentInfo* ac = offer->GetContentByName("audio"); 486 const ContentInfo* vc = offer->GetContentByName("video"); 487 ASSERT_TRUE(ac != NULL); 488 ASSERT_TRUE(vc != NULL); 489 EXPECT_EQ(std::string(NS_JINGLE_RTP), ac->type); 490 EXPECT_EQ(std::string(NS_JINGLE_RTP), vc->type); 491 const AudioContentDescription* acd = 492 static_cast<const AudioContentDescription*>(ac->description); 493 const VideoContentDescription* vcd = 494 static_cast<const VideoContentDescription*>(vc->description); 495 EXPECT_EQ(MEDIA_TYPE_AUDIO, acd->type()); 496 EXPECT_EQ(f1_.audio_codecs(), acd->codecs()); 497 EXPECT_NE(0U, acd->first_ssrc()); // a random nonzero ssrc 498 EXPECT_EQ(kAutoBandwidth, acd->bandwidth()); // default bandwidth (auto) 499 EXPECT_TRUE(acd->rtcp_mux()); // rtcp-mux defaults on 500 ASSERT_CRYPTO(acd, 2U, CS_AES_CM_128_HMAC_SHA1_32); 501 EXPECT_EQ(std::string(cricket::kMediaProtocolSavpf), acd->protocol()); 502 EXPECT_EQ(MEDIA_TYPE_VIDEO, vcd->type()); 503 EXPECT_EQ(f1_.video_codecs(), vcd->codecs()); 504 EXPECT_NE(0U, vcd->first_ssrc()); // a random nonzero ssrc 505 EXPECT_EQ(kAutoBandwidth, vcd->bandwidth()); // default bandwidth (auto) 506 EXPECT_TRUE(vcd->rtcp_mux()); // rtcp-mux defaults on 507 ASSERT_CRYPTO(vcd, 1U, CS_AES_CM_128_HMAC_SHA1_80); 508 EXPECT_EQ(std::string(cricket::kMediaProtocolSavpf), vcd->protocol()); 509 } 510 511 // Test creating an offer with bundle where the Codecs have the same dynamic 512 // RTP playlod type. The test verifies that the offer don't contain the 513 // duplicate RTP payload types. 514 TEST_F(MediaSessionDescriptionFactoryTest, TestBundleOfferWithSameCodecPlType) { 515 const VideoCodec& offered_video_codec = f2_.video_codecs()[0]; 516 const AudioCodec& offered_audio_codec = f2_.audio_codecs()[0]; 517 const DataCodec& offered_data_codec = f2_.data_codecs()[0]; 518 ASSERT_EQ(offered_video_codec.id, offered_audio_codec.id); 519 ASSERT_EQ(offered_video_codec.id, offered_data_codec.id); 520 521 MediaSessionOptions opts; 522 opts.has_audio = true; 523 opts.has_video = true; 524 opts.data_channel_type = cricket::DCT_RTP; 525 opts.bundle_enabled = true; 526 rtc::scoped_ptr<SessionDescription> 527 offer(f2_.CreateOffer(opts, NULL)); 528 const VideoContentDescription* vcd = 529 GetFirstVideoContentDescription(offer.get()); 530 const AudioContentDescription* acd = 531 GetFirstAudioContentDescription(offer.get()); 532 const DataContentDescription* dcd = 533 GetFirstDataContentDescription(offer.get()); 534 ASSERT_TRUE(NULL != vcd); 535 ASSERT_TRUE(NULL != acd); 536 ASSERT_TRUE(NULL != dcd); 537 EXPECT_NE(vcd->codecs()[0].id, acd->codecs()[0].id); 538 EXPECT_NE(vcd->codecs()[0].id, dcd->codecs()[0].id); 539 EXPECT_NE(acd->codecs()[0].id, dcd->codecs()[0].id); 540 EXPECT_EQ(vcd->codecs()[0].name, offered_video_codec.name); 541 EXPECT_EQ(acd->codecs()[0].name, offered_audio_codec.name); 542 EXPECT_EQ(dcd->codecs()[0].name, offered_data_codec.name); 543 } 544 545 // Test creating an updated offer with with bundle, audio, video and data 546 // after an audio only session has been negotiated. 547 TEST_F(MediaSessionDescriptionFactoryTest, 548 TestCreateUpdatedVideoOfferWithBundle) { 549 f1_.set_secure(SEC_ENABLED); 550 f2_.set_secure(SEC_ENABLED); 551 MediaSessionOptions opts; 552 opts.has_audio = true; 553 opts.has_video = false; 554 opts.data_channel_type = cricket::DCT_NONE; 555 opts.bundle_enabled = true; 556 rtc::scoped_ptr<SessionDescription> offer(f1_.CreateOffer(opts, NULL)); 557 rtc::scoped_ptr<SessionDescription> answer( 558 f2_.CreateAnswer(offer.get(), opts, NULL)); 559 560 MediaSessionOptions updated_opts; 561 updated_opts.has_audio = true; 562 updated_opts.has_video = true; 563 updated_opts.data_channel_type = cricket::DCT_RTP; 564 updated_opts.bundle_enabled = true; 565 rtc::scoped_ptr<SessionDescription> updated_offer(f1_.CreateOffer( 566 updated_opts, answer.get())); 567 568 const AudioContentDescription* acd = 569 GetFirstAudioContentDescription(updated_offer.get()); 570 const VideoContentDescription* vcd = 571 GetFirstVideoContentDescription(updated_offer.get()); 572 const DataContentDescription* dcd = 573 GetFirstDataContentDescription(updated_offer.get()); 574 EXPECT_TRUE(NULL != vcd); 575 EXPECT_TRUE(NULL != acd); 576 EXPECT_TRUE(NULL != dcd); 577 578 ASSERT_CRYPTO(acd, 1U, CS_AES_CM_128_HMAC_SHA1_80); 579 EXPECT_EQ(std::string(cricket::kMediaProtocolSavpf), acd->protocol()); 580 ASSERT_CRYPTO(vcd, 1U, CS_AES_CM_128_HMAC_SHA1_80); 581 EXPECT_EQ(std::string(cricket::kMediaProtocolSavpf), vcd->protocol()); 582 ASSERT_CRYPTO(dcd, 1U, CS_AES_CM_128_HMAC_SHA1_80); 583 EXPECT_EQ(std::string(cricket::kMediaProtocolSavpf), dcd->protocol()); 584 } 585 // Create a RTP data offer, and ensure it matches what we expect. 586 TEST_F(MediaSessionDescriptionFactoryTest, TestCreateRtpDataOffer) { 587 MediaSessionOptions opts; 588 opts.data_channel_type = cricket::DCT_RTP; 589 f1_.set_secure(SEC_ENABLED); 590 rtc::scoped_ptr<SessionDescription> 591 offer(f1_.CreateOffer(opts, NULL)); 592 ASSERT_TRUE(offer.get() != NULL); 593 const ContentInfo* ac = offer->GetContentByName("audio"); 594 const ContentInfo* dc = offer->GetContentByName("data"); 595 ASSERT_TRUE(ac != NULL); 596 ASSERT_TRUE(dc != NULL); 597 EXPECT_EQ(std::string(NS_JINGLE_RTP), ac->type); 598 EXPECT_EQ(std::string(NS_JINGLE_RTP), dc->type); 599 const AudioContentDescription* acd = 600 static_cast<const AudioContentDescription*>(ac->description); 601 const DataContentDescription* dcd = 602 static_cast<const DataContentDescription*>(dc->description); 603 EXPECT_EQ(MEDIA_TYPE_AUDIO, acd->type()); 604 EXPECT_EQ(f1_.audio_codecs(), acd->codecs()); 605 EXPECT_NE(0U, acd->first_ssrc()); // a random nonzero ssrc 606 EXPECT_EQ(kAutoBandwidth, acd->bandwidth()); // default bandwidth (auto) 607 EXPECT_TRUE(acd->rtcp_mux()); // rtcp-mux defaults on 608 ASSERT_CRYPTO(acd, 2U, CS_AES_CM_128_HMAC_SHA1_32); 609 EXPECT_EQ(std::string(cricket::kMediaProtocolSavpf), acd->protocol()); 610 EXPECT_EQ(MEDIA_TYPE_DATA, dcd->type()); 611 EXPECT_EQ(f1_.data_codecs(), dcd->codecs()); 612 EXPECT_NE(0U, dcd->first_ssrc()); // a random nonzero ssrc 613 EXPECT_EQ(cricket::kDataMaxBandwidth, 614 dcd->bandwidth()); // default bandwidth (auto) 615 EXPECT_TRUE(dcd->rtcp_mux()); // rtcp-mux defaults on 616 ASSERT_CRYPTO(dcd, 1U, CS_AES_CM_128_HMAC_SHA1_80); 617 EXPECT_EQ(std::string(cricket::kMediaProtocolSavpf), dcd->protocol()); 618 } 619 620 // Create an SCTP data offer with bundle without error. 621 TEST_F(MediaSessionDescriptionFactoryTest, TestCreateSctpDataOffer) { 622 MediaSessionOptions opts; 623 opts.has_audio = false; 624 opts.bundle_enabled = true; 625 opts.data_channel_type = cricket::DCT_SCTP; 626 f1_.set_secure(SEC_ENABLED); 627 rtc::scoped_ptr<SessionDescription> offer(f1_.CreateOffer(opts, NULL)); 628 EXPECT_TRUE(offer.get() != NULL); 629 EXPECT_TRUE(offer->GetContentByName("data") != NULL); 630 } 631 632 // Create an audio, video offer without legacy StreamParams. 633 TEST_F(MediaSessionDescriptionFactoryTest, 634 TestCreateOfferWithoutLegacyStreams) { 635 MediaSessionOptions opts; 636 opts.has_video = true; 637 f1_.set_add_legacy_streams(false); 638 rtc::scoped_ptr<SessionDescription> 639 offer(f1_.CreateOffer(opts, NULL)); 640 ASSERT_TRUE(offer.get() != NULL); 641 const ContentInfo* ac = offer->GetContentByName("audio"); 642 const ContentInfo* vc = offer->GetContentByName("video"); 643 ASSERT_TRUE(ac != NULL); 644 ASSERT_TRUE(vc != NULL); 645 const AudioContentDescription* acd = 646 static_cast<const AudioContentDescription*>(ac->description); 647 const VideoContentDescription* vcd = 648 static_cast<const VideoContentDescription*>(vc->description); 649 650 EXPECT_FALSE(vcd->has_ssrcs()); // No StreamParams. 651 EXPECT_FALSE(acd->has_ssrcs()); // No StreamParams. 652 } 653 654 // Verifies that the order of the media contents in the current 655 // SessionDescription is preserved in the new SessionDescription. 656 TEST_F(MediaSessionDescriptionFactoryTest, TestCreateOfferContentOrder) { 657 MediaSessionOptions opts; 658 opts.has_audio = false; 659 opts.has_video = false; 660 opts.data_channel_type = cricket::DCT_SCTP; 661 662 rtc::scoped_ptr<SessionDescription> offer1(f1_.CreateOffer(opts, NULL)); 663 ASSERT_TRUE(offer1.get() != NULL); 664 EXPECT_EQ(1u, offer1->contents().size()); 665 EXPECT_TRUE(IsMediaContentOfType(&offer1->contents()[0], MEDIA_TYPE_DATA)); 666 667 opts.has_video = true; 668 rtc::scoped_ptr<SessionDescription> offer2( 669 f1_.CreateOffer(opts, offer1.get())); 670 ASSERT_TRUE(offer2.get() != NULL); 671 EXPECT_EQ(2u, offer2->contents().size()); 672 EXPECT_TRUE(IsMediaContentOfType(&offer2->contents()[0], MEDIA_TYPE_DATA)); 673 EXPECT_TRUE(IsMediaContentOfType(&offer2->contents()[1], MEDIA_TYPE_VIDEO)); 674 675 opts.has_audio = true; 676 rtc::scoped_ptr<SessionDescription> offer3( 677 f1_.CreateOffer(opts, offer2.get())); 678 ASSERT_TRUE(offer3.get() != NULL); 679 EXPECT_EQ(3u, offer3->contents().size()); 680 EXPECT_TRUE(IsMediaContentOfType(&offer3->contents()[0], MEDIA_TYPE_DATA)); 681 EXPECT_TRUE(IsMediaContentOfType(&offer3->contents()[1], MEDIA_TYPE_VIDEO)); 682 EXPECT_TRUE(IsMediaContentOfType(&offer3->contents()[2], MEDIA_TYPE_AUDIO)); 683 684 // Verifies the default order is audio-video-data, so that the previous checks 685 // didn't pass by accident. 686 rtc::scoped_ptr<SessionDescription> offer4(f1_.CreateOffer(opts, NULL)); 687 ASSERT_TRUE(offer4.get() != NULL); 688 EXPECT_EQ(3u, offer4->contents().size()); 689 EXPECT_TRUE(IsMediaContentOfType(&offer4->contents()[0], MEDIA_TYPE_AUDIO)); 690 EXPECT_TRUE(IsMediaContentOfType(&offer4->contents()[1], MEDIA_TYPE_VIDEO)); 691 EXPECT_TRUE(IsMediaContentOfType(&offer4->contents()[2], MEDIA_TYPE_DATA)); 692 } 693 694 // Create a typical audio answer, and ensure it matches what we expect. 695 TEST_F(MediaSessionDescriptionFactoryTest, TestCreateAudioAnswer) { 696 f1_.set_secure(SEC_ENABLED); 697 f2_.set_secure(SEC_ENABLED); 698 rtc::scoped_ptr<SessionDescription> offer( 699 f1_.CreateOffer(MediaSessionOptions(), NULL)); 700 ASSERT_TRUE(offer.get() != NULL); 701 rtc::scoped_ptr<SessionDescription> answer( 702 f2_.CreateAnswer(offer.get(), MediaSessionOptions(), NULL)); 703 const ContentInfo* ac = answer->GetContentByName("audio"); 704 const ContentInfo* vc = answer->GetContentByName("video"); 705 ASSERT_TRUE(ac != NULL); 706 ASSERT_TRUE(vc == NULL); 707 EXPECT_EQ(std::string(NS_JINGLE_RTP), ac->type); 708 const AudioContentDescription* acd = 709 static_cast<const AudioContentDescription*>(ac->description); 710 EXPECT_EQ(MEDIA_TYPE_AUDIO, acd->type()); 711 EXPECT_EQ(MAKE_VECTOR(kAudioCodecsAnswer), acd->codecs()); 712 EXPECT_NE(0U, acd->first_ssrc()); // a random nonzero ssrc 713 EXPECT_EQ(kAutoBandwidth, acd->bandwidth()); // negotiated auto bw 714 EXPECT_TRUE(acd->rtcp_mux()); // negotiated rtcp-mux 715 ASSERT_CRYPTO(acd, 1U, CS_AES_CM_128_HMAC_SHA1_32); 716 EXPECT_EQ(std::string(cricket::kMediaProtocolSavpf), acd->protocol()); 717 } 718 719 // Create a typical video answer, and ensure it matches what we expect. 720 TEST_F(MediaSessionDescriptionFactoryTest, TestCreateVideoAnswer) { 721 MediaSessionOptions opts; 722 opts.has_video = true; 723 f1_.set_secure(SEC_ENABLED); 724 f2_.set_secure(SEC_ENABLED); 725 rtc::scoped_ptr<SessionDescription> offer(f1_.CreateOffer(opts, NULL)); 726 ASSERT_TRUE(offer.get() != NULL); 727 rtc::scoped_ptr<SessionDescription> answer( 728 f2_.CreateAnswer(offer.get(), opts, NULL)); 729 const ContentInfo* ac = answer->GetContentByName("audio"); 730 const ContentInfo* vc = answer->GetContentByName("video"); 731 ASSERT_TRUE(ac != NULL); 732 ASSERT_TRUE(vc != NULL); 733 EXPECT_EQ(std::string(NS_JINGLE_RTP), ac->type); 734 EXPECT_EQ(std::string(NS_JINGLE_RTP), vc->type); 735 const AudioContentDescription* acd = 736 static_cast<const AudioContentDescription*>(ac->description); 737 const VideoContentDescription* vcd = 738 static_cast<const VideoContentDescription*>(vc->description); 739 EXPECT_EQ(MEDIA_TYPE_AUDIO, acd->type()); 740 EXPECT_EQ(MAKE_VECTOR(kAudioCodecsAnswer), acd->codecs()); 741 EXPECT_EQ(kAutoBandwidth, acd->bandwidth()); // negotiated auto bw 742 EXPECT_NE(0U, acd->first_ssrc()); // a random nonzero ssrc 743 EXPECT_TRUE(acd->rtcp_mux()); // negotiated rtcp-mux 744 ASSERT_CRYPTO(acd, 1U, CS_AES_CM_128_HMAC_SHA1_32); 745 EXPECT_EQ(MEDIA_TYPE_VIDEO, vcd->type()); 746 EXPECT_EQ(MAKE_VECTOR(kVideoCodecsAnswer), vcd->codecs()); 747 EXPECT_NE(0U, vcd->first_ssrc()); // a random nonzero ssrc 748 EXPECT_TRUE(vcd->rtcp_mux()); // negotiated rtcp-mux 749 ASSERT_CRYPTO(vcd, 1U, CS_AES_CM_128_HMAC_SHA1_80); 750 EXPECT_EQ(std::string(cricket::kMediaProtocolSavpf), vcd->protocol()); 751 } 752 753 TEST_F(MediaSessionDescriptionFactoryTest, TestCreateDataAnswer) { 754 MediaSessionOptions opts; 755 opts.data_channel_type = cricket::DCT_RTP; 756 f1_.set_secure(SEC_ENABLED); 757 f2_.set_secure(SEC_ENABLED); 758 rtc::scoped_ptr<SessionDescription> offer(f1_.CreateOffer(opts, NULL)); 759 ASSERT_TRUE(offer.get() != NULL); 760 rtc::scoped_ptr<SessionDescription> answer( 761 f2_.CreateAnswer(offer.get(), opts, NULL)); 762 const ContentInfo* ac = answer->GetContentByName("audio"); 763 const ContentInfo* vc = answer->GetContentByName("data"); 764 ASSERT_TRUE(ac != NULL); 765 ASSERT_TRUE(vc != NULL); 766 EXPECT_EQ(std::string(NS_JINGLE_RTP), ac->type); 767 EXPECT_EQ(std::string(NS_JINGLE_RTP), vc->type); 768 const AudioContentDescription* acd = 769 static_cast<const AudioContentDescription*>(ac->description); 770 const DataContentDescription* vcd = 771 static_cast<const DataContentDescription*>(vc->description); 772 EXPECT_EQ(MEDIA_TYPE_AUDIO, acd->type()); 773 EXPECT_EQ(MAKE_VECTOR(kAudioCodecsAnswer), acd->codecs()); 774 EXPECT_EQ(kAutoBandwidth, acd->bandwidth()); // negotiated auto bw 775 EXPECT_NE(0U, acd->first_ssrc()); // a random nonzero ssrc 776 EXPECT_TRUE(acd->rtcp_mux()); // negotiated rtcp-mux 777 ASSERT_CRYPTO(acd, 1U, CS_AES_CM_128_HMAC_SHA1_32); 778 EXPECT_EQ(MEDIA_TYPE_DATA, vcd->type()); 779 EXPECT_EQ(MAKE_VECTOR(kDataCodecsAnswer), vcd->codecs()); 780 EXPECT_NE(0U, vcd->first_ssrc()); // a random nonzero ssrc 781 EXPECT_TRUE(vcd->rtcp_mux()); // negotiated rtcp-mux 782 ASSERT_CRYPTO(vcd, 1U, CS_AES_CM_128_HMAC_SHA1_80); 783 EXPECT_EQ(std::string(cricket::kMediaProtocolSavpf), vcd->protocol()); 784 } 785 786 // Verifies that the order of the media contents in the offer is preserved in 787 // the answer. 788 TEST_F(MediaSessionDescriptionFactoryTest, TestCreateAnswerContentOrder) { 789 MediaSessionOptions opts; 790 791 // Creates a data only offer. 792 opts.has_audio = false; 793 opts.data_channel_type = cricket::DCT_SCTP; 794 rtc::scoped_ptr<SessionDescription> offer1(f1_.CreateOffer(opts, NULL)); 795 ASSERT_TRUE(offer1.get() != NULL); 796 797 // Appends audio to the offer. 798 opts.has_audio = true; 799 rtc::scoped_ptr<SessionDescription> offer2( 800 f1_.CreateOffer(opts, offer1.get())); 801 ASSERT_TRUE(offer2.get() != NULL); 802 803 // Appends video to the offer. 804 opts.has_video = true; 805 rtc::scoped_ptr<SessionDescription> offer3( 806 f1_.CreateOffer(opts, offer2.get())); 807 ASSERT_TRUE(offer3.get() != NULL); 808 809 rtc::scoped_ptr<SessionDescription> answer( 810 f2_.CreateAnswer(offer3.get(), opts, NULL)); 811 ASSERT_TRUE(answer.get() != NULL); 812 EXPECT_EQ(3u, answer->contents().size()); 813 EXPECT_TRUE(IsMediaContentOfType(&answer->contents()[0], MEDIA_TYPE_DATA)); 814 EXPECT_TRUE(IsMediaContentOfType(&answer->contents()[1], MEDIA_TYPE_AUDIO)); 815 EXPECT_TRUE(IsMediaContentOfType(&answer->contents()[2], MEDIA_TYPE_VIDEO)); 816 } 817 818 // This test that the media direction is set to send/receive in an answer if 819 // the offer is send receive. 820 TEST_F(MediaSessionDescriptionFactoryTest, CreateAnswerToSendReceiveOffer) { 821 TestMediaDirectionInAnswer(cricket::MD_SENDRECV, cricket::MD_SENDRECV); 822 } 823 824 // This test that the media direction is set to receive only in an answer if 825 // the offer is send only. 826 TEST_F(MediaSessionDescriptionFactoryTest, CreateAnswerToSendOnlyOffer) { 827 TestMediaDirectionInAnswer(cricket::MD_SENDONLY, cricket::MD_RECVONLY); 828 } 829 830 // This test that the media direction is set to send only in an answer if 831 // the offer is recv only. 832 TEST_F(MediaSessionDescriptionFactoryTest, CreateAnswerToRecvOnlyOffer) { 833 TestMediaDirectionInAnswer(cricket::MD_RECVONLY, cricket::MD_SENDONLY); 834 } 835 836 // This test that the media direction is set to inactive in an answer if 837 // the offer is inactive. 838 TEST_F(MediaSessionDescriptionFactoryTest, CreateAnswerToInactiveOffer) { 839 TestMediaDirectionInAnswer(cricket::MD_INACTIVE, cricket::MD_INACTIVE); 840 } 841 842 // Test that a data content with an unknown protocol is rejected in an answer. 843 TEST_F(MediaSessionDescriptionFactoryTest, 844 CreateDataAnswerToOfferWithUnknownProtocol) { 845 MediaSessionOptions opts; 846 opts.data_channel_type = cricket::DCT_RTP; 847 opts.has_audio = false; 848 f1_.set_secure(SEC_ENABLED); 849 f2_.set_secure(SEC_ENABLED); 850 rtc::scoped_ptr<SessionDescription> offer(f1_.CreateOffer(opts, NULL)); 851 ContentInfo* dc_offer= offer->GetContentByName("data"); 852 ASSERT_TRUE(dc_offer != NULL); 853 DataContentDescription* dcd_offer = 854 static_cast<DataContentDescription*>(dc_offer->description); 855 ASSERT_TRUE(dcd_offer != NULL); 856 std::string protocol = "a weird unknown protocol"; 857 dcd_offer->set_protocol(protocol); 858 859 rtc::scoped_ptr<SessionDescription> answer( 860 f2_.CreateAnswer(offer.get(), opts, NULL)); 861 862 const ContentInfo* dc_answer = answer->GetContentByName("data"); 863 ASSERT_TRUE(dc_answer != NULL); 864 EXPECT_TRUE(dc_answer->rejected); 865 const DataContentDescription* dcd_answer = 866 static_cast<const DataContentDescription*>(dc_answer->description); 867 ASSERT_TRUE(dcd_answer != NULL); 868 EXPECT_EQ(protocol, dcd_answer->protocol()); 869 } 870 871 // Test that the media protocol is RTP/AVPF if DTLS and SDES are disabled. 872 TEST_F(MediaSessionDescriptionFactoryTest, AudioOfferAnswerWithCryptoDisabled) { 873 MediaSessionOptions opts; 874 f1_.set_secure(SEC_DISABLED); 875 f2_.set_secure(SEC_DISABLED); 876 tdf1_.set_secure(SEC_DISABLED); 877 tdf2_.set_secure(SEC_DISABLED); 878 879 rtc::scoped_ptr<SessionDescription> offer(f1_.CreateOffer(opts, NULL)); 880 const AudioContentDescription* offer_acd = 881 GetFirstAudioContentDescription(offer.get()); 882 ASSERT_TRUE(offer_acd != NULL); 883 EXPECT_EQ(std::string(cricket::kMediaProtocolAvpf), offer_acd->protocol()); 884 885 rtc::scoped_ptr<SessionDescription> answer( 886 f2_.CreateAnswer(offer.get(), opts, NULL)); 887 888 const ContentInfo* ac_answer = answer->GetContentByName("audio"); 889 ASSERT_TRUE(ac_answer != NULL); 890 EXPECT_FALSE(ac_answer->rejected); 891 892 const AudioContentDescription* answer_acd = 893 GetFirstAudioContentDescription(answer.get()); 894 ASSERT_TRUE(answer_acd != NULL); 895 EXPECT_EQ(std::string(cricket::kMediaProtocolAvpf), answer_acd->protocol()); 896 } 897 898 // Create a video offer and answer and ensure the RTP header extensions 899 // matches what we expect. 900 TEST_F(MediaSessionDescriptionFactoryTest, TestOfferAnswerWithRtpExtensions) { 901 MediaSessionOptions opts; 902 opts.has_video = true; 903 904 f1_.set_audio_rtp_header_extensions(MAKE_VECTOR(kAudioRtpExtension1)); 905 f1_.set_video_rtp_header_extensions(MAKE_VECTOR(kVideoRtpExtension1)); 906 f2_.set_audio_rtp_header_extensions(MAKE_VECTOR(kAudioRtpExtension2)); 907 f2_.set_video_rtp_header_extensions(MAKE_VECTOR(kVideoRtpExtension2)); 908 909 rtc::scoped_ptr<SessionDescription> offer(f1_.CreateOffer(opts, NULL)); 910 ASSERT_TRUE(offer.get() != NULL); 911 rtc::scoped_ptr<SessionDescription> answer( 912 f2_.CreateAnswer(offer.get(), opts, NULL)); 913 914 EXPECT_EQ(MAKE_VECTOR(kAudioRtpExtension1), 915 GetFirstAudioContentDescription( 916 offer.get())->rtp_header_extensions()); 917 EXPECT_EQ(MAKE_VECTOR(kVideoRtpExtension1), 918 GetFirstVideoContentDescription( 919 offer.get())->rtp_header_extensions()); 920 EXPECT_EQ(MAKE_VECTOR(kAudioRtpExtensionAnswer), 921 GetFirstAudioContentDescription( 922 answer.get())->rtp_header_extensions()); 923 EXPECT_EQ(MAKE_VECTOR(kVideoRtpExtensionAnswer), 924 GetFirstVideoContentDescription( 925 answer.get())->rtp_header_extensions()); 926 } 927 928 // Create an audio, video, data answer without legacy StreamParams. 929 TEST_F(MediaSessionDescriptionFactoryTest, 930 TestCreateAnswerWithoutLegacyStreams) { 931 MediaSessionOptions opts; 932 opts.has_video = true; 933 opts.data_channel_type = cricket::DCT_RTP; 934 f1_.set_add_legacy_streams(false); 935 f2_.set_add_legacy_streams(false); 936 rtc::scoped_ptr<SessionDescription> offer(f1_.CreateOffer(opts, NULL)); 937 ASSERT_TRUE(offer.get() != NULL); 938 rtc::scoped_ptr<SessionDescription> answer( 939 f2_.CreateAnswer(offer.get(), opts, NULL)); 940 const ContentInfo* ac = answer->GetContentByName("audio"); 941 const ContentInfo* vc = answer->GetContentByName("video"); 942 const ContentInfo* dc = answer->GetContentByName("data"); 943 ASSERT_TRUE(ac != NULL); 944 ASSERT_TRUE(vc != NULL); 945 const AudioContentDescription* acd = 946 static_cast<const AudioContentDescription*>(ac->description); 947 const VideoContentDescription* vcd = 948 static_cast<const VideoContentDescription*>(vc->description); 949 const DataContentDescription* dcd = 950 static_cast<const DataContentDescription*>(dc->description); 951 952 EXPECT_FALSE(acd->has_ssrcs()); // No StreamParams. 953 EXPECT_FALSE(vcd->has_ssrcs()); // No StreamParams. 954 EXPECT_FALSE(dcd->has_ssrcs()); // No StreamParams. 955 } 956 957 TEST_F(MediaSessionDescriptionFactoryTest, TestPartial) { 958 MediaSessionOptions opts; 959 opts.has_video = true; 960 opts.data_channel_type = cricket::DCT_RTP; 961 f1_.set_secure(SEC_ENABLED); 962 rtc::scoped_ptr<SessionDescription> 963 offer(f1_.CreateOffer(opts, NULL)); 964 ASSERT_TRUE(offer.get() != NULL); 965 const ContentInfo* ac = offer->GetContentByName("audio"); 966 const ContentInfo* vc = offer->GetContentByName("video"); 967 const ContentInfo* dc = offer->GetContentByName("data"); 968 AudioContentDescription* acd = const_cast<AudioContentDescription*>( 969 static_cast<const AudioContentDescription*>(ac->description)); 970 VideoContentDescription* vcd = const_cast<VideoContentDescription*>( 971 static_cast<const VideoContentDescription*>(vc->description)); 972 DataContentDescription* dcd = const_cast<DataContentDescription*>( 973 static_cast<const DataContentDescription*>(dc->description)); 974 975 EXPECT_FALSE(acd->partial()); // default is false. 976 acd->set_partial(true); 977 EXPECT_TRUE(acd->partial()); 978 acd->set_partial(false); 979 EXPECT_FALSE(acd->partial()); 980 981 EXPECT_FALSE(vcd->partial()); // default is false. 982 vcd->set_partial(true); 983 EXPECT_TRUE(vcd->partial()); 984 vcd->set_partial(false); 985 EXPECT_FALSE(vcd->partial()); 986 987 EXPECT_FALSE(dcd->partial()); // default is false. 988 dcd->set_partial(true); 989 EXPECT_TRUE(dcd->partial()); 990 dcd->set_partial(false); 991 EXPECT_FALSE(dcd->partial()); 992 } 993 994 // Create a typical video answer, and ensure it matches what we expect. 995 TEST_F(MediaSessionDescriptionFactoryTest, TestCreateVideoAnswerRtcpMux) { 996 MediaSessionOptions offer_opts; 997 MediaSessionOptions answer_opts; 998 answer_opts.has_video = true; 999 offer_opts.has_video = true; 1000 answer_opts.data_channel_type = cricket::DCT_RTP; 1001 offer_opts.data_channel_type = cricket::DCT_RTP; 1002 1003 rtc::scoped_ptr<SessionDescription> offer; 1004 rtc::scoped_ptr<SessionDescription> answer; 1005 1006 offer_opts.rtcp_mux_enabled = true; 1007 answer_opts.rtcp_mux_enabled = true; 1008 1009 offer.reset(f1_.CreateOffer(offer_opts, NULL)); 1010 answer.reset(f2_.CreateAnswer(offer.get(), answer_opts, NULL)); 1011 ASSERT_TRUE(NULL != GetFirstAudioContentDescription(offer.get())); 1012 ASSERT_TRUE(NULL != GetFirstVideoContentDescription(offer.get())); 1013 ASSERT_TRUE(NULL != GetFirstDataContentDescription(offer.get())); 1014 ASSERT_TRUE(NULL != GetFirstAudioContentDescription(answer.get())); 1015 ASSERT_TRUE(NULL != GetFirstVideoContentDescription(answer.get())); 1016 ASSERT_TRUE(NULL != GetFirstDataContentDescription(answer.get())); 1017 EXPECT_TRUE(GetFirstAudioContentDescription(offer.get())->rtcp_mux()); 1018 EXPECT_TRUE(GetFirstVideoContentDescription(offer.get())->rtcp_mux()); 1019 EXPECT_TRUE(GetFirstDataContentDescription(offer.get())->rtcp_mux()); 1020 EXPECT_TRUE(GetFirstAudioContentDescription(answer.get())->rtcp_mux()); 1021 EXPECT_TRUE(GetFirstVideoContentDescription(answer.get())->rtcp_mux()); 1022 EXPECT_TRUE(GetFirstDataContentDescription(answer.get())->rtcp_mux()); 1023 1024 offer_opts.rtcp_mux_enabled = true; 1025 answer_opts.rtcp_mux_enabled = false; 1026 1027 offer.reset(f1_.CreateOffer(offer_opts, NULL)); 1028 answer.reset(f2_.CreateAnswer(offer.get(), answer_opts, NULL)); 1029 ASSERT_TRUE(NULL != GetFirstAudioContentDescription(offer.get())); 1030 ASSERT_TRUE(NULL != GetFirstVideoContentDescription(offer.get())); 1031 ASSERT_TRUE(NULL != GetFirstDataContentDescription(offer.get())); 1032 ASSERT_TRUE(NULL != GetFirstAudioContentDescription(answer.get())); 1033 ASSERT_TRUE(NULL != GetFirstVideoContentDescription(answer.get())); 1034 ASSERT_TRUE(NULL != GetFirstDataContentDescription(answer.get())); 1035 EXPECT_TRUE(GetFirstAudioContentDescription(offer.get())->rtcp_mux()); 1036 EXPECT_TRUE(GetFirstVideoContentDescription(offer.get())->rtcp_mux()); 1037 EXPECT_TRUE(GetFirstDataContentDescription(offer.get())->rtcp_mux()); 1038 EXPECT_FALSE(GetFirstAudioContentDescription(answer.get())->rtcp_mux()); 1039 EXPECT_FALSE(GetFirstVideoContentDescription(answer.get())->rtcp_mux()); 1040 EXPECT_FALSE(GetFirstDataContentDescription(answer.get())->rtcp_mux()); 1041 1042 offer_opts.rtcp_mux_enabled = false; 1043 answer_opts.rtcp_mux_enabled = true; 1044 1045 offer.reset(f1_.CreateOffer(offer_opts, NULL)); 1046 answer.reset(f2_.CreateAnswer(offer.get(), answer_opts, NULL)); 1047 ASSERT_TRUE(NULL != GetFirstAudioContentDescription(offer.get())); 1048 ASSERT_TRUE(NULL != GetFirstVideoContentDescription(offer.get())); 1049 ASSERT_TRUE(NULL != GetFirstDataContentDescription(offer.get())); 1050 ASSERT_TRUE(NULL != GetFirstAudioContentDescription(answer.get())); 1051 ASSERT_TRUE(NULL != GetFirstVideoContentDescription(answer.get())); 1052 ASSERT_TRUE(NULL != GetFirstDataContentDescription(answer.get())); 1053 EXPECT_FALSE(GetFirstAudioContentDescription(offer.get())->rtcp_mux()); 1054 EXPECT_FALSE(GetFirstVideoContentDescription(offer.get())->rtcp_mux()); 1055 EXPECT_FALSE(GetFirstDataContentDescription(offer.get())->rtcp_mux()); 1056 EXPECT_FALSE(GetFirstAudioContentDescription(answer.get())->rtcp_mux()); 1057 EXPECT_FALSE(GetFirstVideoContentDescription(answer.get())->rtcp_mux()); 1058 EXPECT_FALSE(GetFirstDataContentDescription(answer.get())->rtcp_mux()); 1059 1060 offer_opts.rtcp_mux_enabled = false; 1061 answer_opts.rtcp_mux_enabled = false; 1062 1063 offer.reset(f1_.CreateOffer(offer_opts, NULL)); 1064 answer.reset(f2_.CreateAnswer(offer.get(), answer_opts, NULL)); 1065 ASSERT_TRUE(NULL != GetFirstAudioContentDescription(offer.get())); 1066 ASSERT_TRUE(NULL != GetFirstVideoContentDescription(offer.get())); 1067 ASSERT_TRUE(NULL != GetFirstDataContentDescription(offer.get())); 1068 ASSERT_TRUE(NULL != GetFirstAudioContentDescription(answer.get())); 1069 ASSERT_TRUE(NULL != GetFirstVideoContentDescription(answer.get())); 1070 ASSERT_TRUE(NULL != GetFirstDataContentDescription(answer.get())); 1071 EXPECT_FALSE(GetFirstAudioContentDescription(offer.get())->rtcp_mux()); 1072 EXPECT_FALSE(GetFirstVideoContentDescription(offer.get())->rtcp_mux()); 1073 EXPECT_FALSE(GetFirstDataContentDescription(offer.get())->rtcp_mux()); 1074 EXPECT_FALSE(GetFirstAudioContentDescription(answer.get())->rtcp_mux()); 1075 EXPECT_FALSE(GetFirstVideoContentDescription(answer.get())->rtcp_mux()); 1076 EXPECT_FALSE(GetFirstDataContentDescription(answer.get())->rtcp_mux()); 1077 } 1078 1079 // Create an audio-only answer to a video offer. 1080 TEST_F(MediaSessionDescriptionFactoryTest, TestCreateAudioAnswerToVideo) { 1081 MediaSessionOptions opts; 1082 opts.has_video = true; 1083 rtc::scoped_ptr<SessionDescription> 1084 offer(f1_.CreateOffer(opts, NULL)); 1085 ASSERT_TRUE(offer.get() != NULL); 1086 rtc::scoped_ptr<SessionDescription> answer( 1087 f2_.CreateAnswer(offer.get(), MediaSessionOptions(), NULL)); 1088 const ContentInfo* ac = answer->GetContentByName("audio"); 1089 const ContentInfo* vc = answer->GetContentByName("video"); 1090 ASSERT_TRUE(ac != NULL); 1091 ASSERT_TRUE(vc != NULL); 1092 ASSERT_TRUE(vc->description != NULL); 1093 EXPECT_TRUE(vc->rejected); 1094 } 1095 1096 // Create an audio-only answer to an offer with data. 1097 TEST_F(MediaSessionDescriptionFactoryTest, TestCreateNoDataAnswerToDataOffer) { 1098 MediaSessionOptions opts; 1099 opts.data_channel_type = cricket::DCT_RTP; 1100 rtc::scoped_ptr<SessionDescription> 1101 offer(f1_.CreateOffer(opts, NULL)); 1102 ASSERT_TRUE(offer.get() != NULL); 1103 rtc::scoped_ptr<SessionDescription> answer( 1104 f2_.CreateAnswer(offer.get(), MediaSessionOptions(), NULL)); 1105 const ContentInfo* ac = answer->GetContentByName("audio"); 1106 const ContentInfo* dc = answer->GetContentByName("data"); 1107 ASSERT_TRUE(ac != NULL); 1108 ASSERT_TRUE(dc != NULL); 1109 ASSERT_TRUE(dc->description != NULL); 1110 EXPECT_TRUE(dc->rejected); 1111 } 1112 1113 // Create an answer that rejects the contents which are rejected in the offer. 1114 TEST_F(MediaSessionDescriptionFactoryTest, 1115 CreateAnswerToOfferWithRejectedMedia) { 1116 MediaSessionOptions opts; 1117 opts.has_video = true; 1118 opts.data_channel_type = cricket::DCT_RTP; 1119 rtc::scoped_ptr<SessionDescription> 1120 offer(f1_.CreateOffer(opts, NULL)); 1121 ASSERT_TRUE(offer.get() != NULL); 1122 ContentInfo* ac = offer->GetContentByName("audio"); 1123 ContentInfo* vc = offer->GetContentByName("video"); 1124 ContentInfo* dc = offer->GetContentByName("data"); 1125 ASSERT_TRUE(ac != NULL); 1126 ASSERT_TRUE(vc != NULL); 1127 ASSERT_TRUE(dc != NULL); 1128 ac->rejected = true; 1129 vc->rejected = true; 1130 dc->rejected = true; 1131 rtc::scoped_ptr<SessionDescription> answer( 1132 f2_.CreateAnswer(offer.get(), opts, NULL)); 1133 ac = answer->GetContentByName("audio"); 1134 vc = answer->GetContentByName("video"); 1135 dc = answer->GetContentByName("data"); 1136 ASSERT_TRUE(ac != NULL); 1137 ASSERT_TRUE(vc != NULL); 1138 ASSERT_TRUE(dc != NULL); 1139 EXPECT_TRUE(ac->rejected); 1140 EXPECT_TRUE(vc->rejected); 1141 EXPECT_TRUE(dc->rejected); 1142 } 1143 1144 // Create an audio and video offer with: 1145 // - one video track 1146 // - two audio tracks 1147 // - two data tracks 1148 // and ensure it matches what we expect. Also updates the initial offer by 1149 // adding a new video track and replaces one of the audio tracks. 1150 TEST_F(MediaSessionDescriptionFactoryTest, TestCreateMultiStreamVideoOffer) { 1151 MediaSessionOptions opts; 1152 opts.AddStream(MEDIA_TYPE_VIDEO, kVideoTrack1, kMediaStream1); 1153 opts.AddStream(MEDIA_TYPE_AUDIO, kAudioTrack1, kMediaStream1); 1154 opts.AddStream(MEDIA_TYPE_AUDIO, kAudioTrack2, kMediaStream1); 1155 opts.data_channel_type = cricket::DCT_RTP; 1156 opts.AddStream(MEDIA_TYPE_DATA, kDataTrack1, kMediaStream1); 1157 opts.AddStream(MEDIA_TYPE_DATA, kDataTrack2, kMediaStream1); 1158 1159 f1_.set_secure(SEC_ENABLED); 1160 rtc::scoped_ptr<SessionDescription> offer(f1_.CreateOffer(opts, NULL)); 1161 1162 ASSERT_TRUE(offer.get() != NULL); 1163 const ContentInfo* ac = offer->GetContentByName("audio"); 1164 const ContentInfo* vc = offer->GetContentByName("video"); 1165 const ContentInfo* dc = offer->GetContentByName("data"); 1166 ASSERT_TRUE(ac != NULL); 1167 ASSERT_TRUE(vc != NULL); 1168 ASSERT_TRUE(dc != NULL); 1169 const AudioContentDescription* acd = 1170 static_cast<const AudioContentDescription*>(ac->description); 1171 const VideoContentDescription* vcd = 1172 static_cast<const VideoContentDescription*>(vc->description); 1173 const DataContentDescription* dcd = 1174 static_cast<const DataContentDescription*>(dc->description); 1175 EXPECT_EQ(MEDIA_TYPE_AUDIO, acd->type()); 1176 EXPECT_EQ(f1_.audio_codecs(), acd->codecs()); 1177 1178 const StreamParamsVec& audio_streams = acd->streams(); 1179 ASSERT_EQ(2U, audio_streams.size()); 1180 EXPECT_EQ(audio_streams[0].cname , audio_streams[1].cname); 1181 EXPECT_EQ(kAudioTrack1, audio_streams[0].id); 1182 ASSERT_EQ(1U, audio_streams[0].ssrcs.size()); 1183 EXPECT_NE(0U, audio_streams[0].ssrcs[0]); 1184 EXPECT_EQ(kAudioTrack2, audio_streams[1].id); 1185 ASSERT_EQ(1U, audio_streams[1].ssrcs.size()); 1186 EXPECT_NE(0U, audio_streams[1].ssrcs[0]); 1187 1188 EXPECT_EQ(kAutoBandwidth, acd->bandwidth()); // default bandwidth (auto) 1189 EXPECT_TRUE(acd->rtcp_mux()); // rtcp-mux defaults on 1190 ASSERT_CRYPTO(acd, 2U, CS_AES_CM_128_HMAC_SHA1_32); 1191 1192 EXPECT_EQ(MEDIA_TYPE_VIDEO, vcd->type()); 1193 EXPECT_EQ(f1_.video_codecs(), vcd->codecs()); 1194 ASSERT_CRYPTO(vcd, 1U, CS_AES_CM_128_HMAC_SHA1_80); 1195 1196 const StreamParamsVec& video_streams = vcd->streams(); 1197 ASSERT_EQ(1U, video_streams.size()); 1198 EXPECT_EQ(video_streams[0].cname, audio_streams[0].cname); 1199 EXPECT_EQ(kVideoTrack1, video_streams[0].id); 1200 EXPECT_EQ(kAutoBandwidth, vcd->bandwidth()); // default bandwidth (auto) 1201 EXPECT_TRUE(vcd->rtcp_mux()); // rtcp-mux defaults on 1202 1203 EXPECT_EQ(MEDIA_TYPE_DATA, dcd->type()); 1204 EXPECT_EQ(f1_.data_codecs(), dcd->codecs()); 1205 ASSERT_CRYPTO(dcd, 1U, CS_AES_CM_128_HMAC_SHA1_80); 1206 1207 const StreamParamsVec& data_streams = dcd->streams(); 1208 ASSERT_EQ(2U, data_streams.size()); 1209 EXPECT_EQ(data_streams[0].cname , data_streams[1].cname); 1210 EXPECT_EQ(kDataTrack1, data_streams[0].id); 1211 ASSERT_EQ(1U, data_streams[0].ssrcs.size()); 1212 EXPECT_NE(0U, data_streams[0].ssrcs[0]); 1213 EXPECT_EQ(kDataTrack2, data_streams[1].id); 1214 ASSERT_EQ(1U, data_streams[1].ssrcs.size()); 1215 EXPECT_NE(0U, data_streams[1].ssrcs[0]); 1216 1217 EXPECT_EQ(cricket::kDataMaxBandwidth, 1218 dcd->bandwidth()); // default bandwidth (auto) 1219 EXPECT_TRUE(dcd->rtcp_mux()); // rtcp-mux defaults on 1220 ASSERT_CRYPTO(dcd, 1U, CS_AES_CM_128_HMAC_SHA1_80); 1221 1222 1223 // Update the offer. Add a new video track that is not synched to the 1224 // other tracks and replace audio track 2 with audio track 3. 1225 opts.AddStream(MEDIA_TYPE_VIDEO, kVideoTrack2, kMediaStream2); 1226 opts.RemoveStream(MEDIA_TYPE_AUDIO, kAudioTrack2); 1227 opts.AddStream(MEDIA_TYPE_AUDIO, kAudioTrack3, kMediaStream1); 1228 opts.RemoveStream(MEDIA_TYPE_DATA, kDataTrack2); 1229 opts.AddStream(MEDIA_TYPE_DATA, kDataTrack3, kMediaStream1); 1230 rtc::scoped_ptr<SessionDescription> 1231 updated_offer(f1_.CreateOffer(opts, offer.get())); 1232 1233 ASSERT_TRUE(updated_offer.get() != NULL); 1234 ac = updated_offer->GetContentByName("audio"); 1235 vc = updated_offer->GetContentByName("video"); 1236 dc = updated_offer->GetContentByName("data"); 1237 ASSERT_TRUE(ac != NULL); 1238 ASSERT_TRUE(vc != NULL); 1239 ASSERT_TRUE(dc != NULL); 1240 const AudioContentDescription* updated_acd = 1241 static_cast<const AudioContentDescription*>(ac->description); 1242 const VideoContentDescription* updated_vcd = 1243 static_cast<const VideoContentDescription*>(vc->description); 1244 const DataContentDescription* updated_dcd = 1245 static_cast<const DataContentDescription*>(dc->description); 1246 1247 EXPECT_EQ(acd->type(), updated_acd->type()); 1248 EXPECT_EQ(acd->codecs(), updated_acd->codecs()); 1249 EXPECT_EQ(vcd->type(), updated_vcd->type()); 1250 EXPECT_EQ(vcd->codecs(), updated_vcd->codecs()); 1251 EXPECT_EQ(dcd->type(), updated_dcd->type()); 1252 EXPECT_EQ(dcd->codecs(), updated_dcd->codecs()); 1253 ASSERT_CRYPTO(updated_acd, 2U, CS_AES_CM_128_HMAC_SHA1_32); 1254 EXPECT_TRUE(CompareCryptoParams(acd->cryptos(), updated_acd->cryptos())); 1255 ASSERT_CRYPTO(updated_vcd, 1U, CS_AES_CM_128_HMAC_SHA1_80); 1256 EXPECT_TRUE(CompareCryptoParams(vcd->cryptos(), updated_vcd->cryptos())); 1257 ASSERT_CRYPTO(updated_dcd, 1U, CS_AES_CM_128_HMAC_SHA1_80); 1258 EXPECT_TRUE(CompareCryptoParams(dcd->cryptos(), updated_dcd->cryptos())); 1259 1260 const StreamParamsVec& updated_audio_streams = updated_acd->streams(); 1261 ASSERT_EQ(2U, updated_audio_streams.size()); 1262 EXPECT_EQ(audio_streams[0], updated_audio_streams[0]); 1263 EXPECT_EQ(kAudioTrack3, updated_audio_streams[1].id); // New audio track. 1264 ASSERT_EQ(1U, updated_audio_streams[1].ssrcs.size()); 1265 EXPECT_NE(0U, updated_audio_streams[1].ssrcs[0]); 1266 EXPECT_EQ(updated_audio_streams[0].cname, updated_audio_streams[1].cname); 1267 1268 const StreamParamsVec& updated_video_streams = updated_vcd->streams(); 1269 ASSERT_EQ(2U, updated_video_streams.size()); 1270 EXPECT_EQ(video_streams[0], updated_video_streams[0]); 1271 EXPECT_EQ(kVideoTrack2, updated_video_streams[1].id); 1272 EXPECT_NE(updated_video_streams[1].cname, updated_video_streams[0].cname); 1273 1274 const StreamParamsVec& updated_data_streams = updated_dcd->streams(); 1275 ASSERT_EQ(2U, updated_data_streams.size()); 1276 EXPECT_EQ(data_streams[0], updated_data_streams[0]); 1277 EXPECT_EQ(kDataTrack3, updated_data_streams[1].id); // New data track. 1278 ASSERT_EQ(1U, updated_data_streams[1].ssrcs.size()); 1279 EXPECT_NE(0U, updated_data_streams[1].ssrcs[0]); 1280 EXPECT_EQ(updated_data_streams[0].cname, updated_data_streams[1].cname); 1281 } 1282 1283 // Create an offer with simulcast video stream. 1284 TEST_F(MediaSessionDescriptionFactoryTest, TestCreateSimulcastVideoOffer) { 1285 MediaSessionOptions opts; 1286 const int num_sim_layers = 3; 1287 opts.AddVideoStream(kVideoTrack1, kMediaStream1, num_sim_layers); 1288 rtc::scoped_ptr<SessionDescription> offer(f1_.CreateOffer(opts, NULL)); 1289 1290 ASSERT_TRUE(offer.get() != NULL); 1291 const ContentInfo* vc = offer->GetContentByName("video"); 1292 ASSERT_TRUE(vc != NULL); 1293 const VideoContentDescription* vcd = 1294 static_cast<const VideoContentDescription*>(vc->description); 1295 1296 const StreamParamsVec& video_streams = vcd->streams(); 1297 ASSERT_EQ(1U, video_streams.size()); 1298 EXPECT_EQ(kVideoTrack1, video_streams[0].id); 1299 const SsrcGroup* sim_ssrc_group = 1300 video_streams[0].get_ssrc_group(cricket::kSimSsrcGroupSemantics); 1301 ASSERT_TRUE(sim_ssrc_group != NULL); 1302 EXPECT_EQ(static_cast<size_t>(num_sim_layers), sim_ssrc_group->ssrcs.size()); 1303 } 1304 1305 // Create an audio and video answer to a standard video offer with: 1306 // - one video track 1307 // - two audio tracks 1308 // - two data tracks 1309 // and ensure it matches what we expect. Also updates the initial answer by 1310 // adding a new video track and removes one of the audio tracks. 1311 TEST_F(MediaSessionDescriptionFactoryTest, TestCreateMultiStreamVideoAnswer) { 1312 MediaSessionOptions offer_opts; 1313 offer_opts.has_video = true; 1314 offer_opts.data_channel_type = cricket::DCT_RTP; 1315 f1_.set_secure(SEC_ENABLED); 1316 f2_.set_secure(SEC_ENABLED); 1317 rtc::scoped_ptr<SessionDescription> offer(f1_.CreateOffer(offer_opts, 1318 NULL)); 1319 1320 MediaSessionOptions opts; 1321 opts.AddStream(MEDIA_TYPE_VIDEO, kVideoTrack1, kMediaStream1); 1322 opts.AddStream(MEDIA_TYPE_AUDIO, kAudioTrack1, kMediaStream1); 1323 opts.AddStream(MEDIA_TYPE_AUDIO, kAudioTrack2, kMediaStream1); 1324 opts.data_channel_type = cricket::DCT_RTP; 1325 opts.AddStream(MEDIA_TYPE_DATA, kDataTrack1, kMediaStream1); 1326 opts.AddStream(MEDIA_TYPE_DATA, kDataTrack2, kMediaStream1); 1327 1328 rtc::scoped_ptr<SessionDescription> 1329 answer(f2_.CreateAnswer(offer.get(), opts, NULL)); 1330 1331 ASSERT_TRUE(answer.get() != NULL); 1332 const ContentInfo* ac = answer->GetContentByName("audio"); 1333 const ContentInfo* vc = answer->GetContentByName("video"); 1334 const ContentInfo* dc = answer->GetContentByName("data"); 1335 ASSERT_TRUE(ac != NULL); 1336 ASSERT_TRUE(vc != NULL); 1337 ASSERT_TRUE(dc != NULL); 1338 const AudioContentDescription* acd = 1339 static_cast<const AudioContentDescription*>(ac->description); 1340 const VideoContentDescription* vcd = 1341 static_cast<const VideoContentDescription*>(vc->description); 1342 const DataContentDescription* dcd = 1343 static_cast<const DataContentDescription*>(dc->description); 1344 ASSERT_CRYPTO(acd, 1U, CS_AES_CM_128_HMAC_SHA1_32); 1345 ASSERT_CRYPTO(vcd, 1U, CS_AES_CM_128_HMAC_SHA1_80); 1346 ASSERT_CRYPTO(dcd, 1U, CS_AES_CM_128_HMAC_SHA1_80); 1347 1348 EXPECT_EQ(MEDIA_TYPE_AUDIO, acd->type()); 1349 EXPECT_EQ(MAKE_VECTOR(kAudioCodecsAnswer), acd->codecs()); 1350 1351 const StreamParamsVec& audio_streams = acd->streams(); 1352 ASSERT_EQ(2U, audio_streams.size()); 1353 EXPECT_TRUE(audio_streams[0].cname == audio_streams[1].cname); 1354 EXPECT_EQ(kAudioTrack1, audio_streams[0].id); 1355 ASSERT_EQ(1U, audio_streams[0].ssrcs.size()); 1356 EXPECT_NE(0U, audio_streams[0].ssrcs[0]); 1357 EXPECT_EQ(kAudioTrack2, audio_streams[1].id); 1358 ASSERT_EQ(1U, audio_streams[1].ssrcs.size()); 1359 EXPECT_NE(0U, audio_streams[1].ssrcs[0]); 1360 1361 EXPECT_EQ(kAutoBandwidth, acd->bandwidth()); // default bandwidth (auto) 1362 EXPECT_TRUE(acd->rtcp_mux()); // rtcp-mux defaults on 1363 1364 EXPECT_EQ(MEDIA_TYPE_VIDEO, vcd->type()); 1365 EXPECT_EQ(MAKE_VECTOR(kVideoCodecsAnswer), vcd->codecs()); 1366 1367 const StreamParamsVec& video_streams = vcd->streams(); 1368 ASSERT_EQ(1U, video_streams.size()); 1369 EXPECT_EQ(video_streams[0].cname, audio_streams[0].cname); 1370 EXPECT_EQ(kVideoTrack1, video_streams[0].id); 1371 EXPECT_EQ(kAutoBandwidth, vcd->bandwidth()); // default bandwidth (auto) 1372 EXPECT_TRUE(vcd->rtcp_mux()); // rtcp-mux defaults on 1373 1374 EXPECT_EQ(MEDIA_TYPE_DATA, dcd->type()); 1375 EXPECT_EQ(MAKE_VECTOR(kDataCodecsAnswer), dcd->codecs()); 1376 1377 const StreamParamsVec& data_streams = dcd->streams(); 1378 ASSERT_EQ(2U, data_streams.size()); 1379 EXPECT_TRUE(data_streams[0].cname == data_streams[1].cname); 1380 EXPECT_EQ(kDataTrack1, data_streams[0].id); 1381 ASSERT_EQ(1U, data_streams[0].ssrcs.size()); 1382 EXPECT_NE(0U, data_streams[0].ssrcs[0]); 1383 EXPECT_EQ(kDataTrack2, data_streams[1].id); 1384 ASSERT_EQ(1U, data_streams[1].ssrcs.size()); 1385 EXPECT_NE(0U, data_streams[1].ssrcs[0]); 1386 1387 EXPECT_EQ(cricket::kDataMaxBandwidth, 1388 dcd->bandwidth()); // default bandwidth (auto) 1389 EXPECT_TRUE(dcd->rtcp_mux()); // rtcp-mux defaults on 1390 1391 // Update the answer. Add a new video track that is not synched to the 1392 // other traacks and remove 1 audio track. 1393 opts.AddStream(MEDIA_TYPE_VIDEO, kVideoTrack2, kMediaStream2); 1394 opts.RemoveStream(MEDIA_TYPE_AUDIO, kAudioTrack2); 1395 opts.RemoveStream(MEDIA_TYPE_DATA, kDataTrack2); 1396 rtc::scoped_ptr<SessionDescription> 1397 updated_answer(f2_.CreateAnswer(offer.get(), opts, answer.get())); 1398 1399 ASSERT_TRUE(updated_answer.get() != NULL); 1400 ac = updated_answer->GetContentByName("audio"); 1401 vc = updated_answer->GetContentByName("video"); 1402 dc = updated_answer->GetContentByName("data"); 1403 ASSERT_TRUE(ac != NULL); 1404 ASSERT_TRUE(vc != NULL); 1405 ASSERT_TRUE(dc != NULL); 1406 const AudioContentDescription* updated_acd = 1407 static_cast<const AudioContentDescription*>(ac->description); 1408 const VideoContentDescription* updated_vcd = 1409 static_cast<const VideoContentDescription*>(vc->description); 1410 const DataContentDescription* updated_dcd = 1411 static_cast<const DataContentDescription*>(dc->description); 1412 1413 ASSERT_CRYPTO(updated_acd, 1U, CS_AES_CM_128_HMAC_SHA1_32); 1414 EXPECT_TRUE(CompareCryptoParams(acd->cryptos(), updated_acd->cryptos())); 1415 ASSERT_CRYPTO(updated_vcd, 1U, CS_AES_CM_128_HMAC_SHA1_80); 1416 EXPECT_TRUE(CompareCryptoParams(vcd->cryptos(), updated_vcd->cryptos())); 1417 ASSERT_CRYPTO(updated_dcd, 1U, CS_AES_CM_128_HMAC_SHA1_80); 1418 EXPECT_TRUE(CompareCryptoParams(dcd->cryptos(), updated_dcd->cryptos())); 1419 1420 EXPECT_EQ(acd->type(), updated_acd->type()); 1421 EXPECT_EQ(acd->codecs(), updated_acd->codecs()); 1422 EXPECT_EQ(vcd->type(), updated_vcd->type()); 1423 EXPECT_EQ(vcd->codecs(), updated_vcd->codecs()); 1424 EXPECT_EQ(dcd->type(), updated_dcd->type()); 1425 EXPECT_EQ(dcd->codecs(), updated_dcd->codecs()); 1426 1427 const StreamParamsVec& updated_audio_streams = updated_acd->streams(); 1428 ASSERT_EQ(1U, updated_audio_streams.size()); 1429 EXPECT_TRUE(audio_streams[0] == updated_audio_streams[0]); 1430 1431 const StreamParamsVec& updated_video_streams = updated_vcd->streams(); 1432 ASSERT_EQ(2U, updated_video_streams.size()); 1433 EXPECT_EQ(video_streams[0], updated_video_streams[0]); 1434 EXPECT_EQ(kVideoTrack2, updated_video_streams[1].id); 1435 EXPECT_NE(updated_video_streams[1].cname, updated_video_streams[0].cname); 1436 1437 const StreamParamsVec& updated_data_streams = updated_dcd->streams(); 1438 ASSERT_EQ(1U, updated_data_streams.size()); 1439 EXPECT_TRUE(data_streams[0] == updated_data_streams[0]); 1440 } 1441 1442 1443 // Create an updated offer after creating an answer to the original offer and 1444 // verify that the codecs that were part of the original answer are not changed 1445 // in the updated offer. 1446 TEST_F(MediaSessionDescriptionFactoryTest, 1447 RespondentCreatesOfferAfterCreatingAnswer) { 1448 MediaSessionOptions opts; 1449 opts.has_audio = true; 1450 opts.has_video = true; 1451 1452 rtc::scoped_ptr<SessionDescription> offer(f1_.CreateOffer(opts, NULL)); 1453 rtc::scoped_ptr<SessionDescription> answer( 1454 f2_.CreateAnswer(offer.get(), opts, NULL)); 1455 1456 const AudioContentDescription* acd = 1457 GetFirstAudioContentDescription(answer.get()); 1458 EXPECT_EQ(MAKE_VECTOR(kAudioCodecsAnswer), acd->codecs()); 1459 1460 const VideoContentDescription* vcd = 1461 GetFirstVideoContentDescription(answer.get()); 1462 EXPECT_EQ(MAKE_VECTOR(kVideoCodecsAnswer), vcd->codecs()); 1463 1464 rtc::scoped_ptr<SessionDescription> updated_offer( 1465 f2_.CreateOffer(opts, answer.get())); 1466 1467 // The expected audio codecs are the common audio codecs from the first 1468 // offer/answer exchange plus the audio codecs only |f2_| offer, sorted in 1469 // preference order. 1470 // TODO(wu): |updated_offer| should not include the codec 1471 // (i.e. |kAudioCodecs2[0]|) the other side doesn't support. 1472 const AudioCodec kUpdatedAudioCodecOffer[] = { 1473 kAudioCodecsAnswer[0], 1474 kAudioCodecsAnswer[1], 1475 kAudioCodecs2[0], 1476 }; 1477 1478 // The expected video codecs are the common video codecs from the first 1479 // offer/answer exchange plus the video codecs only |f2_| offer, sorted in 1480 // preference order. 1481 const VideoCodec kUpdatedVideoCodecOffer[] = { 1482 kVideoCodecsAnswer[0], 1483 kVideoCodecs2[1], 1484 }; 1485 1486 const AudioContentDescription* updated_acd = 1487 GetFirstAudioContentDescription(updated_offer.get()); 1488 EXPECT_EQ(MAKE_VECTOR(kUpdatedAudioCodecOffer), updated_acd->codecs()); 1489 1490 const VideoContentDescription* updated_vcd = 1491 GetFirstVideoContentDescription(updated_offer.get()); 1492 EXPECT_EQ(MAKE_VECTOR(kUpdatedVideoCodecOffer), updated_vcd->codecs()); 1493 } 1494 1495 // Create an updated offer after creating an answer to the original offer and 1496 // verify that the codecs that were part of the original answer are not changed 1497 // in the updated offer. In this test Rtx is enabled. 1498 TEST_F(MediaSessionDescriptionFactoryTest, 1499 RespondentCreatesOfferAfterCreatingAnswerWithRtx) { 1500 MediaSessionOptions opts; 1501 opts.has_video = true; 1502 opts.has_audio = false; 1503 std::vector<VideoCodec> f1_codecs = MAKE_VECTOR(kVideoCodecs1); 1504 VideoCodec rtx_f1; 1505 rtx_f1.id = 126; 1506 rtx_f1.name = cricket::kRtxCodecName; 1507 1508 // This creates rtx for H264 with the payload type |f1_| uses. 1509 rtx_f1.params[cricket::kCodecParamAssociatedPayloadType] = 1510 rtc::ToString<int>(kVideoCodecs1[1].id); 1511 f1_codecs.push_back(rtx_f1); 1512 f1_.set_video_codecs(f1_codecs); 1513 1514 std::vector<VideoCodec> f2_codecs = MAKE_VECTOR(kVideoCodecs2); 1515 VideoCodec rtx_f2; 1516 rtx_f2.id = 127; 1517 rtx_f2.name = cricket::kRtxCodecName; 1518 1519 // This creates rtx for H264 with the payload type |f2_| uses. 1520 rtx_f2.params[cricket::kCodecParamAssociatedPayloadType] = 1521 rtc::ToString<int>(kVideoCodecs2[0].id); 1522 f2_codecs.push_back(rtx_f2); 1523 f2_.set_video_codecs(f2_codecs); 1524 1525 rtc::scoped_ptr<SessionDescription> offer(f1_.CreateOffer(opts, NULL)); 1526 ASSERT_TRUE(offer.get() != NULL); 1527 rtc::scoped_ptr<SessionDescription> answer( 1528 f2_.CreateAnswer(offer.get(), opts, NULL)); 1529 1530 const VideoContentDescription* vcd = 1531 GetFirstVideoContentDescription(answer.get()); 1532 1533 std::vector<VideoCodec> expected_codecs = MAKE_VECTOR(kVideoCodecsAnswer); 1534 expected_codecs.push_back(rtx_f1); 1535 1536 EXPECT_EQ(expected_codecs, vcd->codecs()); 1537 1538 // Now, make sure we get same result, except for the preference order, 1539 // if |f2_| creates an updated offer even though the default payload types 1540 // are different from |f1_|. 1541 expected_codecs[0].preference = f1_codecs[1].preference; 1542 1543 rtc::scoped_ptr<SessionDescription> updated_offer( 1544 f2_.CreateOffer(opts, answer.get())); 1545 ASSERT_TRUE(updated_offer); 1546 rtc::scoped_ptr<SessionDescription> updated_answer( 1547 f1_.CreateAnswer(updated_offer.get(), opts, answer.get())); 1548 1549 const VideoContentDescription* updated_vcd = 1550 GetFirstVideoContentDescription(updated_answer.get()); 1551 1552 EXPECT_EQ(expected_codecs, updated_vcd->codecs()); 1553 } 1554 1555 // Create an updated offer that adds video after creating an audio only answer 1556 // to the original offer. This test verifies that if a video codec and the RTX 1557 // codec have the same default payload type as an audio codec that is already in 1558 // use, the added codecs payload types are changed. 1559 TEST_F(MediaSessionDescriptionFactoryTest, 1560 RespondentCreatesOfferWithVideoAndRtxAfterCreatingAudioAnswer) { 1561 std::vector<VideoCodec> f1_codecs = MAKE_VECTOR(kVideoCodecs1); 1562 VideoCodec rtx_f1; 1563 rtx_f1.id = 126; 1564 rtx_f1.name = cricket::kRtxCodecName; 1565 1566 // This creates rtx for H264 with the payload type |f1_| uses. 1567 rtx_f1.params[cricket::kCodecParamAssociatedPayloadType] = 1568 rtc::ToString<int>(kVideoCodecs1[1].id); 1569 f1_codecs.push_back(rtx_f1); 1570 f1_.set_video_codecs(f1_codecs); 1571 1572 MediaSessionOptions opts; 1573 opts.has_audio = true; 1574 opts.has_video = false; 1575 1576 rtc::scoped_ptr<SessionDescription> offer(f1_.CreateOffer(opts, NULL)); 1577 rtc::scoped_ptr<SessionDescription> answer( 1578 f2_.CreateAnswer(offer.get(), opts, NULL)); 1579 1580 const AudioContentDescription* acd = 1581 GetFirstAudioContentDescription(answer.get()); 1582 EXPECT_EQ(MAKE_VECTOR(kAudioCodecsAnswer), acd->codecs()); 1583 1584 // Now - let |f2_| add video with RTX and let the payload type the RTX codec 1585 // reference be the same as an audio codec that was negotiated in the 1586 // first offer/answer exchange. 1587 opts.has_audio = true; 1588 opts.has_video = true; 1589 1590 std::vector<VideoCodec> f2_codecs = MAKE_VECTOR(kVideoCodecs2); 1591 int used_pl_type = acd->codecs()[0].id; 1592 f2_codecs[0].id = used_pl_type; // Set the payload type for H264. 1593 VideoCodec rtx_f2; 1594 rtx_f2.id = 127; 1595 rtx_f2.name = cricket::kRtxCodecName; 1596 rtx_f2.params[cricket::kCodecParamAssociatedPayloadType] = 1597 rtc::ToString<int>(used_pl_type); 1598 f2_codecs.push_back(rtx_f2); 1599 f2_.set_video_codecs(f2_codecs); 1600 1601 rtc::scoped_ptr<SessionDescription> updated_offer( 1602 f2_.CreateOffer(opts, answer.get())); 1603 ASSERT_TRUE(updated_offer); 1604 rtc::scoped_ptr<SessionDescription> updated_answer( 1605 f1_.CreateAnswer(updated_offer.get(), opts, answer.get())); 1606 1607 const AudioContentDescription* updated_acd = 1608 GetFirstAudioContentDescription(answer.get()); 1609 EXPECT_EQ(MAKE_VECTOR(kAudioCodecsAnswer), updated_acd->codecs()); 1610 1611 const VideoContentDescription* updated_vcd = 1612 GetFirstVideoContentDescription(updated_answer.get()); 1613 1614 ASSERT_EQ("H264", updated_vcd->codecs()[0].name); 1615 ASSERT_EQ(std::string(cricket::kRtxCodecName), updated_vcd->codecs()[1].name); 1616 int new_h264_pl_type = updated_vcd->codecs()[0].id; 1617 EXPECT_NE(used_pl_type, new_h264_pl_type); 1618 VideoCodec rtx = updated_vcd->codecs()[1]; 1619 int pt_referenced_by_rtx = rtc::FromString<int>( 1620 rtx.params[cricket::kCodecParamAssociatedPayloadType]); 1621 EXPECT_EQ(new_h264_pl_type, pt_referenced_by_rtx); 1622 } 1623 1624 // Test that RTX is ignored when there is no associated payload type parameter. 1625 TEST_F(MediaSessionDescriptionFactoryTest, RtxWithoutApt) { 1626 MediaSessionOptions opts; 1627 opts.has_video = true; 1628 opts.has_audio = false; 1629 std::vector<VideoCodec> f1_codecs = MAKE_VECTOR(kVideoCodecs1); 1630 VideoCodec rtx_f1; 1631 rtx_f1.id = 126; 1632 rtx_f1.name = cricket::kRtxCodecName; 1633 1634 f1_codecs.push_back(rtx_f1); 1635 f1_.set_video_codecs(f1_codecs); 1636 1637 std::vector<VideoCodec> f2_codecs = MAKE_VECTOR(kVideoCodecs2); 1638 VideoCodec rtx_f2; 1639 rtx_f2.id = 127; 1640 rtx_f2.name = cricket::kRtxCodecName; 1641 1642 // This creates rtx for H264 with the payload type |f2_| uses. 1643 rtx_f2.SetParam(cricket::kCodecParamAssociatedPayloadType, 1644 rtc::ToString<int>(kVideoCodecs2[0].id)); 1645 f2_codecs.push_back(rtx_f2); 1646 f2_.set_video_codecs(f2_codecs); 1647 1648 rtc::scoped_ptr<SessionDescription> offer(f1_.CreateOffer(opts, NULL)); 1649 ASSERT_TRUE(offer.get() != NULL); 1650 // kCodecParamAssociatedPayloadType will always be added to the offer when RTX 1651 // is selected. Manually remove kCodecParamAssociatedPayloadType so that it 1652 // is possible to test that that RTX is dropped when 1653 // kCodecParamAssociatedPayloadType is missing in the offer. 1654 VideoContentDescription* desc = 1655 static_cast<cricket::VideoContentDescription*>( 1656 offer->GetContentDescriptionByName(cricket::CN_VIDEO)); 1657 ASSERT_TRUE(desc != NULL); 1658 std::vector<VideoCodec> codecs = desc->codecs(); 1659 for (std::vector<VideoCodec>::iterator iter = codecs.begin(); 1660 iter != codecs.end(); ++iter) { 1661 if (iter->name.find(cricket::kRtxCodecName) == 0) { 1662 iter->params.clear(); 1663 } 1664 } 1665 desc->set_codecs(codecs); 1666 1667 rtc::scoped_ptr<SessionDescription> answer( 1668 f2_.CreateAnswer(offer.get(), opts, NULL)); 1669 1670 const VideoContentDescription* vcd = 1671 GetFirstVideoContentDescription(answer.get()); 1672 1673 for (std::vector<VideoCodec>::const_iterator iter = vcd->codecs().begin(); 1674 iter != vcd->codecs().end(); ++iter) { 1675 ASSERT_STRNE(iter->name.c_str(), cricket::kRtxCodecName); 1676 } 1677 } 1678 1679 // Create an updated offer after creating an answer to the original offer and 1680 // verify that the RTP header extensions that were part of the original answer 1681 // are not changed in the updated offer. 1682 TEST_F(MediaSessionDescriptionFactoryTest, 1683 RespondentCreatesOfferAfterCreatingAnswerWithRtpExtensions) { 1684 MediaSessionOptions opts; 1685 opts.has_audio = true; 1686 opts.has_video = true; 1687 1688 f1_.set_audio_rtp_header_extensions(MAKE_VECTOR(kAudioRtpExtension1)); 1689 f1_.set_video_rtp_header_extensions(MAKE_VECTOR(kVideoRtpExtension1)); 1690 f2_.set_audio_rtp_header_extensions(MAKE_VECTOR(kAudioRtpExtension2)); 1691 f2_.set_video_rtp_header_extensions(MAKE_VECTOR(kVideoRtpExtension2)); 1692 1693 rtc::scoped_ptr<SessionDescription> offer(f1_.CreateOffer(opts, NULL)); 1694 rtc::scoped_ptr<SessionDescription> answer( 1695 f2_.CreateAnswer(offer.get(), opts, NULL)); 1696 1697 EXPECT_EQ(MAKE_VECTOR(kAudioRtpExtensionAnswer), 1698 GetFirstAudioContentDescription( 1699 answer.get())->rtp_header_extensions()); 1700 EXPECT_EQ(MAKE_VECTOR(kVideoRtpExtensionAnswer), 1701 GetFirstVideoContentDescription( 1702 answer.get())->rtp_header_extensions()); 1703 1704 rtc::scoped_ptr<SessionDescription> updated_offer( 1705 f2_.CreateOffer(opts, answer.get())); 1706 1707 // The expected RTP header extensions in the new offer are the resulting 1708 // extensions from the first offer/answer exchange plus the extensions only 1709 // |f2_| offer. 1710 // Since the default local extension id |f2_| uses has already been used by 1711 // |f1_| for another extensions, it is changed to 13. 1712 const RtpHeaderExtension kUpdatedAudioRtpExtensions[] = { 1713 kAudioRtpExtensionAnswer[0], 1714 RtpHeaderExtension(kAudioRtpExtension2[1].uri, 13), 1715 kAudioRtpExtension2[2], 1716 }; 1717 1718 // Since the default local extension id |f2_| uses has already been used by 1719 // |f1_| for another extensions, is is changed to 12. 1720 const RtpHeaderExtension kUpdatedVideoRtpExtensions[] = { 1721 kVideoRtpExtensionAnswer[0], 1722 RtpHeaderExtension(kVideoRtpExtension2[1].uri, 12), 1723 kVideoRtpExtension2[2], 1724 }; 1725 1726 const AudioContentDescription* updated_acd = 1727 GetFirstAudioContentDescription(updated_offer.get()); 1728 EXPECT_EQ(MAKE_VECTOR(kUpdatedAudioRtpExtensions), 1729 updated_acd->rtp_header_extensions()); 1730 1731 const VideoContentDescription* updated_vcd = 1732 GetFirstVideoContentDescription(updated_offer.get()); 1733 EXPECT_EQ(MAKE_VECTOR(kUpdatedVideoRtpExtensions), 1734 updated_vcd->rtp_header_extensions()); 1735 } 1736 1737 TEST(MediaSessionDescription, CopySessionDescription) { 1738 SessionDescription source; 1739 cricket::ContentGroup group(cricket::CN_AUDIO); 1740 source.AddGroup(group); 1741 AudioContentDescription* acd(new AudioContentDescription()); 1742 acd->set_codecs(MAKE_VECTOR(kAudioCodecs1)); 1743 acd->AddLegacyStream(1); 1744 source.AddContent(cricket::CN_AUDIO, cricket::NS_JINGLE_RTP, acd); 1745 VideoContentDescription* vcd(new VideoContentDescription()); 1746 vcd->set_codecs(MAKE_VECTOR(kVideoCodecs1)); 1747 vcd->AddLegacyStream(2); 1748 source.AddContent(cricket::CN_VIDEO, cricket::NS_JINGLE_RTP, vcd); 1749 1750 rtc::scoped_ptr<SessionDescription> copy(source.Copy()); 1751 ASSERT_TRUE(copy.get() != NULL); 1752 EXPECT_TRUE(copy->HasGroup(cricket::CN_AUDIO)); 1753 const ContentInfo* ac = copy->GetContentByName("audio"); 1754 const ContentInfo* vc = copy->GetContentByName("video"); 1755 ASSERT_TRUE(ac != NULL); 1756 ASSERT_TRUE(vc != NULL); 1757 EXPECT_EQ(std::string(NS_JINGLE_RTP), ac->type); 1758 const AudioContentDescription* acd_copy = 1759 static_cast<const AudioContentDescription*>(ac->description); 1760 EXPECT_EQ(acd->codecs(), acd_copy->codecs()); 1761 EXPECT_EQ(1u, acd->first_ssrc()); 1762 1763 EXPECT_EQ(std::string(NS_JINGLE_RTP), vc->type); 1764 const VideoContentDescription* vcd_copy = 1765 static_cast<const VideoContentDescription*>(vc->description); 1766 EXPECT_EQ(vcd->codecs(), vcd_copy->codecs()); 1767 EXPECT_EQ(2u, vcd->first_ssrc()); 1768 } 1769 1770 // The below TestTransportInfoXXX tests create different offers/answers, and 1771 // ensure the TransportInfo in the SessionDescription matches what we expect. 1772 TEST_F(MediaSessionDescriptionFactoryTest, TestTransportInfoOfferAudio) { 1773 MediaSessionOptions options; 1774 options.has_audio = true; 1775 TestTransportInfo(true, options, false); 1776 } 1777 1778 TEST_F(MediaSessionDescriptionFactoryTest, TestTransportInfoOfferAudioCurrent) { 1779 MediaSessionOptions options; 1780 options.has_audio = true; 1781 TestTransportInfo(true, options, true); 1782 } 1783 1784 TEST_F(MediaSessionDescriptionFactoryTest, TestTransportInfoOfferMultimedia) { 1785 MediaSessionOptions options; 1786 options.has_audio = true; 1787 options.has_video = true; 1788 options.data_channel_type = cricket::DCT_RTP; 1789 TestTransportInfo(true, options, false); 1790 } 1791 1792 TEST_F(MediaSessionDescriptionFactoryTest, 1793 TestTransportInfoOfferMultimediaCurrent) { 1794 MediaSessionOptions options; 1795 options.has_audio = true; 1796 options.has_video = true; 1797 options.data_channel_type = cricket::DCT_RTP; 1798 TestTransportInfo(true, options, true); 1799 } 1800 1801 TEST_F(MediaSessionDescriptionFactoryTest, TestTransportInfoOfferBundle) { 1802 MediaSessionOptions options; 1803 options.has_audio = true; 1804 options.has_video = true; 1805 options.data_channel_type = cricket::DCT_RTP; 1806 options.bundle_enabled = true; 1807 TestTransportInfo(true, options, false); 1808 } 1809 1810 TEST_F(MediaSessionDescriptionFactoryTest, 1811 TestTransportInfoOfferBundleCurrent) { 1812 MediaSessionOptions options; 1813 options.has_audio = true; 1814 options.has_video = true; 1815 options.data_channel_type = cricket::DCT_RTP; 1816 options.bundle_enabled = true; 1817 TestTransportInfo(true, options, true); 1818 } 1819 1820 TEST_F(MediaSessionDescriptionFactoryTest, TestTransportInfoAnswerAudio) { 1821 MediaSessionOptions options; 1822 options.has_audio = true; 1823 TestTransportInfo(false, options, false); 1824 } 1825 1826 TEST_F(MediaSessionDescriptionFactoryTest, 1827 TestTransportInfoAnswerAudioCurrent) { 1828 MediaSessionOptions options; 1829 options.has_audio = true; 1830 TestTransportInfo(false, options, true); 1831 } 1832 1833 TEST_F(MediaSessionDescriptionFactoryTest, TestTransportInfoAnswerMultimedia) { 1834 MediaSessionOptions options; 1835 options.has_audio = true; 1836 options.has_video = true; 1837 options.data_channel_type = cricket::DCT_RTP; 1838 TestTransportInfo(false, options, false); 1839 } 1840 1841 TEST_F(MediaSessionDescriptionFactoryTest, 1842 TestTransportInfoAnswerMultimediaCurrent) { 1843 MediaSessionOptions options; 1844 options.has_audio = true; 1845 options.has_video = true; 1846 options.data_channel_type = cricket::DCT_RTP; 1847 TestTransportInfo(false, options, true); 1848 } 1849 1850 TEST_F(MediaSessionDescriptionFactoryTest, TestTransportInfoAnswerBundle) { 1851 MediaSessionOptions options; 1852 options.has_audio = true; 1853 options.has_video = true; 1854 options.data_channel_type = cricket::DCT_RTP; 1855 options.bundle_enabled = true; 1856 TestTransportInfo(false, options, false); 1857 } 1858 1859 TEST_F(MediaSessionDescriptionFactoryTest, 1860 TestTransportInfoAnswerBundleCurrent) { 1861 MediaSessionOptions options; 1862 options.has_audio = true; 1863 options.has_video = true; 1864 options.data_channel_type = cricket::DCT_RTP; 1865 options.bundle_enabled = true; 1866 TestTransportInfo(false, options, true); 1867 } 1868 1869 // Create an offer with bundle enabled and verify the crypto parameters are 1870 // the common set of the available cryptos. 1871 TEST_F(MediaSessionDescriptionFactoryTest, TestCryptoWithOfferBundle) { 1872 TestCryptoWithBundle(true); 1873 } 1874 1875 // Create an answer with bundle enabled and verify the crypto parameters are 1876 // the common set of the available cryptos. 1877 TEST_F(MediaSessionDescriptionFactoryTest, TestCryptoWithAnswerBundle) { 1878 TestCryptoWithBundle(false); 1879 } 1880 1881 // Verifies that creating answer fails if the offer has UDP/TLS/RTP/SAVPF but 1882 // DTLS is not enabled locally. 1883 TEST_F(MediaSessionDescriptionFactoryTest, 1884 TestOfferDtlsSavpfWithoutDtlsFailed) { 1885 f1_.set_secure(SEC_ENABLED); 1886 f2_.set_secure(SEC_ENABLED); 1887 tdf1_.set_secure(SEC_DISABLED); 1888 tdf2_.set_secure(SEC_DISABLED); 1889 1890 rtc::scoped_ptr<SessionDescription> offer( 1891 f1_.CreateOffer(MediaSessionOptions(), NULL)); 1892 ASSERT_TRUE(offer.get() != NULL); 1893 ContentInfo* offer_content = offer->GetContentByName("audio"); 1894 ASSERT_TRUE(offer_content != NULL); 1895 AudioContentDescription* offer_audio_desc = 1896 static_cast<AudioContentDescription*>(offer_content->description); 1897 offer_audio_desc->set_protocol(cricket::kMediaProtocolDtlsSavpf); 1898 1899 rtc::scoped_ptr<SessionDescription> answer( 1900 f2_.CreateAnswer(offer.get(), MediaSessionOptions(), NULL)); 1901 ASSERT_TRUE(answer != NULL); 1902 ContentInfo* answer_content = answer->GetContentByName("audio"); 1903 ASSERT_TRUE(answer_content != NULL); 1904 1905 ASSERT_TRUE(answer_content->rejected); 1906 } 1907 1908 // Offers UDP/TLS/RTP/SAVPF and verifies the answer can be created and contains 1909 // UDP/TLS/RTP/SAVPF. 1910 TEST_F(MediaSessionDescriptionFactoryTest, TestOfferDtlsSavpfCreateAnswer) { 1911 f1_.set_secure(SEC_ENABLED); 1912 f2_.set_secure(SEC_ENABLED); 1913 tdf1_.set_secure(SEC_ENABLED); 1914 tdf2_.set_secure(SEC_ENABLED); 1915 1916 rtc::scoped_ptr<SessionDescription> offer( 1917 f1_.CreateOffer(MediaSessionOptions(), NULL)); 1918 ASSERT_TRUE(offer.get() != NULL); 1919 ContentInfo* offer_content = offer->GetContentByName("audio"); 1920 ASSERT_TRUE(offer_content != NULL); 1921 AudioContentDescription* offer_audio_desc = 1922 static_cast<AudioContentDescription*>(offer_content->description); 1923 offer_audio_desc->set_protocol(cricket::kMediaProtocolDtlsSavpf); 1924 1925 rtc::scoped_ptr<SessionDescription> answer( 1926 f2_.CreateAnswer(offer.get(), MediaSessionOptions(), NULL)); 1927 ASSERT_TRUE(answer != NULL); 1928 1929 const ContentInfo* answer_content = answer->GetContentByName("audio"); 1930 ASSERT_TRUE(answer_content != NULL); 1931 ASSERT_FALSE(answer_content->rejected); 1932 1933 const AudioContentDescription* answer_audio_desc = 1934 static_cast<const AudioContentDescription*>(answer_content->description); 1935 EXPECT_EQ(std::string(cricket::kMediaProtocolDtlsSavpf), 1936 answer_audio_desc->protocol()); 1937 } 1938 1939 // Test that we include both SDES and DTLS in the offer, but only include SDES 1940 // in the answer if DTLS isn't negotiated. 1941 TEST_F(MediaSessionDescriptionFactoryTest, TestCryptoDtls) { 1942 f1_.set_secure(SEC_ENABLED); 1943 f2_.set_secure(SEC_ENABLED); 1944 tdf1_.set_secure(SEC_ENABLED); 1945 tdf2_.set_secure(SEC_DISABLED); 1946 MediaSessionOptions options; 1947 options.has_audio = true; 1948 options.has_video = true; 1949 rtc::scoped_ptr<SessionDescription> offer, answer; 1950 const cricket::MediaContentDescription* audio_media_desc; 1951 const cricket::MediaContentDescription* video_media_desc; 1952 const cricket::TransportDescription* audio_trans_desc; 1953 const cricket::TransportDescription* video_trans_desc; 1954 1955 // Generate an offer with SDES and DTLS support. 1956 offer.reset(f1_.CreateOffer(options, NULL)); 1957 ASSERT_TRUE(offer.get() != NULL); 1958 1959 audio_media_desc = static_cast<const cricket::MediaContentDescription*>( 1960 offer->GetContentDescriptionByName("audio")); 1961 ASSERT_TRUE(audio_media_desc != NULL); 1962 video_media_desc = static_cast<const cricket::MediaContentDescription*>( 1963 offer->GetContentDescriptionByName("video")); 1964 ASSERT_TRUE(video_media_desc != NULL); 1965 EXPECT_EQ(2u, audio_media_desc->cryptos().size()); 1966 EXPECT_EQ(1u, video_media_desc->cryptos().size()); 1967 1968 audio_trans_desc = offer->GetTransportDescriptionByName("audio"); 1969 ASSERT_TRUE(audio_trans_desc != NULL); 1970 video_trans_desc = offer->GetTransportDescriptionByName("video"); 1971 ASSERT_TRUE(video_trans_desc != NULL); 1972 ASSERT_TRUE(audio_trans_desc->identity_fingerprint.get() != NULL); 1973 ASSERT_TRUE(video_trans_desc->identity_fingerprint.get() != NULL); 1974 1975 // Generate an answer with only SDES support, since tdf2 has crypto disabled. 1976 answer.reset(f2_.CreateAnswer(offer.get(), options, NULL)); 1977 ASSERT_TRUE(answer.get() != NULL); 1978 1979 audio_media_desc = static_cast<const cricket::MediaContentDescription*>( 1980 answer->GetContentDescriptionByName("audio")); 1981 ASSERT_TRUE(audio_media_desc != NULL); 1982 video_media_desc = static_cast<const cricket::MediaContentDescription*>( 1983 answer->GetContentDescriptionByName("video")); 1984 ASSERT_TRUE(video_media_desc != NULL); 1985 EXPECT_EQ(1u, audio_media_desc->cryptos().size()); 1986 EXPECT_EQ(1u, video_media_desc->cryptos().size()); 1987 1988 audio_trans_desc = answer->GetTransportDescriptionByName("audio"); 1989 ASSERT_TRUE(audio_trans_desc != NULL); 1990 video_trans_desc = answer->GetTransportDescriptionByName("video"); 1991 ASSERT_TRUE(video_trans_desc != NULL); 1992 ASSERT_TRUE(audio_trans_desc->identity_fingerprint.get() == NULL); 1993 ASSERT_TRUE(video_trans_desc->identity_fingerprint.get() == NULL); 1994 1995 // Enable DTLS; the answer should now only have DTLS support. 1996 tdf2_.set_secure(SEC_ENABLED); 1997 answer.reset(f2_.CreateAnswer(offer.get(), options, NULL)); 1998 ASSERT_TRUE(answer.get() != NULL); 1999 2000 audio_media_desc = static_cast<const cricket::MediaContentDescription*>( 2001 answer->GetContentDescriptionByName("audio")); 2002 ASSERT_TRUE(audio_media_desc != NULL); 2003 video_media_desc = static_cast<const cricket::MediaContentDescription*>( 2004 answer->GetContentDescriptionByName("video")); 2005 ASSERT_TRUE(video_media_desc != NULL); 2006 EXPECT_TRUE(audio_media_desc->cryptos().empty()); 2007 EXPECT_TRUE(video_media_desc->cryptos().empty()); 2008 EXPECT_EQ(std::string(cricket::kMediaProtocolSavpf), 2009 audio_media_desc->protocol()); 2010 EXPECT_EQ(std::string(cricket::kMediaProtocolSavpf), 2011 video_media_desc->protocol()); 2012 2013 audio_trans_desc = answer->GetTransportDescriptionByName("audio"); 2014 ASSERT_TRUE(audio_trans_desc != NULL); 2015 video_trans_desc = answer->GetTransportDescriptionByName("video"); 2016 ASSERT_TRUE(video_trans_desc != NULL); 2017 ASSERT_TRUE(audio_trans_desc->identity_fingerprint.get() != NULL); 2018 ASSERT_TRUE(video_trans_desc->identity_fingerprint.get() != NULL); 2019 2020 // Try creating offer again. DTLS enabled now, crypto's should be empty 2021 // in new offer. 2022 offer.reset(f1_.CreateOffer(options, offer.get())); 2023 ASSERT_TRUE(offer.get() != NULL); 2024 audio_media_desc = static_cast<const cricket::MediaContentDescription*>( 2025 offer->GetContentDescriptionByName("audio")); 2026 ASSERT_TRUE(audio_media_desc != NULL); 2027 video_media_desc = static_cast<const cricket::MediaContentDescription*>( 2028 offer->GetContentDescriptionByName("video")); 2029 ASSERT_TRUE(video_media_desc != NULL); 2030 EXPECT_TRUE(audio_media_desc->cryptos().empty()); 2031 EXPECT_TRUE(video_media_desc->cryptos().empty()); 2032 2033 audio_trans_desc = offer->GetTransportDescriptionByName("audio"); 2034 ASSERT_TRUE(audio_trans_desc != NULL); 2035 video_trans_desc = offer->GetTransportDescriptionByName("video"); 2036 ASSERT_TRUE(video_trans_desc != NULL); 2037 ASSERT_TRUE(audio_trans_desc->identity_fingerprint.get() != NULL); 2038 ASSERT_TRUE(video_trans_desc->identity_fingerprint.get() != NULL); 2039 } 2040 2041 // Test that an answer can't be created if cryptos are required but the offer is 2042 // unsecure. 2043 TEST_F(MediaSessionDescriptionFactoryTest, TestSecureAnswerToUnsecureOffer) { 2044 MediaSessionOptions options; 2045 f1_.set_secure(SEC_DISABLED); 2046 tdf1_.set_secure(SEC_DISABLED); 2047 f2_.set_secure(SEC_REQUIRED); 2048 tdf1_.set_secure(SEC_ENABLED); 2049 2050 rtc::scoped_ptr<SessionDescription> offer(f1_.CreateOffer(options, 2051 NULL)); 2052 ASSERT_TRUE(offer.get() != NULL); 2053 rtc::scoped_ptr<SessionDescription> answer( 2054 f2_.CreateAnswer(offer.get(), options, NULL)); 2055 EXPECT_TRUE(answer.get() == NULL); 2056 } 2057 2058 // Test that we accept a DTLS offer without SDES and create an appropriate 2059 // answer. 2060 TEST_F(MediaSessionDescriptionFactoryTest, TestCryptoOfferDtlsButNotSdes) { 2061 f1_.set_secure(SEC_DISABLED); 2062 f2_.set_secure(SEC_ENABLED); 2063 tdf1_.set_secure(SEC_ENABLED); 2064 tdf2_.set_secure(SEC_ENABLED); 2065 MediaSessionOptions options; 2066 options.has_audio = true; 2067 options.has_video = true; 2068 options.data_channel_type = cricket::DCT_RTP; 2069 2070 rtc::scoped_ptr<SessionDescription> offer, answer; 2071 2072 // Generate an offer with DTLS but without SDES. 2073 offer.reset(f1_.CreateOffer(options, NULL)); 2074 ASSERT_TRUE(offer.get() != NULL); 2075 2076 const AudioContentDescription* audio_offer = 2077 GetFirstAudioContentDescription(offer.get()); 2078 ASSERT_TRUE(audio_offer->cryptos().empty()); 2079 const VideoContentDescription* video_offer = 2080 GetFirstVideoContentDescription(offer.get()); 2081 ASSERT_TRUE(video_offer->cryptos().empty()); 2082 const DataContentDescription* data_offer = 2083 GetFirstDataContentDescription(offer.get()); 2084 ASSERT_TRUE(data_offer->cryptos().empty()); 2085 2086 const cricket::TransportDescription* audio_offer_trans_desc = 2087 offer->GetTransportDescriptionByName("audio"); 2088 ASSERT_TRUE(audio_offer_trans_desc->identity_fingerprint.get() != NULL); 2089 const cricket::TransportDescription* video_offer_trans_desc = 2090 offer->GetTransportDescriptionByName("video"); 2091 ASSERT_TRUE(video_offer_trans_desc->identity_fingerprint.get() != NULL); 2092 const cricket::TransportDescription* data_offer_trans_desc = 2093 offer->GetTransportDescriptionByName("data"); 2094 ASSERT_TRUE(data_offer_trans_desc->identity_fingerprint.get() != NULL); 2095 2096 // Generate an answer with DTLS. 2097 answer.reset(f2_.CreateAnswer(offer.get(), options, NULL)); 2098 ASSERT_TRUE(answer.get() != NULL); 2099 2100 const cricket::TransportDescription* audio_answer_trans_desc = 2101 answer->GetTransportDescriptionByName("audio"); 2102 EXPECT_TRUE(audio_answer_trans_desc->identity_fingerprint.get() != NULL); 2103 const cricket::TransportDescription* video_answer_trans_desc = 2104 answer->GetTransportDescriptionByName("video"); 2105 EXPECT_TRUE(video_answer_trans_desc->identity_fingerprint.get() != NULL); 2106 const cricket::TransportDescription* data_answer_trans_desc = 2107 answer->GetTransportDescriptionByName("data"); 2108 EXPECT_TRUE(data_answer_trans_desc->identity_fingerprint.get() != NULL); 2109 } 2110 2111 // Verifies if vad_enabled option is set to false, CN codecs are not present in 2112 // offer or answer. 2113 TEST_F(MediaSessionDescriptionFactoryTest, TestVADEnableOption) { 2114 MediaSessionOptions options; 2115 options.has_audio = true; 2116 options.has_video = true; 2117 rtc::scoped_ptr<SessionDescription> offer( 2118 f1_.CreateOffer(options, NULL)); 2119 ASSERT_TRUE(offer.get() != NULL); 2120 const ContentInfo* audio_content = offer->GetContentByName("audio"); 2121 EXPECT_FALSE(VerifyNoCNCodecs(audio_content)); 2122 2123 options.vad_enabled = false; 2124 offer.reset(f1_.CreateOffer(options, NULL)); 2125 ASSERT_TRUE(offer.get() != NULL); 2126 audio_content = offer->GetContentByName("audio"); 2127 EXPECT_TRUE(VerifyNoCNCodecs(audio_content)); 2128 rtc::scoped_ptr<SessionDescription> answer( 2129 f1_.CreateAnswer(offer.get(), options, NULL)); 2130 ASSERT_TRUE(answer.get() != NULL); 2131 audio_content = answer->GetContentByName("audio"); 2132 EXPECT_TRUE(VerifyNoCNCodecs(audio_content)); 2133 } 2134