/* * Copyright (c) 2013 The WebRTC project authors. All Rights Reserved. * * Use of this source code is governed by a BSD-style license * that can be found in the LICENSE file in the root of the source * tree. An additional intellectual property rights grant can be found * in the file PATENTS. All contributing project authors may * be found in the AUTHORS file in the root of the source tree. */ #include "modules/remote_bitrate_estimator/test/bwe_test.h" #include #include #include "modules/include/module_common_types.h" #include "modules/remote_bitrate_estimator/test/bwe_test_framework.h" #include "modules/remote_bitrate_estimator/test/metric_recorder.h" #include "modules/remote_bitrate_estimator/test/packet_receiver.h" #include "modules/remote_bitrate_estimator/test/packet_sender.h" #include "rtc_base/arraysize.h" #include "system_wrappers/include/clock.h" #include "system_wrappers/include/field_trial.h" #include "test/testsupport/perf_test.h" using std::vector; namespace { const int kQuickTestTimeoutMs = 500; double SafeDiv(double a, double b) { return b ? a / b : 0; } } namespace webrtc { namespace testing { namespace bwe { PacketProcessorRunner::PacketProcessorRunner(PacketProcessor* processor) : processor_(processor) {} PacketProcessorRunner::PacketProcessorRunner( const PacketProcessorRunner& runner) = default; PacketProcessorRunner::~PacketProcessorRunner() { for (Packet* packet : queue_) delete packet; } bool PacketProcessorRunner::RunsProcessor( const PacketProcessor* processor) const { return processor == processor_; } void PacketProcessorRunner::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_ms()); } in_out->merge(to_process, DereferencingComparator); } void PacketProcessorRunner::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 (flow_ids.find((*it)->flow_id()) != flow_ids.end()) { Packets::iterator next = it; ++next; out->splice(out->end(), *in, it); it = next; } else { ++it; } } } void PacketProcessorRunner::QueuePackets(Packets* batch, int64_t end_of_batch_time_us) { queue_.merge(*batch, DereferencingComparator); if (queue_.empty()) { return; } Packets::iterator it = queue_.begin(); for (; it != queue_.end(); ++it) { if ((*it)->send_time_us() > end_of_batch_time_us) { break; } } Packets to_transfer; to_transfer.splice(to_transfer.begin(), queue_, queue_.begin(), it); batch->merge(to_transfer, DereferencingComparator); } // Plot link capacity by default. BweTest::BweTest() : BweTest(true) {} BweTest::BweTest(bool plot_capacity) : run_time_ms_(0), time_now_ms_(-1), simulation_interval_ms_(-1), plot_total_available_capacity_(plot_capacity) { links_.push_back(&uplink_); links_.push_back(&downlink_); } BweTest::~BweTest() { for (Packet* packet : packets_) delete packet; } void BweTest::SetUp() { const ::testing::TestInfo* const test_info = ::testing::UnitTest::GetInstance()->current_test_info(); std::string test_name = std::string(test_info->test_case_name()) + "_" + std::string(test_info->name()); BWE_TEST_LOGGING_GLOBAL_CONTEXT(test_name); BWE_TEST_LOGGING_GLOBAL_ENABLE(false); } Link::Link() = default; Link::~Link() = default; void Link::AddPacketProcessor(PacketProcessor* processor, ProcessorType processor_type) { assert(processor); switch (processor_type) { case kSender: senders_.push_back(static_cast(processor)); break; case kReceiver: receivers_.push_back(static_cast(processor)); break; case kRegular: break; } processors_.push_back(PacketProcessorRunner(processor)); } void Link::RemovePacketProcessor(PacketProcessor* processor) { for (std::vector::iterator it = processors_.begin(); it != processors_.end(); ++it) { if (it->RunsProcessor(processor)) { processors_.erase(it); return; } } assert(false); } // Ownership of the created packets is handed over to the caller. void Link::Run(int64_t run_for_ms, int64_t now_ms, Packets* packets) { for (auto& processor : processors_) { processor.RunFor(run_for_ms, now_ms, packets); } } void BweTest::VerboseLogging(bool enable) { BWE_TEST_LOGGING_GLOBAL_ENABLE(enable); } void BweTest::RunFor(int64_t time_ms) { // Set simulation interval from first packet sender. // TODO(holmer): Support different feedback intervals for different flows. // For quick perf tests ignore passed timeout if (field_trial::IsEnabled("WebRTC-QuickPerfTest")) { time_ms = kQuickTestTimeoutMs; } if (!uplink_.senders().empty()) { simulation_interval_ms_ = uplink_.senders()[0]->GetFeedbackIntervalMs(); } else if (!downlink_.senders().empty()) { simulation_interval_ms_ = downlink_.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 are first generated on the first link, passed through all the // PacketProcessors and PacketReceivers. The PacketReceivers produces // FeedbackPackets which are then processed by the next link, where they // at some point will be consumed by a PacketSender. for (Link* link : links_) link->Run(simulation_interval_ms_, time_now_ms_, &packets_); } } std::string BweTest::GetTestName() const { const ::testing::TestInfo* const test_info = ::testing::UnitTest::GetInstance()->current_test_info(); return std::string(test_info->name()); } void BweTest::PrintResults(double max_throughput_kbps, Stats throughput_kbps, int flow_id, Stats flow_delay_ms, Stats flow_throughput_kbps) { std::map> flow_delays_ms; flow_delays_ms[flow_id] = flow_delay_ms; std::map> flow_throughputs_kbps; flow_throughputs_kbps[flow_id] = flow_throughput_kbps; PrintResults(max_throughput_kbps, throughput_kbps, flow_delays_ms, flow_throughputs_kbps); } void BweTest::PrintResults(double max_throughput_kbps, Stats throughput_kbps, std::map> flow_delay_ms, std::map> flow_throughput_kbps) { double utilization = SafeDiv(throughput_kbps.GetMean(), max_throughput_kbps); webrtc::test::PrintResult("BwePerformance", GetTestName(), "Utilization", utilization * 100.0, "%", false); webrtc::test::PrintResult( "BwePerformance", GetTestName(), "Utilization var coeff", SafeDiv(throughput_kbps.GetStdDev(), throughput_kbps.GetMean()), "", false); std::stringstream ss; for (auto& kv : flow_throughput_kbps) { ss.str(""); ss << "Throughput flow " << kv.first; webrtc::test::PrintResultMeanAndError("BwePerformance", GetTestName(), ss.str(), kv.second.GetMean(), kv.second.GetStdDev(), "kbps", false); } for (auto& kv : flow_delay_ms) { ss.str(""); ss << "Delay flow " << kv.first; webrtc::test::PrintResultMeanAndError("BwePerformance", GetTestName(), ss.str(), kv.second.GetMean(), kv.second.GetStdDev(), "ms", false); } double fairness_index = 1.0; if (!flow_throughput_kbps.empty()) { double squared_bitrate_sum = 0.0; fairness_index = 0.0; for (auto kv : flow_throughput_kbps) { squared_bitrate_sum += kv.second.GetMean() * kv.second.GetMean(); fairness_index += kv.second.GetMean(); } fairness_index *= SafeDiv( fairness_index, flow_throughput_kbps.size() * squared_bitrate_sum); } webrtc::test::PrintResult("BwePerformance", GetTestName(), "Fairness", fairness_index * 100, "%", false); } void BweTest::RunFairnessTest(BandwidthEstimatorType bwe_type, size_t num_media_flows, size_t num_tcp_flows, int64_t run_time_seconds, uint32_t capacity_kbps, int64_t max_delay_ms, int64_t rtt_ms, int64_t max_jitter_ms, const int64_t* offsets_ms) { RunFairnessTest(bwe_type, num_media_flows, num_tcp_flows, run_time_seconds, capacity_kbps, max_delay_ms, rtt_ms, max_jitter_ms, offsets_ms, "Fairness_test", bwe_names[bwe_type]); } void BweTest::RunFairnessTest(BandwidthEstimatorType bwe_type, size_t num_media_flows, size_t num_tcp_flows, int64_t run_time_seconds, uint32_t capacity_kbps, int64_t max_delay_ms, int64_t rtt_ms, int64_t max_jitter_ms, const int64_t* offsets_ms, const std::string& title, const std::string& flow_name) { std::set all_flow_ids; std::set media_flow_ids; std::set tcp_flow_ids; int next_flow_id = 0; for (size_t i = 0; i < num_media_flows; ++i) { media_flow_ids.insert(next_flow_id); all_flow_ids.insert(next_flow_id); ++next_flow_id; } for (size_t i = 0; i < num_tcp_flows; ++i) { tcp_flow_ids.insert(next_flow_id); all_flow_ids.insert(next_flow_id); ++next_flow_id; } std::vector sources; std::vector senders; std::vector metric_recorders; int64_t max_offset_ms = 0; for (int media_flow : media_flow_ids) { sources.push_back(new AdaptiveVideoSource(media_flow, 30, 300, 0, offsets_ms[media_flow])); senders.push_back(new PacedVideoSender(&uplink_, sources.back(), bwe_type)); max_offset_ms = std::max(max_offset_ms, offsets_ms[media_flow]); } for (int tcp_flow : tcp_flow_ids) { senders.push_back(new TcpSender(&uplink_, tcp_flow, offsets_ms[tcp_flow])); max_offset_ms = std::max(max_offset_ms, offsets_ms[tcp_flow]); } ChokeFilter choke(&uplink_, all_flow_ids); choke.set_capacity_kbps(capacity_kbps); choke.set_max_delay_ms(max_delay_ms); LinkShare link_share(&choke); int64_t one_way_delay_ms = rtt_ms / 2; DelayFilter delay_uplink(&uplink_, all_flow_ids); delay_uplink.SetOneWayDelayMs(one_way_delay_ms); JitterFilter jitter(&uplink_, all_flow_ids); jitter.SetMaxJitter(max_jitter_ms); std::vector rate_counters; for (int flow : media_flow_ids) { rate_counters.push_back( new RateCounterFilter(&uplink_, flow, "Receiver", bwe_names[bwe_type])); } for (int flow : tcp_flow_ids) { rate_counters.push_back(new RateCounterFilter(&uplink_, flow, "Receiver", bwe_names[kTcpEstimator])); } RateCounterFilter total_utilization( &uplink_, all_flow_ids, "total_utilization", "Total_link_utilization"); std::vector receivers; // Delays is being plotted only for the first flow. // To plot all of them, replace "i == 0" with "true" on new PacketReceiver(). for (int media_flow : media_flow_ids) { metric_recorders.push_back( new MetricRecorder(bwe_names[bwe_type], static_cast(media_flow), senders[media_flow], &link_share)); receivers.push_back(new PacketReceiver(&uplink_, media_flow, bwe_type, media_flow == 0, false, metric_recorders[media_flow])); metric_recorders[media_flow]->set_plot_available_capacity( media_flow == 0 && plot_total_available_capacity_); metric_recorders[media_flow]->set_start_computing_metrics_ms(max_offset_ms); } // Delays is not being plotted only for TCP flows. To plot all of them, // replace first "false" occurence with "true" on new PacketReceiver(). for (int tcp_flow : tcp_flow_ids) { metric_recorders.push_back( new MetricRecorder(bwe_names[kTcpEstimator], static_cast(tcp_flow), senders[tcp_flow], &link_share)); receivers.push_back(new PacketReceiver(&uplink_, tcp_flow, kTcpEstimator, false, false, metric_recorders[tcp_flow])); metric_recorders[tcp_flow]->set_plot_available_capacity( tcp_flow == 0 && plot_total_available_capacity_); } DelayFilter delay_downlink(&downlink_, all_flow_ids); delay_downlink.SetOneWayDelayMs(one_way_delay_ms); RunFor(run_time_seconds * 1000); std::map> flow_throughput_kbps; for (RateCounterFilter* rate_counter : rate_counters) { int flow_id = *rate_counter->flow_ids().begin(); flow_throughput_kbps[flow_id] = rate_counter->GetBitrateStats(); } std::map> flow_delay_ms; for (PacketReceiver* receiver : receivers) { int flow_id = *receiver->flow_ids().begin(); flow_delay_ms[flow_id] = receiver->GetDelayStats(); } PrintResults(capacity_kbps, total_utilization.GetBitrateStats(), flow_delay_ms, flow_throughput_kbps); if (!field_trial::IsEnabled("WebRTC-QuickPerfTest")) { for (int i : all_flow_ids) { metric_recorders[i]->PlotThroughputHistogram( title, flow_name, static_cast(num_media_flows), 0); metric_recorders[i]->PlotLossHistogram(title, flow_name, static_cast(num_media_flows), receivers[i]->GlobalPacketLoss()); } // Pointless to show delay histogram for TCP flow. for (int i : media_flow_ids) { metric_recorders[i]->PlotDelayHistogram(title, bwe_names[bwe_type], static_cast(num_media_flows), one_way_delay_ms); BWE_TEST_LOGGING_BASELINEBAR(5, bwe_names[bwe_type], one_way_delay_ms, i); } } for (VideoSource* source : sources) delete source; for (PacketSender* sender : senders) delete sender; for (RateCounterFilter* rate_counter : rate_counters) delete rate_counter; for (PacketReceiver* receiver : receivers) delete receiver; for (MetricRecorder* recorder : metric_recorders) delete recorder; } void BweTest::RunChoke(BandwidthEstimatorType bwe_type, std::vector capacities_kbps) { int flow_id = bwe_type; AdaptiveVideoSource source(flow_id, 30, 300, 0, 0); VideoSender sender(&uplink_, &source, bwe_type); ChokeFilter choke(&uplink_, flow_id); LinkShare link_share(&choke); MetricRecorder metric_recorder(bwe_names[bwe_type], flow_id, &sender, &link_share); PacketReceiver receiver(&uplink_, flow_id, bwe_type, true, false, &metric_recorder); metric_recorder.set_plot_available_capacity(plot_total_available_capacity_); choke.set_max_delay_ms(500); const int64_t kRunTimeMs = 60 * 1000; std::stringstream title("Choke"); char delimiter = '_'; for (auto it = capacities_kbps.begin(); it != capacities_kbps.end(); ++it) { choke.set_capacity_kbps(*it); RunFor(kRunTimeMs); title << delimiter << (*it); delimiter = '-'; } title << "_kbps,_" << (kRunTimeMs / 1000) << "s_each"; metric_recorder.PlotThroughputHistogram(title.str(), bwe_names[bwe_type], 1, 0); metric_recorder.PlotDelayHistogram(title.str(), bwe_names[bwe_type], 1, 0); // receiver.PlotLossHistogram(title, bwe_names[bwe_type], 1); // receiver.PlotObjectiveHistogram(title, bwe_names[bwe_type], 1); } // 5.1. Single Video and Audio media traffic, forward direction. void BweTest::RunVariableCapacity1SingleFlow(BandwidthEstimatorType bwe_type) { const int kFlowId = 0; // Arbitrary value. AdaptiveVideoSource source(kFlowId, 30, 300, 0, 0); PacedVideoSender sender(&uplink_, &source, bwe_type); DefaultEvaluationFilter up_filter(&uplink_, kFlowId); LinkShare link_share(&(up_filter.choke)); MetricRecorder metric_recorder(bwe_names[bwe_type], kFlowId, &sender, &link_share); PacketReceiver receiver(&uplink_, kFlowId, bwe_type, true, true, &metric_recorder); metric_recorder.set_plot_available_capacity(plot_total_available_capacity_); DelayFilter down_filter(&downlink_, kFlowId); down_filter.SetOneWayDelayMs(kOneWayDelayMs); // Test also with one way propagation delay = 100ms. // up_filter.delay.SetOneWayDelayMs(100); // down_filter.SetOneWayDelayMs(100); up_filter.choke.set_capacity_kbps(1000); RunFor(40 * 1000); // 0-40s. up_filter.choke.set_capacity_kbps(2500); RunFor(20 * 1000); // 40-60s. up_filter.choke.set_capacity_kbps(600); RunFor(20 * 1000); // 60-80s. up_filter.choke.set_capacity_kbps(1000); RunFor(20 * 1000); // 80-100s. std::string title("5.1_Variable_capacity_single_flow"); metric_recorder.PlotThroughputHistogram(title, bwe_names[bwe_type], 1, 0); metric_recorder.PlotDelayHistogram(title, bwe_names[bwe_type], 1, kOneWayDelayMs); metric_recorder.PlotLossHistogram(title, bwe_names[bwe_type], 1, receiver.GlobalPacketLoss()); BWE_TEST_LOGGING_BASELINEBAR(5, bwe_names[bwe_type], kOneWayDelayMs, kFlowId); } // 5.2. Two forward direction competing flows, variable capacity. void BweTest::RunVariableCapacity2MultipleFlows(BandwidthEstimatorType bwe_type, size_t num_flows) { std::vector sources; std::vector senders; std::vector metric_recorders; std::vector receivers; const int64_t kStartingApartMs = 0; // Flows initialized simultaneously. for (size_t i = 0; i < num_flows; ++i) { sources.push_back(new AdaptiveVideoSource(static_cast(i), 30, 300, 0, i * kStartingApartMs)); senders.push_back(new VideoSender(&uplink_, sources[i], bwe_type)); } FlowIds flow_ids = CreateFlowIdRange(0, static_cast(num_flows - 1)); DefaultEvaluationFilter up_filter(&uplink_, flow_ids); LinkShare link_share(&(up_filter.choke)); RateCounterFilter total_utilization(&uplink_, flow_ids, "Total_utilization", "Total_link_utilization"); // Delays is being plotted only for the first flow. // To plot all of them, replace "i == 0" with "true" on new PacketReceiver(). for (size_t i = 0; i < num_flows; ++i) { metric_recorders.push_back(new MetricRecorder( bwe_names[bwe_type], static_cast(i), senders[i], &link_share)); receivers.push_back(new PacketReceiver(&uplink_, static_cast(i), bwe_type, i == 0, false, metric_recorders[i])); metric_recorders[i]->set_plot_available_capacity( i == 0 && plot_total_available_capacity_); } DelayFilter down_filter(&downlink_, flow_ids); down_filter.SetOneWayDelayMs(kOneWayDelayMs); // Test also with one way propagation delay = 100ms. // up_filter.delay.SetOneWayDelayMs(100); // down_filter.SetOneWayDelayMs(100); up_filter.choke.set_capacity_kbps(4000); RunFor(25 * 1000); // 0-25s. up_filter.choke.set_capacity_kbps(2000); RunFor(25 * 1000); // 25-50s. up_filter.choke.set_capacity_kbps(3500); RunFor(25 * 1000); // 50-75s. up_filter.choke.set_capacity_kbps(1000); RunFor(25 * 1000); // 75-100s. up_filter.choke.set_capacity_kbps(2000); RunFor(25 * 1000); // 100-125s. std::string title("5.2_Variable_capacity_two_flows"); for (size_t i = 0; i < num_flows; ++i) { metric_recorders[i]->PlotThroughputHistogram(title, bwe_names[bwe_type], num_flows, 0); metric_recorders[i]->PlotDelayHistogram(title, bwe_names[bwe_type], num_flows, kOneWayDelayMs); metric_recorders[i]->PlotLossHistogram(title, bwe_names[bwe_type], num_flows, receivers[i]->GlobalPacketLoss()); BWE_TEST_LOGGING_BASELINEBAR(5, bwe_names[bwe_type], kOneWayDelayMs, i); } for (VideoSource* source : sources) delete source; for (PacketSender* sender : senders) delete sender; for (MetricRecorder* recorder : metric_recorders) delete recorder; for (PacketReceiver* receiver : receivers) delete receiver; } // 5.3. Bi-directional RMCAT flows. void BweTest::RunBidirectionalFlow(BandwidthEstimatorType bwe_type) { enum direction { kForward = 0, kBackward }; const size_t kNumFlows = 2; std::unique_ptr sources[kNumFlows]; std::unique_ptr senders[kNumFlows]; std::unique_ptr metric_recorders[kNumFlows]; std::unique_ptr receivers[kNumFlows]; sources[kForward].reset(new AdaptiveVideoSource(kForward, 30, 300, 0, 0)); senders[kForward].reset( new VideoSender(&uplink_, sources[kForward].get(), bwe_type)); sources[kBackward].reset(new AdaptiveVideoSource(kBackward, 30, 300, 0, 0)); senders[kBackward].reset( new VideoSender(&downlink_, sources[kBackward].get(), bwe_type)); DefaultEvaluationFilter up_filter(&uplink_, kForward); LinkShare up_link_share(&(up_filter.choke)); metric_recorders[kForward].reset(new MetricRecorder( bwe_names[bwe_type], kForward, senders[kForward].get(), &up_link_share)); receivers[kForward].reset( new PacketReceiver(&uplink_, kForward, bwe_type, true, false, metric_recorders[kForward].get())); metric_recorders[kForward].get()->set_plot_available_capacity( plot_total_available_capacity_); DefaultEvaluationFilter down_filter(&downlink_, kBackward); LinkShare down_link_share(&(down_filter.choke)); metric_recorders[kBackward].reset( new MetricRecorder(bwe_names[bwe_type], kBackward, senders[kBackward].get(), &down_link_share)); receivers[kBackward].reset( new PacketReceiver(&downlink_, kBackward, bwe_type, true, false, metric_recorders[kBackward].get())); metric_recorders[kBackward].get()->set_plot_available_capacity( plot_total_available_capacity_); // Test also with one way propagation delay = 100ms. // up_filter.delay.SetOneWayDelayMs(100); // down_filter.delay.SetOneWayDelayMs(100); up_filter.choke.set_capacity_kbps(2000); down_filter.choke.set_capacity_kbps(2000); RunFor(20 * 1000); // 0-20s. up_filter.choke.set_capacity_kbps(1000); RunFor(15 * 1000); // 20-35s. down_filter.choke.set_capacity_kbps(800); RunFor(5 * 1000); // 35-40s. up_filter.choke.set_capacity_kbps(500); RunFor(20 * 1000); // 40-60s. up_filter.choke.set_capacity_kbps(2000); RunFor(10 * 1000); // 60-70s. down_filter.choke.set_capacity_kbps(2000); RunFor(30 * 1000); // 70-100s. std::string title("5.3_Bidirectional_flows"); for (size_t i = 0; i < kNumFlows; ++i) { metric_recorders[i].get()->PlotThroughputHistogram( title, bwe_names[bwe_type], kNumFlows, 0); metric_recorders[i].get()->PlotDelayHistogram(title, bwe_names[bwe_type], kNumFlows, kOneWayDelayMs); metric_recorders[i].get()->PlotLossHistogram( title, bwe_names[bwe_type], kNumFlows, receivers[i].get()->GlobalPacketLoss()); BWE_TEST_LOGGING_BASELINEBAR(5, bwe_names[bwe_type], kOneWayDelayMs, i); } } // 5.4. Three forward direction competing flows, constant capacity. void BweTest::RunSelfFairness(BandwidthEstimatorType bwe_type) { const int kNumRmcatFlows = 3; const int kNumTcpFlows = 0; const int64_t kRunTimeS = 120; const int kLinkCapacity = 3500; int64_t max_delay_ms = kMaxQueueingDelayMs; int64_t rtt_ms = 2 * kOneWayDelayMs; const int64_t kStartingApartMs = 20 * 1000; int64_t offsets_ms[kNumRmcatFlows]; for (int i = 0; i < kNumRmcatFlows; ++i) { offsets_ms[i] = kStartingApartMs * i; } // Test also with one way propagation delay = 100ms. // rtt_ms = 2 * 100; // Test also with bottleneck queue size = 20ms and 1000ms. // max_delay_ms = 20; // max_delay_ms = 1000; std::string title("5.4_Self_fairness_test"); // Test also with one way propagation delay = 100ms. RunFairnessTest(bwe_type, kNumRmcatFlows, kNumTcpFlows, kRunTimeS, kLinkCapacity, max_delay_ms, rtt_ms, kMaxJitterMs, offsets_ms, title, bwe_names[bwe_type]); } // 5.5. Five competing RMCAT flows under different RTTs. void BweTest::RunRoundTripTimeFairness(BandwidthEstimatorType bwe_type) { const int kAllFlowIds[] = {0, 1, 2, 3, 4}; // Five RMCAT flows. const int64_t kAllOneWayDelayMs[] = {10, 25, 50, 100, 150}; const size_t kNumFlows = arraysize(kAllFlowIds); std::unique_ptr sources[kNumFlows]; std::unique_ptr senders[kNumFlows]; std::unique_ptr metric_recorders[kNumFlows]; // Flows initialized 10 seconds apart. const int64_t kStartingApartMs = 10 * 1000; for (size_t i = 0; i < kNumFlows; ++i) { sources[i].reset(new AdaptiveVideoSource(kAllFlowIds[i], 30, 300, 0, i * kStartingApartMs)); senders[i].reset(new VideoSender(&uplink_, sources[i].get(), bwe_type)); } ChokeFilter choke_filter(&uplink_, CreateFlowIds(kAllFlowIds, kNumFlows)); LinkShare link_share(&choke_filter); JitterFilter jitter_filter(&uplink_, CreateFlowIds(kAllFlowIds, kNumFlows)); std::unique_ptr up_delay_filters[kNumFlows]; for (size_t i = 0; i < kNumFlows; ++i) { up_delay_filters[i].reset(new DelayFilter(&uplink_, kAllFlowIds[i])); } RateCounterFilter total_utilization( &uplink_, CreateFlowIds(kAllFlowIds, kNumFlows), "Total_utilization", "Total_link_utilization"); // Delays is being plotted only for the first flow. // To plot all of them, replace "i == 0" with "true" on new PacketReceiver(). std::unique_ptr receivers[kNumFlows]; for (size_t i = 0; i < kNumFlows; ++i) { metric_recorders[i].reset( new MetricRecorder(bwe_names[bwe_type], static_cast(i), senders[i].get(), &link_share)); receivers[i].reset(new PacketReceiver(&uplink_, kAllFlowIds[i], bwe_type, i == 0, false, metric_recorders[i].get())); metric_recorders[i].get()->set_start_computing_metrics_ms(kStartingApartMs * (kNumFlows - 1)); metric_recorders[i].get()->set_plot_available_capacity( i == 0 && plot_total_available_capacity_); } std::unique_ptr down_delay_filters[kNumFlows]; for (size_t i = 0; i < kNumFlows; ++i) { down_delay_filters[i].reset(new DelayFilter(&downlink_, kAllFlowIds[i])); } jitter_filter.SetMaxJitter(kMaxJitterMs); choke_filter.set_max_delay_ms(kMaxQueueingDelayMs); for (size_t i = 0; i < kNumFlows; ++i) { up_delay_filters[i]->SetOneWayDelayMs(kAllOneWayDelayMs[i]); down_delay_filters[i]->SetOneWayDelayMs(kAllOneWayDelayMs[i]); } choke_filter.set_capacity_kbps(3500); RunFor(300 * 1000); // 0-300s. std::string title("5.5_Round_Trip_Time_Fairness"); for (size_t i = 0; i < kNumFlows; ++i) { metric_recorders[i].get()->PlotThroughputHistogram( title, bwe_names[bwe_type], kNumFlows, 0); metric_recorders[i].get()->PlotDelayHistogram(title, bwe_names[bwe_type], kNumFlows, kOneWayDelayMs); metric_recorders[i].get()->PlotLossHistogram( title, bwe_names[bwe_type], kNumFlows, receivers[i].get()->GlobalPacketLoss()); BWE_TEST_LOGGING_BASELINEBAR(5, bwe_names[bwe_type], kAllOneWayDelayMs[i], i); } } // 5.6. RMCAT Flow competing with a long TCP Flow. void BweTest::RunLongTcpFairness(BandwidthEstimatorType bwe_type) { const size_t kNumRmcatFlows = 1; const size_t kNumTcpFlows = 1; const int64_t kRunTimeS = 120; const int kCapacityKbps = 2000; // Tcp starts at t = 0, media flow at t = 5s. const int64_t kOffSetsMs[] = {5000, 0}; int64_t max_delay_ms = kMaxQueueingDelayMs; int64_t rtt_ms = 2 * kOneWayDelayMs; // Test also with one way propagation delay = 100ms. // rtt_ms = 2 * 100; // Test also with bottleneck queue size = 20ms and 1000ms. // max_delay_ms = 20; // max_delay_ms = 1000; std::string title("5.6_Long_TCP_Fairness"); std::string flow_name = std::string() + bwe_names[bwe_type] + 'x' + bwe_names[kTcpEstimator]; RunFairnessTest(bwe_type, kNumRmcatFlows, kNumTcpFlows, kRunTimeS, kCapacityKbps, max_delay_ms, rtt_ms, kMaxJitterMs, kOffSetsMs, title, flow_name); } // 5.7. RMCAT Flows competing with multiple short TCP Flows. void BweTest::RunMultipleShortTcpFairness( BandwidthEstimatorType bwe_type, std::vector tcp_file_sizes_bytes, std::vector tcp_starting_times_ms) { // Two RMCAT flows and ten TCP flows. const int kAllRmcatFlowIds[] = {0, 1}; const int kAllTcpFlowIds[] = {2, 3, 4, 5, 6, 7, 8, 9, 10, 11}; assert(tcp_starting_times_ms.size() == tcp_file_sizes_bytes.size() && tcp_starting_times_ms.size() == arraysize(kAllTcpFlowIds)); const size_t kNumRmcatFlows = arraysize(kAllRmcatFlowIds); const size_t kNumTotalFlows = kNumRmcatFlows + arraysize(kAllTcpFlowIds); std::unique_ptr sources[kNumRmcatFlows]; std::unique_ptr senders[kNumTotalFlows]; std::unique_ptr metric_recorders[kNumTotalFlows]; std::unique_ptr receivers[kNumTotalFlows]; // RMCAT Flows are initialized simultaneosly at t=5 seconds. const int64_t kRmcatStartingTimeMs = 5 * 1000; for (size_t id : kAllRmcatFlowIds) { sources[id].reset(new AdaptiveVideoSource(static_cast(id), 30, 300, 0, kRmcatStartingTimeMs)); senders[id].reset(new VideoSender(&uplink_, sources[id].get(), bwe_type)); } for (size_t id : kAllTcpFlowIds) { senders[id].reset(new TcpSender(&uplink_, static_cast(id), tcp_starting_times_ms[id - kNumRmcatFlows], tcp_file_sizes_bytes[id - kNumRmcatFlows])); } FlowIds flow_ids = CreateFlowIdRange(0, static_cast(kNumTotalFlows - 1)); DefaultEvaluationFilter up_filter(&uplink_, flow_ids); LinkShare link_share(&(up_filter.choke)); RateCounterFilter total_utilization(&uplink_, flow_ids, "Total_utilization", "Total_link_utilization"); // Delays is being plotted only for the first flow. // To plot all of them, replace "i == 0" with "true" on new PacketReceiver(). for (size_t id : kAllRmcatFlowIds) { metric_recorders[id].reset( new MetricRecorder(bwe_names[bwe_type], static_cast(id), senders[id].get(), &link_share)); receivers[id].reset(new PacketReceiver(&uplink_, static_cast(id), bwe_type, id == 0, false, metric_recorders[id].get())); metric_recorders[id].get()->set_start_computing_metrics_ms( kRmcatStartingTimeMs); metric_recorders[id].get()->set_plot_available_capacity( id == 0 && plot_total_available_capacity_); } // Delays is not being plotted only for TCP flows. To plot all of them, // replace first "false" occurence with "true" on new PacketReceiver(). for (size_t id : kAllTcpFlowIds) { metric_recorders[id].reset( new MetricRecorder(bwe_names[kTcpEstimator], static_cast(id), senders[id].get(), &link_share)); receivers[id].reset(new PacketReceiver(&uplink_, static_cast(id), kTcpEstimator, false, false, metric_recorders[id].get())); metric_recorders[id].get()->set_plot_available_capacity( id == 0 && plot_total_available_capacity_); } DelayFilter down_filter(&downlink_, flow_ids); down_filter.SetOneWayDelayMs(kOneWayDelayMs); // Test also with one way propagation delay = 100ms. // up_filter.delay.SetOneWayDelayMs(100); // down_filter.SetOneWayDelayms(100); // Test also with bottleneck queue size = 20ms and 1000ms. // up_filter.choke.set_max_delay_ms(20); // up_filter.choke.set_max_delay_ms(1000); // Test also with no Jitter: // up_filter.jitter.SetMaxJitter(0); up_filter.choke.set_capacity_kbps(2000); RunFor(300 * 1000); // 0-300s. std::string title("5.7_Multiple_short_TCP_flows"); for (size_t id : kAllRmcatFlowIds) { metric_recorders[id].get()->PlotThroughputHistogram( title, bwe_names[bwe_type], kNumRmcatFlows, 0); metric_recorders[id].get()->PlotDelayHistogram( title, bwe_names[bwe_type], kNumRmcatFlows, kOneWayDelayMs); metric_recorders[id].get()->PlotLossHistogram( title, bwe_names[bwe_type], kNumRmcatFlows, receivers[id].get()->GlobalPacketLoss()); BWE_TEST_LOGGING_BASELINEBAR(5, bwe_names[bwe_type], kOneWayDelayMs, id); } } // 5.8. Three forward direction competing flows, constant capacity. // During the test, one of the flows is paused and later resumed. void BweTest::RunPauseResumeFlows(BandwidthEstimatorType bwe_type) { const int kAllFlowIds[] = {0, 1, 2}; // Three RMCAT flows. const size_t kNumFlows = arraysize(kAllFlowIds); std::unique_ptr sources[kNumFlows]; std::unique_ptr senders[kNumFlows]; std::unique_ptr metric_recorders[kNumFlows]; std::unique_ptr receivers[kNumFlows]; // Flows initialized simultaneously. const int64_t kStartingApartMs = 0; for (size_t i = 0; i < kNumFlows; ++i) { sources[i].reset(new AdaptiveVideoSource(kAllFlowIds[i], 30, 300, 0, i * kStartingApartMs)); senders[i].reset(new VideoSender(&uplink_, sources[i].get(), bwe_type)); } DefaultEvaluationFilter filter(&uplink_, CreateFlowIds(kAllFlowIds, kNumFlows)); LinkShare link_share(&(filter.choke)); RateCounterFilter total_utilization( &uplink_, CreateFlowIds(kAllFlowIds, kNumFlows), "Total_utilization", "Total_link_utilization"); // Delays is being plotted only for the first flow. // To plot all of them, replace "i == 0" with "true" on new PacketReceiver(). for (size_t i = 0; i < kNumFlows; ++i) { metric_recorders[i].reset( new MetricRecorder(bwe_names[bwe_type], static_cast(i), senders[i].get(), &link_share)); receivers[i].reset(new PacketReceiver(&uplink_, kAllFlowIds[i], bwe_type, i == 0, false, metric_recorders[i].get())); metric_recorders[i].get()->set_start_computing_metrics_ms(kStartingApartMs * (kNumFlows - 1)); metric_recorders[i].get()->set_plot_available_capacity( i == 0 && plot_total_available_capacity_); } // Test also with one way propagation delay = 100ms. // filter.delay.SetOneWayDelayMs(100); filter.choke.set_capacity_kbps(3500); RunFor(40 * 1000); // 0-40s. senders[0].get()->Pause(); RunFor(20 * 1000); // 40-60s. senders[0].get()->Resume(20 * 1000); RunFor(60 * 1000); // 60-120s. int64_t paused[] = {20 * 1000, 0, 0}; // First flow is being paused, hence having a different optimum. const std::string optima_lines[] = {"1", "2", "2"}; std::string title("5.8_Pause_and_resume_media_flow"); for (size_t i = 0; i < kNumFlows; ++i) { metric_recorders[i].get()->PlotThroughputHistogram( title, bwe_names[bwe_type], kNumFlows, paused[i], optima_lines[i]); metric_recorders[i].get()->PlotDelayHistogram(title, bwe_names[bwe_type], kNumFlows, kOneWayDelayMs); metric_recorders[i].get()->PlotLossHistogram( title, bwe_names[bwe_type], kNumFlows, receivers[i].get()->GlobalPacketLoss()); BWE_TEST_LOGGING_BASELINEBAR(5, bwe_names[bwe_type], kOneWayDelayMs, i); } } // Following functions are used for randomizing TCP file size and // starting time, used on 5.7 RunMultipleShortTcpFairness. // They are pseudo-random generators, creating always the same // value sequence for a given Random seed. std::vector BweTest::GetFileSizesBytes(int num_files) { // File size chosen from uniform distribution between [100,1000] kB. const int kMinKbytes = 100; const int kMaxKbytes = 1000; Random random(0x12345678); std::vector tcp_file_sizes_bytes; while (num_files-- > 0) { tcp_file_sizes_bytes.push_back(random.Rand(kMinKbytes, kMaxKbytes) * 1000); } return tcp_file_sizes_bytes; } std::vector BweTest::GetStartingTimesMs(int num_files) { // OFF state behaves as an exp. distribution with mean = 10 seconds. const float kMeanMs = 10000.0f; Random random(0x12345678); std::vector tcp_starting_times_ms; // Two TCP Flows are initialized simultaneosly at t=0 seconds. for (int i = 0; i < 2; ++i, --num_files) { tcp_starting_times_ms.push_back(0); } // Other TCP Flows are initialized in an OFF state. while (num_files-- > 0) { tcp_starting_times_ms.push_back( static_cast(random.Exponential(1.0f / kMeanMs))); } return tcp_starting_times_ms; } } // namespace bwe } // namespace testing } // namespace webrtc