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.
5 #include "media/capture/content/video_capture_oracle.h"
7 #include "base/strings/stringprintf.h"
8 #include "testing/gtest/include/gtest/gtest.h"
14 base::TimeTicks
InitialTestTimeTicks() {
15 return base::TimeTicks() + base::TimeDelta::FromSeconds(1);
18 base::TimeDelta
Get30HzPeriod() {
19 return base::TimeDelta::FromSeconds(1) / 30;
22 gfx::Size
Get720pSize() {
23 return gfx::Size(1280, 720);
28 // Tests that VideoCaptureOracle filters out events whose timestamps are
30 TEST(VideoCaptureOracleTest
, EnforcesEventTimeMonotonicity
) {
31 const gfx::Rect
damage_rect(Get720pSize());
32 const base::TimeDelta event_increment
= Get30HzPeriod() * 2;
34 VideoCaptureOracle
oracle(Get30HzPeriod(), Get720pSize(),
35 media::RESOLUTION_POLICY_FIXED_RESOLUTION
, false);
37 base::TimeTicks t
= InitialTestTimeTicks();
38 for (int i
= 0; i
< 10; ++i
) {
40 ASSERT_TRUE(oracle
.ObserveEventAndDecideCapture(
41 VideoCaptureOracle::kCompositorUpdate
, damage_rect
, t
));
44 base::TimeTicks furthest_event_time
= t
;
45 for (int i
= 0; i
< 10; ++i
) {
47 ASSERT_FALSE(oracle
.ObserveEventAndDecideCapture(
48 VideoCaptureOracle::kCompositorUpdate
, damage_rect
, t
));
51 t
= furthest_event_time
;
52 for (int i
= 0; i
< 10; ++i
) {
54 ASSERT_TRUE(oracle
.ObserveEventAndDecideCapture(
55 VideoCaptureOracle::kCompositorUpdate
, damage_rect
, t
));
59 // Tests that VideoCaptureOracle is enforcing the requirement that
60 // successfully captured frames are delivered in order. Otherwise, downstream
61 // consumers could be tripped-up by out-of-order frames or frame timestamps.
62 TEST(VideoCaptureOracleTest
, EnforcesFramesDeliveredInOrder
) {
63 const gfx::Rect
damage_rect(Get720pSize());
64 const base::TimeDelta event_increment
= Get30HzPeriod() * 2;
66 VideoCaptureOracle
oracle(Get30HzPeriod(), Get720pSize(),
67 media::RESOLUTION_POLICY_FIXED_RESOLUTION
, false);
69 // Most basic scenario: Frames delivered one at a time, with no additional
70 // captures in-between deliveries.
71 base::TimeTicks t
= InitialTestTimeTicks();
72 int last_frame_number
;
73 base::TimeTicks ignored
;
74 for (int i
= 0; i
< 10; ++i
) {
76 ASSERT_TRUE(oracle
.ObserveEventAndDecideCapture(
77 VideoCaptureOracle::kCompositorUpdate
, damage_rect
, t
));
78 last_frame_number
= oracle
.RecordCapture(0.0);
79 ASSERT_TRUE(oracle
.CompleteCapture(last_frame_number
, true, &ignored
));
82 // Basic pipelined scenario: More than one frame in-flight at delivery points.
83 for (int i
= 0; i
< 50; ++i
) {
84 const int num_in_flight
= 1 + i
% 3;
85 for (int j
= 0; j
< num_in_flight
; ++j
) {
87 ASSERT_TRUE(oracle
.ObserveEventAndDecideCapture(
88 VideoCaptureOracle::kCompositorUpdate
, damage_rect
, t
));
89 last_frame_number
= oracle
.RecordCapture(0.0);
91 for (int j
= num_in_flight
- 1; j
>= 0; --j
) {
93 oracle
.CompleteCapture(last_frame_number
- j
, true, &ignored
));
97 // Pipelined scenario with successful out-of-order delivery attempts
99 for (int i
= 0; i
< 50; ++i
) {
100 const int num_in_flight
= 1 + i
% 3;
101 for (int j
= 0; j
< num_in_flight
; ++j
) {
102 t
+= event_increment
;
103 ASSERT_TRUE(oracle
.ObserveEventAndDecideCapture(
104 VideoCaptureOracle::kCompositorUpdate
, damage_rect
, t
));
105 last_frame_number
= oracle
.RecordCapture(0.0);
107 ASSERT_TRUE(oracle
.CompleteCapture(last_frame_number
, true, &ignored
));
108 for (int j
= 1; j
< num_in_flight
; ++j
) {
110 oracle
.CompleteCapture(last_frame_number
- j
, true, &ignored
));
114 // Pipelined scenario with successful delivery attempts accepted after an
115 // unsuccessful out of order delivery attempt.
116 for (int i
= 0; i
< 50; ++i
) {
117 const int num_in_flight
= 1 + i
% 3;
118 for (int j
= 0; j
< num_in_flight
; ++j
) {
119 t
+= event_increment
;
120 ASSERT_TRUE(oracle
.ObserveEventAndDecideCapture(
121 VideoCaptureOracle::kCompositorUpdate
, damage_rect
, t
));
122 last_frame_number
= oracle
.RecordCapture(0.0);
124 // Report the last frame as an out of order failure.
125 ASSERT_FALSE(oracle
.CompleteCapture(last_frame_number
, false, &ignored
));
126 for (int j
= 1; j
< num_in_flight
- 1; ++j
) {
128 oracle
.CompleteCapture(last_frame_number
- j
, true, &ignored
));
133 // Tests that VideoCaptureOracle transitions between using its two samplers in a
134 // way that does not introduce severe jank, pauses, etc.
135 TEST(VideoCaptureOracleTest
, TransitionsSmoothlyBetweenSamplers
) {
136 const gfx::Rect
animation_damage_rect(Get720pSize());
137 const base::TimeDelta event_increment
= Get30HzPeriod() * 2;
139 VideoCaptureOracle
oracle(Get30HzPeriod(), Get720pSize(),
140 media::RESOLUTION_POLICY_FIXED_RESOLUTION
, false);
142 // Run sequences of animation events and non-animation events through the
143 // oracle. As the oracle transitions between each sampler, make sure the
144 // frame timestamps won't trip-up downstream consumers.
145 base::TimeTicks t
= InitialTestTimeTicks();
146 base::TimeTicks last_frame_timestamp
;
147 for (int i
= 0; i
< 1000; ++i
) {
148 t
+= event_increment
;
150 // For every 100 events, provide 50 that will cause the
151 // AnimatedContentSampler to lock-in, followed by 50 that will cause it to
152 // lock-out (i.e., the oracle will use the SmoothEventSampler instead).
153 const bool provide_animated_content_event
=
154 (i
% 100) >= 25 && (i
% 100) < 75;
156 // Only the few events that trigger the lock-out transition should be
157 // dropped, because the AnimatedContentSampler doesn't yet realize the
158 // animation ended. Otherwise, the oracle should always decide to sample
159 // because one of its samplers says to.
160 const bool require_oracle_says_sample
= (i
% 100) < 75 || (i
% 100) >= 78;
161 const bool oracle_says_sample
= oracle
.ObserveEventAndDecideCapture(
162 VideoCaptureOracle::kCompositorUpdate
,
163 provide_animated_content_event
? animation_damage_rect
: gfx::Rect(),
165 if (require_oracle_says_sample
)
166 ASSERT_TRUE(oracle_says_sample
);
167 if (!oracle_says_sample
) {
168 ASSERT_EQ(base::TimeDelta(), oracle
.estimated_frame_duration());
171 ASSERT_LT(base::TimeDelta(), oracle
.estimated_frame_duration());
173 const int frame_number
= oracle
.RecordCapture(0.0);
175 base::TimeTicks frame_timestamp
;
176 ASSERT_TRUE(oracle
.CompleteCapture(frame_number
, true, &frame_timestamp
));
177 ASSERT_FALSE(frame_timestamp
.is_null());
178 if (!last_frame_timestamp
.is_null()) {
179 const base::TimeDelta delta
= frame_timestamp
- last_frame_timestamp
;
180 EXPECT_LE(event_increment
.InMicroseconds(), delta
.InMicroseconds());
181 // Right after the AnimatedContentSampler lock-out transition, there were
182 // a few frames dropped, so allow a gap in the timestamps. Otherwise, the
183 // delta between frame timestamps should never be more than 2X the
184 // |event_increment|.
185 const base::TimeDelta max_acceptable_delta
=
186 (i
% 100) == 78 ? event_increment
* 5 : event_increment
* 2;
187 EXPECT_GE(max_acceptable_delta
.InMicroseconds(), delta
.InMicroseconds());
189 last_frame_timestamp
= frame_timestamp
;
193 // Tests that VideoCaptureOracle prevents timer polling from initiating
194 // simultaneous captures.
195 TEST(VideoCaptureOracleTest
, SamplesOnlyOneOverdueFrameAtATime
) {
196 const base::TimeDelta vsync_interval
= base::TimeDelta::FromSeconds(1) / 60;
197 const base::TimeDelta timer_interval
= base::TimeDelta::FromMilliseconds(
198 VideoCaptureOracle::kMinTimerPollPeriodMillis
);
200 VideoCaptureOracle
oracle(Get30HzPeriod(), Get720pSize(),
201 media::RESOLUTION_POLICY_FIXED_RESOLUTION
, false);
203 // Have the oracle observe some compositor events. Simulate that each capture
204 // completes successfully.
205 base::TimeTicks t
= InitialTestTimeTicks();
206 base::TimeTicks ignored
;
207 bool did_complete_a_capture
= false;
208 for (int i
= 0; i
< 10; ++i
) {
210 if (oracle
.ObserveEventAndDecideCapture(
211 VideoCaptureOracle::kCompositorUpdate
, gfx::Rect(), t
)) {
213 oracle
.CompleteCapture(oracle
.RecordCapture(0.0), true, &ignored
));
214 did_complete_a_capture
= true;
217 ASSERT_TRUE(did_complete_a_capture
);
219 // Start one more compositor-based capture, but do not notify of completion
221 for (int i
= 0; i
<= 10; ++i
) {
222 ASSERT_GT(10, i
) << "BUG: Seems like it'll never happen!";
224 if (oracle
.ObserveEventAndDecideCapture(
225 VideoCaptureOracle::kCompositorUpdate
, gfx::Rect(), t
)) {
229 int frame_number
= oracle
.RecordCapture(0.0);
231 // Stop providing the compositor events and start providing timer polling
232 // events. No overdue samplings should be recommended because of the
233 // not-yet-complete compositor-based capture.
234 for (int i
= 0; i
< 10; ++i
) {
236 ASSERT_FALSE(oracle
.ObserveEventAndDecideCapture(
237 VideoCaptureOracle::kTimerPoll
, gfx::Rect(), t
));
240 // Now, complete the oustanding compositor-based capture and continue
241 // providing timer polling events. The oracle should start recommending
243 ASSERT_TRUE(oracle
.CompleteCapture(frame_number
, true, &ignored
));
244 did_complete_a_capture
= false;
245 for (int i
= 0; i
< 10; ++i
) {
247 if (oracle
.ObserveEventAndDecideCapture(VideoCaptureOracle::kTimerPoll
,
250 oracle
.CompleteCapture(oracle
.RecordCapture(0.0), true, &ignored
));
251 did_complete_a_capture
= true;
254 ASSERT_TRUE(did_complete_a_capture
);
256 // Start one more timer-based capture, but do not notify of completion yet.
257 for (int i
= 0; i
<= 10; ++i
) {
258 ASSERT_GT(10, i
) << "BUG: Seems like it'll never happen!";
260 if (oracle
.ObserveEventAndDecideCapture(VideoCaptureOracle::kTimerPoll
,
265 frame_number
= oracle
.RecordCapture(0.0);
267 // Confirm that the oracle does not recommend sampling until the outstanding
268 // timer-based capture completes.
269 for (int i
= 0; i
< 10; ++i
) {
271 ASSERT_FALSE(oracle
.ObserveEventAndDecideCapture(
272 VideoCaptureOracle::kTimerPoll
, gfx::Rect(), t
));
274 ASSERT_TRUE(oracle
.CompleteCapture(frame_number
, true, &ignored
));
275 for (int i
= 0; i
<= 10; ++i
) {
276 ASSERT_GT(10, i
) << "BUG: Seems like it'll never happen!";
278 if (oracle
.ObserveEventAndDecideCapture(VideoCaptureOracle::kTimerPoll
,
285 // Tests that VideoCaptureOracle does not rapidly change proposed capture sizes,
286 // to allow both the source content and the rest of the end-to-end system to
288 TEST(VideoCaptureOracleTest
, DoesNotRapidlyChangeCaptureSize
) {
289 VideoCaptureOracle
oracle(Get30HzPeriod(), Get720pSize(),
290 media::RESOLUTION_POLICY_ANY_WITHIN_LIMIT
, true);
292 // Run 30 seconds of frame captures without any source size changes.
293 base::TimeTicks t
= InitialTestTimeTicks();
294 const base::TimeDelta event_increment
= Get30HzPeriod() * 2;
295 base::TimeTicks end_t
= t
+ base::TimeDelta::FromSeconds(30);
296 for (; t
< end_t
; t
+= event_increment
) {
297 ASSERT_TRUE(oracle
.ObserveEventAndDecideCapture(
298 VideoCaptureOracle::kCompositorUpdate
, gfx::Rect(), t
));
299 ASSERT_EQ(Get720pSize(), oracle
.capture_size());
300 base::TimeTicks ignored
;
302 oracle
.CompleteCapture(oracle
.RecordCapture(0.0), true, &ignored
));
305 // Now run 30 seconds of frame captures with lots of random source size
306 // changes. Check that there was no more than one size change per second.
307 gfx::Size source_size
= oracle
.capture_size();
308 base::TimeTicks time_of_last_size_change
= InitialTestTimeTicks();
309 gfx::Size last_capture_size
= oracle
.capture_size();
310 end_t
= t
+ base::TimeDelta::FromSeconds(30);
311 for (; t
< end_t
; t
+= event_increment
) {
312 // Change the source size every frame to a random non-empty size.
313 const gfx::Size last_source_size
= source_size
;
314 source_size
.SetSize(((last_source_size
.width() * 11 + 12345) % 1280) + 1,
315 ((last_source_size
.height() * 11 + 12345) % 720) + 1);
316 ASSERT_NE(last_source_size
, source_size
);
317 oracle
.SetSourceSize(source_size
);
319 ASSERT_TRUE(oracle
.ObserveEventAndDecideCapture(
320 VideoCaptureOracle::kCompositorUpdate
, gfx::Rect(), t
));
322 if (oracle
.capture_size() != last_capture_size
) {
323 ASSERT_GE(t
- time_of_last_size_change
, base::TimeDelta::FromSeconds(1));
324 time_of_last_size_change
= t
;
325 last_capture_size
= oracle
.capture_size();
328 base::TimeTicks ignored
;
330 oracle
.CompleteCapture(oracle
.RecordCapture(0.0), true, &ignored
));
336 // Tests that VideoCaptureOracle can auto-throttle by stepping the capture size
337 // up or down. When |is_content_animating| is false, there is more
338 // aggressiveness expected in the timing of stepping upwards. If
339 // |with_consumer_feedback| is false, only buffer pool utilization varies and no
340 // consumer feedback is provided. If |with_consumer_feedback| is true, the
341 // buffer pool utilization is held constant at 25%, and the consumer utilization
343 void RunAutoThrottleTest(bool is_content_animating
,
344 bool with_consumer_feedback
) {
345 SCOPED_TRACE(::testing::Message()
346 << "RunAutoThrottleTest("
347 << "(is_content_animating=" << is_content_animating
348 << ", with_consumer_feedback=" << with_consumer_feedback
<< ")");
350 VideoCaptureOracle
oracle(Get30HzPeriod(), Get720pSize(),
351 media::RESOLUTION_POLICY_ANY_WITHIN_LIMIT
, true);
353 // Run 10 seconds of frame captures with 90% utilization expect no capture
355 base::TimeTicks t
= InitialTestTimeTicks();
356 base::TimeTicks time_of_last_size_change
= t
;
357 const base::TimeDelta event_increment
= Get30HzPeriod() * 2;
358 base::TimeTicks end_t
= t
+ base::TimeDelta::FromSeconds(10);
359 for (; t
< end_t
; t
+= event_increment
) {
360 ASSERT_TRUE(oracle
.ObserveEventAndDecideCapture(
361 VideoCaptureOracle::kCompositorUpdate
,
362 is_content_animating
? gfx::Rect(Get720pSize()) : gfx::Rect(), t
));
363 ASSERT_EQ(Get720pSize(), oracle
.capture_size());
364 const double utilization
= 0.9;
365 const int frame_number
=
366 oracle
.RecordCapture(with_consumer_feedback
? 0.25 : utilization
);
367 base::TimeTicks ignored
;
368 ASSERT_TRUE(oracle
.CompleteCapture(frame_number
, true, &ignored
));
369 if (with_consumer_feedback
)
370 oracle
.RecordConsumerFeedback(frame_number
, utilization
);
373 // Cause two downward steppings in resolution. First, indicate overload
374 // until the resolution steps down. Then, indicate a 90% utilization and
375 // expect the resolution to remain constant. Repeat.
376 for (int i
= 0; i
< 2; ++i
) {
377 const gfx::Size starting_size
= oracle
.capture_size();
378 SCOPED_TRACE(::testing::Message() << "Stepping down from "
379 << starting_size
.ToString()
382 gfx::Size stepped_down_size
;
383 end_t
= t
+ base::TimeDelta::FromSeconds(10);
384 for (; t
< end_t
; t
+= event_increment
) {
385 ASSERT_TRUE(oracle
.ObserveEventAndDecideCapture(
386 VideoCaptureOracle::kCompositorUpdate
,
387 is_content_animating
? gfx::Rect(Get720pSize()) : gfx::Rect(), t
));
389 if (stepped_down_size
.IsEmpty()) {
390 if (oracle
.capture_size() != starting_size
) {
391 time_of_last_size_change
= t
;
392 stepped_down_size
= oracle
.capture_size();
393 ASSERT_GT(starting_size
.width(), stepped_down_size
.width());
394 ASSERT_GT(starting_size
.height(), stepped_down_size
.height());
397 ASSERT_EQ(stepped_down_size
, oracle
.capture_size());
400 const double utilization
= stepped_down_size
.IsEmpty() ? 1.5 : 0.9;
401 const int frame_number
=
402 oracle
.RecordCapture(with_consumer_feedback
? 0.25 : utilization
);
403 base::TimeTicks ignored
;
404 ASSERT_TRUE(oracle
.CompleteCapture(frame_number
, true, &ignored
));
405 if (with_consumer_feedback
)
406 oracle
.RecordConsumerFeedback(frame_number
, utilization
);
410 // Now, cause two upward steppings in resolution. First, indicate
411 // under-utilization until the resolution steps up. Then, indicate a 90%
412 // utilization and expect the resolution to remain constant. Repeat.
413 for (int i
= 0; i
< 2; ++i
) {
414 const gfx::Size starting_size
= oracle
.capture_size();
415 SCOPED_TRACE(::testing::Message() << "Stepping up from "
416 << starting_size
.ToString()
419 gfx::Size stepped_up_size
;
420 end_t
= t
+ base::TimeDelta::FromSeconds(is_content_animating
? 90 : 10);
421 for (; t
< end_t
; t
+= event_increment
) {
422 ASSERT_TRUE(oracle
.ObserveEventAndDecideCapture(
423 VideoCaptureOracle::kCompositorUpdate
,
424 is_content_animating
? gfx::Rect(Get720pSize()) : gfx::Rect(), t
));
426 if (stepped_up_size
.IsEmpty()) {
427 if (oracle
.capture_size() != starting_size
) {
428 // When content is animating, a much longer amount of time must pass
429 // before the capture size will step up.
430 ASSERT_LT(base::TimeDelta::FromSeconds(is_content_animating
? 15 : 1),
431 t
- time_of_last_size_change
);
432 time_of_last_size_change
= t
;
433 stepped_up_size
= oracle
.capture_size();
434 ASSERT_LT(starting_size
.width(), stepped_up_size
.width());
435 ASSERT_LT(starting_size
.height(), stepped_up_size
.height());
438 ASSERT_EQ(stepped_up_size
, oracle
.capture_size());
441 const double utilization
= stepped_up_size
.IsEmpty() ? 0.0 : 0.9;
442 const int frame_number
=
443 oracle
.RecordCapture(with_consumer_feedback
? 0.25 : utilization
);
444 base::TimeTicks ignored
;
445 ASSERT_TRUE(oracle
.CompleteCapture(frame_number
, true, &ignored
));
446 if (with_consumer_feedback
)
447 oracle
.RecordConsumerFeedback(frame_number
, utilization
);
454 // Tests that VideoCaptureOracle can auto-throttle by stepping the capture size
455 // up or down, using utilization feedback signals from either the buffer pool or
456 // the consumer, and with slightly different behavior depending on whether
457 // content is animating.
458 TEST(VideoCaptureOracleTest
, AutoThrottlesBasedOnUtilizationFeedback
) {
459 RunAutoThrottleTest(false, false);
460 RunAutoThrottleTest(false, true);
461 RunAutoThrottleTest(true, false);
462 RunAutoThrottleTest(true, true);
465 // Tests that VideoCaptureOracle does not change the capture size if
466 // auto-throttling is enabled when using a fixed resolution policy.
467 TEST(VideoCaptureOracleTest
, DoesNotAutoThrottleWhenResolutionIsFixed
) {
468 VideoCaptureOracle
oracle(Get30HzPeriod(), Get720pSize(),
469 media::RESOLUTION_POLICY_FIXED_RESOLUTION
, true);
471 // Run 10 seconds of frame captures with 90% utilization expect no capture
473 base::TimeTicks t
= InitialTestTimeTicks();
474 const base::TimeDelta event_increment
= Get30HzPeriod() * 2;
475 base::TimeTicks end_t
= t
+ base::TimeDelta::FromSeconds(10);
476 for (; t
< end_t
; t
+= event_increment
) {
477 ASSERT_TRUE(oracle
.ObserveEventAndDecideCapture(
478 VideoCaptureOracle::kCompositorUpdate
, gfx::Rect(), t
));
479 ASSERT_EQ(Get720pSize(), oracle
.capture_size());
480 base::TimeTicks ignored
;
482 oracle
.CompleteCapture(oracle
.RecordCapture(0.9), true, &ignored
));
485 // Now run 10 seconds with overload indicated. Still, expect no capture size
487 end_t
= t
+ base::TimeDelta::FromSeconds(10);
488 for (; t
< end_t
; t
+= event_increment
) {
489 ASSERT_TRUE(oracle
.ObserveEventAndDecideCapture(
490 VideoCaptureOracle::kCompositorUpdate
, gfx::Rect(), t
));
491 ASSERT_EQ(Get720pSize(), oracle
.capture_size());
492 base::TimeTicks ignored
;
494 oracle
.CompleteCapture(oracle
.RecordCapture(2.0), true, &ignored
));