blob: f1623cb6e974b66c4857570bf23ef3a4df3782f2 [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;
kthelgason55a01352017-04-04 02:31:42 -070042static const int kMinFramesNeededToScale = 2 * 30;
pbos@webrtc.orga0d78272014-09-12 11:51:47 +000043
kthelgason876222f2016-11-29 01:44:11 -080044static VideoEncoder::QpThresholds CodecTypeToDefaultThresholds(
45 VideoCodecType codec_type) {
46 int low = -1;
47 int high = -1;
kthelgason478681e2016-09-28 08:17:43 -070048 switch (codec_type) {
49 case kVideoCodecH264:
50 low = kLowH264QpThreshold;
51 high = kHighH264QpThreshold;
52 break;
53 case kVideoCodecVP8:
54 low = kLowVp8QpThreshold;
55 high = kHighVp8QpThreshold;
56 break;
57 default:
58 RTC_NOTREACHED() << "Invalid codec type for QualityScaler.";
59 }
kthelgason876222f2016-11-29 01:44:11 -080060 return VideoEncoder::QpThresholds(low, high);
kthelgason478681e2016-09-28 08:17:43 -070061}
kthelgason876222f2016-11-29 01:44:11 -080062} // namespace
kthelgason478681e2016-09-28 08:17:43 -070063
kthelgason876222f2016-11-29 01:44:11 -080064class QualityScaler::CheckQPTask : public rtc::QueuedTask {
65 public:
66 explicit CheckQPTask(QualityScaler* scaler) : scaler_(scaler) {
67 LOG(LS_INFO) << "Created CheckQPTask. Scheduling on queue...";
68 rtc::TaskQueue::Current()->PostDelayedTask(
69 std::unique_ptr<rtc::QueuedTask>(this), scaler_->GetSamplingPeriodMs());
Alex Glazneva9d08922016-02-19 15:24:06 -080070 }
kthelgason876222f2016-11-29 01:44:11 -080071 void Stop() {
72 RTC_DCHECK_CALLED_SEQUENTIALLY(&task_checker_);
73 LOG(LS_INFO) << "Stopping QP Check task.";
74 stop_ = true;
75 }
76
77 private:
78 bool Run() override {
79 RTC_DCHECK_CALLED_SEQUENTIALLY(&task_checker_);
80 if (stop_)
81 return true; // TaskQueue will free this task.
82 scaler_->CheckQP();
83 rtc::TaskQueue::Current()->PostDelayedTask(
84 std::unique_ptr<rtc::QueuedTask>(this), scaler_->GetSamplingPeriodMs());
85 return false; // Retain the task in order to reuse it.
86 }
87
88 QualityScaler* const scaler_;
89 bool stop_ = false;
90 rtc::SequencedTaskChecker task_checker_;
91};
92
sprangb1ca0732017-02-01 08:38:12 -080093QualityScaler::QualityScaler(AdaptationObserverInterface* observer,
kthelgason876222f2016-11-29 01:44:11 -080094 VideoCodecType codec_type)
95 : QualityScaler(observer, CodecTypeToDefaultThresholds(codec_type)) {}
96
sprangb1ca0732017-02-01 08:38:12 -080097QualityScaler::QualityScaler(AdaptationObserverInterface* observer,
kthelgason876222f2016-11-29 01:44:11 -080098 VideoEncoder::QpThresholds thresholds)
99 : QualityScaler(observer, thresholds, kMeasureMs) {}
100
101// Protected ctor, should not be called directly.
sprangb1ca0732017-02-01 08:38:12 -0800102QualityScaler::QualityScaler(AdaptationObserverInterface* observer,
kthelgason876222f2016-11-29 01:44:11 -0800103 VideoEncoder::QpThresholds thresholds,
104 int64_t sampling_period)
105 : check_qp_task_(nullptr),
106 observer_(observer),
107 sampling_period_ms_(sampling_period),
108 fast_rampup_(true),
109 // Arbitrarily choose size based on 30 fps for 5 seconds.
110 average_qp_(5 * 30),
111 framedrop_percent_(5 * 30),
112 thresholds_(thresholds) {
113 RTC_DCHECK_CALLED_SEQUENTIALLY(&task_checker_);
114 RTC_DCHECK(observer_ != nullptr);
115 check_qp_task_ = new CheckQPTask(this);
glaznevd1c44352017-03-23 14:40:08 -0700116 LOG(LS_INFO) << "QP thresholds: low: " << thresholds_.low
117 << ", high: " << thresholds_.high;
pbos@webrtc.orga0d78272014-09-12 11:51:47 +0000118}
119
kthelgason876222f2016-11-29 01:44:11 -0800120QualityScaler::~QualityScaler() {
121 RTC_DCHECK_CALLED_SEQUENTIALLY(&task_checker_);
122 check_qp_task_->Stop();
123}
124
125int64_t QualityScaler::GetSamplingPeriodMs() const {
126 RTC_DCHECK_CALLED_SEQUENTIALLY(&task_checker_);
127 return fast_rampup_ ? sampling_period_ms_
128 : (sampling_period_ms_ * kSamplePeriodScaleFactor);
129}
130
131void QualityScaler::ReportDroppedFrame() {
132 RTC_DCHECK_CALLED_SEQUENTIALLY(&task_checker_);
133 framedrop_percent_.AddSample(100);
pbos@webrtc.orga0d78272014-09-12 11:51:47 +0000134}
135
jackychen98d8cf52015-05-21 11:12:02 -0700136void QualityScaler::ReportQP(int qp) {
kthelgason876222f2016-11-29 01:44:11 -0800137 RTC_DCHECK_CALLED_SEQUENTIALLY(&task_checker_);
pbos@webrtc.orga0d78272014-09-12 11:51:47 +0000138 framedrop_percent_.AddSample(0);
kthelgason194f40a2016-09-14 02:14:58 -0700139 average_qp_.AddSample(qp);
pbos@webrtc.orga0d78272014-09-12 11:51:47 +0000140}
141
kthelgason876222f2016-11-29 01:44:11 -0800142void QualityScaler::CheckQP() {
143 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
147 // If we have not observed at least this many frames we can't
148 // make a good scaling decision.
149 if (framedrop_percent_.size() < kMinFramesNeededToScale)
150 return;
151
kthelgason194f40a2016-09-14 02:14:58 -0700152 // Check if we should scale down due to high frame drop.
kthelgason876222f2016-11-29 01:44:11 -0800153 const rtc::Optional<int> drop_rate = framedrop_percent_.GetAverage();
kthelgason194f40a2016-09-14 02:14:58 -0700154 if (drop_rate && *drop_rate >= kFramedropPercentThreshold) {
kthelgason876222f2016-11-29 01:44:11 -0800155 ReportQPHigh();
kthelgason194f40a2016-09-14 02:14:58 -0700156 return;
157 }
158
159 // Check if we should scale up or down based on QP.
kthelgason876222f2016-11-29 01:44:11 -0800160 const rtc::Optional<int> avg_qp = average_qp_.GetAverage();
glaznevd1c44352017-03-23 14:40:08 -0700161 if (avg_qp) {
162 LOG(LS_INFO) << "Checking average QP " << *avg_qp;
163 if (*avg_qp > thresholds_.high) {
164 ReportQPHigh();
165 return;
166 }
167 if (*avg_qp <= thresholds_.low) {
168 // QP has been low. We want to try a higher resolution.
169 ReportQPLow();
170 return;
171 }
kthelgason194f40a2016-09-14 02:14:58 -0700172 }
173}
174
kthelgason876222f2016-11-29 01:44:11 -0800175void QualityScaler::ReportQPLow() {
176 RTC_DCHECK_CALLED_SEQUENTIALLY(&task_checker_);
kthelgason194f40a2016-09-14 02:14:58 -0700177 ClearSamples();
sprangb1ca0732017-02-01 08:38:12 -0800178 observer_->AdaptUp(AdaptationObserverInterface::AdaptReason::kQuality);
kthelgason194f40a2016-09-14 02:14:58 -0700179}
180
kthelgason876222f2016-11-29 01:44:11 -0800181void QualityScaler::ReportQPHigh() {
182 RTC_DCHECK_CALLED_SEQUENTIALLY(&task_checker_);
kthelgason194f40a2016-09-14 02:14:58 -0700183 ClearSamples();
sprangb1ca0732017-02-01 08:38:12 -0800184 observer_->AdaptDown(AdaptationObserverInterface::AdaptReason::kQuality);
kthelgason194f40a2016-09-14 02:14:58 -0700185 // If we've scaled down, wait longer before scaling up again.
186 if (fast_rampup_) {
187 fast_rampup_ = false;
kthelgason194f40a2016-09-14 02:14:58 -0700188 }
jackychen6e2ce6e2015-07-13 16:26:33 -0700189}
pbos@webrtc.orga0d78272014-09-12 11:51:47 +0000190
pbos@webrtc.orga0d78272014-09-12 11:51:47 +0000191void QualityScaler::ClearSamples() {
kthelgason876222f2016-11-29 01:44:11 -0800192 RTC_DCHECK_CALLED_SEQUENTIALLY(&task_checker_);
pbos@webrtc.orga0d78272014-09-12 11:51:47 +0000193 framedrop_percent_.Reset();
kthelgason194f40a2016-09-14 02:14:58 -0700194 average_qp_.Reset();
pboscbac40d2016-04-13 02:51:02 -0700195}
pbos@webrtc.orga0d78272014-09-12 11:51:47 +0000196} // namespace webrtc