Home | History | Annotate | Download | only in base
      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