Line data Source code
1 : /*
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 : */
10 :
11 : #include "webrtc/modules/video_coding/utility/quality_scaler.h"
12 :
13 : #include <math.h>
14 :
15 : #include <algorithm>
16 : #include <memory>
17 :
18 : #include "webrtc/base/checks.h"
19 : #include "webrtc/base/logging.h"
20 : #include "webrtc/base/task_queue.h"
21 :
22 : // 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
27 :
28 : namespace webrtc {
29 :
30 : namespace {
31 : // Threshold constant used until first downscale (to permit fast rampup).
32 : static const int kMeasureMs = 2000;
33 : static const float kSamplePeriodScaleFactor = 2.5;
34 : static const int kFramedropPercentThreshold = 60;
35 : // QP scaling threshold defaults:
36 : static const int kLowH264QpThreshold = 24;
37 : static 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].
40 : static const int kLowVp8QpThreshold = 29;
41 : static const int kHighVp8QpThreshold = 95;
42 : const ScalingObserverInterface::ScaleReason scale_reason_ =
43 : ScalingObserverInterface::ScaleReason::kQuality;
44 :
45 0 : static VideoEncoder::QpThresholds CodecTypeToDefaultThresholds(
46 : VideoCodecType codec_type) {
47 0 : int low = -1;
48 0 : int high = -1;
49 0 : switch (codec_type) {
50 : case kVideoCodecH264:
51 0 : low = kLowH264QpThreshold;
52 0 : high = kHighH264QpThreshold;
53 0 : break;
54 : case kVideoCodecVP8:
55 0 : low = kLowVp8QpThreshold;
56 0 : high = kHighVp8QpThreshold;
57 0 : break;
58 : default:
59 0 : RTC_NOTREACHED() << "Invalid codec type for QualityScaler.";
60 : }
61 0 : return VideoEncoder::QpThresholds(low, high);
62 : }
63 : } // namespace
64 :
65 0 : class QualityScaler::CheckQPTask : public rtc::QueuedTask {
66 : public:
67 0 : explicit CheckQPTask(QualityScaler* scaler) : scaler_(scaler) {
68 0 : LOG(LS_INFO) << "Created CheckQPTask. Scheduling on queue...";
69 0 : rtc::TaskQueue::Current()->PostDelayedTask(
70 0 : std::unique_ptr<rtc::QueuedTask>(this), scaler_->GetSamplingPeriodMs());
71 0 : }
72 0 : void Stop() {
73 0 : RTC_DCHECK_CALLED_SEQUENTIALLY(&task_checker_);
74 0 : LOG(LS_INFO) << "Stopping QP Check task.";
75 0 : stop_ = true;
76 0 : }
77 :
78 : private:
79 0 : bool Run() override {
80 0 : RTC_DCHECK_CALLED_SEQUENTIALLY(&task_checker_);
81 0 : if (stop_)
82 0 : return true; // TaskQueue will free this task.
83 0 : scaler_->CheckQP();
84 0 : rtc::TaskQueue::Current()->PostDelayedTask(
85 0 : std::unique_ptr<rtc::QueuedTask>(this), scaler_->GetSamplingPeriodMs());
86 0 : 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 :
94 0 : QualityScaler::QualityScaler(ScalingObserverInterface* observer,
95 0 : VideoCodecType codec_type)
96 0 : : QualityScaler(observer, CodecTypeToDefaultThresholds(codec_type)) {}
97 :
98 0 : QualityScaler::QualityScaler(ScalingObserverInterface* observer,
99 0 : VideoEncoder::QpThresholds thresholds)
100 0 : : QualityScaler(observer, thresholds, kMeasureMs) {}
101 :
102 : // Protected ctor, should not be called directly.
103 0 : QualityScaler::QualityScaler(ScalingObserverInterface* observer,
104 : VideoEncoder::QpThresholds thresholds,
105 0 : 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 0 : thresholds_(thresholds) {
114 0 : RTC_DCHECK_CALLED_SEQUENTIALLY(&task_checker_);
115 0 : RTC_DCHECK(observer_ != nullptr);
116 0 : check_qp_task_ = new CheckQPTask(this);
117 0 : }
118 :
119 0 : QualityScaler::~QualityScaler() {
120 0 : RTC_DCHECK_CALLED_SEQUENTIALLY(&task_checker_);
121 0 : check_qp_task_->Stop();
122 0 : }
123 :
124 0 : int64_t QualityScaler::GetSamplingPeriodMs() const {
125 0 : RTC_DCHECK_CALLED_SEQUENTIALLY(&task_checker_);
126 0 : return fast_rampup_ ? sampling_period_ms_
127 0 : : (sampling_period_ms_ * kSamplePeriodScaleFactor);
128 : }
129 :
130 0 : void QualityScaler::ReportDroppedFrame() {
131 0 : RTC_DCHECK_CALLED_SEQUENTIALLY(&task_checker_);
132 0 : framedrop_percent_.AddSample(100);
133 0 : }
134 :
135 0 : void QualityScaler::ReportQP(int qp) {
136 0 : RTC_DCHECK_CALLED_SEQUENTIALLY(&task_checker_);
137 0 : framedrop_percent_.AddSample(0);
138 0 : average_qp_.AddSample(qp);
139 0 : }
140 :
141 0 : void QualityScaler::CheckQP() {
142 0 : RTC_DCHECK_CALLED_SEQUENTIALLY(&task_checker_);
143 : // Should be set through InitEncode -> Should be set by now.
144 0 : RTC_DCHECK_GE(thresholds_.low, 0);
145 0 : LOG(LS_INFO) << "Checking if average QP exceeds threshold";
146 : // Check if we should scale down due to high frame drop.
147 0 : const rtc::Optional<int> drop_rate = framedrop_percent_.GetAverage();
148 0 : if (drop_rate && *drop_rate >= kFramedropPercentThreshold) {
149 0 : ReportQPHigh();
150 0 : return;
151 : }
152 :
153 : // Check if we should scale up or down based on QP.
154 0 : const rtc::Optional<int> avg_qp = average_qp_.GetAverage();
155 0 : if (avg_qp && *avg_qp > thresholds_.high) {
156 0 : ReportQPHigh();
157 0 : return;
158 : }
159 0 : if (avg_qp && *avg_qp <= thresholds_.low) {
160 : // QP has been low. We want to try a higher resolution.
161 0 : ReportQPLow();
162 0 : return;
163 : }
164 : }
165 :
166 0 : void QualityScaler::ReportQPLow() {
167 0 : RTC_DCHECK_CALLED_SEQUENTIALLY(&task_checker_);
168 0 : LOG(LS_INFO) << "QP has been low, asking for higher resolution.";
169 0 : ClearSamples();
170 0 : observer_->ScaleUp(scale_reason_);
171 0 : }
172 :
173 0 : void QualityScaler::ReportQPHigh() {
174 0 : RTC_DCHECK_CALLED_SEQUENTIALLY(&task_checker_);
175 0 : LOG(LS_INFO) << "QP has been high , asking for lower resolution.";
176 0 : ClearSamples();
177 0 : observer_->ScaleDown(scale_reason_);
178 : // If we've scaled down, wait longer before scaling up again.
179 0 : if (fast_rampup_) {
180 0 : fast_rampup_ = false;
181 : }
182 0 : }
183 :
184 0 : void QualityScaler::ClearSamples() {
185 0 : RTC_DCHECK_CALLED_SEQUENTIALLY(&task_checker_);
186 0 : framedrop_percent_.Reset();
187 0 : average_qp_.Reset();
188 0 : }
189 : } // namespace webrtc
|