Throttle frame-rate In VP9 encoder in steady state for screenshare

If minQP is reached and encoder undershoot consistently, we consider the
quality good enough and throttle encode frame rate.

This CL also adds perf tests for high fps vp9 screenshare.

Bug: webrtc:10310
Change-Id: I49fc7d31f9f596a9ecb5f85fe9e0c7861d4915f9
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/125761
Commit-Queue: Ilya Nikolaevskiy <ilnik@webrtc.org>
Reviewed-by: Erik Språng <sprang@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#26997}
This commit is contained in:
Ilya Nikolaevskiy
2019-03-06 16:04:32 +01:00
committed by Commit Bot
parent 0cb858c7f2
commit 6117068af4
3 changed files with 136 additions and 10 deletions

View File

@ -47,7 +47,7 @@ uint8_t kUpdBufIdx[4] = {0, 0, 1, 0};
int kMaxNumTiles4kVideo = 8;
// Maximum allowed PID difference for variable frame-rate mode.
// Maximum allowed PID difference for differnet per-layer frame-rate case.
const int kMaxAllowedPidDIff = 30;
// Only positive speeds, range for real-time coding currently is: 5 - 8.
@ -173,7 +173,12 @@ VP9EncoderImpl::VP9EncoderImpl(const cricket::VideoCodec& codec)
full_superframe_drop_(true),
first_frame_in_picture_(true),
ss_info_needed_(false),
is_flexible_mode_(false) {
is_flexible_mode_(false),
variable_framerate_experiment_(ParseVariableFramerateConfig(
"WebRTC-VP9VariableFramerateScreenshare")),
variable_framerate_controller_(
variable_framerate_experiment_.framerate_limit),
num_steady_state_frames_(0) {
codec_ = {};
memset(&svc_params_, 0, sizeof(vpx_svc_extra_cfg_t));
}
@ -742,13 +747,39 @@ int VP9EncoderImpl::Encode(const VideoFrame& input_image,
const uint32_t frame_timestamp_ms =
1000 * input_image.timestamp() / kVideoPayloadTypeFrequency;
// To ensure that several rate-limiters with different limits don't
// interfere, they must be queried in order of increasing limit.
bool use_steady_state_limiter =
variable_framerate_experiment_.enabled &&
input_image.update_rect().IsEmpty() &&
num_steady_state_frames_ >=
variable_framerate_experiment_.frames_before_steady_state;
for (uint8_t sl_idx = 0; sl_idx < num_active_spatial_layers_; ++sl_idx) {
const float layer_fps =
framerate_controller_[layer_id.spatial_layer_id].GetTargetRate();
// Use steady state rate-limiter at the correct place.
if (use_steady_state_limiter &&
layer_fps > variable_framerate_experiment_.framerate_limit - 1e-9) {
if (variable_framerate_controller_.DropFrame(frame_timestamp_ms)) {
layer_id.spatial_layer_id = num_active_spatial_layers_;
}
// Break always: if rate limiter triggered frame drop, no need to
// continue; otherwise, the rate is less than the next limiters.
break;
}
if (framerate_controller_[sl_idx].DropFrame(frame_timestamp_ms)) {
++layer_id.spatial_layer_id;
} else {
break;
}
}
if (use_steady_state_limiter &&
layer_id.spatial_layer_id < num_active_spatial_layers_) {
variable_framerate_controller_.AddFrame(frame_timestamp_ms);
}
}
RTC_DCHECK_LE(layer_id.spatial_layer_id, num_active_spatial_layers_);
@ -1328,14 +1359,30 @@ void VP9EncoderImpl::DeliverBufferedFrame(bool end_of_picture) {
encoded_complete_callback_->OnEncodedImage(encoded_image_, &codec_specific_,
&frag_info);
encoded_image_.set_size(0);
if (codec_.mode == VideoCodecMode::kScreensharing) {
const uint8_t spatial_idx = encoded_image_.SpatialIndex().value_or(0);
const uint32_t frame_timestamp_ms =
1000 * encoded_image_.Timestamp() / kVideoPayloadTypeFrequency;
framerate_controller_[spatial_idx].AddFrame(frame_timestamp_ms);
const size_t steady_state_size = SteadyStateSize(
spatial_idx, codec_specific_.codecSpecific.VP9.temporal_idx);
// Only frames on spatial layers, which may be limited in a steady state
// are considered for steady state detection.
if (framerate_controller_[spatial_idx].GetTargetRate() >
variable_framerate_experiment_.framerate_limit + 1e-9) {
if (encoded_image_.qp_ <=
variable_framerate_experiment_.steady_state_qp &&
encoded_image_.size() <= steady_state_size) {
++num_steady_state_frames_;
} else {
num_steady_state_frames_ = 0;
}
}
}
encoded_image_.set_size(0);
}
}
@ -1372,6 +1419,42 @@ VideoEncoder::EncoderInfo VP9EncoderImpl::GetEncoderInfo() const {
return info;
}
size_t VP9EncoderImpl::SteadyStateSize(int sid, int tid) {
const size_t bitrate_bps = current_bitrate_allocation_.GetBitrate(
sid, tid == kNoTemporalIdx ? 0 : tid);
const float fps = (codec_.mode == VideoCodecMode::kScreensharing)
? framerate_controller_[sid].GetTargetRate()
: codec_.maxFramerate;
return static_cast<size_t>(
bitrate_bps / (8 * fps) *
(100 -
variable_framerate_experiment_.steady_state_undershoot_percentage) /
100 +
0.5);
}
// static
VP9EncoderImpl::VariableFramerateExperiment
VP9EncoderImpl::ParseVariableFramerateConfig(std::string group_name) {
FieldTrialFlag enabled = FieldTrialFlag("Enabled");
FieldTrialParameter<double> framerate_limit("min_fps", 5.0);
FieldTrialParameter<int> qp("min_qp", 32);
FieldTrialParameter<int> undershoot_percentage("undershoot", 30);
FieldTrialParameter<int> frames_before_steady_state(
"frames_before_steady_state", 5);
ParseFieldTrial({&enabled, &framerate_limit, &qp, &undershoot_percentage,
&frames_before_steady_state},
field_trial::FindFullName(group_name));
VariableFramerateExperiment config;
config.enabled = enabled.Get();
config.framerate_limit = framerate_limit.Get();
config.steady_state_qp = qp.Get();
config.steady_state_undershoot_percentage = undershoot_percentage.Get();
config.frames_before_steady_state = frames_before_steady_state.Get();
return config;
}
VP9DecoderImpl::VP9DecoderImpl()
: decode_complete_callback_(nullptr),
inited_(false),

View File

@ -16,6 +16,7 @@
#include <map>
#include <memory>
#include <string>
#include <vector>
#include "modules/video_coding/codecs/vp9/include/vp9.h"
@ -95,6 +96,8 @@ class VP9EncoderImpl : public VP9Encoder {
// percentage of the per frame bandwidth
uint32_t MaxIntraTarget(uint32_t optimal_buffer_size);
size_t SteadyStateSize(int sid, int tid);
EncodedImage encoded_image_;
CodecSpecificInfo codec_specific_;
EncodedImageCallback* encoded_complete_callback_;
@ -151,6 +154,25 @@ class VP9EncoderImpl : public VP9Encoder {
size_t temporal_layer_id = 0;
};
std::map<size_t, RefFrameBuffer> ref_buf_;
// Variable frame-rate related fields and methods.
const struct VariableFramerateExperiment {
bool enabled;
// Framerate is limited to this value in steady state.
float framerate_limit;
// This qp or below is considered a steady state.
int steady_state_qp;
// Frames of at least this percentage below ideal for configured bitrate are
// considered in a steady state.
int steady_state_undershoot_percentage;
// Number of consecutive frames with good QP and size required to detect
// the steady state.
int frames_before_steady_state;
} variable_framerate_experiment_;
static VariableFramerateExperiment ParseVariableFramerateConfig(
std::string group_name);
FramerateController variable_framerate_controller_;
int num_steady_state_frames_;
};
class VP9DecoderImpl : public VP9Decoder {

View File

@ -877,19 +877,40 @@ const ParamsWithLogging::Video kSimulcastVp8VideoLow = {
2, 400000, false, false, false, "ConferenceMotion_1280_720_50"};
#if defined(RTC_ENABLE_VP9)
TEST(FullStackTest, ScreenshareSlidesVP9_2SL) {
TEST(FullStackTest, ScreenshareSlidesVP9_3SL_High_Fps) {
auto fixture = CreateVideoQualityTestFixture();
ParamsWithLogging screenshare;
screenshare.call.send_side_bwe = true;
screenshare.video[0] = {true, 1850, 1110, 5, 50000,
200000, 2000000, false, "VP9", 1,
0, 400000, false, false, false, ""};
screenshare.video[0] = {true, 1850, 1110, 30, 50000, 200000,
2000000, false, "VP9", 1, 0, 400000,
false, false, false, ""};
screenshare.screenshare[0] = {true, false, 10};
screenshare.analyzer = {"screenshare_slides_vp9_2sl", 0.0, 0.0,
screenshare.analyzer = {"screenshare_slides_vp9_3sl_high_fps", 0.0, 0.0,
kFullStackTestDurationSecs};
screenshare.ss[0] = {
std::vector<VideoStream>(), 0, 2, 1, InterLayerPredMode::kOn,
std::vector<SpatialLayer>(), false};
std::vector<VideoStream>(), 0, 3, 2, InterLayerPredMode::kOn,
std::vector<SpatialLayer>(), true};
fixture->RunWithAnalyzer(screenshare);
}
TEST(FullStackTest, ScreenshareSlidesVP9_3SL_Variable_Fps) {
webrtc::test::ScopedFieldTrials override_trials(
AppendFieldTrials("WebRTC-VP9VariableFramerateScreenshare/"
"Enabled,min_qp:32,min_fps:5.0,undershoot:30,frames_"
"before_steady_state:5/"));
auto fixture = CreateVideoQualityTestFixture();
ParamsWithLogging screenshare;
screenshare.call.send_side_bwe = true;
screenshare.video[0] = {true, 1850, 1110, 30, 50000, 200000,
2000000, false, "VP9", 1, 0, 400000,
false, false, false, ""};
screenshare.screenshare[0] = {true, false, 10};
screenshare.analyzer = {"screenshare_slides_vp9_3sl_variable_fps", 0.0, 0.0,
kFullStackTestDurationSecs};
screenshare.ss[0] = {
std::vector<VideoStream>(), 0, 3, 2, InterLayerPredMode::kOn,
std::vector<SpatialLayer>(), true};
fixture->RunWithAnalyzer(screenshare);
}