blob: ba1d9789c783230b5cf48fad2699a723669ae411 [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;
kthelgason876222f2016-11-29 01:44:11 -080042const ScalingObserverInterface::ScaleReason scale_reason_ =
43 ScalingObserverInterface::ScaleReason::kQuality;
pbos@webrtc.orga0d78272014-09-12 11:51:47 +000044
kthelgason876222f2016-11-29 01:44:11 -080045static VideoEncoder::QpThresholds CodecTypeToDefaultThresholds(
46 VideoCodecType codec_type) {
47 int low = -1;
48 int high = -1;
kthelgason478681e2016-09-28 08:17:43 -070049 switch (codec_type) {
50 case kVideoCodecH264:
51 low = kLowH264QpThreshold;
52 high = kHighH264QpThreshold;
53 break;
54 case kVideoCodecVP8:
55 low = kLowVp8QpThreshold;
56 high = kHighVp8QpThreshold;
57 break;
58 default:
59 RTC_NOTREACHED() << "Invalid codec type for QualityScaler.";
60 }
kthelgason876222f2016-11-29 01:44:11 -080061 return VideoEncoder::QpThresholds(low, high);
kthelgason478681e2016-09-28 08:17:43 -070062}
kthelgason876222f2016-11-29 01:44:11 -080063} // namespace
kthelgason478681e2016-09-28 08:17:43 -070064
kthelgason876222f2016-11-29 01:44:11 -080065class QualityScaler::CheckQPTask : public rtc::QueuedTask {
66 public:
67 explicit CheckQPTask(QualityScaler* scaler) : scaler_(scaler) {
68 LOG(LS_INFO) << "Created CheckQPTask. Scheduling on queue...";
69 rtc::TaskQueue::Current()->PostDelayedTask(
70 std::unique_ptr<rtc::QueuedTask>(this), scaler_->GetSamplingPeriodMs());
Alex Glazneva9d08922016-02-19 15:24:06 -080071 }
kthelgason876222f2016-11-29 01:44:11 -080072 void Stop() {
73 RTC_DCHECK_CALLED_SEQUENTIALLY(&task_checker_);
74 LOG(LS_INFO) << "Stopping QP Check task.";
75 stop_ = true;
76 }
77
78 private:
79 bool Run() override {
80 RTC_DCHECK_CALLED_SEQUENTIALLY(&task_checker_);
81 if (stop_)
82 return true; // TaskQueue will free this task.
83 scaler_->CheckQP();
84 rtc::TaskQueue::Current()->PostDelayedTask(
85 std::unique_ptr<rtc::QueuedTask>(this), scaler_->GetSamplingPeriodMs());
86 return false; // Retain the task in order to reuse it.
87 }
88
89 QualityScaler* const scaler_;
90 bool stop_ = false;
91 rtc::SequencedTaskChecker task_checker_;
92};
93
94QualityScaler::QualityScaler(ScalingObserverInterface* observer,
95 VideoCodecType codec_type)
96 : QualityScaler(observer, CodecTypeToDefaultThresholds(codec_type)) {}
97
98QualityScaler::QualityScaler(ScalingObserverInterface* observer,
99 VideoEncoder::QpThresholds thresholds)
100 : QualityScaler(observer, thresholds, kMeasureMs) {}
101
102// Protected ctor, should not be called directly.
103QualityScaler::QualityScaler(ScalingObserverInterface* observer,
104 VideoEncoder::QpThresholds thresholds,
105 int64_t sampling_period)
106 : check_qp_task_(nullptr),
107 observer_(observer),
108 sampling_period_ms_(sampling_period),
109 fast_rampup_(true),
110 // Arbitrarily choose size based on 30 fps for 5 seconds.
111 average_qp_(5 * 30),
112 framedrop_percent_(5 * 30),
113 thresholds_(thresholds) {
114 RTC_DCHECK_CALLED_SEQUENTIALLY(&task_checker_);
115 RTC_DCHECK(observer_ != nullptr);
116 check_qp_task_ = new CheckQPTask(this);
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);
145 LOG(LS_INFO) << "Checking if average QP exceeds threshold";
kthelgason194f40a2016-09-14 02:14:58 -0700146 // Check if we should scale down due to high frame drop.
kthelgason876222f2016-11-29 01:44:11 -0800147 const rtc::Optional<int> drop_rate = framedrop_percent_.GetAverage();
kthelgason194f40a2016-09-14 02:14:58 -0700148 if (drop_rate && *drop_rate >= kFramedropPercentThreshold) {
kthelgason876222f2016-11-29 01:44:11 -0800149 ReportQPHigh();
kthelgason194f40a2016-09-14 02:14:58 -0700150 return;
151 }
152
153 // Check if we should scale up or down based on QP.
kthelgason876222f2016-11-29 01:44:11 -0800154 const rtc::Optional<int> avg_qp = average_qp_.GetAverage();
155 if (avg_qp && *avg_qp > thresholds_.high) {
156 ReportQPHigh();
kthelgason194f40a2016-09-14 02:14:58 -0700157 return;
158 }
kthelgason876222f2016-11-29 01:44:11 -0800159 if (avg_qp && *avg_qp <= thresholds_.low) {
kthelgason194f40a2016-09-14 02:14:58 -0700160 // QP has been low. We want to try a higher resolution.
kthelgason876222f2016-11-29 01:44:11 -0800161 ReportQPLow();
kthelgason194f40a2016-09-14 02:14:58 -0700162 return;
163 }
164}
165
kthelgason876222f2016-11-29 01:44:11 -0800166void QualityScaler::ReportQPLow() {
167 RTC_DCHECK_CALLED_SEQUENTIALLY(&task_checker_);
168 LOG(LS_INFO) << "QP has been low, asking for higher resolution.";
kthelgason194f40a2016-09-14 02:14:58 -0700169 ClearSamples();
kthelgason876222f2016-11-29 01:44:11 -0800170 observer_->ScaleUp(scale_reason_);
kthelgason194f40a2016-09-14 02:14:58 -0700171}
172
kthelgason876222f2016-11-29 01:44:11 -0800173void QualityScaler::ReportQPHigh() {
174 RTC_DCHECK_CALLED_SEQUENTIALLY(&task_checker_);
175 LOG(LS_INFO) << "QP has been high , asking for lower resolution.";
kthelgason194f40a2016-09-14 02:14:58 -0700176 ClearSamples();
kthelgason876222f2016-11-29 01:44:11 -0800177 observer_->ScaleDown(scale_reason_);
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