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