Add frame rate throttling to vp8 screenshare_layers.
Drop frames if incoming frame rate is higher than the configured max framerate. BUG=webrtc:6897 Review-Url: https://codereview.webrtc.org/2578993002 Cr-Commit-Position: refs/heads/master@{#15819}
This commit is contained in:
@ -76,7 +76,7 @@ ScreenshareLayers::ScreenshareLayers(int num_temporal_layers,
|
|||||||
min_qp_(-1),
|
min_qp_(-1),
|
||||||
max_qp_(-1),
|
max_qp_(-1),
|
||||||
max_debt_bytes_(0),
|
max_debt_bytes_(0),
|
||||||
framerate_(-1),
|
encode_framerate_(1000.0f, 1000.0f), // 1 second window, second scale.
|
||||||
bitrate_updated_(false) {
|
bitrate_updated_(false) {
|
||||||
RTC_CHECK_GT(num_temporal_layers, 0);
|
RTC_CHECK_GT(num_temporal_layers, 0);
|
||||||
RTC_CHECK_LE(num_temporal_layers, 2);
|
RTC_CHECK_LE(num_temporal_layers, 2);
|
||||||
@ -97,8 +97,15 @@ int ScreenshareLayers::EncodeFlags(uint32_t timestamp) {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const int64_t now_ms = clock_->TimeInMilliseconds();
|
||||||
|
if (target_framerate_.value_or(0) > 0 &&
|
||||||
|
encode_framerate_.Rate(now_ms).value_or(0) > *target_framerate_) {
|
||||||
|
// Max framerate exceeded, drop frame.
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
if (stats_.first_frame_time_ms_ == -1)
|
if (stats_.first_frame_time_ms_ == -1)
|
||||||
stats_.first_frame_time_ms_ = clock_->TimeInMilliseconds();
|
stats_.first_frame_time_ms_ = now_ms;
|
||||||
|
|
||||||
int64_t unwrapped_timestamp = time_wrap_handler_.Unwrap(timestamp);
|
int64_t unwrapped_timestamp = time_wrap_handler_.Unwrap(timestamp);
|
||||||
int flags = 0;
|
int flags = 0;
|
||||||
@ -148,7 +155,8 @@ int ScreenshareLayers::EncodeFlags(uint32_t timestamp) {
|
|||||||
|
|
||||||
int64_t ts_diff;
|
int64_t ts_diff;
|
||||||
if (last_timestamp_ == -1) {
|
if (last_timestamp_ == -1) {
|
||||||
ts_diff = kOneSecond90Khz / (framerate_ <= 0 ? 5 : framerate_);
|
ts_diff =
|
||||||
|
kOneSecond90Khz / incoming_framerate_.value_or(*target_framerate_);
|
||||||
} else {
|
} else {
|
||||||
ts_diff = unwrapped_timestamp - last_timestamp_;
|
ts_diff = unwrapped_timestamp - last_timestamp_;
|
||||||
}
|
}
|
||||||
@ -162,13 +170,28 @@ int ScreenshareLayers::EncodeFlags(uint32_t timestamp) {
|
|||||||
std::vector<uint32_t> ScreenshareLayers::OnRatesUpdated(int bitrate_kbps,
|
std::vector<uint32_t> ScreenshareLayers::OnRatesUpdated(int bitrate_kbps,
|
||||||
int max_bitrate_kbps,
|
int max_bitrate_kbps,
|
||||||
int framerate) {
|
int framerate) {
|
||||||
|
RTC_DCHECK_GT(framerate, 0);
|
||||||
|
if (!target_framerate_) {
|
||||||
|
// First OnRatesUpdated() is called during construction, with the configured
|
||||||
|
// targets as parameters.
|
||||||
|
target_framerate_.emplace(framerate);
|
||||||
|
incoming_framerate_ = target_framerate_;
|
||||||
|
bitrate_updated_ = true;
|
||||||
|
} else {
|
||||||
bitrate_updated_ =
|
bitrate_updated_ =
|
||||||
bitrate_kbps != static_cast<int>(layers_[0].target_rate_kbps_) ||
|
bitrate_kbps != static_cast<int>(layers_[0].target_rate_kbps_) ||
|
||||||
max_bitrate_kbps != static_cast<int>(layers_[1].target_rate_kbps_) ||
|
max_bitrate_kbps != static_cast<int>(layers_[1].target_rate_kbps_) ||
|
||||||
framerate != framerate_;
|
(incoming_framerate_ &&
|
||||||
|
framerate != static_cast<int>(*incoming_framerate_));
|
||||||
|
if (framerate < 0) {
|
||||||
|
incoming_framerate_.reset();
|
||||||
|
} else {
|
||||||
|
incoming_framerate_.emplace(framerate);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
layers_[0].target_rate_kbps_ = bitrate_kbps;
|
layers_[0].target_rate_kbps_ = bitrate_kbps;
|
||||||
layers_[1].target_rate_kbps_ = max_bitrate_kbps;
|
layers_[1].target_rate_kbps_ = max_bitrate_kbps;
|
||||||
framerate_ = framerate;
|
|
||||||
|
|
||||||
std::vector<uint32_t> allocation;
|
std::vector<uint32_t> allocation;
|
||||||
allocation.push_back(bitrate_kbps);
|
allocation.push_back(bitrate_kbps);
|
||||||
@ -180,6 +203,9 @@ std::vector<uint32_t> ScreenshareLayers::OnRatesUpdated(int bitrate_kbps,
|
|||||||
void ScreenshareLayers::FrameEncoded(unsigned int size,
|
void ScreenshareLayers::FrameEncoded(unsigned int size,
|
||||||
uint32_t timestamp,
|
uint32_t timestamp,
|
||||||
int qp) {
|
int qp) {
|
||||||
|
if (size > 0)
|
||||||
|
encode_framerate_.Update(1, clock_->TimeInMilliseconds());
|
||||||
|
|
||||||
if (number_of_temporal_layers_ == 1)
|
if (number_of_temporal_layers_ == 1)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
@ -301,8 +327,9 @@ bool ScreenshareLayers::UpdateConfiguration(vpx_codec_enc_cfg_t* cfg) {
|
|||||||
layers_[1].enhanced_max_qp = min_qp_ + (((max_qp_ - min_qp_) * 85) / 100);
|
layers_[1].enhanced_max_qp = min_qp_ + (((max_qp_ - min_qp_) * 85) / 100);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (framerate_ > 0) {
|
if (incoming_framerate_) {
|
||||||
int avg_frame_size = (target_bitrate_kbps * 1000) / (8 * framerate_);
|
int avg_frame_size =
|
||||||
|
(target_bitrate_kbps * 1000) / (8 * *incoming_framerate_);
|
||||||
max_debt_bytes_ = 4 * avg_frame_size;
|
max_debt_bytes_ = 4 * avg_frame_size;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -11,6 +11,7 @@
|
|||||||
|
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
|
#include "webrtc/base/rate_statistics.h"
|
||||||
#include "webrtc/base/timeutils.h"
|
#include "webrtc/base/timeutils.h"
|
||||||
#include "webrtc/modules/video_coding/codecs/vp8/temporal_layers.h"
|
#include "webrtc/modules/video_coding/codecs/vp8/temporal_layers.h"
|
||||||
#include "webrtc/modules/video_coding/utility/frame_dropper.h"
|
#include "webrtc/modules/video_coding/utility/frame_dropper.h"
|
||||||
@ -74,7 +75,13 @@ class ScreenshareLayers : public TemporalLayers {
|
|||||||
int min_qp_;
|
int min_qp_;
|
||||||
int max_qp_;
|
int max_qp_;
|
||||||
uint32_t max_debt_bytes_;
|
uint32_t max_debt_bytes_;
|
||||||
int framerate_;
|
|
||||||
|
// Configured max framerate.
|
||||||
|
rtc::Optional<uint32_t> target_framerate_;
|
||||||
|
// Incoming framerate from capturer.
|
||||||
|
rtc::Optional<uint32_t> incoming_framerate_;
|
||||||
|
// Tracks what framerate we actually encode, and drops frames on overshoot.
|
||||||
|
RateStatistics encode_framerate_;
|
||||||
bool bitrate_updated_;
|
bool bitrate_updated_;
|
||||||
|
|
||||||
static const int kMaxNumTemporalLayers = 2;
|
static const int kMaxNumTemporalLayers = 2;
|
||||||
|
|||||||
@ -577,4 +577,46 @@ TEST_F(ScreenshareLayerTest, AllowsUpdateConfigBeforeSetRates) {
|
|||||||
EXPECT_FALSE(layers_->UpdateConfiguration(&cfg));
|
EXPECT_FALSE(layers_->UpdateConfiguration(&cfg));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_F(ScreenshareLayerTest, RespectsConfiguredFramerate) {
|
||||||
|
ConfigureBitrates();
|
||||||
|
|
||||||
|
int64_t kTestSpanMs = 2000;
|
||||||
|
int64_t kFrameIntervalsMs = 1000 / kFrameRate;
|
||||||
|
|
||||||
|
uint32_t timestamp = 1234;
|
||||||
|
int num_input_frames = 0;
|
||||||
|
int num_discarded_frames = 0;
|
||||||
|
|
||||||
|
// Send at regular rate - no drops expected.
|
||||||
|
for (int64_t i = 0; i < kTestSpanMs; i += kFrameIntervalsMs) {
|
||||||
|
if (layers_->EncodeFlags(timestamp) == -1) {
|
||||||
|
++num_discarded_frames;
|
||||||
|
} else {
|
||||||
|
size_t frame_size_bytes = kDefaultTl0BitrateKbps * kFrameIntervalsMs / 8;
|
||||||
|
layers_->FrameEncoded(frame_size_bytes, timestamp, kDefaultQp);
|
||||||
|
}
|
||||||
|
timestamp += kFrameIntervalsMs * 90;
|
||||||
|
clock_.AdvanceTimeMilliseconds(kFrameIntervalsMs);
|
||||||
|
++num_input_frames;
|
||||||
|
}
|
||||||
|
EXPECT_EQ(0, num_discarded_frames);
|
||||||
|
|
||||||
|
// Send at twice the configured rate - drop every other frame.
|
||||||
|
num_input_frames = 0;
|
||||||
|
num_discarded_frames = 0;
|
||||||
|
for (int64_t i = 0; i < kTestSpanMs; i += kFrameIntervalsMs / 2) {
|
||||||
|
if (layers_->EncodeFlags(timestamp) == -1) {
|
||||||
|
++num_discarded_frames;
|
||||||
|
} else {
|
||||||
|
size_t frame_size_bytes = kDefaultTl0BitrateKbps * kFrameIntervalsMs / 8;
|
||||||
|
layers_->FrameEncoded(frame_size_bytes, timestamp, kDefaultQp);
|
||||||
|
}
|
||||||
|
timestamp += kFrameIntervalsMs * 90 / 2;
|
||||||
|
clock_.AdvanceTimeMilliseconds(kFrameIntervalsMs / 2);
|
||||||
|
++num_input_frames;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Allow for some rounding errors in the measurements.
|
||||||
|
EXPECT_NEAR(num_discarded_frames, num_input_frames / 2, 2);
|
||||||
|
}
|
||||||
} // namespace webrtc
|
} // namespace webrtc
|
||||||
|
|||||||
Reference in New Issue
Block a user