blob: 273d7a61ec95528d712420c8b7b1f84fa69e73e8 [file] [log] [blame]
pbos@webrtc.orga0d78272014-09-12 11:51:47 +00001/*
2 * Copyright (c) 2014 The WebRTC project authors. All Rights Reserved.
3 *
4 * Use of this source code is governed by a BSD-style license
5 * that can be found in the LICENSE file in the root of the source
6 * tree. An additional intellectual property rights grant can be found
7 * in the file PATENTS. All contributing project authors may
8 * be found in the AUTHORS file in the root of the source tree.
9 */
Niels Möller718a7632016-06-13 13:06:01 +020010
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020011#include "modules/video_coding/utility/quality_scaler.h"
pbos@webrtc.orga0d78272014-09-12 11:51:47 +000012
Kári Tristan Helgason5a20ed32016-09-15 10:56:19 +020013#include <math.h>
14
kthelgason194f40a2016-09-14 02:14:58 -070015#include <algorithm>
kthelgason876222f2016-11-29 01:44:11 -080016#include <memory>
Kári Tristan Helgason5a20ed32016-09-15 10:56:19 +020017
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020018#include "rtc_base/checks.h"
19#include "rtc_base/logging.h"
Åsa Perssona945aee2018-04-24 16:53:25 +020020#include "rtc_base/numerics/exp_filter.h"
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020021#include "rtc_base/task_queue.h"
Åsa Perssona945aee2018-04-24 16:53:25 +020022#include "rtc_base/timeutils.h"
kthelgason478681e2016-09-28 08:17:43 -070023
Kári Tristan Helgason5a20ed32016-09-15 10:56:19 +020024// TODO(kthelgason): Some versions of Android have issues with log2.
25// See https://code.google.com/p/android/issues/detail?id=212634 for details
26#if defined(WEBRTC_ANDROID)
27#define log2(x) (log(x) / log(2))
28#endif
kthelgason194f40a2016-09-14 02:14:58 -070029
pbos@webrtc.orga0d78272014-09-12 11:51:47 +000030namespace webrtc {
31
Peter Boström926dfcd2016-04-14 14:48:10 +020032namespace {
Niels Möller225c7872018-02-22 15:03:53 +010033// TODO(nisse): Delete, delegate to encoders.
pboscbac40d2016-04-13 02:51:02 -070034// Threshold constant used until first downscale (to permit fast rampup).
kthelgason876222f2016-11-29 01:44:11 -080035static const int kMeasureMs = 2000;
36static const float kSamplePeriodScaleFactor = 2.5;
pbos@webrtc.orga0d78272014-09-12 11:51:47 +000037static const int kFramedropPercentThreshold = 60;
kthelgason55a01352017-04-04 02:31:42 -070038static const int kMinFramesNeededToScale = 2 * 30;
pbos@webrtc.orga0d78272014-09-12 11:51:47 +000039
kthelgason876222f2016-11-29 01:44:11 -080040} // namespace
kthelgason478681e2016-09-28 08:17:43 -070041
Åsa Perssona945aee2018-04-24 16:53:25 +020042class QualityScaler::QpSmoother {
43 public:
44 explicit QpSmoother(float alpha)
45 : alpha_(alpha), last_sample_ms_(rtc::TimeMillis()), smoother_(alpha) {}
46
47 rtc::Optional<int> GetAvg() const {
48 float value = smoother_.filtered();
49 if (value == rtc::ExpFilter::kValueUndefined) {
50 return rtc::nullopt;
51 }
52 return static_cast<int>(value);
53 }
54
55 void Add(float sample) {
56 int64_t now_ms = rtc::TimeMillis();
57 smoother_.Apply(static_cast<float>(now_ms - last_sample_ms_), sample);
58 last_sample_ms_ = now_ms;
59 }
60
61 void Reset() { smoother_.Reset(alpha_); }
62
63 private:
64 const float alpha_;
65 int64_t last_sample_ms_;
66 rtc::ExpFilter smoother_;
67};
68
Åsa Persson04d5f1d2018-04-20 15:19:11 +020069class QualityScaler::CheckQpTask : public rtc::QueuedTask {
kthelgason876222f2016-11-29 01:44:11 -080070 public:
Åsa Persson04d5f1d2018-04-20 15:19:11 +020071 explicit CheckQpTask(QualityScaler* scaler) : scaler_(scaler) {
72 RTC_LOG(LS_INFO) << "Created CheckQpTask. Scheduling on queue...";
kthelgason876222f2016-11-29 01:44:11 -080073 rtc::TaskQueue::Current()->PostDelayedTask(
74 std::unique_ptr<rtc::QueuedTask>(this), scaler_->GetSamplingPeriodMs());
Alex Glazneva9d08922016-02-19 15:24:06 -080075 }
kthelgason876222f2016-11-29 01:44:11 -080076 void Stop() {
77 RTC_DCHECK_CALLED_SEQUENTIALLY(&task_checker_);
Mirko Bonadei675513b2017-11-09 11:09:25 +010078 RTC_LOG(LS_INFO) << "Stopping QP Check task.";
kthelgason876222f2016-11-29 01:44:11 -080079 stop_ = true;
80 }
81
82 private:
83 bool Run() override {
84 RTC_DCHECK_CALLED_SEQUENTIALLY(&task_checker_);
85 if (stop_)
86 return true; // TaskQueue will free this task.
Åsa Persson04d5f1d2018-04-20 15:19:11 +020087 scaler_->CheckQp();
kthelgason876222f2016-11-29 01:44:11 -080088 rtc::TaskQueue::Current()->PostDelayedTask(
89 std::unique_ptr<rtc::QueuedTask>(this), scaler_->GetSamplingPeriodMs());
90 return false; // Retain the task in order to reuse it.
91 }
92
93 QualityScaler* const scaler_;
94 bool stop_ = false;
95 rtc::SequencedTaskChecker task_checker_;
96};
97
sprangb1ca0732017-02-01 08:38:12 -080098QualityScaler::QualityScaler(AdaptationObserverInterface* observer,
kthelgason876222f2016-11-29 01:44:11 -080099 VideoEncoder::QpThresholds thresholds)
100 : QualityScaler(observer, thresholds, kMeasureMs) {}
101
102// Protected ctor, should not be called directly.
sprangb1ca0732017-02-01 08:38:12 -0800103QualityScaler::QualityScaler(AdaptationObserverInterface* observer,
kthelgason876222f2016-11-29 01:44:11 -0800104 VideoEncoder::QpThresholds thresholds,
Åsa Persson0ad2d8a2018-04-19 11:06:11 +0200105 int64_t sampling_period_ms)
kthelgason876222f2016-11-29 01:44:11 -0800106 : check_qp_task_(nullptr),
107 observer_(observer),
Åsa Persson0ad2d8a2018-04-19 11:06:11 +0200108 thresholds_(thresholds),
109 sampling_period_ms_(sampling_period_ms),
kthelgason876222f2016-11-29 01:44:11 -0800110 fast_rampup_(true),
111 // Arbitrarily choose size based on 30 fps for 5 seconds.
112 average_qp_(5 * 30),
Åsa Perssona945aee2018-04-24 16:53:25 +0200113 framedrop_percent_media_opt_(5 * 30),
114 framedrop_percent_all_(5 * 30),
115 experiment_enabled_(QualityScalingExperiment::Enabled()),
116 observed_enough_frames_(false) {
kthelgason876222f2016-11-29 01:44:11 -0800117 RTC_DCHECK_CALLED_SEQUENTIALLY(&task_checker_);
Åsa Perssona945aee2018-04-24 16:53:25 +0200118 if (experiment_enabled_) {
119 config_ = QualityScalingExperiment::GetConfig();
120 qp_smoother_high_.reset(new QpSmoother(config_.alpha_high));
121 qp_smoother_low_.reset(new QpSmoother(config_.alpha_low));
122 }
kthelgason876222f2016-11-29 01:44:11 -0800123 RTC_DCHECK(observer_ != nullptr);
Åsa Persson04d5f1d2018-04-20 15:19:11 +0200124 check_qp_task_ = new CheckQpTask(this);
Mirko Bonadei675513b2017-11-09 11:09:25 +0100125 RTC_LOG(LS_INFO) << "QP thresholds: low: " << thresholds_.low
126 << ", high: " << thresholds_.high;
pbos@webrtc.orga0d78272014-09-12 11:51:47 +0000127}
128
kthelgason876222f2016-11-29 01:44:11 -0800129QualityScaler::~QualityScaler() {
130 RTC_DCHECK_CALLED_SEQUENTIALLY(&task_checker_);
131 check_qp_task_->Stop();
132}
133
134int64_t QualityScaler::GetSamplingPeriodMs() const {
135 RTC_DCHECK_CALLED_SEQUENTIALLY(&task_checker_);
Åsa Perssona945aee2018-04-24 16:53:25 +0200136 if (fast_rampup_) {
137 return sampling_period_ms_;
138 }
139 if (experiment_enabled_ && !observed_enough_frames_) {
140 // Use half the interval while waiting for enough frames.
141 return sampling_period_ms_ / 2;
142 }
143 return sampling_period_ms_ * kSamplePeriodScaleFactor;
kthelgason876222f2016-11-29 01:44:11 -0800144}
145
Åsa Perssona945aee2018-04-24 16:53:25 +0200146void QualityScaler::ReportDroppedFrameByMediaOpt() {
kthelgason876222f2016-11-29 01:44:11 -0800147 RTC_DCHECK_CALLED_SEQUENTIALLY(&task_checker_);
Åsa Perssona945aee2018-04-24 16:53:25 +0200148 framedrop_percent_media_opt_.AddSample(100);
149 framedrop_percent_all_.AddSample(100);
150}
151
152void QualityScaler::ReportDroppedFrameByEncoder() {
153 RTC_DCHECK_CALLED_SEQUENTIALLY(&task_checker_);
154 framedrop_percent_all_.AddSample(100);
pbos@webrtc.orga0d78272014-09-12 11:51:47 +0000155}
156
Åsa Persson04d5f1d2018-04-20 15:19:11 +0200157void QualityScaler::ReportQp(int qp) {
kthelgason876222f2016-11-29 01:44:11 -0800158 RTC_DCHECK_CALLED_SEQUENTIALLY(&task_checker_);
Åsa Perssona945aee2018-04-24 16:53:25 +0200159 framedrop_percent_media_opt_.AddSample(0);
160 framedrop_percent_all_.AddSample(0);
kthelgason194f40a2016-09-14 02:14:58 -0700161 average_qp_.AddSample(qp);
Åsa Perssona945aee2018-04-24 16:53:25 +0200162 if (qp_smoother_high_)
163 qp_smoother_high_->Add(qp);
164 if (qp_smoother_low_)
165 qp_smoother_low_->Add(qp);
pbos@webrtc.orga0d78272014-09-12 11:51:47 +0000166}
167
Åsa Persson04d5f1d2018-04-20 15:19:11 +0200168void QualityScaler::CheckQp() {
kthelgason876222f2016-11-29 01:44:11 -0800169 RTC_DCHECK_CALLED_SEQUENTIALLY(&task_checker_);
jackychen61b4d512015-04-21 15:30:11 -0700170 // Should be set through InitEncode -> Should be set by now.
kthelgason876222f2016-11-29 01:44:11 -0800171 RTC_DCHECK_GE(thresholds_.low, 0);
kthelgason55a01352017-04-04 02:31:42 -0700172
Åsa Persson0ad2d8a2018-04-19 11:06:11 +0200173 // If we have not observed at least this many frames we can't make a good
174 // scaling decision.
Åsa Perssona945aee2018-04-24 16:53:25 +0200175 const size_t frames = config_.use_all_drop_reasons
176 ? framedrop_percent_all_.size()
177 : framedrop_percent_media_opt_.size();
178 if (frames < kMinFramesNeededToScale) {
179 observed_enough_frames_ = false;
kthelgason55a01352017-04-04 02:31:42 -0700180 return;
Åsa Perssona945aee2018-04-24 16:53:25 +0200181 }
182 observed_enough_frames_ = true;
kthelgason55a01352017-04-04 02:31:42 -0700183
kthelgason194f40a2016-09-14 02:14:58 -0700184 // Check if we should scale down due to high frame drop.
Åsa Perssona945aee2018-04-24 16:53:25 +0200185 const rtc::Optional<int> drop_rate =
186 config_.use_all_drop_reasons ? framedrop_percent_all_.GetAverage()
187 : framedrop_percent_media_opt_.GetAverage();
kthelgason194f40a2016-09-14 02:14:58 -0700188 if (drop_rate && *drop_rate >= kFramedropPercentThreshold) {
Åsa Persson0ad2d8a2018-04-19 11:06:11 +0200189 RTC_LOG(LS_INFO) << "Reporting high QP, framedrop percent " << *drop_rate;
Åsa Persson04d5f1d2018-04-20 15:19:11 +0200190 ReportQpHigh();
kthelgason194f40a2016-09-14 02:14:58 -0700191 return;
192 }
193
194 // Check if we should scale up or down based on QP.
Åsa Perssona945aee2018-04-24 16:53:25 +0200195 const rtc::Optional<int> avg_qp_high = qp_smoother_high_
196 ? qp_smoother_high_->GetAvg()
197 : average_qp_.GetAverage();
198 const rtc::Optional<int> avg_qp_low =
199 qp_smoother_low_ ? qp_smoother_low_->GetAvg() : average_qp_.GetAverage();
200 if (avg_qp_high && avg_qp_low) {
201 RTC_LOG(LS_INFO) << "Checking average QP " << *avg_qp_high << " ("
202 << *avg_qp_low << ").";
203 if (*avg_qp_high > thresholds_.high) {
Åsa Persson04d5f1d2018-04-20 15:19:11 +0200204 ReportQpHigh();
glaznevd1c44352017-03-23 14:40:08 -0700205 return;
206 }
Åsa Perssona945aee2018-04-24 16:53:25 +0200207 if (*avg_qp_low <= thresholds_.low) {
glaznevd1c44352017-03-23 14:40:08 -0700208 // QP has been low. We want to try a higher resolution.
Åsa Persson04d5f1d2018-04-20 15:19:11 +0200209 ReportQpLow();
glaznevd1c44352017-03-23 14:40:08 -0700210 return;
211 }
kthelgason194f40a2016-09-14 02:14:58 -0700212 }
213}
214
Åsa Persson04d5f1d2018-04-20 15:19:11 +0200215void QualityScaler::ReportQpLow() {
kthelgason876222f2016-11-29 01:44:11 -0800216 RTC_DCHECK_CALLED_SEQUENTIALLY(&task_checker_);
kthelgason194f40a2016-09-14 02:14:58 -0700217 ClearSamples();
sprangb1ca0732017-02-01 08:38:12 -0800218 observer_->AdaptUp(AdaptationObserverInterface::AdaptReason::kQuality);
kthelgason194f40a2016-09-14 02:14:58 -0700219}
220
Åsa Persson04d5f1d2018-04-20 15:19:11 +0200221void QualityScaler::ReportQpHigh() {
kthelgason876222f2016-11-29 01:44:11 -0800222 RTC_DCHECK_CALLED_SEQUENTIALLY(&task_checker_);
kthelgason194f40a2016-09-14 02:14:58 -0700223 ClearSamples();
sprangb1ca0732017-02-01 08:38:12 -0800224 observer_->AdaptDown(AdaptationObserverInterface::AdaptReason::kQuality);
kthelgason194f40a2016-09-14 02:14:58 -0700225 // If we've scaled down, wait longer before scaling up again.
226 if (fast_rampup_) {
227 fast_rampup_ = false;
kthelgason194f40a2016-09-14 02:14:58 -0700228 }
jackychen6e2ce6e2015-07-13 16:26:33 -0700229}
pbos@webrtc.orga0d78272014-09-12 11:51:47 +0000230
pbos@webrtc.orga0d78272014-09-12 11:51:47 +0000231void QualityScaler::ClearSamples() {
kthelgason876222f2016-11-29 01:44:11 -0800232 RTC_DCHECK_CALLED_SEQUENTIALLY(&task_checker_);
Åsa Perssona945aee2018-04-24 16:53:25 +0200233 framedrop_percent_media_opt_.Reset();
234 framedrop_percent_all_.Reset();
kthelgason194f40a2016-09-14 02:14:58 -0700235 average_qp_.Reset();
Åsa Perssona945aee2018-04-24 16:53:25 +0200236 if (qp_smoother_high_)
237 qp_smoother_high_->Reset();
238 if (qp_smoother_low_)
239 qp_smoother_low_->Reset();
pboscbac40d2016-04-13 02:51:02 -0700240}
pbos@webrtc.orga0d78272014-09-12 11:51:47 +0000241} // namespace webrtc