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

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: Created 5 years, 6 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
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 half-life of data points provided to the long-term accumulators, which
40 // are used to evaluate whether to increase the capture size.
41 const int kPessimisticEvaluationMicros = 30000000;
42
43 // The minimum amount of time that must pass between changes to the capture
44 // size. This throttles the rate of size changes, to avoid stressing consumers
45 // and to allow the end-to-end system sufficient time to stabilize before
46 // re-evaluating the capture size.
47 const int kMinSizeChangePeriodMicros = 3000000;
48
49 // The maximum amount of time that may elapse without a feedback update. Any
50 // longer, and currently-accumulated feedback is not considered recent enough to
51 // base decisions off of. This prevents changes to the capture size when there
52 // is an unexpected pause in events.
53 const int kMaxTimeSinceLastFeedbackUpdateMicros = 1000000;
54
55 // The amount of additional time, since content animation was last detected, to
56 // continue being pessimistic about increasing the capture size. This is used
57 // to prevent breif periods of non-animating content from throwing off the
58 // heuristics that decide whether to increase the capture size.
59 const int kPessimisticWatchPeriodMicros = 3000000;
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 |analyze_time|.
hubbe 2015/06/23 20:04:31 analyze_time is roughly *now*, isn't it? (Perhaps
miu 2015/06/25 20:48:59 Done. Makes sense here, to aid in readability. H
81 bool HasSufficientRecentFeedback(const FeedbackSignalAccumulator& accumulator,
82 base::TimeTicks analyze_time) {
83 const base::TimeDelta amount_of_history =
84 accumulator.update_time() - accumulator.reset_time();
85 return (amount_of_history.InMicroseconds() >= kMinSizeChangePeriodMicros) &&
86 ((analyze_time - 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)),
107 pessimistic_pool_utilization_(base::TimeDelta::FromMicroseconds(
108 kPessimisticEvaluationMicros)),
109 pessimistic_capable_area_(base::TimeDelta::FromMicroseconds(
110 kPessimisticEvaluationMicros)) {}
47 111
48 VideoCaptureOracle::~VideoCaptureOracle() {} 112 VideoCaptureOracle::~VideoCaptureOracle() {}
49 113
114 void VideoCaptureOracle::SetSourceSize(const gfx::Size& source_size) {
115 resolution_chooser_.SetSourceSize(source_size);
116 // If the |resolution_chooser_| computed a new capture size, that will become
117 // visible via a future call to ObserveEventAndDecideCapture().
118 }
119
50 bool VideoCaptureOracle::ObserveEventAndDecideCapture( 120 bool VideoCaptureOracle::ObserveEventAndDecideCapture(
51 Event event, 121 Event event,
52 const gfx::Rect& damage_rect, 122 const gfx::Rect& damage_rect,
53 base::TimeTicks event_time) { 123 base::TimeTicks event_time) {
54 DCHECK_GE(event, 0); 124 DCHECK_GE(event, 0);
55 DCHECK_LT(event, kNumEvents); 125 DCHECK_LT(event, kNumEvents);
56 if (event_time < last_event_time_[event]) { 126 if (event_time < last_event_time_[event]) {
57 LOG(WARNING) << "Event time is not monotonically non-decreasing. " 127 LOG(WARNING) << "Event time is not monotonically non-decreasing. "
58 << "Deciding not to capture this frame."; 128 << "Deciding not to capture this frame.";
59 return false; 129 return false;
60 } 130 }
61 last_event_time_[event] = event_time; 131 last_event_time_[event] = event_time;
62 132
63 bool should_sample = false; 133 bool should_sample = false;
64 duration_of_next_frame_ = base::TimeDelta(); 134 duration_of_next_frame_ = base::TimeDelta();
65 switch (event) { 135 switch (event) {
66 case kCompositorUpdate: 136 case kCompositorUpdate: {
67 smoothing_sampler_.ConsiderPresentationEvent(event_time); 137 smoothing_sampler_.ConsiderPresentationEvent(event_time);
138 const bool had_proposal = content_sampler_.HasProposal();
68 content_sampler_.ConsiderPresentationEvent(damage_rect, event_time); 139 content_sampler_.ConsiderPresentationEvent(damage_rect, event_time);
69 if (content_sampler_.HasProposal()) { 140 if (content_sampler_.HasProposal()) {
141 VLOG_IF(1, !had_proposal) << "Content sampler now detects animation.";
70 should_sample = content_sampler_.ShouldSample(); 142 should_sample = content_sampler_.ShouldSample();
71 if (should_sample) { 143 if (should_sample) {
72 event_time = content_sampler_.frame_timestamp(); 144 event_time = content_sampler_.frame_timestamp();
73 duration_of_next_frame_ = content_sampler_.sampling_period(); 145 duration_of_next_frame_ = content_sampler_.sampling_period();
74 } 146 }
147 last_time_animation_was_detected_ = event_time;
75 } else { 148 } else {
149 VLOG_IF(1, had_proposal) << "Content sampler detects animation ended.";
76 should_sample = smoothing_sampler_.ShouldSample(); 150 should_sample = smoothing_sampler_.ShouldSample();
77 if (should_sample) 151 if (should_sample)
78 duration_of_next_frame_ = smoothing_sampler_.min_capture_period(); 152 duration_of_next_frame_ = smoothing_sampler_.min_capture_period();
79 } 153 }
80 break; 154 break;
155 }
156
81 case kTimerPoll: 157 case kTimerPoll:
82 // While the timer is firing, only allow a sampling if there are none 158 // While the timer is firing, only allow a sampling if there are none
83 // currently in-progress. 159 // currently in-progress.
84 if (num_frames_pending_ == 0) { 160 if (num_frames_pending_ == 0) {
85 should_sample = smoothing_sampler_.IsOverdueForSamplingAt(event_time); 161 should_sample = smoothing_sampler_.IsOverdueForSamplingAt(event_time);
86 if (should_sample) 162 if (should_sample)
87 duration_of_next_frame_ = smoothing_sampler_.min_capture_period(); 163 duration_of_next_frame_ = smoothing_sampler_.min_capture_period();
88 } 164 }
89 break; 165 break;
166
90 case kNumEvents: 167 case kNumEvents:
91 NOTREACHED(); 168 NOTREACHED();
92 break; 169 break;
93 } 170 }
94 171
172 if (!should_sample)
173 return false;
174
175 // Update |capture_size_| and reset all feedback signal accumulators if
176 // either: 1) this is the first frame; or 2) |resolution_chooser_| has an
177 // updated capture size and sufficient time has passed since the last size
178 // change.
179 if (next_frame_number_ == 0) {
180 CommitCaptureSizeAndReset(event_time - duration_of_next_frame_);
181 } else if (capture_size_ != resolution_chooser_.capture_size()) {
182 const base::TimeDelta time_since_last_change =
183 event_time - buffer_pool_utilization_.reset_time();
184 if (time_since_last_change.InMicroseconds() >= kMinSizeChangePeriodMicros)
185 CommitCaptureSizeAndReset(GetFrameTimestamp(next_frame_number_ - 1));
186 }
187
95 SetFrameTimestamp(next_frame_number_, event_time); 188 SetFrameTimestamp(next_frame_number_, event_time);
96 return should_sample; 189 return true;
97 } 190 }
98 191
99 int VideoCaptureOracle::RecordCapture() { 192 int VideoCaptureOracle::RecordCapture(double pool_utilization) {
100 smoothing_sampler_.RecordSample(); 193 smoothing_sampler_.RecordSample();
101 content_sampler_.RecordSample(GetFrameTimestamp(next_frame_number_)); 194 const base::TimeTicks timestamp = GetFrameTimestamp(next_frame_number_);
195 content_sampler_.RecordSample(timestamp);
196
197 if (std::isfinite(pool_utilization) && pool_utilization >= 0.0) {
hubbe 2015/06/23 20:04:31 This seems like it could hide bugs. Can we DCHECK(
miu 2015/06/25 20:48:59 Done.
198 buffer_pool_utilization_.Update(pool_utilization, timestamp);
199 pessimistic_pool_utilization_.Update(pool_utilization, timestamp);
200 }
201 AnalyzeAndAdjust(timestamp);
202
102 num_frames_pending_++; 203 num_frames_pending_++;
103 return next_frame_number_++; 204 return next_frame_number_++;
104 } 205 }
105 206
207 void VideoCaptureOracle::RecordWillNotCapture(double pool_utilization) {
208 VLOG(1) << "Client rejects proposal to capture frame (at #"
209 << next_frame_number_ << ").";
210
211 const base::TimeTicks timestamp = GetFrameTimestamp(next_frame_number_);
212 if (std::isfinite(pool_utilization) && pool_utilization >= 0.0)
213 buffer_pool_utilization_.Update(pool_utilization, timestamp);
214 ResetPessimisticAccumulators(timestamp);
215 AnalyzeAndAdjust(timestamp);
216
217 // Note: Do not advance |next_frame_number_| since it will be re-used for the
218 // next capture proposal.
219 }
220
106 bool VideoCaptureOracle::CompleteCapture(int frame_number, 221 bool VideoCaptureOracle::CompleteCapture(int frame_number,
107 bool capture_was_successful, 222 bool capture_was_successful,
108 base::TimeTicks* frame_timestamp) { 223 base::TimeTicks* frame_timestamp) {
109 num_frames_pending_--; 224 num_frames_pending_--;
225 DCHECK_GE(num_frames_pending_, 0);
110 226
111 // Drop frame if previously delivered frame number is higher. 227 // Drop frame if previously delivered frame number is higher.
112 if (last_successfully_delivered_frame_number_ > frame_number) { 228 if (last_successfully_delivered_frame_number_ > frame_number) {
113 LOG_IF(WARNING, capture_was_successful) 229 LOG_IF(WARNING, capture_was_successful)
114 << "Out of order frame delivery detected (have #" << frame_number 230 << "Out of order frame delivery detected (have #" << frame_number
115 << ", last was #" << last_successfully_delivered_frame_number_ 231 << ", last was #" << last_successfully_delivered_frame_number_
116 << "). Dropping frame."; 232 << "). Dropping frame.";
117 return false; 233 return false;
118 } 234 }
119 235
236 if (!IsFrameInRecentHistory(frame_number)) {
237 LOG(WARNING) << "Very old capture being ignored: frame #" << frame_number;
238 return false;
239 }
240
120 if (!capture_was_successful) { 241 if (!capture_was_successful) {
121 VLOG(2) << "Capture of frame #" << frame_number << " was not successful."; 242 VLOG(2) << "Capture of frame #" << frame_number << " was not successful.";
122 return false; 243 return false;
123 } 244 }
124 245
125 DCHECK_NE(last_successfully_delivered_frame_number_, frame_number); 246 DCHECK_NE(last_successfully_delivered_frame_number_, frame_number);
126 last_successfully_delivered_frame_number_ = frame_number; 247 last_successfully_delivered_frame_number_ = frame_number;
127 248
128 *frame_timestamp = GetFrameTimestamp(frame_number); 249 *frame_timestamp = GetFrameTimestamp(frame_number);
129 250
130 // If enabled, log a measurement of how this frame timestamp has incremented 251 // If enabled, log a measurement of how this frame timestamp has incremented
131 // in relation to an ideal increment. 252 // in relation to an ideal increment.
132 if (VLOG_IS_ON(2) && frame_number > 0) { 253 if (VLOG_IS_ON(3) && frame_number > 0) {
133 const base::TimeDelta delta = 254 const base::TimeDelta delta =
134 *frame_timestamp - GetFrameTimestamp(frame_number - 1); 255 *frame_timestamp - GetFrameTimestamp(frame_number - 1);
135 if (content_sampler_.HasProposal()) { 256 if (content_sampler_.HasProposal()) {
136 const double estimated_frame_rate = 257 const double estimated_frame_rate =
137 1000000.0 / content_sampler_.detected_period().InMicroseconds(); 258 1000000.0 / content_sampler_.detected_period().InMicroseconds();
138 const int rounded_frame_rate = 259 const int rounded_frame_rate =
139 static_cast<int>(estimated_frame_rate + 0.5); 260 static_cast<int>(estimated_frame_rate + 0.5);
140 VLOG(2) << base::StringPrintf( 261 VLOG_STREAM(3) << base::StringPrintf(
141 "Captured #%d: delta=%" PRId64 " usec" 262 "Captured #%d: delta=%" PRId64 " usec"
142 ", now locked into {%s}, %+0.1f%% slower than %d FPS", 263 ", now locked into {%s}, %+0.1f%% slower than %d FPS",
143 frame_number, 264 frame_number,
144 delta.InMicroseconds(), 265 delta.InMicroseconds(),
145 content_sampler_.detected_region().ToString().c_str(), 266 content_sampler_.detected_region().ToString().c_str(),
146 100.0 * FractionFromExpectedFrameRate(delta, rounded_frame_rate), 267 100.0 * FractionFromExpectedFrameRate(delta, rounded_frame_rate),
147 rounded_frame_rate); 268 rounded_frame_rate);
148 } else { 269 } else {
149 VLOG(2) << base::StringPrintf( 270 VLOG_STREAM(3) << base::StringPrintf(
150 "Captured #%d: delta=%" PRId64 " usec" 271 "Captured #%d: delta=%" PRId64 " usec"
151 ", d/30fps=%+0.1f%%, d/25fps=%+0.1f%%, d/24fps=%+0.1f%%", 272 ", d/30fps=%+0.1f%%, d/25fps=%+0.1f%%, d/24fps=%+0.1f%%",
152 frame_number, 273 frame_number,
153 delta.InMicroseconds(), 274 delta.InMicroseconds(),
154 100.0 * FractionFromExpectedFrameRate(delta, 30), 275 100.0 * FractionFromExpectedFrameRate(delta, 30),
155 100.0 * FractionFromExpectedFrameRate(delta, 25), 276 100.0 * FractionFromExpectedFrameRate(delta, 25),
156 100.0 * FractionFromExpectedFrameRate(delta, 24)); 277 100.0 * FractionFromExpectedFrameRate(delta, 24));
157 } 278 }
158 } 279 }
159 280
160 return !frame_timestamp->is_null(); 281 return true;
282 }
283
284 void VideoCaptureOracle::RecordConsumerFeedback(int frame_number,
285 double resource_utilization) {
286 if (!std::isfinite(resource_utilization) || resource_utilization <= 0.0)
hubbe 2015/06/23 20:04:31 Bug hiding! However, these values come from the no
miu 2015/06/25 20:48:59 Done.
287 return; // Invalid data point.
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);
303 pessimistic_capable_area_.Update(area_at_full_utilization, timestamp);
161 } 304 }
162 305
163 base::TimeTicks VideoCaptureOracle::GetFrameTimestamp(int frame_number) const { 306 base::TimeTicks VideoCaptureOracle::GetFrameTimestamp(int frame_number) const {
164 DCHECK_LE(frame_number, next_frame_number_); 307 DCHECK(IsFrameInRecentHistory(frame_number));
165 DCHECK_LT(next_frame_number_ - frame_number, kMaxFrameTimestamps);
166 return frame_timestamps_[frame_number % kMaxFrameTimestamps]; 308 return frame_timestamps_[frame_number % kMaxFrameTimestamps];
167 } 309 }
168 310
169 void VideoCaptureOracle::SetFrameTimestamp(int frame_number, 311 void VideoCaptureOracle::SetFrameTimestamp(int frame_number,
170 base::TimeTicks timestamp) { 312 base::TimeTicks timestamp) {
313 DCHECK(IsFrameInRecentHistory(frame_number));
171 frame_timestamps_[frame_number % kMaxFrameTimestamps] = timestamp; 314 frame_timestamps_[frame_number % kMaxFrameTimestamps] = timestamp;
172 } 315 }
173 316
317 bool VideoCaptureOracle::IsFrameInRecentHistory(int frame_number) const {
318 return ((next_frame_number_ - frame_number) < kMaxFrameTimestamps &&
319 frame_number <= next_frame_number_ &&
320 frame_number >= 0);
321 }
322
323 void VideoCaptureOracle::CommitCaptureSizeAndReset(
324 base::TimeTicks last_frame_time) {
325 capture_size_ = resolution_chooser_.capture_size();
326 VLOG(2) << "Now proposing a capture size of " << capture_size_.ToString();
327
328 // Reset each short-term feedback accumulator with a stable-state starting
329 // value.
330 const base::TimeTicks ignore_before_time = JustAfter(last_frame_time);
331 buffer_pool_utilization_.Reset(1.0, ignore_before_time);
332 estimated_capable_area_.Reset(capture_size_.GetArea(), ignore_before_time);
333
334 ResetPessimisticAccumulators(last_frame_time);
335 }
336
337 void VideoCaptureOracle::ResetPessimisticAccumulators(
338 base::TimeTicks event_time) {
339 // Reset each pessimistic feedback accumulator with "overloaded" values so
340 // that a long period of time must pass before they produce a result that
341 // would encourage increasing the capture size.
342 const base::TimeTicks ignore_before_time = JustAfter(event_time);
343 pessimistic_pool_utilization_.Reset(1.5, ignore_before_time);
hubbe 2015/06/23 20:04:31 1.5? constant?
miu 2015/06/25 20:48:59 Not applicable, since I got rid of these extra acc
344 pessimistic_capable_area_.Reset(0.0, ignore_before_time);
345 }
346
347 void VideoCaptureOracle::AnalyzeAndAdjust(const base::TimeTicks analyze_time) {
348 const int decreased_area = AnalyzeForDecreasedArea(analyze_time);
349 if (decreased_area > 0) {
350 ResetPessimisticAccumulators(analyze_time);
351 resolution_chooser_.SetTargetFrameArea(decreased_area);
352 return;
353 }
354
355 const int increased_area = AnalyzeForIncreasedArea(analyze_time);
356 if (increased_area > 0) {
357 resolution_chooser_.SetTargetFrameArea(increased_area);
358 return;
359 }
360
361 // Explicitly set the target frame area to the current capture area. This
362 // cancels-out the results of a previous call to this method, where the
363 // |resolution_chooser_| may have been instructed to increase or decrease the
364 // capture size. Conditions may have changed since then which indicate no
365 // change should be committed (via CommitCaptureSizeAndReset()).
366 resolution_chooser_.SetTargetFrameArea(capture_size_.GetArea());
367 }
368
369 int VideoCaptureOracle::AnalyzeForDecreasedArea(base::TimeTicks analyze_time) {
370 const int current_area = capture_size_.GetArea();
371 DCHECK_GT(current_area, 0);
372
373 // Translate the recent-average buffer pool utilization to be in terms of
374 // "capable number of pixels per frame," for an apples-to-apples comparison
375 // below.
376 int buffer_capable_area;
377 if (HasSufficientRecentFeedback(buffer_pool_utilization_, analyze_time) &&
378 buffer_pool_utilization_.current() > 1.0) {
379 // This calculation is hand-wavy, but seems to work well in a variety of
380 // situations.
381 buffer_capable_area = static_cast<int>(
382 current_area / buffer_pool_utilization_.current());
383 } else {
384 buffer_capable_area = current_area;
385 }
386
387 int consumer_capable_area;
388 if (HasSufficientRecentFeedback(estimated_capable_area_, analyze_time)) {
389 consumer_capable_area =
390 base::saturated_cast<int>(estimated_capable_area_.current());
391 } else {
392 consumer_capable_area = current_area;
393 }
394
395 // If either of the "capable areas" is less than the current capture area,
396 // decrease the capture area by AT LEAST one step.
397 int decreased_area = -1;
398 const int capable_area = std::min(buffer_capable_area, consumer_capable_area);
399 if (capable_area < current_area) {
400 decreased_area = std::min(
401 capable_area,
402 resolution_chooser_.FindSnappedFrameSize(current_area, -1).GetArea());
403 VLOG(2) << "Proposing a "
404 << (100.0 * (current_area - decreased_area) / current_area)
405 << "% decrease in capture area. :-(";
406 }
407
408 // Always log the capability interpretations at verbose logging level 3. At
409 // level 2, only log when when proposing a decreased area.
410 VLOG(decreased_area == -1 ? 3 : 2)
411 << "Capability of pool=" << (100.0 * buffer_capable_area / current_area)
412 << "%, consumer=" << (100.0 * consumer_capable_area / current_area)
413 << '%';
414
415 return decreased_area;
416 }
417
418 int VideoCaptureOracle::AnalyzeForIncreasedArea(base::TimeTicks analyze_time) {
419 // While content is animating, be pessimistic about increasing the capture
420 // area since the end-to-end system should prove, over a longer period of
421 // time, that it will be able to handle increased load without dropping a
422 // frame. Otherwise, be aggressive about increasing the capture area to
423 // improve the quality of non-animating content (where frame drops are not as
424 // important).
425 const bool use_pessimistic_outlook =
426 (analyze_time - last_time_animation_was_detected_).InMicroseconds() <
427 kPessimisticWatchPeriodMicros;
428 DCHECK(!content_sampler_.HasProposal() || use_pessimistic_outlook);
429
430 // If buffer pool utilization is already at maximum, do not propose an
431 // increase in area.
432 FeedbackSignalAccumulator* const pool_utilization = use_pessimistic_outlook ?
433 &pessimistic_pool_utilization_ : &buffer_pool_utilization_;
434 if (!HasSufficientRecentFeedback(*pool_utilization, analyze_time) ||
435 pool_utilization->current() >= 1.0) {
436 return -1;
437 }
438
439 // Compute what one step up in capture size/area would be. If the current
440 // area is already at the maximum, no further analysis is necessary.
441 const int current_area = capture_size_.GetArea();
442 const int increased_area =
443 resolution_chooser_.FindSnappedFrameSize(current_area, +1).GetArea();
444 if (increased_area <= current_area)
445 return -1;
446
447 // Determine whether the consumer could handle an increase in area.
448 FeedbackSignalAccumulator* const capable_area = use_pessimistic_outlook ?
449 &pessimistic_capable_area_ : &estimated_capable_area_;
450 if (HasSufficientRecentFeedback(*capable_area, analyze_time)) {
451 if (capable_area->current() < increased_area)
452 return -1; // Not capable.
453 } else if (estimated_capable_area_.update_time() ==
454 estimated_capable_area_.reset_time()) {
hubbe 2015/06/23 20:04:31 odd indentation
miu 2015/06/25 20:48:59 Done.
455 // The consumer does not provide any feedback. In this case, the consumer's
456 // capability isn't a consideration.
457 } else {
458 // Consumer is providing feedback, but hasn't reported it recently. Just in
459 // case it's stalled, don't make things worse by increasing the capture
460 // area.
461 return -1;
462 }
463
464 VLOG(2) << "Proposing a "
465 << (100.0 * (increased_area - current_area) / current_area)
466 << "% increase in capture area based on "
467 << (use_pessimistic_outlook ? "pessimistic" : "short-term")
468 << " outlook. :-)";
469
470 return increased_area;
471 }
472
174 } // namespace media 473 } // namespace media
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698