blob: 151a0a2c91a7d761f25504fac4d85d2e4891e242 [file] [log] [blame]
mflodman@webrtc.orge6168f52013-06-26 11:23:01 +00001/*
2 * Copyright (c) 2013 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/video_engine/overuse_frame_detector.h"
12
mflodman@webrtc.orgd4412fe2013-07-31 16:42:21 +000013#include <assert.h>
14
mflodman@webrtc.orge6168f52013-06-26 11:23:01 +000015#include "webrtc/system_wrappers/interface/clock.h"
16#include "webrtc/system_wrappers/interface/critical_section_wrapper.h"
mflodman@webrtc.org6879c8a2013-07-23 11:35:00 +000017#include "webrtc/video_engine/include/vie_base.h"
mflodman@webrtc.orge6168f52013-06-26 11:23:01 +000018
19namespace webrtc {
20
mflodman@webrtc.orgd4412fe2013-07-31 16:42:21 +000021// TODO(mflodman) Test different values for all of these to trigger correctly,
22// avoid fluctuations etc.
23
24namespace {
25// Interval for 'Process' to be called.
mflodman@webrtc.orge6168f52013-06-26 11:23:01 +000026const int64_t kProcessIntervalMs = 2000;
mflodman@webrtc.orgd4412fe2013-07-31 16:42:21 +000027
28// Duration capture and encode samples are valid.
mflodman@webrtc.orge6168f52013-06-26 11:23:01 +000029const int kOveruseHistoryMs = 5000;
mflodman@webrtc.orgd4412fe2013-07-31 16:42:21 +000030
31// The minimum history to trigger an overuse or underuse.
32const int64_t kMinValidHistoryMs = kOveruseHistoryMs / 2;
33
34// Encode / capture ratio to decide an overuse.
mflodman@webrtc.orge6168f52013-06-26 11:23:01 +000035const float kMinEncodeRatio = 29 / 30.0f;
mflodman@webrtc.orgd4412fe2013-07-31 16:42:21 +000036
37// Minimum time between two callbacks.
mflodman@webrtc.org6879c8a2013-07-23 11:35:00 +000038const int kMinCallbackDeltaMs = 30000;
mflodman@webrtc.orge6168f52013-06-26 11:23:01 +000039
mflodman@webrtc.orgd4412fe2013-07-31 16:42:21 +000040// Safety margin between encode time for different resolutions to decide if we
41// can trigger an underuse callback.
42// TODO(mflodman): This should be improved, e.g. test time per pixel?
43const float kIncreaseThreshold = 1.5f;
44} // namespace
45
mflodman@webrtc.org6879c8a2013-07-23 11:35:00 +000046OveruseFrameDetector::OveruseFrameDetector(Clock* clock)
mflodman@webrtc.orge6168f52013-06-26 11:23:01 +000047 : crit_(CriticalSectionWrapper::CreateCriticalSection()),
mflodman@webrtc.org6879c8a2013-07-23 11:35:00 +000048 observer_(NULL),
mflodman@webrtc.orge6168f52013-06-26 11:23:01 +000049 clock_(clock),
mflodman@webrtc.org6879c8a2013-07-23 11:35:00 +000050 last_process_time_(clock->TimeInMilliseconds()),
mflodman@webrtc.orgd4412fe2013-07-31 16:42:21 +000051 last_callback_time_(clock->TimeInMilliseconds()),
52 underuse_encode_timing_enabled_(false),
53 num_pixels_(0),
54 max_num_pixels_(0) {
mflodman@webrtc.orge6168f52013-06-26 11:23:01 +000055}
56
57OveruseFrameDetector::~OveruseFrameDetector() {
58}
59
mflodman@webrtc.org6879c8a2013-07-23 11:35:00 +000060void OveruseFrameDetector::SetObserver(CpuOveruseObserver* observer) {
61 CriticalSectionScoped cs(crit_.get());
62 observer_ = observer;
63}
64
mflodman@webrtc.orgd4412fe2013-07-31 16:42:21 +000065void OveruseFrameDetector::set_underuse_encode_timing_enabled(bool enable) {
mflodman@webrtc.orge6168f52013-06-26 11:23:01 +000066 CriticalSectionScoped cs(crit_.get());
mflodman@webrtc.orgd4412fe2013-07-31 16:42:21 +000067 underuse_encode_timing_enabled_ = enable;
68}
69
70void OveruseFrameDetector::FrameCaptured() {
71 CriticalSectionScoped cs(crit_.get());
mflodman@webrtc.orge6168f52013-06-26 11:23:01 +000072 capture_times_.push_back(clock_->TimeInMilliseconds());
73}
74
mflodman@webrtc.orgd4412fe2013-07-31 16:42:21 +000075void OveruseFrameDetector::FrameEncoded(int64_t encode_time, size_t width,
76 size_t height) {
77 assert(encode_time >= 0);
mflodman@webrtc.orge6168f52013-06-26 11:23:01 +000078 CriticalSectionScoped cs(crit_.get());
mflodman@webrtc.orgd4412fe2013-07-31 16:42:21 +000079 // The frame is disregarded in case of a reset, to startup in a fresh state.
80 if (MaybeResetResolution(width, height))
81 return;
82
83 encode_times_.push_back(std::make_pair(clock_->TimeInMilliseconds(),
84 encode_time));
mflodman@webrtc.orge6168f52013-06-26 11:23:01 +000085}
86
87int32_t OveruseFrameDetector::TimeUntilNextProcess() {
mflodman@webrtc.orgd4412fe2013-07-31 16:42:21 +000088 CriticalSectionScoped cs(crit_.get());
mflodman@webrtc.orge6168f52013-06-26 11:23:01 +000089 return last_process_time_ + kProcessIntervalMs - clock_->TimeInMilliseconds();
90}
91
92int32_t OveruseFrameDetector::Process() {
93 CriticalSectionScoped cs(crit_.get());
mflodman@webrtc.org6879c8a2013-07-23 11:35:00 +000094 int64_t now = clock_->TimeInMilliseconds();
95 if (now < last_process_time_ + kProcessIntervalMs)
mflodman@webrtc.orge6168f52013-06-26 11:23:01 +000096 return 0;
97
mflodman@webrtc.org6879c8a2013-07-23 11:35:00 +000098 last_process_time_ = now;
mflodman@webrtc.orgd4412fe2013-07-31 16:42:21 +000099 RemoveOldSamples();
100
101 // Don't trigger an overuse unless we've encoded at least one frame.
102 if (!observer_ || encode_times_.empty() || capture_times_.empty())
mflodman@webrtc.org6879c8a2013-07-23 11:35:00 +0000103 return 0;
104
mflodman@webrtc.orgd4412fe2013-07-31 16:42:21 +0000105 // At least half the maximum history should be filled before we trigger an
106 // overuse.
107 // TODO(mflodman) Shall the time difference between the first and the last
108 // sample be checked instead?
109 if (encode_times_.front().first > now - kMinValidHistoryMs) {
mflodman@webrtc.orge6168f52013-06-26 11:23:01 +0000110 return 0;
mflodman@webrtc.org6879c8a2013-07-23 11:35:00 +0000111 }
mflodman@webrtc.orgd4412fe2013-07-31 16:42:21 +0000112
113 if (IsOverusing()) {
114 // Overuse detected.
115 // Remember the average encode time for this overuse, as a help to trigger
116 // normal usage.
117 encode_overuse_times_[num_pixels_] = CalculateAverageEncodeTime();
118 RemoveAllSamples();
mflodman@webrtc.orge6168f52013-06-26 11:23:01 +0000119 observer_->OveruseDetected();
mflodman@webrtc.org6879c8a2013-07-23 11:35:00 +0000120 last_callback_time_ = now;
mflodman@webrtc.orgd4412fe2013-07-31 16:42:21 +0000121 } else if (IsUnderusing(now)) {
122 RemoveAllSamples();
mflodman@webrtc.org6879c8a2013-07-23 11:35:00 +0000123 observer_->NormalUsage();
124 last_callback_time_ = now;
mflodman@webrtc.orge6168f52013-06-26 11:23:01 +0000125 }
126 return 0;
127}
128
mflodman@webrtc.orgd4412fe2013-07-31 16:42:21 +0000129void OveruseFrameDetector::RemoveOldSamples() {
mflodman@webrtc.orge6168f52013-06-26 11:23:01 +0000130 int64_t time_now = clock_->TimeInMilliseconds();
mflodman@webrtc.org6879c8a2013-07-23 11:35:00 +0000131 while (!capture_times_.empty() &&
mflodman@webrtc.orge6168f52013-06-26 11:23:01 +0000132 capture_times_.front() < time_now - kOveruseHistoryMs) {
133 capture_times_.pop_front();
134 }
mflodman@webrtc.org6879c8a2013-07-23 11:35:00 +0000135 while (!encode_times_.empty() &&
mflodman@webrtc.orgd4412fe2013-07-31 16:42:21 +0000136 encode_times_.front().first < time_now - kOveruseHistoryMs) {
mflodman@webrtc.orge6168f52013-06-26 11:23:01 +0000137 encode_times_.pop_front();
138 }
139}
mflodman@webrtc.orgd4412fe2013-07-31 16:42:21 +0000140
141void OveruseFrameDetector::RemoveAllSamples() {
142 capture_times_.clear();
143 encode_times_.clear();
144}
145
146int64_t OveruseFrameDetector::CalculateAverageEncodeTime() const {
147 if (encode_times_.empty())
148 return 0;
149
150 int64_t total_encode_time = 0;
151 for (std::list<EncodeTime>::const_iterator it = encode_times_.begin();
152 it != encode_times_.end(); ++it) {
153 total_encode_time += it->second;
154 }
155 return total_encode_time / encode_times_.size();
156}
157
158bool OveruseFrameDetector::MaybeResetResolution(size_t width, size_t height) {
159 int num_pixels = width * height;
160 if (num_pixels == num_pixels_)
161 return false;
162
163 RemoveAllSamples();
164 num_pixels_ = num_pixels;
165 if (num_pixels > max_num_pixels_)
166 max_num_pixels_ = num_pixels;
167
168 return true;
169}
170
171bool OveruseFrameDetector::IsOverusing() {
172 if (encode_times_.empty())
173 return false;
174
175 float encode_ratio = encode_times_.size() /
176 static_cast<float>(capture_times_.size());
177 return encode_ratio < kMinEncodeRatio;
178}
179
180bool OveruseFrameDetector::IsUnderusing(int64_t time_now) {
181 if (time_now < last_callback_time_ + kMinCallbackDeltaMs ||
182 num_pixels_ >= max_num_pixels_) {
183 return false;
184 }
185 bool underusing = true;
186 if (underuse_encode_timing_enabled_) {
187 int prev_overuse_encode_time = 0;
188 for (std::map<int, int64_t>::reverse_iterator rit =
189 encode_overuse_times_.rbegin();
190 rit != encode_overuse_times_.rend() && rit->first > num_pixels_;
191 ++rit) {
192 prev_overuse_encode_time = rit->second;
193 }
194 // TODO(mflodman): This might happen now if the resolution is decreased
195 // by the user before an overuse has been triggered.
196 assert(prev_overuse_encode_time > 0);
197
198 // TODO(mflodman) Use some other way to guess if an increased resolution
199 // might work or not, e.g. encode time per pixel?
200 if (CalculateAverageEncodeTime() * kIncreaseThreshold >
201 prev_overuse_encode_time) {
202 underusing = false;
203 }
204 }
205 return underusing;
206}
mflodman@webrtc.orge6168f52013-06-26 11:23:01 +0000207} // namespace webrtc