Home | History | Annotate | Download | only in test
      1 /*
      2  *  Copyright (c) 2012 The WebRTC project authors. All Rights Reserved.
      3  *
      4  *  Use of this source code is governed by a BSD-style license
      5  *  that can be found in the LICENSE file in the root of the source
      6  *  tree. An additional intellectual property rights grant can be found
      7  *  in the file PATENTS.  All contributing project authors may
      8  *  be found in the AUTHORS file in the root of the source tree.
      9  */
     10 
     11 #include "webrtc/test/fake_network_pipe.h"
     12 
     13 #include <assert.h>
     14 #include <math.h>
     15 #include <string.h>
     16 #include <algorithm>
     17 
     18 #include "webrtc/call.h"
     19 #include "webrtc/system_wrappers/interface/critical_section_wrapper.h"
     20 #include "webrtc/system_wrappers/interface/tick_util.h"
     21 
     22 namespace webrtc {
     23 
     24 const double kPi = 3.14159265;
     25 const int kDefaultProcessIntervalMs = 30;
     26 
     27 static int GaussianRandom(int mean_delay_ms, int standard_deviation_ms) {
     28   // Creating a Normal distribution variable from two independent uniform
     29   // variables based on the Box-Muller transform.
     30   double uniform1 = (rand() + 1.0) / (RAND_MAX + 1.0);  // NOLINT
     31   double uniform2 = (rand() + 1.0) / (RAND_MAX + 1.0);  // NOLINT
     32   return static_cast<int>(mean_delay_ms + standard_deviation_ms *
     33                           sqrt(-2 * log(uniform1)) * cos(2 * kPi * uniform2));
     34 }
     35 
     36 static bool UniformLoss(int loss_percent) {
     37   int outcome = rand() % 100;
     38   return outcome < loss_percent;
     39 }
     40 
     41 class NetworkPacket {
     42  public:
     43   NetworkPacket(const uint8_t* data, size_t length, int64_t send_time,
     44       int64_t arrival_time)
     45       : data_(NULL),
     46         data_length_(length),
     47         send_time_(send_time),
     48         arrival_time_(arrival_time) {
     49     data_ = new uint8_t[length];
     50     memcpy(data_, data, length);
     51   }
     52   ~NetworkPacket() {
     53     delete [] data_;
     54   }
     55 
     56   uint8_t* data() const { return data_; }
     57   size_t data_length() const { return data_length_; }
     58   int64_t send_time() const { return send_time_; }
     59   int64_t arrival_time() const { return arrival_time_; }
     60   void IncrementArrivalTime(int64_t extra_delay) {
     61     arrival_time_+= extra_delay;
     62   }
     63 
     64  private:
     65   // The packet data.
     66   uint8_t* data_;
     67   // Length of data_.
     68   size_t data_length_;
     69   // The time the packet was sent out on the network.
     70   const int64_t send_time_;
     71   // The time the packet should arrive at the reciver.
     72   int64_t arrival_time_;
     73 };
     74 
     75 FakeNetworkPipe::FakeNetworkPipe(
     76     const FakeNetworkPipe::Config& config)
     77     : lock_(CriticalSectionWrapper::CreateCriticalSection()),
     78       packet_receiver_(NULL),
     79       config_(config),
     80       dropped_packets_(0),
     81       sent_packets_(0),
     82       total_packet_delay_(0),
     83       next_process_time_(TickTime::MillisecondTimestamp()) {
     84 }
     85 
     86 FakeNetworkPipe::~FakeNetworkPipe() {
     87   while (!capacity_link_.empty()) {
     88     delete capacity_link_.front();
     89     capacity_link_.pop();
     90   }
     91   while (!delay_link_.empty()) {
     92     delete delay_link_.front();
     93     delay_link_.pop();
     94   }
     95 }
     96 
     97 void FakeNetworkPipe::SetReceiver(PacketReceiver* receiver) {
     98   packet_receiver_ = receiver;
     99 }
    100 
    101 void FakeNetworkPipe::SetConfig(const FakeNetworkPipe::Config& config) {
    102   CriticalSectionScoped crit(lock_.get());
    103   config_ = config;  // Shallow copy of the struct.
    104 }
    105 
    106 void FakeNetworkPipe::SendPacket(const uint8_t* data, size_t data_length) {
    107   // A NULL packet_receiver_ means that this pipe will terminate the flow of
    108   // packets.
    109   if (packet_receiver_ == NULL)
    110     return;
    111   CriticalSectionScoped crit(lock_.get());
    112   if (config_.queue_length_packets > 0 &&
    113       capacity_link_.size() >= config_.queue_length_packets) {
    114     // Too many packet on the link, drop this one.
    115     ++dropped_packets_;
    116     return;
    117   }
    118 
    119   int64_t time_now = TickTime::MillisecondTimestamp();
    120 
    121   // Delay introduced by the link capacity.
    122   int64_t capacity_delay_ms = 0;
    123   if (config_.link_capacity_kbps > 0)
    124     capacity_delay_ms = data_length / (config_.link_capacity_kbps / 8);
    125   int64_t network_start_time = time_now;
    126 
    127   // Check if there already are packets on the link and change network start
    128   // time if there is.
    129   if (capacity_link_.size() > 0)
    130     network_start_time = capacity_link_.back()->arrival_time();
    131 
    132   int64_t arrival_time = network_start_time + capacity_delay_ms;
    133   NetworkPacket* packet = new NetworkPacket(data, data_length, time_now,
    134                                             arrival_time);
    135   capacity_link_.push(packet);
    136 }
    137 
    138 float FakeNetworkPipe::PercentageLoss() {
    139   CriticalSectionScoped crit(lock_.get());
    140   if (sent_packets_ == 0)
    141     return 0;
    142 
    143   return static_cast<float>(dropped_packets_) /
    144       (sent_packets_ + dropped_packets_);
    145 }
    146 
    147 int FakeNetworkPipe::AverageDelay() {
    148   CriticalSectionScoped crit(lock_.get());
    149   if (sent_packets_ == 0)
    150     return 0;
    151 
    152   return total_packet_delay_ / static_cast<int>(sent_packets_);
    153 }
    154 
    155 void FakeNetworkPipe::Process() {
    156   int64_t time_now = TickTime::MillisecondTimestamp();
    157   std::queue<NetworkPacket*> packets_to_deliver;
    158   {
    159     CriticalSectionScoped crit(lock_.get());
    160     // Check the capacity link first.
    161     while (capacity_link_.size() > 0 &&
    162            time_now >= capacity_link_.front()->arrival_time()) {
    163       // Time to get this packet.
    164       NetworkPacket* packet = capacity_link_.front();
    165       capacity_link_.pop();
    166 
    167       // Packets are randomly dropped after being affected by the bottleneck.
    168       if (UniformLoss(config_.loss_percent)) {
    169         delete packet;
    170         continue;
    171       }
    172 
    173       // Add extra delay and jitter, but make sure the arrival time is not
    174       // earlier than the last packet in the queue.
    175       int extra_delay = GaussianRandom(config_.queue_delay_ms,
    176                                        config_.delay_standard_deviation_ms);
    177       if (delay_link_.size() > 0 &&
    178           packet->arrival_time() + extra_delay <
    179           delay_link_.back()->arrival_time()) {
    180         extra_delay = delay_link_.back()->arrival_time() -
    181             packet->arrival_time();
    182       }
    183       packet->IncrementArrivalTime(extra_delay);
    184       if (packet->arrival_time() < next_process_time_)
    185         next_process_time_ = packet->arrival_time();
    186       delay_link_.push(packet);
    187     }
    188 
    189     // Check the extra delay queue.
    190     while (delay_link_.size() > 0 &&
    191            time_now >= delay_link_.front()->arrival_time()) {
    192       // Deliver this packet.
    193       NetworkPacket* packet = delay_link_.front();
    194       packets_to_deliver.push(packet);
    195       delay_link_.pop();
    196       // |time_now| might be later than when the packet should have arrived, due
    197       // to NetworkProcess being called too late. For stats, use the time it
    198       // should have been on the link.
    199       total_packet_delay_ += packet->arrival_time() - packet->send_time();
    200     }
    201     sent_packets_ += packets_to_deliver.size();
    202   }
    203   while (!packets_to_deliver.empty()) {
    204     NetworkPacket* packet = packets_to_deliver.front();
    205     packets_to_deliver.pop();
    206     packet_receiver_->DeliverPacket(packet->data(), packet->data_length());
    207     delete packet;
    208   }
    209 }
    210 
    211 int FakeNetworkPipe::TimeUntilNextProcess() const {
    212   CriticalSectionScoped crit(lock_.get());
    213   if (capacity_link_.size() == 0 || delay_link_.size() == 0)
    214     return kDefaultProcessIntervalMs;
    215   return std::max(static_cast<int>(next_process_time_ -
    216       TickTime::MillisecondTimestamp()), 0);
    217 }
    218 
    219 }  // namespace webrtc
    220