blob: 2e4b966ac7eb80d69ee198a0eb6c23a7cb0854b8 [file]
/*
* 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 <cstddef>
#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::GetAveragePacingDelay;
using test::GetCurrentRoundTripTime;
using test::GetFirstReportAtOrAfter;
using test::GetPacketsLost;
using test::GetPacketsReceived;
using test::GetPacketsReceivedWithEct1;
using test::GetPacketsSent;
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(AvailableSendBitrateIsZeroOrBetween, low, high, "") {
DataRate available_bwe = GetAvailableSendBitrate(arg);
if (available_bwe == DataRate::Zero() ||
(available_bwe > low && available_bwe < high)) {
return true;
}
*result_listener << "the available send bitrate is " << available_bwe.kbps()
<< "kbps, which is neither 0 nor 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 neither 0 nor between " << low.kbps()
<< "kbps and " << high.kbps() << " kbps";
return true;
}
MATCHER_P2(AveragePacingDelayIsBelow, max_delay, previous_report, "") {
TimeDelta pacing_delay = GetAveragePacingDelay(arg, previous_report);
if (pacing_delay < max_delay) {
return true;
}
*result_listener << "the pacing delay is " << pacing_delay.ms()
<< "ms, which is not below " << max_delay.ms() << "ms";
return false;
}
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*> CreateNetworkPathWithPause(
PeerScenario& s,
DataRate link_capacity,
TimeDelta pause_start,
TimeDelta pause_duration) {
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(pause_start.ms());
updated_capacity->set_link_capacity_kbps(0);
updated_capacity = schedule.add_item();
updated_capacity->set_time_since_first_sent_packet_ms(
(pause_start + pause_duration).ms());
updated_capacity->set_link_capacity_kbps(link_capacity.kbps());
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()};
}
std::vector<EmulatedNetworkNode*> CreateNetworkPathWithVaryingCapacity(
PeerScenario& s,
DataRate link_capacity_1 = DataRate::KilobitsPerSec(40),
DataRate link_capacity_2 = DataRate::KilobitsPerSec(90),
TimeDelta interval = TimeDelta::Millis(800),
TimeDelta one_way_delay = TimeDelta::Millis(100)) {
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(one_way_delay.ms());
auto updated_capacity = schedule.add_item();
updated_capacity->set_time_since_first_sent_packet_ms(interval.ms());
updated_capacity->set_link_capacity_kbps(link_capacity_2.kbps());
updated_capacity->set_queue_delay_ms(one_way_delay.ms());
schedule.set_repeat_schedule_after_last_ms(interval.ms());
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,feedback_fraction:0.10"},
{"WebRTC-Bwe-ScreamV2", "Enabled"}};
// Audio is negotiated and sent.
bool send_audio = true;
// Video is negotiated and sent.
bool send_video = true;
// Video capturer is producing frames.
bool video_capture_enabled = true;
// Media is sent in both directions (caller and callee).
bool send_in_two_directions = false;
// Adaptive frame length allow audio to adapt to BWE.
bool adaptive_ptime = false;
// 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 media between a caller and a callee.
SendMediaTestResult SendMedia(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) {
PeerScenarioClient::AudioSendTrack audio =
caller->CreateAudio("AUDIO_1", {});
if (params.adaptive_ptime) {
RtpParameters caller_audio_params = audio.sender->GetParameters();
for (auto& encoding : caller_audio_params.encodings) {
// Adaptive frame length allow audio to adapt to BWE.
encoding.adaptive_ptime = true;
}
audio.sender->SetParameters(caller_audio_params);
}
}
if (params.send_video) {
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);
}
}
if (params.send_in_two_directions) {
if (params.send_audio) {
PeerScenarioClient::AudioSendTrack audio =
callee->CreateAudio("AUDIO_2", {});
if (params.adaptive_ptime) {
RtpParameters callee_audio_params = audio.sender->GetParameters();
for (auto& encoding : callee_audio_params.encodings) {
// Adaptive frame length allow audio to adapt to BWE.
encoding.adaptive_ptime = true;
}
audio.sender->SetParameters(callee_audio_params);
}
}
if (params.send_video) {
test::PeerScenarioClient::VideoSendTrack video_track =
callee->CreateVideo("VIDEO_2", 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 = SendMedia(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 = SendMedia(std::move(params), s);
EXPECT_THAT(
result.caller().subspan(0, 4),
Each(AvailableSendBitrateIsBetween(DataRate::KilobitsPerSec(200),
DataRate::KilobitsPerSec(700))));
EXPECT_THAT(result.caller().subspan(4), Each(AvailableSendBitrateIsBetween(
DataRate::KilobitsPerSec(400),
DataRate::KilobitsPerSec(650))));
}
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 = SendMedia(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 = SendMedia(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 = SendMedia(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 = SendMedia(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(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 = SendMedia(std::move(params), s);
// Ignore result first 2s since ramp up is rather slow at higher RTT.
ASSERT_GT(result.caller().subspan(2).size(), 0u);
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 = SendMedia(std::move(params), s);
EXPECT_THAT(result.caller().subspan(1), Each(AvailableSendBitrateIsBetween(
DataRate::KilobitsPerSec(600),
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 = SendMedia(std::move(params), s);
EXPECT_THAT(result.caller().subspan(1), Each(AvailableSendBitrateIsBetween(
DataRate::KilobitsPerSec(1300),
DataRate::KilobitsPerSec(2500))));
}
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 = SendMedia(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 = SendMedia(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 = SendMedia(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 = SendMedia(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 = SendMedia(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"));
std::optional<scoped_refptr<const RTCStatsReport>> report_after_6s =
GetFirstReportAtOrAfter(start_time + TimeDelta::Seconds(6),
result.caller_stats);
std::optional<scoped_refptr<const RTCStatsReport>> report_after_7s =
GetFirstReportAtOrAfter(start_time + TimeDelta::Seconds(7),
result.caller_stats);
std::optional<scoped_refptr<const RTCStatsReport>> report_after_8s =
GetFirstReportAtOrAfter(start_time + TimeDelta::Seconds(8),
result.caller_stats);
ASSERT_TRUE(report_after_6s.has_value());
ASSERT_TRUE(report_after_7s.has_value());
ASSERT_TRUE(report_after_8s.has_value());
// Video encoder target rate is set to zero while congested and pacer queue
// delay exceeds max pushback delay.
EXPECT_EQ(GetAvailableSendBitrate(report_after_6s.value()), DataRate::Zero());
// Because the encoder was paused, the pacer queue was bounded and quickly
// drained upon resumption (~6s). By 8s, average pacing delay is back to
// normal (< 50 ms).
EXPECT_LT(
GetAveragePacingDelay(report_after_8s.value(), report_after_7s.value()),
TimeDelta::Millis(50));
// Target rate is within reason at the end of the call.
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 = SendMedia(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 = SendMedia(std::move(params), s);
// Ignore estimate during rampup.
EXPECT_THAT(result.caller().subspan(1), Each(AvailableSendBitrateIsBetween(
DataRate::KilobitsPerSec(1200),
DataRate::KilobitsPerSec(2500))));
}
TEST(ScreamTest, MaybeTest(ScreencastSlideChange500Kbit30msRttNoEcn)) {
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(500), TimeDelta::Millis(15));
params.caller_to_callee_path = params.callee_to_caller_path =
CreateNetworkPath(s, /*use_dual_pi= */ false,
DataRate::KilobitsPerSec(500), TimeDelta::Millis(15));
params.caller_video_conf = {
.generator = {.image_slides =
test::FrameGeneratorCapturerConfig::ImageSlides{
.change_interval = TimeDelta::Seconds(3)}}};
SendMediaTestResult result = SendMedia(std::move(params), s);
// Ignore estimate during rampup.
EXPECT_THAT(result.caller().subspan(1), Each(AvailableSendBitrateIsBetween(
DataRate::KilobitsPerSec(200),
DataRate::KilobitsPerSec(700))));
}
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 = SendMedia(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 = SendMedia(std::move(params), s);
EXPECT_THAT(result.caller().subspan(1), Each(AvailableSendBitrateIsBetween(
DataRate::KilobitsPerSec(1200),
DataRate::KilobitsPerSec(5000))));
}
TEST(ScreamTest, MaybeTest(LinkCapacity2MbitRepeatedDelaySpikesNoEcn)) {
PeerScenario s(*testing::UnitTest::GetInstance()->current_test_info());
SendMediaTestParams params{.test_duration = TimeDelta::Seconds(20),
.stats_interval = TimeDelta::Millis(200)};
params.caller_to_callee_path = CreateNetworkPathWithRepeatedPause(
s, DataRate::KilobitsPerSec(2000), TimeDelta::Millis(100),
TimeDelta::Millis(200));
params.callee_to_caller_path = CreateNetworkPathWithRepeatedPause(
s, DataRate::KilobitsPerSec(2000), TimeDelta::Millis(100),
TimeDelta::Millis(200));
SendMediaTestResult result = SendMedia(std::move(params), s);
// Ignore BWE the first second.
EXPECT_THAT(result.caller().subspan(5), Each(AvailableSendBitrateIsBetween(
DataRate::KilobitsPerSec(550),
DataRate::KilobitsPerSec(2000))));
// Ensure average pacing delay between two reports is below 70ms.
for (size_t i = 1; i < result.caller_stats.size(); ++i) {
EXPECT_THAT(result.caller_stats[i],
AveragePacingDelayIsBelow(TimeDelta::Millis(70),
result.caller_stats[i - 1]));
}
}
TEST(ScreamTest, MaybeTest(PacerDelayIsAcceptableAfterSingleDelaySpike)) {
PeerScenario s(*testing::UnitTest::GetInstance()->current_test_info());
SendMediaTestParams params{.test_duration = TimeDelta::Seconds(10),
.stats_interval = TimeDelta::Millis(200)};
params.caller_to_callee_path =
CreateNetworkPathWithPause(s, DataRate::KilobitsPerSec(1000),
TimeDelta::Seconds(5), TimeDelta::Millis(500));
params.callee_to_caller_path =
CreateNetworkPathWithPause(s, DataRate::KilobitsPerSec(1000),
TimeDelta::Seconds(5), TimeDelta::Millis(500));
SendMediaTestResult result = SendMedia(std::move(params), s);
// Ensure average pacing delay between two reports stays below 1000ms.
for (size_t i = 1; i < result.caller_stats.size(); ++i) {
EXPECT_THAT(result.caller_stats[i],
AveragePacingDelayIsBelow(TimeDelta::Millis(1000),
result.caller_stats[i - 1]));
}
}
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 = SendMedia(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 = SendMedia(std::move(params), s);
// In the beginning, the initial video frame burst at 100 kbps link capacity
// can build up a pacer queue > 500 ms while congested, causing the encoder to
// temporarily pause (target rate = 0). Once the pacer drains (after ~3.5s),
// bitrate stabilizes between 10 kbps and 150 kbps.
EXPECT_THAT(result.caller().subspan(4), 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 = SendMedia(std::move(params), s);
EXPECT_THAT(
GetPacketsLost(result.callee_stats.back()) /
static_cast<double>(GetPacketsSent(result.caller_stats.back())),
Lt(0.07));
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 = SendMedia(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(LowBweOnLinkWith5PercentUniformLoss)) {
PeerScenario s(*testing::UnitTest::GetInstance()->current_test_info());
SendMediaTestParams params;
NetworkEmulationManager::SimulatedNetworkNode::Builder network_builder =
s.net()->NodeBuilder().capacity_Mbps(3).delay_ms(40);
params.callee_to_caller_path =
CreateNetworkPath(network_builder, /*use_dual_pi= */ false);
params.caller_to_callee_path =
CreateNetworkPath(network_builder.loss(0.05), /*use_dual_pi= */ false);
SendMediaTestResult result = SendMedia(std::move(params), s);
ASSERT_GE(GetPacketsLost(result.callee_stats.back()),
0.02 * GetPacketsSent(result.caller_stats.back()));
// Ignore estimate during rampup.
ASSERT_GT(result.caller().subspan(2).size(), 0u);
EXPECT_THAT(result.caller().subspan(2), Each(AvailableSendBitrateIsBetween(
DataRate::KilobitsPerSec(100),
DataRate::KilobitsPerSec(1800))));
}
TEST(ScreamTest, MaybeTest(Ignores1PercentUniformLossOnLinkWith80msRtt)) {
PeerScenario s(*testing::UnitTest::GetInstance()->current_test_info());
SendMediaTestParams params;
NetworkEmulationManager::SimulatedNetworkNode::Builder network_builder =
s.net()->NodeBuilder().capacity_Mbps(3).delay_ms(40);
params.callee_to_caller_path =
CreateNetworkPath(network_builder, /*use_dual_pi= */ false);
params.caller_to_callee_path =
CreateNetworkPath(network_builder.loss(0.01), /*use_dual_pi= */ false);
SendMediaTestResult result = SendMedia(std::move(params), s);
ASSERT_GE(GetPacketsLost(result.callee_stats.back()),
0.005 * GetPacketsSent(result.caller_stats.back()));
// Ignore estimate during rampup.
ASSERT_GT(result.caller().subspan(2).size(), 0u);
EXPECT_THAT(result.caller().subspan(2), Each(AvailableSendBitrateIsBetween(
DataRate::KilobitsPerSec(1000),
DataRate::KilobitsPerSec(3000))));
}
TEST(ScreamTest, MaybeTest(IgnoresPacketReorderingOnLinkWith80msRtt)) {
PeerScenario s(*testing::UnitTest::GetInstance()->current_test_info());
SendMediaTestParams params;
NetworkEmulationManager::SimulatedNetworkNode::Builder network_builder =
s.net()->NodeBuilder().capacity_Mbps(3).delay_ms(40);
params.callee_to_caller_path =
CreateNetworkPath(network_builder, /*use_dual_pi= */ false);
params.caller_to_callee_path = CreateNetworkPath(
network_builder.allow_reordering().delay_standard_deviation_ms(5),
/*use_dual_pi= */ false);
SendMediaTestResult result = SendMedia(std::move(params), s);
// Ignore estimate during rampup.
ASSERT_GT(result.caller().subspan(2).size(), 0u);
EXPECT_THAT(result.caller().subspan(2), Each(AvailableSendBitrateIsBetween(
DataRate::KilobitsPerSec(1000),
DataRate::KilobitsPerSec(3000))));
}
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 = SendMedia(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]));
ASSERT_GT(result.caller().subspan(1).size(), 0u);
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.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 = SendMedia(std::move(params), s);
// Keep alive packets ensure feedback is eventually received even if feedback
// packets are lost. When feedback is not received, the pacer queue builds up
// and the video encoder is paused (BWE drops to zero) to prevent queue bloat.
EXPECT_GT(GetPacketsSent(result.caller_stats.back()),
GetPacketsSent(result.caller_stats[5]));
int zero_bwe_count = 0;
for (const auto& report : result.caller().subspan(1)) {
if (GetAvailableSendBitrate(report) == DataRate::Zero()) {
++zero_bwe_count;
}
}
// BWE can drop to zero when feedback is lost and the encoder is paused, but
// should not remain zero throughout the call.
EXPECT_LT(zero_bwe_count,
static_cast<int>(result.caller().subspan(1).size()));
ASSERT_GT(result.caller().subspan(1).size(), 0u);
EXPECT_THAT(
result.caller().subspan(1),
Each(AvailableSendBitrateIsZeroOrBetween(
DataRate::KilobitsPerSec(300), 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 = SendMedia(std::move(params), s);
EXPECT_THAT(
GetPacketsLost(result.callee_stats.back()) /
static_cast<double>(GetPacketsSent(result.caller_stats.back())),
AllOf(Lt(0.11), 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 = SendMedia(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))));
}
TEST(ScreamTest,
MaybeTest(AudioOnlyBothDirectionsLinkCapacity40To90KbpsRtt200ms)) {
PeerScenario s(*testing::UnitTest::GetInstance()->current_test_info());
SendMediaTestParams params;
params.send_audio = true;
params.send_video = false;
params.adaptive_ptime = true;
params.send_in_two_directions = true;
params.test_duration = TimeDelta::Seconds(20);
params.caller_to_callee_path = CreateNetworkPathWithVaryingCapacity(
s, DataRate::KilobitsPerSec(30), DataRate::KilobitsPerSec(150),
TimeDelta::Millis(800), TimeDelta::Millis(100));
params.callee_to_caller_path = CreateNetworkPathWithVaryingCapacity(
s, DataRate::KilobitsPerSec(30), DataRate::KilobitsPerSec(150),
TimeDelta::Millis(800), TimeDelta::Millis(100));
SendMediaTestResult result = SendMedia(std::move(params), s);
ASSERT_GT(result.caller().subspan(2).size(), 0u);
EXPECT_THAT(result.caller().subspan(2),
Each(CurrentRoundTripTimeIsBetween(TimeDelta::Millis(180),
TimeDelta::Millis(500))));
ASSERT_GT(result.callee().subspan(2).size(), 0u);
EXPECT_THAT(result.callee().subspan(2),
Each(CurrentRoundTripTimeIsBetween(TimeDelta::Millis(180),
TimeDelta::Millis(500))));
EXPECT_THAT(result.caller().subspan(2),
Each(AvailableSendBitrateIsBetween(
DataRate::KilobitsPerSec(5), DataRate::KilobitsPerSec(60))));
EXPECT_THAT(result.callee().subspan(2),
Each(AvailableSendBitrateIsBetween(
DataRate::KilobitsPerSec(5), DataRate::KilobitsPerSec(60))));
}
} // namespace
} // namespace webrtc