OLD | NEW |
---|---|
1 /* | 1 /* |
2 * Copyright (c) 2013 The WebRTC project authors. All Rights Reserved. | 2 * Copyright (c) 2013 The WebRTC project authors. All Rights Reserved. |
3 * | 3 * |
4 * Use of this source code is governed by a BSD-style license | 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 | 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 | 6 * tree. An additional intellectual property rights grant can be found |
7 * in the file PATENTS. All contributing project authors may | 7 * in the file PATENTS. All contributing project authors may |
8 * be found in the AUTHORS file in the root of the source tree. | 8 * be found in the AUTHORS file in the root of the source tree. |
9 */ | 9 */ |
10 | 10 |
11 #include "webrtc/video/overuse_frame_detector.h" | 11 #include "webrtc/video/overuse_frame_detector.h" |
12 | 12 |
13 #include <assert.h> | 13 #include <assert.h> |
14 #include <math.h> | 14 #include <math.h> |
15 | 15 |
16 #include <algorithm> | 16 #include <algorithm> |
17 #include <list> | 17 #include <list> |
18 #include <map> | 18 #include <map> |
19 | 19 |
20 #include "webrtc/base/checks.h" | 20 #include "webrtc/base/checks.h" |
21 #include "webrtc/base/exp_filter.h" | 21 #include "webrtc/base/exp_filter.h" |
22 #include "webrtc/base/logging.h" | 22 #include "webrtc/base/logging.h" |
23 #include "webrtc/system_wrappers/include/clock.h" | 23 #include "webrtc/system_wrappers/include/clock.h" |
24 #include "webrtc/video_frame.h" | |
24 | 25 |
25 namespace webrtc { | 26 namespace webrtc { |
26 | 27 |
27 namespace { | 28 namespace { |
28 const int64_t kProcessIntervalMs = 5000; | 29 const int64_t kProcessIntervalMs = 5000; |
29 | 30 |
30 // Delay between consecutive rampups. (Used for quick recovery.) | 31 // Delay between consecutive rampups. (Used for quick recovery.) |
31 const int kQuickRampUpDelayMs = 10 * 1000; | 32 const int kQuickRampUpDelayMs = 10 * 1000; |
32 // Delay between rampup attempts. Initially uses standard, scales up to max. | 33 // Delay between rampup attempts. Initially uses standard, scales up to max. |
33 const int kStandardRampUpDelayMs = 40 * 1000; | 34 const int kStandardRampUpDelayMs = 40 * 1000; |
(...skipping 74 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
108 const float kWeightFactorFrameDiff; | 109 const float kWeightFactorFrameDiff; |
109 const float kWeightFactorProcessing; | 110 const float kWeightFactorProcessing; |
110 const float kInitialSampleDiffMs; | 111 const float kInitialSampleDiffMs; |
111 const float kMaxSampleDiffMs; | 112 const float kMaxSampleDiffMs; |
112 uint64_t count_; | 113 uint64_t count_; |
113 const CpuOveruseOptions options_; | 114 const CpuOveruseOptions options_; |
114 rtc::scoped_ptr<rtc::ExpFilter> filtered_processing_ms_; | 115 rtc::scoped_ptr<rtc::ExpFilter> filtered_processing_ms_; |
115 rtc::scoped_ptr<rtc::ExpFilter> filtered_frame_diff_ms_; | 116 rtc::scoped_ptr<rtc::ExpFilter> filtered_frame_diff_ms_; |
116 }; | 117 }; |
117 | 118 |
118 // Class for calculating the processing time of frames. | |
119 class OveruseFrameDetector::FrameQueue { | |
120 public: | |
121 FrameQueue() : last_processing_time_ms_(-1) {} | |
122 ~FrameQueue() {} | |
123 | |
124 // Called when a frame is captured. | |
125 // Starts the measuring of the processing time of the frame. | |
126 void Start(int64_t capture_time, int64_t now) { | |
127 const size_t kMaxSize = 90; // Allows for processing time of 1.5s at 60fps. | |
128 if (frame_times_.size() > kMaxSize) { | |
129 LOG(LS_WARNING) << "Max size reached, removed oldest frame."; | |
130 frame_times_.erase(frame_times_.begin()); | |
131 } | |
132 if (frame_times_.find(capture_time) != frame_times_.end()) { | |
133 // Frame should not exist. | |
134 assert(false); | |
135 return; | |
136 } | |
137 frame_times_[capture_time] = now; | |
138 } | |
139 | |
140 // Called when the processing of a frame has finished. | |
141 // Returns the processing time of the frame. | |
142 int End(int64_t capture_time, int64_t now) { | |
143 std::map<int64_t, int64_t>::iterator it = frame_times_.find(capture_time); | |
144 if (it == frame_times_.end()) { | |
145 return -1; | |
146 } | |
147 // Remove any old frames up to current. | |
148 // Old frames have been skipped by the capture process thread. | |
149 // TODO(asapersson): Consider measuring time from first frame in list. | |
150 last_processing_time_ms_ = now - (*it).second; | |
151 frame_times_.erase(frame_times_.begin(), ++it); | |
152 return last_processing_time_ms_; | |
153 } | |
154 | |
155 void Reset() { frame_times_.clear(); } | |
156 int NumFrames() const { return static_cast<int>(frame_times_.size()); } | |
157 int last_processing_time_ms() const { return last_processing_time_ms_; } | |
158 | |
159 private: | |
160 // Captured frames mapped by the capture time. | |
161 std::map<int64_t, int64_t> frame_times_; | |
162 int last_processing_time_ms_; | |
163 }; | |
164 | |
165 | |
166 OveruseFrameDetector::OveruseFrameDetector( | 119 OveruseFrameDetector::OveruseFrameDetector( |
167 Clock* clock, | 120 Clock* clock, |
168 const CpuOveruseOptions& options, | 121 const CpuOveruseOptions& options, |
169 CpuOveruseObserver* observer, | 122 CpuOveruseObserver* observer, |
170 CpuOveruseMetricsObserver* metrics_observer) | 123 CpuOveruseMetricsObserver* metrics_observer) |
171 : options_(options), | 124 : options_(options), |
172 observer_(observer), | 125 observer_(observer), |
173 metrics_observer_(metrics_observer), | 126 metrics_observer_(metrics_observer), |
127 last_encode_time_ms_(0), | |
174 clock_(clock), | 128 clock_(clock), |
175 num_process_times_(0), | 129 num_process_times_(0), |
176 last_capture_time_(0), | 130 last_capture_time_(0), |
177 num_pixels_(0), | 131 num_pixels_(0), |
178 next_process_time_(clock_->TimeInMilliseconds()), | 132 next_process_time_(clock_->TimeInMilliseconds()), |
179 last_overuse_time_(0), | 133 last_overuse_time_(0), |
180 checks_above_threshold_(0), | 134 checks_above_threshold_(0), |
181 num_overuse_detections_(0), | 135 num_overuse_detections_(0), |
182 last_rampup_time_(0), | 136 last_rampup_time_(0), |
183 in_quick_rampup_(false), | 137 in_quick_rampup_(false), |
184 current_rampup_delay_ms_(kStandardRampUpDelayMs), | 138 current_rampup_delay_ms_(kStandardRampUpDelayMs), |
185 last_sample_time_ms_(0), | 139 last_sample_time_ms_(0), |
186 usage_(new SendProcessingUsage(options)), | 140 usage_(new SendProcessingUsage(options)) { |
187 frame_queue_(new FrameQueue()) { | |
188 RTC_DCHECK(metrics_observer != nullptr); | 141 RTC_DCHECK(metrics_observer != nullptr); |
189 // Make sure stats are initially up-to-date. This simplifies unit testing | 142 // Make sure stats are initially up-to-date. This simplifies unit testing |
190 // since we don't have to trigger an update using one of the methods which | 143 // since we don't have to trigger an update using one of the methods which |
191 // would also alter the overuse state. | 144 // would also alter the overuse state. |
192 UpdateCpuOveruseMetrics(); | 145 EncodedFrameTimeMeasured(0); |
mflodman
2016/01/21 08:00:04
Is this really needed? For the callback or to actu
pbos-webrtc
2016/01/21 14:18:33
Removed, I don't think we should have to report th
| |
193 processing_thread_.DetachFromThread(); | 146 processing_thread_.DetachFromThread(); |
194 } | 147 } |
195 | 148 |
196 OveruseFrameDetector::~OveruseFrameDetector() { | 149 OveruseFrameDetector::~OveruseFrameDetector() { |
197 } | 150 } |
198 | 151 |
199 int OveruseFrameDetector::LastProcessingTimeMs() const { | 152 void OveruseFrameDetector::EncodedFrameTimeMeasured(int encode_time_ms) { |
mflodman
2016/01/21 08:00:04
I'd prefer encoded_time_ms or encoder_process_time
pbos-webrtc
2016/01/21 14:18:33
encode_duration_ms done
| |
200 rtc::CritScope cs(&crit_); | 153 last_encode_time_ms_ = encode_time_ms; |
201 return frame_queue_->last_processing_time_ms(); | |
202 } | |
203 | |
204 int OveruseFrameDetector::FramesInQueue() const { | |
205 rtc::CritScope cs(&crit_); | |
206 return frame_queue_->NumFrames(); | |
207 } | |
208 | |
209 void OveruseFrameDetector::UpdateCpuOveruseMetrics() { | |
210 metrics_.encode_usage_percent = usage_->Value(); | 154 metrics_.encode_usage_percent = usage_->Value(); |
211 | 155 |
212 metrics_observer_->CpuOveruseMetricsUpdated(metrics_); | 156 metrics_observer_->OnEncodedFrameTimeMeasured(encode_time_ms, metrics_); |
213 } | 157 } |
214 | 158 |
215 int64_t OveruseFrameDetector::TimeUntilNextProcess() { | 159 int64_t OveruseFrameDetector::TimeUntilNextProcess() { |
216 RTC_DCHECK(processing_thread_.CalledOnValidThread()); | 160 RTC_DCHECK(processing_thread_.CalledOnValidThread()); |
217 return next_process_time_ - clock_->TimeInMilliseconds(); | 161 return next_process_time_ - clock_->TimeInMilliseconds(); |
218 } | 162 } |
219 | 163 |
220 bool OveruseFrameDetector::FrameSizeChanged(int num_pixels) const { | 164 bool OveruseFrameDetector::FrameSizeChanged(int num_pixels) const { |
221 if (num_pixels != num_pixels_) { | 165 if (num_pixels != num_pixels_) { |
222 return true; | 166 return true; |
223 } | 167 } |
224 return false; | 168 return false; |
225 } | 169 } |
226 | 170 |
227 bool OveruseFrameDetector::FrameTimeoutDetected(int64_t now) const { | 171 bool OveruseFrameDetector::FrameTimeoutDetected(int64_t now) const { |
228 if (last_capture_time_ == 0) { | 172 if (last_capture_time_ == 0) { |
229 return false; | 173 return false; |
230 } | 174 } |
231 return (now - last_capture_time_) > options_.frame_timeout_interval_ms; | 175 return (now - last_capture_time_) > options_.frame_timeout_interval_ms; |
232 } | 176 } |
233 | 177 |
234 void OveruseFrameDetector::ResetAll(int num_pixels) { | 178 void OveruseFrameDetector::ResetAll(int num_pixels) { |
235 num_pixels_ = num_pixels; | 179 num_pixels_ = num_pixels; |
236 usage_->Reset(); | 180 usage_->Reset(); |
237 frame_queue_->Reset(); | 181 frame_timing_.clear(); |
238 last_capture_time_ = 0; | 182 last_capture_time_ = 0; |
239 num_process_times_ = 0; | 183 num_process_times_ = 0; |
240 UpdateCpuOveruseMetrics(); | 184 EncodedFrameTimeMeasured(last_encode_time_ms_); |
mflodman
2016/01/21 08:00:05
Do we need this here? What is the benefit compared
pbos-webrtc
2016/01/21 14:18:33
No don't think we need to re-report here even.
| |
241 } | 185 } |
242 | 186 |
243 void OveruseFrameDetector::FrameCaptured(int width, | 187 void OveruseFrameDetector::FrameCaptured(const VideoFrame& frame) { |
244 int height, | |
245 int64_t capture_time_ms) { | |
246 rtc::CritScope cs(&crit_); | 188 rtc::CritScope cs(&crit_); |
247 | 189 |
248 int64_t now = clock_->TimeInMilliseconds(); | 190 int64_t now = clock_->TimeInMilliseconds(); |
249 if (FrameSizeChanged(width * height) || FrameTimeoutDetected(now)) { | 191 if (FrameSizeChanged(frame.width() * frame.height()) || |
250 ResetAll(width * height); | 192 FrameTimeoutDetected(now)) { |
193 ResetAll(frame.width() * frame.height()); | |
251 } | 194 } |
252 | 195 |
253 if (last_capture_time_ != 0) | 196 if (last_capture_time_ != 0) |
254 usage_->AddCaptureSample(now - last_capture_time_); | 197 usage_->AddCaptureSample(now - last_capture_time_); |
255 | 198 |
256 last_capture_time_ = now; | 199 last_capture_time_ = now; |
257 | 200 |
258 frame_queue_->Start(capture_time_ms, now); | 201 frame_timing_.push_back(FrameTiming(frame.timestamp(), now)); |
mflodman
2016/01/21 08:00:04
Is it better to use timestamp than capture_time_ms
pbos-webrtc
2016/01/21 14:18:33
We don't get capture_time_ms out of the encoder af
mflodman
2016/02/05 08:19:03
Maybe this is something we should do in the future
| |
259 } | 202 } |
260 | 203 |
261 void OveruseFrameDetector::FrameSent(int64_t capture_time_ms) { | 204 void OveruseFrameDetector::FrameSent(const EncodedImage& frame) { |
262 rtc::CritScope cs(&crit_); | 205 rtc::CritScope cs(&crit_); |
263 int delay_ms = frame_queue_->End(capture_time_ms, | 206 // Delay before reporting actual encoding time, used to have the ability to |
264 clock_->TimeInMilliseconds()); | 207 // detect total encoding time when encoding more than one layer. Encoding is |
265 if (delay_ms > 0) { | 208 // here assumed to finish within a second (or that we get enough long-time |
266 AddProcessingTime(delay_ms); | 209 // samples before one second to trigger an overuse even when this is not the |
210 // case). | |
211 static const int64_t kEncodingTimeDelayMs = 1000; | |
mflodman
2016/01/21 08:00:04
I'd prefer to call this something indicating this
pbos-webrtc
2016/01/21 14:18:33
kEncodingTimeMeasureWindowsMs, let me know if you
| |
212 int64_t now = clock_->TimeInMilliseconds(); | |
213 for (auto& it : frame_timing_) { | |
214 if (it.timestamp == frame._timeStamp) { | |
215 it.last_send_ms = now; | |
216 break; | |
217 } | |
218 } | |
219 // TODO(pbos): Handle the case/log errors when not finding the corresponding | |
220 // frame (either very slow encoding or incorrect wrong timestamps returned | |
221 // from the encoder). | |
222 // This is currently the case for all frames on ChromeOS, so logging them | |
223 // would be spammy, and triggering overuse would be wrong. | |
224 // https://crbug.com/350106 | |
225 while (!frame_timing_.empty()) { | |
226 FrameTiming timing = frame_timing_.front(); | |
227 if (now - timing.capture_ms < kEncodingTimeDelayMs) | |
228 break; | |
229 if (timing.last_send_ms != -1) { | |
230 AddProcessingTime( | |
231 static_cast<int>(timing.last_send_ms - timing.capture_ms)); | |
232 } | |
233 frame_timing_.pop_front(); | |
267 } | 234 } |
268 } | 235 } |
269 | 236 |
270 void OveruseFrameDetector::AddProcessingTime(int elapsed_ms) { | 237 void OveruseFrameDetector::AddProcessingTime(int elapsed_ms) { |
271 int64_t now = clock_->TimeInMilliseconds(); | 238 int64_t now = clock_->TimeInMilliseconds(); |
272 if (last_sample_time_ms_ != 0) { | 239 if (last_sample_time_ms_ != 0) { |
273 int64_t diff_ms = now - last_sample_time_ms_; | 240 int64_t diff_ms = now - last_sample_time_ms_; |
274 usage_->AddSample(elapsed_ms, diff_ms); | 241 usage_->AddSample(elapsed_ms, diff_ms); |
275 } | 242 } |
276 last_sample_time_ms_ = now; | 243 last_sample_time_ms_ = now; |
277 UpdateCpuOveruseMetrics(); | 244 EncodedFrameTimeMeasured(elapsed_ms); |
278 } | 245 } |
279 | 246 |
280 int32_t OveruseFrameDetector::Process() { | 247 int32_t OveruseFrameDetector::Process() { |
281 RTC_DCHECK(processing_thread_.CalledOnValidThread()); | 248 RTC_DCHECK(processing_thread_.CalledOnValidThread()); |
282 | 249 |
283 int64_t now = clock_->TimeInMilliseconds(); | 250 int64_t now = clock_->TimeInMilliseconds(); |
284 | 251 |
285 // Used to protect against Process() being called too often. | 252 // Used to protect against Process() being called too often. |
286 if (now < next_process_time_) | 253 if (now < next_process_time_) |
287 return 0; | 254 return 0; |
(...skipping 67 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
355 bool OveruseFrameDetector::IsUnderusing(const CpuOveruseMetrics& metrics, | 322 bool OveruseFrameDetector::IsUnderusing(const CpuOveruseMetrics& metrics, |
356 int64_t time_now) { | 323 int64_t time_now) { |
357 int delay = in_quick_rampup_ ? kQuickRampUpDelayMs : current_rampup_delay_ms_; | 324 int delay = in_quick_rampup_ ? kQuickRampUpDelayMs : current_rampup_delay_ms_; |
358 if (time_now < last_rampup_time_ + delay) | 325 if (time_now < last_rampup_time_ + delay) |
359 return false; | 326 return false; |
360 | 327 |
361 return metrics.encode_usage_percent < | 328 return metrics.encode_usage_percent < |
362 options_.low_encode_usage_threshold_percent; | 329 options_.low_encode_usage_threshold_percent; |
363 } | 330 } |
364 } // namespace webrtc | 331 } // namespace webrtc |
OLD | NEW |