1 /* 2 * libjingle 3 * Copyright 2012 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 30 #include "talk/base/buffer.h" 31 #include "talk/base/gunit.h" 32 #include "talk/base/helpers.h" 33 #include "talk/base/scoped_ptr.h" 34 #include "talk/base/ssladapter.h" 35 #include "talk/base/timing.h" 36 #include "talk/media/base/constants.h" 37 #include "talk/media/base/fakenetworkinterface.h" 38 #include "talk/media/base/rtpdataengine.h" 39 #include "talk/media/base/rtputils.h" 40 41 class FakeTiming : public talk_base::Timing { 42 public: 43 FakeTiming() : now_(0.0) {} 44 45 virtual double TimerNow() { 46 return now_; 47 } 48 49 void set_now(double now) { 50 now_ = now; 51 } 52 53 private: 54 double now_; 55 }; 56 57 class FakeDataReceiver : public sigslot::has_slots<> { 58 public: 59 FakeDataReceiver() : has_received_data_(false) {} 60 61 void OnDataReceived( 62 const cricket::ReceiveDataParams& params, 63 const char* data, size_t len) { 64 has_received_data_ = true; 65 last_received_data_ = std::string(data, len); 66 last_received_data_len_ = len; 67 last_received_data_params_ = params; 68 } 69 70 bool has_received_data() const { return has_received_data_; } 71 std::string last_received_data() const { return last_received_data_; } 72 size_t last_received_data_len() const { return last_received_data_len_; } 73 cricket::ReceiveDataParams last_received_data_params() const { 74 return last_received_data_params_; 75 } 76 77 private: 78 bool has_received_data_; 79 std::string last_received_data_; 80 size_t last_received_data_len_; 81 cricket::ReceiveDataParams last_received_data_params_; 82 }; 83 84 class RtpDataMediaChannelTest : public testing::Test { 85 protected: 86 static void SetUpTestCase() { 87 talk_base::InitializeSSL(); 88 } 89 90 static void TearDownTestCase() { 91 talk_base::CleanupSSL(); 92 } 93 94 virtual void SetUp() { 95 // Seed needed for each test to satisfy expectations. 96 iface_.reset(new cricket::FakeNetworkInterface()); 97 timing_ = new FakeTiming(); 98 dme_.reset(CreateEngine(timing_)); 99 receiver_.reset(new FakeDataReceiver()); 100 } 101 102 void SetNow(double now) { 103 timing_->set_now(now); 104 } 105 106 cricket::RtpDataEngine* CreateEngine(FakeTiming* timing) { 107 cricket::RtpDataEngine* dme = new cricket::RtpDataEngine(); 108 dme->SetTiming(timing); 109 return dme; 110 } 111 112 cricket::RtpDataMediaChannel* CreateChannel() { 113 return CreateChannel(dme_.get()); 114 } 115 116 cricket::RtpDataMediaChannel* CreateChannel(cricket::RtpDataEngine* dme) { 117 cricket::RtpDataMediaChannel* channel = 118 static_cast<cricket::RtpDataMediaChannel*>(dme->CreateChannel( 119 cricket::DCT_RTP)); 120 channel->SetInterface(iface_.get()); 121 channel->SignalDataReceived.connect( 122 receiver_.get(), &FakeDataReceiver::OnDataReceived); 123 return channel; 124 } 125 126 FakeDataReceiver* receiver() { 127 return receiver_.get(); 128 } 129 130 bool HasReceivedData() { 131 return receiver_->has_received_data(); 132 } 133 134 std::string GetReceivedData() { 135 return receiver_->last_received_data(); 136 } 137 138 size_t GetReceivedDataLen() { 139 return receiver_->last_received_data_len(); 140 } 141 142 cricket::ReceiveDataParams GetReceivedDataParams() { 143 return receiver_->last_received_data_params(); 144 } 145 146 bool HasSentData(int count) { 147 return (iface_->NumRtpPackets() > count); 148 } 149 150 std::string GetSentData(int index) { 151 // Assume RTP header of length 12 152 talk_base::scoped_ptr<const talk_base::Buffer> packet( 153 iface_->GetRtpPacket(index)); 154 if (packet->length() > 12) { 155 return std::string(packet->data() + 12, packet->length() - 12); 156 } else { 157 return ""; 158 } 159 } 160 161 cricket::RtpHeader GetSentDataHeader(int index) { 162 talk_base::scoped_ptr<const talk_base::Buffer> packet( 163 iface_->GetRtpPacket(index)); 164 cricket::RtpHeader header; 165 GetRtpHeader(packet->data(), packet->length(), &header); 166 return header; 167 } 168 169 private: 170 talk_base::scoped_ptr<cricket::RtpDataEngine> dme_; 171 // Timing passed into dme_. Owned by dme_; 172 FakeTiming* timing_; 173 talk_base::scoped_ptr<cricket::FakeNetworkInterface> iface_; 174 talk_base::scoped_ptr<FakeDataReceiver> receiver_; 175 }; 176 177 TEST_F(RtpDataMediaChannelTest, SetUnknownCodecs) { 178 talk_base::scoped_ptr<cricket::RtpDataMediaChannel> dmc(CreateChannel()); 179 180 cricket::DataCodec known_codec; 181 known_codec.id = 103; 182 known_codec.name = "google-data"; 183 cricket::DataCodec unknown_codec; 184 unknown_codec.id = 104; 185 unknown_codec.name = "unknown-data"; 186 187 std::vector<cricket::DataCodec> known_codecs; 188 known_codecs.push_back(known_codec); 189 190 std::vector<cricket::DataCodec> unknown_codecs; 191 unknown_codecs.push_back(unknown_codec); 192 193 std::vector<cricket::DataCodec> mixed_codecs; 194 mixed_codecs.push_back(known_codec); 195 mixed_codecs.push_back(unknown_codec); 196 197 EXPECT_TRUE(dmc->SetSendCodecs(known_codecs)); 198 EXPECT_FALSE(dmc->SetSendCodecs(unknown_codecs)); 199 EXPECT_TRUE(dmc->SetSendCodecs(mixed_codecs)); 200 EXPECT_TRUE(dmc->SetRecvCodecs(known_codecs)); 201 EXPECT_FALSE(dmc->SetRecvCodecs(unknown_codecs)); 202 EXPECT_FALSE(dmc->SetRecvCodecs(mixed_codecs)); 203 } 204 205 TEST_F(RtpDataMediaChannelTest, AddRemoveSendStream) { 206 talk_base::scoped_ptr<cricket::RtpDataMediaChannel> dmc(CreateChannel()); 207 208 cricket::StreamParams stream1; 209 stream1.add_ssrc(41); 210 EXPECT_TRUE(dmc->AddSendStream(stream1)); 211 cricket::StreamParams stream2; 212 stream2.add_ssrc(42); 213 EXPECT_TRUE(dmc->AddSendStream(stream2)); 214 215 EXPECT_TRUE(dmc->RemoveSendStream(41)); 216 EXPECT_TRUE(dmc->RemoveSendStream(42)); 217 EXPECT_FALSE(dmc->RemoveSendStream(43)); 218 } 219 220 TEST_F(RtpDataMediaChannelTest, AddRemoveRecvStream) { 221 talk_base::scoped_ptr<cricket::RtpDataMediaChannel> dmc(CreateChannel()); 222 223 cricket::StreamParams stream1; 224 stream1.add_ssrc(41); 225 EXPECT_TRUE(dmc->AddRecvStream(stream1)); 226 cricket::StreamParams stream2; 227 stream2.add_ssrc(42); 228 EXPECT_TRUE(dmc->AddRecvStream(stream2)); 229 EXPECT_FALSE(dmc->AddRecvStream(stream2)); 230 231 EXPECT_TRUE(dmc->RemoveRecvStream(41)); 232 EXPECT_TRUE(dmc->RemoveRecvStream(42)); 233 } 234 235 TEST_F(RtpDataMediaChannelTest, SendData) { 236 talk_base::scoped_ptr<cricket::RtpDataMediaChannel> dmc(CreateChannel()); 237 238 cricket::SendDataParams params; 239 params.ssrc = 42; 240 unsigned char data[] = "food"; 241 talk_base::Buffer payload(data, 4); 242 unsigned char padded_data[] = { 243 0x00, 0x00, 0x00, 0x00, 244 'f', 'o', 'o', 'd', 245 }; 246 cricket::SendDataResult result; 247 248 // Not sending 249 EXPECT_FALSE(dmc->SendData(params, payload, &result)); 250 EXPECT_EQ(cricket::SDR_ERROR, result); 251 EXPECT_FALSE(HasSentData(0)); 252 ASSERT_TRUE(dmc->SetSend(true)); 253 254 // Unknown stream name. 255 EXPECT_FALSE(dmc->SendData(params, payload, &result)); 256 EXPECT_EQ(cricket::SDR_ERROR, result); 257 EXPECT_FALSE(HasSentData(0)); 258 259 cricket::StreamParams stream; 260 stream.add_ssrc(42); 261 ASSERT_TRUE(dmc->AddSendStream(stream)); 262 263 // Unknown codec; 264 EXPECT_FALSE(dmc->SendData(params, payload, &result)); 265 EXPECT_EQ(cricket::SDR_ERROR, result); 266 EXPECT_FALSE(HasSentData(0)); 267 268 cricket::DataCodec codec; 269 codec.id = 103; 270 codec.name = cricket::kGoogleRtpDataCodecName; 271 std::vector<cricket::DataCodec> codecs; 272 codecs.push_back(codec); 273 ASSERT_TRUE(dmc->SetSendCodecs(codecs)); 274 275 // Length too large; 276 std::string x10000(10000, 'x'); 277 EXPECT_FALSE(dmc->SendData( 278 params, talk_base::Buffer(x10000.data(), x10000.length()), &result)); 279 EXPECT_EQ(cricket::SDR_ERROR, result); 280 EXPECT_FALSE(HasSentData(0)); 281 282 // Finally works! 283 EXPECT_TRUE(dmc->SendData(params, payload, &result)); 284 EXPECT_EQ(cricket::SDR_SUCCESS, result); 285 ASSERT_TRUE(HasSentData(0)); 286 EXPECT_EQ(sizeof(padded_data), GetSentData(0).length()); 287 EXPECT_EQ(0, memcmp( 288 padded_data, GetSentData(0).data(), sizeof(padded_data))); 289 cricket::RtpHeader header0 = GetSentDataHeader(0); 290 EXPECT_NE(0, header0.seq_num); 291 EXPECT_NE(0U, header0.timestamp); 292 EXPECT_EQ(header0.ssrc, 42U); 293 EXPECT_EQ(header0.payload_type, 103); 294 295 // Should bump timestamp by 180000 because the clock rate is 90khz. 296 SetNow(2); 297 298 EXPECT_TRUE(dmc->SendData(params, payload, &result)); 299 ASSERT_TRUE(HasSentData(1)); 300 EXPECT_EQ(sizeof(padded_data), GetSentData(1).length()); 301 EXPECT_EQ(0, memcmp( 302 padded_data, GetSentData(1).data(), sizeof(padded_data))); 303 cricket::RtpHeader header1 = GetSentDataHeader(1); 304 EXPECT_EQ(header1.ssrc, 42U); 305 EXPECT_EQ(header1.payload_type, 103); 306 EXPECT_EQ(static_cast<uint16>(header0.seq_num + 1), 307 static_cast<uint16>(header1.seq_num)); 308 EXPECT_EQ(header0.timestamp + 180000, header1.timestamp); 309 } 310 311 TEST_F(RtpDataMediaChannelTest, SendDataMultipleClocks) { 312 // Timings owned by RtpDataEngines. 313 FakeTiming* timing1 = new FakeTiming(); 314 talk_base::scoped_ptr<cricket::RtpDataEngine> dme1(CreateEngine(timing1)); 315 talk_base::scoped_ptr<cricket::RtpDataMediaChannel> dmc1( 316 CreateChannel(dme1.get())); 317 FakeTiming* timing2 = new FakeTiming(); 318 talk_base::scoped_ptr<cricket::RtpDataEngine> dme2(CreateEngine(timing2)); 319 talk_base::scoped_ptr<cricket::RtpDataMediaChannel> dmc2( 320 CreateChannel(dme2.get())); 321 322 ASSERT_TRUE(dmc1->SetSend(true)); 323 ASSERT_TRUE(dmc2->SetSend(true)); 324 325 cricket::StreamParams stream1; 326 stream1.add_ssrc(41); 327 ASSERT_TRUE(dmc1->AddSendStream(stream1)); 328 cricket::StreamParams stream2; 329 stream2.add_ssrc(42); 330 ASSERT_TRUE(dmc2->AddSendStream(stream2)); 331 332 cricket::DataCodec codec; 333 codec.id = 103; 334 codec.name = cricket::kGoogleRtpDataCodecName; 335 std::vector<cricket::DataCodec> codecs; 336 codecs.push_back(codec); 337 ASSERT_TRUE(dmc1->SetSendCodecs(codecs)); 338 ASSERT_TRUE(dmc2->SetSendCodecs(codecs)); 339 340 cricket::SendDataParams params1; 341 params1.ssrc = 41; 342 cricket::SendDataParams params2; 343 params2.ssrc = 42; 344 345 unsigned char data[] = "foo"; 346 talk_base::Buffer payload(data, 3); 347 cricket::SendDataResult result; 348 349 EXPECT_TRUE(dmc1->SendData(params1, payload, &result)); 350 EXPECT_TRUE(dmc2->SendData(params2, payload, &result)); 351 352 // Should bump timestamp by 90000 because the clock rate is 90khz. 353 timing1->set_now(1); 354 // Should bump timestamp by 180000 because the clock rate is 90khz. 355 timing2->set_now(2); 356 357 EXPECT_TRUE(dmc1->SendData(params1, payload, &result)); 358 EXPECT_TRUE(dmc2->SendData(params2, payload, &result)); 359 360 ASSERT_TRUE(HasSentData(3)); 361 cricket::RtpHeader header1a = GetSentDataHeader(0); 362 cricket::RtpHeader header2a = GetSentDataHeader(1); 363 cricket::RtpHeader header1b = GetSentDataHeader(2); 364 cricket::RtpHeader header2b = GetSentDataHeader(3); 365 366 EXPECT_EQ(static_cast<uint16>(header1a.seq_num + 1), 367 static_cast<uint16>(header1b.seq_num)); 368 EXPECT_EQ(header1a.timestamp + 90000, header1b.timestamp); 369 EXPECT_EQ(static_cast<uint16>(header2a.seq_num + 1), 370 static_cast<uint16>(header2b.seq_num)); 371 EXPECT_EQ(header2a.timestamp + 180000, header2b.timestamp); 372 } 373 374 TEST_F(RtpDataMediaChannelTest, SendDataRate) { 375 talk_base::scoped_ptr<cricket::RtpDataMediaChannel> dmc(CreateChannel()); 376 377 ASSERT_TRUE(dmc->SetSend(true)); 378 379 cricket::DataCodec codec; 380 codec.id = 103; 381 codec.name = cricket::kGoogleRtpDataCodecName; 382 std::vector<cricket::DataCodec> codecs; 383 codecs.push_back(codec); 384 ASSERT_TRUE(dmc->SetSendCodecs(codecs)); 385 386 cricket::StreamParams stream; 387 stream.add_ssrc(42); 388 ASSERT_TRUE(dmc->AddSendStream(stream)); 389 390 cricket::SendDataParams params; 391 params.ssrc = 42; 392 unsigned char data[] = "food"; 393 talk_base::Buffer payload(data, 4); 394 cricket::SendDataResult result; 395 396 // With rtp overhead of 32 bytes, each one of our packets is 36 397 // bytes, or 288 bits. So, a limit of 872bps will allow 3 packets, 398 // but not four. 399 dmc->SetMaxSendBandwidth(872); 400 401 EXPECT_TRUE(dmc->SendData(params, payload, &result)); 402 EXPECT_TRUE(dmc->SendData(params, payload, &result)); 403 EXPECT_TRUE(dmc->SendData(params, payload, &result)); 404 EXPECT_FALSE(dmc->SendData(params, payload, &result)); 405 EXPECT_FALSE(dmc->SendData(params, payload, &result)); 406 407 SetNow(0.9); 408 EXPECT_FALSE(dmc->SendData(params, payload, &result)); 409 410 SetNow(1.1); 411 EXPECT_TRUE(dmc->SendData(params, payload, &result)); 412 EXPECT_TRUE(dmc->SendData(params, payload, &result)); 413 SetNow(1.9); 414 EXPECT_TRUE(dmc->SendData(params, payload, &result)); 415 416 SetNow(2.2); 417 EXPECT_TRUE(dmc->SendData(params, payload, &result)); 418 EXPECT_TRUE(dmc->SendData(params, payload, &result)); 419 EXPECT_TRUE(dmc->SendData(params, payload, &result)); 420 EXPECT_FALSE(dmc->SendData(params, payload, &result)); 421 } 422 423 TEST_F(RtpDataMediaChannelTest, ReceiveData) { 424 // PT= 103, SN=2, TS=3, SSRC = 4, data = "abcde" 425 unsigned char data[] = { 426 0x80, 0x67, 0x00, 0x02, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x2A, 427 0x00, 0x00, 0x00, 0x00, 428 'a', 'b', 'c', 'd', 'e' 429 }; 430 talk_base::Buffer packet(data, sizeof(data)); 431 432 talk_base::scoped_ptr<cricket::RtpDataMediaChannel> dmc(CreateChannel()); 433 434 // SetReceived not called. 435 dmc->OnPacketReceived(&packet, talk_base::PacketTime()); 436 EXPECT_FALSE(HasReceivedData()); 437 438 dmc->SetReceive(true); 439 440 // Unknown payload id 441 dmc->OnPacketReceived(&packet, talk_base::PacketTime()); 442 EXPECT_FALSE(HasReceivedData()); 443 444 cricket::DataCodec codec; 445 codec.id = 103; 446 codec.name = cricket::kGoogleRtpDataCodecName; 447 std::vector<cricket::DataCodec> codecs; 448 codecs.push_back(codec); 449 ASSERT_TRUE(dmc->SetRecvCodecs(codecs)); 450 451 // Unknown stream 452 dmc->OnPacketReceived(&packet, talk_base::PacketTime()); 453 EXPECT_FALSE(HasReceivedData()); 454 455 cricket::StreamParams stream; 456 stream.add_ssrc(42); 457 ASSERT_TRUE(dmc->AddRecvStream(stream)); 458 459 // Finally works! 460 dmc->OnPacketReceived(&packet, talk_base::PacketTime()); 461 EXPECT_TRUE(HasReceivedData()); 462 EXPECT_EQ("abcde", GetReceivedData()); 463 EXPECT_EQ(5U, GetReceivedDataLen()); 464 } 465 466 TEST_F(RtpDataMediaChannelTest, InvalidRtpPackets) { 467 unsigned char data[] = { 468 0x80, 0x65, 0x00, 0x02 469 }; 470 talk_base::Buffer packet(data, sizeof(data)); 471 472 talk_base::scoped_ptr<cricket::RtpDataMediaChannel> dmc(CreateChannel()); 473 474 // Too short 475 dmc->OnPacketReceived(&packet, talk_base::PacketTime()); 476 EXPECT_FALSE(HasReceivedData()); 477 } 478