Supervised user import: Listen for profile creation/deletion
[chromium-blink-merge.git] / content / browser / media / capture / animated_content_sampler_unittest.cc
bloba1017370ce892d4e6ef72159d676b055b0770132
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 "content/browser/media/capture/animated_content_sampler.h"
7 #include <cstdlib>
8 #include <utility>
9 #include <vector>
11 #include "base/logging.h"
12 #include "base/memory/scoped_ptr.h"
13 #include "base/time/time.h"
14 #include "testing/gtest/include/gtest/gtest.h"
15 #include "ui/gfx/geometry/rect.h"
17 namespace content {
19 namespace {
21 base::TimeTicks InitialTestTimeTicks() {
22 return base::TimeTicks() + base::TimeDelta::FromSeconds(1);
25 } // namespace
27 class AnimatedContentSamplerTest : public ::testing::Test {
28 public:
29 AnimatedContentSamplerTest() {}
30 ~AnimatedContentSamplerTest() override {}
32 void SetUp() override {
33 const base::TimeDelta since_epoch =
34 InitialTestTimeTicks() - base::TimeTicks::UnixEpoch();
35 rand_seed_ = abs(static_cast<int>(since_epoch.InMicroseconds()));
36 sampler_.reset(new AnimatedContentSampler(GetMinCapturePeriod()));
39 protected:
40 // Overridden by subclass for parameterized tests.
41 virtual base::TimeDelta GetMinCapturePeriod() const {
42 return base::TimeDelta::FromSeconds(1) / 30;
45 AnimatedContentSampler* sampler() const {
46 return sampler_.get();
49 int GetRandomInRange(int begin, int end) {
50 const int len = end - begin;
51 const int rand_offset = (len == 0) ? 0 : (NextRandomInt() % (end - begin));
52 return begin + rand_offset;
55 gfx::Rect GetRandomDamageRect() {
56 return gfx::Rect(0, 0, GetRandomInRange(1, 100), GetRandomInRange(1, 100));
59 gfx::Rect GetContentDamageRect() {
60 // This must be distinct from anything GetRandomDamageRect() could return.
61 return gfx::Rect(0, 0, 1280, 720);
64 // Directly inject an observation. Only used to test
65 // ElectMajorityDamageRect().
66 void ObserveDamageRect(const gfx::Rect& damage_rect) {
67 sampler_->observations_.push_back(
68 AnimatedContentSampler::Observation(damage_rect, base::TimeTicks()));
71 gfx::Rect ElectMajorityDamageRect() const {
72 return sampler_->ElectMajorityDamageRect();
75 private:
76 // Note: Not using base::RandInt() because it is horribly slow on debug
77 // builds. The following is a very simple, deterministic LCG:
78 int NextRandomInt() {
79 rand_seed_ = (1103515245 * rand_seed_ + 12345) % (1 << 31);
80 return rand_seed_;
83 int rand_seed_;
84 scoped_ptr<AnimatedContentSampler> sampler_;
87 TEST_F(AnimatedContentSamplerTest, ElectsNoneFromZeroDamageRects) {
88 EXPECT_EQ(gfx::Rect(), ElectMajorityDamageRect());
91 TEST_F(AnimatedContentSamplerTest, ElectsMajorityFromOneDamageRect) {
92 const gfx::Rect the_one_rect(0, 0, 1, 1);
93 ObserveDamageRect(the_one_rect);
94 EXPECT_EQ(the_one_rect, ElectMajorityDamageRect());
97 TEST_F(AnimatedContentSamplerTest, ElectsNoneFromTwoDamageRectsOfSameArea) {
98 const gfx::Rect one_rect(0, 0, 1, 1);
99 const gfx::Rect another_rect(1, 1, 1, 1);
100 ObserveDamageRect(one_rect);
101 ObserveDamageRect(another_rect);
102 EXPECT_EQ(gfx::Rect(), ElectMajorityDamageRect());
105 TEST_F(AnimatedContentSamplerTest, ElectsLargerOfTwoDamageRects_1) {
106 const gfx::Rect one_rect(0, 0, 1, 1);
107 const gfx::Rect another_rect(0, 0, 2, 2);
108 ObserveDamageRect(one_rect);
109 ObserveDamageRect(another_rect);
110 EXPECT_EQ(another_rect, ElectMajorityDamageRect());
113 TEST_F(AnimatedContentSamplerTest, ElectsLargerOfTwoDamageRects_2) {
114 const gfx::Rect one_rect(0, 0, 2, 2);
115 const gfx::Rect another_rect(0, 0, 1, 1);
116 ObserveDamageRect(one_rect);
117 ObserveDamageRect(another_rect);
118 EXPECT_EQ(one_rect, ElectMajorityDamageRect());
121 TEST_F(AnimatedContentSamplerTest, ElectsSameAsMooreDemonstration) {
122 // A more complex sequence (from Moore's web site): Three different Rects with
123 // the same area, but occurring a different number of times. C should win the
124 // vote.
125 const gfx::Rect rect_a(0, 0, 1, 4);
126 const gfx::Rect rect_b(1, 1, 4, 1);
127 const gfx::Rect rect_c(2, 2, 2, 2);
128 for (int i = 0; i < 3; ++i)
129 ObserveDamageRect(rect_a);
130 for (int i = 0; i < 2; ++i)
131 ObserveDamageRect(rect_c);
132 for (int i = 0; i < 2; ++i)
133 ObserveDamageRect(rect_b);
134 for (int i = 0; i < 3; ++i)
135 ObserveDamageRect(rect_c);
136 ObserveDamageRect(rect_b);
137 for (int i = 0; i < 2; ++i)
138 ObserveDamageRect(rect_c);
139 EXPECT_EQ(rect_c, ElectMajorityDamageRect());
142 TEST_F(AnimatedContentSamplerTest, Elects24FpsVideoInsteadOf48FpsSpinner) {
143 // Scenario: 24 FPS 720x480 Video versus 48 FPS 96x96 "Busy Spinner"
144 const gfx::Rect video_rect(100, 100, 720, 480);
145 const gfx::Rect spinner_rect(360, 0, 96, 96);
146 for (int i = 0; i < 100; ++i) {
147 // |video_rect| occurs once for every two |spinner_rect|. Vary the order
148 // of events between the two:
149 ObserveDamageRect(video_rect);
150 ObserveDamageRect(spinner_rect);
151 ObserveDamageRect(spinner_rect);
152 ObserveDamageRect(video_rect);
153 ObserveDamageRect(spinner_rect);
154 ObserveDamageRect(spinner_rect);
155 ObserveDamageRect(spinner_rect);
156 ObserveDamageRect(video_rect);
157 ObserveDamageRect(spinner_rect);
158 ObserveDamageRect(spinner_rect);
159 ObserveDamageRect(video_rect);
160 ObserveDamageRect(spinner_rect);
162 EXPECT_EQ(video_rect, ElectMajorityDamageRect());
165 namespace {
167 // A test scenario for AnimatedContentSamplerParameterizedTest.
168 struct Scenario {
169 base::TimeDelta vsync_interval; // Reflects compositor's update rate.
170 base::TimeDelta min_capture_period; // Reflects maximum capture rate.
171 base::TimeDelta content_period; // Reflects content animation rate.
173 Scenario(base::TimeDelta v, base::TimeDelta m, base::TimeDelta c)
174 : vsync_interval(v), min_capture_period(m), content_period(c) {
175 CHECK(content_period >= vsync_interval)
176 << "Bad test params: Impossible to animate faster than the compositor.";
180 // Value printer for Scenario.
181 ::std::ostream& operator<<(::std::ostream& os, const Scenario& s) {
182 return os << "{ vsync_interval=" << s.vsync_interval.InMicroseconds()
183 << ", min_capture_period=" << s.min_capture_period.InMicroseconds()
184 << ", content_period=" << s.content_period.InMicroseconds()
185 << " }";
188 base::TimeDelta FpsAsPeriod(int frame_rate) {
189 return base::TimeDelta::FromSeconds(1) / frame_rate;
192 } // namespace
194 class AnimatedContentSamplerParameterizedTest
195 : public AnimatedContentSamplerTest,
196 public ::testing::WithParamInterface<Scenario> {
197 public:
198 AnimatedContentSamplerParameterizedTest()
199 : count_dropped_frames_(0), count_sampled_frames_(0) {}
200 virtual ~AnimatedContentSamplerParameterizedTest() {}
202 protected:
203 typedef std::pair<gfx::Rect, base::TimeTicks> Event;
205 base::TimeDelta GetMinCapturePeriod() const override {
206 return GetParam().min_capture_period;
209 // Generate a sequence of events from the compositor pipeline. The event
210 // times will all be at compositor vsync boundaries.
211 std::vector<Event> GenerateEventSequence(base::TimeTicks begin,
212 base::TimeTicks end,
213 bool include_content_frame_events,
214 bool include_random_events) {
215 DCHECK(GetParam().content_period >= GetParam().vsync_interval);
216 base::TimeTicks next_content_time = begin - GetParam().content_period;
217 std::vector<Event> events;
218 for (base::TimeTicks compositor_time = begin; compositor_time < end;
219 compositor_time += GetParam().vsync_interval) {
220 if (include_content_frame_events && next_content_time < compositor_time) {
221 events.push_back(Event(GetContentDamageRect(), compositor_time));
222 next_content_time += GetParam().content_period;
223 } else if (include_random_events && GetRandomInRange(0, 1) == 0) {
224 events.push_back(Event(GetRandomDamageRect(), compositor_time));
228 DCHECK(!events.empty());
229 return events;
232 // Feed |events| through the sampler, and detect whether the expected
233 // lock-in/out transition occurs. Also, track and measure the frame drop
234 // ratio and check it against the expected drop rate.
235 void RunEventSequence(const std::vector<Event> events,
236 bool was_detecting_before,
237 bool is_detecting_after,
238 bool simulate_pipeline_back_pressure) {
239 gfx::Rect first_detected_region;
241 EXPECT_EQ(was_detecting_before, sampler()->HasProposal());
242 bool has_detection_switched = false;
243 ResetFrameCounters();
244 for (std::vector<Event>::const_iterator i = events.begin();
245 i != events.end(); ++i) {
246 sampler()->ConsiderPresentationEvent(i->first, i->second);
248 // Detect when the sampler locks in/out, and that it stays that way for
249 // all further iterations of this loop.
250 if (!has_detection_switched &&
251 was_detecting_before != sampler()->HasProposal()) {
252 has_detection_switched = true;
254 ASSERT_EQ(
255 has_detection_switched ? is_detecting_after : was_detecting_before,
256 sampler()->HasProposal());
258 if (sampler()->HasProposal()) {
259 // Make sure the sampler doesn't flip-flop and keep proposing sampling
260 // based on locking into different regions.
261 if (first_detected_region.IsEmpty()) {
262 first_detected_region = sampler()->detected_region();
263 ASSERT_FALSE(first_detected_region.IsEmpty());
264 } else {
265 EXPECT_EQ(first_detected_region, sampler()->detected_region());
268 if (simulate_pipeline_back_pressure && GetRandomInRange(0, 2) == 0)
269 ClientCannotSampleFrame(*i);
270 else
271 ClientDoesWhatSamplerProposes(*i);
272 } else {
273 EXPECT_FALSE(sampler()->ShouldSample());
274 if (!simulate_pipeline_back_pressure || GetRandomInRange(0, 2) == 1)
275 sampler()->RecordSample(i->second);
278 EXPECT_EQ(is_detecting_after, sampler()->HasProposal());
279 ExpectFrameDropRatioIsCorrect();
282 void ResetFrameCounters() {
283 count_dropped_frames_ = 0;
284 count_sampled_frames_ = 0;
287 // Keep track what the sampler is proposing, and call RecordSample() if it
288 // proposes sampling |event|.
289 void ClientDoesWhatSamplerProposes(const Event& event) {
290 if (sampler()->ShouldSample()) {
291 EXPECT_EQ(GetContentDamageRect(), event.first);
292 sampler()->RecordSample(sampler()->frame_timestamp());
293 ++count_sampled_frames_;
294 } else if (event.first == GetContentDamageRect()) {
295 ++count_dropped_frames_;
299 // RecordSample() is not called, but for testing, keep track of what the
300 // sampler is proposing for |event|.
301 void ClientCannotSampleFrame(const Event& event) {
302 if (sampler()->ShouldSample()) {
303 EXPECT_EQ(GetContentDamageRect(), event.first);
304 ++count_sampled_frames_;
305 } else if (event.first == GetContentDamageRect()) {
306 ++count_dropped_frames_;
310 // Confirm the AnimatedContentSampler is not dropping more frames than
311 // expected, given current test parameters.
312 void ExpectFrameDropRatioIsCorrect() {
313 if (count_sampled_frames_ == 0) {
314 EXPECT_EQ(0, count_dropped_frames_);
315 return;
317 const double content_framerate =
318 1000000.0 / GetParam().content_period.InMicroseconds();
319 const double capture_framerate =
320 1000000.0 / GetParam().min_capture_period.InMicroseconds();
321 const double expected_drop_rate = std::max(
322 0.0, (content_framerate - capture_framerate) / capture_framerate);
323 const double actual_drop_rate =
324 static_cast<double>(count_dropped_frames_) / count_sampled_frames_;
325 EXPECT_NEAR(expected_drop_rate, actual_drop_rate, 0.015);
328 private:
329 // These counters only include the frames with the desired content.
330 int count_dropped_frames_;
331 int count_sampled_frames_;
334 // Tests that the implementation locks in/out of frames containing stable
335 // animated content, whether or not random events are also simultaneously
336 // present.
337 TEST_P(AnimatedContentSamplerParameterizedTest, DetectsAnimatedContent) {
338 // |begin| refers to the start of an event sequence in terms of the
339 // Compositor's clock.
340 base::TimeTicks begin = InitialTestTimeTicks();
342 // Provide random events and expect no lock-in.
343 base::TimeTicks end = begin + base::TimeDelta::FromSeconds(5);
344 RunEventSequence(GenerateEventSequence(begin, end, false, true),
345 false,
346 false,
347 false);
348 begin = end;
350 // Provide content frame events with some random events mixed-in, and expect
351 // the sampler to lock-in.
352 end = begin + base::TimeDelta::FromSeconds(5);
353 RunEventSequence(GenerateEventSequence(begin, end, true, true),
354 false,
355 true,
356 false);
357 begin = end;
359 // Continue providing content frame events without the random events mixed-in
360 // and expect the lock-in to hold.
361 end = begin + base::TimeDelta::FromSeconds(5);
362 RunEventSequence(GenerateEventSequence(begin, end, true, false),
363 true,
364 true,
365 false);
366 begin = end;
368 // Continue providing just content frame events and expect the lock-in to
369 // hold. Also simulate the capture pipeline experiencing back pressure.
370 end = begin + base::TimeDelta::FromSeconds(20);
371 RunEventSequence(GenerateEventSequence(begin, end, true, false),
372 true,
373 true,
374 true);
375 begin = end;
377 // Provide a half-second of random events only, and expect the lock-in to be
378 // broken.
379 end = begin + base::TimeDelta::FromMilliseconds(500);
380 RunEventSequence(GenerateEventSequence(begin, end, false, true),
381 true,
382 false,
383 false);
384 begin = end;
386 // Now, go back to providing content frame events, and expect the sampler to
387 // lock-in once again.
388 end = begin + base::TimeDelta::FromSeconds(5);
389 RunEventSequence(GenerateEventSequence(begin, end, true, false),
390 false,
391 true,
392 false);
393 begin = end;
396 // Tests that AnimatedContentSampler won't lock in to, nor flip-flop between,
397 // two animations of the same pixel change rate. VideoCaptureOracle should
398 // revert to using the SmoothEventSampler for these kinds of situations, as
399 // there is no "right answer" as to which animation to lock into.
400 TEST_P(AnimatedContentSamplerParameterizedTest,
401 DoesNotLockInToTwoCompetingAnimations) {
402 // Don't test when the event stream cannot indicate two separate content
403 // animations under the current test parameters.
404 if (GetParam().content_period < 2 * GetParam().vsync_interval)
405 return;
407 // Start the first animation and run for a bit, and expect the sampler to
408 // lock-in.
409 base::TimeTicks begin = InitialTestTimeTicks();
410 base::TimeTicks end = begin + base::TimeDelta::FromSeconds(5);
411 RunEventSequence(GenerateEventSequence(begin, end, true, false),
412 false,
413 true,
414 false);
415 begin = end;
417 // Now, keep the first animation and blend in an second animation of the same
418 // size and frame rate, but at a different position. This will should cause
419 // the sampler to enter an "undetected" state since it's unclear which
420 // animation should be locked into.
421 end = begin + base::TimeDelta::FromSeconds(20);
422 std::vector<Event> first_animation_events =
423 GenerateEventSequence(begin, end, true, false);
424 gfx::Rect second_animation_rect(
425 gfx::Point(0, GetContentDamageRect().height()),
426 GetContentDamageRect().size());
427 std::vector<Event> both_animations_events;
428 base::TimeDelta second_animation_offset = GetParam().vsync_interval;
429 for (std::vector<Event>::const_iterator i = first_animation_events.begin();
430 i != first_animation_events.end(); ++i) {
431 both_animations_events.push_back(*i);
432 both_animations_events.push_back(
433 Event(second_animation_rect, i->second + second_animation_offset));
435 RunEventSequence(both_animations_events, true, false, false);
436 begin = end;
438 // Now, run just the first animation, and expect the sampler to lock-in once
439 // again.
440 end = begin + base::TimeDelta::FromSeconds(5);
441 RunEventSequence(GenerateEventSequence(begin, end, true, false),
442 false,
443 true,
444 false);
445 begin = end;
447 // Now, blend in the second animation again, but it has half the frame rate of
448 // the first animation and damage Rects with twice the area. This will should
449 // cause the sampler to enter an "undetected" state again. This tests that
450 // pixel-weighting is being accounted for in the sampler's logic.
451 end = begin + base::TimeDelta::FromSeconds(20);
452 first_animation_events = GenerateEventSequence(begin, end, true, false);
453 second_animation_rect.set_width(second_animation_rect.width() * 2);
454 both_animations_events.clear();
455 bool include_second_animation_frame = true;
456 for (std::vector<Event>::const_iterator i = first_animation_events.begin();
457 i != first_animation_events.end(); ++i) {
458 both_animations_events.push_back(*i);
459 if (include_second_animation_frame) {
460 both_animations_events.push_back(
461 Event(second_animation_rect, i->second + second_animation_offset));
463 include_second_animation_frame = !include_second_animation_frame;
465 RunEventSequence(both_animations_events, true, false, false);
466 begin = end;
469 // Tests that the frame timestamps are smooth; meaning, that when run through a
470 // simulated compositor, each frame is held displayed for the right number of
471 // v-sync intervals.
472 TEST_P(AnimatedContentSamplerParameterizedTest, FrameTimestampsAreSmooth) {
473 // Generate 30 seconds of animated content events, run the events through
474 // AnimatedContentSampler, and record all frame timestamps being proposed
475 // once lock-in is continuous.
476 base::TimeTicks begin = InitialTestTimeTicks();
477 std::vector<Event> events = GenerateEventSequence(
478 begin,
479 begin + base::TimeDelta::FromSeconds(20),
480 true,
481 false);
482 typedef std::vector<base::TimeTicks> Timestamps;
483 Timestamps frame_timestamps;
484 for (std::vector<Event>::const_iterator i = events.begin(); i != events.end();
485 ++i) {
486 sampler()->ConsiderPresentationEvent(i->first, i->second);
487 if (sampler()->HasProposal()) {
488 if (sampler()->ShouldSample()) {
489 frame_timestamps.push_back(sampler()->frame_timestamp());
490 sampler()->RecordSample(sampler()->frame_timestamp());
492 } else {
493 frame_timestamps.clear(); // Reset until continuous lock-in.
496 ASSERT_LE(2u, frame_timestamps.size());
498 // Iterate through the |frame_timestamps|, building a histogram counting the
499 // number of times each frame was displayed k times. For example, 10 frames
500 // of 30 Hz content on a 60 Hz v-sync interval should result in
501 // display_counts[2] == 10. Quit early if any one frame was obviously
502 // repeated too many times.
503 const int64 max_expected_repeats_per_frame = 1 +
504 std::max(GetParam().min_capture_period, GetParam().content_period) /
505 GetParam().vsync_interval;
506 std::vector<size_t> display_counts(max_expected_repeats_per_frame + 1, 0);
507 base::TimeTicks last_present_time = frame_timestamps.front();
508 for (Timestamps::const_iterator i = frame_timestamps.begin() + 1;
509 i != frame_timestamps.end(); ++i) {
510 const size_t num_vsync_intervals = static_cast<size_t>(
511 (*i - last_present_time) / GetParam().vsync_interval);
512 ASSERT_LT(0u, num_vsync_intervals);
513 ASSERT_GT(display_counts.size(), num_vsync_intervals); // Quit early.
514 ++display_counts[num_vsync_intervals];
515 last_present_time += num_vsync_intervals * GetParam().vsync_interval;
518 // Analyze the histogram for an expected result pattern. If the frame
519 // timestamps are smooth, there should only be one or two buckets with
520 // non-zero counts and they should be next to each other. Because the clock
521 // precision for the event_times provided to the sampler is very granular
522 // (i.e., the vsync_interval), it's okay if other buckets have a tiny "stray"
523 // count in this test.
524 size_t highest_count = 0;
525 size_t second_highest_count = 0;
526 for (size_t repeats = 1; repeats < display_counts.size(); ++repeats) {
527 DVLOG(1) << "display_counts[" << repeats << "] is "
528 << display_counts[repeats];
529 if (display_counts[repeats] >= highest_count) {
530 second_highest_count = highest_count;
531 highest_count = display_counts[repeats];
532 } else if (display_counts[repeats] > second_highest_count) {
533 second_highest_count = display_counts[repeats];
536 size_t stray_count_remaining =
537 (frame_timestamps.size() - 1) - (highest_count + second_highest_count);
538 // Expect no more than 0.75% of frames fall outside the two main buckets.
539 EXPECT_GT(frame_timestamps.size() * 75 / 10000, stray_count_remaining);
540 for (size_t repeats = 1; repeats < display_counts.size() - 1; ++repeats) {
541 if (display_counts[repeats] == highest_count) {
542 EXPECT_EQ(second_highest_count, display_counts[repeats + 1]);
543 ++repeats;
544 } else if (display_counts[repeats] == second_highest_count) {
545 EXPECT_EQ(highest_count, display_counts[repeats + 1]);
546 ++repeats;
547 } else {
548 EXPECT_GE(stray_count_remaining, display_counts[repeats]);
549 stray_count_remaining -= display_counts[repeats];
554 // Tests that frame timestamps are "lightly pushed" back towards the original
555 // presentation event times, which tells us the AnimatedContentSampler can
556 // account for sources of timestamp drift and correct the drift.
557 TEST_P(AnimatedContentSamplerParameterizedTest,
558 FrameTimestampsConvergeTowardsEventTimes) {
559 const int max_drift_increment_millis = 3;
561 // Generate a full minute of events.
562 const base::TimeTicks begin = InitialTestTimeTicks();
563 const base::TimeTicks end = begin + base::TimeDelta::FromMinutes(1);
564 std::vector<Event> events = GenerateEventSequence(begin, end, true, false);
566 // Modify the event sequence so that 1-3 ms of additional drift is suddenly
567 // present every 100 events. This is meant to simulate that, external to
568 // AnimatedContentSampler, the video hardware vsync timebase is being
569 // refreshed and is showing severe drift from the system clock.
570 base::TimeDelta accumulated_drift;
571 for (size_t i = 1; i < events.size(); ++i) {
572 if (i % 100 == 0) {
573 accumulated_drift += base::TimeDelta::FromMilliseconds(
574 GetRandomInRange(1, max_drift_increment_millis + 1));
576 events[i].second += accumulated_drift;
579 // Run all the events through the sampler and track the last rewritten frame
580 // timestamp.
581 base::TimeTicks last_frame_timestamp;
582 for (std::vector<Event>::const_iterator i = events.begin(); i != events.end();
583 ++i) {
584 sampler()->ConsiderPresentationEvent(i->first, i->second);
585 if (sampler()->ShouldSample())
586 last_frame_timestamp = sampler()->frame_timestamp();
589 // If drift was accounted for, the |last_frame_timestamp| should be close to
590 // the last event's timestamp.
591 const base::TimeDelta total_error =
592 events.back().second - last_frame_timestamp;
593 const base::TimeDelta max_acceptable_error = GetParam().min_capture_period +
594 base::TimeDelta::FromMilliseconds(max_drift_increment_millis);
595 EXPECT_NEAR(0.0,
596 total_error.InMicroseconds(),
597 max_acceptable_error.InMicroseconds());
600 INSTANTIATE_TEST_CASE_P(
602 AnimatedContentSamplerParameterizedTest,
603 ::testing::Values(
604 // Typical frame rate content: Compositor runs at 60 Hz, capture at 30
605 // Hz, and content video animates at 30, 25, or 24 Hz.
606 Scenario(FpsAsPeriod(60), FpsAsPeriod(30), FpsAsPeriod(30)),
607 Scenario(FpsAsPeriod(60), FpsAsPeriod(30), FpsAsPeriod(25)),
608 Scenario(FpsAsPeriod(60), FpsAsPeriod(30), FpsAsPeriod(24)),
610 // High frame rate content that leverages the Compositor's
611 // capabilities, but capture is still at 30 Hz.
612 Scenario(FpsAsPeriod(60), FpsAsPeriod(30), FpsAsPeriod(60)),
613 Scenario(FpsAsPeriod(60), FpsAsPeriod(30), FpsAsPeriod(50)),
614 Scenario(FpsAsPeriod(60), FpsAsPeriod(30), FpsAsPeriod(48)),
616 // High frame rate content that leverages the Compositor's
617 // capabilities, and capture is also a buttery 60 Hz.
618 Scenario(FpsAsPeriod(60), FpsAsPeriod(60), FpsAsPeriod(60)),
619 Scenario(FpsAsPeriod(60), FpsAsPeriod(60), FpsAsPeriod(50)),
620 Scenario(FpsAsPeriod(60), FpsAsPeriod(60), FpsAsPeriod(48)),
622 // On some platforms, the Compositor runs at 50 Hz.
623 Scenario(FpsAsPeriod(50), FpsAsPeriod(30), FpsAsPeriod(30)),
624 Scenario(FpsAsPeriod(50), FpsAsPeriod(30), FpsAsPeriod(25)),
625 Scenario(FpsAsPeriod(50), FpsAsPeriod(30), FpsAsPeriod(24)),
626 Scenario(FpsAsPeriod(50), FpsAsPeriod(30), FpsAsPeriod(50)),
627 Scenario(FpsAsPeriod(50), FpsAsPeriod(30), FpsAsPeriod(48)),
629 // Stable, but non-standard content frame rates.
630 Scenario(FpsAsPeriod(60), FpsAsPeriod(30), FpsAsPeriod(16)),
631 Scenario(FpsAsPeriod(60), FpsAsPeriod(30), FpsAsPeriod(20)),
632 Scenario(FpsAsPeriod(60), FpsAsPeriod(30), FpsAsPeriod(23)),
633 Scenario(FpsAsPeriod(60), FpsAsPeriod(30), FpsAsPeriod(26)),
634 Scenario(FpsAsPeriod(60), FpsAsPeriod(30), FpsAsPeriod(27)),
635 Scenario(FpsAsPeriod(60), FpsAsPeriod(30), FpsAsPeriod(28)),
636 Scenario(FpsAsPeriod(60), FpsAsPeriod(30), FpsAsPeriod(29)),
637 Scenario(FpsAsPeriod(60), FpsAsPeriod(30), FpsAsPeriod(31)),
638 Scenario(FpsAsPeriod(60), FpsAsPeriod(30), FpsAsPeriod(32)),
639 Scenario(FpsAsPeriod(60), FpsAsPeriod(30), FpsAsPeriod(33))));
641 } // namespace content