blob: a5f6593e66b615231bf82ce08c3f8fc4148d4e63 [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"
20#include "rtc_base/task_queue.h"
kthelgason478681e2016-09-28 08:17:43 -070021
Kári Tristan Helgason5a20ed32016-09-15 10:56:19 +020022// TODO(kthelgason): Some versions of Android have issues with log2.
23// See https://code.google.com/p/android/issues/detail?id=212634 for details
24#if defined(WEBRTC_ANDROID)
25#define log2(x) (log(x) / log(2))
26#endif
kthelgason194f40a2016-09-14 02:14:58 -070027
pbos@webrtc.orga0d78272014-09-12 11:51:47 +000028namespace webrtc {
29
Peter Boström926dfcd2016-04-14 14:48:10 +020030namespace {
Niels Möller225c7872018-02-22 15:03:53 +010031// TODO(nisse): Delete, delegate to encoders.
pboscbac40d2016-04-13 02:51:02 -070032// Threshold constant used until first downscale (to permit fast rampup).
kthelgason876222f2016-11-29 01:44:11 -080033static const int kMeasureMs = 2000;
34static const float kSamplePeriodScaleFactor = 2.5;
pbos@webrtc.orga0d78272014-09-12 11:51:47 +000035static const int kFramedropPercentThreshold = 60;
kthelgason55a01352017-04-04 02:31:42 -070036static const int kMinFramesNeededToScale = 2 * 30;
pbos@webrtc.orga0d78272014-09-12 11:51:47 +000037
kthelgason876222f2016-11-29 01:44:11 -080038} // namespace
kthelgason478681e2016-09-28 08:17:43 -070039
kthelgason876222f2016-11-29 01:44:11 -080040class QualityScaler::CheckQPTask : public rtc::QueuedTask {
41 public:
42 explicit CheckQPTask(QualityScaler* scaler) : scaler_(scaler) {
Mirko Bonadei675513b2017-11-09 11:09:25 +010043 RTC_LOG(LS_INFO) << "Created CheckQPTask. Scheduling on queue...";
kthelgason876222f2016-11-29 01:44:11 -080044 rtc::TaskQueue::Current()->PostDelayedTask(
45 std::unique_ptr<rtc::QueuedTask>(this), scaler_->GetSamplingPeriodMs());
Alex Glazneva9d08922016-02-19 15:24:06 -080046 }
kthelgason876222f2016-11-29 01:44:11 -080047 void Stop() {
48 RTC_DCHECK_CALLED_SEQUENTIALLY(&task_checker_);
Mirko Bonadei675513b2017-11-09 11:09:25 +010049 RTC_LOG(LS_INFO) << "Stopping QP Check task.";
kthelgason876222f2016-11-29 01:44:11 -080050 stop_ = true;
51 }
52
53 private:
54 bool Run() override {
55 RTC_DCHECK_CALLED_SEQUENTIALLY(&task_checker_);
56 if (stop_)
57 return true; // TaskQueue will free this task.
58 scaler_->CheckQP();
59 rtc::TaskQueue::Current()->PostDelayedTask(
60 std::unique_ptr<rtc::QueuedTask>(this), scaler_->GetSamplingPeriodMs());
61 return false; // Retain the task in order to reuse it.
62 }
63
64 QualityScaler* const scaler_;
65 bool stop_ = false;
66 rtc::SequencedTaskChecker task_checker_;
67};
68
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)
kthelgason876222f2016-11-29 01:44:11 -080077 : check_qp_task_(nullptr),
78 observer_(observer),
Åsa Persson0ad2d8a2018-04-19 11:06:11 +020079 thresholds_(thresholds),
80 sampling_period_ms_(sampling_period_ms),
kthelgason876222f2016-11-29 01:44:11 -080081 fast_rampup_(true),
82 // Arbitrarily choose size based on 30 fps for 5 seconds.
83 average_qp_(5 * 30),
Åsa Persson0ad2d8a2018-04-19 11:06:11 +020084 framedrop_percent_(5 * 30) {
kthelgason876222f2016-11-29 01:44:11 -080085 RTC_DCHECK_CALLED_SEQUENTIALLY(&task_checker_);
86 RTC_DCHECK(observer_ != nullptr);
87 check_qp_task_ = new CheckQPTask(this);
Mirko Bonadei675513b2017-11-09 11:09:25 +010088 RTC_LOG(LS_INFO) << "QP thresholds: low: " << thresholds_.low
89 << ", high: " << thresholds_.high;
pbos@webrtc.orga0d78272014-09-12 11:51:47 +000090}
91
kthelgason876222f2016-11-29 01:44:11 -080092QualityScaler::~QualityScaler() {
93 RTC_DCHECK_CALLED_SEQUENTIALLY(&task_checker_);
94 check_qp_task_->Stop();
95}
96
97int64_t QualityScaler::GetSamplingPeriodMs() const {
98 RTC_DCHECK_CALLED_SEQUENTIALLY(&task_checker_);
99 return fast_rampup_ ? sampling_period_ms_
100 : (sampling_period_ms_ * kSamplePeriodScaleFactor);
101}
102
103void QualityScaler::ReportDroppedFrame() {
104 RTC_DCHECK_CALLED_SEQUENTIALLY(&task_checker_);
105 framedrop_percent_.AddSample(100);
pbos@webrtc.orga0d78272014-09-12 11:51:47 +0000106}
107
jackychen98d8cf52015-05-21 11:12:02 -0700108void QualityScaler::ReportQP(int qp) {
kthelgason876222f2016-11-29 01:44:11 -0800109 RTC_DCHECK_CALLED_SEQUENTIALLY(&task_checker_);
pbos@webrtc.orga0d78272014-09-12 11:51:47 +0000110 framedrop_percent_.AddSample(0);
kthelgason194f40a2016-09-14 02:14:58 -0700111 average_qp_.AddSample(qp);
pbos@webrtc.orga0d78272014-09-12 11:51:47 +0000112}
113
kthelgason876222f2016-11-29 01:44:11 -0800114void QualityScaler::CheckQP() {
115 RTC_DCHECK_CALLED_SEQUENTIALLY(&task_checker_);
jackychen61b4d512015-04-21 15:30:11 -0700116 // Should be set through InitEncode -> Should be set by now.
kthelgason876222f2016-11-29 01:44:11 -0800117 RTC_DCHECK_GE(thresholds_.low, 0);
kthelgason55a01352017-04-04 02:31:42 -0700118
Åsa Persson0ad2d8a2018-04-19 11:06:11 +0200119 // If we have not observed at least this many frames we can't make a good
120 // scaling decision.
kthelgason55a01352017-04-04 02:31:42 -0700121 if (framedrop_percent_.size() < kMinFramesNeededToScale)
122 return;
123
kthelgason194f40a2016-09-14 02:14:58 -0700124 // Check if we should scale down due to high frame drop.
kthelgason876222f2016-11-29 01:44:11 -0800125 const rtc::Optional<int> drop_rate = framedrop_percent_.GetAverage();
kthelgason194f40a2016-09-14 02:14:58 -0700126 if (drop_rate && *drop_rate >= kFramedropPercentThreshold) {
Åsa Persson0ad2d8a2018-04-19 11:06:11 +0200127 RTC_LOG(LS_INFO) << "Reporting high QP, framedrop percent " << *drop_rate;
kthelgason876222f2016-11-29 01:44:11 -0800128 ReportQPHigh();
kthelgason194f40a2016-09-14 02:14:58 -0700129 return;
130 }
131
132 // Check if we should scale up or down based on QP.
kthelgason876222f2016-11-29 01:44:11 -0800133 const rtc::Optional<int> avg_qp = average_qp_.GetAverage();
glaznevd1c44352017-03-23 14:40:08 -0700134 if (avg_qp) {
Mirko Bonadei675513b2017-11-09 11:09:25 +0100135 RTC_LOG(LS_INFO) << "Checking average QP " << *avg_qp;
glaznevd1c44352017-03-23 14:40:08 -0700136 if (*avg_qp > thresholds_.high) {
137 ReportQPHigh();
138 return;
139 }
140 if (*avg_qp <= thresholds_.low) {
141 // QP has been low. We want to try a higher resolution.
142 ReportQPLow();
143 return;
144 }
kthelgason194f40a2016-09-14 02:14:58 -0700145 }
146}
147
kthelgason876222f2016-11-29 01:44:11 -0800148void QualityScaler::ReportQPLow() {
149 RTC_DCHECK_CALLED_SEQUENTIALLY(&task_checker_);
kthelgason194f40a2016-09-14 02:14:58 -0700150 ClearSamples();
sprangb1ca0732017-02-01 08:38:12 -0800151 observer_->AdaptUp(AdaptationObserverInterface::AdaptReason::kQuality);
kthelgason194f40a2016-09-14 02:14:58 -0700152}
153
kthelgason876222f2016-11-29 01:44:11 -0800154void QualityScaler::ReportQPHigh() {
155 RTC_DCHECK_CALLED_SEQUENTIALLY(&task_checker_);
kthelgason194f40a2016-09-14 02:14:58 -0700156 ClearSamples();
sprangb1ca0732017-02-01 08:38:12 -0800157 observer_->AdaptDown(AdaptationObserverInterface::AdaptReason::kQuality);
kthelgason194f40a2016-09-14 02:14:58 -0700158 // If we've scaled down, wait longer before scaling up again.
159 if (fast_rampup_) {
160 fast_rampup_ = false;
kthelgason194f40a2016-09-14 02:14:58 -0700161 }
jackychen6e2ce6e2015-07-13 16:26:33 -0700162}
pbos@webrtc.orga0d78272014-09-12 11:51:47 +0000163
pbos@webrtc.orga0d78272014-09-12 11:51:47 +0000164void QualityScaler::ClearSamples() {
kthelgason876222f2016-11-29 01:44:11 -0800165 RTC_DCHECK_CALLED_SEQUENTIALLY(&task_checker_);
pbos@webrtc.orga0d78272014-09-12 11:51:47 +0000166 framedrop_percent_.Reset();
kthelgason194f40a2016-09-14 02:14:58 -0700167 average_qp_.Reset();
pboscbac40d2016-04-13 02:51:02 -0700168}
pbos@webrtc.orga0d78272014-09-12 11:51:47 +0000169} // namespace webrtc