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