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 namespace webrtc {
14
15 static const int kMinFps = 10;
16 static const int kMeasureSeconds = 5;
17 static const int kFramedropPercentThreshold = 60;
18 static const int kLowQpThresholdDenominator = 3;
19
QualityScaler()20 QualityScaler::QualityScaler()
21 : num_samples_(0), low_qp_threshold_(-1), downscale_shift_(0) {
22 }
23
Init(int max_qp)24 void QualityScaler::Init(int max_qp) {
25 ClearSamples();
26 downscale_shift_ = 0;
27 low_qp_threshold_ = max_qp / kLowQpThresholdDenominator ;
28 }
29
ReportFramerate(int framerate)30 void QualityScaler::ReportFramerate(int framerate) {
31 num_samples_ = static_cast<size_t>(
32 kMeasureSeconds * (framerate < kMinFps ? kMinFps : framerate));
33 }
34
ReportEncodedFrame(int qp)35 void QualityScaler::ReportEncodedFrame(int qp) {
36 average_qp_.AddSample(qp);
37 framedrop_percent_.AddSample(0);
38 }
39
ReportDroppedFrame()40 void QualityScaler::ReportDroppedFrame() {
41 framedrop_percent_.AddSample(100);
42 }
43
GetScaledResolution(const I420VideoFrame & frame)44 QualityScaler::Resolution QualityScaler::GetScaledResolution(
45 const I420VideoFrame& frame) {
46 // Both of these should be set through InitEncode -> Should be set by now.
47 assert(low_qp_threshold_ >= 0);
48 assert(num_samples_ > 0);
49 // Update scale factor.
50 int avg;
51 if (framedrop_percent_.GetAverage(num_samples_, &avg) &&
52 avg >= kFramedropPercentThreshold) {
53 AdjustScale(false);
54 } else if (average_qp_.GetAverage(num_samples_, &avg) &&
55 avg <= low_qp_threshold_) {
56 AdjustScale(true);
57 }
58
59 Resolution res;
60 res.width = frame.width();
61 res.height = frame.height();
62
63 assert(downscale_shift_ >= 0);
64 for (int shift = downscale_shift_;
65 shift > 0 && res.width > 1 && res.height > 1;
66 --shift) {
67 res.width >>= 1;
68 res.height >>= 1;
69 }
70
71 return res;
72 }
73
GetScaledFrame(const I420VideoFrame & frame)74 const I420VideoFrame& QualityScaler::GetScaledFrame(
75 const I420VideoFrame& frame) {
76 Resolution res = GetScaledResolution(frame);
77 if (res.width == frame.width())
78 return frame;
79
80 scaler_.Set(frame.width(),
81 frame.height(),
82 res.width,
83 res.height,
84 kI420,
85 kI420,
86 kScaleBox);
87 if (scaler_.Scale(frame, &scaled_frame_) != 0)
88 return frame;
89
90 scaled_frame_.set_ntp_time_ms(frame.ntp_time_ms());
91 scaled_frame_.set_timestamp(frame.timestamp());
92 scaled_frame_.set_render_time_ms(frame.render_time_ms());
93
94 return scaled_frame_;
95 }
96
MovingAverage()97 QualityScaler::MovingAverage::MovingAverage() : sum_(0) {
98 }
99
AddSample(int sample)100 void QualityScaler::MovingAverage::AddSample(int sample) {
101 samples_.push_back(sample);
102 sum_ += sample;
103 }
104
GetAverage(size_t num_samples,int * avg)105 bool QualityScaler::MovingAverage::GetAverage(size_t num_samples, int* avg) {
106 assert(num_samples > 0);
107 if (num_samples > samples_.size())
108 return false;
109
110 // Remove old samples.
111 while (num_samples < samples_.size()) {
112 sum_ -= samples_.front();
113 samples_.pop_front();
114 }
115
116 *avg = sum_ / static_cast<int>(num_samples);
117 return true;
118 }
119
Reset()120 void QualityScaler::MovingAverage::Reset() {
121 sum_ = 0;
122 samples_.clear();
123 }
124
ClearSamples()125 void QualityScaler::ClearSamples() {
126 average_qp_.Reset();
127 framedrop_percent_.Reset();
128 }
129
AdjustScale(bool up)130 void QualityScaler::AdjustScale(bool up) {
131 downscale_shift_ += up ? -1 : 1;
132 if (downscale_shift_ < 0)
133 downscale_shift_ = 0;
134 ClearSamples();
135 }
136
137 } // namespace webrtc
138