blob: 538b91a181dd4881be64e12e91601242c71afcfc [file] [log] [blame]
/*
* Copyright (c) 2025 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 <map>
#include <memory>
#include <optional>
#include <span>
#include <string>
#include <utility>
#include <vector>
#include "absl/flags/flag.h"
#include "api/audio_options.h"
#include "api/rtp_parameters.h"
#include "api/scoped_refptr.h"
#include "api/stats/rtc_stats_report.h"
#include "api/test/network_emulation/dual_pi2_network_queue.h"
#include "api/test/network_emulation/network_config_schedule.pb.h"
#include "api/test/network_emulation/network_emulation_interfaces.h"
#include "api/test/network_emulation/network_queue.h"
#include "api/test/network_emulation/schedulable_network_node_builder.h"
#include "api/test/network_emulation/token_bucket_network_behavior_builder.h"
#include "api/test/network_emulation_manager.h"
#include "api/units/data_rate.h"
#include "api/units/data_size.h"
#include "api/units/time_delta.h"
#include "api/units/timestamp.h"
#include "rtc_base/logging.h"
#include "test/create_frame_generator_capturer.h"
#include "test/gmock.h"
#include "test/gtest.h"
#include "test/network/network_emulation.h"
#include "test/peer_scenario/bwe_integration_tests/stats_utilities.h"
#include "test/peer_scenario/peer_scenario.h"
#include "test/peer_scenario/peer_scenario_client.h"
// Tests for SCReAM.
// Per default, the tests currently does not set expectations on BWE. The
// reason is due to that the tests are not deterministic enough on all
// platforms.
// TODO: bugs.webrtc.org/447037083 - Attempt to make tests stable enough for
// the none deterministic behaviour to not matter.
// To enable BWE expectations, set the flag --enable-scream-bwe-expectations.
// To write RTC eventlogs to file, set the flag --peer_logs=true and
// --peer_logs_root=<path>.
ABSL_FLAG(bool,
enable_scream_bwe_expectations,
false,
"Enable BWE expectations in Scream tests.");
namespace webrtc {
namespace {
using test::GetAvailableSendBitrate;
using test::GetCurrentRoundTripTime;
using test::GetFirstReportAtOrAfter;
using test::GetPacketsLost;
using test::GetPacketsReceived;
using test::GetPacketsReceivedWithCe;
using test::GetPacketsReceivedWithEct1;
using test::GetPacketsSent;
using test::GetPacketsSentWithEct1;
using test::GetStatsAndProcess;
using test::PeerScenario;
using test::PeerScenarioClient;
using ::testing::AllOf;
using ::testing::Each;
using ::testing::Gt;
using ::testing::HasSubstr;
using ::testing::Lt;
using ::testing::TestWithParam;
#if defined(ADDRESS_SANITIZER)
// These tests are too slow to run with ASAN.
#define MaybeTest(t) DISABLED_##t
#else
#define MaybeTest(t) t
#endif
MATCHER_P2(AvailableSendBitrateIsBetween, low, high, "") {
DataRate available_bwe = GetAvailableSendBitrate(arg);
if (available_bwe > low && available_bwe < high) {
return true;
}
*result_listener << "the available send bitrate is " << available_bwe.kbps()
<< "kbps, which is not between " << low.kbps() << "kbps and "
<< high.kbps() << " kbps";
if (absl::GetFlag(FLAGS_enable_scream_bwe_expectations)) {
// TODO: bugs.webrtc.org/447037083 - Attempt to make tests stable enough for
// the none deterministic behaviour to not matter.
return false;
}
RTC_LOG(LS_ERROR) << "!!! the available send bitrate is "
<< available_bwe.kbps() << "kbps, which is not between "
<< low.kbps() << "kbps and " << high.kbps() << " kbps";
return true;
}
MATCHER_P2(CurrentRoundTripTimeIsBetween, low, high, "") {
TimeDelta rtt = GetCurrentRoundTripTime(arg);
if (rtt > low && rtt < high) {
return true;
}
*result_listener << "the current round trip time is " << rtt.ms()
<< "ms, which is not between " << low.ms() << "ms and "
<< high.ms() << " ms";
return false;
}
std::vector<EmulatedNetworkNode*> CreateNetworkPath(
NetworkEmulationManager::SimulatedNetworkNode::Builder& network_builder,
bool use_dual_pi) {
std::unique_ptr<NetworkQueueFactory> queue_factory;
if (use_dual_pi) {
queue_factory = std::make_unique<DualPi2NetworkQueueFactory>(
DualPi2NetworkQueue::Config({.target_delay = TimeDelta::Millis(10)}));
network_builder.queue_factory(*queue_factory);
}
return {network_builder.Build().node};
}
std::vector<EmulatedNetworkNode*> CreateNetworkPath(PeerScenario& s,
bool use_dual_pi,
DataRate link_capacity,
TimeDelta one_way_delay) {
NetworkEmulationManager::SimulatedNetworkNode::Builder network_builder =
s.net()
->NodeBuilder()
.capacity(link_capacity)
.delay_ms(one_way_delay.ms());
return CreateNetworkPath(network_builder, use_dual_pi);
}
std::vector<EmulatedNetworkNode*> CreateNetworkPathWithPauseBetween3sAnd6s(
PeerScenario& s) {
network_behaviour::NetworkConfigSchedule schedule;
auto initial_config = schedule.add_item();
initial_config->set_link_capacity_kbps(1000);
auto updated_capacity = schedule.add_item();
updated_capacity->set_time_since_first_sent_packet_ms(3000);
updated_capacity->set_link_capacity_kbps(0);
updated_capacity = schedule.add_item();
updated_capacity->set_time_since_first_sent_packet_ms(6000);
updated_capacity->set_link_capacity_kbps(1000);
SchedulableNetworkNodeBuilder schedulable_builder(*s.net(),
std::move(schedule));
return {schedulable_builder.Build()};
}
std::vector<EmulatedNetworkNode*> CreateNetworkPathWithRepeatedPause(
PeerScenario& s,
DataRate link_capacity,
TimeDelta pause_duration,
TimeDelta repeat_pause_interval) {
network_behaviour::NetworkConfigSchedule schedule;
auto initial_config = schedule.add_item();
initial_config->set_link_capacity_kbps(link_capacity.kbps());
initial_config->set_queue_delay_ms(10);
auto updated_capacity = schedule.add_item();
updated_capacity->set_time_since_first_sent_packet_ms(
repeat_pause_interval.ms());
updated_capacity->set_link_capacity_kbps(0);
schedule.set_repeat_schedule_after_last_ms(pause_duration.ms());
SchedulableNetworkNodeBuilder schedulable_builder(*s.net(),
std::move(schedule));
return {schedulable_builder.Build()};
}
std::vector<EmulatedNetworkNode*> CreateNetworkPath1MbitDelayIncreaseAfter3S(
PeerScenario& s) {
network_behaviour::NetworkConfigSchedule schedule;
auto initial_config = schedule.add_item();
initial_config->set_link_capacity_kbps(1000);
initial_config->set_queue_delay_ms(10);
auto updated_latency = schedule.add_item();
updated_latency->set_time_since_first_sent_packet_ms(3000);
updated_latency->set_queue_delay_ms(80);
SchedulableNetworkNodeBuilder schedulable_builder(*s.net(),
std::move(schedule));
return {schedulable_builder.Build()};
}
std::vector<EmulatedNetworkNode*> CreateNetworkPathWithChangedCapacityAfter5s(
PeerScenario& s,
DataRate link_capacity_1,
DataRate link_capacity_2) {
network_behaviour::NetworkConfigSchedule schedule;
auto initial_config = schedule.add_item();
initial_config->set_link_capacity_kbps(link_capacity_1.kbps());
initial_config->set_queue_delay_ms(15);
auto updated_capacity = schedule.add_item();
updated_capacity->set_time_since_first_sent_packet_ms(5000);
updated_capacity->set_link_capacity_kbps(link_capacity_2.kbps());
SchedulableNetworkNodeBuilder schedulable_builder(*s.net(),
std::move(schedule));
return {schedulable_builder.Build()};
}
struct SendMediaTestResult {
// Stats gathered every second during the call.
std::vector<scoped_refptr<const RTCStatsReport>> caller_stats;
std::vector<scoped_refptr<const RTCStatsReport>> callee_stats;
std::span<const scoped_refptr<const RTCStatsReport>> caller() const {
return std::span<const scoped_refptr<const RTCStatsReport>>(caller_stats);
}
std::span<const scoped_refptr<const RTCStatsReport>> callee() const {
return std::span<const scoped_refptr<const RTCStatsReport>>(callee_stats);
}
};
struct SendMediaTestParams {
std::vector<EmulatedNetworkNode*> caller_to_callee_path;
std::vector<EmulatedNetworkNode*> callee_to_caller_path;
std::map</*trial*/ std::string, /*group*/ std::string> field_trials = {
{"WebRTC-RFC8888CongestionControlFeedback", "Enabled,offer:true"},
{"WebRTC-Bwe-ScreamV2", "Enabled"}};
// Audio is negotiated and sent.
bool send_audio = true;
// Video capturer is producing frames.
bool video_capture_enabled = true;
// Configure and set the max video encoding bitrate. If not set, the max
// default per codec and resolution is used.
std::optional<DataRate> max_video_bitrate;
PeerScenarioClient::VideoSendTrackConfig caller_video_conf = {
.generator = {.squares_video =
test::FrameGeneratorCapturerConfig::SquaresVideo{
.framerate = 30,
.width = 1280,
.height = 720,
}}};
TimeDelta test_duration = TimeDelta::Seconds(10);
TimeDelta stats_interval = TimeDelta::Seconds(1);
};
// Sends audio and video from a caller to a callee with symmetric
// uplink/downlink network.
SendMediaTestResult SendMediaInOneDirection(SendMediaTestParams params,
PeerScenario& s) {
PeerScenarioClient::Config config;
for (auto [trial, group] : params.field_trials) {
config.field_trials.Set(trial, group);
}
PeerScenarioClient* caller = s.CreateClient(config);
PeerScenarioClient* callee = s.CreateClient(config);
if (params.send_audio) {
caller->CreateAudio("AUDIO_1", {});
}
test::PeerScenarioClient::VideoSendTrack video_track =
caller->CreateVideo("VIDEO_1", params.caller_video_conf);
if (!params.video_capture_enabled) {
// Stop the capturer.
video_track.source->Stop();
}
if (params.max_video_bitrate.has_value()) {
RtpParameters rtp_parameters = video_track.sender->GetParameters();
rtp_parameters.encodings[0].max_bitrate_bps =
params.max_video_bitrate->bps();
video_track.sender->SetParameters(rtp_parameters);
}
s.SimpleConnection(caller, callee, std::move(params.caller_to_callee_path),
std::move(params.callee_to_caller_path));
SendMediaTestResult result;
Timestamp end_time = s.net()->Now() + params.test_duration;
while (s.net()->Now() < end_time) {
s.ProcessMessages(params.stats_interval);
result.caller_stats.push_back(GetStatsAndProcess(s, caller));
result.callee_stats.push_back(GetStatsAndProcess(s, callee));
}
return result;
}
// This test is not using Scream - it is only here as a reference.
TEST(ScreamTest, MaybeTest(LinkCapacity600KbpsRtt100msNoEcnWithGoogCC)) {
PeerScenario s(*testing::UnitTest::GetInstance()->current_test_info());
SendMediaTestParams params{
.field_trials = {
{"WebRTC-RFC8888CongestionControlFeedback", "Enabled,offer:true"}}};
params.callee_to_caller_path =
CreateNetworkPath(s, /*use_dual_pi= */ false,
DataRate::KilobitsPerSec(600), TimeDelta::Millis(50));
params.caller_to_callee_path =
CreateNetworkPath(s, /*use_dual_pi= */ false,
DataRate::KilobitsPerSec(600), TimeDelta::Millis(50));
SendMediaTestResult result = SendMediaInOneDirection(std::move(params), s);
EXPECT_THAT(result.caller_stats.back(),
AvailableSendBitrateIsBetween(DataRate::KilobitsPerSec(450),
DataRate::KilobitsPerSec(700)));
}
TEST(ScreamTest, MaybeTest(LinkCapacity600KbpsRtt100msNoEcn)) {
PeerScenario s(*testing::UnitTest::GetInstance()->current_test_info());
SendMediaTestParams params;
params.callee_to_caller_path =
CreateNetworkPath(s, /*use_dual_pi= */ false,
DataRate::KilobitsPerSec(600), TimeDelta::Millis(50));
params.caller_to_callee_path =
CreateNetworkPath(s, /*use_dual_pi= */ false,
DataRate::KilobitsPerSec(600), TimeDelta::Millis(50));
SendMediaTestResult result = SendMediaInOneDirection(std::move(params), s);
EXPECT_THAT(result.caller().subspan(1), Each(AvailableSendBitrateIsBetween(
DataRate::KilobitsPerSec(250),
DataRate::KilobitsPerSec(700))));
}
TEST(ScreamTest,
MaybeTest(LinkCapacityIncreaseFrom500KbitTo5MbpsAfter5sNoEcn)) {
PeerScenario s(*testing::UnitTest::GetInstance()->current_test_info());
SendMediaTestParams params;
params.caller_to_callee_path = CreateNetworkPathWithChangedCapacityAfter5s(
s, DataRate::KilobitsPerSec(500), DataRate::KilobitsPerSec(5000));
params.callee_to_caller_path =
CreateNetworkPath(s, /*use_dual_pi= */ false,
DataRate::KilobitsPerSec(3000), TimeDelta::Millis(25));
SendMediaTestResult result = SendMediaInOneDirection(std::move(params), s);
// Stats 2-5s
EXPECT_THAT(
result.caller().subspan(1, 3),
Each(AvailableSendBitrateIsBetween(DataRate::KilobitsPerSec(200),
DataRate::KilobitsPerSec(600))));
// Stats after 9s
EXPECT_THAT(result.caller().subspan(9), Each(AvailableSendBitrateIsBetween(
DataRate::KilobitsPerSec(1200),
DataRate::KilobitsPerSec(5000))));
}
TEST(
ScreamTest,
MaybeTest(
LinkCapacityIncreaseFrom80KbitTo5MbpsAfter5sVideoCaptureStoppedNoEcn)) {
PeerScenario s(*testing::UnitTest::GetInstance()->current_test_info());
SendMediaTestParams params;
params.video_capture_enabled = false;
params.max_video_bitrate = DataRate::KilobitsPerSec(3000);
params.caller_to_callee_path = CreateNetworkPathWithChangedCapacityAfter5s(
s, DataRate::KilobitsPerSec(80), DataRate::KilobitsPerSec(5000));
params.callee_to_caller_path =
CreateNetworkPath(s, /*use_dual_pi= */ false,
DataRate::KilobitsPerSec(3000), TimeDelta::Millis(25));
SendMediaTestResult result = SendMediaInOneDirection(std::move(params), s);
// Stats 2-5s
EXPECT_THAT(
result.caller().subspan(1, 3),
Each(AvailableSendBitrateIsBetween(DataRate::KilobitsPerSec(10),
DataRate::KilobitsPerSec(140))));
EXPECT_THAT(result.caller()[3],
CurrentRoundTripTimeIsBetween(TimeDelta::Millis(40),
TimeDelta::Millis(200)));
// Stats after 9s
// TODO: bugs.webrtc.org/447037083 - Figure out why setting max_video_bitrate
// does not force available send bitrate to increase despite that the camera
// is disabled.
EXPECT_THAT(result.caller_stats.back(),
AvailableSendBitrateIsBetween(DataRate::KilobitsPerSec(100),
DataRate::KilobitsPerSec(6500)));
}
TEST(ScreamTest, MaybeTest(LinkCapacity600KbpsRtt20msNoEcn)) {
PeerScenario s(*testing::UnitTest::GetInstance()->current_test_info());
SendMediaTestParams params;
params.test_duration = TimeDelta::Seconds(30);
params.callee_to_caller_path =
CreateNetworkPath(s, /*use_dual_pi= */ false,
DataRate::KilobitsPerSec(600), TimeDelta::Millis(10));
params.caller_to_callee_path =
CreateNetworkPath(s, /*use_dual_pi= */ false,
DataRate::KilobitsPerSec(600), TimeDelta::Millis(10));
SendMediaTestResult result = SendMediaInOneDirection(std::move(params), s);
EXPECT_THAT(result.caller().subspan(1), Each(AvailableSendBitrateIsBetween(
DataRate::KilobitsPerSec(200),
DataRate::KilobitsPerSec(900))));
}
TEST(ScreamTest, MaybeTest(LinkCapacity600KbpsRtt100msEcn)) {
PeerScenario s(*testing::UnitTest::GetInstance()->current_test_info());
SendMediaTestParams params;
params.callee_to_caller_path =
CreateNetworkPath(s, /*use_dual_pi= */ true,
DataRate::KilobitsPerSec(600), TimeDelta::Millis(50));
params.caller_to_callee_path =
CreateNetworkPath(s, /*use_dual_pi= */ true,
DataRate::KilobitsPerSec(600), TimeDelta::Millis(50));
SendMediaTestResult result = SendMediaInOneDirection(std::move(params), s);
// Allow rampup to take 3s.
EXPECT_THAT(result.caller().subspan(3), Each(AvailableSendBitrateIsBetween(
DataRate::KilobitsPerSec(350),
DataRate::KilobitsPerSec(660))));
}
TEST(ScreamTest, MaybeTest(LinkCapacity600KbpsRtt100msEcnAfterCe)) {
PeerScenario s(*testing::UnitTest::GetInstance()->current_test_info());
SendMediaTestParams params;
params.callee_to_caller_path =
CreateNetworkPath(s, /*use_dual_pi= */ true,
DataRate::KilobitsPerSec(600), TimeDelta::Millis(50));
params.caller_to_callee_path =
CreateNetworkPath(s, /*use_dual_pi= */ true,
DataRate::KilobitsPerSec(600), TimeDelta::Millis(50));
params.field_trials = {
{"WebRTC-RFC8888CongestionControlFeedback", "Enabled,offer:true"},
{"WebRTC-Bwe-ScreamV2", "mode:only_after_ce"}};
SendMediaTestResult result = SendMediaInOneDirection(std::move(params), s);
// All packets are sent as ECT1.
EXPECT_EQ(GetPacketsSent(result.caller_stats.back()),
GetPacketsSentWithEct1(result.caller_stats.back()));
// Not all packets has been received yet.
EXPECT_GE(GetPacketsSentWithEct1(result.caller_stats.back()),
0.9 * (GetPacketsReceived(result.callee_stats.back())));
EXPECT_THAT(result.caller_stats.back(),
AvailableSendBitrateIsBetween(DataRate::KilobitsPerSec(350),
DataRate::KilobitsPerSec(660)));
}
// Test that we can switch from Goog CC sending ECT1 to send ECT 0 and adapt.
TEST(ScreamTest, MaybeTest(LinkCapacity600KbpsRtt100msEcnWithGoogCcAfterCe)) {
PeerScenario s(*testing::UnitTest::GetInstance()->current_test_info());
SendMediaTestParams params;
params.callee_to_caller_path =
CreateNetworkPath(s, /*use_dual_pi= */ true,
DataRate::KilobitsPerSec(600), TimeDelta::Millis(50));
params.caller_to_callee_path =
CreateNetworkPath(s, /*use_dual_pi= */ true,
DataRate::KilobitsPerSec(600), TimeDelta::Millis(50));
params.field_trials = {
{"WebRTC-RFC8888CongestionControlFeedback", "Enabled,offer:true"},
{"WebRTC-Bwe-ScreamV2", "mode:goog_cc_with_ect1"}};
SendMediaTestResult result = SendMediaInOneDirection(std::move(params), s);
EXPECT_THAT(result.caller_stats.back(),
AvailableSendBitrateIsBetween(DataRate::KilobitsPerSec(350),
DataRate::KilobitsPerSec(660)));
// Not all packets are sent as ECT1 since packets are supposed to be sent as
// not ECT if CE is detected.
EXPECT_GT(GetPacketsSent(result.caller_stats.back()),
GetPacketsSentWithEct1(result.caller_stats.back()));
EXPECT_GE(GetPacketsReceivedWithCe(result.callee_stats.back()), 1);
}
TEST(ScreamTest, MaybeTest(LinkCapacity1000KbpsRtt100msEcn)) {
PeerScenario s(*testing::UnitTest::GetInstance()->current_test_info());
SendMediaTestParams params;
params.callee_to_caller_path =
CreateNetworkPath(s, /*use_dual_pi= */ true,
DataRate::KilobitsPerSec(1000), TimeDelta::Millis(50));
params.caller_to_callee_path =
CreateNetworkPath(s, /*use_dual_pi= */ true,
DataRate::KilobitsPerSec(1000), TimeDelta::Millis(50));
SendMediaTestResult result = SendMediaInOneDirection(std::move(params), s);
// Ignore result first 2s since ramp up is rather slow at higher RTT.
EXPECT_THAT(result.caller().subspan(2), Each(AvailableSendBitrateIsBetween(
DataRate::KilobitsPerSec(600),
DataRate::KilobitsPerSec(1000))));
}
TEST(ScreamTest, MaybeTest(LinkCapacity1500KbpsRtt30msNoEcn)) {
PeerScenario s(*testing::UnitTest::GetInstance()->current_test_info());
SendMediaTestParams params;
params.test_duration = TimeDelta::Seconds(30);
params.callee_to_caller_path =
CreateNetworkPath(s, /*use_dual_pi= */ false,
DataRate::KilobitsPerSec(1500), TimeDelta::Millis(15));
params.caller_to_callee_path =
CreateNetworkPath(s, /*use_dual_pi= */ false,
DataRate::KilobitsPerSec(1500), TimeDelta::Millis(15));
SendMediaTestResult result = SendMediaInOneDirection(std::move(params), s);
EXPECT_THAT(result.caller().subspan(1), Each(AvailableSendBitrateIsBetween(
DataRate::KilobitsPerSec(800),
DataRate::KilobitsPerSec(2200))));
}
TEST(ScreamTest, MaybeTest(LinkCapacity2MbpsRtt50msNoEcn)) {
PeerScenario s(*testing::UnitTest::GetInstance()->current_test_info());
SendMediaTestParams params;
params.test_duration = TimeDelta::Seconds(30);
params.callee_to_caller_path =
CreateNetworkPath(s, /*use_dual_pi= */ false,
DataRate::KilobitsPerSec(2000), TimeDelta::Millis(25));
params.caller_to_callee_path =
CreateNetworkPath(s, /*use_dual_pi= */ false,
DataRate::KilobitsPerSec(2000), TimeDelta::Millis(25));
SendMediaTestResult result = SendMediaInOneDirection(std::move(params), s);
EXPECT_THAT(result.caller().subspan(1), Each(AvailableSendBitrateIsBetween(
DataRate::KilobitsPerSec(1300),
DataRate::KilobitsPerSec(2600))));
}
TEST(ScreamTest, MaybeTest(LinkCapacity2MbpsRtt50msEcn)) {
PeerScenario s(*testing::UnitTest::GetInstance()->current_test_info());
SendMediaTestParams params;
params.test_duration = TimeDelta::Seconds(30);
params.callee_to_caller_path =
CreateNetworkPath(s, /*use_dual_pi= */ true,
DataRate::KilobitsPerSec(2000), TimeDelta::Millis(25));
params.caller_to_callee_path =
CreateNetworkPath(s, /*use_dual_pi= */ true,
DataRate::KilobitsPerSec(2000), TimeDelta::Millis(25));
SendMediaTestResult result = SendMediaInOneDirection(std::move(params), s);
EXPECT_THAT(result.caller().subspan(1), Each(AvailableSendBitrateIsBetween(
DataRate::KilobitsPerSec(1300),
DataRate::KilobitsPerSec(2300))));
}
TEST(ScreamTest, MaybeTest(LinkCapacity2MbpsRtt50msNoEcnWithGoogCC)) {
PeerScenario s(*testing::UnitTest::GetInstance()->current_test_info());
SendMediaTestParams params;
params.test_duration = TimeDelta::Seconds(30);
params.callee_to_caller_path =
CreateNetworkPath(s, /*use_dual_pi= */ false,
DataRate::KilobitsPerSec(2000), TimeDelta::Millis(25));
params.caller_to_callee_path =
CreateNetworkPath(s, /*use_dual_pi= */ false,
DataRate::KilobitsPerSec(2000), TimeDelta::Millis(25));
params.field_trials = {
{"WebRTC-RFC8888CongestionControlFeedback", "Enabled,offer:true"},
};
SendMediaTestResult result = SendMediaInOneDirection(std::move(params), s);
EXPECT_THAT(result.caller_stats.back(),
AvailableSendBitrateIsBetween(DataRate::KilobitsPerSec(1000),
DataRate::KilobitsPerSec(2600)));
}
TEST(ScreamTest, MaybeTest(LinkCapacity4MbpsRtt50ms10MsDelayStdDev)) {
PeerScenario s(*testing::UnitTest::GetInstance()->current_test_info());
NetworkEmulationManager::SimulatedNetworkNode::Builder network_builder(
s.net());
network_builder.delay_ms(25);
network_builder.delay_standard_deviation_ms(10);
network_builder.capacity_Mbps(4);
SendMediaTestParams params;
params.test_duration = TimeDelta::Seconds(30);
params.callee_to_caller_path =
CreateNetworkPath(network_builder, /*use_dual_pi= */ false);
params.caller_to_callee_path =
CreateNetworkPath(network_builder, /*use_dual_pi= */ false);
SendMediaTestResult result = SendMediaInOneDirection(std::move(params), s);
EXPECT_THAT(result.caller().subspan(1), Each(AvailableSendBitrateIsBetween(
DataRate::KilobitsPerSec(700),
DataRate::KilobitsPerSec(4100))));
}
TEST(ScreamTest, MaybeTest(LinkCapacity2MbpsRtt50msNoEcnWithTwcc)) {
PeerScenario s(*testing::UnitTest::GetInstance()->current_test_info());
SendMediaTestParams params;
params.test_duration = TimeDelta::Seconds(30);
params.callee_to_caller_path =
CreateNetworkPath(s, /*use_dual_pi= */ false,
DataRate::KilobitsPerSec(2000), TimeDelta::Millis(25));
params.caller_to_callee_path =
CreateNetworkPath(s, /*use_dual_pi= */ false,
DataRate::KilobitsPerSec(2000), TimeDelta::Millis(25));
params.field_trials = {
{"WebRTC-RFC8888CongestionControlFeedback", "Disabled"},
{"WebRTC-Bwe-ScreamV2", "mode:always"},
};
SendMediaTestResult result = SendMediaInOneDirection(std::move(params), s);
// BWE rampup is quite slow since feedback is only sent every 90ms
// approximately.
EXPECT_THAT(result.caller().subspan(5), Each(AvailableSendBitrateIsBetween(
DataRate::KilobitsPerSec(700),
DataRate::KilobitsPerSec(2600))));
}
TEST(ScreamTest, MaybeTest(CallerPauseSendingVideoIfFeedbackNotReceived)) {
PeerScenario s(*testing::UnitTest::GetInstance()->current_test_info());
SendMediaTestParams params;
params.callee_to_caller_path = CreateNetworkPathWithPauseBetween3sAnd6s(s);
params.caller_to_callee_path =
CreateNetworkPath(s, /*use_dual_pi= */ false,
DataRate::KilobitsPerSec(600), TimeDelta::Millis(50));
Timestamp start_time = s.net()->Now();
SendMediaTestResult result = SendMediaInOneDirection(std::move(params), s);
std::optional<scoped_refptr<const RTCStatsReport>> report_after_4s =
GetFirstReportAtOrAfter(start_time + TimeDelta::Seconds(4),
result.caller_stats);
std::optional<scoped_refptr<const RTCStatsReport>> report_after_5s =
GetFirstReportAtOrAfter(start_time + TimeDelta::Seconds(5),
result.caller_stats);
ASSERT_TRUE(report_after_4s.has_value());
ASSERT_TRUE(report_after_5s.has_value());
ASSERT_GT(GetPacketsSent(report_after_4s.value(), "video"), 0);
// Audio not paused.
EXPECT_LT(GetPacketsSent(report_after_4s.value()),
GetPacketsSent(report_after_5s.value()));
// video paused.
EXPECT_EQ(GetPacketsSent(report_after_4s.value(), "video"),
GetPacketsSent(report_after_5s.value(), "video"));
// video resumed.
EXPECT_LT(GetPacketsSent(report_after_4s.value(), "video"),
GetPacketsSent(result.caller_stats.back(), "video"));
// Target rate is within reason at the end of the call.
// TODO: bugs.webrtc.org/447037083 - There is no pushback between pacer queue
// encoder. If pacer queue is paused for too long, the pacer will send
// packets too fast.
// EXPECT_GT(GetAvailableSendBitrate(result.caller_stats.back()),
// DataRate::KilobitsPerSec(400));
EXPECT_LT(GetAvailableSendBitrate(result.caller_stats.back()),
DataRate::KilobitsPerSec(800));
}
TEST(ScreamTest,
MaybeTest(CallerResetQueueDelayEstimateAfterIncreasedFixedDelay)) {
PeerScenario s(*testing::UnitTest::GetInstance()->current_test_info());
SendMediaTestParams params{.test_duration = TimeDelta::Seconds(35)};
params.caller_to_callee_path = CreateNetworkPath1MbitDelayIncreaseAfter3S(s);
params.callee_to_caller_path =
CreateNetworkPath(s, /*use_dual_pi= */ false,
DataRate::KilobitsPerSec(2000), TimeDelta::Millis(10));
SendMediaTestResult result = SendMediaInOneDirection(std::move(params), s);
// After the increased delay, BWE should drop
EXPECT_LT(GetAvailableSendBitrate(result.caller_stats[5]),
DataRate::KilobitsPerSec(400));
// But have recovered by the end of the test.
EXPECT_THAT(result.caller_stats.back(),
AvailableSendBitrateIsBetween(DataRate::KilobitsPerSec(600),
DataRate::KilobitsPerSec(1200)));
}
TEST(ScreamTest, MaybeTest(ScreencastSlideChange2Mbit50msRttNoEcn)) {
PeerScenario s(*testing::UnitTest::GetInstance()->current_test_info());
SendMediaTestParams params{.test_duration = TimeDelta::Seconds(20)};
params.caller_to_callee_path =
CreateNetworkPath(s, /*use_dual_pi= */ false,
DataRate::KilobitsPerSec(2000), TimeDelta::Millis(25));
params.caller_to_callee_path = params.callee_to_caller_path =
CreateNetworkPath(s, /*use_dual_pi= */ false,
DataRate::KilobitsPerSec(2000), TimeDelta::Millis(25));
params.caller_video_conf = {
.generator = {.image_slides =
test::FrameGeneratorCapturerConfig::ImageSlides{
.change_interval = TimeDelta::Seconds(5)}}};
SendMediaTestResult result = SendMediaInOneDirection(std::move(params), s);
// Ignore estimate during rampup.
EXPECT_THAT(result.caller().subspan(1), Each(AvailableSendBitrateIsBetween(
DataRate::KilobitsPerSec(1200),
DataRate::KilobitsPerSec(2800))));
}
TEST(ScreamTest, MaybeTest(ScreencastSlideChangeRepeatedDelaySpikes)) {
PeerScenario s(*testing::UnitTest::GetInstance()->current_test_info());
SendMediaTestParams params{.test_duration = TimeDelta::Seconds(20)};
params.caller_to_callee_path = CreateNetworkPathWithRepeatedPause(
s, DataRate::KilobitsPerSec(4000), TimeDelta::Millis(500),
TimeDelta::Seconds(4));
params.callee_to_caller_path =
CreateNetworkPath(s, /*use_dual_pi= */ false,
DataRate::KilobitsPerSec(2000), TimeDelta::Millis(25));
params.caller_video_conf = {
.generator = {.image_slides =
test::FrameGeneratorCapturerConfig::ImageSlides{
.change_interval = TimeDelta::Seconds(5)}}};
SendMediaTestResult result = SendMediaInOneDirection(std::move(params), s);
EXPECT_THAT(result.caller(), Each(AvailableSendBitrateIsBetween(
DataRate::KilobitsPerSec(500),
DataRate::KilobitsPerSec(4300))));
}
TEST(ScreamTest, MaybeTest(LinkCapacity5MbitRepeatedDelaySpikesNoEcn)) {
PeerScenario s(*testing::UnitTest::GetInstance()->current_test_info());
SendMediaTestParams params{.test_duration = TimeDelta::Seconds(20)};
params.caller_to_callee_path = CreateNetworkPathWithRepeatedPause(
s, DataRate::KilobitsPerSec(5000), TimeDelta::Millis(100),
TimeDelta::Millis(200));
params.callee_to_caller_path = CreateNetworkPathWithRepeatedPause(
s, DataRate::KilobitsPerSec(5000), TimeDelta::Millis(100),
TimeDelta::Millis(200));
SendMediaTestResult result = SendMediaInOneDirection(std::move(params), s);
EXPECT_THAT(result.caller().subspan(1), Each(AvailableSendBitrateIsBetween(
DataRate::KilobitsPerSec(1200),
DataRate::KilobitsPerSec(5000))));
}
TEST(ScreamTest, MaybeTest(RampupFastOnLinkCapacity50Mbit20MsRttNoEcn)) {
PeerScenario s(*testing::UnitTest::GetInstance()->current_test_info());
SendMediaTestParams params{.test_duration = TimeDelta::Seconds(1),
.stats_interval = TimeDelta::Millis(100)};
params.caller_to_callee_path =
CreateNetworkPath(s, /*use_dual_pi= */ false,
DataRate::KilobitsPerSec(50000), TimeDelta::Millis(10));
params.callee_to_caller_path =
CreateNetworkPath(s, /*use_dual_pi= */ false,
DataRate::KilobitsPerSec(50000), TimeDelta::Millis(10));
SendMediaTestResult result = SendMediaInOneDirection(std::move(params), s);
// After 400ms.
EXPECT_THAT(result.caller_stats[3],
AvailableSendBitrateIsBetween(DataRate::KilobitsPerSec(1200),
DataRate::KilobitsPerSec(2500)));
}
TEST(ScreamTest, MaybeTest(LinkCapacity100Kbit50msRttNoEcn)) {
PeerScenario s(*testing::UnitTest::GetInstance()->current_test_info());
SendMediaTestParams params;
params.caller_to_callee_path =
CreateNetworkPath(s, /*use_dual_pi= */ false,
DataRate::KilobitsPerSec(100), TimeDelta::Millis(25));
params.callee_to_caller_path =
CreateNetworkPath(s, /*use_dual_pi= */ false,
DataRate::KilobitsPerSec(100), TimeDelta::Millis(25));
SendMediaTestResult result = SendMediaInOneDirection(std::move(params), s);
EXPECT_THAT(result.caller(), Each(AvailableSendBitrateIsBetween(
DataRate::KilobitsPerSec(10),
DataRate::KilobitsPerSec(150))));
}
TEST(ScreamTest, MaybeTest(LinkCapacity1MbitRtt50msWithShortQueuesNoEcn)) {
PeerScenario s(*testing::UnitTest::GetInstance()->current_test_info());
SendMediaTestParams params;
NetworkEmulationManager::SimulatedNetworkNode::Builder network_builder =
s.net()->NodeBuilder().capacity_Mbps(1).delay_ms(25);
params.callee_to_caller_path =
CreateNetworkPath(network_builder, /*use_dual_pi= */ false);
params.caller_to_callee_path = CreateNetworkPath(
network_builder.packet_queue_length(3), /*use_dual_pi= */ false);
SendMediaTestResult result = SendMediaInOneDirection(std::move(params), s);
EXPECT_THAT(
GetPacketsLost(result.callee_stats.back()) /
static_cast<double>(GetPacketsSent(result.caller_stats.back())),
Lt(0.05));
EXPECT_THAT(result.caller().subspan(1), Each(AvailableSendBitrateIsBetween(
DataRate::KilobitsPerSec(200),
DataRate::KilobitsPerSec(1100))));
}
TEST(ScreamTest,
MaybeTest(LinkCapacity1MbitRtt50msWith10PercentRandomLossNoEcn)) {
PeerScenario s(*testing::UnitTest::GetInstance()->current_test_info());
SendMediaTestParams params;
NetworkEmulationManager::SimulatedNetworkNode::Builder network_builder =
s.net()->NodeBuilder().capacity_Mbps(1).delay_ms(25);
params.callee_to_caller_path =
CreateNetworkPath(network_builder, /*use_dual_pi= */ false);
params.caller_to_callee_path =
CreateNetworkPath(network_builder.loss(0.1), /*use_dual_pi= */ false);
SendMediaTestResult result = SendMediaInOneDirection(std::move(params), s);
ASSERT_GE(GetPacketsLost(result.callee_stats.back()),
0.05 * GetPacketsSent(result.caller_stats.back()));
// Ignore estimate during rampup.
EXPECT_THAT(result.caller().subspan(1), Each(AvailableSendBitrateIsBetween(
DataRate::KilobitsPerSec(100),
DataRate::KilobitsPerSec(1100))));
}
TEST(ScreamTest, MaybeTest(ReturnLinkWithBurstLoss)) {
PeerScenario s(*testing::UnitTest::GetInstance()->current_test_info());
SendMediaTestParams params{.test_duration = TimeDelta::Seconds(20)};
NetworkEmulationManager::SimulatedNetworkNode::Builder network_builder =
s.net()->NodeBuilder().capacity_Mbps(1).delay_ms(25);
params.caller_to_callee_path =
CreateNetworkPath(network_builder, /*use_dual_pi= */ false);
params.callee_to_caller_path =
CreateNetworkPath(network_builder.loss(0.2).avg_burst_loss_length(3),
/*use_dual_pi= */ false);
SendMediaTestResult result = SendMediaInOneDirection(std::move(params), s);
// Audio packets are sent even if congestion window is full and ensures
// feedback is eventually received even if feedback packets are lost.
EXPECT_GT(GetPacketsSent(result.caller_stats.back()),
GetPacketsSent(result.caller_stats[5]));
EXPECT_THAT(result.caller().subspan(1), Each(AvailableSendBitrateIsBetween(
DataRate::KilobitsPerSec(300),
DataRate::KilobitsPerSec(1300))));
}
TEST(ScreamTest, MaybeTest(SendVideoOnlyReturnLinkWithBurstLoss)) {
PeerScenario s(*testing::UnitTest::GetInstance()->current_test_info());
SendMediaTestParams params;
params.send_audio = false;
NetworkEmulationManager::SimulatedNetworkNode::Builder network_builder =
s.net()->NodeBuilder().capacity_Mbps(1).delay_ms(25);
params.send_audio = false;
params.caller_to_callee_path =
CreateNetworkPath(network_builder, /*use_dual_pi= */ false);
params.callee_to_caller_path =
CreateNetworkPath(network_builder.loss(0.2).avg_burst_loss_length(3),
/*use_dual_pi= */ false);
SendMediaTestResult result = SendMediaInOneDirection(std::move(params), s);
// Keep alive packets are used for ensuring feedback is eventually received
// even if feedback packets are lost. Due to that the pacer pace out all
// packets to fast if queued too long, BWE drop to a very low value.
EXPECT_GT(GetPacketsSent(result.caller_stats.back()),
GetPacketsSent(result.caller_stats[5]));
EXPECT_THAT(result.caller().subspan(1), Each(AvailableSendBitrateIsBetween(
DataRate::KilobitsPerSec(10),
DataRate::KilobitsPerSec(1200))));
}
// Test that Scream adapt to a link with traffic policing on the network path
// from caller to calee.
TEST(ScreamTest, MaybeTest(LinkCapacity5MbitPolicedTo256Kbit)) {
PeerScenario s(*testing::UnitTest::GetInstance()->current_test_info());
SendMediaTestParams params;
NetworkEmulationManager::SimulatedNetworkNode::Builder network_builder =
s.net()->NodeBuilder().capacity_Mbps(5).delay_ms(25);
params.caller_to_callee_path = {
s.net()->NodeBuilder().capacity_Mbps(5).delay_ms(25).Build().node,
TokenBucketNetworkBehaviorNodeBuilder(s.net())
.burst(DataSize::Bytes(16384)) // 0.5s at 256kbps.
.rate(DataRate::KilobitsPerSec(256))
.Build()};
params.callee_to_caller_path =
CreateNetworkPath(network_builder, /*use_dual_pi= */ false);
SendMediaTestResult result = SendMediaInOneDirection(std::move(params), s);
EXPECT_THAT(
GetPacketsLost(result.callee_stats.back()) /
static_cast<double>(GetPacketsSent(result.caller_stats.back())),
AllOf(Lt(0.08), Gt(0.01)));
EXPECT_THAT(result.caller().subspan(1), Each(AvailableSendBitrateIsBetween(
DataRate::KilobitsPerSec(150),
DataRate::KilobitsPerSec(900))));
}
TEST(ScreamTest, MaybeTest(LinkCapacity5MbitWithCrossTrafficNoEcn)) {
PeerScenario s(*testing::UnitTest::GetInstance()->current_test_info());
SendMediaTestParams params;
params.test_duration = TimeDelta::Seconds(30);
params.caller_to_callee_path =
CreateNetworkPath(s, /*use_dual_pi= */ false,
DataRate::KilobitsPerSec(5000), TimeDelta::Millis(25));
params.callee_to_caller_path =
CreateNetworkPath(s, /*use_dual_pi= */ false,
DataRate::KilobitsPerSec(5000), TimeDelta::Millis(25));
// Simulate a file upload on the path from caller to calee.
webrtc::TcpMessageRoute* tcp_route = s.net()->CreateTcpRoute(
s.net()->CreateRoute({params.caller_to_callee_path}),
s.net()->CreateRoute({params.callee_to_caller_path}));
Timestamp start_time = s.net()->Now();
Timestamp tcp_message_delivered_time = Timestamp::MinusInfinity();
s.net()->time_controller()->GetMainThread()->PostDelayedTask(
[&]() {
tcp_route->SendMessage(
/*size=*/2'000'000,
/*on_received=*/[&tcp_message_delivered_time, &s]() {
tcp_message_delivered_time = s.net()->Now();
});
},
TimeDelta::Seconds(3));
SendMediaTestResult result = SendMediaInOneDirection(std::move(params), s);
ASSERT_TRUE(tcp_message_delivered_time.IsFinite());
int index_where_available_bitrate_should_have_recovered =
(tcp_message_delivered_time - start_time).seconds<int>() + 10;
EXPECT_THAT(
result.caller().subspan(
index_where_available_bitrate_should_have_recovered),
Each(AvailableSendBitrateIsBetween(DataRate::KilobitsPerSec(1500),
DataRate::KilobitsPerSec(5000))));
}
} // namespace
} // namespace webrtc