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:

committed by
Commit Bot

parent
0cb858c7f2
commit
6117068af4
@ -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),
|
||||
|
@ -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 {
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user