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 #include "webrtc/modules/video_coding/utility/quality_scaler.h"
11
12 namespace webrtc {
13
14 static const int kMinFps = 10;
15 static const int kMeasureSeconds = 5;
16 static const int kFramedropPercentThreshold = 60;
17
18 const int QualityScaler::kDefaultLowQpDenominator = 3;
19 // Note that this is the same for width and height to permit 120x90 in both
20 // portrait and landscape mode.
21 const int QualityScaler::kDefaultMinDownscaleDimension = 90;
22
QualityScaler()23 QualityScaler::QualityScaler()
24 : num_samples_(0),
25 low_qp_threshold_(-1),
26 downscale_shift_(0),
27 framerate_down_(false),
28 min_width_(kDefaultMinDownscaleDimension),
29 min_height_(kDefaultMinDownscaleDimension) {}
30
Init(int low_qp_threshold,int high_qp_threshold,bool use_framerate_reduction)31 void QualityScaler::Init(int low_qp_threshold,
32 int high_qp_threshold,
33 bool use_framerate_reduction) {
34 ClearSamples();
35 low_qp_threshold_ = low_qp_threshold;
36 high_qp_threshold_ = high_qp_threshold;
37 use_framerate_reduction_ = use_framerate_reduction;
38 target_framerate_ = -1;
39 }
40
SetMinResolution(int min_width,int min_height)41 void QualityScaler::SetMinResolution(int min_width, int min_height) {
42 min_width_ = min_width;
43 min_height_ = min_height;
44 }
45
46 // Report framerate(fps) to estimate # of samples.
ReportFramerate(int framerate)47 void QualityScaler::ReportFramerate(int framerate) {
48 num_samples_ = static_cast<size_t>(
49 kMeasureSeconds * (framerate < kMinFps ? kMinFps : framerate));
50 framerate_ = framerate;
51 }
52
ReportQP(int qp)53 void QualityScaler::ReportQP(int qp) {
54 framedrop_percent_.AddSample(0);
55 average_qp_.AddSample(qp);
56 }
57
ReportDroppedFrame()58 void QualityScaler::ReportDroppedFrame() {
59 framedrop_percent_.AddSample(100);
60 }
61
OnEncodeFrame(const VideoFrame & frame)62 void QualityScaler::OnEncodeFrame(const VideoFrame& frame) {
63 // Should be set through InitEncode -> Should be set by now.
64 assert(low_qp_threshold_ >= 0);
65 assert(num_samples_ > 0);
66 res_.width = frame.width();
67 res_.height = frame.height();
68
69 // Update scale factor.
70 int avg_drop = 0;
71 int avg_qp = 0;
72
73 // When encoder consistently overshoots, framerate reduction and spatial
74 // resizing will be triggered to get a smoother video.
75 if ((framedrop_percent_.GetAverage(num_samples_, &avg_drop) &&
76 avg_drop >= kFramedropPercentThreshold) ||
77 (average_qp_.GetAverage(num_samples_, &avg_qp) &&
78 avg_qp > high_qp_threshold_)) {
79 // Reducing frame rate before spatial resolution change.
80 // Reduce frame rate only when it is above a certain number.
81 // Only one reduction is allowed for now.
82 // TODO(jackychen): Allow more than one framerate reduction.
83 if (use_framerate_reduction_ && !framerate_down_ && framerate_ >= 20) {
84 target_framerate_ = framerate_ / 2;
85 framerate_down_ = true;
86 // If frame rate has been updated, clear the buffer. We don't want
87 // spatial resolution to change right after frame rate change.
88 ClearSamples();
89 } else {
90 AdjustScale(false);
91 }
92 } else if (average_qp_.GetAverage(num_samples_, &avg_qp) &&
93 avg_qp <= low_qp_threshold_) {
94 if (use_framerate_reduction_ && framerate_down_) {
95 target_framerate_ = -1;
96 framerate_down_ = false;
97 ClearSamples();
98 } else {
99 AdjustScale(true);
100 }
101 }
102
103 assert(downscale_shift_ >= 0);
104 for (int shift = downscale_shift_;
105 shift > 0 && (res_.width / 2 >= min_width_) &&
106 (res_.height / 2 >= min_height_);
107 --shift) {
108 res_.width /= 2;
109 res_.height /= 2;
110 }
111 }
112
GetScaledResolution() const113 QualityScaler::Resolution QualityScaler::GetScaledResolution() const {
114 return res_;
115 }
116
GetTargetFramerate() const117 int QualityScaler::GetTargetFramerate() const {
118 return target_framerate_;
119 }
120
GetScaledFrame(const VideoFrame & frame)121 const VideoFrame& QualityScaler::GetScaledFrame(const VideoFrame& frame) {
122 Resolution res = GetScaledResolution();
123 if (res.width == frame.width())
124 return frame;
125
126 scaler_.Set(frame.width(), frame.height(), res.width, res.height, kI420,
127 kI420, kScaleBox);
128 if (scaler_.Scale(frame, &scaled_frame_) != 0)
129 return frame;
130
131 scaled_frame_.set_ntp_time_ms(frame.ntp_time_ms());
132 scaled_frame_.set_timestamp(frame.timestamp());
133 scaled_frame_.set_render_time_ms(frame.render_time_ms());
134
135 return scaled_frame_;
136 }
137
ClearSamples()138 void QualityScaler::ClearSamples() {
139 framedrop_percent_.Reset();
140 average_qp_.Reset();
141 }
142
AdjustScale(bool up)143 void QualityScaler::AdjustScale(bool up) {
144 downscale_shift_ += up ? -1 : 1;
145 if (downscale_shift_ < 0)
146 downscale_shift_ = 0;
147 ClearSamples();
148 }
149
150 } // namespace webrtc
151