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 55cb274209565687ccf146477f6ff8928a85c6fd..48240be1b6351ba611dcb7076e2a60e45ba3d13b 100644 |
--- a/webrtc/modules/video_coding/utility/quality_scaler.cc |
+++ b/webrtc/modules/video_coding/utility/quality_scaler.cc |
@@ -13,8 +13,11 @@ |
#include <math.h> |
#include <algorithm> |
+#include <memory> |
#include "webrtc/base/checks.h" |
+#include "webrtc/base/logging.h" |
+#include "webrtc/base/task_queue.h" |
// TODO(kthelgason): Some versions of Android have issues with log2. |
// See https://code.google.com/p/android/issues/detail?id=212634 for details |
@@ -27,20 +30,8 @@ namespace webrtc { |
namespace { |
// Threshold constant used until first downscale (to permit fast rampup). |
static const int kMeasureSecondsFastUpscale = 2; |
-static const int kMeasureSecondsUpscale = 5; |
-static const int kMeasureSecondsDownscale = 5; |
kthelgason
2016/10/06 14:34:13
Note that there is a behaviour change here. We no
|
+static const int kMeasureSeconds = 5; |
static const int kFramedropPercentThreshold = 60; |
-// Min width/height to downscale to, set to not go below QVGA, but with some |
-// margin to permit "almost-QVGA" resolutions, such as QCIF. |
-static const int kMinDownscaleDimension = 140; |
-// Initial resolutions corresponding to a bitrate. Aa bit above their actual |
-// values to permit near-VGA and near-QVGA resolutions to use the same |
-// mechanism. |
-static const int kVgaBitrateThresholdKbps = 500; |
-static const int kVgaNumPixels = 700 * 500; // 640x480 |
-static const int kQvgaBitrateThresholdKbps = 250; |
-static const int kQvgaNumPixels = 400 * 300; // 320x240 |
- |
// QP scaling threshold defaults: |
static const int kLowH264QpThreshold = 24; |
static const int kHighH264QpThreshold = 37; |
@@ -50,17 +41,47 @@ static const int kLowVp8QpThreshold = 29; |
static const int kHighVp8QpThreshold = 95; |
} // namespace |
+class QualityScaler::CheckQPTask : public rtc::QueuedTask { |
+ public: |
+ explicit CheckQPTask(QualityScaler* scaler) : scaler_(scaler) { |
+ RTC_DCHECK(scaler != nullptr); |
+ LOG(LS_) << "Created CheckQPTask. Scheduling on queue..."; |
+ const auto q = rtc::TaskQueue::Current(); |
+ q->PostDelayedTask(std::unique_ptr<rtc::QueuedTask>(this), |
+ scaler_->GetTimeoutMs()); |
+ } |
+ private: |
+ friend class QualityScaler; |
+ void Stop() { stop_ = true; } |
+ bool Run() override { |
+ if (stop_) |
+ return true; // TaskQueue will free this task. |
+ scaler_->CheckQP(); |
+ rtc::TaskQueue::Current()->PostDelayedTask( |
+ std::unique_ptr<rtc::QueuedTask>(this), scaler_->GetTimeoutMs()); |
+ return false; // Retain the task in order to reuse it. |
+ } |
+ |
+ QualityScaler* const scaler_; |
+ bool stop_; |
+}; |
+ |
// 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(VideoCodecType codec_type, |
- int initial_bitrate_kbps, |
- int width, |
- int height, |
+ : average_qp_(kMeasureSeconds * 30), |
+ framedrop_percent_(kMeasureSeconds * 30), |
+ low_qp_threshold_(-1) { |
+ task_checker_.Detach(); |
+} |
+ |
+QualityScaler::~QualityScaler() { |
+ RTC_DCHECK(check_qp_task_ == nullptr) << "Must call Stop."; |
+} |
+ |
+void QualityScaler::Init(ScalingInterface* observer, |
+ VideoCodecType codec_type, |
int fps) { |
+ RTC_DCHECK_CALLED_SEQUENTIALLY(&task_checker_); |
int low = -1, high = -1; |
switch (codec_type) { |
case kVideoCodecH264: |
@@ -74,141 +95,104 @@ void QualityScaler::Init(VideoCodecType codec_type, |
default: |
RTC_NOTREACHED() << "Invalid codec type for QualityScaler."; |
} |
- Init(low, high, initial_bitrate_kbps, width, height, fps); |
+ Init(observer, low, high, fps); |
} |
-void QualityScaler::Init(int low_qp_threshold, |
+void QualityScaler::Init(ScalingInterface* observer, |
+ int low_qp_threshold, |
int high_qp_threshold, |
- int initial_bitrate_kbps, |
- int width, |
- int height, |
int fps) { |
+ RTC_DCHECK_CALLED_SEQUENTIALLY(&task_checker_); |
+ RTC_DCHECK(observer != nullptr); |
+ observer_ = observer; |
ClearSamples(); |
low_qp_threshold_ = low_qp_threshold; |
high_qp_threshold_ = high_qp_threshold; |
- downscale_shift_ = 0; |
fast_rampup_ = true; |
- ReportFramerate(fps); |
- const int init_width = width; |
- const int init_height = height; |
- if (initial_bitrate_kbps > 0) { |
- int init_num_pixels = width * height; |
- if (initial_bitrate_kbps < kVgaBitrateThresholdKbps) |
- init_num_pixels = kVgaNumPixels; |
- if (initial_bitrate_kbps < kQvgaBitrateThresholdKbps) |
- init_num_pixels = kQvgaNumPixels; |
- while (width * height > init_num_pixels) { |
- ++downscale_shift_; |
- width /= 2; |
- height /= 2; |
- } |
- } |
- UpdateTargetResolution(init_width, init_height); |
+ // Use a faster window for upscaling initially. |
+ // This enables faster initial rampups without risking strong up-down |
+ // behavior later. |
+ measure_interval_ = kMeasureSecondsFastUpscale; |
ReportFramerate(fps); |
+ check_qp_task_ = new CheckQPTask(this); |
+} |
+ |
+void QualityScaler::Stop() { |
+ RTC_DCHECK_CALLED_SEQUENTIALLY(&task_checker_); |
+ check_qp_task_->Stop(); |
+ check_qp_task_ = nullptr; |
+} |
+ |
+int64_t QualityScaler::GetTimeoutMs() { |
+ RTC_DCHECK_CALLED_SEQUENTIALLY(&task_checker_); |
+ return measure_interval_ * 1000; |
} |
// Report framerate(fps) to estimate # of samples. |
void QualityScaler::ReportFramerate(int framerate) { |
- // 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_)); |
+ RTC_DCHECK_CALLED_SEQUENTIALLY(&task_checker_); |
+ average_qp_ = MovingAverage(framerate * measure_interval_); |
+ framedrop_percent_ = MovingAverage(framerate * measure_interval_); |
+} |
+ |
+void QualityScaler::ReportDroppedFrame() { |
+ RTC_DCHECK_CALLED_SEQUENTIALLY(&task_checker_); |
+ framedrop_percent_.AddSample(100); |
} |
void QualityScaler::ReportQP(int qp) { |
+ RTC_DCHECK_CALLED_SEQUENTIALLY(&task_checker_); |
framedrop_percent_.AddSample(0); |
average_qp_.AddSample(qp); |
} |
-void QualityScaler::ReportDroppedFrame() { |
- framedrop_percent_.AddSample(100); |
-} |
- |
-void QualityScaler::OnEncodeFrame(int width, int height) { |
+void QualityScaler::CheckQP() { |
+ RTC_DCHECK_CALLED_SEQUENTIALLY(&task_checker_); |
// Should be set through InitEncode -> Should be set by now. |
RTC_DCHECK_GE(low_qp_threshold_, 0); |
- if (target_res_.width != width || target_res_.height != height) { |
- UpdateTargetResolution(width, height); |
- } |
- |
+ LOG(LS_INFO) << "Checking if average QP exceeds threshold"; |
// Check if we should scale down due to high frame drop. |
- const auto drop_rate = framedrop_percent_.GetAverage(num_samples_downscale_); |
+ const auto drop_rate = framedrop_percent_.GetAverage(); |
if (drop_rate && *drop_rate >= kFramedropPercentThreshold) { |
- ScaleDown(); |
+ ReportQPHigh(); |
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(); |
+ const auto avg_qp= average_qp_.GetAverage(); |
+ if (avg_qp && *avg_qp> high_qp_threshold_) { |
+ ReportQPHigh(); |
return; |
} |
- const auto avg_qp_up = average_qp_.GetAverage(num_samples_upscale_); |
- if (avg_qp_up && *avg_qp_up <= low_qp_threshold_) { |
+ if (avg_qp && *avg_qp <= low_qp_threshold_) { |
// QP has been low. We want to try a higher resolution. |
- ScaleUp(); |
+ ReportQPLow(); |
return; |
} |
} |
-void QualityScaler::ScaleUp() { |
- downscale_shift_ = std::max(0, downscale_shift_ - 1); |
+void QualityScaler::ReportQPLow() { |
+ RTC_DCHECK_CALLED_SEQUENTIALLY(&task_checker_); |
+ LOG(LS_INFO) << "QP has been low, asking for higher resolution."; |
ClearSamples(); |
+ observer_->ScaleUp(); |
} |
-void QualityScaler::ScaleDown() { |
- downscale_shift_ = std::min(maximum_shift_, downscale_shift_ + 1); |
+void QualityScaler::ReportQPHigh() { |
+ RTC_DCHECK_CALLED_SEQUENTIALLY(&task_checker_); |
+ LOG(LS_INFO) << "QP has been high , asking for lower resolution."; |
ClearSamples(); |
+ observer_->ScaleDown(); |
// 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; |
- } |
-} |
- |
-QualityScaler::Resolution QualityScaler::GetScaledResolution() const { |
- 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(); |
- const int src_width = frame->width(); |
- const int src_height = frame->height(); |
- |
- if (res.width == src_width && res.height == src_height) |
- return frame; |
- rtc::scoped_refptr<I420Buffer> scaled_buffer = |
- pool_.CreateBuffer(res.width, res.height); |
- |
- scaled_buffer->ScaleFrom(frame); |
- |
- return scaled_buffer; |
-} |
- |
-void QualityScaler::UpdateTargetResolution(int width, int height) { |
- if (width < kMinDownscaleDimension || height < kMinDownscaleDimension) { |
- maximum_shift_ = 0; |
- } else { |
- maximum_shift_ = static_cast<int>( |
- log2(std::min(width, height) / kMinDownscaleDimension)); |
+ measure_interval_ = kMeasureSeconds; |
} |
- target_res_ = Resolution{width, height}; |
} |
void QualityScaler::ClearSamples() { |
+ RTC_DCHECK_CALLED_SEQUENTIALLY(&task_checker_); |
framedrop_percent_.Reset(); |
average_qp_.Reset(); |
} |