| Index: webrtc/modules/video_coding/utility/quality_scaler.cc
|
| diff --git a/webrtc/modules/video_coding/utility/quality_scaler.cc b/webrtc/modules/video_coding/utility/quality_scaler.cc
|
| index c509e843642f2237e94fe1727749479a9523faa7..99bc6dad223bdaa58fcfcb56da6d451cf0ab0ad4 100644
|
| --- a/webrtc/modules/video_coding/utility/quality_scaler.cc
|
| +++ b/webrtc/modules/video_coding/utility/quality_scaler.cc
|
| @@ -10,10 +10,12 @@
|
|
|
| #include "webrtc/modules/video_coding/utility/quality_scaler.h"
|
|
|
| +#include <algorithm>
|
| +#include <cmath>
|
| +
|
| namespace webrtc {
|
|
|
| namespace {
|
| -static const int kMinFps = 5;
|
| // Threshold constant used until first downscale (to permit fast rampup).
|
| static const int kMeasureSecondsFastUpscale = 2;
|
| static const int kMeasureSecondsUpscale = 5;
|
| @@ -46,7 +48,11 @@ const int QualityScaler::kLowH264QpThreshold = 24;
|
| const int QualityScaler::kBadH264QpThreshold = 37;
|
| #endif
|
|
|
| -QualityScaler::QualityScaler() : low_qp_threshold_(-1) {}
|
| +// Default values. Should immediately get set to something more sensible.
|
| +QualityScaler::QualityScaler()
|
| + : average_qp_(kMeasureSecondsUpscale * 30),
|
| + framedrop_percent_(kMeasureSecondsUpscale * 30),
|
| + low_qp_threshold_(-1) {}
|
|
|
| void QualityScaler::Init(int low_qp_threshold,
|
| int high_qp_threshold,
|
| @@ -54,14 +60,15 @@ void QualityScaler::Init(int low_qp_threshold,
|
| int width,
|
| int height,
|
| int fps) {
|
| - ClearSamples();
|
| low_qp_threshold_ = low_qp_threshold;
|
| high_qp_threshold_ = high_qp_threshold;
|
| downscale_shift_ = 0;
|
| - // Use a faster window for upscaling initially (but be more graceful later).
|
| - // This enables faster initial rampups without risking strong up-down
|
| - // behavior later.
|
| - measure_seconds_upscale_ = kMeasureSecondsFastUpscale;
|
| +
|
| + fast_rampup_ = true;
|
| +
|
| + ClearSamples();
|
| + ReportFramerate(fps);
|
| +
|
| const int init_width = width;
|
| const int init_height = height;
|
| if (initial_bitrate_kbps > 0) {
|
| @@ -76,24 +83,28 @@ void QualityScaler::Init(int low_qp_threshold,
|
| height /= 2;
|
| }
|
| }
|
| -
|
| - // Zero out width/height so they can be checked against inside
|
| - // UpdateTargetResolution.
|
| - res_.width = res_.height = 0;
|
| UpdateTargetResolution(init_width, init_height);
|
| ReportFramerate(fps);
|
| }
|
|
|
| // Report framerate(fps) to estimate # of samples.
|
| void QualityScaler::ReportFramerate(int framerate) {
|
| - framerate_ = framerate;
|
| - UpdateSampleCounts();
|
| + // Use a faster window for upscaling initially.
|
| + // This enables faster initial rampups without risking strong up-down
|
| + // behavior later.
|
| + num_samples_upscale_ = framerate * (fast_rampup_ ? kMeasureSecondsFastUpscale
|
| + : kMeasureSecondsUpscale);
|
| + num_samples_downscale_ = framerate * kMeasureSecondsDownscale;
|
| +
|
| + average_qp_ =
|
| + MovingAverage(std::max(num_samples_upscale_, num_samples_downscale_));
|
| + framedrop_percent_ =
|
| + MovingAverage(std::max(num_samples_upscale_, num_samples_downscale_));
|
| }
|
|
|
| void QualityScaler::ReportQP(int qp) {
|
| framedrop_percent_.AddSample(0);
|
| - average_qp_downscale_.AddSample(qp);
|
| - average_qp_upscale_.AddSample(qp);
|
| + average_qp_.AddSample(qp);
|
| }
|
|
|
| void QualityScaler::ReportDroppedFrame() {
|
| @@ -103,34 +114,58 @@ void QualityScaler::ReportDroppedFrame() {
|
| void QualityScaler::OnEncodeFrame(int width, int height) {
|
| // Should be set through InitEncode -> Should be set by now.
|
| RTC_DCHECK_GE(low_qp_threshold_, 0);
|
| - RTC_DCHECK_GT(num_samples_upscale_, 0u);
|
| - RTC_DCHECK_GT(num_samples_downscale_, 0u);
|
| -
|
| - // Update scale factor.
|
| - int avg_drop = 0;
|
| - int avg_qp = 0;
|
| -
|
| - if ((framedrop_percent_.GetAverage(num_samples_downscale_, &avg_drop) &&
|
| - avg_drop >= kFramedropPercentThreshold) ||
|
| - (average_qp_downscale_.GetAverage(num_samples_downscale_, &avg_qp) &&
|
| - avg_qp > high_qp_threshold_)) {
|
| - AdjustScale(false);
|
| - } else if (average_qp_upscale_.GetAverage(num_samples_upscale_, &avg_qp) &&
|
| - avg_qp <= low_qp_threshold_) {
|
| - AdjustScale(true);
|
| + if (target_res_.width != width || target_res_.height != height) {
|
| + UpdateTargetResolution(width, height);
|
| + }
|
| +
|
| + // Check if we should scale down due to high frame drop.
|
| + const auto drop_rate = framedrop_percent_.GetAverage(num_samples_downscale_);
|
| + if (drop_rate && *drop_rate >= kFramedropPercentThreshold) {
|
| + ScaleDown();
|
| + return;
|
| + }
|
| +
|
| + // Check if we should scale up or down based on QP.
|
| + const auto avg_qp_down = average_qp_.GetAverage(num_samples_downscale_);
|
| + if (avg_qp_down && *avg_qp_down > high_qp_threshold_) {
|
| + ScaleDown();
|
| + return;
|
| + }
|
| + const auto avg_qp_up = average_qp_.GetAverage(num_samples_upscale_);
|
| + if (avg_qp_up && *avg_qp_up <= low_qp_threshold_) {
|
| + // QP has been low. We want to try a higher resolution.
|
| + ScaleUp();
|
| + return;
|
| + }
|
| +}
|
| +
|
| +void QualityScaler::ScaleUp() {
|
| + downscale_shift_ = std::max(0, downscale_shift_ - 1);
|
| + ClearSamples();
|
| +}
|
| +
|
| +void QualityScaler::ScaleDown() {
|
| + downscale_shift_ = std::min(maximum_shift_, downscale_shift_ + 1);
|
| + ClearSamples();
|
| + // If we've scaled down, wait longer before scaling up again.
|
| + if (fast_rampup_) {
|
| + fast_rampup_ = false;
|
| + num_samples_upscale_ = (num_samples_upscale_ / kMeasureSecondsFastUpscale) *
|
| + kMeasureSecondsUpscale;
|
| }
|
| - UpdateTargetResolution(width, height);
|
| }
|
|
|
| QualityScaler::Resolution QualityScaler::GetScaledResolution() const {
|
| - return res_;
|
| + const int frame_width = target_res_.width >> downscale_shift_;
|
| + const int frame_height = target_res_.height >> downscale_shift_;
|
| + return Resolution{frame_width, frame_height};
|
| }
|
|
|
| rtc::scoped_refptr<VideoFrameBuffer> QualityScaler::GetScaledBuffer(
|
| const rtc::scoped_refptr<VideoFrameBuffer>& frame) {
|
| Resolution res = GetScaledResolution();
|
| - int src_width = frame->width();
|
| - int src_height = frame->height();
|
| + const int src_width = frame->width();
|
| + const int src_height = frame->height();
|
|
|
| if (res.width == src_width && res.height == src_height)
|
| return frame;
|
| @@ -142,50 +177,20 @@ rtc::scoped_refptr<VideoFrameBuffer> QualityScaler::GetScaledBuffer(
|
| return scaled_buffer;
|
| }
|
|
|
| -void QualityScaler::UpdateTargetResolution(int frame_width, int frame_height) {
|
| - RTC_DCHECK_GE(downscale_shift_, 0);
|
| - int shifts_performed = 0;
|
| - for (int shift = downscale_shift_;
|
| - shift > 0 && (frame_width / 2 >= kMinDownscaleDimension) &&
|
| - (frame_height / 2 >= kMinDownscaleDimension);
|
| - --shift, ++shifts_performed) {
|
| - frame_width /= 2;
|
| - frame_height /= 2;
|
| - }
|
| - // Clamp to number of shifts actually performed to not be stuck trying to
|
| - // scale way beyond QVGA.
|
| - downscale_shift_ = shifts_performed;
|
| - if (res_.width == frame_width && res_.height == frame_height) {
|
| - // No reset done/needed, using same resolution.
|
| - return;
|
| +void QualityScaler::UpdateTargetResolution(int width, int height) {
|
| + if (width < kMinDownscaleDimension || height < kMinDownscaleDimension) {
|
| + maximum_shift_ = 0;
|
| + } else {
|
| + maximum_shift_ = static_cast<int>(
|
| + std::log2(std::min(width, height) / kMinDownscaleDimension));
|
| }
|
| - res_.width = frame_width;
|
| - res_.height = frame_height;
|
| - ClearSamples();
|
| + target_res_ = Resolution{width, height};
|
| }
|
|
|
| void QualityScaler::ClearSamples() {
|
| framedrop_percent_.Reset();
|
| - average_qp_downscale_.Reset();
|
| - average_qp_upscale_.Reset();
|
| + average_qp_.Reset();
|
| }
|
|
|
| -void QualityScaler::UpdateSampleCounts() {
|
| - num_samples_downscale_ = static_cast<size_t>(
|
| - kMeasureSecondsDownscale * (framerate_ < kMinFps ? kMinFps : framerate_));
|
| - num_samples_upscale_ = static_cast<size_t>(
|
| - measure_seconds_upscale_ * (framerate_ < kMinFps ? kMinFps : framerate_));
|
| -}
|
| -
|
| -void QualityScaler::AdjustScale(bool up) {
|
| - downscale_shift_ += up ? -1 : 1;
|
| - if (downscale_shift_ < 0)
|
| - downscale_shift_ = 0;
|
| - if (!up) {
|
| - // First downscale hit, start using a slower threshold for going up.
|
| - measure_seconds_upscale_ = kMeasureSecondsUpscale;
|
| - UpdateSampleCounts();
|
| - }
|
| -}
|
|
|
| } // namespace webrtc
|
|
|