blob: 760ebd7b5174c540df63c86da94c78b8368ea4b4 [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
kjellander@webrtc.orgb7ce9642015-11-18 23:04:10 +010011#include "webrtc/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
Edward Lemurc20978e2017-07-06 19:44:34 +020018#include "webrtc/rtc_base/checks.h"
19#include "webrtc/rtc_base/logging.h"
20#include "webrtc/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 {
pboscbac40d2016-04-13 02:51:02 -070031// Threshold constant used until first downscale (to permit fast rampup).
kthelgason876222f2016-11-29 01:44:11 -080032static const int kMeasureMs = 2000;
33static const float kSamplePeriodScaleFactor = 2.5;
pbos@webrtc.orga0d78272014-09-12 11:51:47 +000034static const int kFramedropPercentThreshold = 60;
kthelgason478681e2016-09-28 08:17:43 -070035// QP scaling threshold defaults:
36static const int kLowH264QpThreshold = 24;
37static const int kHighH264QpThreshold = 37;
38// QP is obtained from VP8-bitstream for HW, so the QP corresponds to the
39// bitstream range of [0, 127] and not the user-level range of [0,63].
40static const int kLowVp8QpThreshold = 29;
41static const int kHighVp8QpThreshold = 95;
asapersson1e15a992017-06-07 04:09:45 -070042// QP is obtained from VP9-bitstream for HW, so the QP corresponds to the
43// bitstream range of [0, 255] and not the user-level range of [0,63].
44// Current VP9 settings are mapped from VP8 thresholds above.
45static const int kLowVp9QpThreshold = 96;
glaznevda4eba12017-06-13 11:34:49 -070046static const int kHighVp9QpThreshold = 185;
kthelgason55a01352017-04-04 02:31:42 -070047static const int kMinFramesNeededToScale = 2 * 30;
pbos@webrtc.orga0d78272014-09-12 11:51:47 +000048
kthelgason876222f2016-11-29 01:44:11 -080049static VideoEncoder::QpThresholds CodecTypeToDefaultThresholds(
50 VideoCodecType codec_type) {
51 int low = -1;
52 int high = -1;
kthelgason478681e2016-09-28 08:17:43 -070053 switch (codec_type) {
54 case kVideoCodecH264:
55 low = kLowH264QpThreshold;
56 high = kHighH264QpThreshold;
57 break;
58 case kVideoCodecVP8:
59 low = kLowVp8QpThreshold;
60 high = kHighVp8QpThreshold;
61 break;
asapersson1e15a992017-06-07 04:09:45 -070062 case kVideoCodecVP9:
63 low = kLowVp9QpThreshold;
64 high = kHighVp9QpThreshold;
65 break;
kthelgason478681e2016-09-28 08:17:43 -070066 default:
67 RTC_NOTREACHED() << "Invalid codec type for QualityScaler.";
68 }
kthelgason876222f2016-11-29 01:44:11 -080069 return VideoEncoder::QpThresholds(low, high);
kthelgason478681e2016-09-28 08:17:43 -070070}
kthelgason876222f2016-11-29 01:44:11 -080071} // namespace
kthelgason478681e2016-09-28 08:17:43 -070072
kthelgason876222f2016-11-29 01:44:11 -080073class QualityScaler::CheckQPTask : public rtc::QueuedTask {
74 public:
75 explicit CheckQPTask(QualityScaler* scaler) : scaler_(scaler) {
76 LOG(LS_INFO) << "Created CheckQPTask. Scheduling on queue...";
77 rtc::TaskQueue::Current()->PostDelayedTask(
78 std::unique_ptr<rtc::QueuedTask>(this), scaler_->GetSamplingPeriodMs());
Alex Glazneva9d08922016-02-19 15:24:06 -080079 }
kthelgason876222f2016-11-29 01:44:11 -080080 void Stop() {
81 RTC_DCHECK_CALLED_SEQUENTIALLY(&task_checker_);
82 LOG(LS_INFO) << "Stopping QP Check task.";
83 stop_ = true;
84 }
85
86 private:
87 bool Run() override {
88 RTC_DCHECK_CALLED_SEQUENTIALLY(&task_checker_);
89 if (stop_)
90 return true; // TaskQueue will free this task.
91 scaler_->CheckQP();
92 rtc::TaskQueue::Current()->PostDelayedTask(
93 std::unique_ptr<rtc::QueuedTask>(this), scaler_->GetSamplingPeriodMs());
94 return false; // Retain the task in order to reuse it.
95 }
96
97 QualityScaler* const scaler_;
98 bool stop_ = false;
99 rtc::SequencedTaskChecker task_checker_;
100};
101
sprangb1ca0732017-02-01 08:38:12 -0800102QualityScaler::QualityScaler(AdaptationObserverInterface* observer,
kthelgason876222f2016-11-29 01:44:11 -0800103 VideoCodecType codec_type)
104 : QualityScaler(observer, CodecTypeToDefaultThresholds(codec_type)) {}
105
sprangb1ca0732017-02-01 08:38:12 -0800106QualityScaler::QualityScaler(AdaptationObserverInterface* observer,
kthelgason876222f2016-11-29 01:44:11 -0800107 VideoEncoder::QpThresholds thresholds)
108 : QualityScaler(observer, thresholds, kMeasureMs) {}
109
110// Protected ctor, should not be called directly.
sprangb1ca0732017-02-01 08:38:12 -0800111QualityScaler::QualityScaler(AdaptationObserverInterface* observer,
kthelgason876222f2016-11-29 01:44:11 -0800112 VideoEncoder::QpThresholds thresholds,
113 int64_t sampling_period)
114 : check_qp_task_(nullptr),
115 observer_(observer),
116 sampling_period_ms_(sampling_period),
117 fast_rampup_(true),
118 // Arbitrarily choose size based on 30 fps for 5 seconds.
119 average_qp_(5 * 30),
120 framedrop_percent_(5 * 30),
121 thresholds_(thresholds) {
122 RTC_DCHECK_CALLED_SEQUENTIALLY(&task_checker_);
123 RTC_DCHECK(observer_ != nullptr);
124 check_qp_task_ = new CheckQPTask(this);
glaznevd1c44352017-03-23 14:40:08 -0700125 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_);
136 return fast_rampup_ ? sampling_period_ms_
137 : (sampling_period_ms_ * kSamplePeriodScaleFactor);
138}
139
140void QualityScaler::ReportDroppedFrame() {
141 RTC_DCHECK_CALLED_SEQUENTIALLY(&task_checker_);
142 framedrop_percent_.AddSample(100);
pbos@webrtc.orga0d78272014-09-12 11:51:47 +0000143}
144
jackychen98d8cf52015-05-21 11:12:02 -0700145void QualityScaler::ReportQP(int qp) {
kthelgason876222f2016-11-29 01:44:11 -0800146 RTC_DCHECK_CALLED_SEQUENTIALLY(&task_checker_);
pbos@webrtc.orga0d78272014-09-12 11:51:47 +0000147 framedrop_percent_.AddSample(0);
kthelgason194f40a2016-09-14 02:14:58 -0700148 average_qp_.AddSample(qp);
pbos@webrtc.orga0d78272014-09-12 11:51:47 +0000149}
150
kthelgason876222f2016-11-29 01:44:11 -0800151void QualityScaler::CheckQP() {
152 RTC_DCHECK_CALLED_SEQUENTIALLY(&task_checker_);
jackychen61b4d512015-04-21 15:30:11 -0700153 // Should be set through InitEncode -> Should be set by now.
kthelgason876222f2016-11-29 01:44:11 -0800154 RTC_DCHECK_GE(thresholds_.low, 0);
kthelgason55a01352017-04-04 02:31:42 -0700155
156 // If we have not observed at least this many frames we can't
157 // make a good scaling decision.
158 if (framedrop_percent_.size() < kMinFramesNeededToScale)
159 return;
160
kthelgason194f40a2016-09-14 02:14:58 -0700161 // Check if we should scale down due to high frame drop.
kthelgason876222f2016-11-29 01:44:11 -0800162 const rtc::Optional<int> drop_rate = framedrop_percent_.GetAverage();
kthelgason194f40a2016-09-14 02:14:58 -0700163 if (drop_rate && *drop_rate >= kFramedropPercentThreshold) {
kthelgason876222f2016-11-29 01:44:11 -0800164 ReportQPHigh();
kthelgason194f40a2016-09-14 02:14:58 -0700165 return;
166 }
167
168 // Check if we should scale up or down based on QP.
kthelgason876222f2016-11-29 01:44:11 -0800169 const rtc::Optional<int> avg_qp = average_qp_.GetAverage();
glaznevd1c44352017-03-23 14:40:08 -0700170 if (avg_qp) {
171 LOG(LS_INFO) << "Checking average QP " << *avg_qp;
172 if (*avg_qp > thresholds_.high) {
173 ReportQPHigh();
174 return;
175 }
176 if (*avg_qp <= thresholds_.low) {
177 // QP has been low. We want to try a higher resolution.
178 ReportQPLow();
179 return;
180 }
kthelgason194f40a2016-09-14 02:14:58 -0700181 }
182}
183
kthelgason876222f2016-11-29 01:44:11 -0800184void QualityScaler::ReportQPLow() {
185 RTC_DCHECK_CALLED_SEQUENTIALLY(&task_checker_);
kthelgason194f40a2016-09-14 02:14:58 -0700186 ClearSamples();
sprangb1ca0732017-02-01 08:38:12 -0800187 observer_->AdaptUp(AdaptationObserverInterface::AdaptReason::kQuality);
kthelgason194f40a2016-09-14 02:14:58 -0700188}
189
kthelgason876222f2016-11-29 01:44:11 -0800190void QualityScaler::ReportQPHigh() {
191 RTC_DCHECK_CALLED_SEQUENTIALLY(&task_checker_);
kthelgason194f40a2016-09-14 02:14:58 -0700192 ClearSamples();
sprangb1ca0732017-02-01 08:38:12 -0800193 observer_->AdaptDown(AdaptationObserverInterface::AdaptReason::kQuality);
kthelgason194f40a2016-09-14 02:14:58 -0700194 // If we've scaled down, wait longer before scaling up again.
195 if (fast_rampup_) {
196 fast_rampup_ = false;
kthelgason194f40a2016-09-14 02:14:58 -0700197 }
jackychen6e2ce6e2015-07-13 16:26:33 -0700198}
pbos@webrtc.orga0d78272014-09-12 11:51:47 +0000199
pbos@webrtc.orga0d78272014-09-12 11:51:47 +0000200void QualityScaler::ClearSamples() {
kthelgason876222f2016-11-29 01:44:11 -0800201 RTC_DCHECK_CALLED_SEQUENTIALLY(&task_checker_);
pbos@webrtc.orga0d78272014-09-12 11:51:47 +0000202 framedrop_percent_.Reset();
kthelgason194f40a2016-09-14 02:14:58 -0700203 average_qp_.Reset();
pboscbac40d2016-04-13 02:51:02 -0700204}
pbos@webrtc.orga0d78272014-09-12 11:51:47 +0000205} // namespace webrtc