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/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