OLD | NEW |
1 // Copyright (c) 2015 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2015 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "media/capture/content/video_capture_oracle.h" | 5 #include "media/capture/content/video_capture_oracle.h" |
6 | 6 |
7 #include <algorithm> | 7 #include <algorithm> |
8 | 8 |
9 #include "base/format_macros.h" | 9 #include "base/format_macros.h" |
10 #include "base/numerics/safe_conversions.h" | 10 #include "base/numerics/safe_conversions.h" |
11 #include "base/strings/stringprintf.h" | 11 #include "base/strings/stringprintf.h" |
12 | 12 |
13 namespace media { | 13 namespace media { |
14 | 14 |
15 namespace { | 15 namespace { |
16 | 16 |
17 // When a non-compositor event arrives after animation has halted, this | 17 // This value controls how many redundant, timer-base captures occur when the |
18 // controls how much time must elapse before deciding to allow a capture. | 18 // content is static. Redundantly capturing the same frame allows iterative |
19 const int kAnimationHaltPeriodBeforeOtherSamplingMicros = 250000; | 19 // quality enhancement, and also allows the buffer to fill in "buffered mode". |
20 | 20 // |
21 // When estimating frame durations, this is the hard upper-bound on the | 21 // TODO(nick): Controlling this here is a hack and a layering violation, since |
22 // estimate. | 22 // it's a strategy specific to the WebRTC consumer, and probably just papers |
23 const int kUpperBoundDurationEstimateMicros = 1000000; // 1 second | 23 // over some frame dropping and quality bugs. It should either be controlled at |
| 24 // a higher level, or else redundant frame generation should be pushed down |
| 25 // further into the WebRTC encoding stack. |
| 26 const int kNumRedundantCapturesOfStaticContent = 200; |
24 | 27 |
25 // The half-life of data points provided to the accumulator used when evaluating | 28 // The half-life of data points provided to the accumulator used when evaluating |
26 // the recent utilization of the buffer pool. This value is based on a | 29 // the recent utilization of the buffer pool. This value is based on a |
27 // simulation, and reacts quickly to change to avoid depleting the buffer pool | 30 // simulation, and reacts quickly to change to avoid depleting the buffer pool |
28 // (which would cause hard frame drops). | 31 // (which would cause hard frame drops). |
29 const int kBufferUtilizationEvaluationMicros = 200000; // 0.2 seconds | 32 const int kBufferUtilizationEvaluationMicros = 200000; // 0.2 seconds |
30 | 33 |
31 // The half-life of data points provided to the accumulator used when evaluating | 34 // The half-life of data points provided to the accumulator used when evaluating |
32 // the recent resource utilization of the consumer. The trade-off made here is | 35 // the recent resource utilization of the consumer. The trade-off made here is |
33 // reaction time versus over-reacting to outlier data points. | 36 // reaction time versus over-reacting to outlier data points. |
(...skipping 62 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
96 | 99 |
97 VideoCaptureOracle::VideoCaptureOracle( | 100 VideoCaptureOracle::VideoCaptureOracle( |
98 base::TimeDelta min_capture_period, | 101 base::TimeDelta min_capture_period, |
99 const gfx::Size& max_frame_size, | 102 const gfx::Size& max_frame_size, |
100 media::ResolutionChangePolicy resolution_change_policy, | 103 media::ResolutionChangePolicy resolution_change_policy, |
101 bool enable_auto_throttling) | 104 bool enable_auto_throttling) |
102 : auto_throttling_enabled_(enable_auto_throttling), | 105 : auto_throttling_enabled_(enable_auto_throttling), |
103 next_frame_number_(0), | 106 next_frame_number_(0), |
104 last_successfully_delivered_frame_number_(-1), | 107 last_successfully_delivered_frame_number_(-1), |
105 num_frames_pending_(0), | 108 num_frames_pending_(0), |
106 smoothing_sampler_(min_capture_period), | 109 smoothing_sampler_(min_capture_period, |
| 110 kNumRedundantCapturesOfStaticContent), |
107 content_sampler_(min_capture_period), | 111 content_sampler_(min_capture_period), |
108 resolution_chooser_(max_frame_size, resolution_change_policy), | 112 resolution_chooser_(max_frame_size, resolution_change_policy), |
109 buffer_pool_utilization_(base::TimeDelta::FromMicroseconds( | 113 buffer_pool_utilization_(base::TimeDelta::FromMicroseconds( |
110 kBufferUtilizationEvaluationMicros)), | 114 kBufferUtilizationEvaluationMicros)), |
111 estimated_capable_area_(base::TimeDelta::FromMicroseconds( | 115 estimated_capable_area_(base::TimeDelta::FromMicroseconds( |
112 kConsumerCapabilityEvaluationMicros)) { | 116 kConsumerCapabilityEvaluationMicros)) { |
113 VLOG(1) << "Auto-throttling is " | 117 VLOG(1) << "Auto-throttling is " |
114 << (auto_throttling_enabled_ ? "enabled." : "disabled."); | 118 << (auto_throttling_enabled_ ? "enabled." : "disabled."); |
115 } | 119 } |
116 | 120 |
(...skipping 36 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
153 duration_of_next_frame_ = content_sampler_.sampling_period(); | 157 duration_of_next_frame_ = content_sampler_.sampling_period(); |
154 } | 158 } |
155 last_time_animation_was_detected_ = event_time; | 159 last_time_animation_was_detected_ = event_time; |
156 } else { | 160 } else { |
157 VLOG_IF(1, had_proposal) << "Content sampler detects animation ended."; | 161 VLOG_IF(1, had_proposal) << "Content sampler detects animation ended."; |
158 should_sample = smoothing_sampler_.ShouldSample(); | 162 should_sample = smoothing_sampler_.ShouldSample(); |
159 } | 163 } |
160 break; | 164 break; |
161 } | 165 } |
162 | 166 |
163 case kActiveRefreshRequest: | 167 case kTimerPoll: |
164 case kPassiveRefreshRequest: | 168 // While the timer is firing, only allow a sampling if there are none |
| 169 // currently in-progress. |
| 170 if (num_frames_pending_ == 0) |
| 171 should_sample = smoothing_sampler_.IsOverdueForSamplingAt(event_time); |
| 172 break; |
| 173 |
165 case kMouseCursorUpdate: | 174 case kMouseCursorUpdate: |
166 // Only allow non-compositor samplings when content has not recently been | 175 // Only allow a sampling if there are none currently in-progress. |
167 // animating, and only if there are no samplings currently in progress. | |
168 if (num_frames_pending_ == 0) { | 176 if (num_frames_pending_ == 0) { |
169 if (!content_sampler_.HasProposal() || | 177 smoothing_sampler_.ConsiderPresentationEvent(event_time); |
170 ((event_time - last_time_animation_was_detected_).InMicroseconds() > | 178 should_sample = smoothing_sampler_.ShouldSample(); |
171 kAnimationHaltPeriodBeforeOtherSamplingMicros)) { | |
172 smoothing_sampler_.ConsiderPresentationEvent(event_time); | |
173 should_sample = smoothing_sampler_.ShouldSample(); | |
174 } | |
175 } | 179 } |
176 break; | 180 break; |
177 | |
178 case kNumEvents: | 181 case kNumEvents: |
179 NOTREACHED(); | 182 NOTREACHED(); |
180 break; | 183 break; |
181 } | 184 } |
182 | 185 |
183 if (!should_sample) | 186 if (!should_sample) |
184 return false; | 187 return false; |
185 | 188 |
186 // If the exact duration of the next frame has not been determined, estimate | 189 // If the exact duration of the next frame has not been determined, estimate |
187 // it using the difference between the current and last frame. | 190 // it using the difference between the current and last frame. |
188 if (duration_of_next_frame_.is_zero()) { | 191 if (duration_of_next_frame_.is_zero()) { |
189 if (next_frame_number_ > 0) { | 192 if (next_frame_number_ > 0) { |
190 duration_of_next_frame_ = | 193 duration_of_next_frame_ = |
191 event_time - GetFrameTimestamp(next_frame_number_ - 1); | 194 event_time - GetFrameTimestamp(next_frame_number_ - 1); |
192 } | 195 } |
193 const base::TimeDelta upper_bound = | 196 const base::TimeDelta upper_bound = base::TimeDelta::FromMilliseconds( |
194 base::TimeDelta::FromMilliseconds(kUpperBoundDurationEstimateMicros); | 197 SmoothEventSampler::OVERDUE_DIRTY_THRESHOLD_MILLIS); |
195 duration_of_next_frame_ = | 198 duration_of_next_frame_ = |
196 std::max(std::min(duration_of_next_frame_, upper_bound), | 199 std::max(std::min(duration_of_next_frame_, upper_bound), |
197 smoothing_sampler_.min_capture_period()); | 200 smoothing_sampler_.min_capture_period()); |
198 } | 201 } |
199 | 202 |
200 // Update |capture_size_| and reset all feedback signal accumulators if | 203 // Update |capture_size_| and reset all feedback signal accumulators if |
201 // either: 1) this is the first frame; or 2) |resolution_chooser_| has an | 204 // either: 1) this is the first frame; or 2) |resolution_chooser_| has an |
202 // updated capture size and sufficient time has passed since the last size | 205 // updated capture size and sufficient time has passed since the last size |
203 // change. | 206 // change. |
204 if (next_frame_number_ == 0) { | 207 if (next_frame_number_ == 0) { |
(...skipping 125 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
330 // Translate the utilization metric to be in terms of the capable frame area | 333 // Translate the utilization metric to be in terms of the capable frame area |
331 // and update the feedback accumulators. Research suggests utilization is at | 334 // and update the feedback accumulators. Research suggests utilization is at |
332 // most linearly proportional to area, and typically is sublinear. Either | 335 // most linearly proportional to area, and typically is sublinear. Either |
333 // way, the end-to-end system should converge to the right place using the | 336 // way, the end-to-end system should converge to the right place using the |
334 // more-conservative assumption (linear). | 337 // more-conservative assumption (linear). |
335 const int area_at_full_utilization = | 338 const int area_at_full_utilization = |
336 base::saturated_cast<int>(capture_size_.GetArea() / resource_utilization); | 339 base::saturated_cast<int>(capture_size_.GetArea() / resource_utilization); |
337 estimated_capable_area_.Update(area_at_full_utilization, timestamp); | 340 estimated_capable_area_.Update(area_at_full_utilization, timestamp); |
338 } | 341 } |
339 | 342 |
340 // static | |
341 const char* VideoCaptureOracle::EventAsString(Event event) { | |
342 switch (event) { | |
343 case kCompositorUpdate: | |
344 return "compositor"; | |
345 case kActiveRefreshRequest: | |
346 return "active_refresh"; | |
347 case kPassiveRefreshRequest: | |
348 return "passive_refresh"; | |
349 case kMouseCursorUpdate: | |
350 return "mouse"; | |
351 case kNumEvents: | |
352 break; | |
353 } | |
354 NOTREACHED(); | |
355 return "unknown"; | |
356 } | |
357 | |
358 base::TimeTicks VideoCaptureOracle::GetFrameTimestamp(int frame_number) const { | 343 base::TimeTicks VideoCaptureOracle::GetFrameTimestamp(int frame_number) const { |
359 DCHECK(IsFrameInRecentHistory(frame_number)); | 344 DCHECK(IsFrameInRecentHistory(frame_number)); |
360 return frame_timestamps_[frame_number % kMaxFrameTimestamps]; | 345 return frame_timestamps_[frame_number % kMaxFrameTimestamps]; |
361 } | 346 } |
362 | 347 |
363 void VideoCaptureOracle::SetFrameTimestamp(int frame_number, | 348 void VideoCaptureOracle::SetFrameTimestamp(int frame_number, |
364 base::TimeTicks timestamp) { | 349 base::TimeTicks timestamp) { |
365 DCHECK(IsFrameInRecentHistory(frame_number)); | 350 DCHECK(IsFrameInRecentHistory(frame_number)); |
366 frame_timestamps_[frame_number % kMaxFrameTimestamps] = timestamp; | 351 frame_timestamps_[frame_number % kMaxFrameTimestamps] = timestamp; |
367 } | 352 } |
(...skipping 180 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
548 // Content is not animating, so permit an immediate increase in the capture | 533 // Content is not animating, so permit an immediate increase in the capture |
549 // area. This allows the system to quickly improve the quality of | 534 // area. This allows the system to quickly improve the quality of |
550 // non-animating content (frame drops are not much of a concern). | 535 // non-animating content (frame drops are not much of a concern). |
551 VLOG(2) << "Proposing a " | 536 VLOG(2) << "Proposing a " |
552 << (100.0 * (increased_area - current_area) / current_area) | 537 << (100.0 * (increased_area - current_area) / current_area) |
553 << "% increase in capture area for non-animating content. :-)"; | 538 << "% increase in capture area for non-animating content. :-)"; |
554 return increased_area; | 539 return increased_area; |
555 } | 540 } |
556 | 541 |
557 } // namespace media | 542 } // namespace media |
OLD | NEW |