blob: 3dd3dde93a92b0a7f84357a20e1e6b6db95a3d8e [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);
pbos@webrtc.orga0d78272014-09-12 11:51:47 +0000115}
116
kthelgason876222f2016-11-29 01:44:11 -0800117QualityScaler::~QualityScaler() {
118 RTC_DCHECK_CALLED_SEQUENTIALLY(&task_checker_);
119 check_qp_task_->Stop();
120}
121
122int64_t QualityScaler::GetSamplingPeriodMs() const {
123 RTC_DCHECK_CALLED_SEQUENTIALLY(&task_checker_);
124 return fast_rampup_ ? sampling_period_ms_
125 : (sampling_period_ms_ * kSamplePeriodScaleFactor);
126}
127
128void QualityScaler::ReportDroppedFrame() {
129 RTC_DCHECK_CALLED_SEQUENTIALLY(&task_checker_);
130 framedrop_percent_.AddSample(100);
pbos@webrtc.orga0d78272014-09-12 11:51:47 +0000131}
132
jackychen98d8cf52015-05-21 11:12:02 -0700133void QualityScaler::ReportQP(int qp) {
kthelgason876222f2016-11-29 01:44:11 -0800134 RTC_DCHECK_CALLED_SEQUENTIALLY(&task_checker_);
pbos@webrtc.orga0d78272014-09-12 11:51:47 +0000135 framedrop_percent_.AddSample(0);
kthelgason194f40a2016-09-14 02:14:58 -0700136 average_qp_.AddSample(qp);
pbos@webrtc.orga0d78272014-09-12 11:51:47 +0000137}
138
kthelgason876222f2016-11-29 01:44:11 -0800139void QualityScaler::CheckQP() {
140 RTC_DCHECK_CALLED_SEQUENTIALLY(&task_checker_);
jackychen61b4d512015-04-21 15:30:11 -0700141 // Should be set through InitEncode -> Should be set by now.
kthelgason876222f2016-11-29 01:44:11 -0800142 RTC_DCHECK_GE(thresholds_.low, 0);
143 LOG(LS_INFO) << "Checking if average QP exceeds threshold";
kthelgason194f40a2016-09-14 02:14:58 -0700144 // Check if we should scale down due to high frame drop.
kthelgason876222f2016-11-29 01:44:11 -0800145 const rtc::Optional<int> drop_rate = framedrop_percent_.GetAverage();
kthelgason194f40a2016-09-14 02:14:58 -0700146 if (drop_rate && *drop_rate >= kFramedropPercentThreshold) {
kthelgason876222f2016-11-29 01:44:11 -0800147 ReportQPHigh();
kthelgason194f40a2016-09-14 02:14:58 -0700148 return;
149 }
150
151 // Check if we should scale up or down based on QP.
kthelgason876222f2016-11-29 01:44:11 -0800152 const rtc::Optional<int> avg_qp = average_qp_.GetAverage();
153 if (avg_qp && *avg_qp > thresholds_.high) {
154 ReportQPHigh();
kthelgason194f40a2016-09-14 02:14:58 -0700155 return;
156 }
kthelgason876222f2016-11-29 01:44:11 -0800157 if (avg_qp && *avg_qp <= thresholds_.low) {
kthelgason194f40a2016-09-14 02:14:58 -0700158 // QP has been low. We want to try a higher resolution.
kthelgason876222f2016-11-29 01:44:11 -0800159 ReportQPLow();
kthelgason194f40a2016-09-14 02:14:58 -0700160 return;
161 }
162}
163
kthelgason876222f2016-11-29 01:44:11 -0800164void QualityScaler::ReportQPLow() {
165 RTC_DCHECK_CALLED_SEQUENTIALLY(&task_checker_);
166 LOG(LS_INFO) << "QP has been low, asking for higher resolution.";
kthelgason194f40a2016-09-14 02:14:58 -0700167 ClearSamples();
sprangb1ca0732017-02-01 08:38:12 -0800168 observer_->AdaptUp(AdaptationObserverInterface::AdaptReason::kQuality);
kthelgason194f40a2016-09-14 02:14:58 -0700169}
170
kthelgason876222f2016-11-29 01:44:11 -0800171void QualityScaler::ReportQPHigh() {
172 RTC_DCHECK_CALLED_SEQUENTIALLY(&task_checker_);
173 LOG(LS_INFO) << "QP has been high , asking for lower resolution.";
kthelgason194f40a2016-09-14 02:14:58 -0700174 ClearSamples();
sprangb1ca0732017-02-01 08:38:12 -0800175 observer_->AdaptDown(AdaptationObserverInterface::AdaptReason::kQuality);
kthelgason194f40a2016-09-14 02:14:58 -0700176 // If we've scaled down, wait longer before scaling up again.
177 if (fast_rampup_) {
178 fast_rampup_ = false;
kthelgason194f40a2016-09-14 02:14:58 -0700179 }
jackychen6e2ce6e2015-07-13 16:26:33 -0700180}
pbos@webrtc.orga0d78272014-09-12 11:51:47 +0000181
pbos@webrtc.orga0d78272014-09-12 11:51:47 +0000182void QualityScaler::ClearSamples() {
kthelgason876222f2016-11-29 01:44:11 -0800183 RTC_DCHECK_CALLED_SEQUENTIALLY(&task_checker_);
pbos@webrtc.orga0d78272014-09-12 11:51:47 +0000184 framedrop_percent_.Reset();
kthelgason194f40a2016-09-14 02:14:58 -0700185 average_qp_.Reset();
pboscbac40d2016-04-13 02:51:02 -0700186}
pbos@webrtc.orga0d78272014-09-12 11:51:47 +0000187} // namespace webrtc