/* * Copyright 2018 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/congestion_controller/bbr/bbr_factory.h" #include "rtc_base/experiments/field_trial_parser.h" #include "rtc_base/experiments/field_trial_units.h" #include "rtc_base/random.h" #include "test/field_trial.h" #include "test/gtest.h" #include "test/scenario/scenario.h" namespace webrtc { namespace test { namespace { constexpr int64_t kRunTimeMs = 60000; using ::testing::Combine; using ::testing::make_tuple; using ::testing::tuple; using ::testing::Values; using Codec = VideoStreamConfig::Encoder::Codec; using CodecImpl = VideoStreamConfig::Encoder::Implementation; struct CallTestConfig { struct Scenario { FieldTrialParameter random_seed; FieldTrialFlag return_traffic; FieldTrialParameter capacity; FieldTrialParameter propagation_delay; FieldTrialParameter cross_traffic; FieldTrialParameter delay_noise; FieldTrialParameter loss_rate; Scenario() : random_seed("rs", 1), return_traffic("ret"), capacity("bw", DataRate::kbps(300)), propagation_delay("dl", TimeDelta::ms(100)), cross_traffic("ct", DataRate::Zero()), delay_noise("dn", TimeDelta::Zero()), loss_rate("pl", 0) {} void Parse(std::string config_str) { ParseFieldTrial( {&random_seed, &return_traffic, &capacity, &propagation_delay, &cross_traffic, &delay_noise, &loss_rate}, config_str); } } scenario; struct Tuning { FieldTrialFlag use_bbr; FieldTrialFlag bbr_no_target_rate; FieldTrialOptional bbr_initial_window; FieldTrialParameter bbr_encoder_gain; Tuning() : use_bbr("bbr"), bbr_no_target_rate("notr"), bbr_initial_window("iw", DataSize::bytes(8000)), bbr_encoder_gain("eg", 0.8) {} void Parse(std::string config_str) { ParseFieldTrial( { &use_bbr, &bbr_no_target_rate, &bbr_initial_window, &bbr_encoder_gain, }, config_str); } } tuning; void Parse(std::string scenario_string, std::string tuning_string) { scenario.Parse(scenario_string); tuning.Parse(tuning_string); scenario_str = scenario_string; tuning_str = tuning_string; } std::string scenario_str; std::string tuning_str; std::string BbrTrial() const { char trial_buf[1024]; rtc::SimpleStringBuilder trial(trial_buf); trial << "WebRTC-BweBbrConfig/"; trial << "encoder_rate_gain_in_probe_rtt:0.5"; trial.AppendFormat(",encoder_rate_gain:%.1lf", tuning.bbr_encoder_gain.Get()); if (tuning.bbr_no_target_rate) trial << ",pacing_rate_as_target:1"; if (tuning.bbr_initial_window) trial << ",initial_cwin:" << tuning.bbr_initial_window->bytes(); trial << "/"; return trial.str(); } std::string FieldTrials() const { std::string trials; if (tuning.use_bbr) { trials += "WebRTC-BweCongestionController/Enabled,BBR/" "WebRTC-Pacer-DrainQueue/Disabled/" "WebRTC-Pacer-PadInSilence/Enabled/" "WebRTC-Pacer-BlockAudio/Disabled/" "WebRTC-Audio-SendSideBwe/Enabled/" "WebRTC-SendSideBwe-WithOverhead/Enabled/"; trials += BbrTrial(); } return trials; } std::string Name() const { char raw_name[1024]; rtc::SimpleStringBuilder name(raw_name); for (char c : scenario_str + "__tun__" + tuning_str) { if (c == ':') { continue; } else if (c == ',') { name << "_"; } else if (c == '%') { name << "p"; } else { name << c; } } return name.str(); } }; } // namespace class BbrScenarioTest : public ::testing::Test, public ::testing::WithParamInterface> { public: BbrScenarioTest() { conf_.Parse(::testing::get<0>(GetParam()), ::testing::get<1>(GetParam())); field_trial_.reset(new test::ScopedFieldTrials(conf_.FieldTrials())); } CallTestConfig conf_; private: std::unique_ptr field_trial_; }; TEST_P(BbrScenarioTest, ReceivesVideo) { BbrNetworkControllerFactory bbr_factory; Scenario s("bbr_test_gen/bbr__" + conf_.Name()); CallClientConfig call_config; if (conf_.tuning.use_bbr) { call_config.transport.cc_factory = &bbr_factory; } call_config.transport.rates.min_rate = DataRate::kbps(30); call_config.transport.rates.max_rate = DataRate::kbps(1800); CallClient* alice = s.CreateClient("send", call_config); CallClient* bob = s.CreateClient("return", call_config); NetworkSimulationConfig net_conf; net_conf.bandwidth = conf_.scenario.capacity; net_conf.delay = conf_.scenario.propagation_delay; net_conf.loss_rate = conf_.scenario.loss_rate; net_conf.delay_std_dev = conf_.scenario.delay_noise; auto* send_net = s.CreateMutableSimulationNode(net_conf); auto* ret_net = s.CreateMutableSimulationNode(net_conf); auto route = s.CreateRoutes(alice, {send_net->node()}, bob, {ret_net->node()}); VideoStreamPair* alice_video = s.CreateVideoStream(route->forward(), [&](VideoStreamConfig* c) { c->encoder.fake.max_rate = DataRate::kbps(1800); }); s.CreateAudioStream(route->forward(), [&](AudioStreamConfig* c) { if (conf_.tuning.use_bbr) { c->stream.in_bandwidth_estimation = true; c->encoder.fixed_rate = DataRate::kbps(31); } }); VideoStreamPair* bob_video = nullptr; if (conf_.scenario.return_traffic) { bob_video = s.CreateVideoStream(route->reverse(), [&](VideoStreamConfig* c) { c->encoder.fake.max_rate = DataRate::kbps(1800); }); s.CreateAudioStream(route->reverse(), [&](AudioStreamConfig* c) { if (conf_.tuning.use_bbr) { c->stream.in_bandwidth_estimation = true; c->encoder.fixed_rate = DataRate::kbps(31); } }); } RandomWalkConfig cross_config; cross_config.peak_rate = conf_.scenario.cross_traffic; cross_config.random_seed = conf_.scenario.random_seed; auto* cross_traffic = s.net()->CreateRandomWalkCrossTraffic( s.net()->CreateTrafficRoute({send_net->node()}), cross_config); s.CreatePrinter("send.stats.txt", TimeDelta::ms(100), {alice->StatsPrinter(), alice_video->send()->StatsPrinter(), cross_traffic->StatsPrinter(), send_net->ConfigPrinter()}); std::vector return_printers{ bob->StatsPrinter(), ColumnPrinter::Fixed("cross_traffic_rate", "0"), ret_net->ConfigPrinter()}; if (bob_video) return_printers.push_back(bob_video->send()->StatsPrinter()); s.CreatePrinter("return.stats.txt", TimeDelta::ms(100), return_printers); s.RunFor(TimeDelta::ms(kRunTimeMs)); } INSTANTIATE_TEST_SUITE_P(Selected, BbrScenarioTest, Values(make_tuple("rs:1,bw:150,dl:100,ct:100", "bbr"))); INSTANTIATE_TEST_SUITE_P( OneWayTuning, BbrScenarioTest, Values(make_tuple("bw:150,dl:100", "bbr,iw:,eg:100%,notr"), make_tuple("bw:150,dl:100", "bbr,iw:8000,eg:100%,notr"), make_tuple("bw:150,dl:100", "bbr,iw:8000,eg:100%"), make_tuple("bw:150,dl:100", "bbr,iw:8000,eg:80%"))); INSTANTIATE_TEST_SUITE_P(OneWayTuned, BbrScenarioTest, Values(make_tuple("bw:150,dl:100", "bbr"), make_tuple("bw:150,dl:100", ""), make_tuple("bw:800,dl:100", "bbr"), make_tuple("bw:800,dl:100", ""))); INSTANTIATE_TEST_SUITE_P(OneWayDegraded, BbrScenarioTest, Values(make_tuple("bw:150,dl:100,dn:30,pl:5%", "bbr"), make_tuple("bw:150,dl:100,dn:30,pl:5%", ""), make_tuple("bw:150,ct:100,dl:100", "bbr"), make_tuple("bw:150,ct:100,dl:100", ""), make_tuple("bw:800,dl:100,dn:30,pl:5%", "bbr"), make_tuple("bw:800,dl:100,dn:30,pl:5%", ""), make_tuple("bw:800,ct:600,dl:100", "bbr"), make_tuple("bw:800,ct:600,dl:100", ""))); INSTANTIATE_TEST_SUITE_P(TwoWay, BbrScenarioTest, Values(make_tuple("ret,bw:150,dl:100", "bbr"), make_tuple("ret,bw:150,dl:100", ""), make_tuple("ret,bw:800,dl:100", "bbr"), make_tuple("ret,bw:800,dl:100", ""), make_tuple("ret,bw:150,dl:50", "bbr"), make_tuple("ret,bw:150,dl:50", ""))); } // namespace test } // namespace webrtc