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/frame_callback.h" |
23 #include "webrtc/system_wrappers/include/clock.h" | 24 #include "webrtc/system_wrappers/include/clock.h" |
| 25 #include "webrtc/video_frame.h" |
24 | 26 |
25 namespace webrtc { | 27 namespace webrtc { |
26 | 28 |
27 namespace { | 29 namespace { |
28 const int64_t kProcessIntervalMs = 5000; | 30 const int64_t kProcessIntervalMs = 5000; |
29 | 31 |
30 // Delay between consecutive rampups. (Used for quick recovery.) | 32 // Delay between consecutive rampups. (Used for quick recovery.) |
31 const int kQuickRampUpDelayMs = 10 * 1000; | 33 const int kQuickRampUpDelayMs = 10 * 1000; |
32 // Delay between rampup attempts. Initially uses standard, scales up to max. | 34 // Delay between rampup attempts. Initially uses standard, scales up to max. |
33 const int kStandardRampUpDelayMs = 40 * 1000; | 35 const int kStandardRampUpDelayMs = 40 * 1000; |
(...skipping 74 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
108 const float kWeightFactorFrameDiff; | 110 const float kWeightFactorFrameDiff; |
109 const float kWeightFactorProcessing; | 111 const float kWeightFactorProcessing; |
110 const float kInitialSampleDiffMs; | 112 const float kInitialSampleDiffMs; |
111 const float kMaxSampleDiffMs; | 113 const float kMaxSampleDiffMs; |
112 uint64_t count_; | 114 uint64_t count_; |
113 const CpuOveruseOptions options_; | 115 const CpuOveruseOptions options_; |
114 rtc::scoped_ptr<rtc::ExpFilter> filtered_processing_ms_; | 116 rtc::scoped_ptr<rtc::ExpFilter> filtered_processing_ms_; |
115 rtc::scoped_ptr<rtc::ExpFilter> filtered_frame_diff_ms_; | 117 rtc::scoped_ptr<rtc::ExpFilter> filtered_frame_diff_ms_; |
116 }; | 118 }; |
117 | 119 |
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( | 120 OveruseFrameDetector::OveruseFrameDetector( |
167 Clock* clock, | 121 Clock* clock, |
168 const CpuOveruseOptions& options, | 122 const CpuOveruseOptions& options, |
169 CpuOveruseObserver* observer, | 123 CpuOveruseObserver* observer, |
| 124 EncodedFrameObserver* encoder_timing, |
170 CpuOveruseMetricsObserver* metrics_observer) | 125 CpuOveruseMetricsObserver* metrics_observer) |
171 : options_(options), | 126 : options_(options), |
172 observer_(observer), | 127 observer_(observer), |
| 128 encoder_timing_(encoder_timing), |
173 metrics_observer_(metrics_observer), | 129 metrics_observer_(metrics_observer), |
174 clock_(clock), | 130 clock_(clock), |
175 num_process_times_(0), | 131 num_process_times_(0), |
176 last_capture_time_(0), | 132 last_capture_time_(0), |
177 num_pixels_(0), | 133 num_pixels_(0), |
178 next_process_time_(clock_->TimeInMilliseconds()), | 134 next_process_time_(clock_->TimeInMilliseconds()), |
179 last_overuse_time_(0), | 135 last_overuse_time_(0), |
180 checks_above_threshold_(0), | 136 checks_above_threshold_(0), |
181 num_overuse_detections_(0), | 137 num_overuse_detections_(0), |
182 last_rampup_time_(0), | 138 last_rampup_time_(0), |
183 in_quick_rampup_(false), | 139 in_quick_rampup_(false), |
184 current_rampup_delay_ms_(kStandardRampUpDelayMs), | 140 current_rampup_delay_ms_(kStandardRampUpDelayMs), |
185 last_sample_time_ms_(0), | 141 last_sample_time_ms_(0), |
186 usage_(new SendProcessingUsage(options)), | 142 usage_(new SendProcessingUsage(options)) { |
187 frame_queue_(new FrameQueue()) { | |
188 RTC_DCHECK(metrics_observer != nullptr); | 143 RTC_DCHECK(metrics_observer != nullptr); |
189 // 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 | |
191 // would also alter the overuse state. | |
192 UpdateCpuOveruseMetrics(); | |
193 processing_thread_.DetachFromThread(); | 144 processing_thread_.DetachFromThread(); |
194 } | 145 } |
195 | 146 |
196 OveruseFrameDetector::~OveruseFrameDetector() { | 147 OveruseFrameDetector::~OveruseFrameDetector() { |
197 } | 148 } |
198 | 149 |
199 int OveruseFrameDetector::LastProcessingTimeMs() const { | 150 void OveruseFrameDetector::EncodedFrameTimeMeasured(int encode_duration_ms) { |
200 rtc::CritScope cs(&crit_); | |
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(); | 151 metrics_.encode_usage_percent = usage_->Value(); |
211 | 152 |
212 metrics_observer_->CpuOveruseMetricsUpdated(metrics_); | 153 metrics_observer_->OnEncodedFrameTimeMeasured(encode_duration_ms, metrics_); |
213 } | 154 } |
214 | 155 |
215 int64_t OveruseFrameDetector::TimeUntilNextProcess() { | 156 int64_t OveruseFrameDetector::TimeUntilNextProcess() { |
216 RTC_DCHECK(processing_thread_.CalledOnValidThread()); | 157 RTC_DCHECK(processing_thread_.CalledOnValidThread()); |
217 return next_process_time_ - clock_->TimeInMilliseconds(); | 158 return next_process_time_ - clock_->TimeInMilliseconds(); |
218 } | 159 } |
219 | 160 |
220 bool OveruseFrameDetector::FrameSizeChanged(int num_pixels) const { | 161 bool OveruseFrameDetector::FrameSizeChanged(int num_pixels) const { |
221 if (num_pixels != num_pixels_) { | 162 if (num_pixels != num_pixels_) { |
222 return true; | 163 return true; |
223 } | 164 } |
224 return false; | 165 return false; |
225 } | 166 } |
226 | 167 |
227 bool OveruseFrameDetector::FrameTimeoutDetected(int64_t now) const { | 168 bool OveruseFrameDetector::FrameTimeoutDetected(int64_t now) const { |
228 if (last_capture_time_ == 0) { | 169 if (last_capture_time_ == 0) { |
229 return false; | 170 return false; |
230 } | 171 } |
231 return (now - last_capture_time_) > options_.frame_timeout_interval_ms; | 172 return (now - last_capture_time_) > options_.frame_timeout_interval_ms; |
232 } | 173 } |
233 | 174 |
234 void OveruseFrameDetector::ResetAll(int num_pixels) { | 175 void OveruseFrameDetector::ResetAll(int num_pixels) { |
235 num_pixels_ = num_pixels; | 176 num_pixels_ = num_pixels; |
236 usage_->Reset(); | 177 usage_->Reset(); |
237 frame_queue_->Reset(); | 178 frame_timing_.clear(); |
238 last_capture_time_ = 0; | 179 last_capture_time_ = 0; |
239 num_process_times_ = 0; | 180 num_process_times_ = 0; |
240 UpdateCpuOveruseMetrics(); | |
241 } | 181 } |
242 | 182 |
243 void OveruseFrameDetector::FrameCaptured(int width, | 183 void OveruseFrameDetector::FrameCaptured(const VideoFrame& frame) { |
244 int height, | |
245 int64_t capture_time_ms) { | |
246 rtc::CritScope cs(&crit_); | 184 rtc::CritScope cs(&crit_); |
247 | 185 |
248 int64_t now = clock_->TimeInMilliseconds(); | 186 int64_t now = clock_->TimeInMilliseconds(); |
249 if (FrameSizeChanged(width * height) || FrameTimeoutDetected(now)) { | 187 if (FrameSizeChanged(frame.width() * frame.height()) || |
250 ResetAll(width * height); | 188 FrameTimeoutDetected(now)) { |
| 189 ResetAll(frame.width() * frame.height()); |
251 } | 190 } |
252 | 191 |
253 if (last_capture_time_ != 0) | 192 if (last_capture_time_ != 0) |
254 usage_->AddCaptureSample(now - last_capture_time_); | 193 usage_->AddCaptureSample(now - last_capture_time_); |
255 | 194 |
256 last_capture_time_ = now; | 195 last_capture_time_ = now; |
257 | 196 |
258 frame_queue_->Start(capture_time_ms, now); | 197 frame_timing_.push_back( |
| 198 FrameTiming(frame.ntp_time_ms(), frame.timestamp(), now)); |
259 } | 199 } |
260 | 200 |
261 void OveruseFrameDetector::FrameSent(int64_t capture_time_ms) { | 201 void OveruseFrameDetector::FrameSent(uint32_t timestamp) { |
262 rtc::CritScope cs(&crit_); | 202 rtc::CritScope cs(&crit_); |
263 int delay_ms = frame_queue_->End(capture_time_ms, | 203 // Delay before reporting actual encoding time, used to have the ability to |
264 clock_->TimeInMilliseconds()); | 204 // detect total encoding time when encoding more than one layer. Encoding is |
265 if (delay_ms > 0) { | 205 // here assumed to finish within a second (or that we get enough long-time |
266 AddProcessingTime(delay_ms); | 206 // samples before one second to trigger an overuse even when this is not the |
| 207 // case). |
| 208 static const int64_t kEncodingTimeMeasureWindowMs = 1000; |
| 209 int64_t now = clock_->TimeInMilliseconds(); |
| 210 for (auto& it : frame_timing_) { |
| 211 if (it.timestamp == timestamp) { |
| 212 it.last_send_ms = now; |
| 213 break; |
| 214 } |
| 215 } |
| 216 // TODO(pbos): Handle the case/log errors when not finding the corresponding |
| 217 // frame (either very slow encoding or incorrect wrong timestamps returned |
| 218 // from the encoder). |
| 219 // This is currently the case for all frames on ChromeOS, so logging them |
| 220 // would be spammy, and triggering overuse would be wrong. |
| 221 // https://crbug.com/350106 |
| 222 while (!frame_timing_.empty()) { |
| 223 FrameTiming timing = frame_timing_.front(); |
| 224 if (now - timing.capture_ms < kEncodingTimeMeasureWindowMs) |
| 225 break; |
| 226 if (timing.last_send_ms != -1) { |
| 227 int encode_duration_ms = |
| 228 static_cast<int>(timing.last_send_ms - timing.capture_ms); |
| 229 if (encoder_timing_) { |
| 230 encoder_timing_->OnEncodeTiming(timing.capture_ntp_ms, |
| 231 encode_duration_ms); |
| 232 } |
| 233 AddProcessingTime(encode_duration_ms); |
| 234 } |
| 235 frame_timing_.pop_front(); |
267 } | 236 } |
268 } | 237 } |
269 | 238 |
270 void OveruseFrameDetector::AddProcessingTime(int elapsed_ms) { | 239 void OveruseFrameDetector::AddProcessingTime(int elapsed_ms) { |
271 int64_t now = clock_->TimeInMilliseconds(); | 240 int64_t now = clock_->TimeInMilliseconds(); |
272 if (last_sample_time_ms_ != 0) { | 241 if (last_sample_time_ms_ != 0) { |
273 int64_t diff_ms = now - last_sample_time_ms_; | 242 int64_t diff_ms = now - last_sample_time_ms_; |
274 usage_->AddSample(elapsed_ms, diff_ms); | 243 usage_->AddSample(elapsed_ms, diff_ms); |
275 } | 244 } |
276 last_sample_time_ms_ = now; | 245 last_sample_time_ms_ = now; |
277 UpdateCpuOveruseMetrics(); | 246 EncodedFrameTimeMeasured(elapsed_ms); |
278 } | 247 } |
279 | 248 |
280 int32_t OveruseFrameDetector::Process() { | 249 int32_t OveruseFrameDetector::Process() { |
281 RTC_DCHECK(processing_thread_.CalledOnValidThread()); | 250 RTC_DCHECK(processing_thread_.CalledOnValidThread()); |
282 | 251 |
283 int64_t now = clock_->TimeInMilliseconds(); | 252 int64_t now = clock_->TimeInMilliseconds(); |
284 | 253 |
285 // Used to protect against Process() being called too often. | 254 // Used to protect against Process() being called too often. |
286 if (now < next_process_time_) | 255 if (now < next_process_time_) |
287 return 0; | 256 return 0; |
(...skipping 67 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
355 bool OveruseFrameDetector::IsUnderusing(const CpuOveruseMetrics& metrics, | 324 bool OveruseFrameDetector::IsUnderusing(const CpuOveruseMetrics& metrics, |
356 int64_t time_now) { | 325 int64_t time_now) { |
357 int delay = in_quick_rampup_ ? kQuickRampUpDelayMs : current_rampup_delay_ms_; | 326 int delay = in_quick_rampup_ ? kQuickRampUpDelayMs : current_rampup_delay_ms_; |
358 if (time_now < last_rampup_time_ + delay) | 327 if (time_now < last_rampup_time_ + delay) |
359 return false; | 328 return false; |
360 | 329 |
361 return metrics.encode_usage_percent < | 330 return metrics.encode_usage_percent < |
362 options_.low_encode_usage_threshold_percent; | 331 options_.low_encode_usage_threshold_percent; |
363 } | 332 } |
364 } // namespace webrtc | 333 } // namespace webrtc |
OLD | NEW |