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