Allow packets to be reordered in the fake network pipe.
BUG=
Review URL: https://codereview.webrtc.org/1606183002
Cr-Original-Commit-Position: refs/heads/master@{#11384}
Cr-Mirrored-From: https://chromium.googlesource.com/external/webrtc
Cr-Mirrored-Commit: a2c55235cae5f16715911bff6109fe915a8085bf
diff --git a/test/fake_network_pipe.cc b/test/fake_network_pipe.cc
index 491a052..ea4e551 100644
--- a/test/fake_network_pipe.cc
+++ b/test/fake_network_pipe.cc
@@ -20,60 +20,16 @@
namespace webrtc {
-const double kPi = 3.14159265;
-
-static int GaussianRandom(int mean_delay_ms, int standard_deviation_ms) {
- // Creating a Normal distribution variable from two independent uniform
- // variables based on the Box-Muller transform.
- double uniform1 = (rand() + 1.0) / (RAND_MAX + 1.0); // NOLINT
- double uniform2 = (rand() + 1.0) / (RAND_MAX + 1.0); // NOLINT
- return static_cast<int>(mean_delay_ms + standard_deviation_ms *
- sqrt(-2 * log(uniform1)) * cos(2 * kPi * uniform2));
-}
-
-static bool UniformLoss(int loss_percent) {
- int outcome = rand() % 100;
- return outcome < loss_percent;
-}
-
-class NetworkPacket {
- public:
- NetworkPacket(const uint8_t* data, size_t length, int64_t send_time,
- int64_t arrival_time)
- : data_(NULL),
- data_length_(length),
- send_time_(send_time),
- arrival_time_(arrival_time) {
- data_ = new uint8_t[length];
- memcpy(data_, data, length);
- }
- ~NetworkPacket() {
- delete [] data_;
- }
-
- uint8_t* data() const { return data_; }
- size_t data_length() const { return data_length_; }
- int64_t send_time() const { return send_time_; }
- int64_t arrival_time() const { return arrival_time_; }
- void IncrementArrivalTime(int64_t extra_delay) {
- arrival_time_+= extra_delay;
- }
-
- private:
- // The packet data.
- uint8_t* data_;
- // Length of data_.
- size_t data_length_;
- // The time the packet was sent out on the network.
- const int64_t send_time_;
- // The time the packet should arrive at the reciver.
- int64_t arrival_time_;
-};
-
FakeNetworkPipe::FakeNetworkPipe(Clock* clock,
const FakeNetworkPipe::Config& config)
+ : FakeNetworkPipe(clock, config, 1) {}
+
+FakeNetworkPipe::FakeNetworkPipe(Clock* clock,
+ const FakeNetworkPipe::Config& config,
+ uint64_t seed)
: clock_(clock),
packet_receiver_(NULL),
+ random_(seed),
config_(config),
dropped_packets_(0),
sent_packets_(0),
@@ -86,8 +42,8 @@
capacity_link_.pop();
}
while (!delay_link_.empty()) {
- delete delay_link_.front();
- delay_link_.pop();
+ delete *delay_link_.begin();
+ delay_link_.erase(delay_link_.begin());
}
}
@@ -123,7 +79,7 @@
// Check if there already are packets on the link and change network start
// time if there is.
- if (capacity_link_.size() > 0)
+ if (!capacity_link_.empty())
network_start_time = capacity_link_.back()->arrival_time();
int64_t arrival_time = network_start_time + capacity_delay_ms;
@@ -156,41 +112,42 @@
{
rtc::CritScope crit(&lock_);
// Check the capacity link first.
- while (capacity_link_.size() > 0 &&
+ while (!capacity_link_.empty() &&
time_now >= capacity_link_.front()->arrival_time()) {
// Time to get this packet.
NetworkPacket* packet = capacity_link_.front();
capacity_link_.pop();
// Packets are randomly dropped after being affected by the bottleneck.
- if (UniformLoss(config_.loss_percent)) {
+ if (random_.Rand(100) < static_cast<uint32_t>(config_.loss_percent)) {
delete packet;
continue;
}
- // Add extra delay and jitter, but make sure the arrival time is not
- // earlier than the last packet in the queue.
- int extra_delay = GaussianRandom(config_.queue_delay_ms,
- config_.delay_standard_deviation_ms);
- if (delay_link_.size() > 0 &&
- packet->arrival_time() + extra_delay <
- delay_link_.back()->arrival_time()) {
- extra_delay = delay_link_.back()->arrival_time() -
- packet->arrival_time();
+ int arrival_time_jitter = random_.Gaussian(
+ config_.queue_delay_ms, config_.delay_standard_deviation_ms);
+
+ // If reordering is not allowed then adjust arrival_time_jitter
+ // to make sure all packets are sent in order.
+ if (!config_.allow_reordering && !delay_link_.empty() &&
+ packet->arrival_time() + arrival_time_jitter <
+ (*delay_link_.rbegin())->arrival_time()) {
+ arrival_time_jitter =
+ (*delay_link_.rbegin())->arrival_time() - packet->arrival_time();
}
- packet->IncrementArrivalTime(extra_delay);
+ packet->IncrementArrivalTime(arrival_time_jitter);
if (packet->arrival_time() < next_process_time_)
next_process_time_ = packet->arrival_time();
- delay_link_.push(packet);
+ delay_link_.insert(packet);
}
// Check the extra delay queue.
- while (delay_link_.size() > 0 &&
- time_now >= delay_link_.front()->arrival_time()) {
+ while (!delay_link_.empty() &&
+ time_now >= (*delay_link_.begin())->arrival_time()) {
// Deliver this packet.
- NetworkPacket* packet = delay_link_.front();
+ NetworkPacket* packet = *delay_link_.begin();
packets_to_deliver.push(packet);
- delay_link_.pop();
+ delay_link_.erase(delay_link_.begin());
// |time_now| might be later than when the packet should have arrived, due
// to NetworkProcess being called too late. For stats, use the time it
// should have been on the link.
@@ -210,7 +167,7 @@
int64_t FakeNetworkPipe::TimeUntilNextProcess() const {
rtc::CritScope crit(&lock_);
const int64_t kDefaultProcessIntervalMs = 30;
- if (capacity_link_.size() == 0 || delay_link_.size() == 0)
+ if (capacity_link_.empty() || delay_link_.empty())
return kDefaultProcessIntervalMs;
return std::max<int64_t>(next_process_time_ - clock_->TimeInMilliseconds(),
0);
diff --git a/test/fake_network_pipe.h b/test/fake_network_pipe.h
index 99c5c13..d488d49 100644
--- a/test/fake_network_pipe.h
+++ b/test/fake_network_pipe.h
@@ -11,10 +11,13 @@
#ifndef WEBRTC_TEST_FAKE_NETWORK_PIPE_H_
#define WEBRTC_TEST_FAKE_NETWORK_PIPE_H_
+#include <set>
+#include <string.h>
#include <queue>
#include "webrtc/base/constructormagic.h"
#include "webrtc/base/criticalsection.h"
+#include "webrtc/base/random.h"
#include "webrtc/base/scoped_ptr.h"
#include "webrtc/typedefs.h"
@@ -22,9 +25,40 @@
class Clock;
class CriticalSectionWrapper;
-class NetworkPacket;
class PacketReceiver;
+class NetworkPacket {
+ public:
+ NetworkPacket(const uint8_t* data,
+ size_t length,
+ int64_t send_time,
+ int64_t arrival_time)
+ : data_(new uint8_t[length]),
+ data_length_(length),
+ send_time_(send_time),
+ arrival_time_(arrival_time) {
+ memcpy(data_.get(), data, length);
+ }
+
+ uint8_t* data() const { return data_.get(); }
+ size_t data_length() const { return data_length_; }
+ int64_t send_time() const { return send_time_; }
+ int64_t arrival_time() const { return arrival_time_; }
+ void IncrementArrivalTime(int64_t extra_delay) {
+ arrival_time_ += extra_delay;
+ }
+
+ private:
+ // The packet data.
+ rtc::scoped_ptr<uint8_t[]> data_;
+ // Length of data_.
+ size_t data_length_;
+ // The time the packet was sent out on the network.
+ const int64_t send_time_;
+ // The time the packet should arrive at the receiver.
+ int64_t arrival_time_;
+};
+
// Class faking a network link. This is a simple and naive solution just faking
// capacity and adding an extra transport delay in addition to the capacity
// introduced delay.
@@ -44,9 +78,14 @@
int link_capacity_kbps = 0;
// Random packet loss.
int loss_percent = 0;
+ // If packets are allowed to be reordered.
+ bool allow_reordering = false;
};
FakeNetworkPipe(Clock* clock, const FakeNetworkPipe::Config& config);
+ FakeNetworkPipe(Clock* clock,
+ const FakeNetworkPipe::Config& config,
+ uint64_t seed);
~FakeNetworkPipe();
// Must not be called in parallel with SendPacket or Process.
@@ -74,7 +113,17 @@
rtc::CriticalSection lock_;
PacketReceiver* packet_receiver_;
std::queue<NetworkPacket*> capacity_link_;
- std::queue<NetworkPacket*> delay_link_;
+ Random random_;
+
+ // Since we need to access both the packet with the earliest and latest
+ // arrival time we need to use a multiset to keep all packets sorted,
+ // hence, we cannot use a priority queue.
+ struct PacketArrivalTimeComparator {
+ bool operator()(const NetworkPacket* p1, const NetworkPacket* p2) {
+ return p1->arrival_time() < p2->arrival_time();
+ }
+ };
+ std::multiset<NetworkPacket*, PacketArrivalTimeComparator> delay_link_;
// Link configuration.
Config config_;
diff --git a/test/fake_network_pipe_unittest.cc b/test/fake_network_pipe_unittest.cc
index ff18993..233c597 100644
--- a/test/fake_network_pipe_unittest.cc
+++ b/test/fake_network_pipe_unittest.cc
@@ -23,28 +23,45 @@
namespace webrtc {
-class MockReceiver : public PacketReceiver {
+class TestReceiver : public PacketReceiver {
public:
- MockReceiver() {}
- virtual ~MockReceiver() {}
+ TestReceiver() {}
+ virtual ~TestReceiver() {}
void IncomingPacket(const uint8_t* data, size_t length) {
DeliverPacket(MediaType::ANY, data, length, PacketTime());
delete [] data;
}
- MOCK_METHOD4(
+ virtual MOCK_METHOD4(
DeliverPacket,
DeliveryStatus(MediaType, const uint8_t*, size_t, const PacketTime&));
};
+class ReorderTestReceiver : public TestReceiver {
+ public:
+ ReorderTestReceiver() {}
+ virtual ~ReorderTestReceiver() {}
+
+ DeliveryStatus DeliverPacket(MediaType media_type,
+ const uint8_t* packet,
+ size_t length,
+ const PacketTime& packet_time) override {
+ int seq_num;
+ memcpy(&seq_num, packet, sizeof(int));
+ delivered_sequence_numbers_.push_back(seq_num);
+ return PacketReceiver::DELIVERY_OK;
+ }
+ std::vector<int> delivered_sequence_numbers_;
+};
+
class FakeNetworkPipeTest : public ::testing::Test {
public:
FakeNetworkPipeTest() : fake_clock_(12345) {}
protected:
virtual void SetUp() {
- receiver_.reset(new MockReceiver());
+ receiver_.reset(new TestReceiver());
ON_CALL(*receiver_, DeliverPacket(_, _, _, _))
.WillByDefault(Return(PacketReceiver::DELIVERY_OK));
}
@@ -52,19 +69,23 @@
virtual void TearDown() {
}
- void SendPackets(FakeNetworkPipe* pipe, int number_packets, int kPacketSize) {
- rtc::scoped_ptr<uint8_t[]> packet(new uint8_t[kPacketSize]);
+ void SendPackets(FakeNetworkPipe* pipe, int number_packets, int packet_size) {
+ RTC_DCHECK_GE(packet_size, static_cast<int>(sizeof(int)));
+ rtc::scoped_ptr<uint8_t[]> packet(new uint8_t[packet_size]);
for (int i = 0; i < number_packets; ++i) {
- pipe->SendPacket(packet.get(), kPacketSize);
+ // Set a sequence number for the packets by
+ // using the first bytes in the packet.
+ memcpy(packet.get(), &i, sizeof(int));
+ pipe->SendPacket(packet.get(), packet_size);
}
}
- int PacketTimeMs(int capacity_kbps, int kPacketSize) const {
- return 8 * kPacketSize / capacity_kbps;
+ int PacketTimeMs(int capacity_kbps, int packet_size) const {
+ return 8 * packet_size / capacity_kbps;
}
SimulatedClock fake_clock_;
- rtc::scoped_ptr<MockReceiver> receiver_;
+ rtc::scoped_ptr<TestReceiver> receiver_;
};
void DeleteMemory(uint8_t* data, int length) { delete [] data; }
@@ -308,4 +329,53 @@
EXPECT_CALL(*receiver_, DeliverPacket(_, _, _, _)).Times(0);
pipe->Process();
}
+
+// At first disallow reordering and then allow reordering.
+TEST_F(FakeNetworkPipeTest, DisallowReorderingThenAllowReordering) {
+ FakeNetworkPipe::Config config;
+ config.queue_length_packets = 1000;
+ config.link_capacity_kbps = 800;
+ config.queue_delay_ms = 100;
+ config.delay_standard_deviation_ms = 10;
+ rtc::scoped_ptr<FakeNetworkPipe> pipe(
+ new FakeNetworkPipe(&fake_clock_, config));
+ ReorderTestReceiver* receiver = new ReorderTestReceiver();
+ receiver_.reset(receiver);
+ pipe->SetReceiver(receiver_.get());
+
+ const uint32_t kNumPackets = 100;
+ const int kPacketSize = 10;
+ SendPackets(pipe.get(), kNumPackets, kPacketSize);
+ fake_clock_.AdvanceTimeMilliseconds(1000);
+ pipe->Process();
+
+ // Confirm that all packets have been delivered in order.
+ EXPECT_EQ(kNumPackets, receiver->delivered_sequence_numbers_.size());
+ int last_seq_num = -1;
+ for (int seq_num : receiver->delivered_sequence_numbers_) {
+ EXPECT_GT(seq_num, last_seq_num);
+ last_seq_num = seq_num;
+ }
+
+ config.allow_reordering = true;
+ pipe->SetConfig(config);
+ SendPackets(pipe.get(), kNumPackets, kPacketSize);
+ fake_clock_.AdvanceTimeMilliseconds(1000);
+ receiver->delivered_sequence_numbers_.clear();
+ pipe->Process();
+
+ // Confirm that all packets have been delivered
+ // and that reordering has occured.
+ EXPECT_EQ(kNumPackets, receiver->delivered_sequence_numbers_.size());
+ bool reordering_has_occured = false;
+ last_seq_num = -1;
+ for (int seq_num : receiver->delivered_sequence_numbers_) {
+ if (last_seq_num > seq_num) {
+ reordering_has_occured = true;
+ break;
+ }
+ last_seq_num = seq_num;
+ }
+ EXPECT_TRUE(reordering_has_occured);
+}
} // namespace webrtc
diff --git a/video/screenshare_loopback.cc b/video/screenshare_loopback.cc
index 6479aa4..91002ef 100644
--- a/video/screenshare_loopback.cc
+++ b/video/screenshare_loopback.cc
@@ -174,6 +174,8 @@
DEFINE_bool(send_side_bwe, true, "Use send-side bandwidth estimation");
+DEFINE_bool(allow_reordering, false, "Allow packet reordering to occur");
+
DEFINE_string(
force_fieldtrials,
"",
@@ -212,6 +214,7 @@
pipe_config.queue_length_packets = flags::QueueSize();
pipe_config.queue_delay_ms = flags::AvgPropagationDelayMs();
pipe_config.delay_standard_deviation_ms = flags::StdPropagationDelayMs();
+ pipe_config.allow_reordering = flags::FLAGS_allow_reordering;
Call::Config::BitrateConfig call_bitrate_config;
call_bitrate_config.min_bitrate_bps = flags::MinBitrateKbps() * 1000;
diff --git a/video/video_loopback.cc b/video/video_loopback.cc
index 2338a84..87aacc6 100644
--- a/video/video_loopback.cc
+++ b/video/video_loopback.cc
@@ -176,6 +176,8 @@
DEFINE_bool(send_side_bwe, true, "Use send-side bandwidth estimation");
+DEFINE_bool(allow_reordering, false, "Allow packet reordering to occur");
+
DEFINE_string(
force_fieldtrials,
"",
@@ -201,6 +203,7 @@
pipe_config.queue_length_packets = flags::QueueSize();
pipe_config.queue_delay_ms = flags::AvgPropagationDelayMs();
pipe_config.delay_standard_deviation_ms = flags::StdPropagationDelayMs();
+ pipe_config.allow_reordering = flags::FLAGS_allow_reordering;
Call::Config::BitrateConfig call_bitrate_config;
call_bitrate_config.min_bitrate_bps = flags::MinBitrateKbps() * 1000;