| /* |
| * Copyright 2017 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 "call/call_factory.h" |
| |
| #include <stdio.h> |
| |
| #include <memory> |
| #include <string> |
| #include <utility> |
| #include <vector> |
| |
| #include "absl/memory/memory.h" |
| #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 "rtc_base/experiments/field_trial_list.h" |
| #include "rtc_base/experiments/field_trial_parser.h" |
| |
| namespace webrtc { |
| namespace { |
| using TimeScopedNetworkConfig = DegradedCall::TimeScopedNetworkConfig; |
| |
| bool ParseConfigParam(const FieldTrialsView& 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<TimeScopedNetworkConfig> ParseDegradationConfig( |
| const FieldTrialsView& trials, |
| bool send) { |
| std::string exp_prefix = "WebRTCFakeNetwork"; |
| if (send) { |
| exp_prefix += "Send"; |
| } else { |
| exp_prefix += "Receive"; |
| } |
| |
| TimeScopedNetworkConfig config; |
| bool configured = false; |
| configured |= |
| 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(trials, exp_prefix + "QueueLength", &queue_length)) { |
| RTC_CHECK_GE(queue_length, 0); |
| config.queue_length_packets = queue_length; |
| configured = true; |
| } |
| 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(trials, exp_prefix + "AllowReordering", |
| &allow_reordering)) { |
| config.allow_reordering = true; |
| configured = true; |
| } |
| configured |= ParseConfigParam(trials, exp_prefix + "AvgBurstLossLength", |
| &config.avg_burst_loss_length); |
| return configured ? absl::optional<TimeScopedNetworkConfig>(config) |
| : absl::nullopt; |
| } |
| |
| std::vector<TimeScopedNetworkConfig> GetNetworkConfigs( |
| const FieldTrialsView& 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() { |
| call_thread_.Detach(); |
| } |
| |
| Call* CallFactory::CreateCall(const Call::Config& config) { |
| RTC_DCHECK_RUN_ON(&call_thread_); |
| 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(); |
| |
| Call* call = |
| Call::Create(config, Clock::GetRealTimeClock(), |
| config.rtp_transport_controller_send_factory->Create( |
| transportConfig, Clock::GetRealTimeClock())); |
| |
| if (!send_degradation_configs.empty() || |
| !receive_degradation_configs.empty()) { |
| return new DegradedCall(absl::WrapUnique(call), send_degradation_configs, |
| receive_degradation_configs); |
| } |
| |
| return call; |
| } |
| |
| std::unique_ptr<CallFactoryInterface> CreateCallFactory() { |
| return std::unique_ptr<CallFactoryInterface>(new CallFactory()); |
| } |
| |
| } // namespace webrtc |