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