Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(257)

Side by Side Diff: media/capture/video_capture_oracle.cc

Issue 1199593005: Automatic resolution throttling for screen capture pipeline. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@resolution_chooser_ITEM13
Patch Set: Addressed hubbe's comments, and replaced pessimistic accumulators with simple proving-period logic. REBASE Created 5 years, 5 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « media/capture/video_capture_oracle.h ('k') | media/capture/video_capture_oracle_unittest.cc » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
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/video_capture_oracle.h" 5 #include "media/capture/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/strings/stringprintf.h" 11 #include "base/strings/stringprintf.h"
11 12
12 namespace media { 13 namespace media {
13 14
14 namespace { 15 namespace {
15 16
16 // This value controls how many redundant, timer-base captures occur when the 17 // This value controls how many redundant, timer-base captures occur when the
17 // content is static. Redundantly capturing the same frame allows iterative 18 // content is static. Redundantly capturing the same frame allows iterative
18 // quality enhancement, and also allows the buffer to fill in "buffered mode". 19 // quality enhancement, and also allows the buffer to fill in "buffered mode".
19 // 20 //
20 // TODO(nick): Controlling this here is a hack and a layering violation, since 21 // TODO(nick): Controlling this here is a hack and a layering violation, since
21 // it's a strategy specific to the WebRTC consumer, and probably just papers 22 // it's a strategy specific to the WebRTC consumer, and probably just papers
22 // over some frame dropping and quality bugs. It should either be controlled at 23 // over some frame dropping and quality bugs. It should either be controlled at
23 // a higher level, or else redundant frame generation should be pushed down 24 // a higher level, or else redundant frame generation should be pushed down
24 // further into the WebRTC encoding stack. 25 // further into the WebRTC encoding stack.
25 const int kNumRedundantCapturesOfStaticContent = 200; 26 const int kNumRedundantCapturesOfStaticContent = 200;
26 27
28 // 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
30 // simulation, and reacts quickly to change to avoid depleting the buffer pool
31 // (which would cause hard frame drops).
32 const int kBufferUtilizationEvaluationMicros = 200000;
33
34 // 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
36 // reaction time versus over-reacting to outlier data points.
37 const int kConsumerCapabilityEvaluationMicros = 1000000;
38
39 // The minimum amount of time that must pass between changes to the capture
40 // size. This throttles the rate of size changes, to avoid stressing consumers
41 // and to allow the end-to-end system sufficient time to stabilize before
42 // re-evaluating the capture size.
43 const int kMinSizeChangePeriodMicros = 3000000;
44
45 // The maximum amount of time that may elapse without a feedback update. Any
46 // longer, and currently-accumulated feedback is not considered recent enough to
47 // base decisions off of. This prevents changes to the capture size when there
48 // is an unexpected pause in events.
49 const int kMaxTimeSinceLastFeedbackUpdateMicros = 1000000;
50
51 // The amount of additional time, since content animation was last detected, to
52 // continue being extra-careful about increasing the capture size. This is used
53 // to prevent breif periods of non-animating content from throwing off the
54 // heuristics that decide whether to increase the capture size.
55 const int kDebouncingPeriodForAnimatedContentMicros = 3000000;
hubbe 2015/06/25 21:57:43 All of these constants are a bit hard to read (too
miu 2015/07/01 22:11:47 Done. Sort of. It all comes back down to all tho
56
57 // When content is animating, this is the length of time the system must be
58 // contiguously under-utilized before increasing the capture size.
59 const int kProvingPeriodForAnimatedContentMicros = 30000000;
60
27 // Given the amount of time between frames, compare to the expected amount of 61 // Given the amount of time between frames, compare to the expected amount of
28 // time between frames at |frame_rate| and return the fractional difference. 62 // time between frames at |frame_rate| and return the fractional difference.
29 double FractionFromExpectedFrameRate(base::TimeDelta delta, int frame_rate) { 63 double FractionFromExpectedFrameRate(base::TimeDelta delta, int frame_rate) {
30 DCHECK_GT(frame_rate, 0); 64 DCHECK_GT(frame_rate, 0);
31 const base::TimeDelta expected_delta = 65 const base::TimeDelta expected_delta =
32 base::TimeDelta::FromSeconds(1) / frame_rate; 66 base::TimeDelta::FromSeconds(1) / frame_rate;
33 return (delta - expected_delta).InMillisecondsF() / 67 return (delta - expected_delta).InMillisecondsF() /
34 expected_delta.InMillisecondsF(); 68 expected_delta.InMillisecondsF();
35 } 69 }
36 70
71 // Returns the next-higher TimeTicks value.
72 // TODO(miu): Patch FeedbackSignalAccumulator reset behavior and remove this
73 // hack.
74 base::TimeTicks JustAfter(base::TimeTicks t) {
75 return t + base::TimeDelta::FromMicroseconds(1);
76 }
77
78 // Returns true if updates have been accumulated by |accumulator| for a
79 // sufficient amount of time and the latest update was fairly recent, relative
80 // to |now|.
81 bool HasSufficientRecentFeedback(const FeedbackSignalAccumulator& accumulator,
82 base::TimeTicks now) {
83 const base::TimeDelta amount_of_history =
84 accumulator.update_time() - accumulator.reset_time();
85 return (amount_of_history.InMicroseconds() >= kMinSizeChangePeriodMicros) &&
86 ((now - accumulator.update_time()).InMicroseconds() <=
87 kMaxTimeSinceLastFeedbackUpdateMicros);
88 }
89
37 } // anonymous namespace 90 } // anonymous namespace
38 91
39 VideoCaptureOracle::VideoCaptureOracle(base::TimeDelta min_capture_period) 92 VideoCaptureOracle::VideoCaptureOracle(
93 base::TimeDelta min_capture_period,
94 const gfx::Size& max_frame_size,
95 media::ResolutionChangePolicy resolution_change_policy)
40 : next_frame_number_(0), 96 : next_frame_number_(0),
41 last_successfully_delivered_frame_number_(-1), 97 last_successfully_delivered_frame_number_(-1),
42 num_frames_pending_(0), 98 num_frames_pending_(0),
43 smoothing_sampler_(min_capture_period, 99 smoothing_sampler_(min_capture_period,
44 kNumRedundantCapturesOfStaticContent), 100 kNumRedundantCapturesOfStaticContent),
45 content_sampler_(min_capture_period) { 101 content_sampler_(min_capture_period),
46 } 102 resolution_chooser_(max_frame_size, resolution_change_policy),
103 buffer_pool_utilization_(base::TimeDelta::FromMicroseconds(
104 kBufferUtilizationEvaluationMicros)),
105 estimated_capable_area_(base::TimeDelta::FromMicroseconds(
106 kConsumerCapabilityEvaluationMicros)) {}
47 107
48 VideoCaptureOracle::~VideoCaptureOracle() {} 108 VideoCaptureOracle::~VideoCaptureOracle() {}
49 109
110 void VideoCaptureOracle::SetSourceSize(const gfx::Size& source_size) {
111 resolution_chooser_.SetSourceSize(source_size);
112 // If the |resolution_chooser_| computed a new capture size, that will become
113 // visible via a future call to ObserveEventAndDecideCapture().
114 }
115
50 bool VideoCaptureOracle::ObserveEventAndDecideCapture( 116 bool VideoCaptureOracle::ObserveEventAndDecideCapture(
51 Event event, 117 Event event,
52 const gfx::Rect& damage_rect, 118 const gfx::Rect& damage_rect,
53 base::TimeTicks event_time) { 119 base::TimeTicks event_time) {
54 DCHECK_GE(event, 0); 120 DCHECK_GE(event, 0);
55 DCHECK_LT(event, kNumEvents); 121 DCHECK_LT(event, kNumEvents);
56 if (event_time < last_event_time_[event]) { 122 if (event_time < last_event_time_[event]) {
57 LOG(WARNING) << "Event time is not monotonically non-decreasing. " 123 LOG(WARNING) << "Event time is not monotonically non-decreasing. "
58 << "Deciding not to capture this frame."; 124 << "Deciding not to capture this frame.";
59 return false; 125 return false;
60 } 126 }
61 last_event_time_[event] = event_time; 127 last_event_time_[event] = event_time;
62 128
63 bool should_sample = false; 129 bool should_sample = false;
64 duration_of_next_frame_ = base::TimeDelta(); 130 duration_of_next_frame_ = base::TimeDelta();
65 switch (event) { 131 switch (event) {
66 case kCompositorUpdate: 132 case kCompositorUpdate: {
67 smoothing_sampler_.ConsiderPresentationEvent(event_time); 133 smoothing_sampler_.ConsiderPresentationEvent(event_time);
134 const bool had_proposal = content_sampler_.HasProposal();
68 content_sampler_.ConsiderPresentationEvent(damage_rect, event_time); 135 content_sampler_.ConsiderPresentationEvent(damage_rect, event_time);
69 if (content_sampler_.HasProposal()) { 136 if (content_sampler_.HasProposal()) {
137 VLOG_IF(1, !had_proposal) << "Content sampler now detects animation.";
70 should_sample = content_sampler_.ShouldSample(); 138 should_sample = content_sampler_.ShouldSample();
71 if (should_sample) { 139 if (should_sample) {
72 event_time = content_sampler_.frame_timestamp(); 140 event_time = content_sampler_.frame_timestamp();
73 duration_of_next_frame_ = content_sampler_.sampling_period(); 141 duration_of_next_frame_ = content_sampler_.sampling_period();
74 } 142 }
143 last_time_animation_was_detected_ = event_time;
75 } else { 144 } else {
145 VLOG_IF(1, had_proposal) << "Content sampler detects animation ended.";
76 should_sample = smoothing_sampler_.ShouldSample(); 146 should_sample = smoothing_sampler_.ShouldSample();
77 if (should_sample) 147 if (should_sample)
78 duration_of_next_frame_ = smoothing_sampler_.min_capture_period(); 148 duration_of_next_frame_ = smoothing_sampler_.min_capture_period();
79 } 149 }
80 break; 150 break;
151 }
152
81 case kTimerPoll: 153 case kTimerPoll:
82 // While the timer is firing, only allow a sampling if there are none 154 // While the timer is firing, only allow a sampling if there are none
83 // currently in-progress. 155 // currently in-progress.
84 if (num_frames_pending_ == 0) { 156 if (num_frames_pending_ == 0) {
85 should_sample = smoothing_sampler_.IsOverdueForSamplingAt(event_time); 157 should_sample = smoothing_sampler_.IsOverdueForSamplingAt(event_time);
86 if (should_sample) 158 if (should_sample)
87 duration_of_next_frame_ = smoothing_sampler_.min_capture_period(); 159 duration_of_next_frame_ = smoothing_sampler_.min_capture_period();
88 } 160 }
89 break; 161 break;
162
90 case kNumEvents: 163 case kNumEvents:
91 NOTREACHED(); 164 NOTREACHED();
92 break; 165 break;
93 } 166 }
94 167
168 if (!should_sample)
169 return false;
170
171 // Update |capture_size_| and reset all feedback signal accumulators if
172 // either: 1) this is the first frame; or 2) |resolution_chooser_| has an
173 // updated capture size and sufficient time has passed since the last size
174 // change.
175 if (next_frame_number_ == 0) {
176 CommitCaptureSizeAndReset(event_time - duration_of_next_frame_);
177 } else if (capture_size_ != resolution_chooser_.capture_size()) {
178 const base::TimeDelta time_since_last_change =
179 event_time - buffer_pool_utilization_.reset_time();
180 if (time_since_last_change.InMicroseconds() >= kMinSizeChangePeriodMicros)
181 CommitCaptureSizeAndReset(GetFrameTimestamp(next_frame_number_ - 1));
182 }
183
95 SetFrameTimestamp(next_frame_number_, event_time); 184 SetFrameTimestamp(next_frame_number_, event_time);
96 return should_sample; 185 return true;
97 } 186 }
98 187
99 int VideoCaptureOracle::RecordCapture() { 188 int VideoCaptureOracle::RecordCapture(double pool_utilization) {
189 DCHECK(std::isfinite(pool_utilization) && pool_utilization >= 0.0);
190
100 smoothing_sampler_.RecordSample(); 191 smoothing_sampler_.RecordSample();
101 content_sampler_.RecordSample(GetFrameTimestamp(next_frame_number_)); 192 const base::TimeTicks timestamp = GetFrameTimestamp(next_frame_number_);
193 content_sampler_.RecordSample(timestamp);
194
195 buffer_pool_utilization_.Update(pool_utilization, timestamp);
196 AnalyzeAndAdjust(timestamp);
197
102 num_frames_pending_++; 198 num_frames_pending_++;
103 return next_frame_number_++; 199 return next_frame_number_++;
104 } 200 }
105 201
202 void VideoCaptureOracle::RecordWillNotCapture(double pool_utilization) {
203 DCHECK(std::isfinite(pool_utilization) && pool_utilization >= 0.0);
204
205 VLOG(1) << "Client rejects proposal to capture frame (at #"
206 << next_frame_number_ << ").";
207
208 const base::TimeTicks timestamp = GetFrameTimestamp(next_frame_number_);
209 buffer_pool_utilization_.Update(pool_utilization, timestamp);
210 AnalyzeAndAdjust(timestamp);
211
212 // Note: Do not advance |next_frame_number_| since it will be re-used for the
213 // next capture proposal.
214 }
215
106 bool VideoCaptureOracle::CompleteCapture(int frame_number, 216 bool VideoCaptureOracle::CompleteCapture(int frame_number,
107 bool capture_was_successful, 217 bool capture_was_successful,
108 base::TimeTicks* frame_timestamp) { 218 base::TimeTicks* frame_timestamp) {
109 num_frames_pending_--; 219 num_frames_pending_--;
220 DCHECK_GE(num_frames_pending_, 0);
110 221
111 // Drop frame if previously delivered frame number is higher. 222 // Drop frame if previously delivered frame number is higher.
112 if (last_successfully_delivered_frame_number_ > frame_number) { 223 if (last_successfully_delivered_frame_number_ > frame_number) {
113 LOG_IF(WARNING, capture_was_successful) 224 LOG_IF(WARNING, capture_was_successful)
114 << "Out of order frame delivery detected (have #" << frame_number 225 << "Out of order frame delivery detected (have #" << frame_number
115 << ", last was #" << last_successfully_delivered_frame_number_ 226 << ", last was #" << last_successfully_delivered_frame_number_
116 << "). Dropping frame."; 227 << "). Dropping frame.";
117 return false; 228 return false;
118 } 229 }
119 230
231 if (!IsFrameInRecentHistory(frame_number)) {
232 LOG(WARNING) << "Very old capture being ignored: frame #" << frame_number;
233 return false;
234 }
235
120 if (!capture_was_successful) { 236 if (!capture_was_successful) {
121 VLOG(2) << "Capture of frame #" << frame_number << " was not successful."; 237 VLOG(2) << "Capture of frame #" << frame_number << " was not successful.";
122 return false; 238 return false;
123 } 239 }
124 240
125 DCHECK_NE(last_successfully_delivered_frame_number_, frame_number); 241 DCHECK_NE(last_successfully_delivered_frame_number_, frame_number);
126 last_successfully_delivered_frame_number_ = frame_number; 242 last_successfully_delivered_frame_number_ = frame_number;
127 243
128 *frame_timestamp = GetFrameTimestamp(frame_number); 244 *frame_timestamp = GetFrameTimestamp(frame_number);
129 245
130 // If enabled, log a measurement of how this frame timestamp has incremented 246 // If enabled, log a measurement of how this frame timestamp has incremented
131 // in relation to an ideal increment. 247 // in relation to an ideal increment.
132 if (VLOG_IS_ON(2) && frame_number > 0) { 248 if (VLOG_IS_ON(3) && frame_number > 0) {
133 const base::TimeDelta delta = 249 const base::TimeDelta delta =
134 *frame_timestamp - GetFrameTimestamp(frame_number - 1); 250 *frame_timestamp - GetFrameTimestamp(frame_number - 1);
135 if (content_sampler_.HasProposal()) { 251 if (content_sampler_.HasProposal()) {
136 const double estimated_frame_rate = 252 const double estimated_frame_rate =
137 1000000.0 / content_sampler_.detected_period().InMicroseconds(); 253 1000000.0 / content_sampler_.detected_period().InMicroseconds();
138 const int rounded_frame_rate = 254 const int rounded_frame_rate =
139 static_cast<int>(estimated_frame_rate + 0.5); 255 static_cast<int>(estimated_frame_rate + 0.5);
140 VLOG(2) << base::StringPrintf( 256 VLOG_STREAM(3) << base::StringPrintf(
141 "Captured #%d: delta=%" PRId64 " usec" 257 "Captured #%d: delta=%" PRId64 " usec"
142 ", now locked into {%s}, %+0.1f%% slower than %d FPS", 258 ", now locked into {%s}, %+0.1f%% slower than %d FPS",
143 frame_number, 259 frame_number,
144 delta.InMicroseconds(), 260 delta.InMicroseconds(),
145 content_sampler_.detected_region().ToString().c_str(), 261 content_sampler_.detected_region().ToString().c_str(),
146 100.0 * FractionFromExpectedFrameRate(delta, rounded_frame_rate), 262 100.0 * FractionFromExpectedFrameRate(delta, rounded_frame_rate),
147 rounded_frame_rate); 263 rounded_frame_rate);
148 } else { 264 } else {
149 VLOG(2) << base::StringPrintf( 265 VLOG_STREAM(3) << base::StringPrintf(
150 "Captured #%d: delta=%" PRId64 " usec" 266 "Captured #%d: delta=%" PRId64 " usec"
151 ", d/30fps=%+0.1f%%, d/25fps=%+0.1f%%, d/24fps=%+0.1f%%", 267 ", d/30fps=%+0.1f%%, d/25fps=%+0.1f%%, d/24fps=%+0.1f%%",
152 frame_number, 268 frame_number,
153 delta.InMicroseconds(), 269 delta.InMicroseconds(),
154 100.0 * FractionFromExpectedFrameRate(delta, 30), 270 100.0 * FractionFromExpectedFrameRate(delta, 30),
155 100.0 * FractionFromExpectedFrameRate(delta, 25), 271 100.0 * FractionFromExpectedFrameRate(delta, 25),
156 100.0 * FractionFromExpectedFrameRate(delta, 24)); 272 100.0 * FractionFromExpectedFrameRate(delta, 24));
157 } 273 }
158 } 274 }
159 275
160 return !frame_timestamp->is_null(); 276 return true;
277 }
278
279 void VideoCaptureOracle::RecordConsumerFeedback(int frame_number,
280 double resource_utilization) {
281 if (!std::isfinite(resource_utilization)) {
282 LOG(DFATAL) << "Non-finite utilization provided by consumer for frame #"
283 << frame_number << ": " << resource_utilization;
284 return;
285 }
286 if (resource_utilization <= 0.0)
287 return; // Non-positive values are normal, meaning N/A.
288
289 if (!IsFrameInRecentHistory(frame_number)) {
290 VLOG(1) << "Very old frame feedback being ignored: frame #" << frame_number;
291 return;
292 }
293 const base::TimeTicks timestamp = GetFrameTimestamp(frame_number);
294
295 // Translate the utilization metric to be in terms of the capable frame area
296 // and update the feedback accumulators. Research suggests utilization is at
297 // most linearly proportional to area, and typically is sublinear. Either
298 // way, the end-to-end system should converge to the right place using the
299 // more-conservative assumption (linear).
300 const int area_at_full_utilization =
301 base::saturated_cast<int>(capture_size_.GetArea() / resource_utilization);
302 estimated_capable_area_.Update(area_at_full_utilization, timestamp);
161 } 303 }
162 304
163 base::TimeTicks VideoCaptureOracle::GetFrameTimestamp(int frame_number) const { 305 base::TimeTicks VideoCaptureOracle::GetFrameTimestamp(int frame_number) const {
164 DCHECK_LE(frame_number, next_frame_number_); 306 DCHECK(IsFrameInRecentHistory(frame_number));
165 DCHECK_LT(next_frame_number_ - frame_number, kMaxFrameTimestamps);
166 return frame_timestamps_[frame_number % kMaxFrameTimestamps]; 307 return frame_timestamps_[frame_number % kMaxFrameTimestamps];
167 } 308 }
168 309
169 void VideoCaptureOracle::SetFrameTimestamp(int frame_number, 310 void VideoCaptureOracle::SetFrameTimestamp(int frame_number,
170 base::TimeTicks timestamp) { 311 base::TimeTicks timestamp) {
312 DCHECK(IsFrameInRecentHistory(frame_number));
171 frame_timestamps_[frame_number % kMaxFrameTimestamps] = timestamp; 313 frame_timestamps_[frame_number % kMaxFrameTimestamps] = timestamp;
172 } 314 }
173 315
316 bool VideoCaptureOracle::IsFrameInRecentHistory(int frame_number) const {
317 return ((next_frame_number_ - frame_number) < kMaxFrameTimestamps &&
318 frame_number <= next_frame_number_ &&
319 frame_number >= 0);
320 }
321
322 void VideoCaptureOracle::CommitCaptureSizeAndReset(
323 base::TimeTicks last_frame_time) {
324 capture_size_ = resolution_chooser_.capture_size();
325 VLOG(2) << "Now proposing a capture size of " << capture_size_.ToString();
326
327 // Reset each short-term feedback accumulator with a stable-state starting
328 // value.
329 const base::TimeTicks ignore_before_time = JustAfter(last_frame_time);
330 buffer_pool_utilization_.Reset(1.0, ignore_before_time);
331 estimated_capable_area_.Reset(capture_size_.GetArea(), ignore_before_time);
332
333 // With the new capture size, erase any prior conclusion about the end-to-end
334 // system being under-utilized.
335 start_time_of_underutilization_ = base::TimeTicks();
336 }
337
338 void VideoCaptureOracle::AnalyzeAndAdjust(const base::TimeTicks analyze_time) {
339 const int decreased_area = AnalyzeForDecreasedArea(analyze_time);
340 if (decreased_area > 0) {
341 resolution_chooser_.SetTargetFrameArea(decreased_area);
342 return;
343 }
344
345 const int increased_area = AnalyzeForIncreasedArea(analyze_time);
346 if (increased_area > 0) {
347 resolution_chooser_.SetTargetFrameArea(increased_area);
348 return;
349 }
350
351 // Explicitly set the target frame area to the current capture area. This
352 // cancels-out the results of a previous call to this method, where the
353 // |resolution_chooser_| may have been instructed to increase or decrease the
354 // capture size. Conditions may have changed since then which indicate no
355 // change should be committed (via CommitCaptureSizeAndReset()).
356 resolution_chooser_.SetTargetFrameArea(capture_size_.GetArea());
357 }
358
359 int VideoCaptureOracle::AnalyzeForDecreasedArea(base::TimeTicks analyze_time) {
360 const int current_area = capture_size_.GetArea();
361 DCHECK_GT(current_area, 0);
362
363 // Translate the recent-average buffer pool utilization to be in terms of
364 // "capable number of pixels per frame," for an apples-to-apples comparison
365 // below.
366 int buffer_capable_area;
367 if (HasSufficientRecentFeedback(buffer_pool_utilization_, analyze_time) &&
368 buffer_pool_utilization_.current() > 1.0) {
369 // This calculation is hand-wavy, but seems to work well in a variety of
370 // situations.
371 buffer_capable_area = static_cast<int>(
372 current_area / buffer_pool_utilization_.current());
373 } else {
374 buffer_capable_area = current_area;
375 }
376
377 int consumer_capable_area;
378 if (HasSufficientRecentFeedback(estimated_capable_area_, analyze_time)) {
379 consumer_capable_area =
380 base::saturated_cast<int>(estimated_capable_area_.current());
381 } else {
382 consumer_capable_area = current_area;
383 }
384
385 // If either of the "capable areas" is less than the current capture area,
386 // decrease the capture area by AT LEAST one step.
387 int decreased_area = -1;
388 const int capable_area = std::min(buffer_capable_area, consumer_capable_area);
389 if (capable_area < current_area) {
390 decreased_area = std::min(
391 capable_area,
392 resolution_chooser_.FindSmallerFrameSize(current_area, 1).GetArea());
393 start_time_of_underutilization_ = base::TimeTicks();
394 VLOG(2) << "Proposing a "
395 << (100.0 * (current_area - decreased_area) / current_area)
396 << "% decrease in capture area. :-(";
397 }
398
399 // Always log the capability interpretations at verbose logging level 3. At
400 // level 2, only log when when proposing a decreased area.
401 VLOG(decreased_area == -1 ? 3 : 2)
402 << "Capability of pool=" << (100.0 * buffer_capable_area / current_area)
403 << "%, consumer=" << (100.0 * consumer_capable_area / current_area)
404 << '%';
405
406 return decreased_area;
407 }
408
409 int VideoCaptureOracle::AnalyzeForIncreasedArea(base::TimeTicks analyze_time) {
410 // Compute what one step up in capture size/area would be. If the current
411 // area is already at the maximum, no further analysis is necessary.
412 const int current_area = capture_size_.GetArea();
413 const int increased_area =
414 resolution_chooser_.FindLargerFrameSize(current_area, 1).GetArea();
415 if (increased_area <= current_area)
416 return -1;
417
418 // Determine whether the buffer pool could handle an increase in area.
419 if (!HasSufficientRecentFeedback(buffer_pool_utilization_, analyze_time))
420 return -1;
421 if (buffer_pool_utilization_.current() > 0.0) {
422 const int buffer_capable_area = base::saturated_cast<int>(
423 current_area / buffer_pool_utilization_.current());
424 if (buffer_capable_area < increased_area) {
425 start_time_of_underutilization_ = base::TimeTicks();
426 return -1; // Buffer pool is not under-utilized.
427 }
428 }
429
430 // Determine whether the consumer could handle an increase in area.
431 if (HasSufficientRecentFeedback(estimated_capable_area_, analyze_time)) {
432 if (estimated_capable_area_.current() < increased_area) {
433 start_time_of_underutilization_ = base::TimeTicks();
434 return -1; // Consumer is not under-utilized.
435 }
436 } else if (estimated_capable_area_.update_time() ==
437 estimated_capable_area_.reset_time()) {
438 // The consumer does not provide any feedback. In this case, the consumer's
439 // capability isn't a consideration.
440 } else {
441 // Consumer is providing feedback, but hasn't reported it recently. Just in
442 // case it's stalled, don't make things worse by increasing the capture
443 // area.
444 start_time_of_underutilization_ = base::TimeTicks();
445 return -1;
446 }
447
448 // At this point, the system is currently under-utilized. Reset the start
449 // time if the system was not under-utilized when the last analysis was made.
450 if (start_time_of_underutilization_.is_null())
451 start_time_of_underutilization_ = analyze_time;
452
453 // While content is animating, require a "proving period" of contiguous
454 // under-utilization before increasing the capture area. This will mitigate
455 // the risk of causing frames to be dropped when increasing the load. If
456 // content is not animating, be aggressive about increasing the capture area,
457 // to improve the quality of non-animating content (where frame drops are not
458 // much of a concern).
459 if ((analyze_time - last_time_animation_was_detected_).InMicroseconds() <
460 kDebouncingPeriodForAnimatedContentMicros) {
461 if ((analyze_time - start_time_of_underutilization_).InMicroseconds() <
462 kProvingPeriodForAnimatedContentMicros) {
463 // Content is animating and the system has not been contiguously
464 // under-utilizated for long enough.
465 return -1;
466 }
467 }
468
469 VLOG(2) << "Proposing a "
470 << (100.0 * (increased_area - current_area) / current_area)
471 << "% increase in capture area. :-)";
472
473 return increased_area;
474 }
475
174 } // namespace media 476 } // namespace media
OLDNEW
« no previous file with comments | « media/capture/video_capture_oracle.h ('k') | media/capture/video_capture_oracle_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698