Repo created

This commit is contained in:
Fr4nz D13trich 2025-11-22 14:04:28 +01:00
parent 81b91f4139
commit f8c34fa5ee
22732 changed files with 4815320 additions and 2 deletions

View file

@ -0,0 +1,13 @@
asapersson@webrtc.org
sprang@webrtc.org
srte@webrtc.org
per-file audio_allocation_settings*=srte@webrtc.org
per-file congestion_controller_experiment*=srte@webrtc.org
per-file cpu_speed_experiment*=asapersson@webrtc.org
per-file field_trial*=srte@webrtc.org
per-file keyframe_interval_settings*=brandtr@webrtc.org
per-file normalize_simulcast_size_experiment*=asapersson@webrtc.org
per-file quality_scaling_experiment*=asapersson@webrtc.org
per-file rtt_mult_experiment*=mhoro@webrtc.org
per-file rate_control_settings*=srte@webrtc.org

View file

@ -0,0 +1,87 @@
/*
* Copyright (c) 2018 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 "rtc_base/experiments/alr_experiment.h"
#include <inttypes.h>
#include <stdio.h>
#include <string>
#include "absl/strings/string_view.h"
#include "api/field_trials_view.h"
#include "rtc_base/logging.h"
namespace webrtc {
namespace {
constexpr absl::string_view kDefaultProbingScreenshareBweSettings =
"1.0,2875,80,40,-60,3";
} // namespace
bool AlrExperimentSettings::MaxOneFieldTrialEnabled(
const FieldTrialsView& key_value_config) {
return key_value_config.Lookup(kStrictPacingAndProbingExperimentName)
.empty() ||
key_value_config.Lookup(kScreenshareProbingBweExperimentName).empty();
}
absl::optional<AlrExperimentSettings>
AlrExperimentSettings::CreateFromFieldTrial(
const FieldTrialsView& key_value_config,
absl::string_view experiment_name) {
absl::optional<AlrExperimentSettings> ret;
std::string group_name = key_value_config.Lookup(experiment_name);
const std::string kIgnoredSuffix = "_Dogfood";
std::string::size_type suffix_pos = group_name.rfind(kIgnoredSuffix);
if (suffix_pos != std::string::npos &&
suffix_pos == group_name.length() - kIgnoredSuffix.length()) {
group_name.resize(group_name.length() - kIgnoredSuffix.length());
}
if (group_name.empty()) {
if (experiment_name == kScreenshareProbingBweExperimentName) {
// This experiment is now default-on with fixed settings.
// TODO(sprang): Remove this kill-switch and clean up experiment code.
group_name = kDefaultProbingScreenshareBweSettings;
} else {
return ret;
}
}
AlrExperimentSettings settings;
if (sscanf(group_name.c_str(), "%f,%" PRId64 ",%d,%d,%d,%d",
&settings.pacing_factor, &settings.max_paced_queue_time,
&settings.alr_bandwidth_usage_percent,
&settings.alr_start_budget_level_percent,
&settings.alr_stop_budget_level_percent,
&settings.group_id) == 6) {
ret.emplace(settings);
RTC_LOG(LS_INFO) << "Using ALR experiment settings: "
"pacing factor: "
<< settings.pacing_factor << ", max pacer queue length: "
<< settings.max_paced_queue_time
<< ", ALR bandwidth usage percent: "
<< settings.alr_bandwidth_usage_percent
<< ", ALR start budget level percent: "
<< settings.alr_start_budget_level_percent
<< ", ALR end budget level percent: "
<< settings.alr_stop_budget_level_percent
<< ", ALR experiment group ID: " << settings.group_id;
} else {
RTC_LOG(LS_INFO) << "Failed to parse ALR experiment: " << experiment_name;
}
return ret;
}
} // namespace webrtc

View file

@ -0,0 +1,48 @@
/*
* Copyright (c) 2018 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.
*/
#ifndef RTC_BASE_EXPERIMENTS_ALR_EXPERIMENT_H_
#define RTC_BASE_EXPERIMENTS_ALR_EXPERIMENT_H_
#include <stdint.h>
#include "absl/strings/string_view.h"
#include "absl/types/optional.h"
#include "api/field_trials_view.h"
namespace webrtc {
struct AlrExperimentSettings {
public:
float pacing_factor;
int64_t max_paced_queue_time;
int alr_bandwidth_usage_percent;
int alr_start_budget_level_percent;
int alr_stop_budget_level_percent;
// Will be sent to the receive side for stats slicing.
// Can be 0..6, because it's sent as a 3 bits value and there's also
// reserved value to indicate absence of experiment.
int group_id;
static constexpr absl::string_view kScreenshareProbingBweExperimentName =
"WebRTC-ProbingScreenshareBwe";
static constexpr absl::string_view kStrictPacingAndProbingExperimentName =
"WebRTC-StrictPacingAndProbing";
static absl::optional<AlrExperimentSettings> CreateFromFieldTrial(
const FieldTrialsView& key_value_config,
absl::string_view experiment_name);
static bool MaxOneFieldTrialEnabled(const FieldTrialsView& key_value_config);
private:
AlrExperimentSettings() = default;
};
} // namespace webrtc
#endif // RTC_BASE_EXPERIMENTS_ALR_EXPERIMENT_H_

View file

@ -0,0 +1,487 @@
/*
* Copyright 2019 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 "rtc_base/experiments/balanced_degradation_settings.h"
#include <limits>
#include "rtc_base/experiments/field_trial_list.h"
#include "rtc_base/experiments/field_trial_parser.h"
#include "rtc_base/logging.h"
namespace webrtc {
namespace {
constexpr char kFieldTrial[] = "WebRTC-Video-BalancedDegradationSettings";
constexpr int kMinFps = 1;
constexpr int kMaxFps = 100; // 100 means unlimited fps.
std::vector<BalancedDegradationSettings::Config> DefaultConfigs() {
return {{320 * 240,
7,
0,
0,
BalancedDegradationSettings::kNoFpsDiff,
{0, 0, 0, 0, 0},
{0, 0, 0, 0, 0},
{0, 0, 0, 0, 0},
{0, 0, 0, 0, 0},
{0, 0, 0, 0, 0}},
{480 * 360,
10,
0,
0,
1,
{0, 0, 0, 0, 0},
{0, 0, 0, 0, 0},
{0, 0, 0, 0, 0},
{0, 0, 0, 0, 0},
{0, 0, 0, 0, 0}},
{640 * 480,
15,
0,
0,
1,
{0, 0, 0, 0, 0},
{0, 0, 0, 0, 0},
{0, 0, 0, 0, 0},
{0, 0, 0, 0, 0},
{0, 0, 0, 0, 0}}};
}
bool IsValidConfig(
const BalancedDegradationSettings::CodecTypeSpecific& config) {
if (config.GetQpLow().has_value() != config.GetQpHigh().has_value()) {
RTC_LOG(LS_WARNING) << "Neither or both thresholds should be set.";
return false;
}
if (config.GetQpLow().has_value() && config.GetQpHigh().has_value() &&
config.GetQpLow().value() >= config.GetQpHigh().value()) {
RTC_LOG(LS_WARNING) << "Invalid threshold value, low >= high threshold.";
return false;
}
if (config.GetFps().has_value() && (config.GetFps().value() < kMinFps ||
config.GetFps().value() > kMaxFps)) {
RTC_LOG(LS_WARNING) << "Unsupported fps setting, value ignored.";
return false;
}
return true;
}
bool IsValid(const BalancedDegradationSettings::CodecTypeSpecific& config1,
const BalancedDegradationSettings::CodecTypeSpecific& config2) {
bool both_or_none_set = ((config1.qp_low > 0) == (config2.qp_low > 0) &&
(config1.qp_high > 0) == (config2.qp_high > 0) &&
(config1.fps > 0) == (config2.fps > 0));
if (!both_or_none_set) {
RTC_LOG(LS_WARNING) << "Invalid value, all/none should be set.";
return false;
}
if (config1.fps > 0 && config1.fps < config2.fps) {
RTC_LOG(LS_WARNING) << "Invalid fps/pixel value provided.";
return false;
}
return true;
}
bool IsValid(const std::vector<BalancedDegradationSettings::Config>& configs) {
if (configs.size() <= 1) {
if (configs.size() == 1)
RTC_LOG(LS_WARNING) << "Unsupported size, value ignored.";
return false;
}
for (const auto& config : configs) {
if (config.fps < kMinFps || config.fps > kMaxFps) {
RTC_LOG(LS_WARNING) << "Unsupported fps setting, value ignored.";
return false;
}
}
int last_kbps = configs[0].kbps;
for (size_t i = 1; i < configs.size(); ++i) {
if (configs[i].kbps > 0) {
if (configs[i].kbps < last_kbps) {
RTC_LOG(LS_WARNING) << "Invalid bitrate value provided.";
return false;
}
last_kbps = configs[i].kbps;
}
}
for (size_t i = 1; i < configs.size(); ++i) {
if (configs[i].pixels < configs[i - 1].pixels ||
configs[i].fps < configs[i - 1].fps) {
RTC_LOG(LS_WARNING) << "Invalid fps/pixel value provided.";
return false;
}
if (!IsValid(configs[i].vp8, configs[i - 1].vp8) ||
!IsValid(configs[i].vp9, configs[i - 1].vp9) ||
!IsValid(configs[i].h264, configs[i - 1].h264) ||
!IsValid(configs[i].av1, configs[i - 1].av1) ||
!IsValid(configs[i].generic, configs[i - 1].generic)) {
return false;
}
}
for (const auto& config : configs) {
if (!IsValidConfig(config.vp8) || !IsValidConfig(config.vp9) ||
!IsValidConfig(config.h264) || !IsValidConfig(config.av1) ||
!IsValidConfig(config.generic)) {
return false;
}
}
return true;
}
std::vector<BalancedDegradationSettings::Config> GetValidOrDefault(
const std::vector<BalancedDegradationSettings::Config>& configs) {
if (IsValid(configs)) {
return configs;
}
return DefaultConfigs();
}
absl::optional<VideoEncoder::QpThresholds> GetThresholds(
VideoCodecType type,
const BalancedDegradationSettings::Config& config) {
absl::optional<int> low;
absl::optional<int> high;
switch (type) {
case kVideoCodecVP8:
low = config.vp8.GetQpLow();
high = config.vp8.GetQpHigh();
break;
case kVideoCodecVP9:
low = config.vp9.GetQpLow();
high = config.vp9.GetQpHigh();
break;
case kVideoCodecH265:
// TODO(bugs.webrtc.org/13485): Use H264 QP thresholds for now.
case kVideoCodecH264:
low = config.h264.GetQpLow();
high = config.h264.GetQpHigh();
break;
case kVideoCodecAV1:
low = config.av1.GetQpLow();
high = config.av1.GetQpHigh();
break;
case kVideoCodecGeneric:
low = config.generic.GetQpLow();
high = config.generic.GetQpHigh();
break;
default:
break;
}
if (low && high) {
RTC_LOG(LS_INFO) << "QP thresholds: low: " << *low << ", high: " << *high;
return absl::optional<VideoEncoder::QpThresholds>(
VideoEncoder::QpThresholds(*low, *high));
}
return absl::nullopt;
}
int GetFps(VideoCodecType type,
const absl::optional<BalancedDegradationSettings::Config>& config) {
if (!config.has_value()) {
return std::numeric_limits<int>::max();
}
absl::optional<int> fps;
switch (type) {
case kVideoCodecVP8:
fps = config->vp8.GetFps();
break;
case kVideoCodecH265:
// TODO(bugs.webrtc.org/13485): Use VP9 bitrate limits for now.
case kVideoCodecVP9:
fps = config->vp9.GetFps();
break;
case kVideoCodecH264:
fps = config->h264.GetFps();
break;
case kVideoCodecAV1:
fps = config->av1.GetFps();
break;
case kVideoCodecGeneric:
fps = config->generic.GetFps();
break;
default:
break;
}
const int framerate = fps.value_or(config->fps);
return (framerate == kMaxFps) ? std::numeric_limits<int>::max() : framerate;
}
absl::optional<int> GetKbps(
VideoCodecType type,
const absl::optional<BalancedDegradationSettings::Config>& config) {
if (!config.has_value())
return absl::nullopt;
absl::optional<int> kbps;
switch (type) {
case kVideoCodecVP8:
kbps = config->vp8.GetKbps();
break;
case kVideoCodecH265:
// TODO(bugs.webrtc.org/13485): Use VP9 bitrate limits for now.
case kVideoCodecVP9:
kbps = config->vp9.GetKbps();
break;
case kVideoCodecH264:
kbps = config->h264.GetKbps();
break;
case kVideoCodecAV1:
kbps = config->av1.GetKbps();
break;
case kVideoCodecGeneric:
kbps = config->generic.GetKbps();
break;
default:
break;
}
if (kbps.has_value())
return kbps;
return config->kbps > 0 ? absl::optional<int>(config->kbps) : absl::nullopt;
}
absl::optional<int> GetKbpsRes(
VideoCodecType type,
const absl::optional<BalancedDegradationSettings::Config>& config) {
if (!config.has_value())
return absl::nullopt;
absl::optional<int> kbps_res;
switch (type) {
case kVideoCodecVP8:
kbps_res = config->vp8.GetKbpsRes();
break;
case kVideoCodecH265:
// TODO(bugs.webrtc.org/13485): Use VP9 bitrate limits for now.
case kVideoCodecVP9:
kbps_res = config->vp9.GetKbpsRes();
break;
case kVideoCodecH264:
kbps_res = config->h264.GetKbpsRes();
break;
case kVideoCodecAV1:
kbps_res = config->av1.GetKbpsRes();
break;
case kVideoCodecGeneric:
kbps_res = config->generic.GetKbpsRes();
break;
default:
break;
}
if (kbps_res.has_value())
return kbps_res;
return config->kbps_res > 0 ? absl::optional<int>(config->kbps_res)
: absl::nullopt;
}
} // namespace
absl::optional<int> BalancedDegradationSettings::CodecTypeSpecific::GetQpLow()
const {
return (qp_low > 0) ? absl::optional<int>(qp_low) : absl::nullopt;
}
absl::optional<int> BalancedDegradationSettings::CodecTypeSpecific::GetQpHigh()
const {
return (qp_high > 0) ? absl::optional<int>(qp_high) : absl::nullopt;
}
absl::optional<int> BalancedDegradationSettings::CodecTypeSpecific::GetFps()
const {
return (fps > 0) ? absl::optional<int>(fps) : absl::nullopt;
}
absl::optional<int> BalancedDegradationSettings::CodecTypeSpecific::GetKbps()
const {
return (kbps > 0) ? absl::optional<int>(kbps) : absl::nullopt;
}
absl::optional<int> BalancedDegradationSettings::CodecTypeSpecific::GetKbpsRes()
const {
return (kbps_res > 0) ? absl::optional<int>(kbps_res) : absl::nullopt;
}
BalancedDegradationSettings::Config::Config() = default;
BalancedDegradationSettings::Config::Config(int pixels,
int fps,
int kbps,
int kbps_res,
int fps_diff,
CodecTypeSpecific vp8,
CodecTypeSpecific vp9,
CodecTypeSpecific h264,
CodecTypeSpecific av1,
CodecTypeSpecific generic)
: pixels(pixels),
fps(fps),
kbps(kbps),
kbps_res(kbps_res),
fps_diff(fps_diff),
vp8(vp8),
vp9(vp9),
h264(h264),
av1(av1),
generic(generic) {}
BalancedDegradationSettings::BalancedDegradationSettings(
const FieldTrialsView& field_trials) {
FieldTrialStructList<Config> configs(
{FieldTrialStructMember("pixels", [](Config* c) { return &c->pixels; }),
FieldTrialStructMember("fps", [](Config* c) { return &c->fps; }),
FieldTrialStructMember("kbps", [](Config* c) { return &c->kbps; }),
FieldTrialStructMember("kbps_res",
[](Config* c) { return &c->kbps_res; }),
FieldTrialStructMember("fps_diff",
[](Config* c) { return &c->fps_diff; }),
FieldTrialStructMember("vp8_qp_low",
[](Config* c) { return &c->vp8.qp_low; }),
FieldTrialStructMember("vp8_qp_high",
[](Config* c) { return &c->vp8.qp_high; }),
FieldTrialStructMember("vp8_fps", [](Config* c) { return &c->vp8.fps; }),
FieldTrialStructMember("vp8_kbps",
[](Config* c) { return &c->vp8.kbps; }),
FieldTrialStructMember("vp8_kbps_res",
[](Config* c) { return &c->vp8.kbps_res; }),
FieldTrialStructMember("vp9_qp_low",
[](Config* c) { return &c->vp9.qp_low; }),
FieldTrialStructMember("vp9_qp_high",
[](Config* c) { return &c->vp9.qp_high; }),
FieldTrialStructMember("vp9_fps", [](Config* c) { return &c->vp9.fps; }),
FieldTrialStructMember("vp9_kbps",
[](Config* c) { return &c->vp9.kbps; }),
FieldTrialStructMember("vp9_kbps_res",
[](Config* c) { return &c->vp9.kbps_res; }),
FieldTrialStructMember("h264_qp_low",
[](Config* c) { return &c->h264.qp_low; }),
FieldTrialStructMember("h264_qp_high",
[](Config* c) { return &c->h264.qp_high; }),
FieldTrialStructMember("h264_fps",
[](Config* c) { return &c->h264.fps; }),
FieldTrialStructMember("h264_kbps",
[](Config* c) { return &c->h264.kbps; }),
FieldTrialStructMember("h264_kbps_res",
[](Config* c) { return &c->h264.kbps_res; }),
FieldTrialStructMember("av1_qp_low",
[](Config* c) { return &c->av1.qp_low; }),
FieldTrialStructMember("av1_qp_high",
[](Config* c) { return &c->av1.qp_high; }),
FieldTrialStructMember("av1_fps", [](Config* c) { return &c->av1.fps; }),
FieldTrialStructMember("av1_kbps",
[](Config* c) { return &c->av1.kbps; }),
FieldTrialStructMember("av1_kbps_res",
[](Config* c) { return &c->av1.kbps_res; }),
FieldTrialStructMember("generic_qp_low",
[](Config* c) { return &c->generic.qp_low; }),
FieldTrialStructMember("generic_qp_high",
[](Config* c) { return &c->generic.qp_high; }),
FieldTrialStructMember("generic_fps",
[](Config* c) { return &c->generic.fps; }),
FieldTrialStructMember("generic_kbps",
[](Config* c) { return &c->generic.kbps; }),
FieldTrialStructMember("generic_kbps_res",
[](Config* c) { return &c->generic.kbps_res; })},
{});
ParseFieldTrial({&configs}, field_trials.Lookup(kFieldTrial));
configs_ = GetValidOrDefault(configs.Get());
RTC_DCHECK_GT(configs_.size(), 1);
}
BalancedDegradationSettings::~BalancedDegradationSettings() {}
std::vector<BalancedDegradationSettings::Config>
BalancedDegradationSettings::GetConfigs() const {
return configs_;
}
int BalancedDegradationSettings::MinFps(VideoCodecType type, int pixels) const {
return GetFps(type, GetMinFpsConfig(pixels));
}
absl::optional<BalancedDegradationSettings::Config>
BalancedDegradationSettings::GetMinFpsConfig(int pixels) const {
for (const auto& config : configs_) {
if (pixels <= config.pixels)
return config;
}
return absl::nullopt;
}
int BalancedDegradationSettings::MaxFps(VideoCodecType type, int pixels) const {
return GetFps(type, GetMaxFpsConfig(pixels));
}
absl::optional<BalancedDegradationSettings::Config>
BalancedDegradationSettings::GetMaxFpsConfig(int pixels) const {
for (size_t i = 0; i < configs_.size() - 1; ++i) {
if (pixels <= configs_[i].pixels)
return configs_[i + 1];
}
return absl::nullopt;
}
bool BalancedDegradationSettings::CanAdaptUp(VideoCodecType type,
int pixels,
uint32_t bitrate_bps) const {
absl::optional<int> min_kbps = GetKbps(type, GetMaxFpsConfig(pixels));
if (!min_kbps.has_value() || bitrate_bps == 0) {
return true; // No limit configured or bitrate provided.
}
return bitrate_bps >= static_cast<uint32_t>(min_kbps.value() * 1000);
}
bool BalancedDegradationSettings::CanAdaptUpResolution(
VideoCodecType type,
int pixels,
uint32_t bitrate_bps) const {
absl::optional<int> min_kbps = GetKbpsRes(type, GetMaxFpsConfig(pixels));
if (!min_kbps.has_value() || bitrate_bps == 0) {
return true; // No limit configured or bitrate provided.
}
return bitrate_bps >= static_cast<uint32_t>(min_kbps.value() * 1000);
}
absl::optional<int> BalancedDegradationSettings::MinFpsDiff(int pixels) const {
for (const auto& config : configs_) {
if (pixels <= config.pixels) {
return (config.fps_diff > kNoFpsDiff)
? absl::optional<int>(config.fps_diff)
: absl::nullopt;
}
}
return absl::nullopt;
}
absl::optional<VideoEncoder::QpThresholds>
BalancedDegradationSettings::GetQpThresholds(VideoCodecType type,
int pixels) const {
return GetThresholds(type, GetConfig(pixels));
}
BalancedDegradationSettings::Config BalancedDegradationSettings::GetConfig(
int pixels) const {
for (const auto& config : configs_) {
if (pixels <= config.pixels)
return config;
}
return configs_.back(); // Use last above highest pixels.
}
} // namespace webrtc

View file

@ -0,0 +1,143 @@
/*
* Copyright 2019 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.
*/
#ifndef RTC_BASE_EXPERIMENTS_BALANCED_DEGRADATION_SETTINGS_H_
#define RTC_BASE_EXPERIMENTS_BALANCED_DEGRADATION_SETTINGS_H_
#include <vector>
#include "absl/types/optional.h"
#include "api/field_trials_view.h"
#include "api/video_codecs/video_encoder.h"
namespace webrtc {
class BalancedDegradationSettings {
public:
static constexpr int kNoFpsDiff = -100;
BalancedDegradationSettings(const FieldTrialsView& field_trials);
~BalancedDegradationSettings();
struct CodecTypeSpecific {
CodecTypeSpecific() {}
CodecTypeSpecific(int qp_low, int qp_high, int fps, int kbps, int kbps_res)
: qp_low(qp_low),
qp_high(qp_high),
fps(fps),
kbps(kbps),
kbps_res(kbps_res) {}
bool operator==(const CodecTypeSpecific& o) const {
return qp_low == o.qp_low && qp_high == o.qp_high && fps == o.fps &&
kbps == o.kbps && kbps_res == o.kbps_res;
}
absl::optional<int> GetQpLow() const;
absl::optional<int> GetQpHigh() const;
absl::optional<int> GetFps() const;
absl::optional<int> GetKbps() const;
absl::optional<int> GetKbpsRes() const;
// Optional settings.
int qp_low = 0;
int qp_high = 0;
int fps = 0; // If unset, defaults to `fps` in Config.
int kbps = 0; // If unset, defaults to `kbps` in Config.
int kbps_res = 0; // If unset, defaults to `kbps_res` in Config.
};
struct Config {
Config();
Config(int pixels,
int fps,
int kbps,
int kbps_res,
int fps_diff,
CodecTypeSpecific vp8,
CodecTypeSpecific vp9,
CodecTypeSpecific h264,
CodecTypeSpecific av1,
CodecTypeSpecific generic);
bool operator==(const Config& o) const {
return pixels == o.pixels && fps == o.fps && kbps == o.kbps &&
kbps_res == o.kbps_res && fps_diff == o.fps_diff && vp8 == o.vp8 &&
vp9 == o.vp9 && h264 == o.h264 && av1 == o.av1 &&
generic == o.generic;
}
// Example:
// WebRTC-Video-BalancedDegradationSettings/pixels:100|200|300,fps:5|15|25/
// pixels <= 100 -> min framerate: 5 fps
// pixels <= 200 -> min framerate: 15 fps
// pixels <= 300 -> min framerate: 25 fps
//
// WebRTC-Video-BalancedDegradationSettings/pixels:100|200|300,
// fps:5|15|25, // Min framerate.
// kbps:0|60|70, // Min bitrate needed to adapt up.
// kbps_res:0|65|75/ // Min bitrate needed to adapt up in resolution.
//
// pixels: fps: kbps: kbps_res:
// 300 30 - -
// 300 25 70 kbps 75 kbps
// 200 25 70 kbps -
// 200 15 60 kbps 65 kbps
// 100 15 60 kbps -
// 100 5
// optional optional
int pixels = 0; // Video frame size.
// If the frame size is less than or equal to `pixels`:
int fps = 0; // Min framerate to be used.
int kbps = 0; // Min bitrate needed to adapt up (resolution/fps).
int kbps_res = 0; // Min bitrate needed to adapt up in resolution.
int fps_diff = kNoFpsDiff; // Min fps reduction needed (input fps - `fps`)
// w/o triggering a new subsequent downgrade
// check.
CodecTypeSpecific vp8;
CodecTypeSpecific vp9;
CodecTypeSpecific h264;
CodecTypeSpecific av1;
CodecTypeSpecific generic;
};
// Returns configurations from field trial on success (default on failure).
std::vector<Config> GetConfigs() const;
// Gets the min/max framerate from `configs_` based on `pixels`.
int MinFps(VideoCodecType type, int pixels) const;
int MaxFps(VideoCodecType type, int pixels) const;
// Checks if quality can be increased based on `pixels` and `bitrate_bps`.
bool CanAdaptUp(VideoCodecType type, int pixels, uint32_t bitrate_bps) const;
bool CanAdaptUpResolution(VideoCodecType type,
int pixels,
uint32_t bitrate_bps) const;
// Gets the min framerate diff from `configs_` based on `pixels`.
absl::optional<int> MinFpsDiff(int pixels) const;
// Gets QpThresholds for the codec `type` based on `pixels`.
absl::optional<VideoEncoder::QpThresholds> GetQpThresholds(
VideoCodecType type,
int pixels) const;
private:
absl::optional<Config> GetMinFpsConfig(int pixels) const;
absl::optional<Config> GetMaxFpsConfig(int pixels) const;
Config GetConfig(int pixels) const;
std::vector<Config> configs_;
};
} // namespace webrtc
#endif // RTC_BASE_EXPERIMENTS_BALANCED_DEGRADATION_SETTINGS_H_

View file

@ -0,0 +1,43 @@
/*
* Copyright (c) 2021 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 "rtc_base/experiments/bandwidth_quality_scaler_settings.h"
#include "api/transport/field_trial_based_config.h"
#include "rtc_base/logging.h"
namespace webrtc {
BandwidthQualityScalerSettings::BandwidthQualityScalerSettings(
const FieldTrialsView* const key_value_config)
: bitrate_state_update_interval_s_("bitrate_state_update_interval_s_") {
ParseFieldTrial(
{&bitrate_state_update_interval_s_},
key_value_config->Lookup("WebRTC-Video-BandwidthQualityScalerSettings"));
}
BandwidthQualityScalerSettings
BandwidthQualityScalerSettings::ParseFromFieldTrials() {
FieldTrialBasedConfig field_trial_config;
return BandwidthQualityScalerSettings(&field_trial_config);
}
absl::optional<uint32_t>
BandwidthQualityScalerSettings::BitrateStateUpdateInterval() const {
if (bitrate_state_update_interval_s_ &&
bitrate_state_update_interval_s_.Value() <= 0) {
RTC_LOG(LS_WARNING)
<< "Unsupported bitrate_state_update_interval_s_ value, ignored.";
return absl::nullopt;
}
return bitrate_state_update_interval_s_.GetOptional();
}
} // namespace webrtc

View file

@ -0,0 +1,35 @@
/*
* Copyright (c) 2021 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.
*/
#ifndef RTC_BASE_EXPERIMENTS_BANDWIDTH_QUALITY_SCALER_SETTINGS_H_
#define RTC_BASE_EXPERIMENTS_BANDWIDTH_QUALITY_SCALER_SETTINGS_H_
#include "absl/types/optional.h"
#include "api/field_trials_view.h"
#include "rtc_base/experiments/field_trial_parser.h"
namespace webrtc {
class BandwidthQualityScalerSettings final {
public:
static BandwidthQualityScalerSettings ParseFromFieldTrials();
absl::optional<uint32_t> BitrateStateUpdateInterval() const;
private:
explicit BandwidthQualityScalerSettings(
const FieldTrialsView* const key_value_config);
FieldTrialOptional<uint32_t> bitrate_state_update_interval_s_;
};
} // namespace webrtc
#endif // RTC_BASE_EXPERIMENTS_BANDWIDTH_QUALITY_SCALER_SETTINGS_H_

View file

@ -0,0 +1,89 @@
/*
* Copyright 2018 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 "rtc_base/experiments/cpu_speed_experiment.h"
#include <stdio.h>
#include "rtc_base/experiments/field_trial_list.h"
#include "rtc_base/logging.h"
#include "system_wrappers/include/field_trial.h"
namespace webrtc {
namespace {
constexpr char kFieldTrial[] = "WebRTC-VP8-CpuSpeed-Arm";
constexpr int kMinSetting = -16;
constexpr int kMaxSetting = -1;
std::vector<CpuSpeedExperiment::Config> GetValidOrEmpty(
const std::vector<CpuSpeedExperiment::Config>& configs) {
if (configs.empty()) {
return {};
}
for (const auto& config : configs) {
if (config.cpu_speed < kMinSetting || config.cpu_speed > kMaxSetting) {
RTC_LOG(LS_WARNING) << "Unsupported cpu speed setting, value ignored.";
return {};
}
}
for (size_t i = 1; i < configs.size(); ++i) {
if (configs[i].pixels < configs[i - 1].pixels ||
configs[i].cpu_speed > configs[i - 1].cpu_speed) {
RTC_LOG(LS_WARNING) << "Invalid parameter value provided.";
return {};
}
}
return configs;
}
bool HasLeCores(const std::vector<CpuSpeedExperiment::Config>& configs) {
for (const auto& config : configs) {
if (config.cpu_speed_le_cores == 0)
return false;
}
return true;
}
} // namespace
CpuSpeedExperiment::CpuSpeedExperiment() : cores_("cores") {
FieldTrialStructList<Config> configs(
{FieldTrialStructMember("pixels", [](Config* c) { return &c->pixels; }),
FieldTrialStructMember("cpu_speed",
[](Config* c) { return &c->cpu_speed; }),
FieldTrialStructMember(
"cpu_speed_le_cores",
[](Config* c) { return &c->cpu_speed_le_cores; })},
{});
ParseFieldTrial({&configs, &cores_}, field_trial::FindFullName(kFieldTrial));
configs_ = GetValidOrEmpty(configs.Get());
}
CpuSpeedExperiment::~CpuSpeedExperiment() {}
absl::optional<int> CpuSpeedExperiment::GetValue(int pixels,
int num_cores) const {
if (configs_.empty())
return absl::nullopt;
bool use_le = HasLeCores(configs_) && cores_ && num_cores <= cores_.Value();
for (const auto& config : configs_) {
if (pixels <= config.pixels)
return use_le ? absl::optional<int>(config.cpu_speed_le_cores)
: absl::optional<int>(config.cpu_speed);
}
return absl::optional<int>(kMinSetting);
}
} // namespace webrtc

View file

@ -0,0 +1,64 @@
/*
* Copyright 2018 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.
*/
#ifndef RTC_BASE_EXPERIMENTS_CPU_SPEED_EXPERIMENT_H_
#define RTC_BASE_EXPERIMENTS_CPU_SPEED_EXPERIMENT_H_
#include <vector>
#include "absl/types/optional.h"
#include "rtc_base/experiments/field_trial_parser.h"
namespace webrtc {
class CpuSpeedExperiment {
public:
CpuSpeedExperiment();
~CpuSpeedExperiment();
// Example:
// WebRTC-VP8-CpuSpeed-Arm/pixels:100|200|300,cpu_speed:-1|-2|-3/
// pixels <= 100 -> cpu speed: -1
// pixels <= 200 -> cpu speed: -2
// pixels <= 300 -> cpu speed: -3
// WebRTC-VP8-CpuSpeed-Arm/pixels:100|200|300,cpu_speed:-1|-2|-3/,
// cpu_speed_le_cores:-4|-5|-6,cores:3/
// If `num_cores` > 3
// pixels <= 100 -> cpu speed: -1
// pixels <= 200 -> cpu speed: -2
// pixels <= 300 -> cpu speed: -3
// else
// pixels <= 100 -> cpu speed: -4
// pixels <= 200 -> cpu speed: -5
// pixels <= 300 -> cpu speed: -6
struct Config {
int pixels = 0; // The video frame size.
int cpu_speed = 0; // The `cpu_speed` to be used if the frame size is less
// than or equal to `pixels`.
// Optional.
int cpu_speed_le_cores = 0; // Same as `cpu_speed` above but only used if
// `num_cores` <= `cores_`.
};
// Gets the cpu speed based on `pixels` and `num_cores`.
absl::optional<int> GetValue(int pixels, int num_cores) const;
private:
std::vector<Config> configs_;
// Threshold for when to use `cpu_speed_le_cores`.
FieldTrialOptional<int> cores_;
};
} // namespace webrtc
#endif // RTC_BASE_EXPERIMENTS_CPU_SPEED_EXPERIMENT_H_

View file

@ -0,0 +1,236 @@
/*
* Copyright 2021 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 "rtc_base/experiments/encoder_info_settings.h"
#include <stdio.h>
#include "absl/strings/string_view.h"
#include "rtc_base/experiments/field_trial_list.h"
#include "rtc_base/logging.h"
#include "system_wrappers/include/field_trial.h"
namespace webrtc {
namespace {
std::vector<VideoEncoder::ResolutionBitrateLimits> ToResolutionBitrateLimits(
const std::vector<EncoderInfoSettings::BitrateLimit>& limits) {
std::vector<VideoEncoder::ResolutionBitrateLimits> result;
for (const auto& limit : limits) {
result.push_back(VideoEncoder::ResolutionBitrateLimits(
limit.frame_size_pixels, limit.min_start_bitrate_bps,
limit.min_bitrate_bps, limit.max_bitrate_bps));
}
return result;
}
constexpr float kDefaultMinBitratebps = 30000;
} // namespace
// Default bitrate limits for simulcast with one active stream:
// {frame_size_pixels, min_start_bitrate_bps, min_bitrate_bps, max_bitrate_bps}.
std::vector<VideoEncoder::ResolutionBitrateLimits>
EncoderInfoSettings::GetDefaultSinglecastBitrateLimits(
VideoCodecType codec_type) {
if (codec_type == kVideoCodecAV1) {
// AV1 singlecast max bitrate limits are higher than AV1 SVC max limits.
// This is because in singlecast we normally have just one receiver, BWE is
// known end-to-end and the encode target bitrate guarantees delivery of
// video.
// The min bitrate limits are not used in singlecast (used in SVC/simulcast
// to de-/activate spatial layers) and are set to zero. Send resolution in
// singlecast is assumed to be regulated by QP-based quality scaler.
return {{320 * 180, 0, 0, 256000},
{480 * 270, 176000, 0, 384000},
{640 * 360, 256000, 0, 512000},
{960 * 540, 384000, 0, 1024000},
{1280 * 720, 576000, 0, 1536000}};
}
if (codec_type == kVideoCodecVP9) {
// VP9 singlecast bitrate limits are derived ~directly from VP9 SVC bitrate
// limits. The current max limits are unnecessarily too strict for
// singlecast, where BWE is known end-to-end, especially for low
// resolutions.
return {{320 * 180, 0, 30000, 150000},
{480 * 270, 120000, 30000, 300000},
{640 * 360, 190000, 30000, 420000},
{960 * 540, 350000, 30000, 1000000},
{1280 * 720, 480000, 30000, 1500000}};
}
// VP8 and other codecs.
return {{320 * 180, 0, 30000, 300000},
{480 * 270, 200000, 30000, 500000},
{640 * 360, 300000, 30000, 800000},
{960 * 540, 500000, 30000, 1500000},
{1280 * 720, 900000, 30000, 2500000}};
}
absl::optional<VideoEncoder::ResolutionBitrateLimits>
EncoderInfoSettings::GetDefaultSinglecastBitrateLimitsForResolution(
VideoCodecType codec_type,
int frame_size_pixels) {
VideoEncoder::EncoderInfo info;
info.resolution_bitrate_limits =
GetDefaultSinglecastBitrateLimits(codec_type);
return info.GetEncoderBitrateLimitsForResolution(frame_size_pixels);
}
// Return the suitable bitrate limits for specified resolution when qp is
// untrusted, they are experimental values.
// TODO(bugs.webrtc.org/12942): Maybe we need to add other codecs(VP8/VP9)
// experimental values.
std::vector<VideoEncoder::ResolutionBitrateLimits>
EncoderInfoSettings::GetDefaultSinglecastBitrateLimitsWhenQpIsUntrusted() {
// Specific limits for H264/AVC
return {{0 * 0, 0, 0, 0},
{320 * 180, 0, 30000, 300000},
{480 * 270, 300000, 30000, 500000},
{640 * 360, 500000, 30000, 800000},
{960 * 540, 800000, 30000, 1500000},
{1280 * 720, 1500000, 30000, 2500000},
{1920 * 1080, 2500000, 30000, 4000000}};
}
// Through linear interpolation, return the bitrate limit corresponding to the
// specified |frame_size_pixels|.
absl::optional<VideoEncoder::ResolutionBitrateLimits>
EncoderInfoSettings::GetSinglecastBitrateLimitForResolutionWhenQpIsUntrusted(
absl::optional<int> frame_size_pixels,
const std::vector<VideoEncoder::ResolutionBitrateLimits>&
resolution_bitrate_limits) {
if (!frame_size_pixels.has_value() || frame_size_pixels.value() <= 0) {
return absl::nullopt;
}
std::vector<VideoEncoder::ResolutionBitrateLimits> bitrate_limits =
resolution_bitrate_limits;
// Sort the list of bitrate limits by resolution.
sort(bitrate_limits.begin(), bitrate_limits.end(),
[](const VideoEncoder::ResolutionBitrateLimits& lhs,
const VideoEncoder::ResolutionBitrateLimits& rhs) {
return lhs.frame_size_pixels < rhs.frame_size_pixels;
});
if (bitrate_limits.empty()) {
return absl::nullopt;
}
int interpolation_index = -1;
for (size_t i = 0; i < bitrate_limits.size(); ++i) {
if (bitrate_limits[i].frame_size_pixels >= frame_size_pixels.value()) {
interpolation_index = i;
break;
}
}
// -1 means that the maximum resolution is exceeded, we will select the
// largest data as the return result.
if (interpolation_index == -1) {
return *bitrate_limits.rbegin();
}
// If we have a matching resolution, return directly without interpolation.
if (bitrate_limits[interpolation_index].frame_size_pixels ==
frame_size_pixels.value()) {
return bitrate_limits[interpolation_index];
}
// No matching resolution, do a linear interpolate.
int lower_pixel_count =
bitrate_limits[interpolation_index - 1].frame_size_pixels;
int upper_pixel_count = bitrate_limits[interpolation_index].frame_size_pixels;
float alpha = (frame_size_pixels.value() - lower_pixel_count) * 1.0 /
(upper_pixel_count - lower_pixel_count);
int min_start_bitrate_bps = static_cast<int>(
bitrate_limits[interpolation_index].min_start_bitrate_bps * alpha +
bitrate_limits[interpolation_index - 1].min_start_bitrate_bps *
(1.0 - alpha));
int max_bitrate_bps = static_cast<int>(
bitrate_limits[interpolation_index].max_bitrate_bps * alpha +
bitrate_limits[interpolation_index - 1].max_bitrate_bps * (1.0 - alpha));
if (max_bitrate_bps >= min_start_bitrate_bps) {
return VideoEncoder::ResolutionBitrateLimits(
frame_size_pixels.value(), min_start_bitrate_bps, kDefaultMinBitratebps,
max_bitrate_bps);
} else {
RTC_LOG(LS_WARNING)
<< "BitRate interpolation calculating result is abnormal. "
<< " lower_pixel_count = " << lower_pixel_count
<< " upper_pixel_count = " << upper_pixel_count
<< " frame_size_pixels = " << frame_size_pixels.value()
<< " min_start_bitrate_bps = " << min_start_bitrate_bps
<< " min_bitrate_bps = " << kDefaultMinBitratebps
<< " max_bitrate_bps = " << max_bitrate_bps;
return absl::nullopt;
}
}
EncoderInfoSettings::EncoderInfoSettings(absl::string_view name)
: requested_resolution_alignment_("requested_resolution_alignment"),
apply_alignment_to_all_simulcast_layers_(
"apply_alignment_to_all_simulcast_layers") {
FieldTrialStructList<BitrateLimit> bitrate_limits(
{FieldTrialStructMember(
"frame_size_pixels",
[](BitrateLimit* b) { return &b->frame_size_pixels; }),
FieldTrialStructMember(
"min_start_bitrate_bps",
[](BitrateLimit* b) { return &b->min_start_bitrate_bps; }),
FieldTrialStructMember(
"min_bitrate_bps",
[](BitrateLimit* b) { return &b->min_bitrate_bps; }),
FieldTrialStructMember(
"max_bitrate_bps",
[](BitrateLimit* b) { return &b->max_bitrate_bps; })},
{});
std::string name_str(name);
if (field_trial::FindFullName(name_str).empty()) {
// Encoder name not found, use common string applying to all encoders.
name_str = "WebRTC-GetEncoderInfoOverride";
}
ParseFieldTrial({&bitrate_limits, &requested_resolution_alignment_,
&apply_alignment_to_all_simulcast_layers_},
field_trial::FindFullName(name_str));
resolution_bitrate_limits_ = ToResolutionBitrateLimits(bitrate_limits.Get());
}
absl::optional<uint32_t> EncoderInfoSettings::requested_resolution_alignment()
const {
if (requested_resolution_alignment_ &&
requested_resolution_alignment_.Value() < 1) {
RTC_LOG(LS_WARNING) << "Unsupported alignment value, ignored.";
return absl::nullopt;
}
return requested_resolution_alignment_.GetOptional();
}
EncoderInfoSettings::~EncoderInfoSettings() {}
SimulcastEncoderAdapterEncoderInfoSettings::
SimulcastEncoderAdapterEncoderInfoSettings()
: EncoderInfoSettings(
"WebRTC-SimulcastEncoderAdapter-GetEncoderInfoOverride") {}
LibvpxVp8EncoderInfoSettings::LibvpxVp8EncoderInfoSettings()
: EncoderInfoSettings("WebRTC-VP8-GetEncoderInfoOverride") {}
LibvpxVp9EncoderInfoSettings::LibvpxVp9EncoderInfoSettings()
: EncoderInfoSettings("WebRTC-VP9-GetEncoderInfoOverride") {}
LibaomAv1EncoderInfoSettings::LibaomAv1EncoderInfoSettings()
: EncoderInfoSettings("WebRTC-Av1-GetEncoderInfoOverride") {}
} // namespace webrtc

View file

@ -0,0 +1,100 @@
/*
* Copyright 2021 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.
*/
#ifndef RTC_BASE_EXPERIMENTS_ENCODER_INFO_SETTINGS_H_
#define RTC_BASE_EXPERIMENTS_ENCODER_INFO_SETTINGS_H_
#include <string>
#include <vector>
#include "absl/strings/string_view.h"
#include "absl/types/optional.h"
#include "api/video_codecs/video_encoder.h"
#include "rtc_base/experiments/field_trial_parser.h"
namespace webrtc {
class EncoderInfoSettings {
public:
virtual ~EncoderInfoSettings();
// Bitrate limits per resolution.
struct BitrateLimit {
int frame_size_pixels = 0; // The video frame size.
int min_start_bitrate_bps = 0; // The minimum bitrate to start encoding.
int min_bitrate_bps = 0; // The minimum bitrate.
int max_bitrate_bps = 0; // The maximum bitrate.
};
absl::optional<uint32_t> requested_resolution_alignment() const;
bool apply_alignment_to_all_simulcast_layers() const {
return apply_alignment_to_all_simulcast_layers_.Get();
}
std::vector<VideoEncoder::ResolutionBitrateLimits> resolution_bitrate_limits()
const {
return resolution_bitrate_limits_;
}
static std::vector<VideoEncoder::ResolutionBitrateLimits>
GetDefaultSinglecastBitrateLimits(VideoCodecType codec_type);
static absl::optional<VideoEncoder::ResolutionBitrateLimits>
GetDefaultSinglecastBitrateLimitsForResolution(VideoCodecType codec_type,
int frame_size_pixels);
static std::vector<VideoEncoder::ResolutionBitrateLimits>
GetDefaultSinglecastBitrateLimitsWhenQpIsUntrusted();
static absl::optional<VideoEncoder::ResolutionBitrateLimits>
GetSinglecastBitrateLimitForResolutionWhenQpIsUntrusted(
absl::optional<int> frame_size_pixels,
const std::vector<VideoEncoder::ResolutionBitrateLimits>&
resolution_bitrate_limits);
protected:
explicit EncoderInfoSettings(absl::string_view name);
private:
FieldTrialOptional<uint32_t> requested_resolution_alignment_;
FieldTrialFlag apply_alignment_to_all_simulcast_layers_;
std::vector<VideoEncoder::ResolutionBitrateLimits> resolution_bitrate_limits_;
};
// EncoderInfo settings for SimulcastEncoderAdapter.
class SimulcastEncoderAdapterEncoderInfoSettings : public EncoderInfoSettings {
public:
SimulcastEncoderAdapterEncoderInfoSettings();
~SimulcastEncoderAdapterEncoderInfoSettings() override {}
};
// EncoderInfo settings for LibvpxVp8Encoder.
class LibvpxVp8EncoderInfoSettings : public EncoderInfoSettings {
public:
LibvpxVp8EncoderInfoSettings();
~LibvpxVp8EncoderInfoSettings() override {}
};
// EncoderInfo settings for LibvpxVp9Encoder.
class LibvpxVp9EncoderInfoSettings : public EncoderInfoSettings {
public:
LibvpxVp9EncoderInfoSettings();
~LibvpxVp9EncoderInfoSettings() override {}
};
// EncoderInfo settings for LibaomAv1Encoder.
class LibaomAv1EncoderInfoSettings : public EncoderInfoSettings {
public:
LibaomAv1EncoderInfoSettings();
~LibaomAv1EncoderInfoSettings() override {}
};
} // namespace webrtc
#endif // RTC_BASE_EXPERIMENTS_ENCODER_INFO_SETTINGS_H_

View file

@ -0,0 +1,59 @@
/*
* Copyright 2019 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 "rtc_base/experiments/field_trial_list.h"
#include "absl/strings/string_view.h"
namespace webrtc {
FieldTrialListBase::FieldTrialListBase(absl::string_view key)
: FieldTrialParameterInterface(key),
failed_(false),
parse_got_called_(false) {}
bool FieldTrialListBase::Failed() const {
return failed_;
}
bool FieldTrialListBase::Used() const {
return parse_got_called_;
}
int FieldTrialListWrapper::Length() {
return GetList()->Size();
}
bool FieldTrialListWrapper::Failed() {
return GetList()->Failed();
}
bool FieldTrialListWrapper::Used() {
return GetList()->Used();
}
bool FieldTrialStructListBase::Parse(absl::optional<std::string> str_value) {
RTC_DCHECK_NOTREACHED();
return true;
}
int FieldTrialStructListBase::ValidateAndGetLength() {
int length = -1;
for (std::unique_ptr<FieldTrialListWrapper>& list : sub_lists_) {
if (list->Failed())
return -1;
else if (!list->Used())
continue;
else if (length == -1)
length = list->Length();
else if (length != list->Length())
return -1;
}
return length;
}
} // namespace webrtc

View file

@ -0,0 +1,226 @@
/*
* Copyright 2018 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.
*/
#ifndef RTC_BASE_EXPERIMENTS_FIELD_TRIAL_LIST_H_
#define RTC_BASE_EXPERIMENTS_FIELD_TRIAL_LIST_H_
#include <initializer_list>
#include <memory>
#include <string>
#include <vector>
#include "absl/strings/string_view.h"
#include "rtc_base/experiments/field_trial_parser.h"
#include "rtc_base/string_encode.h"
// List support for field trial strings. FieldTrialList and FieldTrialStructList
// are used similarly to the other FieldTrialParameters, but take a variable
// number of parameters. A FieldTrialList<T> parses a |-delimeted string into a
// list of T, using ParseTypedParameter to parse the individual tokens.
// Example string: "my_list:1|2|3,empty_list,other_list:aardvark".
// A FieldTrialStructList combines multiple lists into a list-of-structs. It
// ensures that all its sublists parse correctly and have the same length, then
// uses user-supplied accessor functions to write those elements into structs of
// a user-supplied type.
// See the unit test for usage and behavior.
namespace webrtc {
class FieldTrialListBase : public FieldTrialParameterInterface {
protected:
friend class FieldTrialListWrapper;
explicit FieldTrialListBase(absl::string_view key);
bool Failed() const;
bool Used() const;
virtual int Size() = 0;
bool failed_;
bool parse_got_called_;
};
// This class represents a vector of type T. The elements are separated by a |
// and parsed using ParseTypedParameter.
template <typename T>
class FieldTrialList : public FieldTrialListBase {
public:
explicit FieldTrialList(absl::string_view key) : FieldTrialList(key, {}) {}
FieldTrialList(absl::string_view key, std::initializer_list<T> default_values)
: FieldTrialListBase(key), values_(default_values) {}
std::vector<T> Get() const { return values_; }
operator std::vector<T>() const { return Get(); }
typename std::vector<T>::const_reference operator[](size_t index) const {
return values_[index];
}
const std::vector<T>* operator->() const { return &values_; }
protected:
bool Parse(absl::optional<std::string> str_value) override {
parse_got_called_ = true;
if (!str_value) {
values_.clear();
return true;
}
std::vector<T> new_values_;
for (const absl::string_view token : rtc::split(str_value.value(), '|')) {
absl::optional<T> value = ParseTypedParameter<T>(token);
if (value) {
new_values_.push_back(*value);
} else {
failed_ = true;
return false;
}
}
values_.swap(new_values_);
return true;
}
int Size() override { return values_.size(); }
private:
std::vector<T> values_;
};
class FieldTrialListWrapper {
public:
virtual ~FieldTrialListWrapper() = default;
// Takes the element at the given index in the wrapped list and writes it to
// the given struct.
virtual void WriteElement(void* struct_to_write, int index) = 0;
virtual FieldTrialListBase* GetList() = 0;
int Length();
// Returns true iff the wrapped list has failed to parse at least one token.
bool Failed();
bool Used();
protected:
FieldTrialListWrapper() = default;
};
namespace field_trial_list_impl {
// The LambdaTypeTraits struct provides type information about lambdas in the
// template expressions below.
template <typename T>
struct LambdaTypeTraits : public LambdaTypeTraits<decltype(&T::operator())> {};
template <typename ClassType, typename RetType, typename SourceType>
struct LambdaTypeTraits<RetType* (ClassType::*)(SourceType*) const> {
using ret = RetType;
using src = SourceType;
};
template <typename T>
struct TypedFieldTrialListWrapper : FieldTrialListWrapper {
public:
TypedFieldTrialListWrapper(absl::string_view key,
std::function<void(void*, T)> sink)
: list_(key), sink_(sink) {}
void WriteElement(void* struct_to_write, int index) override {
sink_(struct_to_write, list_[index]);
}
FieldTrialListBase* GetList() override { return &list_; }
private:
FieldTrialList<T> list_;
std::function<void(void*, T)> sink_;
};
} // namespace field_trial_list_impl
template <typename F,
typename Traits = typename field_trial_list_impl::LambdaTypeTraits<F>>
FieldTrialListWrapper* FieldTrialStructMember(absl::string_view key,
F accessor) {
return new field_trial_list_impl::TypedFieldTrialListWrapper<
typename Traits::ret>(key, [accessor](void* s, typename Traits::ret t) {
*accessor(static_cast<typename Traits::src*>(s)) = t;
});
}
// This base class is here to reduce the amount of code we have to generate for
// each type of FieldTrialStructList.
class FieldTrialStructListBase : public FieldTrialParameterInterface {
protected:
FieldTrialStructListBase(
std::initializer_list<FieldTrialListWrapper*> sub_lists)
: FieldTrialParameterInterface(""), sub_lists_() {
// Take ownership of the list wrappers generated by FieldTrialStructMember
// on the call site.
for (FieldTrialListWrapper* const* it = sub_lists.begin();
it != sub_lists.end(); it++) {
sub_parameters_.push_back((*it)->GetList());
sub_lists_.push_back(std::unique_ptr<FieldTrialListWrapper>(*it));
}
}
// Check that all of our sublists that were in the field trial string had the
// same number of elements. If they do, we return that length. If they had
// different lengths, any sublist had parse failures or no sublists had
// user-supplied values, we return -1.
int ValidateAndGetLength();
bool Parse(absl::optional<std::string> str_value) override;
std::vector<std::unique_ptr<FieldTrialListWrapper>> sub_lists_;
};
template <typename S>
class FieldTrialStructList : public FieldTrialStructListBase {
public:
FieldTrialStructList(std::initializer_list<FieldTrialListWrapper*> l,
std::initializer_list<S> default_list)
: FieldTrialStructListBase(l), values_(default_list) {}
std::vector<S> Get() const { return values_; }
operator std::vector<S>() const { return Get(); }
const S& operator[](size_t index) const { return values_[index]; }
const std::vector<S>* operator->() const { return &values_; }
protected:
void ParseDone() override {
int length = ValidateAndGetLength();
if (length == -1)
return;
std::vector<S> new_values(length, S());
for (std::unique_ptr<FieldTrialListWrapper>& li : sub_lists_) {
if (li->Used()) {
for (int i = 0; i < length; i++) {
li->WriteElement(&new_values[i], i);
}
}
}
values_.swap(new_values);
}
private:
std::vector<S> values_;
};
} // namespace webrtc
#endif // RTC_BASE_EXPERIMENTS_FIELD_TRIAL_LIST_H_

View file

@ -0,0 +1,260 @@
/*
* Copyright 2019 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 "rtc_base/experiments/field_trial_parser.h"
#include <inttypes.h>
#include <algorithm>
#include <map>
#include <type_traits>
#include <utility>
#include "absl/strings/string_view.h"
#include "rtc_base/checks.h"
#include "rtc_base/logging.h"
#include "rtc_base/numerics/safe_conversions.h"
namespace webrtc {
FieldTrialParameterInterface::FieldTrialParameterInterface(
absl::string_view key)
: key_(key) {}
FieldTrialParameterInterface::~FieldTrialParameterInterface() {
RTC_DCHECK(used_) << "Field trial parameter with key: '" << key_
<< "' never used.";
}
void ParseFieldTrial(
std::initializer_list<FieldTrialParameterInterface*> fields,
absl::string_view trial_string) {
std::map<absl::string_view, FieldTrialParameterInterface*> field_map;
FieldTrialParameterInterface* keyless_field = nullptr;
for (FieldTrialParameterInterface* field : fields) {
field->MarkAsUsed();
if (!field->sub_parameters_.empty()) {
for (FieldTrialParameterInterface* sub_field : field->sub_parameters_) {
RTC_DCHECK(!sub_field->key_.empty());
sub_field->MarkAsUsed();
field_map[sub_field->key_] = sub_field;
}
continue;
}
if (field->key_.empty()) {
RTC_DCHECK(!keyless_field);
keyless_field = field;
} else {
field_map[field->key_] = field;
}
}
bool logged_unknown_key = false;
absl::string_view tail = trial_string;
while (!tail.empty()) {
size_t key_end = tail.find_first_of(",:");
absl::string_view key = tail.substr(0, key_end);
absl::optional<std::string> opt_value;
if (key_end == absl::string_view::npos) {
tail = "";
} else if (tail[key_end] == ':') {
tail = tail.substr(key_end + 1);
size_t value_end = tail.find(',');
opt_value.emplace(tail.substr(0, value_end));
if (value_end == absl::string_view::npos) {
tail = "";
} else {
tail = tail.substr(value_end + 1);
}
} else {
RTC_DCHECK_EQ(tail[key_end], ',');
tail = tail.substr(key_end + 1);
}
auto field = field_map.find(key);
if (field != field_map.end()) {
if (!field->second->Parse(std::move(opt_value))) {
RTC_LOG(LS_WARNING) << "Failed to read field with key: '" << key
<< "' in trial: \"" << trial_string << "\"";
}
} else if (!opt_value && keyless_field && !key.empty()) {
if (!keyless_field->Parse(std::string(key))) {
RTC_LOG(LS_WARNING) << "Failed to read empty key field with value '"
<< key << "' in trial: \"" << trial_string << "\"";
}
} else if (key.empty() || key[0] != '_') {
// "_" is be used to prefix keys that are part of the string for
// debugging purposes but not neccessarily used.
// e.g. WebRTC-Experiment/param: value, _DebuggingString
if (!logged_unknown_key) {
RTC_LOG(LS_INFO) << "No field with key: '" << key
<< "' (found in trial: \"" << trial_string << "\")";
std::string valid_keys;
for (const auto& f : field_map) {
valid_keys.append(f.first.data(), f.first.size());
valid_keys += ", ";
}
RTC_LOG(LS_INFO) << "Valid keys are: " << valid_keys;
logged_unknown_key = true;
}
}
}
for (FieldTrialParameterInterface* field : fields) {
field->ParseDone();
}
}
template <>
absl::optional<bool> ParseTypedParameter<bool>(absl::string_view str) {
if (str == "true" || str == "1") {
return true;
} else if (str == "false" || str == "0") {
return false;
}
return absl::nullopt;
}
template <>
absl::optional<double> ParseTypedParameter<double>(absl::string_view str) {
double value;
char unit[2]{0, 0};
if (sscanf(std::string(str).c_str(), "%lf%1s", &value, unit) >= 1) {
if (unit[0] == '%')
return value / 100;
return value;
} else {
return absl::nullopt;
}
}
template <>
absl::optional<int> ParseTypedParameter<int>(absl::string_view str) {
int64_t value;
if (sscanf(std::string(str).c_str(), "%" SCNd64, &value) == 1) {
if (rtc::IsValueInRangeForNumericType<int, int64_t>(value)) {
return static_cast<int>(value);
}
}
return absl::nullopt;
}
template <>
absl::optional<unsigned> ParseTypedParameter<unsigned>(absl::string_view str) {
int64_t value;
if (sscanf(std::string(str).c_str(), "%" SCNd64, &value) == 1) {
if (rtc::IsValueInRangeForNumericType<unsigned, int64_t>(value)) {
return static_cast<unsigned>(value);
}
}
return absl::nullopt;
}
template <>
absl::optional<std::string> ParseTypedParameter<std::string>(
absl::string_view str) {
return std::string(str);
}
template <>
absl::optional<absl::optional<bool>> ParseTypedParameter<absl::optional<bool>>(
absl::string_view str) {
return ParseOptionalParameter<bool>(str);
}
template <>
absl::optional<absl::optional<int>> ParseTypedParameter<absl::optional<int>>(
absl::string_view str) {
return ParseOptionalParameter<int>(str);
}
template <>
absl::optional<absl::optional<unsigned>>
ParseTypedParameter<absl::optional<unsigned>>(absl::string_view str) {
return ParseOptionalParameter<unsigned>(str);
}
template <>
absl::optional<absl::optional<double>>
ParseTypedParameter<absl::optional<double>>(absl::string_view str) {
return ParseOptionalParameter<double>(str);
}
FieldTrialFlag::FieldTrialFlag(absl::string_view key)
: FieldTrialFlag(key, false) {}
FieldTrialFlag::FieldTrialFlag(absl::string_view key, bool default_value)
: FieldTrialParameterInterface(key), value_(default_value) {}
bool FieldTrialFlag::Get() const {
return value_;
}
webrtc::FieldTrialFlag::operator bool() const {
return value_;
}
bool FieldTrialFlag::Parse(absl::optional<std::string> str_value) {
// Only set the flag if there is no argument provided.
if (str_value) {
absl::optional<bool> opt_value = ParseTypedParameter<bool>(*str_value);
if (!opt_value)
return false;
value_ = *opt_value;
} else {
value_ = true;
}
return true;
}
AbstractFieldTrialEnum::AbstractFieldTrialEnum(
absl::string_view key,
int default_value,
std::map<std::string, int> mapping)
: FieldTrialParameterInterface(key),
value_(default_value),
enum_mapping_(mapping) {
for (auto& key_val : enum_mapping_)
valid_values_.insert(key_val.second);
}
AbstractFieldTrialEnum::AbstractFieldTrialEnum(const AbstractFieldTrialEnum&) =
default;
AbstractFieldTrialEnum::~AbstractFieldTrialEnum() = default;
bool AbstractFieldTrialEnum::Parse(absl::optional<std::string> str_value) {
if (str_value) {
auto it = enum_mapping_.find(*str_value);
if (it != enum_mapping_.end()) {
value_ = it->second;
return true;
}
absl::optional<int> value = ParseTypedParameter<int>(*str_value);
if (value.has_value() &&
(valid_values_.find(*value) != valid_values_.end())) {
value_ = *value;
return true;
}
}
return false;
}
template class FieldTrialParameter<bool>;
template class FieldTrialParameter<double>;
template class FieldTrialParameter<int>;
template class FieldTrialParameter<unsigned>;
template class FieldTrialParameter<std::string>;
template class FieldTrialConstrained<double>;
template class FieldTrialConstrained<int>;
template class FieldTrialConstrained<unsigned>;
template class FieldTrialOptional<double>;
template class FieldTrialOptional<int>;
template class FieldTrialOptional<unsigned>;
template class FieldTrialOptional<bool>;
template class FieldTrialOptional<std::string>;
} // namespace webrtc

View file

@ -0,0 +1,291 @@
/*
* Copyright 2018 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.
*/
#ifndef RTC_BASE_EXPERIMENTS_FIELD_TRIAL_PARSER_H_
#define RTC_BASE_EXPERIMENTS_FIELD_TRIAL_PARSER_H_
#include <stdint.h>
#include <initializer_list>
#include <map>
#include <set>
#include <string>
#include <vector>
#include "absl/strings/string_view.h"
#include "absl/types/optional.h"
// Field trial parser functionality. Provides funcitonality to parse field trial
// argument strings in key:value format. Each parameter is described using
// key:value, parameters are separated with a ,. Values can't include the comma
// character, since there's no quote facility. For most types, white space is
// ignored. Parameters are declared with a given type for which an
// implementation of ParseTypedParameter should be provided. The
// ParseTypedParameter implementation is given whatever is between the : and the
// ,. If the key is provided without : a FieldTrialOptional will use nullopt.
// Example string: "my_optional,my_int:3,my_string:hello"
// For further description of usage and behavior, see the examples in the unit
// tests.
namespace webrtc {
class FieldTrialParameterInterface {
public:
virtual ~FieldTrialParameterInterface();
std::string key() const { return key_; }
protected:
// Protected to allow implementations to provide assignment and copy.
FieldTrialParameterInterface(const FieldTrialParameterInterface&) = default;
FieldTrialParameterInterface& operator=(const FieldTrialParameterInterface&) =
default;
explicit FieldTrialParameterInterface(absl::string_view key);
friend void ParseFieldTrial(
std::initializer_list<FieldTrialParameterInterface*> fields,
absl::string_view trial_string);
void MarkAsUsed() { used_ = true; }
virtual bool Parse(absl::optional<std::string> str_value) = 0;
virtual void ParseDone() {}
std::vector<FieldTrialParameterInterface*> sub_parameters_;
private:
std::string key_;
bool used_ = false;
};
// ParseFieldTrial function parses the given string and fills the given fields
// with extracted values if available.
void ParseFieldTrial(
std::initializer_list<FieldTrialParameterInterface*> fields,
absl::string_view trial_string);
// Specialize this in code file for custom types. Should return absl::nullopt if
// the given string cannot be properly parsed.
template <typename T>
absl::optional<T> ParseTypedParameter(absl::string_view);
// This class uses the ParseTypedParameter function to implement a parameter
// implementation with an enforced default value.
template <typename T>
class FieldTrialParameter : public FieldTrialParameterInterface {
public:
FieldTrialParameter(absl::string_view key, T default_value)
: FieldTrialParameterInterface(key), value_(default_value) {}
T Get() const { return value_; }
operator T() const { return Get(); }
const T* operator->() const { return &value_; }
void SetForTest(T value) { value_ = value; }
protected:
bool Parse(absl::optional<std::string> str_value) override {
if (str_value) {
absl::optional<T> value = ParseTypedParameter<T>(*str_value);
if (value.has_value()) {
value_ = value.value();
return true;
}
}
return false;
}
private:
T value_;
};
// This class uses the ParseTypedParameter function to implement a parameter
// implementation with an enforced default value and a range constraint. Values
// outside the configured range will be ignored.
template <typename T>
class FieldTrialConstrained : public FieldTrialParameterInterface {
public:
FieldTrialConstrained(absl::string_view key,
T default_value,
absl::optional<T> lower_limit,
absl::optional<T> upper_limit)
: FieldTrialParameterInterface(key),
value_(default_value),
lower_limit_(lower_limit),
upper_limit_(upper_limit) {}
T Get() const { return value_; }
operator T() const { return Get(); }
const T* operator->() const { return &value_; }
protected:
bool Parse(absl::optional<std::string> str_value) override {
if (str_value) {
absl::optional<T> value = ParseTypedParameter<T>(*str_value);
if (value && (!lower_limit_ || *value >= *lower_limit_) &&
(!upper_limit_ || *value <= *upper_limit_)) {
value_ = *value;
return true;
}
}
return false;
}
private:
T value_;
absl::optional<T> lower_limit_;
absl::optional<T> upper_limit_;
};
class AbstractFieldTrialEnum : public FieldTrialParameterInterface {
public:
AbstractFieldTrialEnum(absl::string_view key,
int default_value,
std::map<std::string, int> mapping);
~AbstractFieldTrialEnum() override;
AbstractFieldTrialEnum(const AbstractFieldTrialEnum&);
protected:
bool Parse(absl::optional<std::string> str_value) override;
protected:
int value_;
std::map<std::string, int> enum_mapping_;
std::set<int> valid_values_;
};
// The FieldTrialEnum class can be used to quickly define a parser for a
// specific enum. It handles values provided as integers and as strings if a
// mapping is provided.
template <typename T>
class FieldTrialEnum : public AbstractFieldTrialEnum {
public:
FieldTrialEnum(absl::string_view key,
T default_value,
std::map<std::string, T> mapping)
: AbstractFieldTrialEnum(key,
static_cast<int>(default_value),
ToIntMap(mapping)) {}
T Get() const { return static_cast<T>(value_); }
operator T() const { return Get(); }
private:
static std::map<std::string, int> ToIntMap(std::map<std::string, T> mapping) {
std::map<std::string, int> res;
for (const auto& it : mapping)
res[it.first] = static_cast<int>(it.second);
return res;
}
};
// This class uses the ParseTypedParameter function to implement an optional
// parameter implementation that can default to absl::nullopt.
template <typename T>
class FieldTrialOptional : public FieldTrialParameterInterface {
public:
explicit FieldTrialOptional(absl::string_view key)
: FieldTrialParameterInterface(key) {}
FieldTrialOptional(absl::string_view key, absl::optional<T> default_value)
: FieldTrialParameterInterface(key), value_(default_value) {}
absl::optional<T> GetOptional() const { return value_; }
const T& Value() const { return value_.value(); }
const T& operator*() const { return value_.value(); }
const T* operator->() const { return &value_.value(); }
explicit operator bool() const { return value_.has_value(); }
protected:
bool Parse(absl::optional<std::string> str_value) override {
if (str_value) {
absl::optional<T> value = ParseTypedParameter<T>(*str_value);
if (!value.has_value())
return false;
value_ = value.value();
} else {
value_ = absl::nullopt;
}
return true;
}
private:
absl::optional<T> value_;
};
// Equivalent to a FieldTrialParameter<bool> in the case that both key and value
// are present. If key is missing, evaluates to false. If key is present, but no
// explicit value is provided, the flag evaluates to true.
class FieldTrialFlag : public FieldTrialParameterInterface {
public:
explicit FieldTrialFlag(absl::string_view key);
FieldTrialFlag(absl::string_view key, bool default_value);
bool Get() const;
explicit operator bool() const;
protected:
bool Parse(absl::optional<std::string> str_value) override;
private:
bool value_;
};
template <typename T>
absl::optional<absl::optional<T>> ParseOptionalParameter(
absl::string_view str) {
if (str.empty())
return absl::optional<T>();
auto parsed = ParseTypedParameter<T>(str);
if (parsed.has_value())
return parsed;
return absl::nullopt;
}
template <>
absl::optional<bool> ParseTypedParameter<bool>(absl::string_view str);
template <>
absl::optional<double> ParseTypedParameter<double>(absl::string_view str);
template <>
absl::optional<int> ParseTypedParameter<int>(absl::string_view str);
template <>
absl::optional<unsigned> ParseTypedParameter<unsigned>(absl::string_view str);
template <>
absl::optional<std::string> ParseTypedParameter<std::string>(
absl::string_view str);
template <>
absl::optional<absl::optional<bool>> ParseTypedParameter<absl::optional<bool>>(
absl::string_view str);
template <>
absl::optional<absl::optional<int>> ParseTypedParameter<absl::optional<int>>(
absl::string_view str);
template <>
absl::optional<absl::optional<unsigned>>
ParseTypedParameter<absl::optional<unsigned>>(absl::string_view str);
template <>
absl::optional<absl::optional<double>>
ParseTypedParameter<absl::optional<double>>(absl::string_view str);
// Accepts true, false, else parsed with sscanf %i, true if != 0.
extern template class FieldTrialParameter<bool>;
// Interpreted using sscanf %lf.
extern template class FieldTrialParameter<double>;
// Interpreted using sscanf %i.
extern template class FieldTrialParameter<int>;
// Interpreted using sscanf %u.
extern template class FieldTrialParameter<unsigned>;
// Using the given value as is.
extern template class FieldTrialParameter<std::string>;
extern template class FieldTrialConstrained<double>;
extern template class FieldTrialConstrained<int>;
extern template class FieldTrialConstrained<unsigned>;
extern template class FieldTrialOptional<double>;
extern template class FieldTrialOptional<int>;
extern template class FieldTrialOptional<unsigned>;
extern template class FieldTrialOptional<bool>;
extern template class FieldTrialOptional<std::string>;
} // namespace webrtc
#endif // RTC_BASE_EXPERIMENTS_FIELD_TRIAL_PARSER_H_

View file

@ -0,0 +1,116 @@
/*
* Copyright 2018 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 "rtc_base/experiments/field_trial_units.h"
#include <stdio.h>
#include <limits>
#include <string>
#include "absl/strings/string_view.h"
#include "absl/types/optional.h"
// Large enough to fit "seconds", the longest supported unit name.
#define RTC_TRIAL_UNIT_LENGTH_STR "7"
#define RTC_TRIAL_UNIT_SIZE 8
namespace webrtc {
namespace {
struct ValueWithUnit {
double value;
std::string unit;
};
absl::optional<ValueWithUnit> ParseValueWithUnit(absl::string_view str) {
if (str == "inf") {
return ValueWithUnit{std::numeric_limits<double>::infinity(), ""};
} else if (str == "-inf") {
return ValueWithUnit{-std::numeric_limits<double>::infinity(), ""};
} else {
double double_val;
char unit_char[RTC_TRIAL_UNIT_SIZE];
unit_char[0] = 0;
if (sscanf(std::string(str).c_str(), "%lf%" RTC_TRIAL_UNIT_LENGTH_STR "s",
&double_val, unit_char) >= 1) {
return ValueWithUnit{double_val, unit_char};
}
}
return absl::nullopt;
}
} // namespace
template <>
absl::optional<DataRate> ParseTypedParameter<DataRate>(absl::string_view str) {
absl::optional<ValueWithUnit> result = ParseValueWithUnit(str);
if (result) {
if (result->unit.empty() || result->unit == "kbps") {
return DataRate::KilobitsPerSec(result->value);
} else if (result->unit == "bps") {
return DataRate::BitsPerSec(result->value);
}
}
return absl::nullopt;
}
template <>
absl::optional<DataSize> ParseTypedParameter<DataSize>(absl::string_view str) {
absl::optional<ValueWithUnit> result = ParseValueWithUnit(str);
if (result) {
if (result->unit.empty() || result->unit == "bytes")
return DataSize::Bytes(result->value);
}
return absl::nullopt;
}
template <>
absl::optional<TimeDelta> ParseTypedParameter<TimeDelta>(
absl::string_view str) {
absl::optional<ValueWithUnit> result = ParseValueWithUnit(str);
if (result) {
if (result->unit == "s" || result->unit == "seconds") {
return TimeDelta::Seconds(result->value);
} else if (result->unit == "us") {
return TimeDelta::Micros(result->value);
} else if (result->unit.empty() || result->unit == "ms") {
return TimeDelta::Millis(result->value);
}
}
return absl::nullopt;
}
template <>
absl::optional<absl::optional<DataRate>>
ParseTypedParameter<absl::optional<DataRate>>(absl::string_view str) {
return ParseOptionalParameter<DataRate>(str);
}
template <>
absl::optional<absl::optional<DataSize>>
ParseTypedParameter<absl::optional<DataSize>>(absl::string_view str) {
return ParseOptionalParameter<DataSize>(str);
}
template <>
absl::optional<absl::optional<TimeDelta>>
ParseTypedParameter<absl::optional<TimeDelta>>(absl::string_view str) {
return ParseOptionalParameter<TimeDelta>(str);
}
template class FieldTrialParameter<DataRate>;
template class FieldTrialParameter<DataSize>;
template class FieldTrialParameter<TimeDelta>;
template class FieldTrialConstrained<DataRate>;
template class FieldTrialConstrained<DataSize>;
template class FieldTrialConstrained<TimeDelta>;
template class FieldTrialOptional<DataRate>;
template class FieldTrialOptional<DataSize>;
template class FieldTrialOptional<TimeDelta>;
} // namespace webrtc

View file

@ -0,0 +1,41 @@
/*
* Copyright 2018 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.
*/
#ifndef RTC_BASE_EXPERIMENTS_FIELD_TRIAL_UNITS_H_
#define RTC_BASE_EXPERIMENTS_FIELD_TRIAL_UNITS_H_
#include "absl/strings/string_view.h"
#include "api/units/data_rate.h"
#include "api/units/data_size.h"
#include "api/units/time_delta.h"
#include "rtc_base/experiments/field_trial_parser.h"
namespace webrtc {
template <>
absl::optional<DataRate> ParseTypedParameter<DataRate>(absl::string_view str);
template <>
absl::optional<DataSize> ParseTypedParameter<DataSize>(absl::string_view str);
template <>
absl::optional<TimeDelta> ParseTypedParameter<TimeDelta>(absl::string_view str);
extern template class FieldTrialParameter<DataRate>;
extern template class FieldTrialParameter<DataSize>;
extern template class FieldTrialParameter<TimeDelta>;
extern template class FieldTrialConstrained<DataRate>;
extern template class FieldTrialConstrained<DataSize>;
extern template class FieldTrialConstrained<TimeDelta>;
extern template class FieldTrialOptional<DataRate>;
extern template class FieldTrialOptional<DataSize>;
extern template class FieldTrialOptional<TimeDelta>;
} // namespace webrtc
#endif // RTC_BASE_EXPERIMENTS_FIELD_TRIAL_UNITS_H_

View file

@ -0,0 +1,39 @@
/*
* Copyright (c) 2019 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 "rtc_base/experiments/keyframe_interval_settings.h"
#include "api/transport/field_trial_based_config.h"
namespace webrtc {
namespace {
constexpr char kFieldTrialName[] = "WebRTC-KeyframeInterval";
} // namespace
KeyframeIntervalSettings::KeyframeIntervalSettings(
const FieldTrialsView* const key_value_config)
: min_keyframe_send_interval_ms_("min_keyframe_send_interval_ms") {
ParseFieldTrial({&min_keyframe_send_interval_ms_},
key_value_config->Lookup(kFieldTrialName));
}
KeyframeIntervalSettings KeyframeIntervalSettings::ParseFromFieldTrials() {
FieldTrialBasedConfig field_trial_config;
return KeyframeIntervalSettings(&field_trial_config);
}
absl::optional<int> KeyframeIntervalSettings::MinKeyframeSendIntervalMs()
const {
return min_keyframe_send_interval_ms_.GetOptional();
}
} // namespace webrtc

View file

@ -0,0 +1,39 @@
/*
* Copyright (c) 2019 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.
*/
#ifndef RTC_BASE_EXPERIMENTS_KEYFRAME_INTERVAL_SETTINGS_H_
#define RTC_BASE_EXPERIMENTS_KEYFRAME_INTERVAL_SETTINGS_H_
#include "absl/types/optional.h"
#include "api/field_trials_view.h"
#include "rtc_base/experiments/field_trial_parser.h"
namespace webrtc {
// TODO(bugs.webrtc.org/10427): Remove and replace with proper configuration
// parameter, or move to using FIR if intent is to avoid triggering multiple
// times to PLIs corresponding to the same request when RTT is large.
class KeyframeIntervalSettings final {
public:
static KeyframeIntervalSettings ParseFromFieldTrials();
// Sender side.
// The encoded keyframe send rate is <= 1/MinKeyframeSendIntervalMs().
absl::optional<int> MinKeyframeSendIntervalMs() const;
private:
explicit KeyframeIntervalSettings(const FieldTrialsView* key_value_config);
FieldTrialOptional<int> min_keyframe_send_interval_ms_;
};
} // namespace webrtc
#endif // RTC_BASE_EXPERIMENTS_KEYFRAME_INTERVAL_SETTINGS_H_

View file

@ -0,0 +1,116 @@
/*
* Copyright 2019 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 "rtc_base/experiments/min_video_bitrate_experiment.h"
#include <string>
#include "rtc_base/checks.h"
#include "rtc_base/experiments/field_trial_parser.h"
#include "rtc_base/logging.h"
#include "system_wrappers/include/field_trial.h"
namespace webrtc {
const int kDefaultMinVideoBitrateBps = 30000;
namespace {
const char kForcedFallbackFieldTrial[] =
"WebRTC-VP8-Forced-Fallback-Encoder-v2";
const char kMinVideoBitrateExperiment[] = "WebRTC-Video-MinVideoBitrate";
absl::optional<int> GetFallbackMinBpsFromFieldTrial(VideoCodecType type) {
if (type != kVideoCodecVP8) {
return absl::nullopt;
}
if (!webrtc::field_trial::IsEnabled(kForcedFallbackFieldTrial)) {
return absl::nullopt;
}
const std::string group =
webrtc::field_trial::FindFullName(kForcedFallbackFieldTrial);
if (group.empty()) {
return absl::nullopt;
}
int min_pixels; // Ignored.
int max_pixels; // Ignored.
int min_bps;
if (sscanf(group.c_str(), "Enabled-%d,%d,%d", &min_pixels, &max_pixels,
&min_bps) != 3) {
return absl::nullopt;
}
if (min_bps <= 0) {
return absl::nullopt;
}
return min_bps;
}
} // namespace
absl::optional<DataRate> GetExperimentalMinVideoBitrate(VideoCodecType type) {
const absl::optional<int> fallback_min_bitrate_bps =
GetFallbackMinBpsFromFieldTrial(type);
if (fallback_min_bitrate_bps) {
return DataRate::BitsPerSec(*fallback_min_bitrate_bps);
}
if (webrtc::field_trial::IsEnabled(kMinVideoBitrateExperiment)) {
webrtc::FieldTrialFlag enabled("Enabled");
// Backwards-compatibility with an old experiment - a generic minimum which,
// if set, applies to all codecs.
webrtc::FieldTrialOptional<webrtc::DataRate> min_video_bitrate("br");
// New experiment - per-codec minimum bitrate.
webrtc::FieldTrialOptional<webrtc::DataRate> min_bitrate_vp8("vp8_br");
webrtc::FieldTrialOptional<webrtc::DataRate> min_bitrate_vp9("vp9_br");
webrtc::FieldTrialOptional<webrtc::DataRate> min_bitrate_av1("av1_br");
webrtc::FieldTrialOptional<webrtc::DataRate> min_bitrate_h264("h264_br");
webrtc::ParseFieldTrial(
{&enabled, &min_video_bitrate, &min_bitrate_vp8, &min_bitrate_vp9,
&min_bitrate_av1, &min_bitrate_h264},
webrtc::field_trial::FindFullName(kMinVideoBitrateExperiment));
if (min_video_bitrate) {
if (min_bitrate_vp8 || min_bitrate_vp9 || min_bitrate_av1 ||
min_bitrate_h264) {
// "br" is mutually-exclusive with the other configuration possibilites.
RTC_LOG(LS_WARNING) << "Self-contradictory experiment config.";
}
return *min_video_bitrate;
}
switch (type) {
case kVideoCodecVP8:
return min_bitrate_vp8.GetOptional();
case kVideoCodecH265:
// TODO(bugs.webrtc.org/13485): Use VP9 bitrate limits for now.
case kVideoCodecVP9:
return min_bitrate_vp9.GetOptional();
case kVideoCodecAV1:
return min_bitrate_av1.GetOptional();
case kVideoCodecH264:
return min_bitrate_h264.GetOptional();
case kVideoCodecGeneric:
case kVideoCodecMultiplex:
return absl::nullopt;
}
RTC_DCHECK_NOTREACHED();
}
return absl::nullopt;
}
} // namespace webrtc

View file

@ -0,0 +1,28 @@
/*
* Copyright 2019 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.
*/
#ifndef RTC_BASE_EXPERIMENTS_MIN_VIDEO_BITRATE_EXPERIMENT_H_
#define RTC_BASE_EXPERIMENTS_MIN_VIDEO_BITRATE_EXPERIMENT_H_
#include "absl/types/optional.h"
#include "api/units/data_rate.h"
#include "api/video/video_codec_type.h"
namespace webrtc {
extern const int kDefaultMinVideoBitrateBps;
// Return the experiment-driven minimum video bitrate.
// If no experiment is effective, returns nullopt.
absl::optional<DataRate> GetExperimentalMinVideoBitrate(VideoCodecType type);
} // namespace webrtc
#endif // RTC_BASE_EXPERIMENTS_MIN_VIDEO_BITRATE_EXPERIMENT_H_

View file

@ -0,0 +1,49 @@
/*
* Copyright 2018 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 "rtc_base/experiments/normalize_simulcast_size_experiment.h"
#include <stdio.h>
#include <string>
#include "rtc_base/logging.h"
#include "system_wrappers/include/field_trial.h"
namespace webrtc {
namespace {
constexpr char kFieldTrial[] = "WebRTC-NormalizeSimulcastResolution";
constexpr int kMinSetting = 0;
constexpr int kMaxSetting = 5;
} // namespace
absl::optional<int> NormalizeSimulcastSizeExperiment::GetBase2Exponent() {
if (!webrtc::field_trial::IsEnabled(kFieldTrial))
return absl::nullopt;
const std::string group = webrtc::field_trial::FindFullName(kFieldTrial);
if (group.empty())
return absl::nullopt;
int exponent;
if (sscanf(group.c_str(), "Enabled-%d", &exponent) != 1) {
RTC_LOG(LS_WARNING) << "No parameter provided.";
return absl::nullopt;
}
if (exponent < kMinSetting || exponent > kMaxSetting) {
RTC_LOG(LS_WARNING) << "Unsupported exp value provided, value ignored.";
return absl::nullopt;
}
return absl::optional<int>(exponent);
}
} // namespace webrtc

View file

@ -0,0 +1,25 @@
/*
* Copyright 2018 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.
*/
#ifndef RTC_BASE_EXPERIMENTS_NORMALIZE_SIMULCAST_SIZE_EXPERIMENT_H_
#define RTC_BASE_EXPERIMENTS_NORMALIZE_SIMULCAST_SIZE_EXPERIMENT_H_
#include "absl/types/optional.h"
namespace webrtc {
class NormalizeSimulcastSizeExperiment {
public:
// Returns the base two exponent from field trial.
static absl::optional<int> GetBase2Exponent();
};
} // namespace webrtc
#endif // RTC_BASE_EXPERIMENTS_NORMALIZE_SIMULCAST_SIZE_EXPERIMENT_H_

View file

@ -0,0 +1,82 @@
/*
* Copyright (c) 2019 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 "rtc_base/experiments/quality_rampup_experiment.h"
#include <algorithm>
#include "api/transport/field_trial_based_config.h"
#include "rtc_base/logging.h"
namespace webrtc {
QualityRampupExperiment::QualityRampupExperiment(
const FieldTrialsView* const key_value_config)
: min_pixels_("min_pixels"),
min_duration_ms_("min_duration_ms"),
max_bitrate_factor_("max_bitrate_factor") {
ParseFieldTrial(
{&min_pixels_, &min_duration_ms_, &max_bitrate_factor_},
key_value_config->Lookup("WebRTC-Video-QualityRampupSettings"));
}
QualityRampupExperiment QualityRampupExperiment::ParseSettings() {
FieldTrialBasedConfig field_trial_config;
return QualityRampupExperiment(&field_trial_config);
}
absl::optional<int> QualityRampupExperiment::MinPixels() const {
return min_pixels_.GetOptional();
}
absl::optional<int> QualityRampupExperiment::MinDurationMs() const {
return min_duration_ms_.GetOptional();
}
absl::optional<double> QualityRampupExperiment::MaxBitrateFactor() const {
return max_bitrate_factor_.GetOptional();
}
void QualityRampupExperiment::SetMaxBitrate(int pixels,
uint32_t max_bitrate_kbps) {
if (!min_pixels_ || pixels < min_pixels_.Value() || max_bitrate_kbps == 0) {
return;
}
max_bitrate_kbps_ = std::max(max_bitrate_kbps_.value_or(0), max_bitrate_kbps);
}
bool QualityRampupExperiment::BwHigh(int64_t now_ms,
uint32_t available_bw_kbps) {
if (!min_pixels_ || !min_duration_ms_ || !max_bitrate_kbps_) {
return false;
}
if (available_bw_kbps <
max_bitrate_kbps_.value() * MaxBitrateFactor().value_or(1)) {
start_ms_.reset();
return false;
}
if (!start_ms_)
start_ms_ = now_ms;
return (now_ms - *start_ms_) >= min_duration_ms_.Value();
}
void QualityRampupExperiment::Reset() {
start_ms_.reset();
max_bitrate_kbps_.reset();
}
bool QualityRampupExperiment::Enabled() const {
return min_pixels_ && min_duration_ms_;
}
} // namespace webrtc

View file

@ -0,0 +1,53 @@
/*
* Copyright (c) 2019 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.
*/
#ifndef RTC_BASE_EXPERIMENTS_QUALITY_RAMPUP_EXPERIMENT_H_
#define RTC_BASE_EXPERIMENTS_QUALITY_RAMPUP_EXPERIMENT_H_
#include "absl/types/optional.h"
#include "api/field_trials_view.h"
#include "rtc_base/experiments/field_trial_parser.h"
namespace webrtc {
class QualityRampupExperiment final {
public:
static QualityRampupExperiment ParseSettings();
absl::optional<int> MinPixels() const;
absl::optional<int> MinDurationMs() const;
absl::optional<double> MaxBitrateFactor() const;
// Sets the max bitrate and the frame size.
// The call has no effect if the frame size is less than `min_pixels_`.
void SetMaxBitrate(int pixels, uint32_t max_bitrate_kbps);
// Returns true if the available bandwidth is a certain percentage
// (max_bitrate_factor_) above `max_bitrate_kbps_` for `min_duration_ms_`.
bool BwHigh(int64_t now_ms, uint32_t available_bw_kbps);
void Reset();
bool Enabled() const;
private:
explicit QualityRampupExperiment(
const FieldTrialsView* const key_value_config);
FieldTrialOptional<int> min_pixels_;
FieldTrialOptional<int> min_duration_ms_;
FieldTrialOptional<double> max_bitrate_factor_;
absl::optional<int64_t> start_ms_;
absl::optional<uint32_t> max_bitrate_kbps_;
};
} // namespace webrtc
#endif // RTC_BASE_EXPERIMENTS_QUALITY_RAMPUP_EXPERIMENT_H_

View file

@ -0,0 +1,96 @@
/*
* Copyright (c) 2019 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 "rtc_base/experiments/quality_scaler_settings.h"
#include "api/field_trials_view.h"
#include "rtc_base/logging.h"
namespace webrtc {
namespace {
const int kMinFrames = 10;
const double kMinScaleFactor = 0.01;
} // namespace
QualityScalerSettings::QualityScalerSettings(
const FieldTrialsView& field_trials)
: sampling_period_ms_("sampling_period_ms"),
average_qp_window_("average_qp_window"),
min_frames_("min_frames"),
initial_scale_factor_("initial_scale_factor"),
scale_factor_("scale_factor"),
initial_bitrate_interval_ms_("initial_bitrate_interval_ms"),
initial_bitrate_factor_("initial_bitrate_factor") {
ParseFieldTrial({&sampling_period_ms_, &average_qp_window_, &min_frames_,
&initial_scale_factor_, &scale_factor_,
&initial_bitrate_interval_ms_, &initial_bitrate_factor_},
field_trials.Lookup("WebRTC-Video-QualityScalerSettings"));
}
absl::optional<int> QualityScalerSettings::SamplingPeriodMs() const {
if (sampling_period_ms_ && sampling_period_ms_.Value() <= 0) {
RTC_LOG(LS_WARNING) << "Unsupported sampling_period_ms value, ignored.";
return absl::nullopt;
}
return sampling_period_ms_.GetOptional();
}
absl::optional<int> QualityScalerSettings::AverageQpWindow() const {
if (average_qp_window_ && average_qp_window_.Value() <= 0) {
RTC_LOG(LS_WARNING) << "Unsupported average_qp_window value, ignored.";
return absl::nullopt;
}
return average_qp_window_.GetOptional();
}
absl::optional<int> QualityScalerSettings::MinFrames() const {
if (min_frames_ && min_frames_.Value() < kMinFrames) {
RTC_LOG(LS_WARNING) << "Unsupported min_frames value, ignored.";
return absl::nullopt;
}
return min_frames_.GetOptional();
}
absl::optional<double> QualityScalerSettings::InitialScaleFactor() const {
if (initial_scale_factor_ &&
initial_scale_factor_.Value() < kMinScaleFactor) {
RTC_LOG(LS_WARNING) << "Unsupported initial_scale_factor value, ignored.";
return absl::nullopt;
}
return initial_scale_factor_.GetOptional();
}
absl::optional<double> QualityScalerSettings::ScaleFactor() const {
if (scale_factor_ && scale_factor_.Value() < kMinScaleFactor) {
RTC_LOG(LS_WARNING) << "Unsupported scale_factor value, ignored.";
return absl::nullopt;
}
return scale_factor_.GetOptional();
}
absl::optional<int> QualityScalerSettings::InitialBitrateIntervalMs() const {
if (initial_bitrate_interval_ms_ &&
initial_bitrate_interval_ms_.Value() < 0) {
RTC_LOG(LS_WARNING) << "Unsupported bitrate_interval value, ignored.";
return absl::nullopt;
}
return initial_bitrate_interval_ms_.GetOptional();
}
absl::optional<double> QualityScalerSettings::InitialBitrateFactor() const {
if (initial_bitrate_factor_ &&
initial_bitrate_factor_.Value() < kMinScaleFactor) {
RTC_LOG(LS_WARNING) << "Unsupported initial_bitrate_factor value, ignored.";
return absl::nullopt;
}
return initial_bitrate_factor_.GetOptional();
}
} // namespace webrtc

View file

@ -0,0 +1,44 @@
/*
* Copyright (c) 2019 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.
*/
#ifndef RTC_BASE_EXPERIMENTS_QUALITY_SCALER_SETTINGS_H_
#define RTC_BASE_EXPERIMENTS_QUALITY_SCALER_SETTINGS_H_
#include "absl/types/optional.h"
#include "api/field_trials_view.h"
#include "rtc_base/experiments/field_trial_parser.h"
namespace webrtc {
class QualityScalerSettings final {
public:
explicit QualityScalerSettings(const FieldTrialsView& field_trials);
absl::optional<int> SamplingPeriodMs() const;
absl::optional<int> AverageQpWindow() const;
absl::optional<int> MinFrames() const;
absl::optional<double> InitialScaleFactor() const;
absl::optional<double> ScaleFactor() const;
absl::optional<int> InitialBitrateIntervalMs() const;
absl::optional<double> InitialBitrateFactor() const;
private:
FieldTrialOptional<int> sampling_period_ms_;
FieldTrialOptional<int> average_qp_window_;
FieldTrialOptional<int> min_frames_;
FieldTrialOptional<double> initial_scale_factor_;
FieldTrialOptional<double> scale_factor_;
FieldTrialOptional<int> initial_bitrate_interval_ms_;
FieldTrialOptional<double> initial_bitrate_factor_;
};
} // namespace webrtc
#endif // RTC_BASE_EXPERIMENTS_QUALITY_SCALER_SETTINGS_H_

View file

@ -0,0 +1,117 @@
/*
* Copyright 2018 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 "rtc_base/experiments/quality_scaling_experiment.h"
#include <stdio.h>
#include <string>
#include "absl/strings/match.h"
#include "api/field_trials_view.h"
#include "api/transport/field_trial_based_config.h"
#include "rtc_base/logging.h"
namespace webrtc {
namespace {
constexpr char kFieldTrial[] = "WebRTC-Video-QualityScaling";
constexpr int kMinQp = 1;
constexpr int kMaxVp8Qp = 127;
constexpr int kMaxVp9Qp = 255;
constexpr int kMaxH264Qp = 51;
constexpr int kMaxGenericQp = 255;
#if !defined(WEBRTC_IOS)
constexpr char kDefaultQualityScalingSetttings[] =
"Enabled-29,95,149,205,24,37,26,36,0.9995,0.9999,1";
#endif
absl::optional<VideoEncoder::QpThresholds> GetThresholds(int low,
int high,
int max) {
if (low < kMinQp || high > max || high < low)
return absl::nullopt;
RTC_LOG(LS_INFO) << "QP thresholds: low: " << low << ", high: " << high;
return absl::optional<VideoEncoder::QpThresholds>(
VideoEncoder::QpThresholds(low, high));
}
} // namespace
bool QualityScalingExperiment::Enabled(const FieldTrialsView& field_trials) {
#if defined(WEBRTC_IOS)
return absl::StartsWith(field_trials.Lookup(kFieldTrial), "Enabled");
#else
return !absl::StartsWith(field_trials.Lookup(kFieldTrial), "Disabled");
#endif
}
absl::optional<QualityScalingExperiment::Settings>
QualityScalingExperiment::ParseSettings(const FieldTrialsView& field_trials) {
std::string group = field_trials.Lookup(kFieldTrial);
// TODO(http://crbug.com/webrtc/12401): Completely remove the experiment code
// after few releases.
#if !defined(WEBRTC_IOS)
if (group.empty())
group = kDefaultQualityScalingSetttings;
#endif
Settings s;
if (sscanf(group.c_str(), "Enabled-%d,%d,%d,%d,%d,%d,%d,%d,%f,%f,%d",
&s.vp8_low, &s.vp8_high, &s.vp9_low, &s.vp9_high, &s.h264_low,
&s.h264_high, &s.generic_low, &s.generic_high, &s.alpha_high,
&s.alpha_low, &s.drop) != 11) {
RTC_LOG(LS_WARNING) << "Invalid number of parameters provided.";
return absl::nullopt;
}
return s;
}
absl::optional<VideoEncoder::QpThresholds>
QualityScalingExperiment::GetQpThresholds(VideoCodecType codec_type,
const FieldTrialsView& field_trials) {
const auto settings = ParseSettings(field_trials);
if (!settings)
return absl::nullopt;
switch (codec_type) {
case kVideoCodecVP8:
return GetThresholds(settings->vp8_low, settings->vp8_high, kMaxVp8Qp);
case kVideoCodecVP9:
return GetThresholds(settings->vp9_low, settings->vp9_high, kMaxVp9Qp);
case kVideoCodecH265:
// TODO(bugs.webrtc.org/13485): Use H264 QP thresholds for now.
case kVideoCodecH264:
return GetThresholds(settings->h264_low, settings->h264_high, kMaxH264Qp);
case kVideoCodecGeneric:
return GetThresholds(settings->generic_low, settings->generic_high,
kMaxGenericQp);
default:
return absl::nullopt;
}
}
QualityScalingExperiment::Config QualityScalingExperiment::GetConfig(
const FieldTrialsView& field_trials) {
const auto settings = ParseSettings(field_trials);
if (!settings)
return Config();
Config config;
config.use_all_drop_reasons = settings->drop > 0;
if (settings->alpha_high < 0 || settings->alpha_low < settings->alpha_high) {
RTC_LOG(LS_WARNING) << "Invalid alpha value provided, using default.";
return config;
}
config.alpha_high = settings->alpha_high;
config.alpha_low = settings->alpha_low;
return config;
}
} // namespace webrtc

View file

@ -0,0 +1,61 @@
/*
* Copyright 2018 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.
*/
#ifndef RTC_BASE_EXPERIMENTS_QUALITY_SCALING_EXPERIMENT_H_
#define RTC_BASE_EXPERIMENTS_QUALITY_SCALING_EXPERIMENT_H_
#include "absl/types/optional.h"
#include "api/field_trials_view.h"
#include "api/video_codecs/video_encoder.h"
namespace webrtc {
class QualityScalingExperiment {
public:
struct Settings {
int vp8_low; // VP8: low QP threshold.
int vp8_high; // VP8: high QP threshold.
int vp9_low; // VP9: low QP threshold.
int vp9_high; // VP9: high QP threshold.
int h264_low; // H264: low QP threshold.
int h264_high; // H264: high QP threshold.
int generic_low; // Generic: low QP threshold.
int generic_high; // Generic: high QP threshold.
float alpha_high; // `alpha_` for ExpFilter used when checking high QP.
float alpha_low; // `alpha_` for ExpFilter used when checking low QP.
int drop; // >0 sets `use_all_drop_reasons` to true.
};
// Used by QualityScaler.
struct Config {
float alpha_high = 0.9995f;
float alpha_low = 0.9999f;
// If set, all type of dropped frames are used.
// Otherwise only dropped frames by MediaOptimization are used.
bool use_all_drop_reasons = false;
};
// Returns true if the experiment is enabled.
static bool Enabled(const FieldTrialsView& field_trials);
// Returns settings from field trial.
static absl::optional<Settings> ParseSettings(
const FieldTrialsView& field_trials);
// Returns QpThresholds for the `codec_type`.
static absl::optional<VideoEncoder::QpThresholds> GetQpThresholds(
VideoCodecType codec_type,
const FieldTrialsView& field_trials);
// Returns parsed values. If the parsing fails, default values are returned.
static Config GetConfig(const FieldTrialsView& field_trials);
};
} // namespace webrtc
#endif // RTC_BASE_EXPERIMENTS_QUALITY_SCALING_EXPERIMENT_H_

View file

@ -0,0 +1,183 @@
/*
* Copyright (c) 2019 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 "rtc_base/experiments/rate_control_settings.h"
#include <inttypes.h>
#include <stdio.h>
#include <string>
#include "absl/strings/match.h"
#include "api/transport/field_trial_based_config.h"
#include "rtc_base/logging.h"
#include "rtc_base/numerics/safe_conversions.h"
namespace webrtc {
namespace {
const int kDefaultAcceptedQueueMs = 350;
const int kDefaultMinPushbackTargetBitrateBps = 30000;
const char kCongestionWindowDefaultFieldTrialString[] =
"QueueSize:350,MinBitrate:30000,DropFrame:true";
const char kUseBaseHeavyVp8Tl3RateAllocationFieldTrialName[] =
"WebRTC-UseBaseHeavyVP8TL3RateAllocation";
bool IsEnabled(const FieldTrialsView* const key_value_config,
absl::string_view key) {
return absl::StartsWith(key_value_config->Lookup(key), "Enabled");
}
} // namespace
constexpr char CongestionWindowConfig::kKey[];
std::unique_ptr<StructParametersParser> CongestionWindowConfig::Parser() {
return StructParametersParser::Create("QueueSize", &queue_size_ms, //
"MinBitrate", &min_bitrate_bps,
"InitWin", &initial_data_window,
"DropFrame", &drop_frame_only);
}
// static
CongestionWindowConfig CongestionWindowConfig::Parse(absl::string_view config) {
CongestionWindowConfig res;
res.Parser()->Parse(config);
return res;
}
constexpr char VideoRateControlConfig::kKey[];
std::unique_ptr<StructParametersParser> VideoRateControlConfig::Parser() {
// The empty comments ensures that each pair is on a separate line.
return StructParametersParser::Create(
"pacing_factor", &pacing_factor, //
"alr_probing", &alr_probing, //
"vp8_qp_max", &vp8_qp_max, //
"vp8_min_pixels", &vp8_min_pixels, //
"trust_vp8", &trust_vp8, //
"trust_vp9", &trust_vp9, //
"bitrate_adjuster", &bitrate_adjuster, //
"adjuster_use_headroom", &adjuster_use_headroom, //
"vp8_s0_boost", &vp8_s0_boost, //
"vp8_base_heavy_tl3_alloc", &vp8_base_heavy_tl3_alloc);
}
RateControlSettings::RateControlSettings(
const FieldTrialsView* const key_value_config) {
std::string congestion_window_config =
key_value_config->Lookup(CongestionWindowConfig::kKey).empty()
? kCongestionWindowDefaultFieldTrialString
: key_value_config->Lookup(CongestionWindowConfig::kKey);
congestion_window_config_ =
CongestionWindowConfig::Parse(congestion_window_config);
video_config_.vp8_base_heavy_tl3_alloc = IsEnabled(
key_value_config, kUseBaseHeavyVp8Tl3RateAllocationFieldTrialName);
video_config_.Parser()->Parse(
key_value_config->Lookup(VideoRateControlConfig::kKey));
}
RateControlSettings::~RateControlSettings() = default;
RateControlSettings::RateControlSettings(RateControlSettings&&) = default;
RateControlSettings RateControlSettings::ParseFromFieldTrials() {
FieldTrialBasedConfig field_trial_config;
return RateControlSettings(&field_trial_config);
}
RateControlSettings RateControlSettings::ParseFromKeyValueConfig(
const FieldTrialsView* const key_value_config) {
FieldTrialBasedConfig field_trial_config;
return RateControlSettings(key_value_config ? key_value_config
: &field_trial_config);
}
bool RateControlSettings::UseCongestionWindow() const {
return static_cast<bool>(congestion_window_config_.queue_size_ms);
}
int64_t RateControlSettings::GetCongestionWindowAdditionalTimeMs() const {
return congestion_window_config_.queue_size_ms.value_or(
kDefaultAcceptedQueueMs);
}
bool RateControlSettings::UseCongestionWindowPushback() const {
return congestion_window_config_.queue_size_ms &&
congestion_window_config_.min_bitrate_bps;
}
bool RateControlSettings::UseCongestionWindowDropFrameOnly() const {
return congestion_window_config_.drop_frame_only;
}
uint32_t RateControlSettings::CongestionWindowMinPushbackTargetBitrateBps()
const {
return congestion_window_config_.min_bitrate_bps.value_or(
kDefaultMinPushbackTargetBitrateBps);
}
absl::optional<DataSize>
RateControlSettings::CongestionWindowInitialDataWindow() const {
return congestion_window_config_.initial_data_window;
}
absl::optional<double> RateControlSettings::GetPacingFactor() const {
return video_config_.pacing_factor;
}
bool RateControlSettings::UseAlrProbing() const {
return video_config_.alr_probing;
}
absl::optional<int> RateControlSettings::LibvpxVp8QpMax() const {
if (video_config_.vp8_qp_max &&
(*video_config_.vp8_qp_max < 0 || *video_config_.vp8_qp_max > 63)) {
RTC_LOG(LS_WARNING) << "Unsupported vp8_qp_max_ value, ignored.";
return absl::nullopt;
}
return video_config_.vp8_qp_max;
}
absl::optional<int> RateControlSettings::LibvpxVp8MinPixels() const {
if (video_config_.vp8_min_pixels && *video_config_.vp8_min_pixels < 1) {
return absl::nullopt;
}
return video_config_.vp8_min_pixels;
}
bool RateControlSettings::LibvpxVp8TrustedRateController() const {
return video_config_.trust_vp8;
}
bool RateControlSettings::Vp8BoostBaseLayerQuality() const {
return video_config_.vp8_s0_boost;
}
bool RateControlSettings::LibvpxVp9TrustedRateController() const {
return video_config_.trust_vp9;
}
bool RateControlSettings::Vp8BaseHeavyTl3RateAllocation() const {
return video_config_.vp8_base_heavy_tl3_alloc;
}
bool RateControlSettings::UseEncoderBitrateAdjuster() const {
return video_config_.bitrate_adjuster;
}
bool RateControlSettings::BitrateAdjusterCanUseNetworkHeadroom() const {
return video_config_.adjuster_use_headroom;
}
} // namespace webrtc

View file

@ -0,0 +1,93 @@
/*
* Copyright (c) 2019 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.
*/
#ifndef RTC_BASE_EXPERIMENTS_RATE_CONTROL_SETTINGS_H_
#define RTC_BASE_EXPERIMENTS_RATE_CONTROL_SETTINGS_H_
#include "absl/types/optional.h"
#include "api/field_trials_view.h"
#include "api/units/data_size.h"
#include "api/video_codecs/video_codec.h"
#include "rtc_base/experiments/struct_parameters_parser.h"
#include "video/config/video_encoder_config.h"
namespace webrtc {
struct CongestionWindowConfig {
static constexpr char kKey[] = "WebRTC-CongestionWindow";
absl::optional<int> queue_size_ms;
absl::optional<int> min_bitrate_bps;
absl::optional<DataSize> initial_data_window;
bool drop_frame_only = false;
std::unique_ptr<StructParametersParser> Parser();
static CongestionWindowConfig Parse(absl::string_view config);
};
struct VideoRateControlConfig {
static constexpr char kKey[] = "WebRTC-VideoRateControl";
absl::optional<double> pacing_factor;
bool alr_probing = false;
absl::optional<int> vp8_qp_max;
absl::optional<int> vp8_min_pixels;
bool trust_vp8 = true;
bool trust_vp9 = true;
bool bitrate_adjuster = true;
bool adjuster_use_headroom = true;
bool vp8_s0_boost = false;
bool vp8_base_heavy_tl3_alloc = false;
std::unique_ptr<StructParametersParser> Parser();
};
class RateControlSettings final {
public:
~RateControlSettings();
RateControlSettings(RateControlSettings&&);
static RateControlSettings ParseFromFieldTrials();
static RateControlSettings ParseFromKeyValueConfig(
const FieldTrialsView* const key_value_config);
// When CongestionWindowPushback is enabled, the pacer is oblivious to
// the congestion window. The relation between outstanding data and
// the congestion window affects encoder allocations directly.
bool UseCongestionWindow() const;
int64_t GetCongestionWindowAdditionalTimeMs() const;
bool UseCongestionWindowPushback() const;
bool UseCongestionWindowDropFrameOnly() const;
uint32_t CongestionWindowMinPushbackTargetBitrateBps() const;
absl::optional<DataSize> CongestionWindowInitialDataWindow() const;
absl::optional<double> GetPacingFactor() const;
bool UseAlrProbing() const;
absl::optional<int> LibvpxVp8QpMax() const;
absl::optional<int> LibvpxVp8MinPixels() const;
bool LibvpxVp8TrustedRateController() const;
bool Vp8BoostBaseLayerQuality() const;
bool Vp8DynamicRateSettings() const;
bool LibvpxVp9TrustedRateController() const;
bool Vp9DynamicRateSettings() const;
bool Vp8BaseHeavyTl3RateAllocation() const;
bool UseEncoderBitrateAdjuster() const;
bool BitrateAdjusterCanUseNetworkHeadroom() const;
private:
explicit RateControlSettings(const FieldTrialsView* const key_value_config);
CongestionWindowConfig congestion_window_config_;
VideoRateControlConfig video_config_;
};
} // namespace webrtc
#endif // RTC_BASE_EXPERIMENTS_RATE_CONTROL_SETTINGS_H_

View file

@ -0,0 +1,39 @@
/*
* Copyright 2018 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 "rtc_base/experiments/rtt_mult_experiment.h"
#include <stdio.h>
#include <algorithm>
#include <string>
#include "rtc_base/logging.h"
#include "system_wrappers/include/field_trial.h"
namespace webrtc {
namespace {
const char kRttMultExperiment[] = "WebRTC-RttMult";
} // namespace
bool RttMultExperiment::RttMultEnabled() {
return !field_trial::IsDisabled(kRttMultExperiment);
}
absl::optional<RttMultExperiment::Settings>
RttMultExperiment::GetRttMultValue() {
if (!RttMultExperiment::RttMultEnabled()) {
return absl::nullopt;
}
return RttMultExperiment::Settings{.rtt_mult_setting = 0.9,
.rtt_mult_add_cap_ms = 200.0};
}
} // namespace webrtc

View file

@ -0,0 +1,35 @@
/*
* Copyright 2018 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.
*/
#ifndef RTC_BASE_EXPERIMENTS_RTT_MULT_EXPERIMENT_H_
#define RTC_BASE_EXPERIMENTS_RTT_MULT_EXPERIMENT_H_
#include "absl/types/optional.h"
namespace webrtc {
class RttMultExperiment {
public:
struct Settings {
float rtt_mult_setting; // Jitter buffer size is increased by this factor
// times the estimated RTT.
float rtt_mult_add_cap_ms; // Jitter buffer size increase is capped by this
// value.
};
// Returns true if the experiment is enabled.
static bool RttMultEnabled();
// Returns rtt_mult value and rtt_mult addition cap value from field trial.
static absl::optional<RttMultExperiment::Settings> GetRttMultValue();
};
} // namespace webrtc
#endif // RTC_BASE_EXPERIMENTS_RTT_MULT_EXPERIMENT_H_

View file

@ -0,0 +1,63 @@
/*
* Copyright 2019 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 "rtc_base/experiments/stable_target_rate_experiment.h"
#include "api/transport/field_trial_based_config.h"
namespace webrtc {
namespace {
constexpr char kFieldTrialName[] = "WebRTC-StableTargetRate";
} // namespace
StableTargetRateExperiment::StableTargetRateExperiment(
const FieldTrialsView* const key_value_config,
double default_video_hysteresis,
double default_screenshare_hysteresis)
: enabled_("enabled", false),
video_hysteresis_factor_("video_hysteresis_factor",
default_video_hysteresis),
screenshare_hysteresis_factor_("screenshare_hysteresis_factor",
default_screenshare_hysteresis) {
ParseFieldTrial(
{&enabled_, &video_hysteresis_factor_, &screenshare_hysteresis_factor_},
key_value_config->Lookup(kFieldTrialName));
}
StableTargetRateExperiment::StableTargetRateExperiment(
const StableTargetRateExperiment&) = default;
StableTargetRateExperiment::StableTargetRateExperiment(
StableTargetRateExperiment&&) = default;
StableTargetRateExperiment StableTargetRateExperiment::ParseFromFieldTrials() {
FieldTrialBasedConfig config;
return ParseFromKeyValueConfig(&config);
}
StableTargetRateExperiment StableTargetRateExperiment::ParseFromKeyValueConfig(
const FieldTrialsView* const key_value_config) {
return StableTargetRateExperiment(key_value_config,
/*default_video_hysteresis=*/1.2,
/*default_screenshare_hysteresis=*/1.35);
}
bool StableTargetRateExperiment::IsEnabled() const {
return enabled_.Get();
}
double StableTargetRateExperiment::GetVideoHysteresisFactor() const {
return video_hysteresis_factor_.Get();
}
double StableTargetRateExperiment::GetScreenshareHysteresisFactor() const {
return screenshare_hysteresis_factor_.Get();
}
} // namespace webrtc

View file

@ -0,0 +1,44 @@
/*
* Copyright 2019 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.
*/
#ifndef RTC_BASE_EXPERIMENTS_STABLE_TARGET_RATE_EXPERIMENT_H_
#define RTC_BASE_EXPERIMENTS_STABLE_TARGET_RATE_EXPERIMENT_H_
#include "api/field_trials_view.h"
#include "rtc_base/experiments/field_trial_parser.h"
namespace webrtc {
class StableTargetRateExperiment {
public:
StableTargetRateExperiment(const StableTargetRateExperiment&);
StableTargetRateExperiment(StableTargetRateExperiment&&);
static StableTargetRateExperiment ParseFromFieldTrials();
static StableTargetRateExperiment ParseFromKeyValueConfig(
const FieldTrialsView* const key_value_config);
bool IsEnabled() const;
double GetVideoHysteresisFactor() const;
double GetScreenshareHysteresisFactor() const;
private:
explicit StableTargetRateExperiment(
const FieldTrialsView* const key_value_config,
double default_video_hysteresis,
double default_screenshare_hysteresis);
FieldTrialParameter<bool> enabled_;
FieldTrialParameter<double> video_hysteresis_factor_;
FieldTrialParameter<double> screenshare_hysteresis_factor_;
};
} // namespace webrtc
#endif // RTC_BASE_EXPERIMENTS_STABLE_TARGET_RATE_EXPERIMENT_H_

View file

@ -0,0 +1,135 @@
/*
* Copyright (c) 2019 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 "rtc_base/experiments/struct_parameters_parser.h"
#include <algorithm>
#include "absl/strings/string_view.h"
#include "rtc_base/logging.h"
namespace webrtc {
namespace {
size_t FindOrEnd(absl::string_view str, size_t start, char delimiter) {
size_t pos = str.find(delimiter, start);
pos = (pos == absl::string_view::npos) ? str.length() : pos;
return pos;
}
} // namespace
namespace struct_parser_impl {
namespace {
inline void StringEncode(std::string* target, bool val) {
*target += rtc::ToString(val);
}
inline void StringEncode(std::string* target, double val) {
*target += rtc::ToString(val);
}
inline void StringEncode(std::string* target, int val) {
*target += rtc::ToString(val);
}
inline void StringEncode(std::string* target, unsigned val) {
*target += rtc::ToString(val);
}
inline void StringEncode(std::string* target, DataRate val) {
*target += webrtc::ToString(val);
}
inline void StringEncode(std::string* target, DataSize val) {
*target += webrtc::ToString(val);
}
inline void StringEncode(std::string* target, TimeDelta val) {
*target += webrtc::ToString(val);
}
template <typename T>
inline void StringEncode(std::string* sb, absl::optional<T> val) {
if (val)
StringEncode(sb, *val);
}
} // namespace
template <typename T>
bool TypedParser<T>::Parse(absl::string_view src, void* target) {
auto parsed = ParseTypedParameter<T>(std::string(src));
if (parsed.has_value())
*reinterpret_cast<T*>(target) = *parsed;
return parsed.has_value();
}
template <typename T>
void TypedParser<T>::Encode(const void* src, std::string* target) {
StringEncode(target, *reinterpret_cast<const T*>(src));
}
template class TypedParser<bool>;
template class TypedParser<double>;
template class TypedParser<int>;
template class TypedParser<unsigned>;
template class TypedParser<absl::optional<double>>;
template class TypedParser<absl::optional<int>>;
template class TypedParser<absl::optional<unsigned>>;
template class TypedParser<DataRate>;
template class TypedParser<DataSize>;
template class TypedParser<TimeDelta>;
template class TypedParser<absl::optional<DataRate>>;
template class TypedParser<absl::optional<DataSize>>;
template class TypedParser<absl::optional<TimeDelta>>;
} // namespace struct_parser_impl
StructParametersParser::StructParametersParser(
std::vector<struct_parser_impl::MemberParameter> members)
: members_(std::move(members)) {}
void StructParametersParser::Parse(absl::string_view src) {
size_t i = 0;
while (i < src.length()) {
size_t val_end = FindOrEnd(src, i, ',');
size_t colon_pos = FindOrEnd(src, i, ':');
size_t key_end = std::min(val_end, colon_pos);
size_t val_begin = key_end + 1u;
absl::string_view key(src.substr(i, key_end - i));
absl::string_view opt_value;
if (val_end >= val_begin)
opt_value = src.substr(val_begin, val_end - val_begin);
i = val_end + 1u;
bool found = false;
for (auto& member : members_) {
if (key == member.key) {
found = true;
if (!member.parser.parse(opt_value, member.member_ptr)) {
RTC_LOG(LS_WARNING) << "Failed to read field with key: '" << key
<< "' in trial: \"" << src << "\"";
}
break;
}
}
// "_" is be used to prefix keys that are part of the string for
// debugging purposes but not neccessarily used.
// e.g. WebRTC-Experiment/param: value, _DebuggingString
if (!found && (key.empty() || key[0] != '_')) {
RTC_LOG(LS_INFO) << "No field with key: '" << key
<< "' (found in trial: \"" << src << "\")";
}
}
}
std::string StructParametersParser::Encode() const {
std::string res;
bool first = true;
for (const auto& member : members_) {
if (!first)
res += ",";
res += member.key;
res += ":";
member.parser.encode(member.member_ptr, &res);
first = false;
}
return res;
}
} // namespace webrtc

View file

@ -0,0 +1,110 @@
/*
* Copyright (c) 2019 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.
*/
#ifndef RTC_BASE_EXPERIMENTS_STRUCT_PARAMETERS_PARSER_H_
#define RTC_BASE_EXPERIMENTS_STRUCT_PARAMETERS_PARSER_H_
#include <functional>
#include <map>
#include <memory>
#include <string>
#include <utility>
#include <vector>
#include "absl/memory/memory.h"
#include "absl/strings/string_view.h"
#include "absl/types/optional.h"
#include "rtc_base/experiments/field_trial_parser.h"
#include "rtc_base/experiments/field_trial_units.h"
#include "rtc_base/string_encode.h"
namespace webrtc {
namespace struct_parser_impl {
struct TypedMemberParser {
public:
bool (*parse)(absl::string_view src, void* target);
void (*encode)(const void* src, std::string* target);
};
struct MemberParameter {
const char* key;
void* member_ptr;
TypedMemberParser parser;
};
template <typename T>
class TypedParser {
public:
static bool Parse(absl::string_view src, void* target);
static void Encode(const void* src, std::string* target);
};
// Instantiated in cc file to avoid duplication during compile. Add additional
// parsers as needed. Generally, try to use these suggested types even if the
// context where the value is used might require a different type. For instance,
// a size_t representing a packet size should use an int parameter as there's no
// need to support packet sizes larger than INT32_MAX.
extern template class TypedParser<bool>;
extern template class TypedParser<double>;
extern template class TypedParser<int>;
extern template class TypedParser<unsigned>;
extern template class TypedParser<absl::optional<double>>;
extern template class TypedParser<absl::optional<int>>;
extern template class TypedParser<absl::optional<unsigned>>;
extern template class TypedParser<DataRate>;
extern template class TypedParser<DataSize>;
extern template class TypedParser<TimeDelta>;
extern template class TypedParser<absl::optional<DataRate>>;
extern template class TypedParser<absl::optional<DataSize>>;
extern template class TypedParser<absl::optional<TimeDelta>>;
template <typename T>
void AddMembers(MemberParameter* out, const char* key, T* member) {
*out = MemberParameter{
key, member,
TypedMemberParser{&TypedParser<T>::Parse, &TypedParser<T>::Encode}};
}
template <typename T, typename... Args>
void AddMembers(MemberParameter* out,
const char* key,
T* member,
Args... args) {
AddMembers(out, key, member);
AddMembers(++out, args...);
}
} // namespace struct_parser_impl
class StructParametersParser {
public:
template <typename T, typename... Args>
static std::unique_ptr<StructParametersParser> Create(const char* first_key,
T* first_member,
Args... args) {
std::vector<struct_parser_impl::MemberParameter> members(
sizeof...(args) / 2 + 1);
struct_parser_impl::AddMembers(&members.front(), std::move(first_key),
first_member, args...);
return absl::WrapUnique(new StructParametersParser(std::move(members)));
}
void Parse(absl::string_view src);
std::string Encode() const;
private:
explicit StructParametersParser(
std::vector<struct_parser_impl::MemberParameter> members);
std::vector<struct_parser_impl::MemberParameter> members_;
};
} // namespace webrtc
#endif // RTC_BASE_EXPERIMENTS_STRUCT_PARAMETERS_PARSER_H_