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
3 // found in the LICENSE file.
4
5 #include "media/capture/content/video_capture_oracle.h"
6
7 #include "base/strings/stringprintf.h"
8 #include "testing/gtest/include/gtest/gtest.h"
9
10 namespace media {
11
12 namespace {
13
InitialTestTimeTicks()14 base::TimeTicks InitialTestTimeTicks() {
15 return base::TimeTicks() + base::TimeDelta::FromSeconds(1);
16 }
17
Get30HzPeriod()18 base::TimeDelta Get30HzPeriod() {
19 return base::TimeDelta::FromSeconds(1) / 30;
20 }
21
Get1080pSize()22 gfx::Size Get1080pSize() {
23 return gfx::Size(1920, 1080);
24 }
25
Get720pSize()26 gfx::Size Get720pSize() {
27 return gfx::Size(1280, 720);
28 }
29
Get360pSize()30 gfx::Size Get360pSize() {
31 return gfx::Size(640, 360);
32 }
33
GetSmallestNonEmptySize()34 gfx::Size GetSmallestNonEmptySize() {
35 return gfx::Size(2, 2);
36 }
37
38 } // namespace
39
40 // Tests that VideoCaptureOracle filters out events whose timestamps are
41 // decreasing.
TEST(VideoCaptureOracleTest,EnforcesEventTimeMonotonicity)42 TEST(VideoCaptureOracleTest, EnforcesEventTimeMonotonicity) {
43 const gfx::Rect damage_rect(Get720pSize());
44 const base::TimeDelta event_increment = Get30HzPeriod() * 2;
45
46 VideoCaptureOracle oracle(false);
47 oracle.SetMinCapturePeriod(Get30HzPeriod());
48 oracle.SetCaptureSizeConstraints(Get720pSize(), Get720pSize(), false);
49
50 base::TimeTicks t = InitialTestTimeTicks();
51 for (int i = 0; i < 10; ++i) {
52 t += event_increment;
53 ASSERT_TRUE(oracle.ObserveEventAndDecideCapture(
54 VideoCaptureOracle::kCompositorUpdate, damage_rect, t));
55 }
56
57 base::TimeTicks furthest_event_time = t;
58 for (int i = 0; i < 10; ++i) {
59 t -= event_increment;
60 ASSERT_FALSE(oracle.ObserveEventAndDecideCapture(
61 VideoCaptureOracle::kCompositorUpdate, damage_rect, t));
62 }
63
64 t = furthest_event_time;
65 for (int i = 0; i < 10; ++i) {
66 t += event_increment;
67 ASSERT_TRUE(oracle.ObserveEventAndDecideCapture(
68 VideoCaptureOracle::kCompositorUpdate, damage_rect, t));
69 }
70 }
71
72 // Tests that VideoCaptureOracle is enforcing the requirement that
73 // successfully captured frames are delivered in order. Otherwise, downstream
74 // consumers could be tripped-up by out-of-order frames or frame timestamps.
TEST(VideoCaptureOracleTest,EnforcesFramesDeliveredInOrder)75 TEST(VideoCaptureOracleTest, EnforcesFramesDeliveredInOrder) {
76 const gfx::Rect damage_rect(Get720pSize());
77 const base::TimeDelta event_increment = Get30HzPeriod() * 2;
78
79 VideoCaptureOracle oracle(false);
80 oracle.SetMinCapturePeriod(Get30HzPeriod());
81 oracle.SetCaptureSizeConstraints(Get720pSize(), Get720pSize(), false);
82
83 // Most basic scenario: Frames delivered one at a time, with no additional
84 // captures in-between deliveries.
85 base::TimeTicks t = InitialTestTimeTicks();
86 int last_frame_number;
87 base::TimeTicks ignored;
88 for (int i = 0; i < 10; ++i) {
89 t += event_increment;
90 ASSERT_TRUE(oracle.ObserveEventAndDecideCapture(
91 VideoCaptureOracle::kCompositorUpdate, damage_rect, t));
92 last_frame_number = oracle.next_frame_number();
93 oracle.RecordCapture(0.0);
94 ASSERT_TRUE(oracle.CompleteCapture(last_frame_number, true, &ignored));
95 }
96
97 // Basic pipelined scenario: More than one frame in-flight at delivery points.
98 for (int i = 0; i < 50; ++i) {
99 const int num_in_flight = 1 + i % 3;
100 for (int j = 0; j < num_in_flight; ++j) {
101 t += event_increment;
102 ASSERT_TRUE(oracle.ObserveEventAndDecideCapture(
103 VideoCaptureOracle::kCompositorUpdate, damage_rect, t));
104 last_frame_number = oracle.next_frame_number();
105 oracle.RecordCapture(0.0);
106 }
107 for (int j = num_in_flight - 1; j >= 0; --j) {
108 ASSERT_TRUE(
109 oracle.CompleteCapture(last_frame_number - j, true, &ignored));
110 }
111 }
112
113 // Pipelined scenario with successful out-of-order delivery attempts
114 // rejected.
115 for (int i = 0; i < 50; ++i) {
116 const int num_in_flight = 1 + i % 3;
117 for (int j = 0; j < num_in_flight; ++j) {
118 t += event_increment;
119 ASSERT_TRUE(oracle.ObserveEventAndDecideCapture(
120 VideoCaptureOracle::kCompositorUpdate, damage_rect, t));
121 last_frame_number = oracle.next_frame_number();
122 oracle.RecordCapture(0.0);
123 }
124 ASSERT_TRUE(oracle.CompleteCapture(last_frame_number, true, &ignored));
125 for (int j = 1; j < num_in_flight; ++j) {
126 ASSERT_FALSE(
127 oracle.CompleteCapture(last_frame_number - j, true, &ignored));
128 }
129 }
130
131 // Pipelined scenario with successful delivery attempts accepted after an
132 // unsuccessful out of order delivery attempt.
133 for (int i = 0; i < 50; ++i) {
134 const int num_in_flight = 1 + i % 3;
135 for (int j = 0; j < num_in_flight; ++j) {
136 t += event_increment;
137 ASSERT_TRUE(oracle.ObserveEventAndDecideCapture(
138 VideoCaptureOracle::kCompositorUpdate, damage_rect, t));
139 last_frame_number = oracle.next_frame_number();
140 oracle.RecordCapture(0.0);
141 }
142 // Report the last frame as an out of order failure.
143 ASSERT_FALSE(oracle.CompleteCapture(last_frame_number, false, &ignored));
144 for (int j = 1; j < num_in_flight - 1; ++j) {
145 ASSERT_TRUE(
146 oracle.CompleteCapture(last_frame_number - j, true, &ignored));
147 }
148 }
149 }
150
151 // Tests that VideoCaptureOracle transitions between using its two samplers in a
152 // way that does not introduce severe jank, pauses, etc.
TEST(VideoCaptureOracleTest,TransitionsSmoothlyBetweenSamplers)153 TEST(VideoCaptureOracleTest, TransitionsSmoothlyBetweenSamplers) {
154 const gfx::Rect animation_damage_rect(Get720pSize());
155 const base::TimeDelta event_increment = Get30HzPeriod() * 2;
156
157 VideoCaptureOracle oracle(false);
158 oracle.SetMinCapturePeriod(Get30HzPeriod());
159 oracle.SetCaptureSizeConstraints(Get720pSize(), Get720pSize(), false);
160
161 // Run sequences of animation events and non-animation events through the
162 // oracle. As the oracle transitions between each sampler, make sure the
163 // frame timestamps won't trip-up downstream consumers.
164 base::TimeTicks t = InitialTestTimeTicks();
165 base::TimeTicks last_frame_timestamp;
166 for (int i = 0; i < 1000; ++i) {
167 t += event_increment;
168
169 // For every 100 events, provide 50 that will cause the
170 // AnimatedContentSampler to lock-in, followed by 50 that will cause it to
171 // lock-out (i.e., the oracle will use the SmoothEventSampler instead).
172 const bool provide_animated_content_event =
173 (i % 100) >= 25 && (i % 100) < 75;
174
175 // Only the few events that trigger the lock-out transition should be
176 // dropped, because the AnimatedContentSampler doesn't yet realize the
177 // animation ended. Otherwise, the oracle should always decide to sample
178 // because one of its samplers says to.
179 const bool require_oracle_says_sample = (i % 100) < 75 || (i % 100) >= 78;
180 const bool oracle_says_sample = oracle.ObserveEventAndDecideCapture(
181 VideoCaptureOracle::kCompositorUpdate,
182 provide_animated_content_event ? animation_damage_rect : gfx::Rect(),
183 t);
184 if (require_oracle_says_sample)
185 ASSERT_TRUE(oracle_says_sample);
186 if (!oracle_says_sample) {
187 ASSERT_EQ(base::TimeDelta(), oracle.estimated_frame_duration());
188 continue;
189 }
190 ASSERT_LT(base::TimeDelta(), oracle.estimated_frame_duration());
191
192 const int frame_number = oracle.next_frame_number();
193 oracle.RecordCapture(0.0);
194
195 base::TimeTicks frame_timestamp;
196 ASSERT_TRUE(oracle.CompleteCapture(frame_number, true, &frame_timestamp));
197 ASSERT_FALSE(frame_timestamp.is_null());
198 if (!last_frame_timestamp.is_null()) {
199 const base::TimeDelta delta = frame_timestamp - last_frame_timestamp;
200 EXPECT_LE(event_increment.InMicroseconds(), delta.InMicroseconds());
201 // Right after the AnimatedContentSampler lock-out transition, there were
202 // a few frames dropped, so allow a gap in the timestamps. Otherwise, the
203 // delta between frame timestamps should never be more than 2X the
204 // |event_increment|.
205 const base::TimeDelta max_acceptable_delta =
206 (i % 100) == 78 ? event_increment * 5 : event_increment * 2;
207 EXPECT_GE(max_acceptable_delta.InMicroseconds(), delta.InMicroseconds());
208 }
209 last_frame_timestamp = frame_timestamp;
210 }
211 }
212
213 // Tests that VideoCaptureOracle prevents refresh request events from initiating
214 // simultaneous captures.
TEST(VideoCaptureOracleTest,SamplesAtCorrectTimesAroundRefreshRequests)215 TEST(VideoCaptureOracleTest, SamplesAtCorrectTimesAroundRefreshRequests) {
216 const base::TimeDelta vsync_interval = base::TimeDelta::FromSeconds(1) / 60;
217 const base::TimeDelta refresh_interval =
218 base::TimeDelta::FromMilliseconds(125); // 8 FPS
219
220 VideoCaptureOracle oracle(false);
221 oracle.SetMinCapturePeriod(Get30HzPeriod());
222 oracle.SetCaptureSizeConstraints(Get720pSize(), Get720pSize(), false);
223
224 // Have the oracle observe some compositor events. Simulate that each capture
225 // completes successfully.
226 base::TimeTicks t = InitialTestTimeTicks();
227 base::TimeTicks ignored;
228 bool did_complete_a_capture = false;
229 for (int i = 0; i < 10; ++i) {
230 t += vsync_interval;
231 if (oracle.ObserveEventAndDecideCapture(
232 VideoCaptureOracle::kCompositorUpdate, gfx::Rect(), t)) {
233 const int frame_number = oracle.next_frame_number();
234 oracle.RecordCapture(0.0);
235 ASSERT_TRUE(oracle.CompleteCapture(frame_number, true, &ignored));
236 did_complete_a_capture = true;
237 }
238 }
239 ASSERT_TRUE(did_complete_a_capture);
240
241 // Start one more compositor-based capture, but do not notify of completion
242 // yet.
243 for (int i = 0; i <= 10; ++i) {
244 ASSERT_GT(10, i) << "BUG: Seems like it'll never happen!";
245 t += vsync_interval;
246 if (oracle.ObserveEventAndDecideCapture(
247 VideoCaptureOracle::kCompositorUpdate, gfx::Rect(), t)) {
248 break;
249 }
250 }
251 int frame_number = oracle.next_frame_number();
252 oracle.RecordCapture(0.0);
253
254 // Stop providing the compositor events and start providing refresh request
255 // events. No overdue samplings should be recommended because of the
256 // not-yet-complete compositor-based capture.
257 for (int i = 0; i < 10; ++i) {
258 t += refresh_interval;
259 ASSERT_FALSE(oracle.ObserveEventAndDecideCapture(
260 VideoCaptureOracle::kRefreshRequest, gfx::Rect(), t));
261 }
262
263 // Now, complete the oustanding compositor-based capture and continue
264 // providing refresh request events. The oracle should start recommending
265 // sampling again.
266 ASSERT_TRUE(oracle.CompleteCapture(frame_number, true, &ignored));
267 did_complete_a_capture = false;
268 for (int i = 0; i < 10; ++i) {
269 t += refresh_interval;
270 if (oracle.ObserveEventAndDecideCapture(VideoCaptureOracle::kRefreshRequest,
271 gfx::Rect(), t)) {
272 const int frame_number = oracle.next_frame_number();
273 oracle.RecordCapture(0.0);
274 ASSERT_TRUE(oracle.CompleteCapture(frame_number, true, &ignored));
275 did_complete_a_capture = true;
276 }
277 }
278 ASSERT_TRUE(did_complete_a_capture);
279
280 // Start one more "refresh" capture, but do not notify of completion yet.
281 for (int i = 0; i <= 10; ++i) {
282 ASSERT_GT(10, i) << "BUG: Seems like it'll never happen!";
283 t += refresh_interval;
284 if (oracle.ObserveEventAndDecideCapture(VideoCaptureOracle::kRefreshRequest,
285 gfx::Rect(), t)) {
286 break;
287 }
288 }
289 frame_number = oracle.next_frame_number();
290 oracle.RecordCapture(0.0);
291
292 // Confirm that the oracle does not recommend sampling until the outstanding
293 // "refresh" capture completes.
294 for (int i = 0; i < 10; ++i) {
295 t += refresh_interval;
296 ASSERT_FALSE(oracle.ObserveEventAndDecideCapture(
297 VideoCaptureOracle::kRefreshRequest, gfx::Rect(), t));
298 }
299 ASSERT_TRUE(oracle.CompleteCapture(frame_number, true, &ignored));
300 for (int i = 0; i <= 10; ++i) {
301 ASSERT_GT(10, i) << "BUG: Seems like it'll never happen!";
302 t += refresh_interval;
303 if (oracle.ObserveEventAndDecideCapture(VideoCaptureOracle::kRefreshRequest,
304 gfx::Rect(), t)) {
305 break;
306 }
307 }
308 }
309
310 // Tests that VideoCaptureOracle does not rapidly change proposed capture sizes,
311 // to allow both the source content and the rest of the end-to-end system to
312 // stabilize.
TEST(VideoCaptureOracleTest,DoesNotRapidlyChangeCaptureSize)313 TEST(VideoCaptureOracleTest, DoesNotRapidlyChangeCaptureSize) {
314 VideoCaptureOracle oracle(true);
315 oracle.SetMinCapturePeriod(Get30HzPeriod());
316 oracle.SetCaptureSizeConstraints(GetSmallestNonEmptySize(), Get720pSize(),
317 false);
318 oracle.SetSourceSize(Get1080pSize());
319
320 // Run 30 seconds of frame captures without any source size changes.
321 base::TimeTicks t = InitialTestTimeTicks();
322 const base::TimeDelta event_increment = Get30HzPeriod() * 2;
323 base::TimeTicks end_t = t + base::TimeDelta::FromSeconds(30);
324 for (; t < end_t; t += event_increment) {
325 ASSERT_TRUE(oracle.ObserveEventAndDecideCapture(
326 VideoCaptureOracle::kCompositorUpdate, gfx::Rect(), t));
327 ASSERT_EQ(Get720pSize(), oracle.capture_size());
328 base::TimeTicks ignored;
329 const int frame_number = oracle.next_frame_number();
330 oracle.RecordCapture(0.0);
331 ASSERT_TRUE(oracle.CompleteCapture(frame_number, true, &ignored));
332 }
333
334 // Now run 30 seconds of frame captures with lots of random source size
335 // changes. Check that there was no more than one size change per second.
336 gfx::Size source_size = oracle.capture_size();
337 base::TimeTicks time_of_last_size_change = InitialTestTimeTicks();
338 gfx::Size last_capture_size = oracle.capture_size();
339 end_t = t + base::TimeDelta::FromSeconds(30);
340 for (; t < end_t; t += event_increment) {
341 // Change the source size every frame to a random non-empty size.
342 const gfx::Size last_source_size = source_size;
343 source_size.SetSize(((last_source_size.width() * 11 + 12345) % 1280) + 1,
344 ((last_source_size.height() * 11 + 12345) % 720) + 1);
345 ASSERT_NE(last_source_size, source_size);
346 oracle.SetSourceSize(source_size);
347
348 ASSERT_TRUE(oracle.ObserveEventAndDecideCapture(
349 VideoCaptureOracle::kCompositorUpdate, gfx::Rect(), t));
350
351 if (oracle.capture_size() != last_capture_size) {
352 ASSERT_GE(t - time_of_last_size_change, base::TimeDelta::FromSeconds(1));
353 time_of_last_size_change = t;
354 last_capture_size = oracle.capture_size();
355 }
356
357 base::TimeTicks ignored;
358 const int frame_number = oracle.next_frame_number();
359 oracle.RecordCapture(0.0);
360 ASSERT_TRUE(oracle.CompleteCapture(frame_number, true, &ignored));
361 }
362 }
363
364 // Tests that VideoCaptureOracle allows every video frame to have a different
365 // size if resize throttling is disabled.
TEST(VideoCaptureOracleTest,ResizeThrottlingDisabled)366 TEST(VideoCaptureOracleTest, ResizeThrottlingDisabled) {
367 VideoCaptureOracle oracle(true);
368 oracle.SetMinCapturePeriod(Get30HzPeriod());
369 oracle.SetMinSizeChangePeriod(base::TimeDelta());
370 oracle.SetCaptureSizeConstraints(GetSmallestNonEmptySize(), Get720pSize(),
371 false);
372 oracle.SetSourceSize(Get1080pSize());
373
374 // Run 30 seconds of frame captures with lots of random source size
375 // changes. The capture size should be different every time.
376 base::TimeTicks t = InitialTestTimeTicks();
377 const base::TimeDelta event_increment = Get30HzPeriod() * 2;
378 base::TimeTicks end_t = t + base::TimeDelta::FromSeconds(30);
379 gfx::Size source_size = oracle.capture_size();
380 gfx::Size last_capture_size = oracle.capture_size();
381 for (; t < end_t; t += event_increment) {
382 // Change the source size every frame to a random non-empty size.
383 const gfx::Size last_source_size = source_size;
384 source_size.SetSize(((last_source_size.width() * 11 + 12345) % 1280) + 1,
385 ((last_source_size.height() * 11 + 12345) % 720) + 1);
386 ASSERT_NE(last_source_size, source_size);
387 oracle.SetSourceSize(source_size);
388
389 ASSERT_TRUE(oracle.ObserveEventAndDecideCapture(
390 VideoCaptureOracle::kCompositorUpdate, gfx::Rect(), t));
391
392 ASSERT_NE(last_capture_size, oracle.capture_size());
393 last_capture_size = oracle.capture_size();
394
395 base::TimeTicks ignored;
396 const int frame_number = oracle.next_frame_number();
397 oracle.RecordCapture(0.0);
398 ASSERT_TRUE(oracle.CompleteCapture(frame_number, true, &ignored));
399 }
400 }
401
402 namespace {
403
404 // Tests that VideoCaptureOracle can auto-throttle by stepping the capture size
405 // up or down. When |is_content_animating| is false, there is more
406 // aggressiveness expected in the timing of stepping upwards. If
407 // |with_consumer_feedback| is false, only buffer pool utilization varies and no
408 // consumer feedback is provided. If |with_consumer_feedback| is true, the
409 // buffer pool utilization is held constant at 25%, and the consumer utilization
410 // feedback varies.
RunAutoThrottleTest(bool is_content_animating,bool with_consumer_feedback)411 void RunAutoThrottleTest(bool is_content_animating,
412 bool with_consumer_feedback) {
413 SCOPED_TRACE(::testing::Message()
414 << "RunAutoThrottleTest("
415 << "(is_content_animating=" << is_content_animating
416 << ", with_consumer_feedback=" << with_consumer_feedback << ")");
417
418 VideoCaptureOracle oracle(true);
419 oracle.SetMinCapturePeriod(Get30HzPeriod());
420 oracle.SetCaptureSizeConstraints(GetSmallestNonEmptySize(), Get720pSize(),
421 false);
422 oracle.SetSourceSize(Get1080pSize());
423
424 // Run 10 seconds of frame captures with 90% utilization expect no capture
425 // size changes.
426 base::TimeTicks t = InitialTestTimeTicks();
427 base::TimeTicks time_of_last_size_change = t;
428 const base::TimeDelta event_increment = Get30HzPeriod() * 2;
429 base::TimeTicks end_t = t + base::TimeDelta::FromSeconds(10);
430 for (; t < end_t; t += event_increment) {
431 ASSERT_TRUE(oracle.ObserveEventAndDecideCapture(
432 VideoCaptureOracle::kCompositorUpdate,
433 is_content_animating ? gfx::Rect(Get720pSize()) : gfx::Rect(), t));
434 ASSERT_EQ(Get720pSize(), oracle.capture_size());
435 const double utilization = 0.9;
436 const int frame_number = oracle.next_frame_number();
437 oracle.RecordCapture(with_consumer_feedback ? 0.25 : utilization);
438 base::TimeTicks ignored;
439 ASSERT_TRUE(oracle.CompleteCapture(frame_number, true, &ignored));
440 if (with_consumer_feedback)
441 oracle.RecordConsumerFeedback(frame_number, utilization);
442 }
443
444 // Cause two downward steppings in resolution. First, indicate overload
445 // until the resolution steps down. Then, indicate a 90% utilization and
446 // expect the resolution to remain constant. Repeat.
447 for (int i = 0; i < 2; ++i) {
448 const gfx::Size starting_size = oracle.capture_size();
449 SCOPED_TRACE(::testing::Message() << "Stepping down from "
450 << starting_size.ToString()
451 << ", i=" << i);
452
453 gfx::Size stepped_down_size;
454 end_t = t + base::TimeDelta::FromSeconds(10);
455 for (; t < end_t; t += event_increment) {
456 ASSERT_TRUE(oracle.ObserveEventAndDecideCapture(
457 VideoCaptureOracle::kCompositorUpdate,
458 is_content_animating ? gfx::Rect(Get720pSize()) : gfx::Rect(), t));
459
460 if (stepped_down_size.IsEmpty()) {
461 if (oracle.capture_size() != starting_size) {
462 time_of_last_size_change = t;
463 stepped_down_size = oracle.capture_size();
464 ASSERT_GT(starting_size.width(), stepped_down_size.width());
465 ASSERT_GT(starting_size.height(), stepped_down_size.height());
466 }
467 } else {
468 ASSERT_EQ(stepped_down_size, oracle.capture_size());
469 }
470
471 const double utilization = stepped_down_size.IsEmpty() ? 1.5 : 0.9;
472 const int frame_number = oracle.next_frame_number();
473 oracle.RecordCapture(with_consumer_feedback ? 0.25 : utilization);
474 base::TimeTicks ignored;
475 ASSERT_TRUE(oracle.CompleteCapture(frame_number, true, &ignored));
476 if (with_consumer_feedback)
477 oracle.RecordConsumerFeedback(frame_number, utilization);
478 }
479 }
480
481 // Now, cause two upward steppings in resolution. First, indicate
482 // under-utilization until the resolution steps up. Then, indicate a 90%
483 // utilization and expect the resolution to remain constant. Repeat.
484 for (int i = 0; i < 2; ++i) {
485 const gfx::Size starting_size = oracle.capture_size();
486 SCOPED_TRACE(::testing::Message() << "Stepping up from "
487 << starting_size.ToString()
488 << ", i=" << i);
489
490 gfx::Size stepped_up_size;
491 end_t = t + base::TimeDelta::FromSeconds(is_content_animating ? 90 : 10);
492 for (; t < end_t; t += event_increment) {
493 ASSERT_TRUE(oracle.ObserveEventAndDecideCapture(
494 VideoCaptureOracle::kCompositorUpdate,
495 is_content_animating ? gfx::Rect(Get720pSize()) : gfx::Rect(), t));
496
497 if (stepped_up_size.IsEmpty()) {
498 if (oracle.capture_size() != starting_size) {
499 // When content is animating, a much longer amount of time must pass
500 // before the capture size will step up.
501 ASSERT_LT(base::TimeDelta::FromSeconds(is_content_animating ? 15 : 1),
502 t - time_of_last_size_change);
503 time_of_last_size_change = t;
504 stepped_up_size = oracle.capture_size();
505 ASSERT_LT(starting_size.width(), stepped_up_size.width());
506 ASSERT_LT(starting_size.height(), stepped_up_size.height());
507 }
508 } else {
509 ASSERT_EQ(stepped_up_size, oracle.capture_size());
510 }
511
512 const double utilization = stepped_up_size.IsEmpty() ? 0.0 : 0.9;
513 const int frame_number = oracle.next_frame_number();
514 oracle.RecordCapture(with_consumer_feedback ? 0.25 : utilization);
515 base::TimeTicks ignored;
516 ASSERT_TRUE(oracle.CompleteCapture(frame_number, true, &ignored));
517 if (with_consumer_feedback)
518 oracle.RecordConsumerFeedback(frame_number, utilization);
519 }
520 }
521 }
522
523 } // namespace
524
525 // Tests that VideoCaptureOracle can auto-throttle by stepping the capture size
526 // up or down, using utilization feedback signals from either the buffer pool or
527 // the consumer, and with slightly different behavior depending on whether
528 // content is animating.
TEST(VideoCaptureOracleTest,AutoThrottlesBasedOnUtilizationFeedback)529 TEST(VideoCaptureOracleTest, AutoThrottlesBasedOnUtilizationFeedback) {
530 RunAutoThrottleTest(false, false);
531 RunAutoThrottleTest(false, true);
532 RunAutoThrottleTest(true, false);
533 RunAutoThrottleTest(true, true);
534 }
535
536 // Tests that, while content is animating, VideoCaptureOracle can make frequent
537 // capture size increases only just after the source size has changed.
538 // Otherwise, capture size increases should only be made cautiously, after a
539 // long "proving period of under-utilization" has elapsed.
TEST(VideoCaptureOracleTest,IncreasesFrequentlyOnlyAfterSourceSizeChange)540 TEST(VideoCaptureOracleTest, IncreasesFrequentlyOnlyAfterSourceSizeChange) {
541 VideoCaptureOracle oracle(true);
542 oracle.SetMinCapturePeriod(Get30HzPeriod());
543 oracle.SetCaptureSizeConstraints(GetSmallestNonEmptySize(), Get720pSize(),
544 false);
545
546 // Start out the source size at 360p, so there is room to grow to the 720p
547 // maximum.
548 oracle.SetSourceSize(Get360pSize());
549
550 // Run 10 seconds of frame captures with under-utilization to represent a
551 // machine that can do more, but won't because the source size is small.
552 base::TimeTicks t = InitialTestTimeTicks();
553 const base::TimeDelta event_increment = Get30HzPeriod() * 2;
554 base::TimeTicks end_t = t + base::TimeDelta::FromSeconds(10);
555 for (; t < end_t; t += event_increment) {
556 if (!oracle.ObserveEventAndDecideCapture(
557 VideoCaptureOracle::kCompositorUpdate, gfx::Rect(Get360pSize()),
558 t)) {
559 continue;
560 }
561 ASSERT_EQ(Get360pSize(), oracle.capture_size());
562 const int frame_number = oracle.next_frame_number();
563 oracle.RecordCapture(0.25);
564 base::TimeTicks ignored;
565 ASSERT_TRUE(oracle.CompleteCapture(frame_number, true, &ignored));
566 }
567
568 // Now, set the source size to 720p, continuing to report under-utilization,
569 // and expect the capture size increases to reach a full 720p within 15
570 // seconds.
571 oracle.SetSourceSize(Get720pSize());
572 gfx::Size last_capture_size = oracle.capture_size();
573 end_t = t + base::TimeDelta::FromSeconds(15);
574 for (; t < end_t; t += event_increment) {
575 if (!oracle.ObserveEventAndDecideCapture(
576 VideoCaptureOracle::kCompositorUpdate, gfx::Rect(Get720pSize()),
577 t)) {
578 continue;
579 }
580 ASSERT_LE(last_capture_size.width(), oracle.capture_size().width());
581 ASSERT_LE(last_capture_size.height(), oracle.capture_size().height());
582 last_capture_size = oracle.capture_size();
583 const int frame_number = oracle.next_frame_number();
584 oracle.RecordCapture(0.25);
585 base::TimeTicks ignored;
586 ASSERT_TRUE(oracle.CompleteCapture(frame_number, true, &ignored));
587 }
588 ASSERT_EQ(Get720pSize(), oracle.capture_size());
589
590 // Now, change the source size again, but report over-utilization so the
591 // capture size will decrease. Once it decreases one step, report 90%
592 // utilization to achieve a steady-state.
593 oracle.SetSourceSize(Get1080pSize());
594 gfx::Size stepped_down_size;
595 end_t = t + base::TimeDelta::FromSeconds(10);
596 for (; t < end_t; t += event_increment) {
597 if (!oracle.ObserveEventAndDecideCapture(
598 VideoCaptureOracle::kCompositorUpdate, gfx::Rect(Get1080pSize()),
599 t)) {
600 continue;
601 }
602
603 if (stepped_down_size.IsEmpty()) {
604 if (oracle.capture_size() != Get720pSize()) {
605 stepped_down_size = oracle.capture_size();
606 ASSERT_GT(Get720pSize().width(), stepped_down_size.width());
607 ASSERT_GT(Get720pSize().height(), stepped_down_size.height());
608 }
609 } else {
610 ASSERT_EQ(stepped_down_size, oracle.capture_size());
611 }
612
613 const double utilization = stepped_down_size.IsEmpty() ? 1.5 : 0.9;
614 const int frame_number = oracle.next_frame_number();
615 oracle.RecordCapture(utilization);
616 base::TimeTicks ignored;
617 ASSERT_TRUE(oracle.CompleteCapture(frame_number, true, &ignored));
618 }
619 ASSERT_FALSE(stepped_down_size.IsEmpty());
620
621 // Now, if we report under-utilization again (without any source size change),
622 // there should be a long "proving period" before there is any increase in
623 // capture size made by the oracle.
624 const base::TimeTicks proving_period_end_time =
625 t + base::TimeDelta::FromSeconds(15);
626 gfx::Size stepped_up_size;
627 end_t = t + base::TimeDelta::FromSeconds(60);
628 for (; t < end_t; t += event_increment) {
629 if (!oracle.ObserveEventAndDecideCapture(
630 VideoCaptureOracle::kCompositorUpdate, gfx::Rect(Get1080pSize()),
631 t)) {
632 continue;
633 }
634
635 if (stepped_up_size.IsEmpty()) {
636 if (oracle.capture_size() != stepped_down_size) {
637 ASSERT_LT(proving_period_end_time, t);
638 stepped_up_size = oracle.capture_size();
639 ASSERT_LT(stepped_down_size.width(), stepped_up_size.width());
640 ASSERT_LT(stepped_down_size.height(), stepped_up_size.height());
641 }
642 } else {
643 ASSERT_EQ(stepped_up_size, oracle.capture_size());
644 }
645
646 const double utilization = stepped_up_size.IsEmpty() ? 0.25 : 0.9;
647 const int frame_number = oracle.next_frame_number();
648 oracle.RecordCapture(utilization);
649 base::TimeTicks ignored;
650 ASSERT_TRUE(oracle.CompleteCapture(frame_number, true, &ignored));
651 }
652 ASSERT_FALSE(stepped_up_size.IsEmpty());
653 }
654
655 // Tests that VideoCaptureOracle does not change the capture size if
656 // auto-throttling is enabled when using a fixed resolution policy.
TEST(VideoCaptureOracleTest,DoesNotAutoThrottleWhenResolutionIsFixed)657 TEST(VideoCaptureOracleTest, DoesNotAutoThrottleWhenResolutionIsFixed) {
658 VideoCaptureOracle oracle(true);
659 oracle.SetMinCapturePeriod(Get30HzPeriod());
660 oracle.SetCaptureSizeConstraints(Get720pSize(), Get720pSize(), false);
661 oracle.SetSourceSize(Get1080pSize());
662
663 // Run 10 seconds of frame captures with 90% utilization expect no capture
664 // size changes.
665 base::TimeTicks t = InitialTestTimeTicks();
666 const base::TimeDelta event_increment = Get30HzPeriod() * 2;
667 base::TimeTicks end_t = t + base::TimeDelta::FromSeconds(10);
668 for (; t < end_t; t += event_increment) {
669 ASSERT_TRUE(oracle.ObserveEventAndDecideCapture(
670 VideoCaptureOracle::kCompositorUpdate, gfx::Rect(), t));
671 ASSERT_EQ(Get720pSize(), oracle.capture_size());
672 base::TimeTicks ignored;
673 const int frame_number = oracle.next_frame_number();
674 oracle.RecordCapture(0.9);
675 ASSERT_TRUE(oracle.CompleteCapture(frame_number, true, &ignored));
676 }
677
678 // Now run 10 seconds with overload indicated. Still, expect no capture size
679 // changes.
680 end_t = t + base::TimeDelta::FromSeconds(10);
681 for (; t < end_t; t += event_increment) {
682 ASSERT_TRUE(oracle.ObserveEventAndDecideCapture(
683 VideoCaptureOracle::kCompositorUpdate, gfx::Rect(), t));
684 ASSERT_EQ(Get720pSize(), oracle.capture_size());
685 base::TimeTicks ignored;
686 const int frame_number = oracle.next_frame_number();
687 oracle.RecordCapture(2.0);
688 ASSERT_TRUE(oracle.CompleteCapture(frame_number, true, &ignored));
689 }
690 }
691
692 } // namespace media
693