blob: ff22862bb53bb8dd767ba229ad4a38a527b70565 [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
kthelgason478681e2016-09-28 08:17:43 -070018#include "webrtc/base/checks.h"
kthelgason876222f2016-11-29 01:44:11 -080019#include "webrtc/base/logging.h"
20#include "webrtc/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;
pbos@webrtc.orga0d78272014-09-12 11:51:47 +000042
kthelgason876222f2016-11-29 01:44:11 -080043static VideoEncoder::QpThresholds CodecTypeToDefaultThresholds(
44 VideoCodecType codec_type) {
45 int low = -1;
46 int high = -1;
kthelgason478681e2016-09-28 08:17:43 -070047 switch (codec_type) {
48 case kVideoCodecH264:
49 low = kLowH264QpThreshold;
50 high = kHighH264QpThreshold;
51 break;
52 case kVideoCodecVP8:
53 low = kLowVp8QpThreshold;
54 high = kHighVp8QpThreshold;
55 break;
56 default:
57 RTC_NOTREACHED() << "Invalid codec type for QualityScaler.";
58 }
kthelgason876222f2016-11-29 01:44:11 -080059 return VideoEncoder::QpThresholds(low, high);
kthelgason478681e2016-09-28 08:17:43 -070060}
kthelgason876222f2016-11-29 01:44:11 -080061} // namespace
kthelgason478681e2016-09-28 08:17:43 -070062
kthelgason876222f2016-11-29 01:44:11 -080063class QualityScaler::CheckQPTask : public rtc::QueuedTask {
64 public:
65 explicit CheckQPTask(QualityScaler* scaler) : scaler_(scaler) {
66 LOG(LS_INFO) << "Created CheckQPTask. Scheduling on queue...";
67 rtc::TaskQueue::Current()->PostDelayedTask(
68 std::unique_ptr<rtc::QueuedTask>(this), scaler_->GetSamplingPeriodMs());
Alex Glazneva9d08922016-02-19 15:24:06 -080069 }
kthelgason876222f2016-11-29 01:44:11 -080070 void Stop() {
71 RTC_DCHECK_CALLED_SEQUENTIALLY(&task_checker_);
72 LOG(LS_INFO) << "Stopping QP Check task.";
73 stop_ = true;
74 }
75
76 private:
77 bool Run() override {
78 RTC_DCHECK_CALLED_SEQUENTIALLY(&task_checker_);
79 if (stop_)
80 return true; // TaskQueue will free this task.
81 scaler_->CheckQP();
82 rtc::TaskQueue::Current()->PostDelayedTask(
83 std::unique_ptr<rtc::QueuedTask>(this), scaler_->GetSamplingPeriodMs());
84 return false; // Retain the task in order to reuse it.
85 }
86
87 QualityScaler* const scaler_;
88 bool stop_ = false;
89 rtc::SequencedTaskChecker task_checker_;
90};
91
sprangb1ca0732017-02-01 08:38:12 -080092QualityScaler::QualityScaler(AdaptationObserverInterface* observer,
kthelgason876222f2016-11-29 01:44:11 -080093 VideoCodecType codec_type)
94 : QualityScaler(observer, CodecTypeToDefaultThresholds(codec_type)) {}
95
sprangb1ca0732017-02-01 08:38:12 -080096QualityScaler::QualityScaler(AdaptationObserverInterface* observer,
kthelgason876222f2016-11-29 01:44:11 -080097 VideoEncoder::QpThresholds thresholds)
98 : QualityScaler(observer, thresholds, kMeasureMs) {}
99
100// Protected ctor, should not be called directly.
sprangb1ca0732017-02-01 08:38:12 -0800101QualityScaler::QualityScaler(AdaptationObserverInterface* observer,
kthelgason876222f2016-11-29 01:44:11 -0800102 VideoEncoder::QpThresholds thresholds,
103 int64_t sampling_period)
104 : check_qp_task_(nullptr),
105 observer_(observer),
106 sampling_period_ms_(sampling_period),
107 fast_rampup_(true),
108 // Arbitrarily choose size based on 30 fps for 5 seconds.
109 average_qp_(5 * 30),
110 framedrop_percent_(5 * 30),
111 thresholds_(thresholds) {
112 RTC_DCHECK_CALLED_SEQUENTIALLY(&task_checker_);
113 RTC_DCHECK(observer_ != nullptr);
114 check_qp_task_ = new CheckQPTask(this);
glaznevd1c44352017-03-23 14:40:08 -0700115 LOG(LS_INFO) << "QP thresholds: low: " << thresholds_.low
116 << ", high: " << thresholds_.high;
pbos@webrtc.orga0d78272014-09-12 11:51:47 +0000117}
118
kthelgason876222f2016-11-29 01:44:11 -0800119QualityScaler::~QualityScaler() {
120 RTC_DCHECK_CALLED_SEQUENTIALLY(&task_checker_);
121 check_qp_task_->Stop();
122}
123
124int64_t QualityScaler::GetSamplingPeriodMs() const {
125 RTC_DCHECK_CALLED_SEQUENTIALLY(&task_checker_);
126 return fast_rampup_ ? sampling_period_ms_
127 : (sampling_period_ms_ * kSamplePeriodScaleFactor);
128}
129
130void QualityScaler::ReportDroppedFrame() {
131 RTC_DCHECK_CALLED_SEQUENTIALLY(&task_checker_);
132 framedrop_percent_.AddSample(100);
pbos@webrtc.orga0d78272014-09-12 11:51:47 +0000133}
134
jackychen98d8cf52015-05-21 11:12:02 -0700135void QualityScaler::ReportQP(int qp) {
kthelgason876222f2016-11-29 01:44:11 -0800136 RTC_DCHECK_CALLED_SEQUENTIALLY(&task_checker_);
pbos@webrtc.orga0d78272014-09-12 11:51:47 +0000137 framedrop_percent_.AddSample(0);
kthelgason194f40a2016-09-14 02:14:58 -0700138 average_qp_.AddSample(qp);
pbos@webrtc.orga0d78272014-09-12 11:51:47 +0000139}
140
kthelgason876222f2016-11-29 01:44:11 -0800141void QualityScaler::CheckQP() {
142 RTC_DCHECK_CALLED_SEQUENTIALLY(&task_checker_);
jackychen61b4d512015-04-21 15:30:11 -0700143 // Should be set through InitEncode -> Should be set by now.
kthelgason876222f2016-11-29 01:44:11 -0800144 RTC_DCHECK_GE(thresholds_.low, 0);
kthelgason194f40a2016-09-14 02:14:58 -0700145 // Check if we should scale down due to high frame drop.
kthelgason876222f2016-11-29 01:44:11 -0800146 const rtc::Optional<int> drop_rate = framedrop_percent_.GetAverage();
kthelgason194f40a2016-09-14 02:14:58 -0700147 if (drop_rate && *drop_rate >= kFramedropPercentThreshold) {
kthelgason876222f2016-11-29 01:44:11 -0800148 ReportQPHigh();
kthelgason194f40a2016-09-14 02:14:58 -0700149 return;
150 }
151
152 // Check if we should scale up or down based on QP.
kthelgason876222f2016-11-29 01:44:11 -0800153 const rtc::Optional<int> avg_qp = average_qp_.GetAverage();
glaznevd1c44352017-03-23 14:40:08 -0700154 if (avg_qp) {
155 LOG(LS_INFO) << "Checking average QP " << *avg_qp;
156 if (*avg_qp > thresholds_.high) {
157 ReportQPHigh();
158 return;
159 }
160 if (*avg_qp <= thresholds_.low) {
161 // QP has been low. We want to try a higher resolution.
162 ReportQPLow();
163 return;
164 }
kthelgason194f40a2016-09-14 02:14:58 -0700165 }
166}
167
kthelgason876222f2016-11-29 01:44:11 -0800168void QualityScaler::ReportQPLow() {
169 RTC_DCHECK_CALLED_SEQUENTIALLY(&task_checker_);
kthelgason194f40a2016-09-14 02:14:58 -0700170 ClearSamples();
sprangb1ca0732017-02-01 08:38:12 -0800171 observer_->AdaptUp(AdaptationObserverInterface::AdaptReason::kQuality);
kthelgason194f40a2016-09-14 02:14:58 -0700172}
173
kthelgason876222f2016-11-29 01:44:11 -0800174void QualityScaler::ReportQPHigh() {
175 RTC_DCHECK_CALLED_SEQUENTIALLY(&task_checker_);
kthelgason194f40a2016-09-14 02:14:58 -0700176 ClearSamples();
sprangb1ca0732017-02-01 08:38:12 -0800177 observer_->AdaptDown(AdaptationObserverInterface::AdaptReason::kQuality);
kthelgason194f40a2016-09-14 02:14:58 -0700178 // If we've scaled down, wait longer before scaling up again.
179 if (fast_rampup_) {
180 fast_rampup_ = false;
kthelgason194f40a2016-09-14 02:14:58 -0700181 }
jackychen6e2ce6e2015-07-13 16:26:33 -0700182}
pbos@webrtc.orga0d78272014-09-12 11:51:47 +0000183
pbos@webrtc.orga0d78272014-09-12 11:51:47 +0000184void QualityScaler::ClearSamples() {
kthelgason876222f2016-11-29 01:44:11 -0800185 RTC_DCHECK_CALLED_SEQUENTIALLY(&task_checker_);
pbos@webrtc.orga0d78272014-09-12 11:51:47 +0000186 framedrop_percent_.Reset();
kthelgason194f40a2016-09-14 02:14:58 -0700187 average_qp_.Reset();
pboscbac40d2016-04-13 02:51:02 -0700188}
pbos@webrtc.orga0d78272014-09-12 11:51:47 +0000189} // namespace webrtc