Implement stable rate support in SimulcastRateAllocator

Bug: webrtc:10126
Change-Id: I2ea8d27b0bd6f7ffd1ebbba451bd1ce1f2eee3d9
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/151121
Reviewed-by: Florent Castelli <orphis@webrtc.org>
Commit-Queue: Erik Språng <sprang@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#29097}
This commit is contained in:
Erik Språng
2019-09-04 16:30:47 +02:00
committed by Commit Bot
parent 059a0b7587
commit 2b9dba3d9c
9 changed files with 173 additions and 78 deletions

View File

@ -264,6 +264,7 @@ rtc_source_set("video_coding_utility") {
"../../rtc_base/experiments:quality_scaler_settings", "../../rtc_base/experiments:quality_scaler_settings",
"../../rtc_base/experiments:quality_scaling_experiment", "../../rtc_base/experiments:quality_scaling_experiment",
"../../rtc_base/experiments:rate_control_settings", "../../rtc_base/experiments:rate_control_settings",
"../../rtc_base/experiments:stable_target_rate_experiment",
"../../rtc_base/synchronization:sequence_checker", "../../rtc_base/synchronization:sequence_checker",
"../../rtc_base/system:arch", "../../rtc_base/system:arch",
"../../rtc_base/system:file_wrapper", "../../rtc_base/system:file_wrapper",

View File

@ -200,15 +200,11 @@ VideoBitrateAllocation SvcRateAllocator::Allocate(
// Figure out how many spatial layers should be active. // Figure out how many spatial layers should be active.
if (experiment_settings_.IsEnabled() && if (experiment_settings_.IsEnabled() &&
parameters.stable_bitrate > DataRate::Zero()) { parameters.stable_bitrate > DataRate::Zero()) {
double hysteresis_factor = 1.0; double hysteresis_factor;
if (codec_.mode == VideoCodecMode::kScreensharing) { if (codec_.mode == VideoCodecMode::kScreensharing) {
hysteresis_factor = hysteresis_factor = experiment_settings_.GetScreenshareHysteresisFactor();
experiment_settings_.GetScreenshareHysteresisFactor().value_or(
hysteresis_factor);
} else { } else {
hysteresis_factor = hysteresis_factor = experiment_settings_.GetVideoHysteresisFactor();
experiment_settings_.GetVideoHysteresisFactor().value_or(
hysteresis_factor);
} }
DataRate stable_rate = DataRate stable_rate =

View File

@ -283,7 +283,9 @@ TEST_P(SvcRateAllocatorTestParametrizedContentType, PaddingBitrate) {
} }
TEST_P(SvcRateAllocatorTestParametrizedContentType, StableBitrate) { TEST_P(SvcRateAllocatorTestParametrizedContentType, StableBitrate) {
ScopedFieldTrials field_trial("WebRTC-StableTargetRate/enabled:true/"); ScopedFieldTrials field_trial(
"WebRTC-StableTargetRate/enabled:true,video_hysteresis_factor:1.0,"
"screenshare_hysteresis_factor:1.0/");
const VideoCodec codec = Configure(1280, 720, 3, 1, is_screen_sharing_); const VideoCodec codec = Configure(1280, 720, 3, 1, is_screen_sharing_);
const auto start_rates = SvcRateAllocator::GetLayerStartBitrates(codec); const auto start_rates = SvcRateAllocator::GetLayerStartBitrates(codec);

View File

@ -59,32 +59,45 @@ float SimulcastRateAllocator::GetTemporalRateAllocation(int num_layers,
SimulcastRateAllocator::SimulcastRateAllocator(const VideoCodec& codec) SimulcastRateAllocator::SimulcastRateAllocator(const VideoCodec& codec)
: codec_(codec), : codec_(codec),
hysteresis_factor_(RateControlSettings::ParseFromFieldTrials() stable_rate_settings_(
.GetSimulcastHysteresisFactor(codec.mode)) {} StableTargetRateExperiment::ParseFromFieldTrials()) {}
SimulcastRateAllocator::~SimulcastRateAllocator() = default; SimulcastRateAllocator::~SimulcastRateAllocator() = default;
VideoBitrateAllocation SimulcastRateAllocator::Allocate( VideoBitrateAllocation SimulcastRateAllocator::Allocate(
VideoBitrateAllocationParameters parameters) { VideoBitrateAllocationParameters parameters) {
VideoBitrateAllocation allocated_bitrates_bps; VideoBitrateAllocation allocated_bitrates;
DistributeAllocationToSimulcastLayers(parameters.total_bitrate.bps(), DataRate stable_rate = parameters.total_bitrate;
&allocated_bitrates_bps); if (stable_rate_settings_.IsEnabled() &&
DistributeAllocationToTemporalLayers(&allocated_bitrates_bps); parameters.stable_bitrate > DataRate::Zero()) {
return allocated_bitrates_bps; stable_rate = std::min(parameters.stable_bitrate, parameters.total_bitrate);
}
DistributeAllocationToSimulcastLayers(parameters.total_bitrate, stable_rate,
&allocated_bitrates);
DistributeAllocationToTemporalLayers(&allocated_bitrates);
return allocated_bitrates;
} }
void SimulcastRateAllocator::DistributeAllocationToSimulcastLayers( void SimulcastRateAllocator::DistributeAllocationToSimulcastLayers(
uint32_t total_bitrate_bps, DataRate total_bitrate,
VideoBitrateAllocation* allocated_bitrates_bps) { DataRate stable_bitrate,
uint32_t left_to_allocate = total_bitrate_bps; VideoBitrateAllocation* allocated_bitrates) {
if (codec_.maxBitrate && codec_.maxBitrate * 1000 < left_to_allocate) DataRate left_in_total_allocation = total_bitrate;
left_to_allocate = codec_.maxBitrate * 1000; DataRate left_in_stable_allocation = stable_bitrate;
if (codec_.maxBitrate) {
DataRate max_rate = DataRate::kbps(codec_.maxBitrate);
left_in_total_allocation = std::min(left_in_total_allocation, max_rate);
left_in_stable_allocation = std::min(left_in_stable_allocation, max_rate);
}
if (codec_.numberOfSimulcastStreams == 0) { if (codec_.numberOfSimulcastStreams == 0) {
// No simulcast, just set the target as this has been capped already. // No simulcast, just set the target as this has been capped already.
if (codec_.active) { if (codec_.active) {
allocated_bitrates_bps->SetBitrate( allocated_bitrates->SetBitrate(
0, 0, std::max(codec_.minBitrate * 1000, left_to_allocate)); 0, 0,
std::max(DataRate::kbps(codec_.minBitrate), left_in_total_allocation)
.bps());
} }
return; return;
} }
@ -115,9 +128,10 @@ void SimulcastRateAllocator::DistributeAllocationToSimulcastLayers(
// Always allocate enough bitrate for the minimum bitrate of the first // Always allocate enough bitrate for the minimum bitrate of the first
// active layer. Suspending below min bitrate is controlled outside the // active layer. Suspending below min bitrate is controlled outside the
// codec implementation and is not overridden by this. // codec implementation and is not overridden by this.
left_to_allocate = std::max( DataRate min_rate = DataRate::kbps(
codec_.simulcastStream[layer_index[active_layer]].minBitrate * 1000, codec_.simulcastStream[layer_index[active_layer]].minBitrate);
left_to_allocate); left_in_total_allocation = std::max(left_in_total_allocation, min_rate);
left_in_stable_allocation = std::max(left_in_stable_allocation, min_rate);
// Begin by allocating bitrate to simulcast streams, putting all bitrate in // Begin by allocating bitrate to simulcast streams, putting all bitrate in
// temporal layer 0. We'll then distribute this bitrate, across potential // temporal layer 0. We'll then distribute this bitrate, across potential
@ -142,25 +156,28 @@ void SimulcastRateAllocator::DistributeAllocationToSimulcastLayers(
} }
// If we can't allocate to the current layer we can't allocate to higher // If we can't allocate to the current layer we can't allocate to higher
// layers because they require a higher minimum bitrate. // layers because they require a higher minimum bitrate.
uint32_t min_bitrate = stream.minBitrate * 1000; DataRate min_bitrate = DataRate::kbps(stream.minBitrate);
DataRate target_bitrate = DataRate::kbps(stream.targetBitrate);
double hysteresis_factor =
codec_.mode == VideoCodecMode::kRealtimeVideo
? stable_rate_settings_.GetVideoHysteresisFactor()
: stable_rate_settings_.GetScreenshareHysteresisFactor();
if (!first_allocation && !stream_enabled_[layer_index[active_layer]]) { if (!first_allocation && !stream_enabled_[layer_index[active_layer]]) {
min_bitrate = std::min( min_bitrate = std::min(hysteresis_factor * min_bitrate, target_bitrate);
static_cast<uint32_t>(hysteresis_factor_ * min_bitrate + 0.5),
stream.targetBitrate * 1000);
} }
if (left_to_allocate < min_bitrate) { if (left_in_stable_allocation < min_bitrate) {
break; break;
} }
// We are allocating to this layer so it is the current active allocation. // We are allocating to this layer so it is the current active allocation.
top_active_layer = layer_index[active_layer]; top_active_layer = layer_index[active_layer];
stream_enabled_[layer_index[active_layer]] = true; stream_enabled_[layer_index[active_layer]] = true;
uint32_t allocation = DataRate layer_rate = std::min(left_in_total_allocation, target_bitrate);
std::min(left_to_allocate, stream.targetBitrate * 1000); allocated_bitrates->SetBitrate(layer_index[active_layer], 0,
allocated_bitrates_bps->SetBitrate(layer_index[active_layer], 0, layer_rate.bps());
allocation); left_in_total_allocation -= layer_rate;
RTC_DCHECK_LE(allocation, left_to_allocate); left_in_stable_allocation -=
left_to_allocate -= allocation; std::min(left_in_stable_allocation, target_bitrate);
} }
// All layers above this one are not active. // All layers above this one are not active.
@ -172,16 +189,16 @@ void SimulcastRateAllocator::DistributeAllocationToSimulcastLayers(
// stream. // stream.
// TODO(sprang): Allocate up to max bitrate for all layers once we have a // TODO(sprang): Allocate up to max bitrate for all layers once we have a
// better idea of possible performance implications. // better idea of possible performance implications.
if (left_to_allocate > 0) { if (left_in_total_allocation > DataRate::Zero()) {
const SimulcastStream& stream = codec_.simulcastStream[top_active_layer]; const SimulcastStream& stream = codec_.simulcastStream[top_active_layer];
uint32_t bitrate_bps = DataRate initial_layer_rate =
allocated_bitrates_bps->GetSpatialLayerSum(top_active_layer); DataRate::bps(allocated_bitrates->GetSpatialLayerSum(top_active_layer));
uint32_t allocation = DataRate additional_allocation =
std::min(left_to_allocate, stream.maxBitrate * 1000 - bitrate_bps); std::min(left_in_total_allocation,
bitrate_bps += allocation; DataRate::kbps(stream.maxBitrate) - initial_layer_rate);
RTC_DCHECK_LE(allocation, left_to_allocate); allocated_bitrates->SetBitrate(
left_to_allocate -= allocation; top_active_layer, 0,
allocated_bitrates_bps->SetBitrate(top_active_layer, 0, bitrate_bps); (initial_layer_rate + additional_allocation).bps());
} }
} }

View File

@ -20,6 +20,7 @@
#include "api/video/video_bitrate_allocator.h" #include "api/video/video_bitrate_allocator.h"
#include "api/video_codecs/video_codec.h" #include "api/video_codecs/video_codec.h"
#include "rtc_base/constructor_magic.h" #include "rtc_base/constructor_magic.h"
#include "rtc_base/experiments/stable_target_rate_experiment.h"
namespace webrtc { namespace webrtc {
@ -36,10 +37,11 @@ class SimulcastRateAllocator : public VideoBitrateAllocator {
private: private:
void DistributeAllocationToSimulcastLayers( void DistributeAllocationToSimulcastLayers(
uint32_t total_bitrate_bps, DataRate total_bitrate,
VideoBitrateAllocation* allocated_bitrates_bps); DataRate stable_bitrate,
VideoBitrateAllocation* allocated_bitrates);
void DistributeAllocationToTemporalLayers( void DistributeAllocationToTemporalLayers(
VideoBitrateAllocation* allocated_bitrates_bps) const; VideoBitrateAllocation* allocated_bitrates) const;
std::vector<uint32_t> DefaultTemporalLayerAllocation(int bitrate_kbps, std::vector<uint32_t> DefaultTemporalLayerAllocation(int bitrate_kbps,
int max_bitrate_kbps, int max_bitrate_kbps,
int simulcast_id) const; int simulcast_id) const;
@ -50,7 +52,7 @@ class SimulcastRateAllocator : public VideoBitrateAllocator {
int NumTemporalStreams(size_t simulcast_id) const; int NumTemporalStreams(size_t simulcast_id) const;
const VideoCodec codec_; const VideoCodec codec_;
const double hysteresis_factor_; const StableTargetRateExperiment stable_rate_settings_;
std::vector<bool> stream_enabled_; std::vector<bool> stream_enabled_;
RTC_DISALLOW_COPY_AND_ASSIGN(SimulcastRateAllocator); RTC_DISALLOW_COPY_AND_ASSIGN(SimulcastRateAllocator);

View File

@ -136,6 +136,24 @@ class SimulcastRateAllocatorTest : public ::testing::TestWithParam<bool> {
DataRate::kbps(target_bitrate), kDefaultFrameRate)); DataRate::kbps(target_bitrate), kDefaultFrameRate));
} }
VideoBitrateAllocation GetAllocation(DataRate target_rate,
DataRate stable_rate) {
return allocator_->Allocate(VideoBitrateAllocationParameters(
target_rate, stable_rate, kDefaultFrameRate));
}
DataRate MinRate(size_t layer_index) const {
return DataRate::kbps(codec_.simulcastStream[layer_index].minBitrate);
}
DataRate TargetRate(size_t layer_index) const {
return DataRate::kbps(codec_.simulcastStream[layer_index].targetBitrate);
}
DataRate MaxRate(size_t layer_index) const {
return DataRate::kbps(codec_.simulcastStream[layer_index].maxBitrate);
}
protected: protected:
static const int kDefaultFrameRate = 30; static const int kDefaultFrameRate = 30;
VideoCodec codec_; VideoCodec codec_;
@ -524,6 +542,71 @@ TEST_F(SimulcastRateAllocatorTest, NonConferenceModeScreenshare) {
EXPECT_EQ(alloc.GetTemporalLayerAllocation(2).size(), 3u); EXPECT_EQ(alloc.GetTemporalLayerAllocation(2).size(), 3u);
} }
TEST_F(SimulcastRateAllocatorTest, StableRate) {
webrtc::test::ScopedFieldTrials field_trials(
"WebRTC-StableTargetRate/"
"enabled:true,"
"video_hysteresis_factor:1.1/");
SetupCodec3SL3TL({true, true, true});
CreateAllocator();
// Let the volatile rate always be be enough for all streams, in this test we
// are only interested in how the stable rate affects enablement.
const DataRate volatile_rate =
(TargetRate(0) + TargetRate(1) + MinRate(2)) * 1.1;
{
// On the first call to a new SimulcastRateAllocator instance, hysteresis
// is disabled, but stable rate still caps layers.
uint32_t expected[] = {TargetRate(0).kbps<uint32_t>(),
MaxRate(1).kbps<uint32_t>()};
ExpectEqual(expected,
GetAllocation(volatile_rate, TargetRate(0) + MinRate(1)));
}
{
// Let stable rate go to a bitrate below what is needed for two streams.
uint32_t expected[] = {MaxRate(0).kbps<uint32_t>(), 0};
ExpectEqual(expected,
GetAllocation(volatile_rate,
TargetRate(0) + MinRate(1) - DataRate::bps(1)));
}
{
// Don't enable stream as we need to get up above hysteresis threshold.
uint32_t expected[] = {MaxRate(0).kbps<uint32_t>(), 0};
ExpectEqual(expected,
GetAllocation(volatile_rate, TargetRate(0) + MinRate(1)));
}
{
// Above threshold with hysteresis, enable second stream.
uint32_t expected[] = {TargetRate(0).kbps<uint32_t>(),
MaxRate(1).kbps<uint32_t>()};
ExpectEqual(expected, GetAllocation(volatile_rate,
(TargetRate(0) + MinRate(1)) * 1.1));
}
{
// Enough to enable all thee layers.
uint32_t expected[] = {
TargetRate(0).kbps<uint32_t>(), TargetRate(1).kbps<uint32_t>(),
(volatile_rate - TargetRate(0) - TargetRate(1)).kbps<uint32_t>()};
ExpectEqual(expected, GetAllocation(volatile_rate, volatile_rate));
}
{
// Drop hysteresis, all three still on.
uint32_t expected[] = {
TargetRate(0).kbps<uint32_t>(), TargetRate(1).kbps<uint32_t>(),
(volatile_rate - TargetRate(0) - TargetRate(1)).kbps<uint32_t>()};
ExpectEqual(expected,
GetAllocation(volatile_rate,
TargetRate(0) + TargetRate(1) + MinRate(2)));
}
}
class ScreenshareRateAllocationTest : public SimulcastRateAllocatorTest { class ScreenshareRateAllocationTest : public SimulcastRateAllocatorTest {
public: public:
void SetupConferenceScreenshare(bool use_simulcast, bool active = true) { void SetupConferenceScreenshare(bool use_simulcast, bool active = true) {

View File

@ -20,8 +20,8 @@ constexpr char kFieldTrialName[] = "WebRTC-StableTargetRate";
StableTargetRateExperiment::StableTargetRateExperiment( StableTargetRateExperiment::StableTargetRateExperiment(
const WebRtcKeyValueConfig* const key_value_config, const WebRtcKeyValueConfig* const key_value_config,
absl::optional<double> default_video_hysteresis, double default_video_hysteresis,
absl::optional<double> default_screenshare_hysteresis) double default_screenshare_hysteresis)
: enabled_("enabled", false), : enabled_("enabled", false),
video_hysteresis_factor_("video_hysteresis_factor", video_hysteresis_factor_("video_hysteresis_factor",
default_video_hysteresis), default_video_hysteresis),
@ -44,31 +44,25 @@ StableTargetRateExperiment StableTargetRateExperiment::ParseFromFieldTrials() {
StableTargetRateExperiment StableTargetRateExperiment::ParseFromKeyValueConfig( StableTargetRateExperiment StableTargetRateExperiment::ParseFromKeyValueConfig(
const WebRtcKeyValueConfig* const key_value_config) { const WebRtcKeyValueConfig* const key_value_config) {
if (key_value_config->Lookup("WebRTC-VideoRateControl") != "") {
RateControlSettings rate_control = RateControlSettings rate_control =
RateControlSettings::ParseFromKeyValueConfig(key_value_config); RateControlSettings::ParseFromKeyValueConfig(key_value_config);
return StableTargetRateExperiment(key_value_config, return StableTargetRateExperiment(
rate_control.GetSimulcastHysteresisFactor( key_value_config,
VideoCodecMode::kRealtimeVideo), rate_control.GetSimulcastHysteresisFactor(VideoCodecMode::kRealtimeVideo),
rate_control.GetSimulcastHysteresisFactor( rate_control.GetSimulcastHysteresisFactor(
VideoCodecMode::kScreensharing)); VideoCodecMode::kScreensharing));
}
return StableTargetRateExperiment(key_value_config, absl::nullopt,
absl::nullopt);
} }
bool StableTargetRateExperiment::IsEnabled() const { bool StableTargetRateExperiment::IsEnabled() const {
return enabled_.Get(); return enabled_.Get();
} }
absl::optional<double> StableTargetRateExperiment::GetVideoHysteresisFactor() double StableTargetRateExperiment::GetVideoHysteresisFactor() const {
const { return video_hysteresis_factor_.Get();
return video_hysteresis_factor_.GetOptional();
} }
absl::optional<double> double StableTargetRateExperiment::GetScreenshareHysteresisFactor() const {
StableTargetRateExperiment::GetScreenshareHysteresisFactor() const { return screenshare_hysteresis_factor_.Get();
return screenshare_hysteresis_factor_.GetOptional();
} }
} // namespace webrtc } // namespace webrtc

View File

@ -25,18 +25,18 @@ class StableTargetRateExperiment {
const WebRtcKeyValueConfig* const key_value_config); const WebRtcKeyValueConfig* const key_value_config);
bool IsEnabled() const; bool IsEnabled() const;
absl::optional<double> GetVideoHysteresisFactor() const; double GetVideoHysteresisFactor() const;
absl::optional<double> GetScreenshareHysteresisFactor() const; double GetScreenshareHysteresisFactor() const;
private: private:
explicit StableTargetRateExperiment( explicit StableTargetRateExperiment(
const WebRtcKeyValueConfig* const key_value_config, const WebRtcKeyValueConfig* const key_value_config,
absl::optional<double> default_video_hysteresis, double default_video_hysteresis,
absl::optional<double> default_screenshare_hysteresis); double default_screenshare_hysteresis);
FieldTrialParameter<bool> enabled_; FieldTrialParameter<bool> enabled_;
FieldTrialOptional<double> video_hysteresis_factor_; FieldTrialParameter<double> video_hysteresis_factor_;
FieldTrialOptional<double> screenshare_hysteresis_factor_; FieldTrialParameter<double> screenshare_hysteresis_factor_;
}; };
} // namespace webrtc } // namespace webrtc

View File

@ -19,8 +19,8 @@ TEST(StableBweExperimentTest, Default) {
StableTargetRateExperiment config = StableTargetRateExperiment config =
StableTargetRateExperiment::ParseFromFieldTrials(); StableTargetRateExperiment::ParseFromFieldTrials();
EXPECT_FALSE(config.IsEnabled()); EXPECT_FALSE(config.IsEnabled());
EXPECT_FALSE(config.GetVideoHysteresisFactor()); EXPECT_EQ(config.GetVideoHysteresisFactor(), 1.0);
EXPECT_FALSE(config.GetScreenshareHysteresisFactor()); EXPECT_EQ(config.GetScreenshareHysteresisFactor(), 1.35);
} }
TEST(StableBweExperimentTest, EnabledNoHysteresis) { TEST(StableBweExperimentTest, EnabledNoHysteresis) {
@ -30,8 +30,8 @@ TEST(StableBweExperimentTest, EnabledNoHysteresis) {
StableTargetRateExperiment config = StableTargetRateExperiment config =
StableTargetRateExperiment::ParseFromFieldTrials(); StableTargetRateExperiment::ParseFromFieldTrials();
EXPECT_TRUE(config.IsEnabled()); EXPECT_TRUE(config.IsEnabled());
EXPECT_FALSE(config.GetVideoHysteresisFactor()); EXPECT_EQ(config.GetVideoHysteresisFactor(), 1.0);
EXPECT_FALSE(config.GetScreenshareHysteresisFactor()); EXPECT_EQ(config.GetScreenshareHysteresisFactor(), 1.35);
} }
TEST(StableBweExperimentTest, EnabledWithHysteresis) { TEST(StableBweExperimentTest, EnabledWithHysteresis) {