Add support for time-varying constraints in DegradedCall.

The fake network configs are now specified using just two field trials:
WebRTC-FakeNetworkSendConfig and WebRTC-FakeNetworkReceiveConfig.
Both of them have the following parameters from
BuiltInNetworkBehaviorConfig:

* queue_length_packets  // Queue length in number of packets.
* queue_delay_ms  // Delay in addition to capacity induced delay.
* delay_standard_deviation_ms   // Standard deviation of the extra delay.
* link_capacity_kbps  // Link capacity in kbps.
* loss_percent  // Random packet loss.
* allow_reordering  // If packets are allowed to be reordered.
* avg_burst_loss_length  // The average length of a burst of lost packets.
* packet_overhead  // Additional bytes to add to packet size.
* codel_active_queue_management  // Enable CoDel active queue management.

Plus:
* duration  // For how long to use this config before progressing.

Example:
WebRTC-FakeNetworkSendConfig/queue_delay_ms:66|1,loss_percent:1|0,link_capacity_kbps:200|10000,queue_length_packets:10|100,duration:15s|45s/
This creates two configs:
1. For 15s, apply 66ms delay, 1% loss, 200kbps bandwidth, 10 packet queue size
2. For 45s, apply 1ms delay, 0% loss, 10Mbps bandwidth, 100 packets queue size
(then repeat)

Bug: webrtc:13655
Change-Id: I0524f572de480731df4d414724203772182c628b
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/251043
Reviewed-by: Stefan Holmer <holmer@google.com>
Commit-Queue: Erik Språng <sprang@webrtc.org>
Cr-Commit-Position: refs/heads/main@{#35952}
This commit is contained in:
Erik Språng 2022-02-08 12:35:46 +01:00 committed by WebRTC LUCI CQ
parent 27c1452fae
commit b5cba85c2f
3 changed files with 154 additions and 44 deletions

View file

@ -15,26 +15,34 @@
#include <memory>
#include <string>
#include <utility>
#include <vector>
#include "absl/types/optional.h"
#include "api/test/simulated_network.h"
#include "api/units/time_delta.h"
#include "call/call.h"
#include "call/degraded_call.h"
#include "call/rtp_transport_config.h"
#include "rtc_base/checks.h"
#include "system_wrappers/include/field_trial.h"
#include "rtc_base/experiments/field_trial_list.h"
#include "rtc_base/experiments/field_trial_parser.h"
namespace webrtc {
namespace {
bool ParseConfigParam(std::string exp_name, int* field) {
std::string group = field_trial::FindFullName(exp_name);
using TimeScopedNetworkConfig = DegradedCall::TimeScopedNetworkConfig;
bool ParseConfigParam(const WebRtcKeyValueConfig& trials,
absl::string_view exp_name,
int* field) {
std::string group = trials.Lookup(exp_name);
if (group.empty())
return false;
return (sscanf(group.c_str(), "%d", field) == 1);
}
absl::optional<webrtc::BuiltInNetworkBehaviorConfig> ParseDegradationConfig(
absl::optional<TimeScopedNetworkConfig> ParseDegradationConfig(
const WebRtcKeyValueConfig& trials,
bool send) {
std::string exp_prefix = "WebRTCFakeNetwork";
if (send) {
@ -43,33 +51,92 @@ absl::optional<webrtc::BuiltInNetworkBehaviorConfig> ParseDegradationConfig(
exp_prefix += "Receive";
}
webrtc::BuiltInNetworkBehaviorConfig config;
TimeScopedNetworkConfig config;
bool configured = false;
configured |=
ParseConfigParam(exp_prefix + "DelayMs", &config.queue_delay_ms);
configured |= ParseConfigParam(exp_prefix + "DelayStdDevMs",
ParseConfigParam(trials, exp_prefix + "DelayMs", &config.queue_delay_ms);
configured |= ParseConfigParam(trials, exp_prefix + "DelayStdDevMs",
&config.delay_standard_deviation_ms);
int queue_length = 0;
if (ParseConfigParam(exp_prefix + "QueueLength", &queue_length)) {
if (ParseConfigParam(trials, exp_prefix + "QueueLength", &queue_length)) {
RTC_CHECK_GE(queue_length, 0);
config.queue_length_packets = queue_length;
configured = true;
}
configured |=
ParseConfigParam(exp_prefix + "CapacityKbps", &config.link_capacity_kbps);
configured |=
ParseConfigParam(exp_prefix + "LossPercent", &config.loss_percent);
configured |= ParseConfigParam(trials, exp_prefix + "CapacityKbps",
&config.link_capacity_kbps);
configured |= ParseConfigParam(trials, exp_prefix + "LossPercent",
&config.loss_percent);
int allow_reordering = 0;
if (ParseConfigParam(exp_prefix + "AllowReordering", &allow_reordering)) {
if (ParseConfigParam(trials, exp_prefix + "AllowReordering",
&allow_reordering)) {
config.allow_reordering = true;
configured = true;
}
configured |= ParseConfigParam(exp_prefix + "AvgBurstLossLength",
configured |= ParseConfigParam(trials, exp_prefix + "AvgBurstLossLength",
&config.avg_burst_loss_length);
return configured
? absl::optional<webrtc::BuiltInNetworkBehaviorConfig>(config)
return configured ? absl::optional<TimeScopedNetworkConfig>(config)
: absl::nullopt;
}
std::vector<TimeScopedNetworkConfig> GetNetworkConfigs(
const WebRtcKeyValueConfig& trials,
bool send) {
FieldTrialStructList<TimeScopedNetworkConfig> trials_list(
{FieldTrialStructMember("queue_length_packets",
[](TimeScopedNetworkConfig* p) {
// FieldTrialParser does not natively support
// size_t type, so use this ugly cast as
// workaround.
return reinterpret_cast<unsigned*>(
&p->queue_length_packets);
}),
FieldTrialStructMember(
"queue_delay_ms",
[](TimeScopedNetworkConfig* p) { return &p->queue_delay_ms; }),
FieldTrialStructMember("delay_standard_deviation_ms",
[](TimeScopedNetworkConfig* p) {
return &p->delay_standard_deviation_ms;
}),
FieldTrialStructMember(
"link_capacity_kbps",
[](TimeScopedNetworkConfig* p) { return &p->link_capacity_kbps; }),
FieldTrialStructMember(
"loss_percent",
[](TimeScopedNetworkConfig* p) { return &p->loss_percent; }),
FieldTrialStructMember(
"allow_reordering",
[](TimeScopedNetworkConfig* p) { return &p->allow_reordering; }),
FieldTrialStructMember("avg_burst_loss_length",
[](TimeScopedNetworkConfig* p) {
return &p->avg_burst_loss_length;
}),
FieldTrialStructMember(
"packet_overhead",
[](TimeScopedNetworkConfig* p) { return &p->packet_overhead; }),
FieldTrialStructMember("codel_active_queue_management",
[](TimeScopedNetworkConfig* p) {
return &p->codel_active_queue_management;
}),
FieldTrialStructMember(
"duration",
[](TimeScopedNetworkConfig* p) { return &p->duration; })},
{});
ParseFieldTrial({&trials_list},
trials.Lookup(send ? "WebRTC-FakeNetworkSendConfig"
: "WebRTC-FakeNetworkReceiveConfig"));
std::vector<TimeScopedNetworkConfig> configs = trials_list.Get();
if (configs.empty()) {
// Try legacy fallback trials.
absl::optional<DegradedCall::TimeScopedNetworkConfig> fallback_config =
ParseDegradationConfig(trials, send);
if (fallback_config.has_value()) {
configs.push_back(*fallback_config);
}
}
return configs;
}
} // namespace
CallFactory::CallFactory() {
@ -78,14 +145,18 @@ CallFactory::CallFactory() {
Call* CallFactory::CreateCall(const Call::Config& config) {
RTC_DCHECK_RUN_ON(&call_thread_);
absl::optional<webrtc::BuiltInNetworkBehaviorConfig> send_degradation_config =
ParseDegradationConfig(true);
absl::optional<webrtc::BuiltInNetworkBehaviorConfig>
receive_degradation_config = ParseDegradationConfig(false);
RTC_DCHECK(config.trials);
std::vector<DegradedCall::TimeScopedNetworkConfig> send_degradation_configs =
GetNetworkConfigs(*config.trials, /*send=*/true);
std::vector<DegradedCall::TimeScopedNetworkConfig>
receive_degradation_configs =
GetNetworkConfigs(*config.trials, /*send=*/false);
RtpTransportConfig transportConfig = config.ExtractTransportConfig();
if (send_degradation_config || receive_degradation_config) {
if (!send_degradation_configs.empty() ||
!receive_degradation_configs.empty()) {
return new DegradedCall(
std::unique_ptr<Call>(Call::Create(
config, Clock::GetRealTimeClock(),
@ -94,7 +165,7 @@ Call* CallFactory::CreateCall(const Call::Config& config) {
config.rtp_transport_controller_send_factory->Create(
transportConfig, Clock::GetRealTimeClock(),
ProcessThread::Create("PacerThread")))),
send_degradation_config, receive_degradation_config,
send_degradation_configs, receive_degradation_configs,
config.task_queue_factory);
}

View file

@ -127,27 +127,39 @@ bool DegradedCall::FakeNetworkPipeTransportAdapter::SendRtcp(
DegradedCall::DegradedCall(
std::unique_ptr<Call> call,
absl::optional<BuiltInNetworkBehaviorConfig> send_config,
absl::optional<BuiltInNetworkBehaviorConfig> receive_config,
const std::vector<TimeScopedNetworkConfig>& send_configs,
const std::vector<TimeScopedNetworkConfig>& receive_configs,
TaskQueueFactory* task_queue_factory)
: clock_(Clock::GetRealTimeClock()),
call_(std::move(call)),
task_queue_factory_(task_queue_factory),
send_config_(send_config),
send_config_index_(0),
send_configs_(send_configs),
send_simulated_network_(nullptr),
receive_config_(receive_config) {
if (receive_config_) {
auto network = std::make_unique<SimulatedNetwork>(*receive_config_);
receive_config_index_(0),
receive_configs_(receive_configs) {
if (!receive_configs_.empty()) {
auto network = std::make_unique<SimulatedNetwork>(receive_configs_[0]);
receive_simulated_network_ = network.get();
receive_pipe_ =
std::make_unique<webrtc::FakeNetworkPipe>(clock_, std::move(network));
receive_pipe_->SetReceiver(call_->Receiver());
if (receive_configs_.size() > 1) {
call_->network_thread()->PostDelayedTask(
ToQueuedTask(task_safety_, [this] { UpdateReceiveNetworkConfig(); }),
receive_configs_[0].duration.ms());
}
if (send_config_) {
auto network = std::make_unique<SimulatedNetwork>(*send_config_);
}
if (!send_configs_.empty()) {
auto network = std::make_unique<SimulatedNetwork>(send_configs_[0]);
send_simulated_network_ = network.get();
send_pipe_ = std::make_unique<FakeNetworkPipeOnTaskQueue>(
task_queue_factory_, clock_, std::move(network));
if (send_configs_.size() > 1) {
call_->network_thread()->PostDelayedTask(
ToQueuedTask(task_safety_, [this] { UpdateSendNetworkConfig(); }),
send_configs_[0].duration.ms());
}
}
}
@ -155,7 +167,7 @@ DegradedCall::~DegradedCall() = default;
AudioSendStream* DegradedCall::CreateAudioSendStream(
const AudioSendStream::Config& config) {
if (send_config_) {
if (!send_configs_.empty()) {
auto transport_adapter = std::make_unique<FakeNetworkPipeTransportAdapter>(
send_pipe_.get(), call_.get(), clock_, config.send_transport);
AudioSendStream::Config degrade_config = config;
@ -189,7 +201,7 @@ VideoSendStream* DegradedCall::CreateVideoSendStream(
VideoSendStream::Config config,
VideoEncoderConfig encoder_config) {
std::unique_ptr<FakeNetworkPipeTransportAdapter> transport_adapter;
if (send_config_) {
if (!send_configs_.empty()) {
transport_adapter = std::make_unique<FakeNetworkPipeTransportAdapter>(
send_pipe_.get(), call_.get(), clock_, config.send_transport);
config.send_transport = transport_adapter.get();
@ -207,7 +219,7 @@ VideoSendStream* DegradedCall::CreateVideoSendStream(
VideoEncoderConfig encoder_config,
std::unique_ptr<FecController> fec_controller) {
std::unique_ptr<FakeNetworkPipeTransportAdapter> transport_adapter;
if (send_config_) {
if (!send_configs_.empty()) {
transport_adapter = std::make_unique<FakeNetworkPipeTransportAdapter>(
send_pipe_.get(), call_.get(), clock_, config.send_transport);
config.send_transport = transport_adapter.get();
@ -251,7 +263,7 @@ void DegradedCall::AddAdaptationResource(
}
PacketReceiver* DegradedCall::Receiver() {
if (receive_config_) {
if (!receive_configs_.empty()) {
return this;
}
return call_->Receiver();
@ -299,7 +311,7 @@ void DegradedCall::OnUpdateSyncGroup(AudioReceiveStream& stream,
}
void DegradedCall::OnSentPacket(const rtc::SentPacket& sent_packet) {
if (send_config_) {
if (!send_configs_.empty()) {
// If we have a degraded send-transport, we have already notified call
// about the supposed network send time. Discard the actual network send
// time in order to properly fool the BWE.
@ -325,4 +337,21 @@ PacketReceiver::DeliveryStatus DegradedCall::DeliverPacket(
receive_pipe_->Process();
return status;
}
void DegradedCall::UpdateSendNetworkConfig() {
send_config_index_ = (send_config_index_ + 1) % send_configs_.size();
send_simulated_network_->SetConfig(send_configs_[send_config_index_]);
call_->network_thread()->PostDelayedTask(
ToQueuedTask(task_safety_, [this] { UpdateSendNetworkConfig(); }),
send_configs_[send_config_index_].duration.ms());
}
void DegradedCall::UpdateReceiveNetworkConfig() {
receive_config_index_ = (receive_config_index_ + 1) % receive_configs_.size();
receive_simulated_network_->SetConfig(
receive_configs_[receive_config_index_]);
call_->network_thread()->PostDelayedTask(
ToQueuedTask(task_safety_, [this] { UpdateReceiveNetworkConfig(); }),
receive_configs_[receive_config_index_].duration.ms());
}
} // namespace webrtc

View file

@ -17,6 +17,7 @@
#include <map>
#include <memory>
#include <string>
#include <vector>
#include "absl/types/optional.h"
#include "api/call/transport.h"
@ -38,15 +39,20 @@
#include "rtc_base/copy_on_write_buffer.h"
#include "rtc_base/network/sent_packet.h"
#include "rtc_base/task_queue.h"
#include "rtc_base/task_utils/pending_task_safety_flag.h"
#include "system_wrappers/include/clock.h"
namespace webrtc {
class DegradedCall : public Call, private PacketReceiver {
public:
struct TimeScopedNetworkConfig : public BuiltInNetworkBehaviorConfig {
TimeDelta duration = TimeDelta::PlusInfinity();
};
explicit DegradedCall(
std::unique_ptr<Call> call,
absl::optional<BuiltInNetworkBehaviorConfig> send_config,
absl::optional<BuiltInNetworkBehaviorConfig> receive_config,
const std::vector<TimeScopedNetworkConfig>& send_configs,
const std::vector<TimeScopedNetworkConfig>& receive_configs,
TaskQueueFactory* task_queue_factory);
~DegradedCall() override;
@ -157,14 +163,17 @@ class DegradedCall : public Call, private PacketReceiver {
Transport* const real_transport_;
};
Clock* const clock_;
const std::unique_ptr<Call> call_;
TaskQueueFactory* const task_queue_factory_;
void SetClientBitratePreferences(
const webrtc::BitrateSettings& preferences) override {}
void UpdateSendNetworkConfig();
void UpdateReceiveNetworkConfig();
const absl::optional<BuiltInNetworkBehaviorConfig> send_config_;
Clock* const clock_;
const std::unique_ptr<Call> call_;
ScopedTaskSafety task_safety_;
TaskQueueFactory* const task_queue_factory_;
size_t send_config_index_;
const std::vector<TimeScopedNetworkConfig> send_configs_;
SimulatedNetwork* send_simulated_network_;
std::unique_ptr<FakeNetworkPipeOnTaskQueue> send_pipe_;
std::map<AudioSendStream*, std::unique_ptr<FakeNetworkPipeTransportAdapter>>
@ -172,7 +181,8 @@ class DegradedCall : public Call, private PacketReceiver {
std::map<VideoSendStream*, std::unique_ptr<FakeNetworkPipeTransportAdapter>>
video_send_transport_adapters_;
const absl::optional<BuiltInNetworkBehaviorConfig> receive_config_;
size_t receive_config_index_;
const std::vector<TimeScopedNetworkConfig> receive_configs_;
SimulatedNetwork* receive_simulated_network_;
std::unique_ptr<FakeNetworkPipe> receive_pipe_;
};