pbos@webrtc.org | a0d7827 | 2014-09-12 11:51:47 +0000 | [diff] [blame] | 1 | /* |
| 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öller | 718a763 | 2016-06-13 13:06:01 +0200 | [diff] [blame] | 10 | |
Mirko Bonadei | 92ea95e | 2017-09-15 06:47:31 +0200 | [diff] [blame] | 11 | #include "modules/video_coding/utility/quality_scaler.h" |
pbos@webrtc.org | a0d7827 | 2014-09-12 11:51:47 +0000 | [diff] [blame] | 12 | |
kthelgason | 876222f | 2016-11-29 01:44:11 -0800 | [diff] [blame] | 13 | #include <memory> |
Sebastian Jansson | ecb6897 | 2019-01-18 10:30:54 +0100 | [diff] [blame] | 14 | #include <utility> |
Kári Tristan Helgason | 5a20ed3 | 2016-09-15 10:56:19 +0200 | [diff] [blame] | 15 | |
Evan Shrubsole | ce0a11d | 2020-04-16 11:36:55 +0200 | [diff] [blame] | 16 | #include "api/video/video_adaptation_reason.h" |
Mirko Bonadei | 92ea95e | 2017-09-15 06:47:31 +0200 | [diff] [blame] | 17 | #include "rtc_base/checks.h" |
Åsa Persson | 517678c | 2019-05-06 14:17:35 +0200 | [diff] [blame] | 18 | #include "rtc_base/experiments/quality_scaler_settings.h" |
Mirko Bonadei | 92ea95e | 2017-09-15 06:47:31 +0200 | [diff] [blame] | 19 | #include "rtc_base/logging.h" |
Åsa Persson | a945aee | 2018-04-24 16:53:25 +0200 | [diff] [blame] | 20 | #include "rtc_base/numerics/exp_filter.h" |
Mirko Bonadei | 92ea95e | 2017-09-15 06:47:31 +0200 | [diff] [blame] | 21 | #include "rtc_base/task_queue.h" |
Henrik Boström | 012aa37 | 2020-04-27 17:40:55 +0200 | [diff] [blame^] | 22 | #include "rtc_base/task_utils/to_queued_task.h" |
| 23 | #include "rtc_base/weak_ptr.h" |
kthelgason | 478681e | 2016-09-28 08:17:43 -0700 | [diff] [blame] | 24 | |
Kári Tristan Helgason | 5a20ed3 | 2016-09-15 10:56:19 +0200 | [diff] [blame] | 25 | // TODO(kthelgason): Some versions of Android have issues with log2. |
| 26 | // See https://code.google.com/p/android/issues/detail?id=212634 for details |
| 27 | #if defined(WEBRTC_ANDROID) |
| 28 | #define log2(x) (log(x) / log(2)) |
| 29 | #endif |
kthelgason | 194f40a | 2016-09-14 02:14:58 -0700 | [diff] [blame] | 30 | |
pbos@webrtc.org | a0d7827 | 2014-09-12 11:51:47 +0000 | [diff] [blame] | 31 | namespace webrtc { |
| 32 | |
Peter Boström | 926dfcd | 2016-04-14 14:48:10 +0200 | [diff] [blame] | 33 | namespace { |
Niels Möller | 225c787 | 2018-02-22 15:03:53 +0100 | [diff] [blame] | 34 | // TODO(nisse): Delete, delegate to encoders. |
pbos | cbac40d | 2016-04-13 02:51:02 -0700 | [diff] [blame] | 35 | // Threshold constant used until first downscale (to permit fast rampup). |
kthelgason | 876222f | 2016-11-29 01:44:11 -0800 | [diff] [blame] | 36 | static const int kMeasureMs = 2000; |
| 37 | static const float kSamplePeriodScaleFactor = 2.5; |
pbos@webrtc.org | a0d7827 | 2014-09-12 11:51:47 +0000 | [diff] [blame] | 38 | static const int kFramedropPercentThreshold = 60; |
Åsa Persson | 517678c | 2019-05-06 14:17:35 +0200 | [diff] [blame] | 39 | static const size_t kMinFramesNeededToScale = 2 * 30; |
pbos@webrtc.org | a0d7827 | 2014-09-12 11:51:47 +0000 | [diff] [blame] | 40 | |
kthelgason | 876222f | 2016-11-29 01:44:11 -0800 | [diff] [blame] | 41 | } // namespace |
kthelgason | 478681e | 2016-09-28 08:17:43 -0700 | [diff] [blame] | 42 | |
Åsa Persson | a945aee | 2018-04-24 16:53:25 +0200 | [diff] [blame] | 43 | class QualityScaler::QpSmoother { |
| 44 | public: |
| 45 | explicit QpSmoother(float alpha) |
Sebastian Jansson | b678940 | 2019-03-01 15:40:49 +0100 | [diff] [blame] | 46 | : alpha_(alpha), |
| 47 | // The initial value of last_sample_ms doesn't matter since the smoother |
| 48 | // will ignore the time delta for the first update. |
| 49 | last_sample_ms_(0), |
| 50 | smoother_(alpha) {} |
Åsa Persson | a945aee | 2018-04-24 16:53:25 +0200 | [diff] [blame] | 51 | |
Danil Chapovalov | 0040b66 | 2018-06-18 10:48:16 +0200 | [diff] [blame] | 52 | absl::optional<int> GetAvg() const { |
Åsa Persson | a945aee | 2018-04-24 16:53:25 +0200 | [diff] [blame] | 53 | float value = smoother_.filtered(); |
| 54 | if (value == rtc::ExpFilter::kValueUndefined) { |
Danil Chapovalov | 0040b66 | 2018-06-18 10:48:16 +0200 | [diff] [blame] | 55 | return absl::nullopt; |
Åsa Persson | a945aee | 2018-04-24 16:53:25 +0200 | [diff] [blame] | 56 | } |
| 57 | return static_cast<int>(value); |
| 58 | } |
| 59 | |
Sebastian Jansson | b678940 | 2019-03-01 15:40:49 +0100 | [diff] [blame] | 60 | void Add(float sample, int64_t time_sent_us) { |
| 61 | int64_t now_ms = time_sent_us / 1000; |
Åsa Persson | a945aee | 2018-04-24 16:53:25 +0200 | [diff] [blame] | 62 | smoother_.Apply(static_cast<float>(now_ms - last_sample_ms_), sample); |
| 63 | last_sample_ms_ = now_ms; |
| 64 | } |
| 65 | |
| 66 | void Reset() { smoother_.Reset(alpha_); } |
| 67 | |
| 68 | private: |
| 69 | const float alpha_; |
| 70 | int64_t last_sample_ms_; |
| 71 | rtc::ExpFilter smoother_; |
| 72 | }; |
| 73 | |
Henrik Boström | 012aa37 | 2020-04-27 17:40:55 +0200 | [diff] [blame^] | 74 | // The QualityScaler checks for QP periodically by queuing CheckQpTasks. The |
| 75 | // task will either run to completion and trigger a new task being queued, or it |
| 76 | // will be destroyed because the QualityScaler is destroyed. |
| 77 | // |
| 78 | // When high or low QP is reported, the task will be pending until a callback is |
| 79 | // invoked. This lets the QualityScalerQpUsageHandlerInterface react to QP usage |
| 80 | // asynchronously and prevents checking for QP until the stream has potentially |
| 81 | // been reconfigured. |
| 82 | class QualityScaler::CheckQpTask { |
| 83 | public: |
| 84 | // The result of one CheckQpTask may influence the delay of the next |
| 85 | // CheckQpTask. |
| 86 | struct Result { |
| 87 | bool observed_enough_frames = false; |
| 88 | bool qp_usage_reported = false; |
| 89 | bool clear_qp_samples = false; |
| 90 | }; |
| 91 | |
| 92 | CheckQpTask(QualityScaler* quality_scaler, Result previous_task_result) |
| 93 | : quality_scaler_(quality_scaler), |
| 94 | state_(State::kNotStarted), |
| 95 | previous_task_result_(previous_task_result), |
| 96 | weak_ptr_factory_(this) {} |
| 97 | |
| 98 | void StartDelayedTask() { |
| 99 | RTC_DCHECK_EQ(state_, State::kNotStarted); |
| 100 | state_ = State::kCheckingQp; |
| 101 | TaskQueueBase::Current()->PostDelayedTask( |
| 102 | ToQueuedTask([this_weak_ptr = weak_ptr_factory_.GetWeakPtr(), this] { |
| 103 | if (!this_weak_ptr) { |
| 104 | // The task has been cancelled through destruction. |
| 105 | return; |
| 106 | } |
| 107 | RTC_DCHECK_EQ(state_, State::kCheckingQp); |
| 108 | RTC_DCHECK_RUN_ON(&quality_scaler_->task_checker_); |
| 109 | switch (quality_scaler_->CheckQp()) { |
| 110 | case QualityScaler::CheckQpResult::kInsufficientSamples: { |
| 111 | result_.observed_enough_frames = false; |
| 112 | // After this line, |this| may be deleted. |
| 113 | DoCompleteTask(); |
| 114 | return; |
| 115 | } |
| 116 | case QualityScaler::CheckQpResult::kNormalQp: { |
| 117 | result_.observed_enough_frames = true; |
| 118 | // After this line, |this| may be deleted. |
| 119 | DoCompleteTask(); |
| 120 | return; |
| 121 | } |
| 122 | case QualityScaler::CheckQpResult::kHighQp: { |
| 123 | result_.observed_enough_frames = true; |
| 124 | result_.qp_usage_reported = true; |
| 125 | state_ = State::kAwaitingQpUsageHandled; |
| 126 | rtc::scoped_refptr<QualityScalerQpUsageHandlerCallbackInterface> |
| 127 | callback = ConstructCallback(); |
| 128 | quality_scaler_->fast_rampup_ = false; |
| 129 | // After this line, |this| may be deleted. |
| 130 | quality_scaler_->handler_->OnReportQpUsageHigh(callback); |
| 131 | return; |
| 132 | } |
| 133 | case QualityScaler::CheckQpResult::kLowQp: { |
| 134 | result_.observed_enough_frames = true; |
| 135 | result_.qp_usage_reported = true; |
| 136 | state_ = State::kAwaitingQpUsageHandled; |
| 137 | rtc::scoped_refptr<QualityScalerQpUsageHandlerCallbackInterface> |
| 138 | callback = ConstructCallback(); |
| 139 | // After this line, |this| may be deleted. |
| 140 | quality_scaler_->handler_->OnReportQpUsageLow(callback); |
| 141 | return; |
| 142 | } |
| 143 | } |
| 144 | }), |
| 145 | GetCheckingQpDelayMs()); |
| 146 | } |
| 147 | |
| 148 | void OnQpUsageHandled(bool clear_qp_samples) { |
| 149 | RTC_DCHECK_EQ(state_, State::kAwaitingQpUsageHandled); |
| 150 | result_.clear_qp_samples = clear_qp_samples; |
| 151 | if (clear_qp_samples) |
| 152 | quality_scaler_->ClearSamples(); |
| 153 | DoCompleteTask(); |
| 154 | } |
| 155 | |
| 156 | bool HasCompletedTask() const { return state_ == State::kCompleted; } |
| 157 | |
| 158 | Result result() const { |
| 159 | RTC_DCHECK(HasCompletedTask()); |
| 160 | return result_; |
| 161 | } |
| 162 | |
| 163 | private: |
| 164 | enum class State { |
| 165 | kNotStarted, |
| 166 | kCheckingQp, |
| 167 | kAwaitingQpUsageHandled, |
| 168 | kCompleted, |
| 169 | }; |
| 170 | |
| 171 | // Defined after the definition of QualityScaler::CheckQpTaskHandlerCallback. |
| 172 | // Gets around a forward declaration issue. |
| 173 | rtc::scoped_refptr<QualityScaler::CheckQpTaskHandlerCallback> |
| 174 | ConstructCallback(); |
| 175 | |
| 176 | // Determines the sampling period of CheckQpTasks. |
| 177 | int64_t GetCheckingQpDelayMs() const { |
| 178 | RTC_DCHECK_RUN_ON(&quality_scaler_->task_checker_); |
| 179 | if (quality_scaler_->fast_rampup_) { |
| 180 | return quality_scaler_->sampling_period_ms_; |
| 181 | } |
| 182 | if (quality_scaler_->experiment_enabled_ && |
| 183 | !previous_task_result_.observed_enough_frames) { |
| 184 | // Use half the interval while waiting for enough frames. |
| 185 | return quality_scaler_->sampling_period_ms_ / 2; |
| 186 | } |
| 187 | if (!previous_task_result_.clear_qp_samples) { |
| 188 | // Check shortly again. |
| 189 | return quality_scaler_->sampling_period_ms_ / 8; |
| 190 | } |
| 191 | if (quality_scaler_->scale_factor_ && |
| 192 | !previous_task_result_.qp_usage_reported) { |
| 193 | // Last CheckQp did not call AdaptDown/Up, possibly reduce interval. |
| 194 | return quality_scaler_->sampling_period_ms_ * |
| 195 | quality_scaler_->scale_factor_.value(); |
| 196 | } |
| 197 | return quality_scaler_->sampling_period_ms_ * |
| 198 | quality_scaler_->initial_scale_factor_; |
| 199 | } |
| 200 | |
| 201 | void DoCompleteTask() { |
| 202 | RTC_DCHECK(state_ == State::kCheckingQp || |
| 203 | state_ == State::kAwaitingQpUsageHandled); |
| 204 | state_ = State::kCompleted; |
| 205 | // Starting the next task deletes the pending task. After this line, |this| |
| 206 | // has been deleted. |
| 207 | quality_scaler_->StartNextCheckQpTask(); |
| 208 | } |
| 209 | |
| 210 | QualityScaler* const quality_scaler_; |
| 211 | State state_; |
| 212 | const Result previous_task_result_; |
| 213 | Result result_; |
| 214 | |
| 215 | rtc::WeakPtrFactory<CheckQpTask> weak_ptr_factory_; |
| 216 | }; |
| 217 | |
| 218 | class QualityScaler::CheckQpTaskHandlerCallback |
| 219 | : public QualityScalerQpUsageHandlerCallbackInterface { |
| 220 | public: |
| 221 | CheckQpTaskHandlerCallback( |
| 222 | rtc::WeakPtr<QualityScaler::CheckQpTask> check_qp_task) |
| 223 | : QualityScalerQpUsageHandlerCallbackInterface(), |
| 224 | check_qp_task_(std::move(check_qp_task)), |
| 225 | was_handled_(false) {} |
| 226 | |
| 227 | ~CheckQpTaskHandlerCallback() { RTC_DCHECK(was_handled_); } |
| 228 | |
| 229 | void OnQpUsageHandled(bool clear_qp_samples) { |
| 230 | RTC_DCHECK(!was_handled_); |
| 231 | was_handled_ = true; |
| 232 | if (!check_qp_task_) { |
| 233 | // The task has been cancelled through destruction; the result of the |
| 234 | // operation is ignored. |
| 235 | return; |
| 236 | } |
| 237 | check_qp_task_->OnQpUsageHandled(clear_qp_samples); |
| 238 | } |
| 239 | |
| 240 | private: |
| 241 | // The callback may outlive the QualityScaler and its task. |
| 242 | rtc::WeakPtr<QualityScaler::CheckQpTask> const check_qp_task_; |
| 243 | bool was_handled_; |
| 244 | }; |
| 245 | |
| 246 | rtc::scoped_refptr<QualityScaler::CheckQpTaskHandlerCallback> |
| 247 | QualityScaler::CheckQpTask::ConstructCallback() { |
| 248 | return new CheckQpTaskHandlerCallback(weak_ptr_factory_.GetWeakPtr()); |
| 249 | } |
| 250 | |
| 251 | QualityScaler::QualityScaler(QualityScalerQpUsageHandlerInterface* handler, |
kthelgason | 876222f | 2016-11-29 01:44:11 -0800 | [diff] [blame] | 252 | VideoEncoder::QpThresholds thresholds) |
Henrik Boström | 012aa37 | 2020-04-27 17:40:55 +0200 | [diff] [blame^] | 253 | : QualityScaler(handler, thresholds, kMeasureMs) {} |
kthelgason | 876222f | 2016-11-29 01:44:11 -0800 | [diff] [blame] | 254 | |
| 255 | // Protected ctor, should not be called directly. |
Henrik Boström | 012aa37 | 2020-04-27 17:40:55 +0200 | [diff] [blame^] | 256 | QualityScaler::QualityScaler(QualityScalerQpUsageHandlerInterface* handler, |
kthelgason | 876222f | 2016-11-29 01:44:11 -0800 | [diff] [blame] | 257 | VideoEncoder::QpThresholds thresholds, |
Åsa Persson | 0ad2d8a | 2018-04-19 11:06:11 +0200 | [diff] [blame] | 258 | int64_t sampling_period_ms) |
Henrik Boström | 012aa37 | 2020-04-27 17:40:55 +0200 | [diff] [blame^] | 259 | : handler_(handler), |
Åsa Persson | 0ad2d8a | 2018-04-19 11:06:11 +0200 | [diff] [blame] | 260 | thresholds_(thresholds), |
| 261 | sampling_period_ms_(sampling_period_ms), |
kthelgason | 876222f | 2016-11-29 01:44:11 -0800 | [diff] [blame] | 262 | fast_rampup_(true), |
| 263 | // Arbitrarily choose size based on 30 fps for 5 seconds. |
| 264 | average_qp_(5 * 30), |
Åsa Persson | a945aee | 2018-04-24 16:53:25 +0200 | [diff] [blame] | 265 | framedrop_percent_media_opt_(5 * 30), |
| 266 | framedrop_percent_all_(5 * 30), |
| 267 | experiment_enabled_(QualityScalingExperiment::Enabled()), |
Åsa Persson | 517678c | 2019-05-06 14:17:35 +0200 | [diff] [blame] | 268 | min_frames_needed_( |
| 269 | QualityScalerSettings::ParseFromFieldTrials().MinFrames().value_or( |
| 270 | kMinFramesNeededToScale)), |
| 271 | initial_scale_factor_(QualityScalerSettings::ParseFromFieldTrials() |
| 272 | .InitialScaleFactor() |
| 273 | .value_or(kSamplePeriodScaleFactor)), |
| 274 | scale_factor_( |
Henrik Boström | 012aa37 | 2020-04-27 17:40:55 +0200 | [diff] [blame^] | 275 | QualityScalerSettings::ParseFromFieldTrials().ScaleFactor()) { |
Sebastian Jansson | b55015e | 2019-04-09 13:44:04 +0200 | [diff] [blame] | 276 | RTC_DCHECK_RUN_ON(&task_checker_); |
Åsa Persson | a945aee | 2018-04-24 16:53:25 +0200 | [diff] [blame] | 277 | if (experiment_enabled_) { |
| 278 | config_ = QualityScalingExperiment::GetConfig(); |
| 279 | qp_smoother_high_.reset(new QpSmoother(config_.alpha_high)); |
| 280 | qp_smoother_low_.reset(new QpSmoother(config_.alpha_low)); |
| 281 | } |
Henrik Boström | 012aa37 | 2020-04-27 17:40:55 +0200 | [diff] [blame^] | 282 | RTC_DCHECK(handler_ != nullptr); |
| 283 | StartNextCheckQpTask(); |
Mirko Bonadei | 675513b | 2017-11-09 11:09:25 +0100 | [diff] [blame] | 284 | RTC_LOG(LS_INFO) << "QP thresholds: low: " << thresholds_.low |
| 285 | << ", high: " << thresholds_.high; |
pbos@webrtc.org | a0d7827 | 2014-09-12 11:51:47 +0000 | [diff] [blame] | 286 | } |
| 287 | |
kthelgason | 876222f | 2016-11-29 01:44:11 -0800 | [diff] [blame] | 288 | QualityScaler::~QualityScaler() { |
Sebastian Jansson | b55015e | 2019-04-09 13:44:04 +0200 | [diff] [blame] | 289 | RTC_DCHECK_RUN_ON(&task_checker_); |
kthelgason | 876222f | 2016-11-29 01:44:11 -0800 | [diff] [blame] | 290 | } |
| 291 | |
Henrik Boström | 012aa37 | 2020-04-27 17:40:55 +0200 | [diff] [blame^] | 292 | void QualityScaler::StartNextCheckQpTask() { |
Sebastian Jansson | b55015e | 2019-04-09 13:44:04 +0200 | [diff] [blame] | 293 | RTC_DCHECK_RUN_ON(&task_checker_); |
Henrik Boström | 012aa37 | 2020-04-27 17:40:55 +0200 | [diff] [blame^] | 294 | RTC_DCHECK(!pending_qp_task_ || pending_qp_task_->HasCompletedTask()) |
| 295 | << "A previous CheckQpTask has not completed yet!"; |
| 296 | CheckQpTask::Result previous_task_result; |
| 297 | if (pending_qp_task_) { |
| 298 | previous_task_result = pending_qp_task_->result(); |
Åsa Persson | a945aee | 2018-04-24 16:53:25 +0200 | [diff] [blame] | 299 | } |
Henrik Boström | 012aa37 | 2020-04-27 17:40:55 +0200 | [diff] [blame^] | 300 | pending_qp_task_ = std::make_unique<CheckQpTask>(this, previous_task_result); |
| 301 | pending_qp_task_->StartDelayedTask(); |
kthelgason | 876222f | 2016-11-29 01:44:11 -0800 | [diff] [blame] | 302 | } |
| 303 | |
Åsa Persson | 1231419 | 2019-06-20 15:45:07 +0200 | [diff] [blame] | 304 | void QualityScaler::SetQpThresholds(VideoEncoder::QpThresholds thresholds) { |
| 305 | RTC_DCHECK_RUN_ON(&task_checker_); |
| 306 | thresholds_ = thresholds; |
| 307 | } |
| 308 | |
Åsa Persson | a945aee | 2018-04-24 16:53:25 +0200 | [diff] [blame] | 309 | void QualityScaler::ReportDroppedFrameByMediaOpt() { |
Sebastian Jansson | b55015e | 2019-04-09 13:44:04 +0200 | [diff] [blame] | 310 | RTC_DCHECK_RUN_ON(&task_checker_); |
Åsa Persson | a945aee | 2018-04-24 16:53:25 +0200 | [diff] [blame] | 311 | framedrop_percent_media_opt_.AddSample(100); |
| 312 | framedrop_percent_all_.AddSample(100); |
| 313 | } |
| 314 | |
| 315 | void QualityScaler::ReportDroppedFrameByEncoder() { |
Sebastian Jansson | b55015e | 2019-04-09 13:44:04 +0200 | [diff] [blame] | 316 | RTC_DCHECK_RUN_ON(&task_checker_); |
Åsa Persson | a945aee | 2018-04-24 16:53:25 +0200 | [diff] [blame] | 317 | framedrop_percent_all_.AddSample(100); |
pbos@webrtc.org | a0d7827 | 2014-09-12 11:51:47 +0000 | [diff] [blame] | 318 | } |
| 319 | |
Sebastian Jansson | b678940 | 2019-03-01 15:40:49 +0100 | [diff] [blame] | 320 | void QualityScaler::ReportQp(int qp, int64_t time_sent_us) { |
Sebastian Jansson | b55015e | 2019-04-09 13:44:04 +0200 | [diff] [blame] | 321 | RTC_DCHECK_RUN_ON(&task_checker_); |
Åsa Persson | a945aee | 2018-04-24 16:53:25 +0200 | [diff] [blame] | 322 | framedrop_percent_media_opt_.AddSample(0); |
| 323 | framedrop_percent_all_.AddSample(0); |
kthelgason | 194f40a | 2016-09-14 02:14:58 -0700 | [diff] [blame] | 324 | average_qp_.AddSample(qp); |
Åsa Persson | a945aee | 2018-04-24 16:53:25 +0200 | [diff] [blame] | 325 | if (qp_smoother_high_) |
Sebastian Jansson | b678940 | 2019-03-01 15:40:49 +0100 | [diff] [blame] | 326 | qp_smoother_high_->Add(qp, time_sent_us); |
Åsa Persson | a945aee | 2018-04-24 16:53:25 +0200 | [diff] [blame] | 327 | if (qp_smoother_low_) |
Sebastian Jansson | b678940 | 2019-03-01 15:40:49 +0100 | [diff] [blame] | 328 | qp_smoother_low_->Add(qp, time_sent_us); |
pbos@webrtc.org | a0d7827 | 2014-09-12 11:51:47 +0000 | [diff] [blame] | 329 | } |
| 330 | |
Åsa Persson | e644a03 | 2019-11-08 15:56:00 +0100 | [diff] [blame] | 331 | bool QualityScaler::QpFastFilterLow() const { |
| 332 | RTC_DCHECK_RUN_ON(&task_checker_); |
| 333 | size_t num_frames = config_.use_all_drop_reasons |
| 334 | ? framedrop_percent_all_.Size() |
| 335 | : framedrop_percent_media_opt_.Size(); |
| 336 | const size_t kMinNumFrames = 10; |
| 337 | if (num_frames < kMinNumFrames) { |
| 338 | return false; // Wait for more frames before making a decision. |
| 339 | } |
| 340 | absl::optional<int> avg_qp_high = qp_smoother_high_ |
| 341 | ? qp_smoother_high_->GetAvg() |
| 342 | : average_qp_.GetAverageRoundedDown(); |
| 343 | return (avg_qp_high) ? (avg_qp_high.value() <= thresholds_.low) : false; |
| 344 | } |
| 345 | |
Henrik Boström | 012aa37 | 2020-04-27 17:40:55 +0200 | [diff] [blame^] | 346 | QualityScaler::CheckQpResult QualityScaler::CheckQp() const { |
Sebastian Jansson | b55015e | 2019-04-09 13:44:04 +0200 | [diff] [blame] | 347 | RTC_DCHECK_RUN_ON(&task_checker_); |
jackychen | 61b4d51 | 2015-04-21 15:30:11 -0700 | [diff] [blame] | 348 | // Should be set through InitEncode -> Should be set by now. |
kthelgason | 876222f | 2016-11-29 01:44:11 -0800 | [diff] [blame] | 349 | RTC_DCHECK_GE(thresholds_.low, 0); |
kthelgason | 55a0135 | 2017-04-04 02:31:42 -0700 | [diff] [blame] | 350 | |
Åsa Persson | 0ad2d8a | 2018-04-19 11:06:11 +0200 | [diff] [blame] | 351 | // If we have not observed at least this many frames we can't make a good |
| 352 | // scaling decision. |
Åsa Persson | a945aee | 2018-04-24 16:53:25 +0200 | [diff] [blame] | 353 | const size_t frames = config_.use_all_drop_reasons |
Ilya Nikolaevskiy | 2634199 | 2018-11-05 12:55:18 +0100 | [diff] [blame] | 354 | ? framedrop_percent_all_.Size() |
| 355 | : framedrop_percent_media_opt_.Size(); |
Åsa Persson | 517678c | 2019-05-06 14:17:35 +0200 | [diff] [blame] | 356 | if (frames < min_frames_needed_) { |
Henrik Boström | 012aa37 | 2020-04-27 17:40:55 +0200 | [diff] [blame^] | 357 | return CheckQpResult::kInsufficientSamples; |
Åsa Persson | a945aee | 2018-04-24 16:53:25 +0200 | [diff] [blame] | 358 | } |
kthelgason | 55a0135 | 2017-04-04 02:31:42 -0700 | [diff] [blame] | 359 | |
kthelgason | 194f40a | 2016-09-14 02:14:58 -0700 | [diff] [blame] | 360 | // Check if we should scale down due to high frame drop. |
Danil Chapovalov | 0040b66 | 2018-06-18 10:48:16 +0200 | [diff] [blame] | 361 | const absl::optional<int> drop_rate = |
Ilya Nikolaevskiy | 2634199 | 2018-11-05 12:55:18 +0100 | [diff] [blame] | 362 | config_.use_all_drop_reasons |
| 363 | ? framedrop_percent_all_.GetAverageRoundedDown() |
| 364 | : framedrop_percent_media_opt_.GetAverageRoundedDown(); |
kthelgason | 194f40a | 2016-09-14 02:14:58 -0700 | [diff] [blame] | 365 | if (drop_rate && *drop_rate >= kFramedropPercentThreshold) { |
Åsa Persson | 0ad2d8a | 2018-04-19 11:06:11 +0200 | [diff] [blame] | 366 | RTC_LOG(LS_INFO) << "Reporting high QP, framedrop percent " << *drop_rate; |
Henrik Boström | 012aa37 | 2020-04-27 17:40:55 +0200 | [diff] [blame^] | 367 | return CheckQpResult::kHighQp; |
kthelgason | 194f40a | 2016-09-14 02:14:58 -0700 | [diff] [blame] | 368 | } |
| 369 | |
| 370 | // Check if we should scale up or down based on QP. |
Ilya Nikolaevskiy | 2634199 | 2018-11-05 12:55:18 +0100 | [diff] [blame] | 371 | const absl::optional<int> avg_qp_high = |
| 372 | qp_smoother_high_ ? qp_smoother_high_->GetAvg() |
| 373 | : average_qp_.GetAverageRoundedDown(); |
Danil Chapovalov | 0040b66 | 2018-06-18 10:48:16 +0200 | [diff] [blame] | 374 | const absl::optional<int> avg_qp_low = |
Ilya Nikolaevskiy | 2634199 | 2018-11-05 12:55:18 +0100 | [diff] [blame] | 375 | qp_smoother_low_ ? qp_smoother_low_->GetAvg() |
| 376 | : average_qp_.GetAverageRoundedDown(); |
Åsa Persson | a945aee | 2018-04-24 16:53:25 +0200 | [diff] [blame] | 377 | if (avg_qp_high && avg_qp_low) { |
| 378 | RTC_LOG(LS_INFO) << "Checking average QP " << *avg_qp_high << " (" |
| 379 | << *avg_qp_low << ")."; |
| 380 | if (*avg_qp_high > thresholds_.high) { |
Henrik Boström | 012aa37 | 2020-04-27 17:40:55 +0200 | [diff] [blame^] | 381 | return CheckQpResult::kHighQp; |
glaznev | d1c4435 | 2017-03-23 14:40:08 -0700 | [diff] [blame] | 382 | } |
Åsa Persson | a945aee | 2018-04-24 16:53:25 +0200 | [diff] [blame] | 383 | if (*avg_qp_low <= thresholds_.low) { |
glaznev | d1c4435 | 2017-03-23 14:40:08 -0700 | [diff] [blame] | 384 | // QP has been low. We want to try a higher resolution. |
Henrik Boström | 012aa37 | 2020-04-27 17:40:55 +0200 | [diff] [blame^] | 385 | return CheckQpResult::kLowQp; |
glaznev | d1c4435 | 2017-03-23 14:40:08 -0700 | [diff] [blame] | 386 | } |
kthelgason | 194f40a | 2016-09-14 02:14:58 -0700 | [diff] [blame] | 387 | } |
Henrik Boström | 012aa37 | 2020-04-27 17:40:55 +0200 | [diff] [blame^] | 388 | return CheckQpResult::kNormalQp; |
jackychen | 6e2ce6e | 2015-07-13 16:26:33 -0700 | [diff] [blame] | 389 | } |
pbos@webrtc.org | a0d7827 | 2014-09-12 11:51:47 +0000 | [diff] [blame] | 390 | |
pbos@webrtc.org | a0d7827 | 2014-09-12 11:51:47 +0000 | [diff] [blame] | 391 | void QualityScaler::ClearSamples() { |
Sebastian Jansson | b55015e | 2019-04-09 13:44:04 +0200 | [diff] [blame] | 392 | RTC_DCHECK_RUN_ON(&task_checker_); |
Åsa Persson | a945aee | 2018-04-24 16:53:25 +0200 | [diff] [blame] | 393 | framedrop_percent_media_opt_.Reset(); |
| 394 | framedrop_percent_all_.Reset(); |
kthelgason | 194f40a | 2016-09-14 02:14:58 -0700 | [diff] [blame] | 395 | average_qp_.Reset(); |
Åsa Persson | a945aee | 2018-04-24 16:53:25 +0200 | [diff] [blame] | 396 | if (qp_smoother_high_) |
| 397 | qp_smoother_high_->Reset(); |
| 398 | if (qp_smoother_low_) |
| 399 | qp_smoother_low_->Reset(); |
pbos | cbac40d | 2016-04-13 02:51:02 -0700 | [diff] [blame] | 400 | } |
Henrik Boström | 012aa37 | 2020-04-27 17:40:55 +0200 | [diff] [blame^] | 401 | |
| 402 | QualityScalerQpUsageHandlerInterface::~QualityScalerQpUsageHandlerInterface() {} |
| 403 | |
| 404 | QualityScalerQpUsageHandlerCallbackInterface:: |
| 405 | QualityScalerQpUsageHandlerCallbackInterface() {} |
| 406 | |
| 407 | QualityScalerQpUsageHandlerCallbackInterface:: |
| 408 | ~QualityScalerQpUsageHandlerCallbackInterface() {} |
| 409 | |
pbos@webrtc.org | a0d7827 | 2014-09-12 11:51:47 +0000 | [diff] [blame] | 410 | } // namespace webrtc |