blob: c55ee2f3b2d4b828b7ab58ec14521952bab04e78 [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
kthelgason876222f2016-11-29 01:44:11 -080013#include <memory>
Sebastian Janssonecb68972019-01-18 10:30:54 +010014#include <utility>
Kári Tristan Helgason5a20ed32016-09-15 10:56:19 +020015
Yves Gerey3e707812018-11-28 16:47:49 +010016#include "absl/types/optional.h"
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020017#include "rtc_base/checks.h"
18#include "rtc_base/logging.h"
Åsa Perssona945aee2018-04-24 16:53:25 +020019#include "rtc_base/numerics/exp_filter.h"
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020020#include "rtc_base/task_queue.h"
Steve Anton10542f22019-01-11 09:11:00 -080021#include "rtc_base/time_utils.h"
kthelgason478681e2016-09-28 08:17:43 -070022
Kári Tristan Helgason5a20ed32016-09-15 10:56:19 +020023// TODO(kthelgason): Some versions of Android have issues with log2.
24// See https://code.google.com/p/android/issues/detail?id=212634 for details
25#if defined(WEBRTC_ANDROID)
26#define log2(x) (log(x) / log(2))
27#endif
kthelgason194f40a2016-09-14 02:14:58 -070028
pbos@webrtc.orga0d78272014-09-12 11:51:47 +000029namespace webrtc {
30
Peter Boström926dfcd2016-04-14 14:48:10 +020031namespace {
Niels Möller225c7872018-02-22 15:03:53 +010032// TODO(nisse): Delete, delegate to encoders.
pboscbac40d2016-04-13 02:51:02 -070033// Threshold constant used until first downscale (to permit fast rampup).
kthelgason876222f2016-11-29 01:44:11 -080034static const int kMeasureMs = 2000;
35static const float kSamplePeriodScaleFactor = 2.5;
pbos@webrtc.orga0d78272014-09-12 11:51:47 +000036static const int kFramedropPercentThreshold = 60;
kthelgason55a01352017-04-04 02:31:42 -070037static const int kMinFramesNeededToScale = 2 * 30;
pbos@webrtc.orga0d78272014-09-12 11:51:47 +000038
kthelgason876222f2016-11-29 01:44:11 -080039} // namespace
kthelgason478681e2016-09-28 08:17:43 -070040
Åsa Perssona945aee2018-04-24 16:53:25 +020041class QualityScaler::QpSmoother {
42 public:
43 explicit QpSmoother(float alpha)
44 : alpha_(alpha), last_sample_ms_(rtc::TimeMillis()), smoother_(alpha) {}
45
Danil Chapovalov0040b662018-06-18 10:48:16 +020046 absl::optional<int> GetAvg() const {
Åsa Perssona945aee2018-04-24 16:53:25 +020047 float value = smoother_.filtered();
48 if (value == rtc::ExpFilter::kValueUndefined) {
Danil Chapovalov0040b662018-06-18 10:48:16 +020049 return absl::nullopt;
Åsa Perssona945aee2018-04-24 16:53:25 +020050 }
51 return static_cast<int>(value);
52 }
53
54 void Add(float sample) {
55 int64_t now_ms = rtc::TimeMillis();
56 smoother_.Apply(static_cast<float>(now_ms - last_sample_ms_), sample);
57 last_sample_ms_ = now_ms;
58 }
59
60 void Reset() { smoother_.Reset(alpha_); }
61
62 private:
63 const float alpha_;
64 int64_t last_sample_ms_;
65 rtc::ExpFilter smoother_;
66};
67
kthelgason876222f2016-11-29 01:44:11 -080068
sprangb1ca0732017-02-01 08:38:12 -080069QualityScaler::QualityScaler(AdaptationObserverInterface* observer,
kthelgason876222f2016-11-29 01:44:11 -080070 VideoEncoder::QpThresholds thresholds)
71 : QualityScaler(observer, thresholds, kMeasureMs) {}
72
73// Protected ctor, should not be called directly.
sprangb1ca0732017-02-01 08:38:12 -080074QualityScaler::QualityScaler(AdaptationObserverInterface* observer,
kthelgason876222f2016-11-29 01:44:11 -080075 VideoEncoder::QpThresholds thresholds,
Åsa Persson0ad2d8a2018-04-19 11:06:11 +020076 int64_t sampling_period_ms)
Sebastian Janssonecb68972019-01-18 10:30:54 +010077 : observer_(observer),
Åsa Persson0ad2d8a2018-04-19 11:06:11 +020078 thresholds_(thresholds),
79 sampling_period_ms_(sampling_period_ms),
kthelgason876222f2016-11-29 01:44:11 -080080 fast_rampup_(true),
81 // Arbitrarily choose size based on 30 fps for 5 seconds.
82 average_qp_(5 * 30),
Åsa Perssona945aee2018-04-24 16:53:25 +020083 framedrop_percent_media_opt_(5 * 30),
84 framedrop_percent_all_(5 * 30),
85 experiment_enabled_(QualityScalingExperiment::Enabled()),
86 observed_enough_frames_(false) {
kthelgason876222f2016-11-29 01:44:11 -080087 RTC_DCHECK_CALLED_SEQUENTIALLY(&task_checker_);
Åsa Perssona945aee2018-04-24 16:53:25 +020088 if (experiment_enabled_) {
89 config_ = QualityScalingExperiment::GetConfig();
90 qp_smoother_high_.reset(new QpSmoother(config_.alpha_high));
91 qp_smoother_low_.reset(new QpSmoother(config_.alpha_low));
92 }
kthelgason876222f2016-11-29 01:44:11 -080093 RTC_DCHECK(observer_ != nullptr);
Sebastian Janssonecb68972019-01-18 10:30:54 +010094 check_qp_task_ = RepeatingTaskHandle::DelayedStart(
95 TimeDelta::ms(GetSamplingPeriodMs()), [this]() {
96 CheckQp();
97 return TimeDelta::ms(GetSamplingPeriodMs());
98 });
Mirko Bonadei675513b2017-11-09 11:09:25 +010099 RTC_LOG(LS_INFO) << "QP thresholds: low: " << thresholds_.low
100 << ", high: " << thresholds_.high;
pbos@webrtc.orga0d78272014-09-12 11:51:47 +0000101}
102
kthelgason876222f2016-11-29 01:44:11 -0800103QualityScaler::~QualityScaler() {
104 RTC_DCHECK_CALLED_SEQUENTIALLY(&task_checker_);
Sebastian Janssonecb68972019-01-18 10:30:54 +0100105 check_qp_task_.Stop();
kthelgason876222f2016-11-29 01:44:11 -0800106}
107
108int64_t QualityScaler::GetSamplingPeriodMs() const {
109 RTC_DCHECK_CALLED_SEQUENTIALLY(&task_checker_);
Åsa Perssona945aee2018-04-24 16:53:25 +0200110 if (fast_rampup_) {
111 return sampling_period_ms_;
112 }
113 if (experiment_enabled_ && !observed_enough_frames_) {
114 // Use half the interval while waiting for enough frames.
115 return sampling_period_ms_ / 2;
116 }
117 return sampling_period_ms_ * kSamplePeriodScaleFactor;
kthelgason876222f2016-11-29 01:44:11 -0800118}
119
Åsa Perssona945aee2018-04-24 16:53:25 +0200120void QualityScaler::ReportDroppedFrameByMediaOpt() {
kthelgason876222f2016-11-29 01:44:11 -0800121 RTC_DCHECK_CALLED_SEQUENTIALLY(&task_checker_);
Åsa Perssona945aee2018-04-24 16:53:25 +0200122 framedrop_percent_media_opt_.AddSample(100);
123 framedrop_percent_all_.AddSample(100);
124}
125
126void QualityScaler::ReportDroppedFrameByEncoder() {
127 RTC_DCHECK_CALLED_SEQUENTIALLY(&task_checker_);
128 framedrop_percent_all_.AddSample(100);
pbos@webrtc.orga0d78272014-09-12 11:51:47 +0000129}
130
Åsa Persson04d5f1d2018-04-20 15:19:11 +0200131void QualityScaler::ReportQp(int qp) {
kthelgason876222f2016-11-29 01:44:11 -0800132 RTC_DCHECK_CALLED_SEQUENTIALLY(&task_checker_);
Åsa Perssona945aee2018-04-24 16:53:25 +0200133 framedrop_percent_media_opt_.AddSample(0);
134 framedrop_percent_all_.AddSample(0);
kthelgason194f40a2016-09-14 02:14:58 -0700135 average_qp_.AddSample(qp);
Åsa Perssona945aee2018-04-24 16:53:25 +0200136 if (qp_smoother_high_)
137 qp_smoother_high_->Add(qp);
138 if (qp_smoother_low_)
139 qp_smoother_low_->Add(qp);
pbos@webrtc.orga0d78272014-09-12 11:51:47 +0000140}
141
Åsa Persson04d5f1d2018-04-20 15:19:11 +0200142void QualityScaler::CheckQp() {
kthelgason876222f2016-11-29 01:44:11 -0800143 RTC_DCHECK_CALLED_SEQUENTIALLY(&task_checker_);
jackychen61b4d512015-04-21 15:30:11 -0700144 // Should be set through InitEncode -> Should be set by now.
kthelgason876222f2016-11-29 01:44:11 -0800145 RTC_DCHECK_GE(thresholds_.low, 0);
kthelgason55a01352017-04-04 02:31:42 -0700146
Åsa Persson0ad2d8a2018-04-19 11:06:11 +0200147 // If we have not observed at least this many frames we can't make a good
148 // scaling decision.
Åsa Perssona945aee2018-04-24 16:53:25 +0200149 const size_t frames = config_.use_all_drop_reasons
Ilya Nikolaevskiy26341992018-11-05 12:55:18 +0100150 ? framedrop_percent_all_.Size()
151 : framedrop_percent_media_opt_.Size();
Åsa Perssona945aee2018-04-24 16:53:25 +0200152 if (frames < kMinFramesNeededToScale) {
153 observed_enough_frames_ = false;
kthelgason55a01352017-04-04 02:31:42 -0700154 return;
Åsa Perssona945aee2018-04-24 16:53:25 +0200155 }
156 observed_enough_frames_ = true;
kthelgason55a01352017-04-04 02:31:42 -0700157
kthelgason194f40a2016-09-14 02:14:58 -0700158 // Check if we should scale down due to high frame drop.
Danil Chapovalov0040b662018-06-18 10:48:16 +0200159 const absl::optional<int> drop_rate =
Ilya Nikolaevskiy26341992018-11-05 12:55:18 +0100160 config_.use_all_drop_reasons
161 ? framedrop_percent_all_.GetAverageRoundedDown()
162 : framedrop_percent_media_opt_.GetAverageRoundedDown();
kthelgason194f40a2016-09-14 02:14:58 -0700163 if (drop_rate && *drop_rate >= kFramedropPercentThreshold) {
Åsa Persson0ad2d8a2018-04-19 11:06:11 +0200164 RTC_LOG(LS_INFO) << "Reporting high QP, framedrop percent " << *drop_rate;
Åsa Persson04d5f1d2018-04-20 15:19:11 +0200165 ReportQpHigh();
kthelgason194f40a2016-09-14 02:14:58 -0700166 return;
167 }
168
169 // Check if we should scale up or down based on QP.
Ilya Nikolaevskiy26341992018-11-05 12:55:18 +0100170 const absl::optional<int> avg_qp_high =
171 qp_smoother_high_ ? qp_smoother_high_->GetAvg()
172 : average_qp_.GetAverageRoundedDown();
Danil Chapovalov0040b662018-06-18 10:48:16 +0200173 const absl::optional<int> avg_qp_low =
Ilya Nikolaevskiy26341992018-11-05 12:55:18 +0100174 qp_smoother_low_ ? qp_smoother_low_->GetAvg()
175 : average_qp_.GetAverageRoundedDown();
Åsa Perssona945aee2018-04-24 16:53:25 +0200176 if (avg_qp_high && avg_qp_low) {
177 RTC_LOG(LS_INFO) << "Checking average QP " << *avg_qp_high << " ("
178 << *avg_qp_low << ").";
179 if (*avg_qp_high > thresholds_.high) {
Åsa Persson04d5f1d2018-04-20 15:19:11 +0200180 ReportQpHigh();
glaznevd1c44352017-03-23 14:40:08 -0700181 return;
182 }
Åsa Perssona945aee2018-04-24 16:53:25 +0200183 if (*avg_qp_low <= thresholds_.low) {
glaznevd1c44352017-03-23 14:40:08 -0700184 // QP has been low. We want to try a higher resolution.
Åsa Persson04d5f1d2018-04-20 15:19:11 +0200185 ReportQpLow();
glaznevd1c44352017-03-23 14:40:08 -0700186 return;
187 }
kthelgason194f40a2016-09-14 02:14:58 -0700188 }
189}
190
Åsa Persson04d5f1d2018-04-20 15:19:11 +0200191void QualityScaler::ReportQpLow() {
kthelgason876222f2016-11-29 01:44:11 -0800192 RTC_DCHECK_CALLED_SEQUENTIALLY(&task_checker_);
kthelgason194f40a2016-09-14 02:14:58 -0700193 ClearSamples();
sprangb1ca0732017-02-01 08:38:12 -0800194 observer_->AdaptUp(AdaptationObserverInterface::AdaptReason::kQuality);
kthelgason194f40a2016-09-14 02:14:58 -0700195}
196
Åsa Persson04d5f1d2018-04-20 15:19:11 +0200197void QualityScaler::ReportQpHigh() {
kthelgason876222f2016-11-29 01:44:11 -0800198 RTC_DCHECK_CALLED_SEQUENTIALLY(&task_checker_);
kthelgason194f40a2016-09-14 02:14:58 -0700199 ClearSamples();
sprangb1ca0732017-02-01 08:38:12 -0800200 observer_->AdaptDown(AdaptationObserverInterface::AdaptReason::kQuality);
kthelgason194f40a2016-09-14 02:14:58 -0700201 // If we've scaled down, wait longer before scaling up again.
202 if (fast_rampup_) {
203 fast_rampup_ = false;
kthelgason194f40a2016-09-14 02:14:58 -0700204 }
jackychen6e2ce6e2015-07-13 16:26:33 -0700205}
pbos@webrtc.orga0d78272014-09-12 11:51:47 +0000206
pbos@webrtc.orga0d78272014-09-12 11:51:47 +0000207void QualityScaler::ClearSamples() {
kthelgason876222f2016-11-29 01:44:11 -0800208 RTC_DCHECK_CALLED_SEQUENTIALLY(&task_checker_);
Åsa Perssona945aee2018-04-24 16:53:25 +0200209 framedrop_percent_media_opt_.Reset();
210 framedrop_percent_all_.Reset();
kthelgason194f40a2016-09-14 02:14:58 -0700211 average_qp_.Reset();
Åsa Perssona945aee2018-04-24 16:53:25 +0200212 if (qp_smoother_high_)
213 qp_smoother_high_->Reset();
214 if (qp_smoother_low_)
215 qp_smoother_low_->Reset();
pboscbac40d2016-04-13 02:51:02 -0700216}
pbos@webrtc.orga0d78272014-09-12 11:51:47 +0000217} // namespace webrtc