1 // Copyright 2015 The Chromium Authors
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/filters/video_renderer_algorithm.h"
13 #include "base/bind.h"
14 #include "base/callback_helpers.h"
15 #include "base/memory/ref_counted.h"
16 #include "base/strings/stringprintf.h"
17 #include "base/test/simple_test_tick_clock.h"
18 #include "base/time/time.h"
19 #include "build/build_config.h"
20 #include "media/base/media_util.h"
21 #include "media/base/timestamp_constants.h"
22 #include "media/base/video_frame_pool.h"
23 #include "media/base/wall_clock_time_source.h"
24 #include "testing/gtest/include/gtest/gtest.h"
28 // Slows down the given |fps| according to NTSC field reduction standards; see
29 // http://en.wikipedia.org/wiki/Frame_rate#Digital_video_and_television
30 static double NTSC(double fps) {
34 // Helper class for generating TimeTicks in a sequence according to a frequency.
37 TickGenerator(base::TimeTicks base_timestamp, double hertz)
40 microseconds_per_tick_(base::Time::kMicrosecondsPerSecond / hertz),
41 base_time_(base_timestamp) {}
43 TickGenerator(const TickGenerator&) = delete;
44 TickGenerator& operator=(const TickGenerator&) = delete;
46 base::TimeDelta interval(int tick_count) const {
47 return base::Microseconds(tick_count * microseconds_per_tick_);
50 base::TimeTicks current() const { return base_time_ + interval(tick_count_); }
51 base::TimeTicks step() { return step(1); }
52 base::TimeTicks step(int n) {
57 double hertz() const { return hertz_; }
59 void Reset(base::TimeTicks base_timestamp) {
60 base_time_ = base_timestamp;
65 // Track a tick count and seconds per tick value to ensure we don't drift too
66 // far due to accumulated errors during testing.
69 const double microseconds_per_tick_;
70 base::TimeTicks base_time_;
73 class VideoRendererAlgorithmTest : public testing::Test {
75 VideoRendererAlgorithmTest()
76 : tick_clock_(new base::SimpleTestTickClock()),
77 algorithm_(base::BindRepeating(&WallClockTimeSource::GetWallClockTimes,
78 base::Unretained(&time_source_)),
80 // Always start the TickClock at a non-zero value since null values have
81 // special connotations.
82 tick_clock_->Advance(base::Microseconds(10000));
83 time_source_.SetTickClockForTesting(tick_clock_.get());
86 VideoRendererAlgorithmTest(const VideoRendererAlgorithmTest&) = delete;
87 VideoRendererAlgorithmTest& operator=(const VideoRendererAlgorithmTest&) =
90 ~VideoRendererAlgorithmTest() override = default;
92 scoped_refptr<VideoFrame> CreateFrame(base::TimeDelta timestamp) {
93 const gfx::Size natural_size(8, 8);
94 return frame_pool_.CreateFrame(PIXEL_FORMAT_I420, natural_size,
95 gfx::Rect(natural_size), natural_size,
99 base::TimeDelta minimum_glitch_time() const {
100 return base::Seconds(
101 VideoRendererAlgorithm::kMinimumAcceptableTimeBetweenGlitchesSecs);
104 base::TimeDelta max_acceptable_drift() const {
105 return algorithm_.max_acceptable_drift_;
108 void disable_cadence_hysteresis() {
109 algorithm_.cadence_estimator_.set_cadence_hysteresis_threshold_for_testing(
113 bool last_render_had_glitch() const {
114 return algorithm_.last_render_had_glitch_;
117 bool is_using_cadence() const {
118 return algorithm_.cadence_estimator_.has_cadence();
121 bool IsCadenceBelowOne() const {
122 if (!is_using_cadence())
125 return algorithm_.cadence_estimator_.avg_cadence_for_testing() < 1.0;
128 double CadenceValue() const {
129 return algorithm_.cadence_estimator_.avg_cadence_for_testing();
132 size_t frames_queued() const { return algorithm_.frame_queue_.size(); }
134 std::string GetCadence(double frame_rate, double display_rate) {
135 TickGenerator display_tg(tick_clock_->NowTicks(), display_rate);
136 TickGenerator frame_tg(base::TimeTicks(), frame_rate);
137 time_source_.StartTicking();
139 // Enqueue enough frames for cadence detection.
140 size_t frames_dropped = 0;
141 disable_cadence_hysteresis();
142 algorithm_.EnqueueFrame(CreateFrame(frame_tg.interval(0)));
143 algorithm_.EnqueueFrame(CreateFrame(frame_tg.interval(1)));
144 EXPECT_TRUE(RenderAndStep(&display_tg, &frames_dropped));
146 // Store cadence before reseting the algorithm.
147 const std::string cadence =
148 algorithm_.cadence_estimator_.GetCadenceForTesting();
149 time_source_.StopTicking();
154 base::TimeDelta CalculateAbsoluteDriftForFrame(base::TimeTicks deadline_min,
156 return algorithm_.CalculateAbsoluteDriftForFrame(deadline_min, frame_index);
159 bool DriftOfLastRenderWasWithinTolerance(base::TimeTicks deadline_min) {
160 return CalculateAbsoluteDriftForFrame(deadline_min, 0) <=
161 algorithm_.max_acceptable_drift_;
164 scoped_refptr<VideoFrame> RenderAndStep(TickGenerator* tg,
165 size_t* frames_dropped) {
166 const base::TimeTicks start = tg->current();
167 const base::TimeTicks end = tg->step();
168 return algorithm_.Render(start, end, frames_dropped);
171 // Allows tests to run a Render() loop with sufficient frames for the various
172 // rendering modes. Upon each Render() |render_test_func| will be called with
173 // the rendered frame and the number of frames dropped.
174 template <typename OnRenderCallback>
175 void RunFramePumpTest(bool reset,
176 TickGenerator* frame_tg,
177 TickGenerator* display_tg,
178 OnRenderCallback render_test_func) {
179 SCOPED_TRACE(base::StringPrintf("Rendering %.03f fps into %0.03f",
180 frame_tg->hertz(), display_tg->hertz()));
181 tick_clock_->Advance(display_tg->current() - tick_clock_->NowTicks());
182 time_source_.StartTicking();
184 const bool fresh_algorithm = !algorithm_.have_rendered_frames_;
186 base::TimeDelta last_start_timestamp = kNoTimestamp;
187 bool should_use_cadence = false;
188 int glitch_count = 0;
189 const base::TimeTicks start_time = tick_clock_->NowTicks();
190 while (tick_clock_->NowTicks() - start_time < minimum_glitch_time()) {
191 while (EffectiveFramesQueued() < 3 ||
192 frame_tg->current() - time_source_.CurrentMediaTime() <
194 algorithm_.EnqueueFrame(
195 CreateFrame(frame_tg->current() - base::TimeTicks()));
199 size_t frames_dropped = 0;
200 const base::TimeTicks deadline_min = display_tg->current();
201 const base::TimeTicks deadline_max = display_tg->step();
202 scoped_refptr<VideoFrame> frame =
203 algorithm_.Render(deadline_min, deadline_max, &frames_dropped);
204 EXPECT_EQ(deadline_max - deadline_min, algorithm_.render_interval());
206 render_test_func(frame, frames_dropped);
207 tick_clock_->Advance(display_tg->current() - tick_clock_->NowTicks());
209 if (HasFatalFailure())
212 // Render() should always return a frame within drift tolerances.
213 ASSERT_TRUE(DriftOfLastRenderWasWithinTolerance(deadline_min));
215 // If we have a frame, the timestamps should always be monotonically
218 if (last_start_timestamp != kNoTimestamp)
219 ASSERT_LE(last_start_timestamp, frame->timestamp());
221 last_start_timestamp = frame->timestamp();
224 // Only verify certain properties for fresh instances.
225 if (fresh_algorithm) {
226 ASSERT_NEAR(frame_tg->interval(1).InMicroseconds(),
227 algorithm_.average_frame_duration().InMicroseconds(), 1);
229 if (is_using_cadence() && last_render_had_glitch())
232 // Once cadence starts, it should never stop for the current set of
234 if (is_using_cadence())
235 should_use_cadence = true;
236 ASSERT_EQ(is_using_cadence(), should_use_cadence);
239 // When there are no frames, we're not using cadence based selection, or a
240 // frame is under cadence the two queue size reports should be equal to
241 // the number of usable frames; i.e. those frames whose end time was not
242 // within the last render interval.
243 if (!is_using_cadence() || !frames_queued() ||
244 GetCurrentFrameDisplayCount() < GetCurrentFrameIdealDisplayCount()) {
245 ASSERT_NEAR(GetUsableFrameCount(deadline_max), EffectiveFramesQueued(),
246 fresh_algorithm ? 0 : 1);
247 } else if (is_using_cadence() && !IsCadenceBelowOne()) {
248 // If there was no glitch in the last render, the two queue sizes should
249 // be off by exactly one frame; i.e., the current frame doesn't count.
250 if (!last_render_had_glitch() && fresh_algorithm)
251 ASSERT_EQ(frames_queued() - 1, EffectiveFramesQueued());
252 } else if (IsCadenceBelowOne()) {
253 // The frame estimate should be off by at most one frame.
254 const size_t estimated_frames_queued =
255 std::floor(frames_queued() * CadenceValue());
256 ASSERT_NEAR(EffectiveFramesQueued(), estimated_frames_queued, 1);
260 // When using cadence, the glitch count should be at most one for when
261 // rendering for the less than minimum_glitch_time().
262 if (fresh_algorithm && is_using_cadence())
263 ASSERT_LE(glitch_count, 1);
265 time_source_.StopTicking();
268 time_source_.SetMediaTime(base::TimeDelta());
272 int FindBestFrameByCoverage(base::TimeTicks deadline_min,
273 base::TimeTicks deadline_max,
275 return algorithm_.FindBestFrameByCoverage(deadline_min, deadline_max,
279 int FindBestFrameByDrift(base::TimeTicks deadline_min,
280 base::TimeDelta* selected_frame_drift) {
281 return algorithm_.FindBestFrameByDrift(deadline_min, selected_frame_drift);
284 int GetCurrentFrameDropCount() const {
285 DCHECK_GT(frames_queued(), 0u);
286 return algorithm_.frame_queue_.front().drop_count;
289 int GetCurrentFrameDisplayCount() const {
290 DCHECK_GT(frames_queued(), 0u);
291 return algorithm_.frame_queue_.front().render_count;
294 int GetCurrentFrameIdealDisplayCount() const {
295 DCHECK_GT(frames_queued(), 0u);
296 return algorithm_.frame_queue_.front().ideal_render_count;
299 int AccountForMissedIntervalsAndStep(TickGenerator* tg) {
300 const base::TimeTicks start = tg->current();
301 const base::TimeTicks end = tg->step();
302 return AccountForMissedIntervals(start, end);
305 int AccountForMissedIntervals(base::TimeTicks deadline_min,
306 base::TimeTicks deadline_max) {
307 algorithm_.AccountForMissedIntervals(deadline_min, deadline_max);
308 return frames_queued() ? GetCurrentFrameDisplayCount() : -1;
311 size_t GetUsableFrameCount(base::TimeTicks deadline_max) {
312 if (is_using_cadence())
313 return frames_queued();
315 for (size_t i = 0; i < frames_queued(); ++i)
316 if (algorithm_.frame_queue_[i].end_time > deadline_max)
317 return frames_queued() - i;
321 size_t EffectiveFramesQueued() {
322 const size_t expected_frames_queued = algorithm_.effective_frames_queued();
323 // These values should always be in sync.
324 algorithm_.UpdateEffectiveFramesQueued();
325 EXPECT_EQ(expected_frames_queued, algorithm_.effective_frames_queued());
326 return expected_frames_queued;
330 NullMediaLog media_log_;
331 VideoFramePool frame_pool_;
332 std::unique_ptr<base::SimpleTestTickClock> tick_clock_;
333 WallClockTimeSource time_source_;
334 VideoRendererAlgorithm algorithm_;
337 TEST_F(VideoRendererAlgorithmTest, Empty) {
338 TickGenerator tg(tick_clock_->NowTicks(), 50);
339 size_t frames_dropped = 0;
340 EXPECT_EQ(0u, frames_queued());
341 EXPECT_FALSE(RenderAndStep(&tg, &frames_dropped));
342 EXPECT_EQ(0u, frames_dropped);
343 EXPECT_EQ(0u, frames_queued());
344 EXPECT_NE(base::TimeDelta(), max_acceptable_drift());
347 TEST_F(VideoRendererAlgorithmTest, Reset) {
348 TickGenerator tg(tick_clock_->NowTicks(), 50);
349 algorithm_.EnqueueFrame(CreateFrame(tg.interval(0)));
350 EXPECT_EQ(1u, frames_queued());
351 EXPECT_NE(base::TimeDelta(), max_acceptable_drift());
353 EXPECT_EQ(0u, frames_queued());
354 EXPECT_NE(base::TimeDelta(), max_acceptable_drift());
356 // Enqueue a frame and render enough such that the next frame should be
357 // considered ineffective.
358 time_source_.StartTicking();
359 size_t frames_dropped = 0;
360 algorithm_.EnqueueFrame(CreateFrame(tg.interval(0)));
361 algorithm_.EnqueueFrame(CreateFrame(tg.interval(1)));
362 scoped_refptr<VideoFrame> frame = RenderAndStep(&tg, &frames_dropped);
364 EXPECT_EQ(tg.interval(0), frame->timestamp());
365 EXPECT_EQ(0u, frames_dropped);
366 EXPECT_EQ(1u, EffectiveFramesQueued());
368 for (int i = 0; i < 2; ++i) {
369 frame = RenderAndStep(&tg, &frames_dropped);
371 EXPECT_EQ(tg.interval(1), frame->timestamp());
372 EXPECT_EQ(0u, frames_dropped);
373 EXPECT_EQ(0u, EffectiveFramesQueued());
375 time_source_.StopTicking();
377 // After reset the new frame should still be counted as ineffective.
379 VideoRendererAlgorithm::ResetFlag::kPreserveNextFrameEstimates);
380 algorithm_.EnqueueFrame(CreateFrame(tg.interval(2)));
381 EXPECT_EQ(0u, EffectiveFramesQueued());
382 algorithm_.EnqueueFrame(CreateFrame(tg.interval(3)));
383 ASSERT_EQ(1u, algorithm_.RemoveExpiredFrames(
384 tg.current() + algorithm_.average_frame_duration()));
387 TEST_F(VideoRendererAlgorithmTest, AccountForMissingIntervals) {
388 TickGenerator tg(tick_clock_->NowTicks(), 50);
389 time_source_.StartTicking();
391 // Disable hysteresis since AccountForMissingIntervals() only affects cadence
393 disable_cadence_hysteresis();
395 // Simulate Render() called before any frames are present.
396 EXPECT_EQ(-1, AccountForMissedIntervalsAndStep(&tg));
398 algorithm_.EnqueueFrame(CreateFrame(tg.interval(0)));
399 algorithm_.EnqueueFrame(CreateFrame(tg.interval(1)));
400 algorithm_.EnqueueFrame(CreateFrame(tg.interval(2)));
401 algorithm_.EnqueueFrame(CreateFrame(tg.interval(3)));
403 // Simulate Render() called before any frames have been rendered.
404 EXPECT_EQ(0, AccountForMissedIntervalsAndStep(&tg));
406 // Render one frame (several are in the past and will be dropped).
407 base::TimeTicks deadline_min = tg.current();
408 base::TimeTicks deadline_max = tg.step();
409 size_t frames_dropped = 0;
410 scoped_refptr<VideoFrame> frame =
411 algorithm_.Render(deadline_min, deadline_max, &frames_dropped);
413 EXPECT_EQ(tg.interval(2), frame->timestamp());
414 EXPECT_EQ(2u, frames_dropped);
416 ASSERT_EQ(1, GetCurrentFrameDisplayCount());
418 // Now calling AccountForMissingIntervals with an interval which overlaps the
419 // previous should do nothing.
420 deadline_min += tg.interval(1) / 2;
421 deadline_max += tg.interval(1) / 2;
422 EXPECT_EQ(1, AccountForMissedIntervals(deadline_min, deadline_max));
424 // Steping by 1.5 intervals, is not enough to increase the count.
425 deadline_min += tg.interval(1);
426 deadline_max += tg.interval(1);
427 EXPECT_EQ(1, AccountForMissedIntervals(deadline_min, deadline_max));
429 // Calling it after a full skipped interval should increase the count by 1 for
430 // each skipped interval.
432 EXPECT_EQ(2, AccountForMissedIntervalsAndStep(&tg));
434 // 4 because [tg.current(), tg.step()] now represents 2 additional intervals.
435 EXPECT_EQ(4, AccountForMissedIntervalsAndStep(&tg));
437 // Frame should be way over cadence and no good frames remain, so last frame
438 // should be returned.
439 frame = RenderAndStep(&tg, &frames_dropped);
441 EXPECT_EQ(tg.interval(3), frame->timestamp());
442 EXPECT_EQ(0u, frames_dropped);
443 EXPECT_EQ(1, GetCurrentFrameDisplayCount());
445 // Stop the time source and verify AccountForMissedIntervals() doesn't try to
446 // account for intervals from pause behavior.
447 time_source_.StopTicking();
448 frame = RenderAndStep(&tg, &frames_dropped);
450 EXPECT_EQ(tg.interval(3), frame->timestamp());
451 EXPECT_EQ(0u, frames_dropped);
452 EXPECT_EQ(2, GetCurrentFrameDisplayCount());
455 frame = RenderAndStep(&tg, &frames_dropped);
457 EXPECT_EQ(tg.interval(3), frame->timestamp());
458 EXPECT_EQ(0u, frames_dropped);
459 EXPECT_EQ(3, GetCurrentFrameDisplayCount());
461 time_source_.StartTicking();
463 // Now run the same test using set_time_stopped();
464 frame = RenderAndStep(&tg, &frames_dropped);
466 EXPECT_EQ(tg.interval(3), frame->timestamp());
467 EXPECT_EQ(0u, frames_dropped);
468 EXPECT_EQ(4, GetCurrentFrameDisplayCount());
470 algorithm_.set_time_stopped();
472 frame = RenderAndStep(&tg, &frames_dropped);
474 EXPECT_EQ(tg.interval(3), frame->timestamp());
475 EXPECT_EQ(0u, frames_dropped);
476 EXPECT_EQ(5, GetCurrentFrameDisplayCount());
479 TEST_F(VideoRendererAlgorithmTest, OnLastFrameDropped) {
480 TickGenerator frame_tg(base::TimeTicks(), 25);
481 TickGenerator display_tg(tick_clock_->NowTicks(), 50);
482 time_source_.StartTicking();
484 // Disable hysteresis since OnLastFrameDropped() only affects cadence based
486 disable_cadence_hysteresis();
488 algorithm_.EnqueueFrame(CreateFrame(frame_tg.interval(0)));
489 algorithm_.EnqueueFrame(CreateFrame(frame_tg.interval(1)));
490 algorithm_.EnqueueFrame(CreateFrame(frame_tg.interval(2)));
492 // Render one frame (several are in the past and will be dropped).
493 size_t frames_dropped = 0;
494 scoped_refptr<VideoFrame> frame = RenderAndStep(&display_tg, &frames_dropped);
496 EXPECT_EQ(frame_tg.interval(0), frame->timestamp());
497 EXPECT_EQ(0u, frames_dropped);
499 // The frame should have its display count decremented once it's reported as
501 ASSERT_EQ(1, GetCurrentFrameDisplayCount());
502 ASSERT_EQ(0, GetCurrentFrameDropCount());
503 algorithm_.OnLastFrameDropped();
504 ASSERT_EQ(1, GetCurrentFrameDisplayCount());
505 ASSERT_EQ(1, GetCurrentFrameDropCount());
507 // Render the frame again and then force another drop.
508 frame = RenderAndStep(&display_tg, &frames_dropped);
510 EXPECT_EQ(frame_tg.interval(0), frame->timestamp());
511 EXPECT_EQ(0u, frames_dropped);
513 ASSERT_EQ(2, GetCurrentFrameDisplayCount());
514 ASSERT_EQ(1, GetCurrentFrameDropCount());
515 algorithm_.OnLastFrameDropped();
516 ASSERT_EQ(2, GetCurrentFrameDisplayCount());
517 ASSERT_EQ(2, GetCurrentFrameDropCount());
519 // The next Render() call should now count this frame as dropped.
520 frame = RenderAndStep(&display_tg, &frames_dropped);
522 EXPECT_EQ(frame_tg.interval(1), frame->timestamp());
523 EXPECT_EQ(1u, frames_dropped);
524 ASSERT_EQ(1, GetCurrentFrameDisplayCount());
525 ASSERT_EQ(0, GetCurrentFrameDropCount());
527 // Rendering again should result in the same frame being displayed.
528 frame = RenderAndStep(&display_tg, &frames_dropped);
530 EXPECT_EQ(frame_tg.interval(1), frame->timestamp());
531 EXPECT_EQ(0u, frames_dropped);
533 // In this case, the drop count is less than the display count, so the frame
534 // should not be counted as dropped.
535 ASSERT_EQ(2, GetCurrentFrameDisplayCount());
536 ASSERT_EQ(0, GetCurrentFrameDropCount());
537 algorithm_.OnLastFrameDropped();
538 ASSERT_EQ(2, GetCurrentFrameDisplayCount());
539 ASSERT_EQ(1, GetCurrentFrameDropCount());
541 // The third frame should be rendered correctly now and the previous frame not
542 // counted as having been dropped.
543 frame = RenderAndStep(&display_tg, &frames_dropped);
545 EXPECT_EQ(frame_tg.interval(2), frame->timestamp());
546 EXPECT_EQ(0u, frames_dropped);
549 TEST_F(VideoRendererAlgorithmTest, OnLastFrameDroppedFirstFrame) {
550 TickGenerator frame_tg(base::TimeTicks(), 25);
551 TickGenerator display_tg(tick_clock_->NowTicks(), 50);
552 time_source_.StartTicking();
554 // Disable hysteresis since OnLastFrameDropped() only affects cadence based
556 disable_cadence_hysteresis();
558 // Use frames in the future to simulate cases where the first frame may be
559 // renderered many times.
560 algorithm_.EnqueueFrame(CreateFrame(frame_tg.interval(5)));
561 algorithm_.EnqueueFrame(CreateFrame(frame_tg.interval(6)));
562 algorithm_.EnqueueFrame(CreateFrame(frame_tg.interval(7)));
564 size_t frames_dropped = 0;
565 scoped_refptr<VideoFrame> frame =
566 algorithm_.Render(base::TimeTicks(), base::TimeTicks(), &frames_dropped);
568 EXPECT_EQ(frame_tg.interval(5), frame->timestamp());
569 EXPECT_EQ(0u, frames_dropped);
571 // The frame should have its drop count updated once it's reported as dropped.
572 ASSERT_EQ(1, GetCurrentFrameDisplayCount());
573 ASSERT_EQ(0, GetCurrentFrameDropCount());
574 algorithm_.OnLastFrameDropped();
575 ASSERT_EQ(1, GetCurrentFrameDisplayCount());
576 ASSERT_EQ(1, GetCurrentFrameDropCount());
578 // Render the frame and check counts at each step.
579 const int kLastValue = 2 * 5 + 2 - 1; // Cadence is 2, -1 for Render() above.
580 for (int i = 0; i < kLastValue; ++i) {
581 frame = RenderAndStep(&display_tg, &frames_dropped);
583 EXPECT_EQ(frame_tg.interval(5), frame->timestamp());
584 EXPECT_EQ(0u, frames_dropped);
586 ASSERT_EQ(i + 2, GetCurrentFrameDisplayCount());
588 ASSERT_EQ(i + 1, GetCurrentFrameDropCount());
589 algorithm_.OnLastFrameDropped();
590 ASSERT_EQ(i + 2, GetCurrentFrameDisplayCount());
591 ASSERT_EQ(i + 2, GetCurrentFrameDropCount());
595 // Ensure the next frame does not pick up the overage.
596 frame = RenderAndStep(&display_tg, &frames_dropped);
598 EXPECT_EQ(frame_tg.interval(6), frame->timestamp());
599 EXPECT_EQ(0u, frames_dropped);
601 ASSERT_EQ(1, GetCurrentFrameDisplayCount());
602 ASSERT_EQ(0, GetCurrentFrameDropCount());
603 algorithm_.OnLastFrameDropped();
604 ASSERT_EQ(1, GetCurrentFrameDisplayCount());
605 ASSERT_EQ(1, GetCurrentFrameDropCount());
607 // Stop time and verify cadence overage isn't accumulated for next frame.
608 time_source_.StopTicking();
609 for (int i = 0; i < 5; ++i) {
610 frame = RenderAndStep(&display_tg, &frames_dropped);
612 EXPECT_EQ(frame_tg.interval(6), frame->timestamp());
613 EXPECT_EQ(0u, frames_dropped);
615 ASSERT_EQ(i + 2, GetCurrentFrameDisplayCount());
616 ASSERT_EQ(1, GetCurrentFrameDropCount());
619 time_source_.StartTicking();
620 frame = RenderAndStep(&display_tg, &frames_dropped);
622 EXPECT_EQ(frame_tg.interval(7), frame->timestamp());
623 EXPECT_EQ(0u, frames_dropped);
625 ASSERT_EQ(1, GetCurrentFrameDisplayCount());
626 ASSERT_EQ(0, GetCurrentFrameDropCount());
627 algorithm_.OnLastFrameDropped();
628 ASSERT_EQ(1, GetCurrentFrameDisplayCount());
629 ASSERT_EQ(1, GetCurrentFrameDropCount());
632 TEST_F(VideoRendererAlgorithmTest, EffectiveFramesQueued) {
633 TickGenerator frame_tg(base::TimeTicks(), 50);
634 TickGenerator display_tg(tick_clock_->NowTicks(), 25);
636 // Disable hysteresis since EffectiveFramesQueued() is tested as part of the
637 // normal frame pump tests when cadence is not present.
638 disable_cadence_hysteresis();
640 EXPECT_EQ(0u, EffectiveFramesQueued());
641 time_source_.StartTicking();
643 algorithm_.EnqueueFrame(CreateFrame(frame_tg.interval(0)));
644 EXPECT_EQ(1u, EffectiveFramesQueued());
646 algorithm_.EnqueueFrame(CreateFrame(frame_tg.interval(1)));
647 EXPECT_EQ(2u, EffectiveFramesQueued());
649 algorithm_.EnqueueFrame(CreateFrame(frame_tg.interval(2)));
650 EXPECT_EQ(3u, EffectiveFramesQueued());
652 algorithm_.EnqueueFrame(CreateFrame(frame_tg.interval(3)));
653 EXPECT_EQ(4u, EffectiveFramesQueued());
654 EXPECT_EQ(4u, frames_queued());
656 // Render one frame which will detect cadence...
657 size_t frames_dropped = 0;
658 scoped_refptr<VideoFrame> frame = RenderAndStep(&display_tg, &frames_dropped);
660 EXPECT_EQ(frame_tg.interval(0), frame->timestamp());
661 EXPECT_EQ(0u, frames_dropped);
663 // Fractional cadence should be detected and the count will decrease.
664 ASSERT_TRUE(is_using_cadence());
665 EXPECT_EQ(1u, EffectiveFramesQueued());
666 EXPECT_EQ(4u, frames_queued());
668 // Dropping the last rendered frame should do nothing, since the last frame
669 // is already excluded from the count if it has a display count of 1.
670 algorithm_.OnLastFrameDropped();
671 EXPECT_EQ(1u, EffectiveFramesQueued());
674 TEST_F(VideoRendererAlgorithmTest, EffectiveFramesQueuedWithoutCadence) {
675 TickGenerator tg(tick_clock_->NowTicks(), 60);
677 EXPECT_EQ(0u, EffectiveFramesQueued());
678 time_source_.StartTicking();
680 algorithm_.EnqueueFrame(CreateFrame(tg.interval(0)));
681 EXPECT_EQ(1u, EffectiveFramesQueued());
683 algorithm_.EnqueueFrame(CreateFrame(tg.interval(1)));
684 EXPECT_EQ(2u, EffectiveFramesQueued());
686 algorithm_.EnqueueFrame(CreateFrame(tg.interval(2)));
687 EXPECT_EQ(3u, EffectiveFramesQueued());
689 algorithm_.EnqueueFrame(CreateFrame(tg.interval(3)));
690 EXPECT_EQ(4u, EffectiveFramesQueued());
691 EXPECT_EQ(4u, frames_queued());
692 EXPECT_EQ(384, algorithm_.GetMemoryUsage());
694 // Issue a render call that should drop the first two frames and mark the 3rd
697 size_t frames_dropped = 0;
698 scoped_refptr<VideoFrame> frame = RenderAndStep(&tg, &frames_dropped);
699 ASSERT_FALSE(is_using_cadence());
701 EXPECT_EQ(2u, frames_dropped);
702 EXPECT_EQ(tg.interval(2), frame->timestamp());
703 EXPECT_EQ(1u, EffectiveFramesQueued());
704 EXPECT_EQ(2u, frames_queued());
705 EXPECT_EQ(192, algorithm_.GetMemoryUsage());
707 // Rendering one more frame should return 0 effective frames queued.
708 frame = RenderAndStep(&tg, &frames_dropped);
709 ASSERT_FALSE(is_using_cadence());
711 EXPECT_EQ(0u, frames_dropped);
712 EXPECT_EQ(tg.interval(3), frame->timestamp());
713 EXPECT_EQ(0u, EffectiveFramesQueued());
714 EXPECT_EQ(1u, frames_queued());
715 EXPECT_EQ(96, algorithm_.GetMemoryUsage());
718 TEST_F(VideoRendererAlgorithmTest, EffectiveFramesQueuedWithoutFrameDropping) {
719 TickGenerator tg(tick_clock_->NowTicks(), 50);
721 algorithm_.disable_frame_dropping();
723 ASSERT_EQ(0u, EffectiveFramesQueued());
724 time_source_.StartTicking();
726 for (size_t i = 0; i < 3; ++i) {
727 algorithm_.EnqueueFrame(CreateFrame(tg.interval(i)));
728 EXPECT_EQ(i + 1, EffectiveFramesQueued());
729 EXPECT_EQ(i + 1, frames_queued());
732 // Issue a render call and verify that undropped frames remain effective.
734 size_t frames_dropped = 0;
735 scoped_refptr<VideoFrame> frame = RenderAndStep(&tg, &frames_dropped);
736 ASSERT_NE(nullptr, frame);
737 EXPECT_EQ(tg.interval(0), frame->timestamp());
738 EXPECT_EQ(0u, frames_dropped);
739 EXPECT_EQ(2u, EffectiveFramesQueued());
741 // As the next frame is consumed, the count of effective frames is
743 frame = RenderAndStep(&tg, &frames_dropped);
744 ASSERT_NE(nullptr, frame);
745 EXPECT_EQ(tg.interval(1), frame->timestamp());
746 EXPECT_EQ(0u, frames_dropped);
747 EXPECT_EQ(1u, EffectiveFramesQueued());
750 // The maximum acceptable drift should be updated once we have two frames.
751 TEST_F(VideoRendererAlgorithmTest, AcceptableDriftUpdated) {
752 TickGenerator tg(tick_clock_->NowTicks(), 50);
754 size_t frames_dropped = 0;
755 const base::TimeDelta original_drift = max_acceptable_drift();
756 algorithm_.EnqueueFrame(CreateFrame(tg.interval(0)));
757 EXPECT_EQ(1u, frames_queued());
758 EXPECT_TRUE(RenderAndStep(&tg, &frames_dropped));
759 EXPECT_EQ(original_drift, max_acceptable_drift());
761 // Time must be ticking to get wall clock times for frames.
762 time_source_.StartTicking();
764 algorithm_.EnqueueFrame(CreateFrame(tg.interval(1)));
765 EXPECT_EQ(2u, frames_queued());
766 EXPECT_TRUE(RenderAndStep(&tg, &frames_dropped));
767 EXPECT_NE(original_drift, max_acceptable_drift());
770 // Verifies behavior when time stops.
771 TEST_F(VideoRendererAlgorithmTest, TimeIsStopped) {
772 TickGenerator tg(tick_clock_->NowTicks(), 50);
774 // Prior to rendering the first frame, the algorithm should always return the
775 // first available frame.
776 size_t frames_dropped = 0;
777 algorithm_.EnqueueFrame(CreateFrame(tg.interval(0)));
778 EXPECT_EQ(1u, frames_queued());
779 scoped_refptr<VideoFrame> frame = RenderAndStep(&tg, &frames_dropped);
781 EXPECT_EQ(tg.interval(0), frame->timestamp());
782 EXPECT_EQ(0u, frames_dropped);
783 EXPECT_EQ(1u, frames_queued());
784 EXPECT_EQ(1u, EffectiveFramesQueued());
786 // The same timestamp should be returned after time starts.
787 tick_clock_->Advance(tg.interval(1));
788 time_source_.StartTicking();
789 frame = RenderAndStep(&tg, &frames_dropped);
791 EXPECT_EQ(tg.interval(0), frame->timestamp());
792 EXPECT_EQ(0u, frames_dropped);
793 EXPECT_EQ(1u, frames_queued());
794 EXPECT_EQ(1u, EffectiveFramesQueued());
796 // Ensure the next suitable frame is vended as time advances.
797 algorithm_.EnqueueFrame(CreateFrame(tg.interval(1)));
798 EXPECT_EQ(2u, frames_queued());
799 EXPECT_EQ(2u, EffectiveFramesQueued());
800 frame = RenderAndStep(&tg, &frames_dropped);
802 EXPECT_EQ(tg.interval(1), frame->timestamp());
803 EXPECT_EQ(0u, frames_dropped);
804 EXPECT_EQ(1u, frames_queued());
805 EXPECT_EQ(0u, EffectiveFramesQueued());
807 // Once time stops ticking, any further frames shouldn't be returned, even if
808 // the interval requested more closely matches.
809 algorithm_.EnqueueFrame(CreateFrame(tg.interval(2)));
810 time_source_.StopTicking();
811 frame = RenderAndStep(&tg, &frames_dropped);
813 EXPECT_EQ(tg.interval(1), frame->timestamp());
814 EXPECT_EQ(0u, frames_dropped);
815 EXPECT_EQ(2u, frames_queued());
816 EXPECT_EQ(1u, EffectiveFramesQueued());
819 // Verify frames inserted out of order end up in the right spot and are rendered
820 // according to the API contract.
821 TEST_F(VideoRendererAlgorithmTest, SortedFrameQueue) {
822 TickGenerator tg(tick_clock_->NowTicks(), 50);
824 // Ensure frames handed in out of order before time starts ticking are sorted
825 // and returned in the correct order upon Render().
826 algorithm_.EnqueueFrame(CreateFrame(tg.interval(3)));
827 algorithm_.EnqueueFrame(CreateFrame(tg.interval(2)));
828 EXPECT_EQ(2u, frames_queued());
829 EXPECT_EQ(2u, EffectiveFramesQueued());
831 time_source_.StartTicking();
833 // The first call should return the earliest frame appended.
834 size_t frames_dropped = 0;
835 scoped_refptr<VideoFrame> frame = RenderAndStep(&tg, &frames_dropped);
836 EXPECT_EQ(0u, frames_dropped);
837 EXPECT_EQ(tg.interval(2), frame->timestamp());
838 EXPECT_EQ(2u, frames_queued());
839 EXPECT_EQ(2u, EffectiveFramesQueued());
841 // Since a frame has already been rendered, queuing this frame and calling
842 // Render() should result in it being dropped; even though it's a better
843 // candidate for the desired interval. The frame is dropped during enqueue so
844 // it won't show up in frames_queued().
845 algorithm_.EnqueueFrame(CreateFrame(tg.interval(1)));
846 EXPECT_EQ(2u, frames_queued());
847 EXPECT_EQ(2u, EffectiveFramesQueued());
848 frame = RenderAndStep(&tg, &frames_dropped);
849 EXPECT_EQ(1u, frames_dropped);
850 EXPECT_EQ(tg.interval(2), frame->timestamp());
851 EXPECT_EQ(2u, frames_queued());
852 EXPECT_EQ(2u, EffectiveFramesQueued());
855 // Run through integer cadence selection for 1, 2, 3, and 4.
856 TEST_F(VideoRendererAlgorithmTest, BestFrameByCadence) {
857 const double kTestRates[][2] = {{60, 60}, {30, 60}, {25, 75}, {25, 100}};
859 for (const auto& test_rate : kTestRates) {
860 disable_cadence_hysteresis();
862 TickGenerator frame_tg(base::TimeTicks(), test_rate[0]);
863 TickGenerator display_tg(tick_clock_->NowTicks(), test_rate[1]);
865 int actual_frame_pattern = 0;
866 const int desired_frame_pattern = test_rate[1] / test_rate[0];
867 scoped_refptr<VideoFrame> current_frame;
869 true, &frame_tg, &display_tg,
870 [¤t_frame, &actual_frame_pattern, desired_frame_pattern, this](
871 scoped_refptr<VideoFrame> frame, size_t frames_dropped) {
873 ASSERT_EQ(0u, frames_dropped);
875 // Each frame should display for exactly it's desired cadence pattern.
876 if (!current_frame || current_frame == frame) {
877 actual_frame_pattern++;
879 ASSERT_EQ(actual_frame_pattern, desired_frame_pattern);
880 actual_frame_pattern = 1;
883 current_frame = frame;
884 ASSERT_TRUE(is_using_cadence());
887 if (HasFatalFailure())
892 TEST_F(VideoRendererAlgorithmTest, BestFrameByCadenceOverdisplayed) {
893 TickGenerator frame_tg(base::TimeTicks(), 25);
894 TickGenerator display_tg(tick_clock_->NowTicks(), 50);
895 time_source_.StartTicking();
896 disable_cadence_hysteresis();
898 algorithm_.EnqueueFrame(CreateFrame(frame_tg.interval(0)));
899 algorithm_.EnqueueFrame(CreateFrame(frame_tg.interval(1)));
901 // Render frames until we've exhausted available frames and the last frame is
902 // forced to be over displayed.
903 for (int i = 0; i < 5; ++i) {
904 size_t frames_dropped = 0;
905 scoped_refptr<VideoFrame> frame =
906 RenderAndStep(&display_tg, &frames_dropped);
908 EXPECT_EQ(frame_tg.interval(i < 4 ? i / 2 : 1), frame->timestamp());
909 EXPECT_EQ(0u, frames_dropped);
910 ASSERT_EQ(2, GetCurrentFrameIdealDisplayCount());
913 // Verify last frame is above cadence (2 in this case)
914 ASSERT_EQ(GetCurrentFrameIdealDisplayCount() + 1,
915 GetCurrentFrameDisplayCount());
916 algorithm_.EnqueueFrame(CreateFrame(frame_tg.interval(2)));
917 algorithm_.EnqueueFrame(CreateFrame(frame_tg.interval(3)));
919 // The next frame should still be displayed once, even though the previous
920 // one was displayed twice; the eventual drift reset will correct this (tested
921 // by BestFrameByCadenceOverdisplayedForDrift below).
922 size_t frames_dropped = 0;
923 scoped_refptr<VideoFrame> frame = RenderAndStep(&display_tg, &frames_dropped);
925 EXPECT_EQ(frame_tg.interval(2), frame->timestamp());
926 EXPECT_EQ(0u, frames_dropped);
928 // Enqueuing a new frame should keep the correct cadence values.
929 algorithm_.EnqueueFrame(CreateFrame(frame_tg.interval(4)));
931 ASSERT_EQ(1, GetCurrentFrameDisplayCount());
932 ASSERT_EQ(0, GetCurrentFrameDropCount());
933 ASSERT_EQ(2, GetCurrentFrameIdealDisplayCount());
936 TEST_F(VideoRendererAlgorithmTest, BestFrameByCadenceOverdisplayedForDrift) {
937 // Use 24.94 to ensure drift expires pretty rapidly (8.36s in this case).
938 TickGenerator frame_tg(base::TimeTicks(), 24.94);
939 TickGenerator display_tg(tick_clock_->NowTicks(), 50);
940 time_source_.StartTicking();
941 disable_cadence_hysteresis();
943 scoped_refptr<VideoFrame> last_frame;
944 bool have_overdisplayed_frame = false;
945 while (!have_overdisplayed_frame) {
946 while (EffectiveFramesQueued() < 2) {
947 algorithm_.EnqueueFrame(
948 CreateFrame(frame_tg.current() - base::TimeTicks()));
952 size_t frames_dropped = 0;
953 last_frame = RenderAndStep(&display_tg, &frames_dropped);
954 ASSERT_TRUE(last_frame);
955 ASSERT_TRUE(is_using_cadence());
956 ASSERT_EQ(0u, frames_dropped);
957 ASSERT_EQ(2, GetCurrentFrameIdealDisplayCount());
958 have_overdisplayed_frame = GetCurrentFrameDisplayCount() > 2;
961 ASSERT_TRUE(last_render_had_glitch());
963 // We've reached the point where the current frame is over displayed due to
964 // drift, the next frame should resume cadence without accounting for the
965 // overdisplayed frame.
967 size_t frames_dropped = 0;
968 scoped_refptr<VideoFrame> next_frame =
969 RenderAndStep(&display_tg, &frames_dropped);
970 ASSERT_EQ(0u, frames_dropped);
971 ASSERT_NE(last_frame, next_frame);
972 ASSERT_TRUE(is_using_cadence());
973 ASSERT_EQ(2, GetCurrentFrameIdealDisplayCount());
974 ASSERT_EQ(1, GetCurrentFrameDisplayCount());
975 last_frame = next_frame;
977 next_frame = RenderAndStep(&display_tg, &frames_dropped);
978 ASSERT_EQ(0u, frames_dropped);
979 ASSERT_EQ(last_frame, next_frame);
980 ASSERT_TRUE(is_using_cadence());
981 ASSERT_EQ(2, GetCurrentFrameIdealDisplayCount());
982 ASSERT_EQ(2, GetCurrentFrameDisplayCount());
985 TEST_F(VideoRendererAlgorithmTest, BestFrameByCoverage) {
986 TickGenerator tg(tick_clock_->NowTicks(), 50);
987 time_source_.StartTicking();
989 algorithm_.EnqueueFrame(CreateFrame(tg.interval(0)));
990 algorithm_.EnqueueFrame(CreateFrame(tg.interval(1)));
991 algorithm_.EnqueueFrame(CreateFrame(tg.interval(2)));
993 base::TimeTicks deadline_min = tg.current();
994 base::TimeTicks deadline_max = deadline_min + tg.interval(1);
996 size_t frames_dropped = 0;
997 scoped_refptr<VideoFrame> frame =
998 algorithm_.Render(deadline_min, deadline_max, &frames_dropped);
1000 EXPECT_EQ(tg.interval(0), frame->timestamp());
1001 EXPECT_EQ(0u, frames_dropped);
1003 int second_best = 0;
1005 // Coverage should be 1 for if the frame overlaps the interval entirely, no
1006 // second best should be found.
1008 FindBestFrameByCoverage(deadline_min, deadline_max, &second_best));
1009 EXPECT_EQ(-1, second_best);
1011 // 49/51 coverage for frame 0 and frame 1 should be within tolerance such that
1012 // the earlier frame should still be chosen.
1013 deadline_min = tg.current() + tg.interval(1) / 2 + base::Microseconds(250);
1014 deadline_max = deadline_min + tg.interval(1);
1016 FindBestFrameByCoverage(deadline_min, deadline_max, &second_best));
1017 EXPECT_EQ(1, second_best);
1019 // 48/52 coverage should result in the second frame being chosen.
1020 deadline_min = tg.current() + tg.interval(1) / 2 + base::Microseconds(500);
1021 deadline_max = deadline_min + tg.interval(1);
1023 FindBestFrameByCoverage(deadline_min, deadline_max, &second_best));
1024 EXPECT_EQ(0, second_best);
1026 // Overlapping three frames should choose the one with the most coverage and
1027 // the second best should be the earliest frame.
1028 deadline_min = tg.current() + tg.interval(1) / 2;
1029 deadline_max = deadline_min + tg.interval(2);
1031 FindBestFrameByCoverage(deadline_min, deadline_max, &second_best));
1032 EXPECT_EQ(0, second_best);
1034 // Requesting coverage outside of all known frames should return -1 for both
1036 deadline_min = tg.current() + tg.interval(frames_queued());
1037 deadline_max = deadline_min + tg.interval(1);
1039 FindBestFrameByCoverage(deadline_min, deadline_max, &second_best));
1040 EXPECT_EQ(-1, second_best);
1043 TEST_F(VideoRendererAlgorithmTest, BestFrameByDriftAndDriftCalculations) {
1044 TickGenerator tg(tick_clock_->NowTicks(), 50);
1045 time_source_.StartTicking();
1047 algorithm_.EnqueueFrame(CreateFrame(tg.interval(0)));
1048 algorithm_.EnqueueFrame(CreateFrame(tg.interval(1)));
1050 size_t frames_dropped = 0;
1051 scoped_refptr<VideoFrame> frame = algorithm_.Render(
1052 tg.current(), tg.current() + tg.interval(1), &frames_dropped);
1054 EXPECT_EQ(tg.interval(0), frame->timestamp());
1055 EXPECT_EQ(0u, frames_dropped);
1057 base::TimeDelta zero_drift, half_drift = tg.interval(1) / 2;
1058 base::TimeDelta detected_drift;
1060 // Frame_0 overlaps the deadline, Frame_1 is a full interval away.
1061 base::TimeTicks deadline = tg.current();
1062 EXPECT_EQ(zero_drift, CalculateAbsoluteDriftForFrame(deadline, 0));
1063 EXPECT_EQ(tg.interval(1), CalculateAbsoluteDriftForFrame(deadline, 1));
1064 EXPECT_EQ(0, FindBestFrameByDrift(deadline, &detected_drift));
1065 EXPECT_EQ(zero_drift, detected_drift);
1067 // Frame_0 overlaps the deadline, Frame_1 is a half interval away.
1068 deadline += half_drift;
1069 EXPECT_EQ(zero_drift, CalculateAbsoluteDriftForFrame(deadline, 0));
1070 EXPECT_EQ(half_drift, CalculateAbsoluteDriftForFrame(deadline, 1));
1071 EXPECT_EQ(0, FindBestFrameByDrift(deadline, &detected_drift));
1072 EXPECT_EQ(zero_drift, detected_drift);
1074 // Both frames overlap the deadline.
1075 deadline += half_drift;
1076 EXPECT_EQ(zero_drift, CalculateAbsoluteDriftForFrame(deadline, 0));
1077 EXPECT_EQ(zero_drift, CalculateAbsoluteDriftForFrame(deadline, 1));
1078 EXPECT_EQ(1, FindBestFrameByDrift(deadline, &detected_drift));
1079 EXPECT_EQ(zero_drift, detected_drift);
1081 // Frame_0 is half an interval away, Frame_1 overlaps the deadline.
1082 deadline += half_drift;
1083 EXPECT_EQ(half_drift, CalculateAbsoluteDriftForFrame(deadline, 0));
1084 EXPECT_EQ(zero_drift, CalculateAbsoluteDriftForFrame(deadline, 1));
1085 EXPECT_EQ(1, FindBestFrameByDrift(deadline, &detected_drift));
1086 EXPECT_EQ(zero_drift, detected_drift);
1088 // Frame_0 is a full interval away, Frame_1 overlaps the deadline.
1089 deadline += half_drift;
1090 EXPECT_EQ(tg.interval(1), CalculateAbsoluteDriftForFrame(deadline, 0));
1091 EXPECT_EQ(zero_drift, CalculateAbsoluteDriftForFrame(deadline, 1));
1092 EXPECT_EQ(1, FindBestFrameByDrift(deadline, &detected_drift));
1093 EXPECT_EQ(zero_drift, detected_drift);
1095 // Both frames are entirely before the deadline.
1096 deadline += half_drift;
1097 EXPECT_EQ(tg.interval(1) + half_drift,
1098 CalculateAbsoluteDriftForFrame(deadline, 0));
1099 EXPECT_EQ(half_drift, CalculateAbsoluteDriftForFrame(deadline, 1));
1100 EXPECT_EQ(1, FindBestFrameByDrift(deadline, &detected_drift));
1101 EXPECT_EQ(half_drift, detected_drift);
1104 // Run through fractional cadence selection for 1/2, 1/3, and 1/4.
1105 TEST_F(VideoRendererAlgorithmTest, BestFrameByFractionalCadence) {
1106 const double kTestRates[][2] = {{120, 60}, {72, 24}, {100, 25}};
1108 for (const auto& test_rate : kTestRates) {
1109 disable_cadence_hysteresis();
1111 TickGenerator frame_tg(base::TimeTicks(), test_rate[0]);
1112 TickGenerator display_tg(tick_clock_->NowTicks(), test_rate[1]);
1114 scoped_refptr<VideoFrame> current_frame;
1115 RunFramePumpTest(true, &frame_tg, &display_tg,
1116 [¤t_frame, this](scoped_refptr<VideoFrame> frame,
1117 size_t frames_dropped) {
1120 // We don't count frames dropped that cadence says we
1122 ASSERT_EQ(0u, frames_dropped);
1123 ASSERT_NE(current_frame, frame);
1124 ASSERT_TRUE(is_using_cadence());
1125 current_frame = frame;
1128 if (HasFatalFailure())
1133 // Verify a 3:2 frame pattern for 23.974fps and 24fps in 60Hz.
1134 TEST_F(VideoRendererAlgorithmTest, FilmCadence) {
1135 const double kTestRates[] = {NTSC(24), 24};
1136 disable_cadence_hysteresis();
1138 for (double frame_rate : kTestRates) {
1139 scoped_refptr<VideoFrame> current_frame;
1140 int actual_frame_pattern = 0, desired_frame_pattern = 3;
1142 TickGenerator frame_tg(base::TimeTicks(), frame_rate);
1143 TickGenerator display_tg(tick_clock_->NowTicks(), 60);
1146 true, &frame_tg, &display_tg,
1147 [¤t_frame, &actual_frame_pattern, &desired_frame_pattern, this](
1148 scoped_refptr<VideoFrame> frame, size_t frames_dropped) {
1150 ASSERT_EQ(0u, frames_dropped);
1152 if (!current_frame || current_frame == frame) {
1153 actual_frame_pattern++;
1155 ASSERT_EQ(actual_frame_pattern, desired_frame_pattern);
1156 actual_frame_pattern = 1;
1157 desired_frame_pattern = (desired_frame_pattern == 3 ? 2 : 3);
1160 current_frame = frame;
1161 ASSERT_TRUE(is_using_cadence());
1164 if (HasFatalFailure())
1169 // Spot check common display and frame rate pairs for correctness.
1170 TEST_F(VideoRendererAlgorithmTest, CadenceCalculations) {
1171 ASSERT_EQ("[3:2]", GetCadence(24, 60));
1172 ASSERT_EQ("[3:2]", GetCadence(NTSC(24), 60));
1173 ASSERT_EQ("[2:3:2:3:2]", GetCadence(25, 60));
1174 ASSERT_EQ("[2]", GetCadence(NTSC(30), 60));
1175 ASSERT_EQ("[2]", GetCadence(30, 60));
1176 ASSERT_EQ("[1:1:2:1:1]", GetCadence(50, 60));
1177 ASSERT_EQ("[1]", GetCadence(NTSC(60), 60));
1178 ASSERT_EQ("[1:0]", GetCadence(120, 60));
1180 // 50Hz is common in the EU.
1181 ASSERT_EQ("[]", GetCadence(NTSC(24), 50));
1182 ASSERT_EQ("[]", GetCadence(24, 50));
1183 ASSERT_EQ("[2]", GetCadence(NTSC(25), 50));
1184 ASSERT_EQ("[2]", GetCadence(25, 50));
1185 ASSERT_EQ("[2:1:2]", GetCadence(NTSC(30), 50));
1186 ASSERT_EQ("[2:1:2]", GetCadence(30, 50));
1187 ASSERT_EQ("[]", GetCadence(NTSC(60), 50));
1188 ASSERT_EQ("[]", GetCadence(60, 50));
1190 ASSERT_EQ("[2:3:2:3:2]", GetCadence(25, NTSC(60)));
1191 ASSERT_EQ("[1:0]", GetCadence(120, NTSC(60)));
1192 ASSERT_EQ("[60]", GetCadence(1, NTSC(60)));
1195 TEST_F(VideoRendererAlgorithmTest, RemoveExpiredFramesWithoutRendering) {
1196 TickGenerator tg(tick_clock_->NowTicks(), 50);
1198 // Removing expired frames before anything is enqueued should do nothing.
1199 ASSERT_EQ(0u, algorithm_.RemoveExpiredFrames(tg.current()));
1201 // First verify that frames without a duration are always effective when only
1202 // one frame is present in the queue.
1203 algorithm_.EnqueueFrame(CreateFrame(tg.interval(0)));
1204 ASSERT_EQ(0u, algorithm_.RemoveExpiredFrames(tg.current()));
1205 EXPECT_EQ(1u, EffectiveFramesQueued());
1207 ASSERT_EQ(0u, algorithm_.RemoveExpiredFrames(tg.current() + tg.interval(3)));
1208 EXPECT_EQ(1u, EffectiveFramesQueued());
1212 // Now try a frame with duration information, this frame should not be counted
1213 // as effective since we know the duration of it. It is not removed since we
1214 // only have one frame in the queue though.
1215 auto frame = CreateFrame(tg.interval(0));
1216 frame->metadata().frame_duration = tg.interval(1);
1217 algorithm_.EnqueueFrame(frame);
1218 ASSERT_EQ(0u, algorithm_.RemoveExpiredFrames(tg.current() + tg.interval(3)));
1219 EXPECT_EQ(0u, EffectiveFramesQueued());
1222 TEST_F(VideoRendererAlgorithmTest, RemoveExpiredFrames) {
1223 TickGenerator tg(tick_clock_->NowTicks(), 50);
1225 // Removing expired frames before anything is enqueued should do nothing.
1226 ASSERT_EQ(0u, algorithm_.RemoveExpiredFrames(tg.current()));
1228 algorithm_.EnqueueFrame(CreateFrame(tg.interval(0)));
1229 ASSERT_EQ(0u, algorithm_.RemoveExpiredFrames(tg.current()));
1230 EXPECT_EQ(1u, EffectiveFramesQueued());
1232 time_source_.StartTicking();
1234 size_t frames_dropped = 0;
1235 scoped_refptr<VideoFrame> frame = RenderAndStep(&tg, &frames_dropped);
1237 EXPECT_EQ(tg.interval(0), frame->timestamp());
1238 EXPECT_EQ(0u, frames_dropped);
1240 algorithm_.EnqueueFrame(CreateFrame(tg.interval(1)));
1241 algorithm_.EnqueueFrame(CreateFrame(tg.interval(2)));
1242 algorithm_.EnqueueFrame(CreateFrame(tg.interval(3)));
1243 algorithm_.EnqueueFrame(CreateFrame(tg.interval(4)));
1244 EXPECT_EQ(5u, EffectiveFramesQueued());
1247 // Two frames are removed, one displayed frame (which should not be counted as
1248 // dropped) and one undisplayed one.
1249 ASSERT_EQ(2u, algorithm_.RemoveExpiredFrames(tg.current()));
1250 // Since we just removed the last rendered frame, OnLastFrameDropped() should
1252 algorithm_.OnLastFrameDropped();
1253 frame = RenderAndStep(&tg, &frames_dropped);
1254 EXPECT_EQ(0u, frames_dropped);
1255 EXPECT_EQ(2u, frames_queued());
1256 EXPECT_EQ(1u, EffectiveFramesQueued());
1258 EXPECT_EQ(tg.interval(3), frame->timestamp());
1260 // Advance expiry enough that one frame is removed, but one remains and is
1261 // still counted as effective; the expired frame was displayed so it is not
1262 // counted as dropped.
1264 0u, algorithm_.RemoveExpiredFrames(tg.current() + tg.interval(1) * 0.9));
1265 EXPECT_EQ(1u, frames_queued());
1266 EXPECT_EQ(1u, EffectiveFramesQueued());
1268 // Advancing expiry once more should mark the frame as ineffective.
1270 ASSERT_EQ(0u, algorithm_.RemoveExpiredFrames(tg.current()));
1271 EXPECT_EQ(1u, frames_queued());
1272 EXPECT_EQ(0u, EffectiveFramesQueued());
1275 TEST_F(VideoRendererAlgorithmTest, RemoveExpiredFramesPartialReset) {
1276 TickGenerator tg(tick_clock_->NowTicks(), 50);
1278 algorithm_.EnqueueFrame(CreateFrame(tg.interval(0)));
1279 algorithm_.EnqueueFrame(CreateFrame(tg.interval(1)));
1280 ASSERT_EQ(0u, algorithm_.RemoveExpiredFrames(tg.current()));
1281 EXPECT_EQ(2u, EffectiveFramesQueued());
1283 time_source_.StartTicking();
1285 // Render such that the next enqueued frame should be counting as expired.
1286 for (int i = 0; i < 3; ++i) {
1287 size_t frames_dropped = 0;
1288 scoped_refptr<VideoFrame> frame = RenderAndStep(&tg, &frames_dropped);
1290 EXPECT_EQ(tg.interval(std::min(i, 1)), frame->timestamp());
1291 EXPECT_EQ(0u, frames_dropped);
1294 time_source_.StopTicking();
1296 VideoRendererAlgorithm::ResetFlag::kPreserveNextFrameEstimates);
1297 // Skip ahead several frames to ensure EnqueueFrame() estimates correctly.
1298 algorithm_.EnqueueFrame(CreateFrame(tg.interval(5)));
1299 EXPECT_EQ(1u, EffectiveFramesQueued());
1300 ASSERT_EQ(0u, algorithm_.RemoveExpiredFrames(tg.current()));
1301 EXPECT_EQ(1u, EffectiveFramesQueued());
1304 TEST_F(VideoRendererAlgorithmTest, RemoveExpiredFramesCadence) {
1305 TickGenerator tg(tick_clock_->NowTicks(), 50);
1306 disable_cadence_hysteresis();
1308 algorithm_.EnqueueFrame(CreateFrame(tg.interval(0)));
1309 algorithm_.EnqueueFrame(CreateFrame(tg.interval(1)));
1310 algorithm_.EnqueueFrame(CreateFrame(tg.interval(2)));
1312 ASSERT_EQ(0u, algorithm_.RemoveExpiredFrames(tg.current()));
1313 EXPECT_EQ(3u, EffectiveFramesQueued());
1315 time_source_.StartTicking();
1317 size_t frames_dropped = 0;
1318 scoped_refptr<VideoFrame> frame = RenderAndStep(&tg, &frames_dropped);
1320 EXPECT_EQ(tg.interval(0), frame->timestamp());
1321 EXPECT_EQ(0u, frames_dropped);
1322 ASSERT_TRUE(is_using_cadence());
1323 EXPECT_EQ(2u, EffectiveFramesQueued());
1325 // Advance expiry enough that some frames are removed, but one remains and is
1326 // still counted as effective. 1 undisplayed and 1 displayed frame will be
1328 ASSERT_EQ(1u, algorithm_.RemoveExpiredFrames(tg.current() + tg.interval(1) +
1329 max_acceptable_drift() * 1.25));
1330 EXPECT_EQ(1u, frames_queued());
1331 EXPECT_EQ(1u, EffectiveFramesQueued());
1333 // Advancing expiry once more should mark the frame as ineffective.
1335 ASSERT_EQ(0u, algorithm_.RemoveExpiredFrames(tg.current()));
1336 EXPECT_EQ(1u, frames_queued());
1337 EXPECT_EQ(0u, EffectiveFramesQueued());
1340 TEST_F(VideoRendererAlgorithmTest, RemoveExpiredFramesFractionalCadence) {
1341 TickGenerator frame_tg(base::TimeTicks(), 60);
1342 TickGenerator display_tg(tick_clock_->NowTicks(), 30);
1343 disable_cadence_hysteresis();
1345 constexpr size_t kFrameCount = 5;
1346 for (size_t i = 0; i < kFrameCount; ++i)
1347 algorithm_.EnqueueFrame(CreateFrame(frame_tg.interval(i)));
1349 ASSERT_EQ(0u, algorithm_.RemoveExpiredFrames(display_tg.current()));
1350 EXPECT_EQ(kFrameCount, EffectiveFramesQueued());
1352 time_source_.StartTicking();
1354 size_t frames_dropped = 0;
1355 scoped_refptr<VideoFrame> frame = RenderAndStep(&display_tg, &frames_dropped);
1357 EXPECT_EQ(frame_tg.interval(0), frame->timestamp());
1358 EXPECT_EQ(0u, frames_dropped);
1359 ASSERT_TRUE(is_using_cadence());
1360 EXPECT_EQ((kFrameCount - 1) / 2, EffectiveFramesQueued());
1361 EXPECT_EQ(kFrameCount, frames_queued());
1363 // Advance expiry enough that some frames are removed, but one remains and is
1364 // still counted as effective. 1 undisplayed and 1 displayed frame will be
1366 ASSERT_EQ(1u, algorithm_.RemoveExpiredFrames(display_tg.current() +
1367 display_tg.interval(1) +
1368 max_acceptable_drift() * 1.25));
1369 EXPECT_EQ(1u, frames_queued());
1370 EXPECT_EQ(1u, EffectiveFramesQueued());
1372 // Advancing expiry once more should mark the frame as ineffective.
1374 ASSERT_EQ(0u, algorithm_.RemoveExpiredFrames(display_tg.current()));
1375 EXPECT_EQ(1u, frames_queued());
1376 EXPECT_EQ(0u, EffectiveFramesQueued());
1379 class VideoRendererAlgorithmCadenceTest
1380 : public VideoRendererAlgorithmTest,
1381 public ::testing::WithParamInterface<::testing::tuple<double, double>> {};
1383 TEST_P(VideoRendererAlgorithmCadenceTest, CadenceTest) {
1384 double display_rate = std::get<0>(GetParam());
1385 double frame_rate = std::get<1>(GetParam());
1387 TickGenerator frame_tg(base::TimeTicks(), frame_rate);
1388 TickGenerator display_tg(tick_clock_->NowTicks(), display_rate);
1390 true, &frame_tg, &display_tg,
1391 [](scoped_refptr<VideoFrame> frame, size_t frames_dropped) {});
1394 // Common display rates.
1395 const double kDisplayRates[] = {
1396 NTSC(24), 24, NTSC(25), 25, NTSC(30), 30, 48,
1397 NTSC(50), 50, NTSC(60), 60, 75, 120, 144,
1400 // List of common frame rate values. Values pulled from local test media,
1401 // videostack test matrix, and Wikipedia.
1402 const double kTestRates[] = {
1403 1, 10, 12.5, 15, NTSC(24), 24, NTSC(25), 25,
1404 NTSC(30), 30, 30.12, 48, NTSC(50), 50, 58.74, NTSC(60),
1405 60, 72, 90, 100, 120, 144, 240, 300,
1408 INSTANTIATE_TEST_SUITE_P(All,
1409 VideoRendererAlgorithmCadenceTest,
1410 ::testing::Combine(::testing::ValuesIn(kDisplayRates),
1411 ::testing::ValuesIn(kTestRates)));
1413 // Rotate through various playback rates and ensure algorithm adapts correctly.
1414 TEST_F(VideoRendererAlgorithmTest, VariablePlaybackRateCadence) {
1415 TickGenerator frame_tg(base::TimeTicks(), NTSC(30));
1416 TickGenerator display_tg(tick_clock_->NowTicks(), 60);
1418 const double kPlaybackRates[] = {1.0, 2, 0.215, 0.5, 1.0, 3.15};
1419 const bool kTestRateHasCadence[std::size(kPlaybackRates)] = {
1420 true, true, true, true, true, false};
1422 for (size_t i = 0; i < std::size(kPlaybackRates); ++i) {
1423 const double playback_rate = kPlaybackRates[i];
1424 SCOPED_TRACE(base::StringPrintf("Playback Rate: %.03f", playback_rate));
1425 time_source_.SetPlaybackRate(playback_rate);
1427 false, &frame_tg, &display_tg,
1428 [](scoped_refptr<VideoFrame> frame, size_t frames_dropped) {});
1429 if (HasFatalFailure())
1432 ASSERT_EQ(kTestRateHasCadence[i], is_using_cadence());
1435 // TODO(dalecurtis): Is there more we can test here?
1438 // Ensures media which only expresses timestamps in milliseconds, gets the right
1439 // cadence detection.
1440 TEST_F(VideoRendererAlgorithmTest, UglyTimestampsHaveCadence) {
1441 TickGenerator display_tg(tick_clock_->NowTicks(), 60);
1442 time_source_.StartTicking();
1444 // 59.94fps, timestamp deltas from https://youtu.be/byoLvAo9qjs
1445 const int kBadTimestampsMs[] = {
1446 17, 16, 17, 17, 16, 17, 17, 16, 17, 17, 17, 16, 17, 17, 16, 17, 17, 16,
1447 17, 17, 16, 17, 17, 16, 17, 17, 16, 17, 17, 17, 16, 17, 17, 16, 17, 17,
1448 16, 17, 17, 16, 17, 17, 16, 17, 17, 16, 17, 17, 16, 17, 17, 17};
1450 // Run throught ~1.6 seconds worth of frames.
1451 bool cadence_detected = false;
1452 base::TimeDelta timestamp;
1453 for (size_t i = 0; i < std::size(kBadTimestampsMs) * 2; ++i) {
1454 while (EffectiveFramesQueued() < 3) {
1455 algorithm_.EnqueueFrame(CreateFrame(timestamp));
1457 base::Milliseconds(kBadTimestampsMs[i % std::size(kBadTimestampsMs)]);
1460 size_t frames_dropped = 0;
1461 RenderAndStep(&display_tg, &frames_dropped);
1462 ASSERT_EQ(0u, frames_dropped);
1464 // Cadence won't be detected immediately on this clip, but it will after
1465 // enough frames are encountered; after which it should not drop out of
1467 if (is_using_cadence())
1468 cadence_detected = true;
1470 if (cadence_detected)
1471 ASSERT_TRUE(is_using_cadence());
1475 // Ensures media with variable frame rate should not be applied with Cadence.
1476 TEST_F(VideoRendererAlgorithmTest, VariableFrameRateNoCadence) {
1477 TickGenerator display_tg(tick_clock_->NowTicks(), 60);
1478 time_source_.StartTicking();
1480 const int kBadTimestampsMs[] = {200, 200, 200, 200, 200, 1000,
1481 1000, 1000, 1000, 200, 200, 200,
1482 200, 200, 1000, 1000, 1000, 1000};
1484 // Run throught ~10 seconds worth of frames.
1485 bool cadence_detected = false;
1486 bool cadence_turned_off = false;
1487 base::TimeDelta timestamp;
1488 for (size_t i = 0; i < std::size(kBadTimestampsMs);) {
1489 while (EffectiveFramesQueued() < 3) {
1490 algorithm_.EnqueueFrame(CreateFrame(timestamp));
1492 base::Milliseconds(kBadTimestampsMs[i % std::size(kBadTimestampsMs)]);
1496 size_t frames_dropped = 0;
1497 RenderAndStep(&display_tg, &frames_dropped);
1498 ASSERT_EQ(0u, frames_dropped);
1500 // Cadence would be detected during the first second, and then
1501 // it should be off due to variable FPS detection, and then for this
1502 // sample, it should never be on.
1503 if (is_using_cadence())
1504 cadence_detected = true;
1506 if (cadence_detected) {
1507 if (!is_using_cadence())
1508 cadence_turned_off = true;
1511 if (cadence_turned_off) {
1512 ASSERT_FALSE(is_using_cadence());
1516 // Make sure Cadence is turned off somewhen, not always on.
1517 ASSERT_TRUE(cadence_turned_off);
1520 TEST_F(VideoRendererAlgorithmTest, EnqueueFrames) {
1521 TickGenerator tg(base::TimeTicks(), 50);
1522 time_source_.StartTicking();
1524 EXPECT_EQ(0u, frames_queued());
1525 scoped_refptr<VideoFrame> frame_1 = CreateFrame(tg.interval(0));
1526 algorithm_.EnqueueFrame(frame_1);
1527 EXPECT_EQ(1u, frames_queued());
1529 // Enqueuing a frame with the same timestamp should always be dropped.
1530 scoped_refptr<VideoFrame> frame_2 = CreateFrame(tg.interval(0));
1531 algorithm_.EnqueueFrame(frame_2);
1532 EXPECT_EQ(1u, frames_queued());
1534 size_t frames_dropped = 0;
1535 scoped_refptr<VideoFrame> rendered_frame =
1536 RenderAndStep(&tg, &frames_dropped);
1537 EXPECT_EQ(1u, frames_queued());
1538 EXPECT_EQ(frame_1, rendered_frame);
1539 EXPECT_EQ(1, GetCurrentFrameDisplayCount());
1541 // The replaced frame should count as dropped.
1542 EXPECT_EQ(1u, frames_dropped);
1544 // Trying to replace frame_1 with frame_2 should do nothing.
1545 algorithm_.EnqueueFrame(frame_2);
1546 EXPECT_EQ(1u, frames_queued());
1548 rendered_frame = RenderAndStep(&tg, &frames_dropped);
1549 EXPECT_EQ(1u, frames_queued());
1550 EXPECT_EQ(frame_1, rendered_frame);
1551 EXPECT_EQ(1u, frames_dropped);
1552 EXPECT_EQ(2, GetCurrentFrameDisplayCount());
1554 // Trying to add a frame < 1 ms after the last frame should drop the frame.
1555 algorithm_.EnqueueFrame(CreateFrame(base::Microseconds(999)));
1556 rendered_frame = RenderAndStep(&tg, &frames_dropped);
1557 EXPECT_EQ(1u, frames_queued());
1558 EXPECT_EQ(frame_1, rendered_frame);
1559 EXPECT_EQ(1u, frames_dropped);
1560 EXPECT_EQ(3, GetCurrentFrameDisplayCount());
1562 scoped_refptr<VideoFrame> frame_3 = CreateFrame(tg.interval(1));
1563 algorithm_.EnqueueFrame(frame_3);
1564 EXPECT_EQ(2u, frames_queued());
1566 // Trying to add a frame < 1 ms before the last frame should drop the frame.
1567 algorithm_.EnqueueFrame(
1568 CreateFrame(tg.interval(1) - base::Microseconds(999)));
1569 rendered_frame = RenderAndStep(&tg, &frames_dropped);
1570 EXPECT_EQ(1u, frames_queued());
1571 EXPECT_EQ(frame_3, rendered_frame);
1572 EXPECT_EQ(1u, frames_dropped);
1573 EXPECT_EQ(1, GetCurrentFrameDisplayCount());
1576 TEST_F(VideoRendererAlgorithmTest, CadenceForFutureFrames) {
1577 TickGenerator tg(base::TimeTicks(), 50);
1578 time_source_.StartTicking();
1580 disable_cadence_hysteresis();
1582 algorithm_.EnqueueFrame(CreateFrame(tg.interval(10)));
1583 algorithm_.EnqueueFrame(CreateFrame(tg.interval(11)));
1584 algorithm_.EnqueueFrame(CreateFrame(tg.interval(12)));
1585 EXPECT_EQ(3u, frames_queued());
1587 // Call Render() a few times to increment the render count.
1588 for (int i = 0; i < 10; ++i) {
1589 size_t frames_dropped = 0;
1590 scoped_refptr<VideoFrame> rendered_frame =
1591 RenderAndStep(&tg, &frames_dropped);
1592 EXPECT_EQ(3u, frames_queued());
1593 EXPECT_EQ(tg.interval(10), rendered_frame->timestamp());
1594 ASSERT_TRUE(is_using_cadence());
1597 // Add some noise to the tick generator so it our first frame
1598 // doesn't line up evenly on a deadline.
1599 tg.Reset(tg.current() + base::Milliseconds(5));
1601 // We're now at the first frame, cadence should be one, so
1602 // it should only be displayed once.
1603 size_t frames_dropped = 0;
1604 scoped_refptr<VideoFrame> rendered_frame =
1605 RenderAndStep(&tg, &frames_dropped);
1606 EXPECT_EQ(3u, frames_queued());
1607 EXPECT_EQ(tg.interval(10), rendered_frame->timestamp());
1608 ASSERT_TRUE(is_using_cadence());
1610 // Then the next frame should be displayed.
1611 rendered_frame = RenderAndStep(&tg, &frames_dropped);
1612 EXPECT_EQ(2u, frames_queued());
1613 EXPECT_EQ(tg.interval(11), rendered_frame->timestamp());
1614 ASSERT_TRUE(is_using_cadence());
1616 // Finally the last frame.
1617 rendered_frame = RenderAndStep(&tg, &frames_dropped);
1618 EXPECT_EQ(1u, frames_queued());
1619 EXPECT_EQ(tg.interval(12), rendered_frame->timestamp());
1620 ASSERT_TRUE(is_using_cadence());
1623 TEST_F(VideoRendererAlgorithmTest, InfiniteDurationMetadata) {
1624 TickGenerator tg(tick_clock_->NowTicks(), 50);
1626 auto frame = CreateFrame(kInfiniteDuration);
1627 frame->metadata().frame_duration = tg.interval(1);
1628 algorithm_.EnqueueFrame(frame);
1630 // This should not crash or fail.
1631 size_t frames_dropped = 0;
1632 frame = RenderAndStep(&tg, &frames_dropped);
1633 EXPECT_TRUE(algorithm_.average_frame_duration().is_zero());
1636 TEST_F(VideoRendererAlgorithmTest, UsesFrameDuration) {
1637 TickGenerator tg(tick_clock_->NowTicks(), 50);
1639 auto frame = CreateFrame(tg.interval(0));
1640 frame->metadata().frame_duration = tg.interval(1);
1641 algorithm_.EnqueueFrame(frame);
1643 // This should not crash or fail.
1644 size_t frames_dropped = 0;
1645 frame = RenderAndStep(&tg, &frames_dropped);
1646 EXPECT_EQ(tg.interval(1), algorithm_.average_frame_duration());
1648 // Add a bunch of normal frames and then one with a 3s duration.
1649 constexpr base::TimeDelta kLongDuration = base::Seconds(3);
1650 for (int i = 1; i < 4; ++i) {
1651 frame = CreateFrame(tg.interval(i));
1652 frame->metadata().frame_duration = i == 3 ? kLongDuration : tg.interval(1);
1653 algorithm_.EnqueueFrame(frame);
1656 frame = RenderAndStep(&tg, &frames_dropped);
1657 EXPECT_EQ(tg.interval(1), algorithm_.average_frame_duration());
1658 EXPECT_EQ(algorithm_.last_frame_end_time(),
1659 base::TimeTicks() + kLongDuration + tg.interval(1) * 3);
1662 // Check that VideoRendererAlgorithm correctly sets WALLCLOCK_FRAME_DURATION
1664 TEST_F(VideoRendererAlgorithmTest, WallClockDurationMetadataSet) {
1665 int playback_rate = 4;
1666 int frame_count = 10;
1667 TickGenerator tg(tick_clock_->NowTicks(), 25);
1669 time_source_.SetPlaybackRate(playback_rate);
1670 auto intended_duration = tg.interval(1) / playback_rate;
1672 for (int i = 0; i < frame_count; i++) {
1673 auto frame = CreateFrame(tg.interval(i));
1674 frame->metadata().frame_duration = tg.interval(1);
1675 algorithm_.EnqueueFrame(frame);
1678 for (int i = 0; i < frame_count; i++) {
1679 size_t frames_dropped = 0;
1680 auto frame = RenderAndStep(&tg, &frames_dropped);
1682 SCOPED_TRACE(base::StringPrintf("Frame #%d", i));
1684 EXPECT_EQ(*frame->metadata().wallclock_frame_duration, intended_duration);
1685 EXPECT_EQ(algorithm_.average_frame_duration(), intended_duration);
1689 } // namespace media