Chromium Code Reviews| 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 // This value controls how many redundant, timer-base captures occur when the | 17 // When estimating frame durations, this is the hard upper-bound on the |
| 18 // content is static. Redundantly capturing the same frame allows iterative | 18 // estimate. |
| 19 // quality enhancement, and also allows the buffer to fill in "buffered mode". | 19 const int kUpperBoundDurationEstimateMicros = 1000000; // 1 second |
|
Irfan
2016/04/06 19:53:05
kUpperBoundFrameDuration... ?
miu
2016/04/06 22:33:53
IMHO, adding "Frame" to this long name doesn't rea
| |
| 20 // | |
| 21 // TODO(nick): Controlling this here is a hack and a layering violation, since | |
| 22 // it's a strategy specific to the WebRTC consumer, and probably just papers | |
| 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; | |
| 27 | 20 |
| 28 // The half-life of data points provided to the accumulator used when evaluating | 21 // The half-life of data points provided to the accumulator used when evaluating |
| 29 // the recent utilization of the buffer pool. This value is based on a | 22 // the recent utilization of the buffer pool. This value is based on a |
| 30 // simulation, and reacts quickly to change to avoid depleting the buffer pool | 23 // simulation, and reacts quickly to change to avoid depleting the buffer pool |
| 31 // (which would cause hard frame drops). | 24 // (which would cause hard frame drops). |
| 32 const int kBufferUtilizationEvaluationMicros = 200000; // 0.2 seconds | 25 const int kBufferUtilizationEvaluationMicros = 200000; // 0.2 seconds |
| 33 | 26 |
| 34 // The half-life of data points provided to the accumulator used when evaluating | 27 // The half-life of data points provided to the accumulator used when evaluating |
| 35 // the recent resource utilization of the consumer. The trade-off made here is | 28 // the recent resource utilization of the consumer. The trade-off made here is |
| 36 // reaction time versus over-reacting to outlier data points. | 29 // reaction time versus over-reacting to outlier data points. |
| (...skipping 62 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 99 | 92 |
| 100 VideoCaptureOracle::VideoCaptureOracle( | 93 VideoCaptureOracle::VideoCaptureOracle( |
| 101 base::TimeDelta min_capture_period, | 94 base::TimeDelta min_capture_period, |
| 102 const gfx::Size& max_frame_size, | 95 const gfx::Size& max_frame_size, |
| 103 media::ResolutionChangePolicy resolution_change_policy, | 96 media::ResolutionChangePolicy resolution_change_policy, |
| 104 bool enable_auto_throttling) | 97 bool enable_auto_throttling) |
| 105 : auto_throttling_enabled_(enable_auto_throttling), | 98 : auto_throttling_enabled_(enable_auto_throttling), |
| 106 next_frame_number_(0), | 99 next_frame_number_(0), |
| 107 last_successfully_delivered_frame_number_(-1), | 100 last_successfully_delivered_frame_number_(-1), |
| 108 num_frames_pending_(0), | 101 num_frames_pending_(0), |
| 109 smoothing_sampler_(min_capture_period, | 102 smoothing_sampler_(min_capture_period), |
| 110 kNumRedundantCapturesOfStaticContent), | |
| 111 content_sampler_(min_capture_period), | 103 content_sampler_(min_capture_period), |
| 112 resolution_chooser_(max_frame_size, resolution_change_policy), | 104 resolution_chooser_(max_frame_size, resolution_change_policy), |
| 113 buffer_pool_utilization_(base::TimeDelta::FromMicroseconds( | 105 buffer_pool_utilization_(base::TimeDelta::FromMicroseconds( |
| 114 kBufferUtilizationEvaluationMicros)), | 106 kBufferUtilizationEvaluationMicros)), |
| 115 estimated_capable_area_(base::TimeDelta::FromMicroseconds( | 107 estimated_capable_area_(base::TimeDelta::FromMicroseconds( |
| 116 kConsumerCapabilityEvaluationMicros)) { | 108 kConsumerCapabilityEvaluationMicros)) { |
| 117 VLOG(1) << "Auto-throttling is " | 109 VLOG(1) << "Auto-throttling is " |
| 118 << (auto_throttling_enabled_ ? "enabled." : "disabled."); | 110 << (auto_throttling_enabled_ ? "enabled." : "disabled."); |
| 119 } | 111 } |
| 120 | 112 |
| (...skipping 16 matching lines...) Expand all Loading... | |
| 137 DCHECK_LT(event, kNumEvents); | 129 DCHECK_LT(event, kNumEvents); |
| 138 if (event_time < last_event_time_[event]) { | 130 if (event_time < last_event_time_[event]) { |
| 139 LOG(WARNING) << "Event time is not monotonically non-decreasing. " | 131 LOG(WARNING) << "Event time is not monotonically non-decreasing. " |
| 140 << "Deciding not to capture this frame."; | 132 << "Deciding not to capture this frame."; |
| 141 return false; | 133 return false; |
| 142 } | 134 } |
| 143 last_event_time_[event] = event_time; | 135 last_event_time_[event] = event_time; |
| 144 | 136 |
| 145 bool should_sample = false; | 137 bool should_sample = false; |
| 146 duration_of_next_frame_ = base::TimeDelta(); | 138 duration_of_next_frame_ = base::TimeDelta(); |
| 147 switch (event) { | 139 switch (event) { |
|
xjz
2016/04/06 17:02:28
nit: maybe just use if (event == kCompositorUpdate
miu
2016/04/06 22:33:53
I would, except for two things:
1. This forces co
| |
| 148 case kCompositorUpdate: { | 140 case kCompositorUpdate: { |
| 149 smoothing_sampler_.ConsiderPresentationEvent(event_time); | 141 smoothing_sampler_.ConsiderPresentationEvent(event_time); |
| 150 const bool had_proposal = content_sampler_.HasProposal(); | 142 const bool had_proposal = content_sampler_.HasProposal(); |
| 151 content_sampler_.ConsiderPresentationEvent(damage_rect, event_time); | 143 content_sampler_.ConsiderPresentationEvent(damage_rect, event_time); |
| 152 if (content_sampler_.HasProposal()) { | 144 if (content_sampler_.HasProposal()) { |
| 153 VLOG_IF(1, !had_proposal) << "Content sampler now detects animation."; | 145 VLOG_IF(1, !had_proposal) << "Content sampler now detects animation."; |
| 154 should_sample = content_sampler_.ShouldSample(); | 146 should_sample = content_sampler_.ShouldSample(); |
| 155 if (should_sample) { | 147 if (should_sample) { |
| 156 event_time = content_sampler_.frame_timestamp(); | 148 event_time = content_sampler_.frame_timestamp(); |
| 157 duration_of_next_frame_ = content_sampler_.sampling_period(); | 149 duration_of_next_frame_ = content_sampler_.sampling_period(); |
| 158 } | 150 } |
| 159 last_time_animation_was_detected_ = event_time; | 151 last_time_animation_was_detected_ = event_time; |
| 160 } else { | 152 } else { |
| 161 VLOG_IF(1, had_proposal) << "Content sampler detects animation ended."; | 153 VLOG_IF(1, had_proposal) << "Content sampler detects animation ended."; |
| 162 should_sample = smoothing_sampler_.ShouldSample(); | 154 should_sample = smoothing_sampler_.ShouldSample(); |
| 163 } | 155 } |
| 164 break; | 156 break; |
| 165 } | 157 } |
| 166 | 158 |
| 167 case kTimerPoll: | 159 case kActiveRefreshRequest: |
| 168 // While the timer is firing, only allow a sampling if there are none | 160 case kPassiveRefreshRequest: |
| 169 // currently in-progress. | |
| 170 if (num_frames_pending_ == 0) | |
| 171 should_sample = smoothing_sampler_.IsOverdueForSamplingAt(event_time); | |
| 172 break; | |
| 173 | |
| 174 case kMouseCursorUpdate: | 161 case kMouseCursorUpdate: |
| 175 // Only allow a sampling if there are none currently in-progress. | 162 // Only allow a sampling if there are none currently in-progress. |
| 176 if (num_frames_pending_ == 0) { | 163 if (num_frames_pending_ == 0) { |
| 177 smoothing_sampler_.ConsiderPresentationEvent(event_time); | 164 smoothing_sampler_.ConsiderPresentationEvent(event_time); |
| 178 should_sample = smoothing_sampler_.ShouldSample(); | 165 should_sample = smoothing_sampler_.ShouldSample(); |
| 179 } | 166 } |
| 180 break; | 167 break; |
| 168 | |
| 181 case kNumEvents: | 169 case kNumEvents: |
| 182 NOTREACHED(); | 170 NOTREACHED(); |
| 183 break; | 171 break; |
| 184 } | 172 } |
| 185 | 173 |
| 186 if (!should_sample) | 174 if (!should_sample) |
| 187 return false; | 175 return false; |
| 188 | 176 |
| 189 // If the exact duration of the next frame has not been determined, estimate | 177 // If the exact duration of the next frame has not been determined, estimate |
| 190 // it using the difference between the current and last frame. | 178 // it using the difference between the current and last frame. |
| 191 if (duration_of_next_frame_.is_zero()) { | 179 if (duration_of_next_frame_.is_zero()) { |
| 192 if (next_frame_number_ > 0) { | 180 if (next_frame_number_ > 0) { |
| 193 duration_of_next_frame_ = | 181 duration_of_next_frame_ = |
| 194 event_time - GetFrameTimestamp(next_frame_number_ - 1); | 182 event_time - GetFrameTimestamp(next_frame_number_ - 1); |
| 195 } | 183 } |
| 196 const base::TimeDelta upper_bound = base::TimeDelta::FromMilliseconds( | 184 const base::TimeDelta upper_bound = |
| 197 SmoothEventSampler::OVERDUE_DIRTY_THRESHOLD_MILLIS); | 185 base::TimeDelta::FromMilliseconds(kUpperBoundDurationEstimateMicros); |
| 198 duration_of_next_frame_ = | 186 duration_of_next_frame_ = |
| 199 std::max(std::min(duration_of_next_frame_, upper_bound), | 187 std::max(std::min(duration_of_next_frame_, upper_bound), |
| 200 smoothing_sampler_.min_capture_period()); | 188 smoothing_sampler_.min_capture_period()); |
| 201 } | 189 } |
| 202 | 190 |
| 203 // Update |capture_size_| and reset all feedback signal accumulators if | 191 // Update |capture_size_| and reset all feedback signal accumulators if |
| 204 // either: 1) this is the first frame; or 2) |resolution_chooser_| has an | 192 // either: 1) this is the first frame; or 2) |resolution_chooser_| has an |
| 205 // updated capture size and sufficient time has passed since the last size | 193 // updated capture size and sufficient time has passed since the last size |
| 206 // change. | 194 // change. |
| 207 if (next_frame_number_ == 0) { | 195 if (next_frame_number_ == 0) { |
| (...skipping 125 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 333 // Translate the utilization metric to be in terms of the capable frame area | 321 // Translate the utilization metric to be in terms of the capable frame area |
| 334 // and update the feedback accumulators. Research suggests utilization is at | 322 // and update the feedback accumulators. Research suggests utilization is at |
| 335 // most linearly proportional to area, and typically is sublinear. Either | 323 // most linearly proportional to area, and typically is sublinear. Either |
| 336 // way, the end-to-end system should converge to the right place using the | 324 // way, the end-to-end system should converge to the right place using the |
| 337 // more-conservative assumption (linear). | 325 // more-conservative assumption (linear). |
| 338 const int area_at_full_utilization = | 326 const int area_at_full_utilization = |
| 339 base::saturated_cast<int>(capture_size_.GetArea() / resource_utilization); | 327 base::saturated_cast<int>(capture_size_.GetArea() / resource_utilization); |
| 340 estimated_capable_area_.Update(area_at_full_utilization, timestamp); | 328 estimated_capable_area_.Update(area_at_full_utilization, timestamp); |
| 341 } | 329 } |
| 342 | 330 |
| 331 // static | |
| 332 const char* VideoCaptureOracle::EventAsString(Event event) { | |
| 333 switch (event) { | |
| 334 case kCompositorUpdate: | |
| 335 return "compositor"; | |
| 336 case kActiveRefreshRequest: | |
| 337 return "active_refresh"; | |
| 338 case kPassiveRefreshRequest: | |
| 339 return "passive_refresh"; | |
| 340 case kMouseCursorUpdate: | |
| 341 return "mouse"; | |
| 342 case kNumEvents: | |
| 343 break; | |
| 344 } | |
| 345 NOTREACHED(); | |
| 346 return "unknown"; | |
| 347 } | |
| 348 | |
| 343 base::TimeTicks VideoCaptureOracle::GetFrameTimestamp(int frame_number) const { | 349 base::TimeTicks VideoCaptureOracle::GetFrameTimestamp(int frame_number) const { |
| 344 DCHECK(IsFrameInRecentHistory(frame_number)); | 350 DCHECK(IsFrameInRecentHistory(frame_number)); |
| 345 return frame_timestamps_[frame_number % kMaxFrameTimestamps]; | 351 return frame_timestamps_[frame_number % kMaxFrameTimestamps]; |
| 346 } | 352 } |
| 347 | 353 |
| 348 void VideoCaptureOracle::SetFrameTimestamp(int frame_number, | 354 void VideoCaptureOracle::SetFrameTimestamp(int frame_number, |
| 349 base::TimeTicks timestamp) { | 355 base::TimeTicks timestamp) { |
| 350 DCHECK(IsFrameInRecentHistory(frame_number)); | 356 DCHECK(IsFrameInRecentHistory(frame_number)); |
| 351 frame_timestamps_[frame_number % kMaxFrameTimestamps] = timestamp; | 357 frame_timestamps_[frame_number % kMaxFrameTimestamps] = timestamp; |
| 352 } | 358 } |
| (...skipping 180 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 533 // Content is not animating, so permit an immediate increase in the capture | 539 // Content is not animating, so permit an immediate increase in the capture |
| 534 // area. This allows the system to quickly improve the quality of | 540 // area. This allows the system to quickly improve the quality of |
| 535 // non-animating content (frame drops are not much of a concern). | 541 // non-animating content (frame drops are not much of a concern). |
| 536 VLOG(2) << "Proposing a " | 542 VLOG(2) << "Proposing a " |
| 537 << (100.0 * (increased_area - current_area) / current_area) | 543 << (100.0 * (increased_area - current_area) / current_area) |
| 538 << "% increase in capture area for non-animating content. :-)"; | 544 << "% increase in capture area for non-animating content. :-)"; |
| 539 return increased_area; | 545 return increased_area; |
| 540 } | 546 } |
| 541 | 547 |
| 542 } // namespace media | 548 } // namespace media |
| OLD | NEW |