mirror of
https://github.com/mollyim/webrtc.git
synced 2025-05-12 21:30:45 +01:00
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:
parent
27c1452fae
commit
b5cba85c2f
3 changed files with 154 additions and 44 deletions
|
@ -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)
|
||||
: absl::nullopt;
|
||||
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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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_;
|
||||
};
|
||||
|
|
Loading…
Reference in a new issue