blob: 9aae17c6b6684d793adbd8dd38f052a54cd16219 [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 */
kjellander@webrtc.orgb7ce9642015-11-18 23:04:10 +010010#include "webrtc/modules/video_coding/utility/quality_scaler.h"
pbos@webrtc.orga0d78272014-09-12 11:51:47 +000011
12namespace webrtc {
13
14static const int kMinFps = 10;
15static const int kMeasureSeconds = 5;
16static const int kFramedropPercentThreshold = 60;
pbos@webrtc.orga0d78272014-09-12 11:51:47 +000017
Peter Boström6a688f52015-06-22 08:02:58 +020018const 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.
21const int QualityScaler::kDefaultMinDownscaleDimension = 90;
22
pbos@webrtc.orga0d78272014-09-12 11:51:47 +000023QualityScaler::QualityScaler()
Peter Boström6a688f52015-06-22 08:02:58 +020024 : num_samples_(0),
25 low_qp_threshold_(-1),
26 downscale_shift_(0),
jackychen6e2ce6e2015-07-13 16:26:33 -070027 framerate_down_(false),
Peter Boström6a688f52015-06-22 08:02:58 +020028 min_width_(kDefaultMinDownscaleDimension),
29 min_height_(kDefaultMinDownscaleDimension) {
pbos@webrtc.orga0d78272014-09-12 11:51:47 +000030}
31
Peter Boström17417702015-09-25 17:03:26 +020032void QualityScaler::Init(int low_qp_threshold,
33 int high_qp_threshold,
34 bool use_framerate_reduction) {
pbos@webrtc.orga0d78272014-09-12 11:51:47 +000035 ClearSamples();
jackychen98d8cf52015-05-21 11:12:02 -070036 low_qp_threshold_ = low_qp_threshold;
Peter Boström17417702015-09-25 17:03:26 +020037 high_qp_threshold_ = high_qp_threshold;
jackychen6e2ce6e2015-07-13 16:26:33 -070038 use_framerate_reduction_ = use_framerate_reduction;
39 target_framerate_ = -1;
pbos@webrtc.orga0d78272014-09-12 11:51:47 +000040}
41
jackychen61b4d512015-04-21 15:30:11 -070042void QualityScaler::SetMinResolution(int min_width, int min_height) {
43 min_width_ = min_width;
44 min_height_ = min_height;
45}
46
jackychen98d8cf52015-05-21 11:12:02 -070047// Report framerate(fps) to estimate # of samples.
pbos@webrtc.orga0d78272014-09-12 11:51:47 +000048void QualityScaler::ReportFramerate(int framerate) {
49 num_samples_ = static_cast<size_t>(
50 kMeasureSeconds * (framerate < kMinFps ? kMinFps : framerate));
jackychen6e2ce6e2015-07-13 16:26:33 -070051 framerate_ = framerate;
pbos@webrtc.orga0d78272014-09-12 11:51:47 +000052}
53
jackychen98d8cf52015-05-21 11:12:02 -070054void QualityScaler::ReportQP(int qp) {
pbos@webrtc.orga0d78272014-09-12 11:51:47 +000055 framedrop_percent_.AddSample(0);
jackychen98d8cf52015-05-21 11:12:02 -070056 average_qp_.AddSample(qp);
pbos@webrtc.orga0d78272014-09-12 11:51:47 +000057}
58
59void QualityScaler::ReportDroppedFrame() {
60 framedrop_percent_.AddSample(100);
61}
62
jackychen6e2ce6e2015-07-13 16:26:33 -070063void QualityScaler::OnEncodeFrame(const VideoFrame& frame) {
jackychen61b4d512015-04-21 15:30:11 -070064 // Should be set through InitEncode -> Should be set by now.
pbos@webrtc.orga0d78272014-09-12 11:51:47 +000065 assert(low_qp_threshold_ >= 0);
66 assert(num_samples_ > 0);
jackychen6e2ce6e2015-07-13 16:26:33 -070067 res_.width = frame.width();
68 res_.height = frame.height();
pbos@webrtc.orga0d78272014-09-12 11:51:47 +000069
jackychen61b4d512015-04-21 15:30:11 -070070 // Update scale factor.
jackychen5af6d472015-05-21 14:11:36 -070071 int avg_drop = 0;
72 int avg_qp = 0;
jackychen6e2ce6e2015-07-13 16:26:33 -070073
74 // When encoder consistently overshoots, framerate reduction and spatial
75 // resizing will be triggered to get a smoother video.
Peter Boström17417702015-09-25 17:03:26 +020076 if ((framedrop_percent_.GetAverage(num_samples_, &avg_drop) &&
77 avg_drop >= kFramedropPercentThreshold) ||
78 (average_qp_.GetAverage(num_samples_, &avg_qp) &&
79 avg_qp > high_qp_threshold_)) {
jackychen6e2ce6e2015-07-13 16:26:33 -070080 // Reducing frame rate before spatial resolution change.
81 // Reduce frame rate only when it is above a certain number.
82 // Only one reduction is allowed for now.
83 // TODO(jackychen): Allow more than one framerate reduction.
84 if (use_framerate_reduction_ && !framerate_down_ && framerate_ >= 20) {
85 target_framerate_ = framerate_ / 2;
86 framerate_down_ = true;
87 // If frame rate has been updated, clear the buffer. We don't want
88 // spatial resolution to change right after frame rate change.
89 ClearSamples();
90 } else {
91 AdjustScale(false);
92 }
jackychen98d8cf52015-05-21 11:12:02 -070093 } else if (average_qp_.GetAverage(num_samples_, &avg_qp) &&
94 avg_qp <= low_qp_threshold_) {
jackychen6e2ce6e2015-07-13 16:26:33 -070095 if (use_framerate_reduction_ && framerate_down_) {
96 target_framerate_ = -1;
97 framerate_down_ = false;
98 ClearSamples();
99 } else {
100 AdjustScale(true);
101 }
jackychen61b4d512015-04-21 15:30:11 -0700102 }
103
pbos@webrtc.orga0d78272014-09-12 11:51:47 +0000104 assert(downscale_shift_ >= 0);
105 for (int shift = downscale_shift_;
jackychen6e2ce6e2015-07-13 16:26:33 -0700106 shift > 0 && (res_.width / 2 >= min_width_) &&
107 (res_.height / 2 >= min_height_);
pbos@webrtc.orga0d78272014-09-12 11:51:47 +0000108 --shift) {
jackychen6e2ce6e2015-07-13 16:26:33 -0700109 res_.width /= 2;
110 res_.height /= 2;
pbos@webrtc.orga0d78272014-09-12 11:51:47 +0000111 }
jackychen6e2ce6e2015-07-13 16:26:33 -0700112}
pbos@webrtc.orga0d78272014-09-12 11:51:47 +0000113
jackychen6e2ce6e2015-07-13 16:26:33 -0700114QualityScaler::Resolution QualityScaler::GetScaledResolution() const {
115 return res_;
116}
117
118int QualityScaler::GetTargetFramerate() const {
119 return target_framerate_;
pbos@webrtc.orga0d78272014-09-12 11:51:47 +0000120}
121
Miguel Casas-Sanchez47650702015-05-29 17:21:40 -0700122const VideoFrame& QualityScaler::GetScaledFrame(const VideoFrame& frame) {
jackychen6e2ce6e2015-07-13 16:26:33 -0700123 Resolution res = GetScaledResolution();
pbos@webrtc.orga0d78272014-09-12 11:51:47 +0000124 if (res.width == frame.width())
125 return frame;
126
127 scaler_.Set(frame.width(),
128 frame.height(),
129 res.width,
130 res.height,
131 kI420,
132 kI420,
133 kScaleBox);
134 if (scaler_.Scale(frame, &scaled_frame_) != 0)
135 return frame;
136
137 scaled_frame_.set_ntp_time_ms(frame.ntp_time_ms());
138 scaled_frame_.set_timestamp(frame.timestamp());
139 scaled_frame_.set_render_time_ms(frame.render_time_ms());
140
141 return scaled_frame_;
142}
143
pbos@webrtc.orga0d78272014-09-12 11:51:47 +0000144void QualityScaler::ClearSamples() {
pbos@webrtc.orga0d78272014-09-12 11:51:47 +0000145 framedrop_percent_.Reset();
jackychen98d8cf52015-05-21 11:12:02 -0700146 average_qp_.Reset();
pbos@webrtc.orga0d78272014-09-12 11:51:47 +0000147}
148
149void QualityScaler::AdjustScale(bool up) {
150 downscale_shift_ += up ? -1 : 1;
151 if (downscale_shift_ < 0)
152 downscale_shift_ = 0;
153 ClearSamples();
154}
155
156} // namespace webrtc