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