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

Side by Side Diff: content/browser/media/capture/animated_content_sampler_unittest.cc

Issue 1123623005: Tab Capture: AnimatedContentSampler subsampling and phase fixes in tests (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Addressed comments and expanded coverage of TargetsSamplingPeriod test. Created 5 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « content/browser/media/capture/animated_content_sampler.cc ('k') | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright (c) 2015 The Chromium Authors. All rights reserved. 1 // Copyright (c) 2015 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "content/browser/media/capture/animated_content_sampler.h" 5 #include "content/browser/media/capture/animated_content_sampler.h"
6 6
7 #include <cstdlib> 7 #include <cmath>
8 #include <utility> 8 #include <utility>
9 #include <vector> 9 #include <vector>
10 10
11 #include "base/logging.h" 11 #include "base/logging.h"
12 #include "base/memory/scoped_ptr.h" 12 #include "base/memory/scoped_ptr.h"
13 #include "base/time/time.h" 13 #include "base/time/time.h"
14 #include "testing/gtest/include/gtest/gtest.h" 14 #include "testing/gtest/include/gtest/gtest.h"
15 #include "ui/gfx/geometry/rect.h" 15 #include "ui/gfx/geometry/rect.h"
16 16
17 namespace content { 17 namespace content {
18 18
19 namespace { 19 namespace {
20 20
21 base::TimeTicks InitialTestTimeTicks() { 21 base::TimeTicks InitialTestTimeTicks() {
22 return base::TimeTicks() + base::TimeDelta::FromSeconds(1); 22 return base::TimeTicks() + base::TimeDelta::FromSeconds(1);
23 } 23 }
24 24
25 base::TimeDelta FpsAsPeriod(int frame_rate) {
26 return base::TimeDelta::FromSeconds(1) / frame_rate;
27 }
28
25 } // namespace 29 } // namespace
26 30
27 class AnimatedContentSamplerTest : public ::testing::Test { 31 class AnimatedContentSamplerTest : public ::testing::Test {
28 public: 32 public:
29 AnimatedContentSamplerTest() {} 33 AnimatedContentSamplerTest() {}
30 ~AnimatedContentSamplerTest() override {} 34 ~AnimatedContentSamplerTest() override {}
31 35
32 void SetUp() override { 36 void SetUp() override {
33 const base::TimeDelta since_epoch = 37 const base::TimeDelta since_epoch =
34 InitialTestTimeTicks() - base::TimeTicks::UnixEpoch(); 38 InitialTestTimeTicks() - base::TimeTicks::UnixEpoch();
(...skipping 30 matching lines...) Expand all
65 // ElectMajorityDamageRect(). 69 // ElectMajorityDamageRect().
66 void ObserveDamageRect(const gfx::Rect& damage_rect) { 70 void ObserveDamageRect(const gfx::Rect& damage_rect) {
67 sampler_->observations_.push_back( 71 sampler_->observations_.push_back(
68 AnimatedContentSampler::Observation(damage_rect, base::TimeTicks())); 72 AnimatedContentSampler::Observation(damage_rect, base::TimeTicks()));
69 } 73 }
70 74
71 gfx::Rect ElectMajorityDamageRect() const { 75 gfx::Rect ElectMajorityDamageRect() const {
72 return sampler_->ElectMajorityDamageRect(); 76 return sampler_->ElectMajorityDamageRect();
73 } 77 }
74 78
79 static base::TimeDelta ComputeSamplingPeriod(
80 base::TimeDelta detected_period,
81 base::TimeDelta target_sampling_period,
82 base::TimeDelta min_capture_period) {
83 return AnimatedContentSampler::ComputeSamplingPeriod(
84 detected_period, target_sampling_period, min_capture_period);
85 }
86
75 private: 87 private:
76 // Note: Not using base::RandInt() because it is horribly slow on debug 88 // Note: Not using base::RandInt() because it is horribly slow on debug
77 // builds. The following is a very simple, deterministic LCG: 89 // builds. The following is a very simple, deterministic LCG:
78 int NextRandomInt() { 90 int NextRandomInt() {
79 rand_seed_ = (1103515245 * rand_seed_ + 12345) % (1 << 31); 91 rand_seed_ = (1103515245 * rand_seed_ + 12345) % (1 << 31);
80 return rand_seed_; 92 return rand_seed_;
81 } 93 }
82 94
83 int rand_seed_; 95 int rand_seed_;
84 scoped_ptr<AnimatedContentSampler> sampler_; 96 scoped_ptr<AnimatedContentSampler> sampler_;
(...skipping 70 matching lines...) Expand 10 before | Expand all | Expand 10 after
155 ObserveDamageRect(spinner_rect); 167 ObserveDamageRect(spinner_rect);
156 ObserveDamageRect(video_rect); 168 ObserveDamageRect(video_rect);
157 ObserveDamageRect(spinner_rect); 169 ObserveDamageRect(spinner_rect);
158 ObserveDamageRect(spinner_rect); 170 ObserveDamageRect(spinner_rect);
159 ObserveDamageRect(video_rect); 171 ObserveDamageRect(video_rect);
160 ObserveDamageRect(spinner_rect); 172 ObserveDamageRect(spinner_rect);
161 } 173 }
162 EXPECT_EQ(video_rect, ElectMajorityDamageRect()); 174 EXPECT_EQ(video_rect, ElectMajorityDamageRect());
163 } 175 }
164 176
177 TEST_F(AnimatedContentSamplerTest, TargetsSamplingPeriod) {
178 struct Helper {
179 static void RunTargetSamplingPeriodTest(int target_fps) {
180 const base::TimeDelta min_capture_period = FpsAsPeriod(60);
181 const base::TimeDelta target_sampling_period = FpsAsPeriod(target_fps);
182
183 for (int content_fps = 1; content_fps <= 60; ++content_fps) {
184 const base::TimeDelta content_period = FpsAsPeriod(content_fps);
185 const base::TimeDelta sampling_period =
186 ComputeSamplingPeriod(content_period,
187 target_sampling_period,
188 min_capture_period);
189 if (content_period >= target_sampling_period) {
190 ASSERT_EQ(content_period, sampling_period);
191 } else {
192 ASSERT_LE(min_capture_period, sampling_period);
193
194 // Check that the sampling rate is as close (or closer) to the target
195 // sampling rate than any integer-subsampling of the content frame
196 // rate.
197 const double absolute_diff =
198 std::abs(1.0 / sampling_period.InSecondsF() - target_fps);
199 const double fudge_for_acceptable_rounding_error = 0.005;
200 for (double divisor = 1; divisor < 4; ++divisor) {
201 SCOPED_TRACE(::testing::Message() << "target_fps=" << target_fps
202 << ", content_fps=" << content_fps
203 << ", divisor=" << divisor);
204 ASSERT_GE(std::abs(content_fps / divisor - target_fps),
205 absolute_diff - fudge_for_acceptable_rounding_error);
206 }
207 }
208 }
209 }
210 };
211
212 for (int target_fps = 1; target_fps <= 60; ++target_fps)
213 Helper::RunTargetSamplingPeriodTest(target_fps);
214 }
215
165 namespace { 216 namespace {
166 217
167 // A test scenario for AnimatedContentSamplerParameterizedTest. 218 // A test scenario for AnimatedContentSamplerParameterizedTest.
168 struct Scenario { 219 struct Scenario {
169 base::TimeDelta vsync_interval; // Reflects compositor's update rate. 220 base::TimeDelta vsync_interval; // Reflects compositor's update rate.
170 base::TimeDelta min_capture_period; // Reflects maximum capture rate. 221 base::TimeDelta min_capture_period; // Reflects maximum capture rate.
171 base::TimeDelta content_period; // Reflects content animation rate. 222 base::TimeDelta content_period; // Reflects content animation rate.
223 base::TimeDelta target_sampling_period;
172 224
173 Scenario(base::TimeDelta v, base::TimeDelta m, base::TimeDelta c) 225 Scenario(int compositor_frequency,
174 : vsync_interval(v), min_capture_period(m), content_period(c) { 226 int max_frame_rate,
227 int content_frame_rate)
228 : vsync_interval(FpsAsPeriod(compositor_frequency)),
229 min_capture_period(FpsAsPeriod(max_frame_rate)),
230 content_period(FpsAsPeriod(content_frame_rate)) {
175 CHECK(content_period >= vsync_interval) 231 CHECK(content_period >= vsync_interval)
176 << "Bad test params: Impossible to animate faster than the compositor."; 232 << "Bad test params: Impossible to animate faster than the compositor.";
177 } 233 }
234
235 Scenario(int compositor_frequency,
236 int max_frame_rate,
237 int content_frame_rate,
238 int target_sampling_rate)
239 : vsync_interval(FpsAsPeriod(compositor_frequency)),
240 min_capture_period(FpsAsPeriod(max_frame_rate)),
241 content_period(FpsAsPeriod(content_frame_rate)),
242 target_sampling_period(FpsAsPeriod(target_sampling_rate)) {
243 CHECK(content_period >= vsync_interval)
244 << "Bad test params: Impossible to animate faster than the compositor.";
245 }
178 }; 246 };
179 247
180 // Value printer for Scenario. 248 // Value printer for Scenario.
181 ::std::ostream& operator<<(::std::ostream& os, const Scenario& s) { 249 ::std::ostream& operator<<(::std::ostream& os, const Scenario& s) {
182 return os << "{ vsync_interval=" << s.vsync_interval.InMicroseconds() 250 return os << "{ vsync_interval=" << s.vsync_interval.InMicroseconds()
183 << ", min_capture_period=" << s.min_capture_period.InMicroseconds() 251 << ", min_capture_period=" << s.min_capture_period.InMicroseconds()
184 << ", content_period=" << s.content_period.InMicroseconds() 252 << ", content_period=" << s.content_period.InMicroseconds()
185 << " }"; 253 << " }";
186 } 254 }
187 255
188 base::TimeDelta FpsAsPeriod(int frame_rate) {
189 return base::TimeDelta::FromSeconds(1) / frame_rate;
190 }
191
192 } // namespace 256 } // namespace
193 257
194 class AnimatedContentSamplerParameterizedTest 258 class AnimatedContentSamplerParameterizedTest
195 : public AnimatedContentSamplerTest, 259 : public AnimatedContentSamplerTest,
196 public ::testing::WithParamInterface<Scenario> { 260 public ::testing::WithParamInterface<Scenario> {
197 public: 261 public:
198 AnimatedContentSamplerParameterizedTest() 262 AnimatedContentSamplerParameterizedTest()
199 : count_dropped_frames_(0), count_sampled_frames_(0) {} 263 : count_dropped_frames_(0), count_sampled_frames_(0) {}
200 virtual ~AnimatedContentSamplerParameterizedTest() {} 264 virtual ~AnimatedContentSamplerParameterizedTest() {}
201 265
266 void SetUp() override {
267 AnimatedContentSamplerTest::SetUp();
268 sampler()->SetTargetSamplingPeriod(GetParam().target_sampling_period);
269 }
270
202 protected: 271 protected:
203 typedef std::pair<gfx::Rect, base::TimeTicks> Event; 272 typedef std::pair<gfx::Rect, base::TimeTicks> Event;
204 273
205 base::TimeDelta GetMinCapturePeriod() const override { 274 base::TimeDelta GetMinCapturePeriod() const override {
206 return GetParam().min_capture_period; 275 return GetParam().min_capture_period;
207 } 276 }
208 277
278 base::TimeDelta ComputeExpectedSamplingPeriod() const {
279 return AnimatedContentSamplerTest::ComputeSamplingPeriod(
280 GetParam().content_period,
281 GetParam().target_sampling_period,
282 GetParam().min_capture_period);
283 }
284
209 // Generate a sequence of events from the compositor pipeline. The event 285 // Generate a sequence of events from the compositor pipeline. The event
210 // times will all be at compositor vsync boundaries. 286 // times will all be at compositor vsync boundaries.
211 std::vector<Event> GenerateEventSequence(base::TimeTicks begin, 287 std::vector<Event> GenerateEventSequence(base::TimeTicks begin,
212 base::TimeTicks end, 288 base::TimeTicks end,
213 bool include_content_frame_events, 289 bool include_content_frame_events,
214 bool include_random_events) { 290 bool include_random_events,
291 base::TimeTicks* next_begin_time) {
215 DCHECK(GetParam().content_period >= GetParam().vsync_interval); 292 DCHECK(GetParam().content_period >= GetParam().vsync_interval);
216 base::TimeTicks next_content_time = begin - GetParam().content_period; 293 base::TimeTicks next_content_time = begin;
217 std::vector<Event> events; 294 std::vector<Event> events;
218 for (base::TimeTicks compositor_time = begin; compositor_time < end; 295 base::TimeTicks compositor_time;
296 for (compositor_time = begin; compositor_time < end;
219 compositor_time += GetParam().vsync_interval) { 297 compositor_time += GetParam().vsync_interval) {
220 if (include_content_frame_events && next_content_time < compositor_time) { 298 if (next_content_time <= compositor_time) {
221 events.push_back(Event(GetContentDamageRect(), compositor_time));
222 next_content_time += GetParam().content_period; 299 next_content_time += GetParam().content_period;
223 } else if (include_random_events && GetRandomInRange(0, 1) == 0) { 300 if (include_content_frame_events) {
301 events.push_back(Event(GetContentDamageRect(), compositor_time));
302 continue;
303 }
304 }
305 if (include_random_events && GetRandomInRange(0, 1) == 0) {
224 events.push_back(Event(GetRandomDamageRect(), compositor_time)); 306 events.push_back(Event(GetRandomDamageRect(), compositor_time));
225 } 307 }
226 } 308 }
227 309
310 if (next_begin_time) {
311 while (compositor_time < next_content_time)
312 compositor_time += GetParam().vsync_interval;
313 *next_begin_time = compositor_time;
314 }
315
228 DCHECK(!events.empty()); 316 DCHECK(!events.empty());
229 return events; 317 return events;
230 } 318 }
231 319
232 // Feed |events| through the sampler, and detect whether the expected 320 // Feed |events| through the sampler, and detect whether the expected
233 // lock-in/out transition occurs. Also, track and measure the frame drop 321 // lock-in/out transition occurs. Also, track and measure the frame drop
234 // ratio and check it against the expected drop rate. 322 // ratio and check it against the expected drop rate.
235 void RunEventSequence(const std::vector<Event> events, 323 void RunEventSequence(const std::vector<Event> events,
236 bool was_detecting_before, 324 bool was_detecting_before,
237 bool is_detecting_after, 325 bool is_detecting_after,
238 bool simulate_pipeline_back_pressure) { 326 bool simulate_pipeline_back_pressure,
327 const char* description) {
328 SCOPED_TRACE(::testing::Message() << "Description: " << description);
329
239 gfx::Rect first_detected_region; 330 gfx::Rect first_detected_region;
240 331
241 EXPECT_EQ(was_detecting_before, sampler()->HasProposal()); 332 EXPECT_EQ(was_detecting_before, sampler()->HasProposal());
242 bool has_detection_switched = false; 333 bool has_detection_switched = false;
334 bool has_detection_flip_flopped_once = false;
243 ResetFrameCounters(); 335 ResetFrameCounters();
244 for (std::vector<Event>::const_iterator i = events.begin(); 336 for (std::vector<Event>::const_iterator i = events.begin();
245 i != events.end(); ++i) { 337 i != events.end(); ++i) {
246 sampler()->ConsiderPresentationEvent(i->first, i->second); 338 sampler()->ConsiderPresentationEvent(i->first, i->second);
247 339
248 // Detect when the sampler locks in/out, and that it stays that way for 340 // Detect when the sampler locks in/out, and that it stays that way for
249 // all further iterations of this loop. 341 // all further iterations of this loop. It is permissible for the lock-in
342 // to flip-flop once, but no more than that.
250 if (!has_detection_switched && 343 if (!has_detection_switched &&
251 was_detecting_before != sampler()->HasProposal()) { 344 was_detecting_before != sampler()->HasProposal()) {
252 has_detection_switched = true; 345 has_detection_switched = true;
346 } else if (has_detection_switched &&
347 is_detecting_after != sampler()->HasProposal()) {
348 ASSERT_FALSE(has_detection_flip_flopped_once);
349 has_detection_flip_flopped_once = true;
350 has_detection_switched = false;
253 } 351 }
254 ASSERT_EQ( 352 ASSERT_EQ(
255 has_detection_switched ? is_detecting_after : was_detecting_before, 353 has_detection_switched ? is_detecting_after : was_detecting_before,
256 sampler()->HasProposal()); 354 sampler()->HasProposal());
257 355
258 if (sampler()->HasProposal()) { 356 if (sampler()->HasProposal()) {
259 // Make sure the sampler doesn't flip-flop and keep proposing sampling 357 // Make sure the sampler doesn't flip-flop and keep proposing sampling
260 // based on locking into different regions. 358 // based on locking into different regions.
261 if (first_detected_region.IsEmpty()) { 359 if (first_detected_region.IsEmpty()) {
262 first_detected_region = sampler()->detected_region(); 360 first_detected_region = sampler()->detected_region();
(...skipping 44 matching lines...) Expand 10 before | Expand all | Expand 10 after
307 } 405 }
308 } 406 }
309 407
310 // Confirm the AnimatedContentSampler is not dropping more frames than 408 // Confirm the AnimatedContentSampler is not dropping more frames than
311 // expected, given current test parameters. 409 // expected, given current test parameters.
312 void ExpectFrameDropRatioIsCorrect() { 410 void ExpectFrameDropRatioIsCorrect() {
313 if (count_sampled_frames_ == 0) { 411 if (count_sampled_frames_ == 0) {
314 EXPECT_EQ(0, count_dropped_frames_); 412 EXPECT_EQ(0, count_dropped_frames_);
315 return; 413 return;
316 } 414 }
317 const double content_framerate = 415 const double expected_sampling_ratio =
318 1000000.0 / GetParam().content_period.InMicroseconds(); 416 GetParam().content_period.InSecondsF() /
319 const double capture_framerate = 417 ComputeExpectedSamplingPeriod().InSecondsF();
320 1000000.0 / GetParam().min_capture_period.InMicroseconds(); 418 const int total_frames = count_dropped_frames_ + count_sampled_frames_;
321 const double expected_drop_rate = std::max( 419 EXPECT_NEAR(total_frames * expected_sampling_ratio,
322 0.0, (content_framerate - capture_framerate) / capture_framerate); 420 count_sampled_frames_,
323 const double actual_drop_rate = 421 1.5);
324 static_cast<double>(count_dropped_frames_) / count_sampled_frames_; 422 EXPECT_NEAR(total_frames * (1.0 - expected_sampling_ratio),
325 EXPECT_NEAR(expected_drop_rate, actual_drop_rate, 0.015); 423 count_dropped_frames_,
424 1.5);
326 } 425 }
327 426
328 private: 427 private:
329 // These counters only include the frames with the desired content. 428 // These counters only include the frames with the desired content.
330 int count_dropped_frames_; 429 int count_dropped_frames_;
331 int count_sampled_frames_; 430 int count_sampled_frames_;
332 }; 431 };
333 432
334 // Tests that the implementation locks in/out of frames containing stable 433 // Tests that the implementation locks in/out of frames containing stable
335 // animated content, whether or not random events are also simultaneously 434 // animated content, whether or not random events are also simultaneously
336 // present. 435 // present.
337 TEST_P(AnimatedContentSamplerParameterizedTest, DetectsAnimatedContent) { 436 TEST_P(AnimatedContentSamplerParameterizedTest, DetectsAnimatedContent) {
338 // |begin| refers to the start of an event sequence in terms of the 437 // |begin| refers to the start of an event sequence in terms of the
339 // Compositor's clock. 438 // Compositor's clock.
340 base::TimeTicks begin = InitialTestTimeTicks(); 439 base::TimeTicks begin = InitialTestTimeTicks();
341 440
342 // Provide random events and expect no lock-in. 441 // Provide random events and expect no lock-in.
343 base::TimeTicks end = begin + base::TimeDelta::FromSeconds(5); 442 RunEventSequence(
344 RunEventSequence(GenerateEventSequence(begin, end, false, true), 443 GenerateEventSequence(begin,
345 false, 444 begin + base::TimeDelta::FromSeconds(5),
346 false, 445 false,
347 false); 446 true,
348 begin = end; 447 &begin),
448 false,
449 false,
450 false,
451 "Provide random events and expect no lock-in.");
452 if (HasFailure())
453 return;
349 454
350 // Provide content frame events with some random events mixed-in, and expect 455 // Provide content frame events with some random events mixed-in, and expect
351 // the sampler to lock-in. 456 // the sampler to lock-in.
352 end = begin + base::TimeDelta::FromSeconds(5); 457 RunEventSequence(
353 RunEventSequence(GenerateEventSequence(begin, end, true, true), 458 GenerateEventSequence(begin,
354 false, 459 begin + base::TimeDelta::FromSeconds(5),
355 true, 460 true,
356 false); 461 true,
357 begin = end; 462 &begin),
463 false,
464 true,
465 false,
466 "Provide content frame events with some random events mixed-in, and "
467 "expect the sampler to lock-in.");
468 if (HasFailure())
469 return;
358 470
359 // Continue providing content frame events without the random events mixed-in 471 // Continue providing content frame events without the random events mixed-in
360 // and expect the lock-in to hold. 472 // and expect the lock-in to hold.
361 end = begin + base::TimeDelta::FromSeconds(5); 473 RunEventSequence(
362 RunEventSequence(GenerateEventSequence(begin, end, true, false), 474 GenerateEventSequence(begin,
363 true, 475 begin + base::TimeDelta::FromSeconds(5),
364 true, 476 true,
365 false); 477 false,
366 begin = end; 478 &begin),
479 true,
480 true,
481 false,
482 "Continue providing content frame events without the random events "
483 "mixed-in and expect the lock-in to hold.");
484 if (HasFailure())
485 return;
367 486
368 // Continue providing just content frame events and expect the lock-in to 487 // Continue providing just content frame events and expect the lock-in to
369 // hold. Also simulate the capture pipeline experiencing back pressure. 488 // hold. Also simulate the capture pipeline experiencing back pressure.
370 end = begin + base::TimeDelta::FromSeconds(20); 489 RunEventSequence(
371 RunEventSequence(GenerateEventSequence(begin, end, true, false), 490 GenerateEventSequence(begin,
372 true, 491 begin + base::TimeDelta::FromSeconds(20),
373 true, 492 true,
374 true); 493 false,
375 begin = end; 494 &begin),
495 true,
496 true,
497 true,
498 "Continue providing just content frame events and expect the lock-in to "
499 "hold. Also simulate the capture pipeline experiencing back pressure.");
500 if (HasFailure())
501 return;
502
376 503
377 // Provide a half-second of random events only, and expect the lock-in to be 504 // Provide a half-second of random events only, and expect the lock-in to be
378 // broken. 505 // broken.
379 end = begin + base::TimeDelta::FromMilliseconds(500); 506 RunEventSequence(
380 RunEventSequence(GenerateEventSequence(begin, end, false, true), 507 GenerateEventSequence(begin,
381 true, 508 begin + base::TimeDelta::FromMilliseconds(500),
382 false, 509 false,
383 false); 510 true,
384 begin = end; 511 &begin),
512 true,
513 false,
514 false,
515 "Provide a half-second of random events only, and expect the lock-in to "
516 "be broken.");
517 if (HasFailure())
518 return;
385 519
386 // Now, go back to providing content frame events, and expect the sampler to 520 // Now, go back to providing content frame events, and expect the sampler to
387 // lock-in once again. 521 // lock-in once again.
388 end = begin + base::TimeDelta::FromSeconds(5); 522 RunEventSequence(
389 RunEventSequence(GenerateEventSequence(begin, end, true, false), 523 GenerateEventSequence(begin,
390 false, 524 begin + base::TimeDelta::FromSeconds(5),
391 true, 525 true,
392 false); 526 false,
393 begin = end; 527 &begin),
528 false,
529 true,
530 false,
531 "Now, go back to providing content frame events, and expect the sampler "
532 "to lock-in once again.");
394 } 533 }
395 534
396 // Tests that AnimatedContentSampler won't lock in to, nor flip-flop between, 535 // Tests that AnimatedContentSampler won't lock in to, nor flip-flop between,
397 // two animations of the same pixel change rate. VideoCaptureOracle should 536 // two animations of the same pixel change rate. VideoCaptureOracle should
398 // revert to using the SmoothEventSampler for these kinds of situations, as 537 // revert to using the SmoothEventSampler for these kinds of situations, as
399 // there is no "right answer" as to which animation to lock into. 538 // there is no "right answer" as to which animation to lock into.
400 TEST_P(AnimatedContentSamplerParameterizedTest, 539 TEST_P(AnimatedContentSamplerParameterizedTest,
401 DoesNotLockInToTwoCompetingAnimations) { 540 DoesNotLockInToTwoCompetingAnimations) {
402 // Don't test when the event stream cannot indicate two separate content 541 // Don't test when the event stream cannot indicate two separate content
403 // animations under the current test parameters. 542 // animations under the current test parameters.
404 if (GetParam().content_period < 2 * GetParam().vsync_interval) 543 if (GetParam().content_period < 2 * GetParam().vsync_interval)
405 return; 544 return;
406 545
407 // Start the first animation and run for a bit, and expect the sampler to 546 // Start the first animation and run for a bit, and expect the sampler to
408 // lock-in. 547 // lock-in.
409 base::TimeTicks begin = InitialTestTimeTicks(); 548 base::TimeTicks begin = InitialTestTimeTicks();
410 base::TimeTicks end = begin + base::TimeDelta::FromSeconds(5); 549 RunEventSequence(
411 RunEventSequence(GenerateEventSequence(begin, end, true, false), 550 GenerateEventSequence(begin,
412 false, 551 begin + base::TimeDelta::FromSeconds(5),
413 true, 552 true,
414 false); 553 false,
415 begin = end; 554 &begin),
555 false,
556 true,
557 false,
558 "Start the first animation and run for a bit, and expect the sampler to "
559 "lock-in.");
560 if (HasFailure())
561 return;
416 562
417 // Now, keep the first animation and blend in an second animation of the same 563 // Now, keep the first animation and blend in a second animation of the same
418 // size and frame rate, but at a different position. This will should cause 564 // 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 565 // the sampler to enter an "undetected" state since it's unclear which
420 // animation should be locked into. 566 // animation should be locked into.
421 end = begin + base::TimeDelta::FromSeconds(20);
422 std::vector<Event> first_animation_events = 567 std::vector<Event> first_animation_events =
423 GenerateEventSequence(begin, end, true, false); 568 GenerateEventSequence(begin,
569 begin + base::TimeDelta::FromSeconds(20),
570 true,
571 false,
572 &begin);
424 gfx::Rect second_animation_rect( 573 gfx::Rect second_animation_rect(
425 gfx::Point(0, GetContentDamageRect().height()), 574 gfx::Point(0, GetContentDamageRect().height()),
426 GetContentDamageRect().size()); 575 GetContentDamageRect().size());
427 std::vector<Event> both_animations_events; 576 std::vector<Event> both_animations_events;
428 base::TimeDelta second_animation_offset = GetParam().vsync_interval; 577 base::TimeDelta second_animation_offset = GetParam().vsync_interval;
429 for (std::vector<Event>::const_iterator i = first_animation_events.begin(); 578 for (std::vector<Event>::const_iterator i = first_animation_events.begin();
430 i != first_animation_events.end(); ++i) { 579 i != first_animation_events.end(); ++i) {
431 both_animations_events.push_back(*i); 580 both_animations_events.push_back(*i);
432 both_animations_events.push_back( 581 both_animations_events.push_back(
433 Event(second_animation_rect, i->second + second_animation_offset)); 582 Event(second_animation_rect, i->second + second_animation_offset));
434 } 583 }
435 RunEventSequence(both_animations_events, true, false, false); 584 RunEventSequence(
436 begin = end; 585 both_animations_events, true, false, false,
586 "Now, blend-in a second animation of the same size and frame rate, but "
587 "at a different position.");
588 if (HasFailure())
589 return;
437 590
438 // Now, run just the first animation, and expect the sampler to lock-in once 591 // Now, run just the first animation, and expect the sampler to lock-in once
439 // again. 592 // again.
440 end = begin + base::TimeDelta::FromSeconds(5); 593 RunEventSequence(
441 RunEventSequence(GenerateEventSequence(begin, end, true, false), 594 GenerateEventSequence(begin,
442 false, 595 begin + base::TimeDelta::FromSeconds(5),
443 true, 596 true,
444 false); 597 false,
445 begin = end; 598 &begin),
599 false,
600 true,
601 false,
602 "Now, run just the first animation, and expect the sampler to lock-in "
603 "once again.");
604 if (HasFailure())
605 return;
446 606
447 // Now, blend in the second animation again, but it has half the frame rate of 607 // 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 608 // 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 609 // cause the sampler to enter an "undetected" state again. This tests that
450 // pixel-weighting is being accounted for in the sampler's logic. 610 // pixel-weighting is being accounted for in the sampler's logic.
451 end = begin + base::TimeDelta::FromSeconds(20); 611 first_animation_events =
452 first_animation_events = GenerateEventSequence(begin, end, true, false); 612 GenerateEventSequence(begin,
613 begin + base::TimeDelta::FromSeconds(20),
614 true,
615 false,
616 &begin);
453 second_animation_rect.set_width(second_animation_rect.width() * 2); 617 second_animation_rect.set_width(second_animation_rect.width() * 2);
454 both_animations_events.clear(); 618 both_animations_events.clear();
455 bool include_second_animation_frame = true; 619 bool include_second_animation_frame = true;
456 for (std::vector<Event>::const_iterator i = first_animation_events.begin(); 620 for (std::vector<Event>::const_iterator i = first_animation_events.begin();
457 i != first_animation_events.end(); ++i) { 621 i != first_animation_events.end(); ++i) {
458 both_animations_events.push_back(*i); 622 both_animations_events.push_back(*i);
459 if (include_second_animation_frame) { 623 if (include_second_animation_frame) {
460 both_animations_events.push_back( 624 both_animations_events.push_back(
461 Event(second_animation_rect, i->second + second_animation_offset)); 625 Event(second_animation_rect, i->second + second_animation_offset));
462 } 626 }
463 include_second_animation_frame = !include_second_animation_frame; 627 include_second_animation_frame = !include_second_animation_frame;
464 } 628 }
465 RunEventSequence(both_animations_events, true, false, false); 629 RunEventSequence(
466 begin = end; 630 both_animations_events, true, false, false,
631 "Now, blend in the second animation again, but it has half the frame "
632 "rate of the first animation and damage Rects with twice the area.");
467 } 633 }
468 634
469 // Tests that the frame timestamps are smooth; meaning, that when run through a 635 // 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 636 // simulated compositor, each frame is held displayed for the right number of
471 // v-sync intervals. 637 // v-sync intervals.
472 TEST_P(AnimatedContentSamplerParameterizedTest, FrameTimestampsAreSmooth) { 638 TEST_P(AnimatedContentSamplerParameterizedTest, FrameTimestampsAreSmooth) {
473 // Generate 30 seconds of animated content events, run the events through 639 // Generate 30 seconds of animated content events, run the events through
474 // AnimatedContentSampler, and record all frame timestamps being proposed 640 // AnimatedContentSampler, and record all frame timestamps being proposed
475 // once lock-in is continuous. 641 // once lock-in is continuous.
476 base::TimeTicks begin = InitialTestTimeTicks(); 642 const base::TimeTicks begin = InitialTestTimeTicks();
477 std::vector<Event> events = GenerateEventSequence( 643 std::vector<Event> events = GenerateEventSequence(
478 begin, 644 begin,
479 begin + base::TimeDelta::FromSeconds(20), 645 begin + base::TimeDelta::FromSeconds(20),
480 true, 646 true,
481 false); 647 false,
648 nullptr);
482 typedef std::vector<base::TimeTicks> Timestamps; 649 typedef std::vector<base::TimeTicks> Timestamps;
483 Timestamps frame_timestamps; 650 Timestamps frame_timestamps;
484 for (std::vector<Event>::const_iterator i = events.begin(); i != events.end(); 651 for (std::vector<Event>::const_iterator i = events.begin(); i != events.end();
485 ++i) { 652 ++i) {
486 sampler()->ConsiderPresentationEvent(i->first, i->second); 653 sampler()->ConsiderPresentationEvent(i->first, i->second);
487 if (sampler()->HasProposal()) { 654 if (sampler()->HasProposal()) {
488 if (sampler()->ShouldSample()) { 655 if (sampler()->ShouldSample()) {
489 frame_timestamps.push_back(sampler()->frame_timestamp()); 656 frame_timestamps.push_back(sampler()->frame_timestamp());
490 sampler()->RecordSample(sampler()->frame_timestamp()); 657 sampler()->RecordSample(sampler()->frame_timestamp());
491 } 658 }
492 } else { 659 } else {
493 frame_timestamps.clear(); // Reset until continuous lock-in. 660 frame_timestamps.clear(); // Reset until continuous lock-in.
494 } 661 }
495 } 662 }
496 ASSERT_LE(2u, frame_timestamps.size()); 663 ASSERT_LE(2u, frame_timestamps.size());
497 664
498 // Iterate through the |frame_timestamps|, building a histogram counting the 665 // Iterate through the |frame_timestamps|, building a histogram counting the
499 // number of times each frame was displayed k times. For example, 10 frames 666 // 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 667 // 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 668 // display_counts[2] == 10. Quit early if any one frame was obviously
502 // repeated too many times. 669 // repeated too many times.
503 const int64 max_expected_repeats_per_frame = 1 + 670 const int64 max_expected_repeats_per_frame = 1 +
504 std::max(GetParam().min_capture_period, GetParam().content_period) / 671 ComputeExpectedSamplingPeriod() / GetParam().vsync_interval;
505 GetParam().vsync_interval;
506 std::vector<size_t> display_counts(max_expected_repeats_per_frame + 1, 0); 672 std::vector<size_t> display_counts(max_expected_repeats_per_frame + 1, 0);
507 base::TimeTicks last_present_time = frame_timestamps.front(); 673 base::TimeTicks last_present_time = frame_timestamps.front();
508 for (Timestamps::const_iterator i = frame_timestamps.begin() + 1; 674 for (Timestamps::const_iterator i = frame_timestamps.begin() + 1;
509 i != frame_timestamps.end(); ++i) { 675 i != frame_timestamps.end(); ++i) {
510 const size_t num_vsync_intervals = static_cast<size_t>( 676 const size_t num_vsync_intervals = static_cast<size_t>(
511 (*i - last_present_time) / GetParam().vsync_interval); 677 (*i - last_present_time) / GetParam().vsync_interval);
512 ASSERT_LT(0u, num_vsync_intervals); 678 ASSERT_LT(0u, num_vsync_intervals);
513 ASSERT_GT(display_counts.size(), num_vsync_intervals); // Quit early. 679 ASSERT_GT(display_counts.size(), num_vsync_intervals); // Quit early.
514 ++display_counts[num_vsync_intervals]; 680 ++display_counts[num_vsync_intervals];
515 last_present_time += num_vsync_intervals * GetParam().vsync_interval; 681 last_present_time += num_vsync_intervals * GetParam().vsync_interval;
(...skipping 18 matching lines...) Expand all
534 } 700 }
535 } 701 }
536 size_t stray_count_remaining = 702 size_t stray_count_remaining =
537 (frame_timestamps.size() - 1) - (highest_count + second_highest_count); 703 (frame_timestamps.size() - 1) - (highest_count + second_highest_count);
538 // Expect no more than 0.75% of frames fall outside the two main buckets. 704 // 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); 705 EXPECT_GT(frame_timestamps.size() * 75 / 10000, stray_count_remaining);
540 for (size_t repeats = 1; repeats < display_counts.size() - 1; ++repeats) { 706 for (size_t repeats = 1; repeats < display_counts.size() - 1; ++repeats) {
541 if (display_counts[repeats] == highest_count) { 707 if (display_counts[repeats] == highest_count) {
542 EXPECT_EQ(second_highest_count, display_counts[repeats + 1]); 708 EXPECT_EQ(second_highest_count, display_counts[repeats + 1]);
543 ++repeats; 709 ++repeats;
544 } else if (display_counts[repeats] == second_highest_count) { 710 } else if (second_highest_count > 0 &&
711 display_counts[repeats] == second_highest_count) {
545 EXPECT_EQ(highest_count, display_counts[repeats + 1]); 712 EXPECT_EQ(highest_count, display_counts[repeats + 1]);
546 ++repeats; 713 ++repeats;
547 } else { 714 } else {
548 EXPECT_GE(stray_count_remaining, display_counts[repeats]); 715 EXPECT_GE(stray_count_remaining, display_counts[repeats]);
549 stray_count_remaining -= display_counts[repeats]; 716 stray_count_remaining -= display_counts[repeats];
550 } 717 }
551 } 718 }
552 } 719 }
553 720
554 // Tests that frame timestamps are "lightly pushed" back towards the original 721 // Tests that frame timestamps are "lightly pushed" back towards the original
555 // presentation event times, which tells us the AnimatedContentSampler can 722 // presentation event times, which tells us the AnimatedContentSampler can
556 // account for sources of timestamp drift and correct the drift. 723 // account for sources of timestamp drift and correct the drift.
557 TEST_P(AnimatedContentSamplerParameterizedTest, 724 TEST_P(AnimatedContentSamplerParameterizedTest,
558 FrameTimestampsConvergeTowardsEventTimes) { 725 FrameTimestampsConvergeTowardsEventTimes) {
559 const int max_drift_increment_millis = 3; 726 const int max_drift_increment_millis = 3;
560 727
561 // Generate a full minute of events. 728 // Generate a full minute of events.
562 const base::TimeTicks begin = InitialTestTimeTicks(); 729 const base::TimeTicks begin = InitialTestTimeTicks();
563 const base::TimeTicks end = begin + base::TimeDelta::FromMinutes(1); 730 std::vector<Event> events =
564 std::vector<Event> events = GenerateEventSequence(begin, end, true, false); 731 GenerateEventSequence(begin,
732 begin + base::TimeDelta::FromMinutes(1),
733 true,
734 false,
735 nullptr);
565 736
566 // Modify the event sequence so that 1-3 ms of additional drift is suddenly 737 // 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 738 // present every 100 events. This is meant to simulate that, external to
568 // AnimatedContentSampler, the video hardware vsync timebase is being 739 // AnimatedContentSampler, the video hardware vsync timebase is being
569 // refreshed and is showing severe drift from the system clock. 740 // refreshed and is showing severe drift from the system clock.
570 base::TimeDelta accumulated_drift; 741 base::TimeDelta accumulated_drift;
571 for (size_t i = 1; i < events.size(); ++i) { 742 for (size_t i = 1; i < events.size(); ++i) {
572 if (i % 100 == 0) { 743 if (i % 100 == 0) {
573 accumulated_drift += base::TimeDelta::FromMilliseconds( 744 accumulated_drift += base::TimeDelta::FromMilliseconds(
574 GetRandomInRange(1, max_drift_increment_millis + 1)); 745 GetRandomInRange(1, max_drift_increment_millis + 1));
(...skipping 21 matching lines...) Expand all
596 total_error.InMicroseconds(), 767 total_error.InMicroseconds(),
597 max_acceptable_error.InMicroseconds()); 768 max_acceptable_error.InMicroseconds());
598 } 769 }
599 770
600 INSTANTIATE_TEST_CASE_P( 771 INSTANTIATE_TEST_CASE_P(
601 , 772 ,
602 AnimatedContentSamplerParameterizedTest, 773 AnimatedContentSamplerParameterizedTest,
603 ::testing::Values( 774 ::testing::Values(
604 // Typical frame rate content: Compositor runs at 60 Hz, capture at 30 775 // Typical frame rate content: Compositor runs at 60 Hz, capture at 30
605 // Hz, and content video animates at 30, 25, or 24 Hz. 776 // Hz, and content video animates at 30, 25, or 24 Hz.
606 Scenario(FpsAsPeriod(60), FpsAsPeriod(30), FpsAsPeriod(30)), 777 Scenario(60, 30, 30),
607 Scenario(FpsAsPeriod(60), FpsAsPeriod(30), FpsAsPeriod(25)), 778 Scenario(60, 30, 25),
608 Scenario(FpsAsPeriod(60), FpsAsPeriod(30), FpsAsPeriod(24)), 779 Scenario(60, 30, 24),
609 780
610 // High frame rate content that leverages the Compositor's 781 // High frame rate content that leverages the Compositor's
611 // capabilities, but capture is still at 30 Hz. 782 // capabilities, but capture is still at 30 Hz.
612 Scenario(FpsAsPeriod(60), FpsAsPeriod(30), FpsAsPeriod(60)), 783 Scenario(60, 30, 60),
613 Scenario(FpsAsPeriod(60), FpsAsPeriod(30), FpsAsPeriod(50)), 784 Scenario(60, 30, 50),
614 Scenario(FpsAsPeriod(60), FpsAsPeriod(30), FpsAsPeriod(48)), 785 Scenario(60, 30, 48),
615 786
616 // High frame rate content that leverages the Compositor's 787 // High frame rate content that leverages the Compositor's
617 // capabilities, and capture is also a buttery 60 Hz. 788 // capabilities, and capture is also a buttery 60 Hz.
618 Scenario(FpsAsPeriod(60), FpsAsPeriod(60), FpsAsPeriod(60)), 789 Scenario(60, 60, 60),
619 Scenario(FpsAsPeriod(60), FpsAsPeriod(60), FpsAsPeriod(50)), 790 Scenario(60, 60, 50),
620 Scenario(FpsAsPeriod(60), FpsAsPeriod(60), FpsAsPeriod(48)), 791 Scenario(60, 60, 48),
792
793 // High frame rate content that leverages the Compositor's
794 // capabilities, but the client has disabled HFR sampling.
795 Scenario(60, 60, 60, 30),
796 Scenario(60, 60, 50, 30),
797 Scenario(60, 60, 48, 30),
621 798
622 // On some platforms, the Compositor runs at 50 Hz. 799 // On some platforms, the Compositor runs at 50 Hz.
623 Scenario(FpsAsPeriod(50), FpsAsPeriod(30), FpsAsPeriod(30)), 800 Scenario(50, 30, 30),
624 Scenario(FpsAsPeriod(50), FpsAsPeriod(30), FpsAsPeriod(25)), 801 Scenario(50, 30, 25),
625 Scenario(FpsAsPeriod(50), FpsAsPeriod(30), FpsAsPeriod(24)), 802 Scenario(50, 30, 24),
626 Scenario(FpsAsPeriod(50), FpsAsPeriod(30), FpsAsPeriod(50)), 803 Scenario(50, 30, 50),
627 Scenario(FpsAsPeriod(50), FpsAsPeriod(30), FpsAsPeriod(48)), 804 Scenario(50, 30, 48),
628 805
629 // Stable, but non-standard content frame rates. 806 // Stable, but non-standard content frame rates.
630 Scenario(FpsAsPeriod(60), FpsAsPeriod(30), FpsAsPeriod(16)), 807 Scenario(60, 30, 16),
631 Scenario(FpsAsPeriod(60), FpsAsPeriod(30), FpsAsPeriod(20)), 808 Scenario(60, 30, 20),
632 Scenario(FpsAsPeriod(60), FpsAsPeriod(30), FpsAsPeriod(23)), 809 Scenario(60, 30, 23),
633 Scenario(FpsAsPeriod(60), FpsAsPeriod(30), FpsAsPeriod(26)), 810 Scenario(60, 30, 26),
634 Scenario(FpsAsPeriod(60), FpsAsPeriod(30), FpsAsPeriod(27)), 811 Scenario(60, 30, 27),
635 Scenario(FpsAsPeriod(60), FpsAsPeriod(30), FpsAsPeriod(28)), 812 Scenario(60, 30, 28),
636 Scenario(FpsAsPeriod(60), FpsAsPeriod(30), FpsAsPeriod(29)), 813 Scenario(60, 30, 29),
637 Scenario(FpsAsPeriod(60), FpsAsPeriod(30), FpsAsPeriod(31)), 814 Scenario(60, 30, 31),
638 Scenario(FpsAsPeriod(60), FpsAsPeriod(30), FpsAsPeriod(32)), 815 Scenario(60, 30, 32),
639 Scenario(FpsAsPeriod(60), FpsAsPeriod(30), FpsAsPeriod(33)))); 816 Scenario(60, 30, 33)));
640 817
641 } // namespace content 818 } // namespace content
OLDNEW
« no previous file with comments | « content/browser/media/capture/animated_content_sampler.cc ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698