mflodman@webrtc.org | e6168f5 | 2013-06-26 11:23:01 +0000 | [diff] [blame] | 1 | /* |
| 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.org | d4412fe | 2013-07-31 16:42:21 +0000 | [diff] [blame^] | 13 | #include <assert.h> |
| 14 | |
mflodman@webrtc.org | e6168f5 | 2013-06-26 11:23:01 +0000 | [diff] [blame] | 15 | #include "webrtc/system_wrappers/interface/clock.h" |
| 16 | #include "webrtc/system_wrappers/interface/critical_section_wrapper.h" |
mflodman@webrtc.org | 6879c8a | 2013-07-23 11:35:00 +0000 | [diff] [blame] | 17 | #include "webrtc/video_engine/include/vie_base.h" |
mflodman@webrtc.org | e6168f5 | 2013-06-26 11:23:01 +0000 | [diff] [blame] | 18 | |
| 19 | namespace webrtc { |
| 20 | |
mflodman@webrtc.org | d4412fe | 2013-07-31 16:42:21 +0000 | [diff] [blame^] | 21 | // TODO(mflodman) Test different values for all of these to trigger correctly, |
| 22 | // avoid fluctuations etc. |
| 23 | |
| 24 | namespace { |
| 25 | // Interval for 'Process' to be called. |
mflodman@webrtc.org | e6168f5 | 2013-06-26 11:23:01 +0000 | [diff] [blame] | 26 | const int64_t kProcessIntervalMs = 2000; |
mflodman@webrtc.org | d4412fe | 2013-07-31 16:42:21 +0000 | [diff] [blame^] | 27 | |
| 28 | // Duration capture and encode samples are valid. |
mflodman@webrtc.org | e6168f5 | 2013-06-26 11:23:01 +0000 | [diff] [blame] | 29 | const int kOveruseHistoryMs = 5000; |
mflodman@webrtc.org | d4412fe | 2013-07-31 16:42:21 +0000 | [diff] [blame^] | 30 | |
| 31 | // The minimum history to trigger an overuse or underuse. |
| 32 | const int64_t kMinValidHistoryMs = kOveruseHistoryMs / 2; |
| 33 | |
| 34 | // Encode / capture ratio to decide an overuse. |
mflodman@webrtc.org | e6168f5 | 2013-06-26 11:23:01 +0000 | [diff] [blame] | 35 | const float kMinEncodeRatio = 29 / 30.0f; |
mflodman@webrtc.org | d4412fe | 2013-07-31 16:42:21 +0000 | [diff] [blame^] | 36 | |
| 37 | // Minimum time between two callbacks. |
mflodman@webrtc.org | 6879c8a | 2013-07-23 11:35:00 +0000 | [diff] [blame] | 38 | const int kMinCallbackDeltaMs = 30000; |
mflodman@webrtc.org | e6168f5 | 2013-06-26 11:23:01 +0000 | [diff] [blame] | 39 | |
mflodman@webrtc.org | d4412fe | 2013-07-31 16:42:21 +0000 | [diff] [blame^] | 40 | // 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? |
| 43 | const float kIncreaseThreshold = 1.5f; |
| 44 | } // namespace |
| 45 | |
mflodman@webrtc.org | 6879c8a | 2013-07-23 11:35:00 +0000 | [diff] [blame] | 46 | OveruseFrameDetector::OveruseFrameDetector(Clock* clock) |
mflodman@webrtc.org | e6168f5 | 2013-06-26 11:23:01 +0000 | [diff] [blame] | 47 | : crit_(CriticalSectionWrapper::CreateCriticalSection()), |
mflodman@webrtc.org | 6879c8a | 2013-07-23 11:35:00 +0000 | [diff] [blame] | 48 | observer_(NULL), |
mflodman@webrtc.org | e6168f5 | 2013-06-26 11:23:01 +0000 | [diff] [blame] | 49 | clock_(clock), |
mflodman@webrtc.org | 6879c8a | 2013-07-23 11:35:00 +0000 | [diff] [blame] | 50 | last_process_time_(clock->TimeInMilliseconds()), |
mflodman@webrtc.org | d4412fe | 2013-07-31 16:42:21 +0000 | [diff] [blame^] | 51 | last_callback_time_(clock->TimeInMilliseconds()), |
| 52 | underuse_encode_timing_enabled_(false), |
| 53 | num_pixels_(0), |
| 54 | max_num_pixels_(0) { |
mflodman@webrtc.org | e6168f5 | 2013-06-26 11:23:01 +0000 | [diff] [blame] | 55 | } |
| 56 | |
| 57 | OveruseFrameDetector::~OveruseFrameDetector() { |
| 58 | } |
| 59 | |
mflodman@webrtc.org | 6879c8a | 2013-07-23 11:35:00 +0000 | [diff] [blame] | 60 | void OveruseFrameDetector::SetObserver(CpuOveruseObserver* observer) { |
| 61 | CriticalSectionScoped cs(crit_.get()); |
| 62 | observer_ = observer; |
| 63 | } |
| 64 | |
mflodman@webrtc.org | d4412fe | 2013-07-31 16:42:21 +0000 | [diff] [blame^] | 65 | void OveruseFrameDetector::set_underuse_encode_timing_enabled(bool enable) { |
mflodman@webrtc.org | e6168f5 | 2013-06-26 11:23:01 +0000 | [diff] [blame] | 66 | CriticalSectionScoped cs(crit_.get()); |
mflodman@webrtc.org | d4412fe | 2013-07-31 16:42:21 +0000 | [diff] [blame^] | 67 | underuse_encode_timing_enabled_ = enable; |
| 68 | } |
| 69 | |
| 70 | void OveruseFrameDetector::FrameCaptured() { |
| 71 | CriticalSectionScoped cs(crit_.get()); |
mflodman@webrtc.org | e6168f5 | 2013-06-26 11:23:01 +0000 | [diff] [blame] | 72 | capture_times_.push_back(clock_->TimeInMilliseconds()); |
| 73 | } |
| 74 | |
mflodman@webrtc.org | d4412fe | 2013-07-31 16:42:21 +0000 | [diff] [blame^] | 75 | void OveruseFrameDetector::FrameEncoded(int64_t encode_time, size_t width, |
| 76 | size_t height) { |
| 77 | assert(encode_time >= 0); |
mflodman@webrtc.org | e6168f5 | 2013-06-26 11:23:01 +0000 | [diff] [blame] | 78 | CriticalSectionScoped cs(crit_.get()); |
mflodman@webrtc.org | d4412fe | 2013-07-31 16:42:21 +0000 | [diff] [blame^] | 79 | // 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.org | e6168f5 | 2013-06-26 11:23:01 +0000 | [diff] [blame] | 85 | } |
| 86 | |
| 87 | int32_t OveruseFrameDetector::TimeUntilNextProcess() { |
mflodman@webrtc.org | d4412fe | 2013-07-31 16:42:21 +0000 | [diff] [blame^] | 88 | CriticalSectionScoped cs(crit_.get()); |
mflodman@webrtc.org | e6168f5 | 2013-06-26 11:23:01 +0000 | [diff] [blame] | 89 | return last_process_time_ + kProcessIntervalMs - clock_->TimeInMilliseconds(); |
| 90 | } |
| 91 | |
| 92 | int32_t OveruseFrameDetector::Process() { |
| 93 | CriticalSectionScoped cs(crit_.get()); |
mflodman@webrtc.org | 6879c8a | 2013-07-23 11:35:00 +0000 | [diff] [blame] | 94 | int64_t now = clock_->TimeInMilliseconds(); |
| 95 | if (now < last_process_time_ + kProcessIntervalMs) |
mflodman@webrtc.org | e6168f5 | 2013-06-26 11:23:01 +0000 | [diff] [blame] | 96 | return 0; |
| 97 | |
mflodman@webrtc.org | 6879c8a | 2013-07-23 11:35:00 +0000 | [diff] [blame] | 98 | last_process_time_ = now; |
mflodman@webrtc.org | d4412fe | 2013-07-31 16:42:21 +0000 | [diff] [blame^] | 99 | 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.org | 6879c8a | 2013-07-23 11:35:00 +0000 | [diff] [blame] | 103 | return 0; |
| 104 | |
mflodman@webrtc.org | d4412fe | 2013-07-31 16:42:21 +0000 | [diff] [blame^] | 105 | // 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.org | e6168f5 | 2013-06-26 11:23:01 +0000 | [diff] [blame] | 110 | return 0; |
mflodman@webrtc.org | 6879c8a | 2013-07-23 11:35:00 +0000 | [diff] [blame] | 111 | } |
mflodman@webrtc.org | d4412fe | 2013-07-31 16:42:21 +0000 | [diff] [blame^] | 112 | |
| 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.org | e6168f5 | 2013-06-26 11:23:01 +0000 | [diff] [blame] | 119 | observer_->OveruseDetected(); |
mflodman@webrtc.org | 6879c8a | 2013-07-23 11:35:00 +0000 | [diff] [blame] | 120 | last_callback_time_ = now; |
mflodman@webrtc.org | d4412fe | 2013-07-31 16:42:21 +0000 | [diff] [blame^] | 121 | } else if (IsUnderusing(now)) { |
| 122 | RemoveAllSamples(); |
mflodman@webrtc.org | 6879c8a | 2013-07-23 11:35:00 +0000 | [diff] [blame] | 123 | observer_->NormalUsage(); |
| 124 | last_callback_time_ = now; |
mflodman@webrtc.org | e6168f5 | 2013-06-26 11:23:01 +0000 | [diff] [blame] | 125 | } |
| 126 | return 0; |
| 127 | } |
| 128 | |
mflodman@webrtc.org | d4412fe | 2013-07-31 16:42:21 +0000 | [diff] [blame^] | 129 | void OveruseFrameDetector::RemoveOldSamples() { |
mflodman@webrtc.org | e6168f5 | 2013-06-26 11:23:01 +0000 | [diff] [blame] | 130 | int64_t time_now = clock_->TimeInMilliseconds(); |
mflodman@webrtc.org | 6879c8a | 2013-07-23 11:35:00 +0000 | [diff] [blame] | 131 | while (!capture_times_.empty() && |
mflodman@webrtc.org | e6168f5 | 2013-06-26 11:23:01 +0000 | [diff] [blame] | 132 | capture_times_.front() < time_now - kOveruseHistoryMs) { |
| 133 | capture_times_.pop_front(); |
| 134 | } |
mflodman@webrtc.org | 6879c8a | 2013-07-23 11:35:00 +0000 | [diff] [blame] | 135 | while (!encode_times_.empty() && |
mflodman@webrtc.org | d4412fe | 2013-07-31 16:42:21 +0000 | [diff] [blame^] | 136 | encode_times_.front().first < time_now - kOveruseHistoryMs) { |
mflodman@webrtc.org | e6168f5 | 2013-06-26 11:23:01 +0000 | [diff] [blame] | 137 | encode_times_.pop_front(); |
| 138 | } |
| 139 | } |
mflodman@webrtc.org | d4412fe | 2013-07-31 16:42:21 +0000 | [diff] [blame^] | 140 | |
| 141 | void OveruseFrameDetector::RemoveAllSamples() { |
| 142 | capture_times_.clear(); |
| 143 | encode_times_.clear(); |
| 144 | } |
| 145 | |
| 146 | int64_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 | |
| 158 | bool 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 | |
| 171 | bool 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 | |
| 180 | bool 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.org | e6168f5 | 2013-06-26 11:23:01 +0000 | [diff] [blame] | 207 | } // namespace webrtc |