Support for simulating multiple independent flows in a network.
R=solenberg@webrtc.org Review URL: https://webrtc-codereview.appspot.com/8639004 git-svn-id: http://webrtc.googlecode.com/svn/trunk@5980 4adac7df-926f-26a2-2b94-8c16560cd09d
This commit is contained in:
parent
46106f2a05
commit
4f616a02bd
@ -8,55 +8,53 @@
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
#include "webrtc/modules/remote_bitrate_estimator/include/remote_bitrate_estimator.h"
|
||||
#include "webrtc/modules/remote_bitrate_estimator/test/bwe_test.h"
|
||||
#include "webrtc/test/testsupport/fileutils.h"
|
||||
|
||||
using std::string;
|
||||
|
||||
namespace webrtc {
|
||||
namespace testing {
|
||||
namespace bwe {
|
||||
#if BWE_TEST_LOGGING_COMPILE_TIME_ENABLE
|
||||
std::vector<BweTestConfig::EstimatorConfig> SingleEstimatorConfig() {
|
||||
BweTestConfig::EstimatorConfig CreateEstimatorConfig(
|
||||
int flow_id, bool plot_delay, bool plot_estimate) {
|
||||
static const AbsoluteSendTimeRemoteBitrateEstimatorFactory factory =
|
||||
AbsoluteSendTimeRemoteBitrateEstimatorFactory();
|
||||
|
||||
std::vector<BweTestConfig::EstimatorConfig> result;
|
||||
result.push_back(BweTestConfig::EstimatorConfig("AST", &factory, kAimdControl,
|
||||
false));
|
||||
return result;
|
||||
}
|
||||
|
||||
std::vector<const PacketSenderFactory*> AdaptiveVideoSenderFactories(
|
||||
uint32_t count) {
|
||||
static const AdaptiveVideoPacketSenderFactory factories[] = {
|
||||
AdaptiveVideoPacketSenderFactory(30.00f, 150, 0x1234, 0.13f),
|
||||
AdaptiveVideoPacketSenderFactory(30.00f, 300, 0x3456, 0.26f),
|
||||
AdaptiveVideoPacketSenderFactory(15.00f, 600, 0x4567, 0.39f),
|
||||
};
|
||||
|
||||
assert(count <= sizeof(factories) / sizeof(factories[0]));
|
||||
|
||||
std::vector<const PacketSenderFactory*> result;
|
||||
for (uint32_t i = 0; i < count; ++i) {
|
||||
result.push_back(&factories[i]);
|
||||
}
|
||||
return result;
|
||||
return BweTestConfig::EstimatorConfig("AST", flow_id, &factory, kAimdControl,
|
||||
plot_delay, plot_estimate);
|
||||
}
|
||||
|
||||
BweTestConfig MakeAdaptiveBweTestConfig(uint32_t sender_count) {
|
||||
BweTestConfig result = {
|
||||
AdaptiveVideoSenderFactories(sender_count), SingleEstimatorConfig()
|
||||
};
|
||||
BweTestConfig result;
|
||||
result.estimator_configs.push_back(CreateEstimatorConfig(0, true, true));
|
||||
return result;
|
||||
}
|
||||
|
||||
BweTestConfig MakeMultiFlowBweTestConfig(int flow_count) {
|
||||
BweTestConfig result;
|
||||
for (int i = 0; i < flow_count; ++i) {
|
||||
result.estimator_configs.push_back(CreateEstimatorConfig(i, false, true));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// This test fixture is used to instantiate tests running with adaptive video
|
||||
// senders.
|
||||
class BweSimulation : public BweTest {
|
||||
class BweSimulation : public BweTest,
|
||||
public ::testing::TestWithParam<BweTestConfig> {
|
||||
public:
|
||||
BweSimulation() : BweTest() {}
|
||||
virtual ~BweSimulation() {}
|
||||
|
||||
virtual void SetUp() {
|
||||
const BweTestConfig& config = GetParam();
|
||||
SetupTestFromConfig(config);
|
||||
}
|
||||
|
||||
private:
|
||||
DISALLOW_COPY_AND_ASSIGN(BweSimulation);
|
||||
};
|
||||
@ -67,6 +65,7 @@ INSTANTIATE_TEST_CASE_P(VideoSendersTest, BweSimulation,
|
||||
|
||||
TEST_P(BweSimulation, SprintUplinkTest) {
|
||||
VerboseLogging(true);
|
||||
AdaptiveVideoSender sender(0, this, 30, 300, 0, 0);
|
||||
RateCounterFilter counter1(this, "sender_output");
|
||||
TraceBasedDeliveryFilter filter(this, "link_capacity");
|
||||
RateCounterFilter counter2(this, "receiver_input");
|
||||
@ -76,6 +75,7 @@ TEST_P(BweSimulation, SprintUplinkTest) {
|
||||
|
||||
TEST_P(BweSimulation, Verizon4gDownlinkTest) {
|
||||
VerboseLogging(true);
|
||||
AdaptiveVideoSender sender(0, this, 30, 300, 0, 0);
|
||||
RateCounterFilter counter1(this, "sender_output");
|
||||
TraceBasedDeliveryFilter filter(this, "link_capacity");
|
||||
RateCounterFilter counter2(this, "receiver_input");
|
||||
@ -85,6 +85,7 @@ TEST_P(BweSimulation, Verizon4gDownlinkTest) {
|
||||
|
||||
TEST_P(BweSimulation, Choke1000kbps500kbps1000kbps) {
|
||||
VerboseLogging(true);
|
||||
AdaptiveVideoSender sender(0, this, 30, 300, 0, 0);
|
||||
ChokeFilter filter(this);
|
||||
RateCounterFilter counter(this, "receiver_input");
|
||||
filter.SetCapacity(1000);
|
||||
@ -98,6 +99,7 @@ TEST_P(BweSimulation, Choke1000kbps500kbps1000kbps) {
|
||||
|
||||
TEST_P(BweSimulation, Choke200kbps30kbps200kbps) {
|
||||
VerboseLogging(true);
|
||||
AdaptiveVideoSender sender(0, this, 30, 300, 0, 0);
|
||||
ChokeFilter filter(this);
|
||||
RateCounterFilter counter(this, "receiver_input");
|
||||
filter.SetCapacity(200);
|
||||
@ -111,6 +113,7 @@ TEST_P(BweSimulation, Choke200kbps30kbps200kbps) {
|
||||
|
||||
TEST_P(BweSimulation, GoogleWifiTrace3Mbps) {
|
||||
VerboseLogging(true);
|
||||
AdaptiveVideoSender sender(0, this, 30, 300, 0, 0);
|
||||
RateCounterFilter counter1(this, "sender_output");
|
||||
TraceBasedDeliveryFilter filter(this, "link_capacity");
|
||||
filter.SetMaxDelay(500);
|
||||
@ -118,6 +121,42 @@ TEST_P(BweSimulation, GoogleWifiTrace3Mbps) {
|
||||
ASSERT_TRUE(filter.Init(test::ResourcePath("google-wifi-3mbps", "rx")));
|
||||
RunFor(300 * 1000);
|
||||
}
|
||||
|
||||
class MultiFlowBweSimulation : public BweSimulation {
|
||||
public:
|
||||
MultiFlowBweSimulation() : BweSimulation() {}
|
||||
virtual ~MultiFlowBweSimulation() {}
|
||||
|
||||
private:
|
||||
DISALLOW_COPY_AND_ASSIGN(MultiFlowBweSimulation);
|
||||
};
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(VideoSendersTest, MultiFlowBweSimulation,
|
||||
::testing::Values(MakeMultiFlowBweTestConfig(3)));
|
||||
|
||||
TEST_P(MultiFlowBweSimulation, SelfFairnessTest) {
|
||||
VerboseLogging(true);
|
||||
const int kAllFlowIds[] = {0, 1, 2};
|
||||
const size_t kNumFlows = sizeof(kAllFlowIds) / sizeof(kAllFlowIds[0]);
|
||||
scoped_ptr<AdaptiveVideoSender> senders[kNumFlows];
|
||||
for (size_t i = 0; i < kNumFlows; ++i) {
|
||||
senders[i].reset(new AdaptiveVideoSender(kAllFlowIds[i], this, 30, 300, 0,
|
||||
0));
|
||||
}
|
||||
// Second and third flow.
|
||||
ChokeFilter choke(this, CreateFlowIds(&kAllFlowIds[1], 2));
|
||||
choke.SetCapacity(1500);
|
||||
// First flow.
|
||||
ChokeFilter choke2(this, CreateFlowIds(&kAllFlowIds[0], 1));
|
||||
choke2.SetCapacity(1000);
|
||||
|
||||
scoped_ptr<RateCounterFilter> rate_counters[kNumFlows];
|
||||
for (size_t i = 0; i < kNumFlows; ++i) {
|
||||
rate_counters[i].reset(new RateCounterFilter(
|
||||
this, CreateFlowIds(&kAllFlowIds[i], 1), "receiver_input"));
|
||||
}
|
||||
RunFor(30 * 60 * 1000);
|
||||
}
|
||||
#endif // BWE_TEST_LOGGING_COMPILE_TIME_ENABLE
|
||||
} // namespace bwe
|
||||
} // namespace testing
|
||||
|
||||
@ -12,68 +12,90 @@
|
||||
#include "webrtc/modules/remote_bitrate_estimator/test/bwe_test.h"
|
||||
#include "webrtc/test/testsupport/fileutils.h"
|
||||
|
||||
using std::string;
|
||||
|
||||
namespace webrtc {
|
||||
namespace testing {
|
||||
namespace bwe {
|
||||
std::vector<const PacketSenderFactory*> VideoSenderFactories(uint32_t count) {
|
||||
static const VideoPacketSenderFactory factories[] = {
|
||||
VideoPacketSenderFactory(30.00f, 150, 0x1234, 0.13f),
|
||||
VideoPacketSenderFactory(15.00f, 500, 0x2345, 0.16f),
|
||||
VideoPacketSenderFactory(30.00f, 1200, 0x3456, 0.26f),
|
||||
VideoPacketSenderFactory(7.49f, 150, 0x4567, 0.05f),
|
||||
VideoPacketSenderFactory(7.50f, 150, 0x5678, 0.15f),
|
||||
VideoPacketSenderFactory(7.51f, 150, 0x6789, 0.25f),
|
||||
VideoPacketSenderFactory(15.02f, 150, 0x7890, 0.27f),
|
||||
VideoPacketSenderFactory(15.03f, 150, 0x8901, 0.38f),
|
||||
VideoPacketSenderFactory(30.02f, 150, 0x9012, 0.39f),
|
||||
VideoPacketSenderFactory(30.03f, 150, 0x0123, 0.52f)
|
||||
};
|
||||
enum Estimator { kAbsSendTime, kTransmissionOffset };
|
||||
|
||||
assert(count <= sizeof(factories) / sizeof(factories[0]));
|
||||
|
||||
std::vector<const PacketSenderFactory*> result;
|
||||
for (uint32_t i = 0; i < count; ++i) {
|
||||
result.push_back(&factories[i]);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
std::vector<BweTestConfig::EstimatorConfig> EstimatorConfigs() {
|
||||
BweTestConfig::EstimatorConfig EstimatorConfigs(Estimator estimator,
|
||||
int flow_id) {
|
||||
static const RemoteBitrateEstimatorFactory factories[] = {
|
||||
RemoteBitrateEstimatorFactory(),
|
||||
AbsoluteSendTimeRemoteBitrateEstimatorFactory()
|
||||
};
|
||||
switch (estimator) {
|
||||
case kTransmissionOffset:
|
||||
return BweTestConfig::EstimatorConfig("TOF", flow_id, &factories[0], true,
|
||||
true);
|
||||
case kAbsSendTime:
|
||||
return BweTestConfig::EstimatorConfig("AST", flow_id, &factories[1], true,
|
||||
true);
|
||||
default:
|
||||
assert(false);
|
||||
return BweTestConfig::EstimatorConfig("AST", flow_id, &factories[1], true,
|
||||
true);
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<BweTestConfig::EstimatorConfig> result;
|
||||
result.push_back(BweTestConfig::EstimatorConfig("TOF", &factories[0]));
|
||||
result.push_back(BweTestConfig::EstimatorConfig("AST", &factories[1]));
|
||||
struct DefaultBweTestConfig {
|
||||
BweTestConfig bwe_test_config;
|
||||
size_t number_of_senders;
|
||||
};
|
||||
|
||||
DefaultBweTestConfig MakeBweTestConfig(uint32_t sender_count,
|
||||
Estimator estimator) {
|
||||
DefaultBweTestConfig result;
|
||||
result.bwe_test_config.estimator_configs.push_back(
|
||||
EstimatorConfigs(estimator, 0));
|
||||
result.number_of_senders = sender_count;
|
||||
return result;
|
||||
}
|
||||
|
||||
BweTestConfig MakeBweTestConfig(uint32_t sender_count) {
|
||||
BweTestConfig result = {
|
||||
VideoSenderFactories(sender_count), EstimatorConfigs()
|
||||
};
|
||||
return result;
|
||||
}
|
||||
class DefaultBweTest : public BweTest,
|
||||
public ::testing::TestWithParam<DefaultBweTestConfig> {
|
||||
public:
|
||||
DefaultBweTest() : packet_senders_() {}
|
||||
virtual ~DefaultBweTest() {}
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(VideoSendersTest, BweTest,
|
||||
::testing::Values(MakeBweTestConfig(1),
|
||||
MakeBweTestConfig(3)));
|
||||
virtual void SetUp() {
|
||||
const DefaultBweTestConfig& config = GetParam();
|
||||
SetupTestFromConfig(config.bwe_test_config);
|
||||
for (size_t i = 0; i < config.number_of_senders; ++i) {
|
||||
packet_senders_.push_back(new VideoSender(0, this, 30, 300, 0, 0));
|
||||
}
|
||||
}
|
||||
|
||||
TEST_P(BweTest, UnlimitedSpeed) {
|
||||
virtual void TearDown() {
|
||||
while (packet_senders_.empty()) {
|
||||
delete packet_senders_.front();
|
||||
packet_senders_.pop_front();
|
||||
}
|
||||
}
|
||||
|
||||
protected:
|
||||
std::list<PacketSender*> packet_senders_;
|
||||
};
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(VideoSendersTest, DefaultBweTest,
|
||||
::testing::Values(MakeBweTestConfig(1, kAbsSendTime),
|
||||
MakeBweTestConfig(3, kAbsSendTime),
|
||||
MakeBweTestConfig(1, kTransmissionOffset),
|
||||
MakeBweTestConfig(3, kTransmissionOffset)));
|
||||
|
||||
TEST_P(DefaultBweTest, UnlimitedSpeed) {
|
||||
VerboseLogging(false);
|
||||
RunFor(10 * 60 * 1000);
|
||||
}
|
||||
|
||||
TEST_P(BweTest, DISABLED_SteadyLoss) {
|
||||
TEST_P(DefaultBweTest, DISABLED_SteadyLoss) {
|
||||
LossFilter loss(this);
|
||||
loss.SetLoss(20.0);
|
||||
RunFor(10 * 60 * 1000);
|
||||
}
|
||||
|
||||
TEST_P(BweTest, IncreasingLoss1) {
|
||||
TEST_P(DefaultBweTest, IncreasingLoss1) {
|
||||
LossFilter loss(this);
|
||||
for (int i = 0; i < 76; ++i) {
|
||||
loss.SetLoss(i);
|
||||
@ -81,13 +103,13 @@ TEST_P(BweTest, IncreasingLoss1) {
|
||||
}
|
||||
}
|
||||
|
||||
TEST_P(BweTest, SteadyDelay) {
|
||||
TEST_P(DefaultBweTest, SteadyDelay) {
|
||||
DelayFilter delay(this);
|
||||
delay.SetDelay(1000);
|
||||
RunFor(10 * 60 * 1000);
|
||||
}
|
||||
|
||||
TEST_P(BweTest, DISABLED_IncreasingDelay1) {
|
||||
TEST_P(DefaultBweTest, DISABLED_IncreasingDelay1) {
|
||||
DelayFilter delay(this);
|
||||
RunFor(10 * 60 * 1000);
|
||||
for (int i = 0; i < 30 * 2; ++i) {
|
||||
@ -97,7 +119,7 @@ TEST_P(BweTest, DISABLED_IncreasingDelay1) {
|
||||
RunFor(10 * 60 * 1000);
|
||||
}
|
||||
|
||||
TEST_P(BweTest, IncreasingDelay2) {
|
||||
TEST_P(DefaultBweTest, IncreasingDelay2) {
|
||||
DelayFilter delay(this);
|
||||
RateCounterFilter counter(this);
|
||||
RunFor(1 * 60 * 1000);
|
||||
@ -109,7 +131,7 @@ TEST_P(BweTest, IncreasingDelay2) {
|
||||
RunFor(10 * 60 * 1000);
|
||||
}
|
||||
|
||||
TEST_P(BweTest, JumpyDelay1) {
|
||||
TEST_P(DefaultBweTest, JumpyDelay1) {
|
||||
DelayFilter delay(this);
|
||||
RunFor(10 * 60 * 1000);
|
||||
for (int i = 1; i < 200; ++i) {
|
||||
@ -122,14 +144,14 @@ TEST_P(BweTest, JumpyDelay1) {
|
||||
RunFor(10 * 60 * 1000);
|
||||
}
|
||||
|
||||
TEST_P(BweTest, SteadyJitter) {
|
||||
TEST_P(DefaultBweTest, SteadyJitter) {
|
||||
JitterFilter jitter(this);
|
||||
RateCounterFilter counter(this);
|
||||
jitter.SetJitter(20);
|
||||
RunFor(2 * 60 * 1000);
|
||||
}
|
||||
|
||||
TEST_P(BweTest, IncreasingJitter1) {
|
||||
TEST_P(DefaultBweTest, IncreasingJitter1) {
|
||||
JitterFilter jitter(this);
|
||||
for (int i = 0; i < 2 * 60 * 2; ++i) {
|
||||
jitter.SetJitter(i);
|
||||
@ -138,7 +160,7 @@ TEST_P(BweTest, IncreasingJitter1) {
|
||||
RunFor(10 * 60 * 1000);
|
||||
}
|
||||
|
||||
TEST_P(BweTest, IncreasingJitter2) {
|
||||
TEST_P(DefaultBweTest, IncreasingJitter2) {
|
||||
JitterFilter jitter(this);
|
||||
RunFor(30 * 1000);
|
||||
for (int i = 1; i < 51; ++i) {
|
||||
@ -149,13 +171,13 @@ TEST_P(BweTest, IncreasingJitter2) {
|
||||
RunFor(10 * 60 * 1000);
|
||||
}
|
||||
|
||||
TEST_P(BweTest, SteadyReorder) {
|
||||
TEST_P(DefaultBweTest, SteadyReorder) {
|
||||
ReorderFilter reorder(this);
|
||||
reorder.SetReorder(20.0);
|
||||
RunFor(10 * 60 * 1000);
|
||||
}
|
||||
|
||||
TEST_P(BweTest, IncreasingReorder1) {
|
||||
TEST_P(DefaultBweTest, IncreasingReorder1) {
|
||||
ReorderFilter reorder(this);
|
||||
for (int i = 0; i < 76; ++i) {
|
||||
reorder.SetReorder(i);
|
||||
@ -163,13 +185,13 @@ TEST_P(BweTest, IncreasingReorder1) {
|
||||
}
|
||||
}
|
||||
|
||||
TEST_P(BweTest, DISABLED_SteadyChoke) {
|
||||
TEST_P(DefaultBweTest, DISABLED_SteadyChoke) {
|
||||
ChokeFilter choke(this);
|
||||
choke.SetCapacity(140);
|
||||
RunFor(10 * 60 * 1000);
|
||||
}
|
||||
|
||||
TEST_P(BweTest, DISABLED_IncreasingChoke1) {
|
||||
TEST_P(DefaultBweTest, DISABLED_IncreasingChoke1) {
|
||||
ChokeFilter choke(this);
|
||||
for (int i = 1200; i >= 100; i -= 100) {
|
||||
choke.SetCapacity(i);
|
||||
@ -177,7 +199,7 @@ TEST_P(BweTest, DISABLED_IncreasingChoke1) {
|
||||
}
|
||||
}
|
||||
|
||||
TEST_P(BweTest, DISABLED_IncreasingChoke2) {
|
||||
TEST_P(DefaultBweTest, DISABLED_IncreasingChoke2) {
|
||||
ChokeFilter choke(this);
|
||||
RunFor(60 * 1000);
|
||||
for (int i = 1200; i >= 100; i -= 20) {
|
||||
@ -186,7 +208,7 @@ TEST_P(BweTest, DISABLED_IncreasingChoke2) {
|
||||
}
|
||||
}
|
||||
|
||||
TEST_P(BweTest, DISABLED_Multi1) {
|
||||
TEST_P(DefaultBweTest, DISABLED_Multi1) {
|
||||
DelayFilter delay(this);
|
||||
ChokeFilter choke(this);
|
||||
RateCounterFilter counter(this);
|
||||
@ -201,7 +223,7 @@ TEST_P(BweTest, DISABLED_Multi1) {
|
||||
RunFor(5 * 60 * 1000);
|
||||
}
|
||||
|
||||
TEST_P(BweTest, Multi2) {
|
||||
TEST_P(DefaultBweTest, Multi2) {
|
||||
ChokeFilter choke(this);
|
||||
JitterFilter jitter(this);
|
||||
RateCounterFilter counter(this);
|
||||
|
||||
@ -23,44 +23,40 @@ namespace webrtc {
|
||||
namespace testing {
|
||||
namespace bwe {
|
||||
|
||||
namespace stl_helpers {
|
||||
template<typename T> void DeleteElements(T* container) {
|
||||
if (!container) return;
|
||||
for (typename T::iterator it = container->begin(); it != container->end();
|
||||
++it) {
|
||||
delete *it;
|
||||
}
|
||||
container->clear();
|
||||
}
|
||||
} // namespace stl_helpers
|
||||
|
||||
class BweTest::TestedEstimator : public RemoteBitrateObserver {
|
||||
class TestedEstimator : public RemoteBitrateObserver {
|
||||
public:
|
||||
static const uint32_t kRemoteBitrateEstimatorMinBitrateBps = 30000;
|
||||
static const int kDelayPlotIntervalMs = 100;
|
||||
|
||||
TestedEstimator(const string& test_name,
|
||||
const BweTestConfig::EstimatorConfig& config)
|
||||
: debug_name_(config.debug_name),
|
||||
delay_log_prefix_(),
|
||||
estimate_log_prefix_(),
|
||||
last_delay_plot_ms_(0),
|
||||
plot_delay_(config.plot_delay),
|
||||
plot_estimate_(config.plot_estimate),
|
||||
clock_(0),
|
||||
stats_(),
|
||||
relative_estimator_stats_(),
|
||||
latest_estimate_bps_(-1),
|
||||
estimator_(config.estimator_factory->Create(
|
||||
this, &clock_, config.control_type,
|
||||
kRemoteBitrateEstimatorMinBitrateBps)),
|
||||
relative_estimator_(NULL),
|
||||
baseline_(BaseLineFileInterface::Create(test_name + "_" + debug_name_,
|
||||
config.update_baseline)) {
|
||||
assert(estimator_.get());
|
||||
assert(baseline_.get());
|
||||
// Setup the prefix strings used when logging.
|
||||
std::stringstream ss;
|
||||
ss << "Delay_" << config.flow_id << "#2";
|
||||
delay_log_prefix_ = ss.str();
|
||||
ss.str("");
|
||||
ss << "Estimate_" << config.flow_id << "#1";
|
||||
estimate_log_prefix_ = ss.str();
|
||||
// Default RTT in RemoteRateControl is 200 ms ; 50 ms is more realistic.
|
||||
estimator_->OnRttUpdate(50);
|
||||
}
|
||||
|
||||
void SetRelativeEstimator(TestedEstimator* relative_estimator) {
|
||||
relative_estimator_ = relative_estimator;
|
||||
}
|
||||
|
||||
void EatPacket(const Packet& packet) {
|
||||
BWE_TEST_LOGGING_CONTEXT(debug_name_);
|
||||
|
||||
@ -70,9 +66,15 @@ class BweTest::TestedEstimator : public RemoteBitrateObserver {
|
||||
// time once packet reaches the estimator.
|
||||
int64_t packet_time_ms = (packet.send_time_us() + 500) / 1000;
|
||||
BWE_TEST_LOGGING_TIME(packet_time_ms);
|
||||
BWE_TEST_LOGGING_PLOT("Delay_#2", clock_.TimeInMilliseconds(),
|
||||
packet_time_ms -
|
||||
(packet.creation_time_us() + 500) / 1000);
|
||||
if (plot_delay_) {
|
||||
if (clock_.TimeInMilliseconds() - last_delay_plot_ms_ >
|
||||
kDelayPlotIntervalMs) {
|
||||
BWE_TEST_LOGGING_PLOT(delay_log_prefix_, clock_.TimeInMilliseconds(),
|
||||
packet_time_ms -
|
||||
(packet.creation_time_us() + 500) / 1000);
|
||||
last_delay_plot_ms_ = clock_.TimeInMilliseconds();
|
||||
}
|
||||
}
|
||||
|
||||
int64_t step_ms = estimator_->TimeUntilNextProcess();
|
||||
while ((clock_.TimeInMilliseconds() + step_ms) < packet_time_ms) {
|
||||
@ -97,14 +99,9 @@ class BweTest::TestedEstimator : public RemoteBitrateObserver {
|
||||
|
||||
double estimated_kbps = static_cast<double>(estimated_bps) / 1000.0;
|
||||
stats_.Push(estimated_kbps);
|
||||
BWE_TEST_LOGGING_PLOT("Estimate_#1", clock_.TimeInMilliseconds(),
|
||||
estimated_kbps);
|
||||
uint32_t relative_estimate_bps = 0;
|
||||
if (relative_estimator_ &&
|
||||
relative_estimator_->LatestEstimate(&relative_estimate_bps)) {
|
||||
double relative_estimate_kbps =
|
||||
static_cast<double>(relative_estimate_bps) / 1000.0;
|
||||
relative_estimator_stats_.Push(estimated_kbps - relative_estimate_kbps);
|
||||
if (plot_estimate_) {
|
||||
BWE_TEST_LOGGING_PLOT(estimate_log_prefix_, clock_.TimeInMilliseconds(),
|
||||
estimated_kbps);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@ -115,10 +112,6 @@ class BweTest::TestedEstimator : public RemoteBitrateObserver {
|
||||
BWE_TEST_LOGGING_CONTEXT(debug_name_);
|
||||
BWE_TEST_LOGGING_CONTEXT("Mean");
|
||||
stats_.Log("kbps");
|
||||
if (relative_estimator_) {
|
||||
BWE_TEST_LOGGING_CONTEXT("Diff");
|
||||
relative_estimator_stats_.Log("kbps");
|
||||
}
|
||||
}
|
||||
|
||||
void VerifyOrWriteBaseline() {
|
||||
@ -144,142 +137,208 @@ class BweTest::TestedEstimator : public RemoteBitrateObserver {
|
||||
}
|
||||
|
||||
string debug_name_;
|
||||
string delay_log_prefix_;
|
||||
string estimate_log_prefix_;
|
||||
int64_t last_delay_plot_ms_;
|
||||
bool plot_delay_;
|
||||
bool plot_estimate_;
|
||||
SimulatedClock clock_;
|
||||
Stats<double> stats_;
|
||||
Stats<double> relative_estimator_stats_;
|
||||
int64_t latest_estimate_bps_;
|
||||
scoped_ptr<RemoteBitrateEstimator> estimator_;
|
||||
TestedEstimator* relative_estimator_;
|
||||
scoped_ptr<BaseLineFileInterface> baseline_;
|
||||
|
||||
DISALLOW_IMPLICIT_CONSTRUCTORS(TestedEstimator);
|
||||
};
|
||||
|
||||
class PacketProcessorRunner {
|
||||
public:
|
||||
explicit PacketProcessorRunner(PacketProcessor* processor)
|
||||
: processor_(processor) {}
|
||||
|
||||
bool HasProcessor(const PacketProcessor* processor) const {
|
||||
return processor == processor_;
|
||||
}
|
||||
|
||||
void RunFor(int64_t time_ms, int64_t time_now_ms, Packets* in_out) {
|
||||
Packets to_process;
|
||||
FindPacketsToProcess(processor_->flow_ids(), in_out, &to_process);
|
||||
processor_->RunFor(time_ms, &to_process);
|
||||
QueuePackets(&to_process, time_now_ms * 1000);
|
||||
if (!to_process.empty()) {
|
||||
processor_->Plot((to_process.back().send_time_us() + 500) / 1000);
|
||||
}
|
||||
in_out->merge(to_process);
|
||||
}
|
||||
|
||||
private:
|
||||
void FindPacketsToProcess(const FlowIds& flow_ids, Packets* in,
|
||||
Packets* out) {
|
||||
assert(out->empty());
|
||||
for (Packets::iterator it = in->begin(); it != in->end();) {
|
||||
// TODO(holmer): Further optimize this by looking for consecutive flow ids
|
||||
// in the packet list and only doing the binary search + splice once for a
|
||||
// sequence.
|
||||
if (std::binary_search(flow_ids.begin(), flow_ids.end(), it->flow_id())) {
|
||||
Packets::iterator next = it;
|
||||
++next;
|
||||
out->splice(out->end(), *in, it);
|
||||
it = next;
|
||||
} else {
|
||||
++it;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void QueuePackets(Packets* batch, int64_t end_of_batch_time_us) {
|
||||
queue_.merge(*batch);
|
||||
if (queue_.empty()) {
|
||||
return;
|
||||
}
|
||||
Packets to_transfer;
|
||||
Packets::iterator it = queue_.begin();
|
||||
for (; it != queue_.end(); ++it) {
|
||||
if (it->send_time_us() > end_of_batch_time_us) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
to_transfer.splice(to_transfer.begin(), queue_, queue_.begin(), it);
|
||||
batch->merge(to_transfer);
|
||||
}
|
||||
|
||||
PacketProcessor* processor_;
|
||||
Packets queue_;
|
||||
};
|
||||
|
||||
BweTest::BweTest()
|
||||
: run_time_ms_(0),
|
||||
time_now_ms_(-1),
|
||||
simulation_interval_ms_(-1),
|
||||
previous_packets_(),
|
||||
packet_senders_(),
|
||||
estimators_(),
|
||||
processors_() {
|
||||
}
|
||||
|
||||
BweTest::~BweTest() {
|
||||
stl_helpers::DeleteElements(&estimators_);
|
||||
stl_helpers::DeleteElements(&packet_senders_);
|
||||
BWE_TEST_LOGGING_GLOBAL_ENABLE(true);
|
||||
for (EstimatorMap::iterator it = estimators_.begin(); it != estimators_.end();
|
||||
++it) {
|
||||
it->second->VerifyOrWriteBaseline();
|
||||
it->second->LogStats();
|
||||
}
|
||||
BWE_TEST_LOGGING_GLOBAL_CONTEXT("");
|
||||
|
||||
for (EstimatorMap::iterator it = estimators_.begin();
|
||||
it != estimators_.end(); ++it) {
|
||||
delete it->second;
|
||||
}
|
||||
}
|
||||
|
||||
void BweTest::SetUp() {
|
||||
void BweTest::SetupTestFromConfig(const BweTestConfig& config) {
|
||||
const ::testing::TestInfo* const test_info =
|
||||
::testing::UnitTest::GetInstance()->current_test_info();
|
||||
string test_name =
|
||||
string(test_info->test_case_name()) + "_" + string(test_info->name());
|
||||
BWE_TEST_LOGGING_GLOBAL_CONTEXT(test_name);
|
||||
|
||||
const BweTestConfig& config = GetParam();
|
||||
|
||||
uint32_t total_capacity = 0;
|
||||
for (vector<const PacketSenderFactory*>::const_iterator it =
|
||||
config.sender_factories.begin(); it != config.sender_factories.end();
|
||||
++it) {
|
||||
PacketSender* sender = (*it)->Create();
|
||||
assert(sender);
|
||||
total_capacity += sender->GetCapacityKbps();
|
||||
packet_senders_.push_back(sender);
|
||||
processors_.push_back(sender);
|
||||
for (vector<BweTestConfig::EstimatorConfig>::const_iterator it =
|
||||
config.estimator_configs.begin(); it != config.estimator_configs.end();
|
||||
++it) {
|
||||
estimators_.insert(std::make_pair(it->flow_id, new TestedEstimator(
|
||||
test_name, *it)));
|
||||
}
|
||||
BWE_TEST_LOGGING_LOG1("RequiredLinkCapacity", "%d kbps", total_capacity)
|
||||
|
||||
// Set simulation interval from first packet sender.
|
||||
if (packet_senders_.size() > 0) {
|
||||
simulation_interval_ms_ = packet_senders_[0]->GetFeedbackIntervalMs();
|
||||
}
|
||||
|
||||
for (vector<BweTestConfig::EstimatorConfig>:: const_iterator it =
|
||||
config.estimator_configs.begin(); it != config.estimator_configs.end();
|
||||
++it) {
|
||||
estimators_.push_back(new TestedEstimator(test_name, *it));
|
||||
}
|
||||
if (estimators_.size() > 1) {
|
||||
// Set all estimators as relative to the first one.
|
||||
for (uint32_t i = 1; i < estimators_.size(); ++i) {
|
||||
estimators_[i]->SetRelativeEstimator(estimators_[0]);
|
||||
}
|
||||
}
|
||||
|
||||
BWE_TEST_LOGGING_GLOBAL_ENABLE(false);
|
||||
}
|
||||
|
||||
void BweTest::TearDown() {
|
||||
BWE_TEST_LOGGING_GLOBAL_ENABLE(true);
|
||||
|
||||
for (vector<TestedEstimator*>::iterator eit = estimators_.begin();
|
||||
eit != estimators_.end(); ++eit) {
|
||||
(*eit)->VerifyOrWriteBaseline();
|
||||
(*eit)->LogStats();
|
||||
}
|
||||
|
||||
BWE_TEST_LOGGING_GLOBAL_CONTEXT("");
|
||||
}
|
||||
|
||||
void BweTest::AddPacketProcessor(
|
||||
PacketProcessor* processor) {
|
||||
void BweTest::AddPacketProcessor(PacketProcessor* processor, bool is_sender) {
|
||||
assert(processor);
|
||||
processors_.push_back(processor);
|
||||
processors_.push_back(PacketProcessorRunner(processor));
|
||||
if (is_sender) {
|
||||
senders_.push_back(static_cast<PacketSender*>(processor));
|
||||
}
|
||||
const FlowIds& flow_ids = processor->flow_ids();
|
||||
for (size_t i = 0; i < flow_ids.size(); ++i) {
|
||||
assert(estimators_.count(flow_ids[i]) == 1);
|
||||
}
|
||||
}
|
||||
|
||||
void BweTest::RemovePacketProcessor(
|
||||
PacketProcessor* processor) {
|
||||
vector<PacketProcessor*>::iterator it =
|
||||
std::find(processors_.begin(), processors_.end(), processor);
|
||||
assert(it != processors_.end());
|
||||
processors_.erase(it);
|
||||
for (vector<PacketProcessorRunner>::iterator it = processors_.begin();
|
||||
it != processors_.end(); ++it) {
|
||||
if (it->HasProcessor(processor)) {
|
||||
processors_.erase(it);
|
||||
return;
|
||||
}
|
||||
}
|
||||
assert(false);
|
||||
}
|
||||
|
||||
void BweTest::VerboseLogging(bool enable) {
|
||||
BWE_TEST_LOGGING_GLOBAL_ENABLE(enable);
|
||||
}
|
||||
|
||||
void BweTest::GiveFeedbackToAffectedSenders(int flow_id,
|
||||
TestedEstimator* estimator) {
|
||||
std::list<PacketSender*> affected_senders;
|
||||
for (std::vector<PacketSender*>::iterator psit =
|
||||
senders_.begin(); psit != senders_.end(); ++psit) {
|
||||
const FlowIds& flow_ids = (*psit)->flow_ids();
|
||||
if (std::binary_search(flow_ids.begin(), flow_ids.end(), flow_id)) {
|
||||
affected_senders.push_back(*psit);
|
||||
}
|
||||
}
|
||||
PacketSender::Feedback feedback = {0};
|
||||
if (estimator->CheckEstimate(&feedback) && !affected_senders.empty()) {
|
||||
// Allocate the bitrate evenly between the senders.
|
||||
feedback.estimated_bps /= affected_senders.size();
|
||||
for (std::list<PacketSender*>::iterator psit = affected_senders.begin();
|
||||
psit != affected_senders.end(); ++psit) {
|
||||
(*psit)->GiveFeedback(feedback);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void BweTest::RunFor(int64_t time_ms) {
|
||||
for (run_time_ms_ += time_ms; run_time_ms_ >= simulation_interval_ms_;
|
||||
run_time_ms_ -= simulation_interval_ms_) {
|
||||
// Set simulation interval from first packet sender.
|
||||
// TODO(holmer): Support different feedback intervals for different flows.
|
||||
if (!senders_.empty()) {
|
||||
simulation_interval_ms_ = senders_[0]->GetFeedbackIntervalMs();
|
||||
}
|
||||
assert(simulation_interval_ms_ > 0);
|
||||
if (time_now_ms_ == -1) {
|
||||
time_now_ms_ = simulation_interval_ms_;
|
||||
}
|
||||
for (run_time_ms_ += time_ms;
|
||||
time_now_ms_ <= run_time_ms_ - simulation_interval_ms_;
|
||||
time_now_ms_ += simulation_interval_ms_) {
|
||||
Packets packets;
|
||||
for (vector<PacketProcessor*>::const_iterator it =
|
||||
for (vector<PacketProcessorRunner>::iterator it =
|
||||
processors_.begin(); it != processors_.end(); ++it) {
|
||||
(*it)->RunFor(simulation_interval_ms_, &packets);
|
||||
if (!packets.empty()) {
|
||||
(*it)->Plot((packets.back().send_time_us() + 500) / 1000);
|
||||
}
|
||||
it->RunFor(simulation_interval_ms_, time_now_ms_, &packets);
|
||||
}
|
||||
|
||||
// Verify packets are in order between batches.
|
||||
if (!packets.empty() && !previous_packets_.empty()) {
|
||||
packets.splice(packets.begin(), previous_packets_,
|
||||
--previous_packets_.end());
|
||||
ASSERT_TRUE(IsTimeSorted(packets));
|
||||
packets.erase(packets.begin());
|
||||
if (!packets.empty()) {
|
||||
if (!previous_packets_.empty()) {
|
||||
packets.splice(packets.begin(), previous_packets_,
|
||||
--previous_packets_.end());
|
||||
ASSERT_TRUE(IsTimeSorted(packets));
|
||||
packets.erase(packets.begin());
|
||||
}
|
||||
ASSERT_LE(packets.front().send_time_us(), time_now_ms_ * 1000);
|
||||
ASSERT_LE(packets.back().send_time_us(), time_now_ms_ * 1000);
|
||||
} else {
|
||||
ASSERT_TRUE(IsTimeSorted(packets));
|
||||
}
|
||||
|
||||
for (PacketsConstIt pit = packets.begin(); pit != packets.end(); ++pit) {
|
||||
for (vector<TestedEstimator*>::iterator eit = estimators_.begin();
|
||||
eit != estimators_.end(); ++eit) {
|
||||
(*eit)->EatPacket(*pit);
|
||||
}
|
||||
for (PacketsConstIt it = packets.begin(); it != packets.end(); ++it) {
|
||||
EstimatorMap::iterator est_it = estimators_.find(it->flow_id());
|
||||
ASSERT_TRUE(est_it != estimators_.end());
|
||||
est_it->second->EatPacket(*it);
|
||||
}
|
||||
|
||||
previous_packets_.swap(packets);
|
||||
|
||||
for (vector<TestedEstimator*>::iterator eit = estimators_.begin();
|
||||
eit != estimators_.end(); ++eit) {
|
||||
PacketSender::Feedback feedback = {0};
|
||||
if ((*eit)->CheckEstimate(&feedback)) {
|
||||
for (vector<PacketSender*>::iterator psit = packet_senders_.begin();
|
||||
psit != packet_senders_.end(); ++psit) {
|
||||
(*psit)->GiveFeedback(feedback);
|
||||
}
|
||||
}
|
||||
for (EstimatorMap::iterator est_it = estimators_.begin();
|
||||
est_it != estimators_.end(); ++est_it) {
|
||||
GiveFeedbackToAffectedSenders(est_it->first, est_it->second);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -8,6 +8,7 @@
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include "gtest/gtest.h"
|
||||
@ -26,60 +27,95 @@ struct BweTestConfig {
|
||||
struct EstimatorConfig {
|
||||
EstimatorConfig()
|
||||
: debug_name(),
|
||||
flow_id(0),
|
||||
estimator_factory(NULL),
|
||||
control_type(kMimdControl),
|
||||
update_baseline(false) {
|
||||
update_baseline(false),
|
||||
plot_delay(true),
|
||||
plot_estimate(true) {
|
||||
}
|
||||
EstimatorConfig(std::string debug_name,
|
||||
const RemoteBitrateEstimatorFactory* estimator_factory)
|
||||
int flow_id,
|
||||
const RemoteBitrateEstimatorFactory* estimator_factory,
|
||||
bool plot_delay,
|
||||
bool plot_estimate)
|
||||
: debug_name(debug_name),
|
||||
flow_id(flow_id),
|
||||
estimator_factory(estimator_factory),
|
||||
control_type(kMimdControl),
|
||||
update_baseline(false) {
|
||||
update_baseline(false),
|
||||
plot_delay(plot_delay),
|
||||
plot_estimate(plot_estimate) {
|
||||
}
|
||||
EstimatorConfig(std::string debug_name,
|
||||
int flow_id,
|
||||
const RemoteBitrateEstimatorFactory* estimator_factory,
|
||||
RateControlType control_type,
|
||||
bool plot_delay,
|
||||
bool plot_estimate)
|
||||
: debug_name(debug_name),
|
||||
flow_id(flow_id),
|
||||
estimator_factory(estimator_factory),
|
||||
control_type(control_type),
|
||||
update_baseline(false),
|
||||
plot_delay(plot_delay),
|
||||
plot_estimate(plot_estimate) {
|
||||
}
|
||||
EstimatorConfig(std::string debug_name,
|
||||
int flow_id,
|
||||
const RemoteBitrateEstimatorFactory* estimator_factory,
|
||||
RateControlType control_type,
|
||||
bool update_baseline)
|
||||
: debug_name(debug_name),
|
||||
flow_id(flow_id),
|
||||
estimator_factory(estimator_factory),
|
||||
control_type(control_type),
|
||||
update_baseline(update_baseline) {
|
||||
update_baseline(update_baseline),
|
||||
plot_delay(false),
|
||||
plot_estimate(false) {
|
||||
}
|
||||
std::string debug_name;
|
||||
int flow_id;
|
||||
const RemoteBitrateEstimatorFactory* estimator_factory;
|
||||
RateControlType control_type;
|
||||
bool update_baseline;
|
||||
bool plot_delay;
|
||||
bool plot_estimate;
|
||||
};
|
||||
|
||||
std::vector<const PacketSenderFactory*> sender_factories;
|
||||
std::vector<EstimatorConfig> estimator_configs;
|
||||
};
|
||||
|
||||
class BweTest : public ::testing::TestWithParam<BweTestConfig>,
|
||||
public PacketProcessorListener {
|
||||
class TestedEstimator;
|
||||
class PacketProcessorRunner;
|
||||
|
||||
class BweTest : public PacketProcessorListener {
|
||||
public:
|
||||
BweTest();
|
||||
virtual ~BweTest();
|
||||
|
||||
virtual void SetUp();
|
||||
virtual void TearDown();
|
||||
virtual void AddPacketProcessor(PacketProcessor* processor);
|
||||
virtual void AddPacketProcessor(PacketProcessor* processor, bool is_sender);
|
||||
virtual void RemovePacketProcessor(PacketProcessor* processor);
|
||||
|
||||
protected:
|
||||
void SetupTestFromConfig(const BweTestConfig& config);
|
||||
void VerboseLogging(bool enable);
|
||||
void RunFor(int64_t time_ms);
|
||||
|
||||
private:
|
||||
class TestedEstimator;
|
||||
typedef std::map<int, TestedEstimator*> EstimatorMap;
|
||||
|
||||
void FindPacketsToProcess(const FlowIds& flow_ids, Packets* in,
|
||||
Packets* out);
|
||||
void GiveFeedbackToAffectedSenders(int flow_id, TestedEstimator* estimator);
|
||||
|
||||
int64_t run_time_ms_;
|
||||
int64_t time_now_ms_;
|
||||
int64_t simulation_interval_ms_;
|
||||
EstimatorMap estimators_;
|
||||
Packets previous_packets_;
|
||||
std::vector<PacketSender*> packet_senders_;
|
||||
std::vector<TestedEstimator*> estimators_;
|
||||
std::vector<PacketProcessor*> processors_;
|
||||
std::vector<PacketSender*> senders_;
|
||||
std::vector<PacketProcessorRunner> processors_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(BweTest);
|
||||
};
|
||||
|
||||
@ -45,6 +45,11 @@ class DelayCapHelper {
|
||||
DISALLOW_COPY_AND_ASSIGN(DelayCapHelper);
|
||||
};
|
||||
|
||||
const FlowIds CreateFlowIds(const int *flow_ids_array, size_t num_flow_ids) {
|
||||
FlowIds flow_ids(&flow_ids_array[0], flow_ids_array + num_flow_ids);
|
||||
return flow_ids;
|
||||
}
|
||||
|
||||
class RateCounter {
|
||||
public:
|
||||
RateCounter()
|
||||
@ -115,26 +120,29 @@ int Random::Gaussian(int mean, int standard_deviation) {
|
||||
}
|
||||
|
||||
Packet::Packet()
|
||||
: creation_time_us_(-1),
|
||||
: flow_id_(0),
|
||||
creation_time_us_(-1),
|
||||
send_time_us_(-1),
|
||||
payload_size_(0) {
|
||||
memset(&header_, 0, sizeof(header_));
|
||||
memset(&header_, 0, sizeof(header_));
|
||||
}
|
||||
|
||||
Packet::Packet(int64_t send_time_us, uint32_t payload_size,
|
||||
Packet::Packet(int flow_id, int64_t send_time_us, uint32_t payload_size,
|
||||
const RTPHeader& header)
|
||||
: creation_time_us_(send_time_us),
|
||||
send_time_us_(send_time_us),
|
||||
payload_size_(payload_size),
|
||||
header_(header) {
|
||||
: flow_id_(flow_id),
|
||||
creation_time_us_(send_time_us),
|
||||
send_time_us_(send_time_us),
|
||||
payload_size_(payload_size),
|
||||
header_(header) {
|
||||
}
|
||||
|
||||
Packet::Packet(int64_t send_time_us, uint32_t sequence_number)
|
||||
: creation_time_us_(send_time_us),
|
||||
: flow_id_(0),
|
||||
creation_time_us_(send_time_us),
|
||||
send_time_us_(send_time_us),
|
||||
payload_size_(0) {
|
||||
memset(&header_, 0, sizeof(header_));
|
||||
header_.sequenceNumber = sequence_number;
|
||||
memset(&header_, 0, sizeof(header_));
|
||||
header_.sequenceNumber = sequence_number;
|
||||
}
|
||||
|
||||
bool Packet::operator<(const Packet& rhs) const {
|
||||
@ -157,10 +165,20 @@ bool IsTimeSorted(const Packets& packets) {
|
||||
return true;
|
||||
}
|
||||
|
||||
PacketProcessor::PacketProcessor(PacketProcessorListener* listener)
|
||||
: listener_(listener) {
|
||||
PacketProcessor::PacketProcessor(PacketProcessorListener* listener,
|
||||
bool is_sender)
|
||||
: listener_(listener), flow_ids_(1, 0) {
|
||||
if (listener_) {
|
||||
listener_->AddPacketProcessor(this);
|
||||
listener_->AddPacketProcessor(this, is_sender);
|
||||
}
|
||||
}
|
||||
|
||||
PacketProcessor::PacketProcessor(PacketProcessorListener* listener,
|
||||
const FlowIds& flow_ids,
|
||||
bool is_sender)
|
||||
: listener_(listener), flow_ids_(flow_ids) {
|
||||
if (listener_) {
|
||||
listener_->AddPacketProcessor(this, is_sender);
|
||||
}
|
||||
}
|
||||
|
||||
@ -171,7 +189,7 @@ PacketProcessor::~PacketProcessor() {
|
||||
}
|
||||
|
||||
RateCounterFilter::RateCounterFilter(PacketProcessorListener* listener)
|
||||
: PacketProcessor(listener),
|
||||
: PacketProcessor(listener, false),
|
||||
rate_counter_(new RateCounter()),
|
||||
pps_stats_(),
|
||||
kbps_stats_(),
|
||||
@ -179,12 +197,28 @@ RateCounterFilter::RateCounterFilter(PacketProcessorListener* listener)
|
||||
|
||||
RateCounterFilter::RateCounterFilter(PacketProcessorListener* listener,
|
||||
const std::string& name)
|
||||
: PacketProcessor(listener),
|
||||
: PacketProcessor(listener, false),
|
||||
rate_counter_(new RateCounter()),
|
||||
pps_stats_(),
|
||||
kbps_stats_(),
|
||||
name_(name) {}
|
||||
|
||||
RateCounterFilter::RateCounterFilter(PacketProcessorListener* listener,
|
||||
const FlowIds& flow_ids,
|
||||
const std::string& name)
|
||||
: PacketProcessor(listener, flow_ids, false),
|
||||
rate_counter_(new RateCounter()),
|
||||
pps_stats_(),
|
||||
kbps_stats_(),
|
||||
name_(name) {
|
||||
std::stringstream ss;
|
||||
ss << name_ << "_";
|
||||
for (size_t i = 0; i < flow_ids.size(); ++i) {
|
||||
ss << flow_ids[i] << ",";
|
||||
}
|
||||
name_ = ss.str();
|
||||
}
|
||||
|
||||
RateCounterFilter::~RateCounterFilter() {
|
||||
LogStats();
|
||||
}
|
||||
@ -223,7 +257,7 @@ void RateCounterFilter::RunFor(int64_t /*time_ms*/, Packets* in_out) {
|
||||
}
|
||||
|
||||
LossFilter::LossFilter(PacketProcessorListener* listener)
|
||||
: PacketProcessor(listener),
|
||||
: PacketProcessor(listener, false),
|
||||
random_(0x12345678),
|
||||
loss_fraction_(0.0f) {
|
||||
}
|
||||
@ -248,7 +282,7 @@ void LossFilter::RunFor(int64_t /*time_ms*/, Packets* in_out) {
|
||||
}
|
||||
|
||||
DelayFilter::DelayFilter(PacketProcessorListener* listener)
|
||||
: PacketProcessor(listener),
|
||||
: PacketProcessor(listener, false),
|
||||
delay_us_(0),
|
||||
last_send_time_us_(0) {
|
||||
}
|
||||
@ -270,7 +304,7 @@ void DelayFilter::RunFor(int64_t /*time_ms*/, Packets* in_out) {
|
||||
}
|
||||
|
||||
JitterFilter::JitterFilter(PacketProcessorListener* listener)
|
||||
: PacketProcessor(listener),
|
||||
: PacketProcessor(listener, false),
|
||||
random_(0x89674523),
|
||||
stddev_jitter_us_(0),
|
||||
last_send_time_us_(0) {
|
||||
@ -295,7 +329,7 @@ void JitterFilter::RunFor(int64_t /*time_ms*/, Packets* in_out) {
|
||||
}
|
||||
|
||||
ReorderFilter::ReorderFilter(PacketProcessorListener* listener)
|
||||
: PacketProcessor(listener),
|
||||
: PacketProcessor(listener, false),
|
||||
random_(0x27452389),
|
||||
reorder_fraction_(0.0f) {
|
||||
}
|
||||
@ -327,7 +361,15 @@ void ReorderFilter::RunFor(int64_t /*time_ms*/, Packets* in_out) {
|
||||
}
|
||||
|
||||
ChokeFilter::ChokeFilter(PacketProcessorListener* listener)
|
||||
: PacketProcessor(listener),
|
||||
: PacketProcessor(listener, false),
|
||||
kbps_(1200),
|
||||
last_send_time_us_(0),
|
||||
delay_cap_helper_(new DelayCapHelper()) {
|
||||
}
|
||||
|
||||
ChokeFilter::ChokeFilter(PacketProcessorListener* listener,
|
||||
const FlowIds& flow_ids)
|
||||
: PacketProcessor(listener, flow_ids, false),
|
||||
kbps_(1200),
|
||||
last_send_time_us_(0),
|
||||
delay_cap_helper_(new DelayCapHelper()) {
|
||||
@ -369,7 +411,7 @@ Stats<double> ChokeFilter::GetDelayStats() const {
|
||||
|
||||
TraceBasedDeliveryFilter::TraceBasedDeliveryFilter(
|
||||
PacketProcessorListener* listener)
|
||||
: PacketProcessor(listener),
|
||||
: PacketProcessor(listener, false),
|
||||
current_offset_us_(0),
|
||||
delivery_times_us_(),
|
||||
next_delivery_it_(),
|
||||
@ -381,7 +423,7 @@ TraceBasedDeliveryFilter::TraceBasedDeliveryFilter(
|
||||
TraceBasedDeliveryFilter::TraceBasedDeliveryFilter(
|
||||
PacketProcessorListener* listener,
|
||||
const std::string& name)
|
||||
: PacketProcessor(listener),
|
||||
: PacketProcessor(listener, false),
|
||||
current_offset_us_(0),
|
||||
delivery_times_us_(),
|
||||
next_delivery_it_(),
|
||||
@ -478,12 +520,18 @@ void TraceBasedDeliveryFilter::ProceedToNextSlot() {
|
||||
}
|
||||
|
||||
PacketSender::PacketSender(PacketProcessorListener* listener)
|
||||
: PacketProcessor(listener) {
|
||||
: PacketProcessor(listener, true) {}
|
||||
|
||||
PacketSender::PacketSender(PacketProcessorListener* listener,
|
||||
const FlowIds& flow_ids)
|
||||
: PacketProcessor(listener, flow_ids, true) {
|
||||
|
||||
}
|
||||
|
||||
VideoSender::VideoSender(PacketProcessorListener* listener, float fps,
|
||||
uint32_t kbps, uint32_t ssrc, float first_frame_offset)
|
||||
: PacketSender(listener),
|
||||
VideoSender::VideoSender(int flow_id, PacketProcessorListener* listener,
|
||||
float fps, uint32_t kbps, uint32_t ssrc,
|
||||
float first_frame_offset)
|
||||
: PacketSender(listener, FlowIds(1, flow_id)),
|
||||
kMaxPayloadSizeBytes(1200),
|
||||
kTimestampBase(0xff80ff00ul),
|
||||
frame_period_ms_(1000.0 / fps),
|
||||
@ -506,7 +554,7 @@ uint32_t VideoSender::GetCapacityKbps() const {
|
||||
void VideoSender::RunFor(int64_t time_ms, Packets* in_out) {
|
||||
assert(in_out);
|
||||
now_ms_ += time_ms;
|
||||
Packets newPackets;
|
||||
Packets new_packets;
|
||||
while (now_ms_ >= next_frame_ms_) {
|
||||
prototype_header_.sequenceNumber++;
|
||||
prototype_header_.timestamp = kTimestampBase +
|
||||
@ -524,21 +572,23 @@ void VideoSender::RunFor(int64_t time_ms, Packets* in_out) {
|
||||
uint32_t payload_size = frame_size_bytes_;
|
||||
while (payload_size > 0) {
|
||||
uint32_t size = std::min(kMaxPayloadSizeBytes, payload_size);
|
||||
newPackets.push_back(Packet(send_time_us, size, prototype_header_));
|
||||
new_packets.push_back(Packet(flow_ids()[0], send_time_us, size,
|
||||
prototype_header_));
|
||||
payload_size -= size;
|
||||
}
|
||||
|
||||
next_frame_ms_ += frame_period_ms_;
|
||||
}
|
||||
in_out->merge(newPackets);
|
||||
in_out->merge(new_packets);
|
||||
}
|
||||
|
||||
AdaptiveVideoSender::AdaptiveVideoSender(PacketProcessorListener* listener,
|
||||
AdaptiveVideoSender::AdaptiveVideoSender(int flow_id,
|
||||
PacketProcessorListener* listener,
|
||||
float fps,
|
||||
uint32_t kbps,
|
||||
uint32_t ssrc,
|
||||
float first_frame_offset)
|
||||
: VideoSender(listener, fps, kbps, ssrc, first_frame_offset) {}
|
||||
: VideoSender(flow_id, listener, fps, kbps, ssrc, first_frame_offset) {}
|
||||
|
||||
void AdaptiveVideoSender::GiveFeedback(const PacketSender::Feedback& feedback) {
|
||||
bytes_per_second_ = feedback.estimated_bps / 8;
|
||||
|
||||
@ -31,6 +31,10 @@ namespace bwe {
|
||||
class DelayCapHelper;
|
||||
class RateCounter;
|
||||
|
||||
|
||||
typedef std::vector<int> FlowIds;
|
||||
const FlowIds CreateFlowIds(const int *flow_ids_array, size_t num_flow_ids);
|
||||
|
||||
template<typename T> class Stats {
|
||||
public:
|
||||
Stats()
|
||||
@ -139,12 +143,13 @@ class Random {
|
||||
class Packet {
|
||||
public:
|
||||
Packet();
|
||||
Packet(int64_t send_time_us, uint32_t payload_size,
|
||||
Packet(int flow_id, int64_t send_time_us, uint32_t payload_size,
|
||||
const RTPHeader& header);
|
||||
Packet(int64_t send_time_us, uint32_t sequence_number);
|
||||
|
||||
bool operator<(const Packet& rhs) const;
|
||||
|
||||
int flow_id() const { return flow_id_; }
|
||||
int64_t creation_time_us() const { return creation_time_us_; }
|
||||
void set_send_time_us(int64_t send_time_us);
|
||||
int64_t send_time_us() const { return send_time_us_; }
|
||||
@ -152,6 +157,7 @@ class Packet {
|
||||
const RTPHeader& header() const { return header_; }
|
||||
|
||||
private:
|
||||
int flow_id_;
|
||||
int64_t creation_time_us_; // Time when the packet was created.
|
||||
int64_t send_time_us_; // Time the packet left last processor touching it.
|
||||
uint32_t payload_size_; // Size of the (non-existent, simulated) payload.
|
||||
@ -170,13 +176,16 @@ class PacketProcessorListener {
|
||||
public:
|
||||
virtual ~PacketProcessorListener() {}
|
||||
|
||||
virtual void AddPacketProcessor(PacketProcessor* processor) = 0;
|
||||
virtual void AddPacketProcessor(PacketProcessor* processor,
|
||||
bool is_sender) = 0;
|
||||
virtual void RemovePacketProcessor(PacketProcessor* processor) = 0;
|
||||
};
|
||||
|
||||
class PacketProcessor {
|
||||
public:
|
||||
explicit PacketProcessor(PacketProcessorListener* listener);
|
||||
PacketProcessor(PacketProcessorListener* listener, bool is_sender);
|
||||
PacketProcessor(PacketProcessorListener* listener, const FlowIds& flow_ids,
|
||||
bool is_sender);
|
||||
virtual ~PacketProcessor();
|
||||
|
||||
// Called after each simulation batch to allow the processor to plot any
|
||||
@ -188,8 +197,11 @@ class PacketProcessor {
|
||||
// |send_time_us_|. The simulation time |time_ms| is optional to use.
|
||||
virtual void RunFor(int64_t time_ms, Packets* in_out) = 0;
|
||||
|
||||
const FlowIds& flow_ids() const { return flow_ids_; }
|
||||
|
||||
private:
|
||||
PacketProcessorListener* listener_;
|
||||
FlowIds flow_ids_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(PacketProcessor);
|
||||
};
|
||||
@ -199,6 +211,9 @@ class RateCounterFilter : public PacketProcessor {
|
||||
explicit RateCounterFilter(PacketProcessorListener* listener);
|
||||
RateCounterFilter(PacketProcessorListener* listener,
|
||||
const std::string& name);
|
||||
RateCounterFilter(PacketProcessorListener* listener,
|
||||
const FlowIds& flow_ids,
|
||||
const std::string& name);
|
||||
virtual ~RateCounterFilter();
|
||||
|
||||
uint32_t packets_per_second() const;
|
||||
@ -283,6 +298,7 @@ class ReorderFilter : public PacketProcessor {
|
||||
class ChokeFilter : public PacketProcessor {
|
||||
public:
|
||||
explicit ChokeFilter(PacketProcessorListener* listener);
|
||||
ChokeFilter(PacketProcessorListener* listener, const FlowIds& flow_ids);
|
||||
virtual ~ChokeFilter();
|
||||
|
||||
void SetCapacity(uint32_t kbps);
|
||||
@ -338,6 +354,7 @@ class PacketSender : public PacketProcessor {
|
||||
};
|
||||
|
||||
explicit PacketSender(PacketProcessorListener* listener);
|
||||
PacketSender(PacketProcessorListener* listener, const FlowIds& flow_ids);
|
||||
virtual ~PacketSender() {}
|
||||
|
||||
virtual uint32_t GetCapacityKbps() const { return 0; }
|
||||
@ -347,23 +364,17 @@ class PacketSender : public PacketProcessor {
|
||||
// Note that changing the feedback interval affects the timing of when the
|
||||
// output of the estimators is sampled and therefore the baseline files may
|
||||
// have to be regenerated.
|
||||
virtual int64_t GetFeedbackIntervalMs() const { return 1000; }
|
||||
virtual int GetFeedbackIntervalMs() const { return 1000; }
|
||||
virtual void GiveFeedback(const Feedback& feedback) {}
|
||||
|
||||
private:
|
||||
DISALLOW_COPY_AND_ASSIGN(PacketSender);
|
||||
};
|
||||
|
||||
struct PacketSenderFactory {
|
||||
PacketSenderFactory() {}
|
||||
virtual ~PacketSenderFactory() {}
|
||||
virtual PacketSender* Create() const = 0;
|
||||
};
|
||||
|
||||
class VideoSender : public PacketSender {
|
||||
public:
|
||||
VideoSender(PacketProcessorListener* listener, float fps, uint32_t kbps,
|
||||
uint32_t ssrc, float first_frame_offset);
|
||||
VideoSender(int flow_id, PacketProcessorListener* listener, float fps,
|
||||
uint32_t kbps, uint32_t ssrc, float first_frame_offset);
|
||||
virtual ~VideoSender() {}
|
||||
|
||||
uint32_t max_payload_size_bytes() const { return kMaxPayloadSizeBytes; }
|
||||
@ -390,48 +401,17 @@ class VideoSender : public PacketSender {
|
||||
|
||||
class AdaptiveVideoSender : public VideoSender {
|
||||
public:
|
||||
AdaptiveVideoSender(PacketProcessorListener* listener, float fps,
|
||||
uint32_t kbps, uint32_t ssrc, float first_frame_offset);
|
||||
AdaptiveVideoSender(int flow_id, PacketProcessorListener* listener,
|
||||
float fps, uint32_t kbps, uint32_t ssrc,
|
||||
float first_frame_offset);
|
||||
virtual ~AdaptiveVideoSender() {}
|
||||
|
||||
virtual int64_t GetFeedbackIntervalMs() const { return 100; }
|
||||
virtual int GetFeedbackIntervalMs() const { return 100; }
|
||||
virtual void GiveFeedback(const Feedback& feedback);
|
||||
|
||||
private:
|
||||
DISALLOW_IMPLICIT_CONSTRUCTORS(AdaptiveVideoSender);
|
||||
};
|
||||
|
||||
class VideoPacketSenderFactory : public PacketSenderFactory {
|
||||
public:
|
||||
VideoPacketSenderFactory(float fps, uint32_t kbps, uint32_t ssrc,
|
||||
float frame_offset)
|
||||
: fps_(fps),
|
||||
kbps_(kbps),
|
||||
ssrc_(ssrc),
|
||||
frame_offset_(frame_offset) {
|
||||
}
|
||||
virtual ~VideoPacketSenderFactory() {}
|
||||
virtual PacketSender* Create() const {
|
||||
return new VideoSender(NULL, fps_, kbps_, ssrc_, frame_offset_);
|
||||
}
|
||||
protected:
|
||||
float fps_;
|
||||
uint32_t kbps_;
|
||||
uint32_t ssrc_;
|
||||
float frame_offset_;
|
||||
};
|
||||
|
||||
class AdaptiveVideoPacketSenderFactory : public VideoPacketSenderFactory {
|
||||
public:
|
||||
AdaptiveVideoPacketSenderFactory(float fps, uint32_t kbps, uint32_t ssrc,
|
||||
float frame_offset)
|
||||
: VideoPacketSenderFactory(fps, kbps, ssrc, frame_offset) {}
|
||||
virtual ~AdaptiveVideoPacketSenderFactory() {}
|
||||
virtual PacketSender* Create() const {
|
||||
return new AdaptiveVideoSender(NULL, fps_, kbps_, ssrc_, frame_offset_);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace bwe
|
||||
} // namespace testing
|
||||
} // namespace webrtc
|
||||
|
||||
@ -184,7 +184,7 @@ class BweTestFramework_RateCounterFilterTest : public ::testing::Test {
|
||||
RTPHeader header;
|
||||
// "Send" a packet every 10 ms.
|
||||
for (int64_t i = 0; i < run_for_ms; i += 10, now_ms_ += 10) {
|
||||
packets.push_back(Packet(now_ms_ * 1000, payload_bits / 8, header));
|
||||
packets.push_back(Packet(0, now_ms_ * 1000, payload_bits / 8, header));
|
||||
}
|
||||
filter_.RunFor(run_for_ms, &packets);
|
||||
ASSERT_TRUE(IsTimeSorted(packets));
|
||||
@ -587,7 +587,7 @@ class BweTestFramework_ChokeFilterTest : public ::testing::Test {
|
||||
int64_t send_time_ms = now_ms_ + (i * run_for_ms) / packets_to_generate;
|
||||
header.sequenceNumber = sequence_number_++;
|
||||
// Payload is 1000 bits.
|
||||
packets.push_back(Packet(send_time_ms * 1000, 125, header));
|
||||
packets.push_back(Packet(0, send_time_ms * 1000, 125, header));
|
||||
send_times_us_.push_back(send_time_ms * 1000);
|
||||
}
|
||||
ASSERT_TRUE(IsTimeSorted(packets));
|
||||
@ -768,7 +768,7 @@ void TestVideoSender(VideoSender* sender, int64_t run_for_ms,
|
||||
|
||||
TEST(BweTestFramework_VideoSenderTest, Fps1Kbps80_1s) {
|
||||
// 1 fps, 80 kbps
|
||||
VideoSender sender(NULL, 1.0f, 80, 0x1234, 0);
|
||||
VideoSender sender(0, NULL, 1.0f, 80, 0x1234, 0);
|
||||
EXPECT_EQ(10000u, sender.bytes_per_second());
|
||||
// We're at 1 fps, so all packets should be generated on first call, giving 10
|
||||
// packets of each 1000 bytes, total 10000 bytes.
|
||||
@ -785,7 +785,7 @@ TEST(BweTestFramework_VideoSenderTest, Fps1Kbps80_1s) {
|
||||
|
||||
TEST(BweTestFramework_VideoSenderTest, Fps1Kbps80_1s_Offset) {
|
||||
// 1 fps, 80 kbps, offset 0.5 of a frame period, ==0.5s in this case.
|
||||
VideoSender sender(NULL, 1.0f, 80, 0x1234, 0.5f);
|
||||
VideoSender sender(0, NULL, 1.0f, 80, 0x1234, 0.5f);
|
||||
EXPECT_EQ(10000u, sender.bytes_per_second());
|
||||
// 499ms, no output.
|
||||
TestVideoSender(&sender, 499, 0, 0, 0);
|
||||
@ -805,7 +805,7 @@ TEST(BweTestFramework_VideoSenderTest, Fps1Kbps80_1s_Offset) {
|
||||
|
||||
TEST(BweTestFramework_VideoSenderTest, Fps50Kpbs80_11s) {
|
||||
// 50 fps, 80 kbps.
|
||||
VideoSender sender(NULL, 50.0f, 80, 0x1234, 0);
|
||||
VideoSender sender(0, NULL, 50.0f, 80, 0x1234, 0);
|
||||
EXPECT_EQ(10000u, sender.bytes_per_second());
|
||||
// 9998ms, should see 500 frames, 200 byte payloads, total 100000 bytes.
|
||||
TestVideoSender(&sender, 9998, 500, 200, 100000);
|
||||
@ -821,7 +821,7 @@ TEST(BweTestFramework_VideoSenderTest, Fps50Kpbs80_11s) {
|
||||
|
||||
TEST(BweTestFramework_VideoSenderTest, Fps10Kpbs120_1s) {
|
||||
// 20 fps, 120 kbps.
|
||||
VideoSender sender(NULL, 20.0f, 120, 0x1234, 0);
|
||||
VideoSender sender(0, NULL, 20.0f, 120, 0x1234, 0);
|
||||
EXPECT_EQ(15000u, sender.bytes_per_second());
|
||||
// 498ms, 10 frames with 750 byte payloads, total 7500 bytes.
|
||||
TestVideoSender(&sender, 498, 10, 750, 7500);
|
||||
@ -837,7 +837,7 @@ TEST(BweTestFramework_VideoSenderTest, Fps10Kpbs120_1s) {
|
||||
|
||||
TEST(BweTestFramework_VideoSenderTest, Fps30Kbps800_20s) {
|
||||
// 20 fps, 820 kbps.
|
||||
VideoSender sender(NULL, 25.0f, 820, 0x1234, 0);
|
||||
VideoSender sender(0, NULL, 25.0f, 820, 0x1234, 0);
|
||||
EXPECT_EQ(102500u, sender.bytes_per_second());
|
||||
// 9998ms, 250 frames. 820 kbps = 102500 bytes/s, so total should be 1025000.
|
||||
// Each frame is 102500/25=4100 bytes, or 5 packets (4 @1000 bytes, 1 @100),
|
||||
@ -858,7 +858,7 @@ TEST(BweTestFramework_VideoSenderTest, Fps30Kbps800_20s) {
|
||||
|
||||
TEST(BweTestFramework_VideoSenderTest, TestAppendInOrder) {
|
||||
// 1 fps, 80 kbps, 250ms offset.
|
||||
VideoSender sender1(NULL, 1.0f, 80, 0x1234, 0.25f);
|
||||
VideoSender sender1(0, NULL, 1.0f, 80, 0x1234, 0.25f);
|
||||
EXPECT_EQ(10000u, sender1.bytes_per_second());
|
||||
Packets packets;
|
||||
// Generate some packets, verify they are sorted.
|
||||
@ -873,7 +873,7 @@ TEST(BweTestFramework_VideoSenderTest, TestAppendInOrder) {
|
||||
EXPECT_EQ(18u, packets.size());
|
||||
|
||||
// Another sender, 2 fps, 160 kpbs, 150ms offset
|
||||
VideoSender sender2(NULL, 2.0f, 160, 0x2234, 0.30f);
|
||||
VideoSender sender2(0, NULL, 2.0f, 160, 0x2234, 0.30f);
|
||||
EXPECT_EQ(20000u, sender2.bytes_per_second());
|
||||
// Generate some packets, verify that they are merged with the packets already
|
||||
// on the list.
|
||||
@ -887,7 +887,7 @@ TEST(BweTestFramework_VideoSenderTest, TestAppendInOrder) {
|
||||
}
|
||||
|
||||
TEST(BweTestFramework_VideoSenderTest, FeedbackIneffective) {
|
||||
VideoSender sender(NULL, 25.0f, 820, 0x1234, 0);
|
||||
VideoSender sender(0, NULL, 25.0f, 820, 0x1234, 0);
|
||||
EXPECT_EQ(102500u, sender.bytes_per_second());
|
||||
TestVideoSender(&sender, 9998, 1000, 500, 1025000);
|
||||
|
||||
@ -899,7 +899,7 @@ TEST(BweTestFramework_VideoSenderTest, FeedbackIneffective) {
|
||||
}
|
||||
|
||||
TEST(BweTestFramework_AdaptiveVideoSenderTest, FeedbackChangesBitrate) {
|
||||
AdaptiveVideoSender sender(NULL, 25.0f, 820, 0x1234, 0);
|
||||
AdaptiveVideoSender sender(0, NULL, 25.0f, 820, 0x1234, 0);
|
||||
EXPECT_EQ(102500u, sender.bytes_per_second());
|
||||
TestVideoSender(&sender, 9998, 1000, 500, 1025000);
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user