Changes default pacing factor to 1.1x
This changes the default behavior to use pacing factor of 1.1x instead of 2.5x, it also sets libvpx rate controler as trusted, turns on the encoder pushback mechanism and sets spatial hysteresis to 1.2. The unused "dynamic rate" settings in libvpx is removed. The new settings matches what has been used in chromium since 2019. If needed, the legacy behavior can be enabled using the field trial WebRTC-VideoRateControl. Bug: webrtc:10155 Change-Id: I8186b491aa5bef61e8f568e96c980ca68f0c208f Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/186661 Commit-Queue: Erik Språng <sprang@webrtc.org> Reviewed-by: Per Kjellander <perkj@webrtc.org> Reviewed-by: Christoffer Rodbro <crodbro@webrtc.org> Cr-Commit-Position: refs/heads/master@{#32477}
This commit is contained in:
@ -68,9 +68,6 @@ constexpr uint32_t kVp832ByteAlign = 32u;
|
||||
constexpr int kRtpTicksPerSecond = 90000;
|
||||
constexpr int kRtpTicksPerMs = kRtpTicksPerSecond / 1000;
|
||||
|
||||
constexpr double kLowRateFactor = 1.0;
|
||||
constexpr double kHighRateFactor = 2.0;
|
||||
|
||||
// VP8 denoiser states.
|
||||
enum denoiserState : uint32_t {
|
||||
kDenoiserOff,
|
||||
@ -82,15 +79,6 @@ enum denoiserState : uint32_t {
|
||||
kDenoiserOnAdaptive
|
||||
};
|
||||
|
||||
// These settings correspond to the settings in vpx_codec_enc_cfg.
|
||||
struct Vp8RateSettings {
|
||||
uint32_t rc_undershoot_pct;
|
||||
uint32_t rc_overshoot_pct;
|
||||
uint32_t rc_buf_sz;
|
||||
uint32_t rc_buf_optimal_sz;
|
||||
uint32_t rc_dropframe_thresh;
|
||||
};
|
||||
|
||||
// Greatest common divisior
|
||||
int GCD(int a, int b) {
|
||||
int c = a % b;
|
||||
@ -102,56 +90,6 @@ int GCD(int a, int b) {
|
||||
return b;
|
||||
}
|
||||
|
||||
uint32_t Interpolate(uint32_t low,
|
||||
uint32_t high,
|
||||
double bandwidth_headroom_factor) {
|
||||
RTC_DCHECK_GE(bandwidth_headroom_factor, kLowRateFactor);
|
||||
RTC_DCHECK_LE(bandwidth_headroom_factor, kHighRateFactor);
|
||||
|
||||
// |factor| is between 0.0 and 1.0.
|
||||
const double factor = bandwidth_headroom_factor - kLowRateFactor;
|
||||
|
||||
return static_cast<uint32_t>(((1.0 - factor) * low) + (factor * high) + 0.5);
|
||||
}
|
||||
|
||||
Vp8RateSettings GetRateSettings(double bandwidth_headroom_factor) {
|
||||
static const Vp8RateSettings low_settings{1000u, 0u, 100u, 30u, 40u};
|
||||
static const Vp8RateSettings high_settings{100u, 15u, 1000u, 600u, 5u};
|
||||
|
||||
if (bandwidth_headroom_factor <= kLowRateFactor) {
|
||||
return low_settings;
|
||||
} else if (bandwidth_headroom_factor >= kHighRateFactor) {
|
||||
return high_settings;
|
||||
}
|
||||
|
||||
Vp8RateSettings settings;
|
||||
settings.rc_undershoot_pct =
|
||||
Interpolate(low_settings.rc_undershoot_pct,
|
||||
high_settings.rc_undershoot_pct, bandwidth_headroom_factor);
|
||||
settings.rc_overshoot_pct =
|
||||
Interpolate(low_settings.rc_overshoot_pct, high_settings.rc_overshoot_pct,
|
||||
bandwidth_headroom_factor);
|
||||
settings.rc_buf_sz =
|
||||
Interpolate(low_settings.rc_buf_sz, high_settings.rc_buf_sz,
|
||||
bandwidth_headroom_factor);
|
||||
settings.rc_buf_optimal_sz =
|
||||
Interpolate(low_settings.rc_buf_optimal_sz,
|
||||
high_settings.rc_buf_optimal_sz, bandwidth_headroom_factor);
|
||||
settings.rc_dropframe_thresh =
|
||||
Interpolate(low_settings.rc_dropframe_thresh,
|
||||
high_settings.rc_dropframe_thresh, bandwidth_headroom_factor);
|
||||
return settings;
|
||||
}
|
||||
|
||||
void UpdateRateSettings(vpx_codec_enc_cfg_t* config,
|
||||
const Vp8RateSettings& new_settings) {
|
||||
config->rc_undershoot_pct = new_settings.rc_undershoot_pct;
|
||||
config->rc_overshoot_pct = new_settings.rc_overshoot_pct;
|
||||
config->rc_buf_sz = new_settings.rc_buf_sz;
|
||||
config->rc_buf_optimal_sz = new_settings.rc_buf_optimal_sz;
|
||||
config->rc_dropframe_thresh = new_settings.rc_dropframe_thresh;
|
||||
}
|
||||
|
||||
static_assert(Vp8EncoderConfig::TemporalLayerConfig::kMaxPeriodicity ==
|
||||
VPX_TS_MAX_PERIODICITY,
|
||||
"Vp8EncoderConfig::kMaxPeriodicity must be kept in sync with the "
|
||||
@ -407,14 +345,6 @@ void LibvpxVp8Encoder::SetRates(const RateControlParameters& parameters) {
|
||||
|
||||
UpdateVpxConfiguration(stream_idx);
|
||||
|
||||
if (rate_control_settings_.Vp8DynamicRateSettings()) {
|
||||
// Tweak rate control settings based on available network headroom.
|
||||
UpdateRateSettings(
|
||||
&vpx_configs_[i],
|
||||
GetRateSettings(parameters.bandwidth_allocation.bps<double>() /
|
||||
parameters.bitrate.get_sum_bps()));
|
||||
}
|
||||
|
||||
vpx_codec_err_t err =
|
||||
libvpx_->codec_enc_config_set(&encoders_[i], &vpx_configs_[i]);
|
||||
if (err != VPX_CODEC_OK) {
|
||||
|
||||
@ -190,68 +190,6 @@ TEST_F(TestVp8Impl, SetRates) {
|
||||
bitrate_allocation, static_cast<double>(codec_settings_.maxFramerate)));
|
||||
}
|
||||
|
||||
TEST_F(TestVp8Impl, DynamicSetRates) {
|
||||
test::ScopedFieldTrials field_trials(
|
||||
"WebRTC-VideoRateControl/vp8_dynamic_rate:true/");
|
||||
auto* const vpx = new NiceMock<MockLibvpxVp8Interface>();
|
||||
LibvpxVp8Encoder encoder((std::unique_ptr<LibvpxInterface>(vpx)),
|
||||
VP8Encoder::Settings());
|
||||
EXPECT_EQ(WEBRTC_VIDEO_CODEC_OK,
|
||||
encoder.InitEncode(&codec_settings_,
|
||||
VideoEncoder::Settings(kCapabilities, 1, 1000)));
|
||||
|
||||
const uint32_t kBitrateBps = 300000;
|
||||
VideoEncoder::RateControlParameters rate_settings;
|
||||
rate_settings.bitrate.SetBitrate(0, 0, kBitrateBps);
|
||||
rate_settings.framerate_fps =
|
||||
static_cast<double>(codec_settings_.maxFramerate);
|
||||
|
||||
// Set rates with no headroom.
|
||||
rate_settings.bandwidth_allocation = DataRate::BitsPerSec(kBitrateBps);
|
||||
EXPECT_CALL(
|
||||
*vpx,
|
||||
codec_enc_config_set(
|
||||
_, AllOf(Field(&vpx_codec_enc_cfg_t::rc_target_bitrate,
|
||||
kBitrateBps / 1000),
|
||||
Field(&vpx_codec_enc_cfg_t::rc_undershoot_pct, 1000u),
|
||||
Field(&vpx_codec_enc_cfg_t::rc_overshoot_pct, 0u),
|
||||
Field(&vpx_codec_enc_cfg_t::rc_buf_sz, 100u),
|
||||
Field(&vpx_codec_enc_cfg_t::rc_buf_optimal_sz, 30u),
|
||||
Field(&vpx_codec_enc_cfg_t::rc_dropframe_thresh, 40u))))
|
||||
.WillOnce(Return(VPX_CODEC_OK));
|
||||
encoder.SetRates(rate_settings);
|
||||
|
||||
// Set rates with max headroom.
|
||||
rate_settings.bandwidth_allocation = DataRate::BitsPerSec(kBitrateBps * 2);
|
||||
EXPECT_CALL(
|
||||
*vpx, codec_enc_config_set(
|
||||
_, AllOf(Field(&vpx_codec_enc_cfg_t::rc_target_bitrate,
|
||||
kBitrateBps / 1000),
|
||||
Field(&vpx_codec_enc_cfg_t::rc_undershoot_pct, 100u),
|
||||
Field(&vpx_codec_enc_cfg_t::rc_overshoot_pct, 15u),
|
||||
Field(&vpx_codec_enc_cfg_t::rc_buf_sz, 1000u),
|
||||
Field(&vpx_codec_enc_cfg_t::rc_buf_optimal_sz, 600u),
|
||||
Field(&vpx_codec_enc_cfg_t::rc_dropframe_thresh, 5u))))
|
||||
.WillOnce(Return(VPX_CODEC_OK));
|
||||
encoder.SetRates(rate_settings);
|
||||
|
||||
// Set rates with headroom half way.
|
||||
rate_settings.bandwidth_allocation =
|
||||
DataRate::BitsPerSec((3 * kBitrateBps) / 2);
|
||||
EXPECT_CALL(
|
||||
*vpx,
|
||||
codec_enc_config_set(
|
||||
_, AllOf(Field(&vpx_codec_enc_cfg_t::rc_target_bitrate,
|
||||
kBitrateBps / 1000),
|
||||
Field(&vpx_codec_enc_cfg_t::rc_undershoot_pct, 550u),
|
||||
Field(&vpx_codec_enc_cfg_t::rc_overshoot_pct, 8u),
|
||||
Field(&vpx_codec_enc_cfg_t::rc_buf_sz, 550u),
|
||||
Field(&vpx_codec_enc_cfg_t::rc_buf_optimal_sz, 315u),
|
||||
Field(&vpx_codec_enc_cfg_t::rc_dropframe_thresh, 23u))))
|
||||
.WillOnce(Return(VPX_CODEC_OK));
|
||||
encoder.SetRates(rate_settings);
|
||||
}
|
||||
|
||||
TEST_F(TestVp8Impl, EncodeFrameAndRelease) {
|
||||
EXPECT_EQ(WEBRTC_VIDEO_CODEC_OK, encoder_->Release());
|
||||
EXPECT_EQ(WEBRTC_VIDEO_CODEC_OK,
|
||||
|
||||
@ -56,9 +56,6 @@ uint8_t kUpdBufIdx[4] = {0, 0, 1, 0};
|
||||
// Maximum allowed PID difference for differnet per-layer frame-rate case.
|
||||
const int kMaxAllowedPidDiff = 30;
|
||||
|
||||
constexpr double kLowRateFactor = 1.0;
|
||||
constexpr double kHighRateFactor = 2.0;
|
||||
|
||||
// TODO(ilink): Tune these thresholds further.
|
||||
// Selected using ConverenceMotion_1280_720_50.yuv clip.
|
||||
// No toggling observed on any link capacity from 100-2000kbps.
|
||||
@ -68,15 +65,6 @@ constexpr double kHighRateFactor = 2.0;
|
||||
constexpr int kLowVp9QpThreshold = 149;
|
||||
constexpr int kHighVp9QpThreshold = 205;
|
||||
|
||||
// These settings correspond to the settings in vpx_codec_enc_cfg.
|
||||
struct Vp9RateSettings {
|
||||
uint32_t rc_undershoot_pct;
|
||||
uint32_t rc_overshoot_pct;
|
||||
uint32_t rc_buf_sz;
|
||||
uint32_t rc_buf_optimal_sz;
|
||||
uint32_t rc_dropframe_thresh;
|
||||
};
|
||||
|
||||
// Only positive speeds, range for real-time coding currently is: 5 - 8.
|
||||
// Lower means slower/better quality, higher means fastest/lower quality.
|
||||
int GetCpuSpeed(int width, int height) {
|
||||
@ -168,56 +156,6 @@ std::pair<size_t, size_t> GetActiveLayers(
|
||||
return {0, 0};
|
||||
}
|
||||
|
||||
uint32_t Interpolate(uint32_t low,
|
||||
uint32_t high,
|
||||
double bandwidth_headroom_factor) {
|
||||
RTC_DCHECK_GE(bandwidth_headroom_factor, kLowRateFactor);
|
||||
RTC_DCHECK_LE(bandwidth_headroom_factor, kHighRateFactor);
|
||||
|
||||
// |factor| is between 0.0 and 1.0.
|
||||
const double factor = bandwidth_headroom_factor - kLowRateFactor;
|
||||
|
||||
return static_cast<uint32_t>(((1.0 - factor) * low) + (factor * high) + 0.5);
|
||||
}
|
||||
|
||||
Vp9RateSettings GetRateSettings(double bandwidth_headroom_factor) {
|
||||
static const Vp9RateSettings low_settings{100u, 0u, 100u, 33u, 40u};
|
||||
static const Vp9RateSettings high_settings{50u, 50u, 1000u, 700u, 5u};
|
||||
|
||||
if (bandwidth_headroom_factor <= kLowRateFactor) {
|
||||
return low_settings;
|
||||
} else if (bandwidth_headroom_factor >= kHighRateFactor) {
|
||||
return high_settings;
|
||||
}
|
||||
|
||||
Vp9RateSettings settings;
|
||||
settings.rc_undershoot_pct =
|
||||
Interpolate(low_settings.rc_undershoot_pct,
|
||||
high_settings.rc_undershoot_pct, bandwidth_headroom_factor);
|
||||
settings.rc_overshoot_pct =
|
||||
Interpolate(low_settings.rc_overshoot_pct, high_settings.rc_overshoot_pct,
|
||||
bandwidth_headroom_factor);
|
||||
settings.rc_buf_sz =
|
||||
Interpolate(low_settings.rc_buf_sz, high_settings.rc_buf_sz,
|
||||
bandwidth_headroom_factor);
|
||||
settings.rc_buf_optimal_sz =
|
||||
Interpolate(low_settings.rc_buf_optimal_sz,
|
||||
high_settings.rc_buf_optimal_sz, bandwidth_headroom_factor);
|
||||
settings.rc_dropframe_thresh =
|
||||
Interpolate(low_settings.rc_dropframe_thresh,
|
||||
high_settings.rc_dropframe_thresh, bandwidth_headroom_factor);
|
||||
return settings;
|
||||
}
|
||||
|
||||
void UpdateRateSettings(vpx_codec_enc_cfg_t* config,
|
||||
const Vp9RateSettings& new_settings) {
|
||||
config->rc_undershoot_pct = new_settings.rc_undershoot_pct;
|
||||
config->rc_overshoot_pct = new_settings.rc_overshoot_pct;
|
||||
config->rc_buf_sz = new_settings.rc_buf_sz;
|
||||
config->rc_buf_optimal_sz = new_settings.rc_buf_optimal_sz;
|
||||
config->rc_dropframe_thresh = new_settings.rc_dropframe_thresh;
|
||||
}
|
||||
|
||||
std::unique_ptr<ScalableVideoController> CreateVp9ScalabilityStructure(
|
||||
const VideoCodec& codec) {
|
||||
int num_spatial_layers = codec.VP9().numberOfSpatialLayers;
|
||||
@ -364,9 +302,6 @@ VP9EncoderImpl::VP9EncoderImpl(const cricket::VideoCodec& codec,
|
||||
trusted_rate_controller_(
|
||||
RateControlSettings::ParseFromKeyValueConfig(&trials)
|
||||
.LibvpxVp9TrustedRateController()),
|
||||
dynamic_rate_settings_(
|
||||
RateControlSettings::ParseFromKeyValueConfig(&trials)
|
||||
.Vp9DynamicRateSettings()),
|
||||
layer_buffering_(false),
|
||||
full_superframe_drop_(true),
|
||||
first_frame_in_picture_(true),
|
||||
@ -586,13 +521,6 @@ void VP9EncoderImpl::SetRates(const RateControlParameters& parameters) {
|
||||
|
||||
codec_.maxFramerate = static_cast<uint32_t>(parameters.framerate_fps + 0.5);
|
||||
|
||||
if (dynamic_rate_settings_) {
|
||||
// Tweak rate control settings based on available network headroom.
|
||||
UpdateRateSettings(
|
||||
config_, GetRateSettings(parameters.bandwidth_allocation.bps<double>() /
|
||||
parameters.bitrate.get_sum_bps()));
|
||||
}
|
||||
|
||||
bool res = SetSvcRates(parameters.bitrate);
|
||||
RTC_DCHECK(res) << "Failed to set new bitrate allocation";
|
||||
config_changed_ = true;
|
||||
|
||||
@ -132,7 +132,6 @@ class VP9EncoderImpl : public VP9Encoder {
|
||||
InterLayerPredMode inter_layer_pred_;
|
||||
bool external_ref_control_;
|
||||
const bool trusted_rate_controller_;
|
||||
const bool dynamic_rate_settings_;
|
||||
bool layer_buffering_;
|
||||
const bool full_superframe_drop_;
|
||||
vpx_svc_frame_drop_t svc_drop_frame_;
|
||||
|
||||
@ -35,6 +35,9 @@ constexpr uint32_t kLegacyScreenshareMaxBitrateKbps = 1000;
|
||||
// Bitrates for upper simulcast screenshare layer.
|
||||
constexpr uint32_t kSimulcastScreenshareMinBitrateKbps = 600;
|
||||
constexpr uint32_t kSimulcastScreenshareMaxBitrateKbps = 1250;
|
||||
// Default video hysteresis factor: allocatable bitrate for next layer must
|
||||
// exceed 20% of min setting in order to be initially turned on.
|
||||
const double kDefaultHysteresis = 1.2;
|
||||
|
||||
class MockTemporalLayers : public Vp8FrameBufferController {
|
||||
public:
|
||||
@ -229,6 +232,7 @@ TEST_F(SimulcastRateAllocatorTest, SingleSimulcastBelowMin) {
|
||||
TEST_F(SimulcastRateAllocatorTest, SignalsBwLimited) {
|
||||
// Enough to enable all layers.
|
||||
const int kVeryBigBitrate = 100000;
|
||||
|
||||
// With simulcast, use the min bitrate from the ss spec instead of the global.
|
||||
SetupCodec3SL3TL({true, true, true});
|
||||
CreateAllocator();
|
||||
@ -240,10 +244,13 @@ TEST_F(SimulcastRateAllocatorTest, SignalsBwLimited) {
|
||||
EXPECT_TRUE(GetAllocation(codec_.simulcastStream[0].targetBitrate +
|
||||
codec_.simulcastStream[1].minBitrate)
|
||||
.is_bw_limited());
|
||||
EXPECT_FALSE(GetAllocation(codec_.simulcastStream[0].targetBitrate +
|
||||
codec_.simulcastStream[1].targetBitrate +
|
||||
codec_.simulcastStream[2].minBitrate)
|
||||
.is_bw_limited());
|
||||
EXPECT_FALSE(
|
||||
GetAllocation(
|
||||
codec_.simulcastStream[0].targetBitrate +
|
||||
codec_.simulcastStream[1].targetBitrate +
|
||||
static_cast<uint32_t>(
|
||||
codec_.simulcastStream[2].minBitrate * kDefaultHysteresis + 0.5))
|
||||
.is_bw_limited());
|
||||
EXPECT_FALSE(GetAllocation(kVeryBigBitrate).is_bw_limited());
|
||||
}
|
||||
|
||||
@ -339,20 +346,23 @@ TEST_F(SimulcastRateAllocatorTest, OneToThreeStreams) {
|
||||
ExpectEqual(expected, GetAllocation(bitrate));
|
||||
}
|
||||
|
||||
uint32_t kMinInitialRateTwoLayers =
|
||||
codec_.simulcastStream[0].targetBitrate +
|
||||
static_cast<uint32_t>(codec_.simulcastStream[1].minBitrate *
|
||||
kDefaultHysteresis);
|
||||
{
|
||||
// Bitrate above target for first stream, but below min for the next one.
|
||||
const uint32_t bitrate = codec_.simulcastStream[0].targetBitrate +
|
||||
codec_.simulcastStream[1].minBitrate - 1;
|
||||
const uint32_t bitrate = kMinInitialRateTwoLayers - 1;
|
||||
uint32_t expected[] = {bitrate, 0, 0};
|
||||
ExpectEqual(expected, GetAllocation(bitrate));
|
||||
}
|
||||
|
||||
{
|
||||
// Just enough for two streams.
|
||||
const uint32_t bitrate = codec_.simulcastStream[0].targetBitrate +
|
||||
codec_.simulcastStream[1].minBitrate;
|
||||
uint32_t expected[] = {codec_.simulcastStream[0].targetBitrate,
|
||||
codec_.simulcastStream[1].minBitrate, 0};
|
||||
const uint32_t bitrate = kMinInitialRateTwoLayers;
|
||||
uint32_t expected[] = {
|
||||
codec_.simulcastStream[0].targetBitrate,
|
||||
kMinInitialRateTwoLayers - codec_.simulcastStream[0].targetBitrate, 0};
|
||||
ExpectEqual(expected, GetAllocation(bitrate));
|
||||
}
|
||||
|
||||
@ -365,11 +375,15 @@ TEST_F(SimulcastRateAllocatorTest, OneToThreeStreams) {
|
||||
ExpectEqual(expected, GetAllocation(bitrate));
|
||||
}
|
||||
|
||||
uint32_t kMinInitialRateThreeLayers =
|
||||
codec_.simulcastStream[0].targetBitrate +
|
||||
codec_.simulcastStream[1].targetBitrate +
|
||||
static_cast<uint32_t>(codec_.simulcastStream[2].minBitrate *
|
||||
kDefaultHysteresis);
|
||||
{
|
||||
// First two streams maxed out, but not enough for third. Nowhere to put
|
||||
// remaining bits.
|
||||
const uint32_t bitrate = codec_.simulcastStream[0].maxBitrate +
|
||||
codec_.simulcastStream[1].maxBitrate + 499;
|
||||
const uint32_t bitrate = kMinInitialRateThreeLayers - 1;
|
||||
uint32_t expected[] = {codec_.simulcastStream[0].targetBitrate,
|
||||
codec_.simulcastStream[1].maxBitrate, 0};
|
||||
ExpectEqual(expected, GetAllocation(bitrate));
|
||||
@ -377,12 +391,12 @@ TEST_F(SimulcastRateAllocatorTest, OneToThreeStreams) {
|
||||
|
||||
{
|
||||
// Just enough for all three streams.
|
||||
const uint32_t bitrate = codec_.simulcastStream[0].targetBitrate +
|
||||
codec_.simulcastStream[1].targetBitrate +
|
||||
codec_.simulcastStream[2].minBitrate;
|
||||
uint32_t expected[] = {codec_.simulcastStream[0].targetBitrate,
|
||||
codec_.simulcastStream[1].targetBitrate,
|
||||
codec_.simulcastStream[2].minBitrate};
|
||||
const uint32_t bitrate = kMinInitialRateThreeLayers;
|
||||
uint32_t expected[] = {
|
||||
codec_.simulcastStream[0].targetBitrate,
|
||||
codec_.simulcastStream[1].targetBitrate,
|
||||
static_cast<uint32_t>(codec_.simulcastStream[2].minBitrate *
|
||||
kDefaultHysteresis)};
|
||||
ExpectEqual(expected, GetAllocation(bitrate));
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user