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

Side by Side Diff: media/filters/video_renderer_algorithm_unittest.cc

Issue 1021943002: Introduce cadence based VideoRendererAlgorithm. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Fix evaluation order. 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 | « media/filters/video_renderer_algorithm.cc ('k') | media/media.gyp » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 // Copyright 2015 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include <cmath>
6
7 #include "base/bind.h"
8 #include "base/bind_helpers.h"
9 #include "base/strings/stringprintf.h"
10 #include "base/test/simple_test_tick_clock.h"
11 #include "media/base/video_frame_pool.h"
12 #include "media/base/wall_clock_time_source.h"
13 #include "media/filters/video_renderer_algorithm.h"
14 #include "testing/gtest/include/gtest/gtest.h"
15
16 namespace media {
17
18 // Slows down the given |fps| according to NTSC field reduction standards; see
19 // http://en.wikipedia.org/wiki/Frame_rate#Digital_video_and_television
20 static double NTSC(double fps) {
21 return fps / 1.001;
22 }
23
24 // Helper class for generating TimeTicks in a sequence according to a frequency.
25 class TickGenerator {
26 public:
27 TickGenerator(base::TimeTicks base_timestamp, double hertz)
28 : tick_count_(0),
29 hertz_(hertz),
30 microseconds_per_tick_(base::Time::kMicrosecondsPerSecond / hertz),
31 base_time_(base_timestamp) {}
32
33 base::TimeDelta interval(int tick_count) const {
34 return base::TimeDelta::FromMicroseconds(tick_count *
35 microseconds_per_tick_);
36 }
37
38 base::TimeTicks current() const { return base_time_ + interval(tick_count_); }
39 base::TimeTicks step() { return step(1); }
40 base::TimeTicks step(int n) {
41 tick_count_ += n;
42 return current();
43 }
44
45 double hertz() const { return hertz_; }
46
47 void Reset(base::TimeTicks base_timestamp) {
48 base_time_ = base_timestamp;
49 tick_count_ = 0;
50 }
51
52 private:
53 // Track a tick count and seconds per tick value to ensure we don't drift too
54 // far due to accumulated errors during testing.
55 int64_t tick_count_;
56 const double hertz_;
57 const double microseconds_per_tick_;
58 base::TimeTicks base_time_;
59
60 DISALLOW_COPY_AND_ASSIGN(TickGenerator);
61 };
62
63 class VideoRendererAlgorithmTest : public testing::Test {
64 public:
65 VideoRendererAlgorithmTest()
66 : tick_clock_(new base::SimpleTestTickClock()),
67 algorithm_(base::Bind(&WallClockTimeSource::GetWallClockTime,
68 base::Unretained(&time_source_))) {
69 // Always start the TickClock at a non-zero value since null values have
70 // special connotations.
71 tick_clock_->Advance(base::TimeDelta::FromMicroseconds(10000));
72 time_source_.SetTickClockForTesting(
73 scoped_ptr<base::TickClock>(tick_clock_));
74 }
75 ~VideoRendererAlgorithmTest() override {}
76
77 scoped_refptr<VideoFrame> CreateFrame(base::TimeDelta timestamp) {
78 const gfx::Size natural_size(8, 8);
79 return frame_pool_.CreateFrame(VideoFrame::YV12, natural_size,
80 gfx::Rect(natural_size), natural_size,
81 timestamp);
82 }
83
84 base::TimeDelta minimum_glitch_time() const {
85 return base::TimeDelta::FromSeconds(
86 VideoRendererAlgorithm::kMinimumAcceptableTimeBetweenGlitchesSecs);
87 }
88
89 base::TimeDelta max_acceptable_drift() const {
90 return algorithm_.max_acceptable_drift_;
91 }
92
93 void disable_cadence_hysteresis() {
94 algorithm_.cadence_estimator_.set_cadence_hysteresis_threshold_for_testing(
95 base::TimeDelta());
96 }
97
98 bool last_render_had_glitch() const {
99 return algorithm_.last_render_had_glitch_;
100 }
101
102 bool is_using_cadence() const {
103 return algorithm_.cadence_estimator_.has_cadence();
104 }
105
106 bool IsUsingFractionalCadence() const {
107 return is_using_cadence() &&
108 !algorithm_.cadence_estimator_.GetCadenceForFrame(1);
109 }
110
111 size_t frames_queued() const { return algorithm_.frame_queue_.size(); }
112
113 int GetCadence(double frame_rate, double display_rate) {
114 TickGenerator display_tg(tick_clock_->NowTicks(), display_rate);
115 TickGenerator frame_tg(base::TimeTicks(), frame_rate);
116 time_source_.StartTicking();
117
118 // Enqueue enough frames for cadence detection.
119 size_t frames_dropped = 0;
120 disable_cadence_hysteresis();
121 algorithm_.EnqueueFrame(CreateFrame(frame_tg.interval(0)));
122 algorithm_.EnqueueFrame(CreateFrame(frame_tg.interval(1)));
123 EXPECT_TRUE(RenderAndStep(&display_tg, &frames_dropped));
124
125 // Store cadence before reseting the algorithm.
126 const int cadence = algorithm_.cadence_estimator_.get_cadence_for_testing();
127 time_source_.StopTicking();
128 algorithm_.Reset();
129 return cadence;
130 }
131
132 base::TimeDelta CalculateAbsoluteDriftForFrame(base::TimeTicks deadline_min,
133 int frame_index) {
134 return algorithm_.CalculateAbsoluteDriftForFrame(deadline_min, frame_index);
135 }
136
137 bool DriftOfLastRenderWasWithinTolerance(base::TimeTicks deadline_min) {
138 return CalculateAbsoluteDriftForFrame(deadline_min, 0) <=
139 algorithm_.max_acceptable_drift_;
140 }
141
142 scoped_refptr<VideoFrame> RenderAndStep(TickGenerator* tg,
143 size_t* frames_dropped) {
144 const base::TimeTicks start = tg->current();
145 const base::TimeTicks end = tg->step();
146 return algorithm_.Render(start, end, frames_dropped);
147 }
148
149 // Allows tests to run a Render() loop with sufficient frames for the various
150 // rendering modes. Upon each Render() |render_test_func| will be called with
151 // the rendered frame and the number of frames dropped.
152 template <typename OnRenderCallback>
153 void RunFramePumpTest(bool reset,
154 TickGenerator* frame_tg,
155 TickGenerator* display_tg,
156 OnRenderCallback render_test_func) {
157 SCOPED_TRACE(base::StringPrintf("Rendering %.03f fps into %0.03f",
158 frame_tg->hertz(), display_tg->hertz()));
159 tick_clock_->Advance(display_tg->current() - tick_clock_->NowTicks());
160 time_source_.StartTicking();
161
162 const bool fresh_algorithm = !algorithm_.have_rendered_frames_;
163
164 base::TimeDelta last_frame_timestamp = kNoTimestamp();
165 bool should_use_cadence = false;
166 int glitch_count = 0;
167 const base::TimeTicks start_time = tick_clock_->NowTicks();
168 while (tick_clock_->NowTicks() - start_time < minimum_glitch_time()) {
169 while (algorithm_.EffectiveFramesQueued() < 3 ||
170 frame_tg->current() - time_source_.CurrentMediaTime() <
171 base::TimeTicks()) {
172 algorithm_.EnqueueFrame(
173 CreateFrame(frame_tg->current() - base::TimeTicks()));
174 frame_tg->step();
175 }
176
177 size_t frames_dropped = 0;
178 const base::TimeTicks deadline_min = display_tg->current();
179 const base::TimeTicks deadline_max = display_tg->step();
180 scoped_refptr<VideoFrame> frame =
181 algorithm_.Render(deadline_min, deadline_max, &frames_dropped);
182
183 render_test_func(frame, frames_dropped);
184 tick_clock_->Advance(display_tg->current() - tick_clock_->NowTicks());
185
186 if (HasFatalFailure())
187 return;
188
189 // Render() should always return a frame within drift tolerances.
190 ASSERT_TRUE(DriftOfLastRenderWasWithinTolerance(deadline_min));
191
192 // If we have a frame, the timestamps should always be monotonically
193 // increasing.
194 if (frame) {
195 if (last_frame_timestamp != kNoTimestamp())
196 ASSERT_LE(last_frame_timestamp, frame->timestamp());
197 else
198 last_frame_timestamp = frame->timestamp();
199 }
200
201 // Only verify certain properties for fresh instances.
202 if (fresh_algorithm) {
203 ASSERT_NEAR(frame_tg->interval(1).InMicroseconds(),
204 algorithm_.average_frame_duration().InMicroseconds(), 1);
205
206 if (is_using_cadence() && last_render_had_glitch())
207 ++glitch_count;
208
209 // Once cadence starts, it should never stop for the current set of
210 // tests.
211 if (is_using_cadence())
212 should_use_cadence = true;
213 ASSERT_EQ(is_using_cadence(), should_use_cadence);
214 }
215
216 // When there are no frames, we're not using cadence based selection, or a
217 // frame is under cadence the two queue size reports should be equal to
218 // the number of usable frames; i.e. those frames whose end time was not
219 // within the last render interval.
220 if (!is_using_cadence() || !frames_queued() ||
221 GetCurrentFrameDisplayCount() < GetCurrentFrameIdealDisplayCount()) {
222 ASSERT_EQ(GetUsableFrameCount(deadline_max),
223 algorithm_.EffectiveFramesQueued());
224 } else if (is_using_cadence() && !IsUsingFractionalCadence()) {
225 // If there was no glitch in the last render, the two queue sizes should
226 // be off by exactly one frame; i.e., the current frame doesn't count.
227 if (!last_render_had_glitch())
228 ASSERT_EQ(frames_queued() - 1, algorithm_.EffectiveFramesQueued());
229 } else if (IsUsingFractionalCadence()) {
230 // The frame estimate should be off by at most one frame.
231 const size_t estimated_frames_queued =
232 frames_queued() /
233 algorithm_.cadence_estimator_.get_cadence_for_testing();
234 ASSERT_NEAR(algorithm_.EffectiveFramesQueued(), estimated_frames_queued,
235 1);
236 }
237 }
238
239 // When using cadence, the glitch count should be at most one for when
240 // rendering for the less than minimum_glitch_time().
241 if (fresh_algorithm && is_using_cadence())
242 ASSERT_LE(glitch_count, 1);
243
244 time_source_.StopTicking();
245 if (reset) {
246 algorithm_.Reset();
247 time_source_.SetMediaTime(base::TimeDelta());
248 }
249 }
250
251 int FindBestFrameByCoverage(base::TimeTicks deadline_min,
252 base::TimeTicks deadline_max,
253 int* second_best) {
254 return algorithm_.FindBestFrameByCoverage(deadline_min, deadline_max,
255 second_best);
256 }
257
258 int FindBestFrameByDrift(base::TimeTicks deadline_min,
259 base::TimeDelta* selected_frame_drift) {
260 return algorithm_.FindBestFrameByDrift(deadline_min, selected_frame_drift);
261 }
262
263 int GetCurrentFrameDropCount() const {
264 DCHECK_GT(frames_queued(), 0u);
265 return algorithm_.frame_queue_[algorithm_.last_frame_index_].drop_count;
266 }
267
268 int GetCurrentFrameDisplayCount() const {
269 DCHECK_GT(frames_queued(), 0u);
270 return algorithm_.frame_queue_[algorithm_.last_frame_index_].render_count;
271 }
272
273 int GetCurrentFrameIdealDisplayCount() const {
274 DCHECK_GT(frames_queued(), 0u);
275 return algorithm_.frame_queue_[algorithm_.last_frame_index_]
276 .ideal_render_count;
277 }
278
279 int AccountForMissedIntervalsAndStep(TickGenerator* tg) {
280 const base::TimeTicks start = tg->current();
281 const base::TimeTicks end = tg->step();
282 return AccountForMissedIntervals(start, end);
283 }
284
285 int AccountForMissedIntervals(base::TimeTicks deadline_min,
286 base::TimeTicks deadline_max) {
287 algorithm_.AccountForMissedIntervals(deadline_min, deadline_max);
288 return frames_queued() ? GetCurrentFrameDisplayCount() : -1;
289 }
290
291 size_t GetUsableFrameCount(base::TimeTicks deadline_max) {
292 if (is_using_cadence())
293 return frames_queued();
294
295 for (size_t i = 0; i < frames_queued(); ++i)
296 if (algorithm_.EndTimeForFrame(i) > deadline_max)
297 return frames_queued() - i;
298 return 0;
299 }
300
301 protected:
302 VideoFramePool frame_pool_;
303 WallClockTimeSource time_source_;
304 base::SimpleTestTickClock* tick_clock_; // Owned by |time_source_|.
305 VideoRendererAlgorithm algorithm_;
306
307 private:
308 DISALLOW_COPY_AND_ASSIGN(VideoRendererAlgorithmTest);
309 };
310
311 TEST_F(VideoRendererAlgorithmTest, Empty) {
312 TickGenerator tg(tick_clock_->NowTicks(), 50);
313 size_t frames_dropped = 0;
314 EXPECT_EQ(0u, frames_queued());
315 EXPECT_FALSE(RenderAndStep(&tg, &frames_dropped));
316 EXPECT_EQ(0u, frames_dropped);
317 EXPECT_EQ(0u, frames_queued());
318 EXPECT_NE(base::TimeDelta(), max_acceptable_drift());
319 }
320
321 TEST_F(VideoRendererAlgorithmTest, Reset) {
322 TickGenerator tg(tick_clock_->NowTicks(), 50);
323 algorithm_.EnqueueFrame(CreateFrame(tg.interval(0)));
324 EXPECT_EQ(1u, frames_queued());
325 EXPECT_NE(base::TimeDelta(), max_acceptable_drift());
326 algorithm_.Reset();
327 EXPECT_EQ(0u, frames_queued());
328 EXPECT_NE(base::TimeDelta(), max_acceptable_drift());
329 }
330
331 TEST_F(VideoRendererAlgorithmTest, AccountForMissingIntervals) {
332 TickGenerator tg(tick_clock_->NowTicks(), 50);
333 time_source_.StartTicking();
334
335 // Disable hysteresis since AccountForMissingIntervals() only affects cadence
336 // based rendering.
337 disable_cadence_hysteresis();
338
339 // Simulate Render() called before any frames are present.
340 EXPECT_EQ(-1, AccountForMissedIntervalsAndStep(&tg));
341
342 algorithm_.EnqueueFrame(CreateFrame(tg.interval(0)));
343 algorithm_.EnqueueFrame(CreateFrame(tg.interval(1)));
344 algorithm_.EnqueueFrame(CreateFrame(tg.interval(2)));
345 algorithm_.EnqueueFrame(CreateFrame(tg.interval(3)));
346
347 // Simulate Render() called before any frames have been rendered.
348 EXPECT_EQ(0, AccountForMissedIntervalsAndStep(&tg));
349
350 // Render one frame (several are in the past and will be dropped).
351 base::TimeTicks deadline_min = tg.current();
352 base::TimeTicks deadline_max = tg.step();
353 size_t frames_dropped = 0;
354 scoped_refptr<VideoFrame> frame =
355 algorithm_.Render(deadline_min, deadline_max, &frames_dropped);
356 ASSERT_TRUE(frame);
357 EXPECT_EQ(tg.interval(2), frame->timestamp());
358 EXPECT_EQ(2u, frames_dropped);
359
360 ASSERT_EQ(1, GetCurrentFrameDisplayCount());
361
362 // Now calling AccountForMissingIntervals with an interval which overlaps the
363 // previous should do nothing.
364 deadline_min += tg.interval(1) / 2;
365 deadline_max += tg.interval(1) / 2;
366 EXPECT_EQ(1, AccountForMissedIntervals(deadline_min, deadline_max));
367
368 // Steping by 1.5 intervals, is not enough to increase the count.
369 deadline_min += tg.interval(1);
370 deadline_max += tg.interval(1);
371 EXPECT_EQ(1, AccountForMissedIntervals(deadline_min, deadline_max));
372
373 // Calling it after a full skipped interval should increase the count by 1 for
374 // each skipped interval.
375 tg.step();
376 EXPECT_EQ(2, AccountForMissedIntervalsAndStep(&tg));
377
378 // 4 because [tg.current(), tg.step()] now represents 2 additional intervals.
379 EXPECT_EQ(4, AccountForMissedIntervalsAndStep(&tg));
380
381 // Frame should be way over cadence and no good frames remain, so last frame
382 // should be returned.
383 frame = RenderAndStep(&tg, &frames_dropped);
384 ASSERT_TRUE(frame);
385 EXPECT_EQ(tg.interval(3), frame->timestamp());
386 EXPECT_EQ(0u, frames_dropped);
387 }
388
389 TEST_F(VideoRendererAlgorithmTest, OnLastFrameDropped) {
390 TickGenerator frame_tg(base::TimeTicks(), 25);
391 TickGenerator display_tg(tick_clock_->NowTicks(), 50);
392 time_source_.StartTicking();
393
394 // Disable hysteresis since OnLastFrameDropped() only affects cadence based
395 // rendering.
396 disable_cadence_hysteresis();
397
398 algorithm_.EnqueueFrame(CreateFrame(frame_tg.interval(0)));
399 algorithm_.EnqueueFrame(CreateFrame(frame_tg.interval(1)));
400 algorithm_.EnqueueFrame(CreateFrame(frame_tg.interval(2)));
401
402 // Render one frame (several are in the past and will be dropped).
403 size_t frames_dropped = 0;
404 scoped_refptr<VideoFrame> frame = RenderAndStep(&display_tg, &frames_dropped);
405 ASSERT_TRUE(frame);
406 EXPECT_EQ(frame_tg.interval(0), frame->timestamp());
407 EXPECT_EQ(0u, frames_dropped);
408
409 // The frame should have its display count decremented once it's reported as
410 // dropped.
411 ASSERT_EQ(1, GetCurrentFrameDisplayCount());
412 ASSERT_EQ(0, GetCurrentFrameDropCount());
413 algorithm_.OnLastFrameDropped();
414 ASSERT_EQ(1, GetCurrentFrameDisplayCount());
415 ASSERT_EQ(1, GetCurrentFrameDropCount());
416
417 // Render the frame again and then force another drop.
418 frame = RenderAndStep(&display_tg, &frames_dropped);
419 ASSERT_TRUE(frame);
420 EXPECT_EQ(frame_tg.interval(0), frame->timestamp());
421 EXPECT_EQ(0u, frames_dropped);
422
423 ASSERT_EQ(2, GetCurrentFrameDisplayCount());
424 ASSERT_EQ(1, GetCurrentFrameDropCount());
425 algorithm_.OnLastFrameDropped();
426 ASSERT_EQ(2, GetCurrentFrameDisplayCount());
427 ASSERT_EQ(2, GetCurrentFrameDropCount());
428
429 // The next Render() call should now count this frame as dropped.
430 frame = RenderAndStep(&display_tg, &frames_dropped);
431 ASSERT_TRUE(frame);
432 EXPECT_EQ(frame_tg.interval(1), frame->timestamp());
433 EXPECT_EQ(1u, frames_dropped);
434 ASSERT_EQ(1, GetCurrentFrameDisplayCount());
435 ASSERT_EQ(0, GetCurrentFrameDropCount());
436
437 // Rendering again should result in the same frame being displayed.
438 frame = RenderAndStep(&display_tg, &frames_dropped);
439 ASSERT_TRUE(frame);
440 EXPECT_EQ(frame_tg.interval(1), frame->timestamp());
441 EXPECT_EQ(0u, frames_dropped);
442
443 // In this case, the drop count is less than the display count, so the frame
444 // should not be counted as dropped.
445 ASSERT_EQ(2, GetCurrentFrameDisplayCount());
446 ASSERT_EQ(0, GetCurrentFrameDropCount());
447 algorithm_.OnLastFrameDropped();
448 ASSERT_EQ(2, GetCurrentFrameDisplayCount());
449 ASSERT_EQ(1, GetCurrentFrameDropCount());
450
451 // The third frame should be rendered correctly now and the previous frame not
452 // counted as having been dropped.
453 frame = RenderAndStep(&display_tg, &frames_dropped);
454 ASSERT_TRUE(frame);
455 EXPECT_EQ(frame_tg.interval(2), frame->timestamp());
456 EXPECT_EQ(0u, frames_dropped);
457 }
458
459 TEST_F(VideoRendererAlgorithmTest, EffectiveFramesQueued) {
460 TickGenerator frame_tg(base::TimeTicks(), 50);
461 TickGenerator display_tg(tick_clock_->NowTicks(), 25);
462
463 // Disable hysteresis since EffectiveFramesQueued() is tested as part of the
464 // normal frame pump tests when cadence is not present.
465 disable_cadence_hysteresis();
466
467 EXPECT_EQ(0u, algorithm_.EffectiveFramesQueued());
468 time_source_.StartTicking();
469
470 algorithm_.EnqueueFrame(CreateFrame(frame_tg.interval(0)));
471 EXPECT_EQ(1u, algorithm_.EffectiveFramesQueued());
472
473 algorithm_.EnqueueFrame(CreateFrame(frame_tg.interval(1)));
474 EXPECT_EQ(2u, algorithm_.EffectiveFramesQueued());
475
476 algorithm_.EnqueueFrame(CreateFrame(frame_tg.interval(2)));
477 EXPECT_EQ(3u, algorithm_.EffectiveFramesQueued());
478
479 algorithm_.EnqueueFrame(CreateFrame(frame_tg.interval(3)));
480 EXPECT_EQ(4u, algorithm_.EffectiveFramesQueued());
481 EXPECT_EQ(4u, frames_queued());
482
483 // Render one frame which will detect cadence...
484 size_t frames_dropped = 0;
485 scoped_refptr<VideoFrame> frame = RenderAndStep(&display_tg, &frames_dropped);
486 ASSERT_TRUE(frame);
487 EXPECT_EQ(frame_tg.interval(0), frame->timestamp());
488 EXPECT_EQ(0u, frames_dropped);
489
490 // Fractional cadence should be detected and the count will decrease.
491 ASSERT_TRUE(is_using_cadence());
492 EXPECT_EQ(1u, algorithm_.EffectiveFramesQueued());
493 EXPECT_EQ(4u, frames_queued());
494
495 // Dropping the last rendered frame should do nothing, since the last frame
496 // is already excluded from the count if it has a display count of 1.
497 algorithm_.OnLastFrameDropped();
498 EXPECT_EQ(1u, algorithm_.EffectiveFramesQueued());
499 }
500
501 TEST_F(VideoRendererAlgorithmTest, EffectiveFramesQueuedWithoutCadence) {
502 TickGenerator tg(tick_clock_->NowTicks(), 60);
503
504 EXPECT_EQ(0u, algorithm_.EffectiveFramesQueued());
505 time_source_.StartTicking();
506
507 algorithm_.EnqueueFrame(CreateFrame(tg.interval(0)));
508 EXPECT_EQ(1u, algorithm_.EffectiveFramesQueued());
509
510 algorithm_.EnqueueFrame(CreateFrame(tg.interval(1)));
511 EXPECT_EQ(2u, algorithm_.EffectiveFramesQueued());
512
513 algorithm_.EnqueueFrame(CreateFrame(tg.interval(2)));
514 EXPECT_EQ(3u, algorithm_.EffectiveFramesQueued());
515
516 algorithm_.EnqueueFrame(CreateFrame(tg.interval(3)));
517 EXPECT_EQ(4u, algorithm_.EffectiveFramesQueued());
518 EXPECT_EQ(4u, frames_queued());
519
520 // Issue a render call that should drop the first two frames and mark the 3rd
521 // as consumed.
522 tg.step(2);
523 size_t frames_dropped = 0;
524 scoped_refptr<VideoFrame> frame = RenderAndStep(&tg, &frames_dropped);
525 ASSERT_FALSE(is_using_cadence());
526 ASSERT_TRUE(frame);
527 EXPECT_EQ(2u, frames_dropped);
528 EXPECT_EQ(tg.interval(2), frame->timestamp());
529 EXPECT_EQ(1u, algorithm_.EffectiveFramesQueued());
530 EXPECT_EQ(2u, frames_queued());
531
532 // Rendering one more frame should return 0 effective frames queued.
533 frame = RenderAndStep(&tg, &frames_dropped);
534 ASSERT_FALSE(is_using_cadence());
535 ASSERT_TRUE(frame);
536 EXPECT_EQ(0u, frames_dropped);
537 EXPECT_EQ(tg.interval(3), frame->timestamp());
538 EXPECT_EQ(0u, algorithm_.EffectiveFramesQueued());
539 EXPECT_EQ(1u, frames_queued());
540 }
541
542 // The maximum acceptable drift should be updated once we have two frames.
543 TEST_F(VideoRendererAlgorithmTest, AcceptableDriftUpdated) {
544 TickGenerator tg(tick_clock_->NowTicks(), 50);
545
546 size_t frames_dropped = 0;
547 const base::TimeDelta original_drift = max_acceptable_drift();
548 algorithm_.EnqueueFrame(CreateFrame(tg.interval(0)));
549 EXPECT_EQ(1u, frames_queued());
550 EXPECT_TRUE(RenderAndStep(&tg, &frames_dropped));
551 EXPECT_EQ(original_drift, max_acceptable_drift());
552
553 // Time must be ticking to get wall clock times for frames.
554 time_source_.StartTicking();
555
556 algorithm_.EnqueueFrame(CreateFrame(tg.interval(1)));
557 EXPECT_EQ(2u, frames_queued());
558 EXPECT_TRUE(RenderAndStep(&tg, &frames_dropped));
559 EXPECT_NE(original_drift, max_acceptable_drift());
560 }
561
562 // Verifies behavior when time stops.
563 TEST_F(VideoRendererAlgorithmTest, TimeIsStopped) {
564 TickGenerator tg(tick_clock_->NowTicks(), 50);
565
566 // Prior to rendering the first frame, the algorithm should always return the
567 // first available frame.
568 size_t frames_dropped = 0;
569 algorithm_.EnqueueFrame(CreateFrame(tg.interval(0)));
570 EXPECT_EQ(1u, frames_queued());
571 scoped_refptr<VideoFrame> frame = RenderAndStep(&tg, &frames_dropped);
572 ASSERT_TRUE(frame);
573 EXPECT_EQ(tg.interval(0), frame->timestamp());
574 EXPECT_EQ(0u, frames_dropped);
575 EXPECT_EQ(1u, frames_queued());
576 EXPECT_EQ(1u, algorithm_.EffectiveFramesQueued());
577
578 // The same timestamp should be returned after time starts.
579 tick_clock_->Advance(tg.interval(1));
580 time_source_.StartTicking();
581 frame = RenderAndStep(&tg, &frames_dropped);
582 ASSERT_TRUE(frame);
583 EXPECT_EQ(tg.interval(0), frame->timestamp());
584 EXPECT_EQ(0u, frames_dropped);
585 EXPECT_EQ(1u, frames_queued());
586 EXPECT_EQ(1u, algorithm_.EffectiveFramesQueued());
587
588 // Ensure the next suitable frame is vended as time advances.
589 algorithm_.EnqueueFrame(CreateFrame(tg.interval(1)));
590 EXPECT_EQ(2u, frames_queued());
591 EXPECT_EQ(2u, algorithm_.EffectiveFramesQueued());
592 frame = RenderAndStep(&tg, &frames_dropped);
593 ASSERT_TRUE(frame);
594 EXPECT_EQ(tg.interval(1), frame->timestamp());
595 EXPECT_EQ(0u, frames_dropped);
596 EXPECT_EQ(1u, frames_queued());
597 EXPECT_EQ(0u, algorithm_.EffectiveFramesQueued());
598
599 // Once time stops ticking, any further frames shouldn't be returned, even if
600 // the interval requested more closely matches.
601 algorithm_.EnqueueFrame(CreateFrame(tg.interval(2)));
602 time_source_.StopTicking();
603 frame = RenderAndStep(&tg, &frames_dropped);
604 ASSERT_TRUE(frame);
605 EXPECT_EQ(tg.interval(1), frame->timestamp());
606 EXPECT_EQ(0u, frames_dropped);
607 EXPECT_EQ(2u, frames_queued());
608 EXPECT_EQ(2u, algorithm_.EffectiveFramesQueued());
609 }
610
611 // Verify frames inserted out of order end up in the right spot and are rendered
612 // according to the API contract.
613 TEST_F(VideoRendererAlgorithmTest, SortedFrameQueue) {
614 TickGenerator tg(tick_clock_->NowTicks(), 50);
615
616 // Ensure frames handed in out of order before time starts ticking are sorted
617 // and returned in the correct order upon Render().
618 algorithm_.EnqueueFrame(CreateFrame(tg.interval(3)));
619 algorithm_.EnqueueFrame(CreateFrame(tg.interval(2)));
620 EXPECT_EQ(2u, frames_queued());
621 EXPECT_EQ(2u, algorithm_.EffectiveFramesQueued());
622
623 time_source_.StartTicking();
624
625 // The first call should return the earliest frame appended.
626 size_t frames_dropped = 0;
627 scoped_refptr<VideoFrame> frame = RenderAndStep(&tg, &frames_dropped);
628 EXPECT_EQ(0u, frames_dropped);
629 EXPECT_EQ(tg.interval(2), frame->timestamp());
630 EXPECT_EQ(2u, frames_queued());
631 EXPECT_EQ(2u, algorithm_.EffectiveFramesQueued());
632
633 // Since a frame has already been rendered, queuing this frame and calling
634 // Render() should result in it being dropped; even though it's a better
635 // candidate for the desired interval.
636 algorithm_.EnqueueFrame(CreateFrame(tg.interval(1)));
637 EXPECT_EQ(3u, frames_queued());
638 EXPECT_EQ(2u, algorithm_.EffectiveFramesQueued());
639 frame = RenderAndStep(&tg, &frames_dropped);
640 EXPECT_EQ(1u, frames_dropped);
641 EXPECT_EQ(tg.interval(2), frame->timestamp());
642 EXPECT_EQ(2u, frames_queued());
643 EXPECT_EQ(2u, algorithm_.EffectiveFramesQueued());
644 }
645
646 // Run through integer cadence selection for 1, 2, 3, and 4.
647 TEST_F(VideoRendererAlgorithmTest, BestFrameByCadence) {
648 const double kTestRates[][2] = {{60, 60}, {30, 60}, {25, 75}, {25, 100}};
649
650 for (const auto& test_rate : kTestRates) {
651 disable_cadence_hysteresis();
652
653 TickGenerator frame_tg(base::TimeTicks(), test_rate[0]);
654 TickGenerator display_tg(tick_clock_->NowTicks(), test_rate[1]);
655
656 int actual_frame_pattern = 0;
657 const int desired_frame_pattern = test_rate[1] / test_rate[0];
658 scoped_refptr<VideoFrame> current_frame;
659 RunFramePumpTest(
660 true, &frame_tg, &display_tg,
661 [&current_frame, &actual_frame_pattern, desired_frame_pattern, this](
662 const scoped_refptr<VideoFrame>& frame, size_t frames_dropped) {
663 ASSERT_TRUE(frame);
664 ASSERT_EQ(0u, frames_dropped);
665
666 // Each frame should display for exactly it's desired cadence pattern.
667 if (!current_frame || current_frame == frame) {
668 actual_frame_pattern++;
669 } else {
670 ASSERT_EQ(actual_frame_pattern, desired_frame_pattern);
671 actual_frame_pattern = 1;
672 }
673
674 current_frame = frame;
675 ASSERT_TRUE(is_using_cadence());
676 });
677
678 if (HasFatalFailure())
679 return;
680 }
681 }
682
683 TEST_F(VideoRendererAlgorithmTest, BestFrameByCadenceOverdisplayed) {
684 TickGenerator frame_tg(base::TimeTicks(), 25);
685 TickGenerator display_tg(tick_clock_->NowTicks(), 50);
686 time_source_.StartTicking();
687 disable_cadence_hysteresis();
688
689 algorithm_.EnqueueFrame(CreateFrame(frame_tg.interval(0)));
690 algorithm_.EnqueueFrame(CreateFrame(frame_tg.interval(1)));
691
692 // Render frames until we've exhausted available frames and the last frame is
693 // forced to be overdisplayed.
694 for (int i = 0; i < 5; ++i) {
695 size_t frames_dropped = 0;
696 scoped_refptr<VideoFrame> frame =
697 RenderAndStep(&display_tg, &frames_dropped);
698 ASSERT_TRUE(frame);
699 EXPECT_EQ(frame_tg.interval(i < 4 ? i / 2 : 1), frame->timestamp());
700 EXPECT_EQ(0u, frames_dropped);
701 ASSERT_EQ(2, GetCurrentFrameIdealDisplayCount());
702 }
703
704 // Verify last frame is above cadence (2 in this case)
705 ASSERT_EQ(GetCurrentFrameIdealDisplayCount() + 1,
706 GetCurrentFrameDisplayCount());
707 algorithm_.EnqueueFrame(CreateFrame(frame_tg.interval(2)));
708 algorithm_.EnqueueFrame(CreateFrame(frame_tg.interval(3)));
709
710 // The next frame should only be displayed once, since the previous one was
711 // overdisplayed by one frame.
712 size_t frames_dropped = 0;
713 scoped_refptr<VideoFrame> frame = RenderAndStep(&display_tg, &frames_dropped);
714 ASSERT_TRUE(frame);
715 EXPECT_EQ(frame_tg.interval(2), frame->timestamp());
716 EXPECT_EQ(0u, frames_dropped);
717 ASSERT_EQ(1, GetCurrentFrameIdealDisplayCount());
718
719 frame = RenderAndStep(&display_tg, &frames_dropped);
720 ASSERT_TRUE(frame);
721 EXPECT_EQ(frame_tg.interval(3), frame->timestamp());
722 EXPECT_EQ(0u, frames_dropped);
723 ASSERT_EQ(2, GetCurrentFrameIdealDisplayCount());
724 }
725
726 TEST_F(VideoRendererAlgorithmTest, BestFrameByCoverage) {
727 TickGenerator tg(tick_clock_->NowTicks(), 50);
728 time_source_.StartTicking();
729
730 algorithm_.EnqueueFrame(CreateFrame(tg.interval(0)));
731 algorithm_.EnqueueFrame(CreateFrame(tg.interval(1)));
732 algorithm_.EnqueueFrame(CreateFrame(tg.interval(2)));
733
734 base::TimeTicks deadline_min = tg.current();
735 base::TimeTicks deadline_max = deadline_min + tg.interval(1);
736
737 size_t frames_dropped = 0;
738 scoped_refptr<VideoFrame> frame =
739 algorithm_.Render(deadline_min, deadline_max, &frames_dropped);
740 ASSERT_TRUE(frame);
741 EXPECT_EQ(tg.interval(0), frame->timestamp());
742 EXPECT_EQ(0u, frames_dropped);
743
744 int second_best = 0;
745
746 // Coverage should be 1 for if the frame overlaps the interval entirely, no
747 // second best should be found.
748 EXPECT_EQ(0,
749 FindBestFrameByCoverage(deadline_min, deadline_max, &second_best));
750 EXPECT_EQ(-1, second_best);
751
752 // 49/51 coverage for frame 0 and frame 1 should be within tolerance such that
753 // the earlier frame should still be chosen.
754 deadline_min = tg.current() + tg.interval(1) / 2 +
755 base::TimeDelta::FromMicroseconds(250);
756 deadline_max = deadline_min + tg.interval(1);
757 EXPECT_EQ(0,
758 FindBestFrameByCoverage(deadline_min, deadline_max, &second_best));
759 EXPECT_EQ(1, second_best);
760
761 // 48/52 coverage should result in the second frame being chosen.
762 deadline_min = tg.current() + tg.interval(1) / 2 +
763 base::TimeDelta::FromMicroseconds(500);
764 deadline_max = deadline_min + tg.interval(1);
765 EXPECT_EQ(1,
766 FindBestFrameByCoverage(deadline_min, deadline_max, &second_best));
767 EXPECT_EQ(0, second_best);
768
769 // Overlapping three frames should choose the one with the most coverage and
770 // the second best should be the earliest frame.
771 deadline_min = tg.current() + tg.interval(1) / 2;
772 deadline_max = deadline_min + tg.interval(2);
773 EXPECT_EQ(1,
774 FindBestFrameByCoverage(deadline_min, deadline_max, &second_best));
775 EXPECT_EQ(0, second_best);
776
777 // Requesting coverage outside of all known frames should return -1 for both
778 // best indices.
779 deadline_min = tg.current() + tg.interval(frames_queued());
780 deadline_max = deadline_min + tg.interval(1);
781 EXPECT_EQ(-1,
782 FindBestFrameByCoverage(deadline_min, deadline_max, &second_best));
783 EXPECT_EQ(-1, second_best);
784 }
785
786 TEST_F(VideoRendererAlgorithmTest, BestFrameByDriftAndDriftCalculations) {
787 TickGenerator tg(tick_clock_->NowTicks(), 50);
788 time_source_.StartTicking();
789
790 algorithm_.EnqueueFrame(CreateFrame(tg.interval(0)));
791 algorithm_.EnqueueFrame(CreateFrame(tg.interval(1)));
792
793 size_t frames_dropped = 0;
794 scoped_refptr<VideoFrame> frame = algorithm_.Render(
795 tg.current(), tg.current() + tg.interval(1), &frames_dropped);
796 ASSERT_TRUE(frame);
797 EXPECT_EQ(tg.interval(0), frame->timestamp());
798 EXPECT_EQ(0u, frames_dropped);
799
800 base::TimeDelta zero_drift, half_drift = tg.interval(1) / 2;
801 base::TimeDelta detected_drift;
802
803 // Frame_0 overlaps the deadline, Frame_1 is a full interval away.
804 base::TimeTicks deadline = tg.current();
805 EXPECT_EQ(zero_drift, CalculateAbsoluteDriftForFrame(deadline, 0));
806 EXPECT_EQ(tg.interval(1), CalculateAbsoluteDriftForFrame(deadline, 1));
807 EXPECT_EQ(0, FindBestFrameByDrift(deadline, &detected_drift));
808 EXPECT_EQ(zero_drift, detected_drift);
809
810 // Frame_0 overlaps the deadline, Frame_1 is a half interval away.
811 deadline += half_drift;
812 EXPECT_EQ(zero_drift, CalculateAbsoluteDriftForFrame(deadline, 0));
813 EXPECT_EQ(half_drift, CalculateAbsoluteDriftForFrame(deadline, 1));
814 EXPECT_EQ(0, FindBestFrameByDrift(deadline, &detected_drift));
815 EXPECT_EQ(zero_drift, detected_drift);
816
817 // Both frames overlap the deadline.
818 deadline += half_drift;
819 EXPECT_EQ(zero_drift, CalculateAbsoluteDriftForFrame(deadline, 0));
820 EXPECT_EQ(zero_drift, CalculateAbsoluteDriftForFrame(deadline, 1));
821 EXPECT_EQ(1, FindBestFrameByDrift(deadline, &detected_drift));
822 EXPECT_EQ(zero_drift, detected_drift);
823
824 // Frame_0 is half an interval away, Frame_1 overlaps the deadline.
825 deadline += half_drift;
826 EXPECT_EQ(half_drift, CalculateAbsoluteDriftForFrame(deadline, 0));
827 EXPECT_EQ(zero_drift, CalculateAbsoluteDriftForFrame(deadline, 1));
828 EXPECT_EQ(1, FindBestFrameByDrift(deadline, &detected_drift));
829 EXPECT_EQ(zero_drift, detected_drift);
830
831 // Frame_0 is a full interval away, Frame_1 overlaps the deadline.
832 deadline += half_drift;
833 EXPECT_EQ(tg.interval(1), CalculateAbsoluteDriftForFrame(deadline, 0));
834 EXPECT_EQ(zero_drift, CalculateAbsoluteDriftForFrame(deadline, 1));
835 EXPECT_EQ(1, FindBestFrameByDrift(deadline, &detected_drift));
836 EXPECT_EQ(zero_drift, detected_drift);
837
838 // Both frames are entirely before the deadline.
839 deadline += half_drift;
840 EXPECT_EQ(tg.interval(1) + half_drift,
841 CalculateAbsoluteDriftForFrame(deadline, 0));
842 EXPECT_EQ(half_drift, CalculateAbsoluteDriftForFrame(deadline, 1));
843 EXPECT_EQ(1, FindBestFrameByDrift(deadline, &detected_drift));
844 EXPECT_EQ(half_drift, detected_drift);
845 }
846
847 // Run through fractional cadence selection for 1/2, 1/3, and 1/4.
848 TEST_F(VideoRendererAlgorithmTest, BestFrameByFractionalCadence) {
849 const double kTestRates[][2] = {{120, 60}, {72, 24}, {100, 25}};
850
851 for (const auto& test_rate : kTestRates) {
852 disable_cadence_hysteresis();
853
854 TickGenerator frame_tg(base::TimeTicks(), test_rate[0]);
855 TickGenerator display_tg(tick_clock_->NowTicks(), test_rate[1]);
856
857 const size_t desired_drop_pattern = test_rate[0] / test_rate[1] - 1;
858 scoped_refptr<VideoFrame> current_frame;
859 RunFramePumpTest(
860 true, &frame_tg, &display_tg,
861 [&current_frame, desired_drop_pattern, this](
862 const scoped_refptr<VideoFrame>& frame, size_t frames_dropped) {
863 ASSERT_TRUE(frame);
864
865 // The first frame should have zero dropped frames, but each Render()
866 // call after should drop the same number of frames based on the
867 // fractional cadence.
868 if (!current_frame)
869 ASSERT_EQ(0u, frames_dropped);
870 else
871 ASSERT_EQ(desired_drop_pattern, frames_dropped);
872
873 ASSERT_NE(current_frame, frame);
874 ASSERT_TRUE(is_using_cadence());
875 current_frame = frame;
876 });
877
878 if (HasFatalFailure())
879 return;
880 }
881 }
882
883 // Verify a 3:2 frame pattern for 23.974fps in 60Hz; doubles as a test for best
884 // frame by coverage.
885 TEST_F(VideoRendererAlgorithmTest, FilmCadence) {
886 const double kTestRates[] = {NTSC(24), 24};
887
888 for (double frame_rate : kTestRates) {
889 scoped_refptr<VideoFrame> current_frame;
890 int actual_frame_pattern = 0, desired_frame_pattern = 3;
891
892 TickGenerator frame_tg(base::TimeTicks(), frame_rate);
893 TickGenerator display_tg(tick_clock_->NowTicks(), 60);
894
895 RunFramePumpTest(
896 true, &frame_tg, &display_tg,
897 [&current_frame, &actual_frame_pattern, &desired_frame_pattern, this](
898 const scoped_refptr<VideoFrame>& frame, size_t frames_dropped) {
899 ASSERT_TRUE(frame);
900 ASSERT_EQ(0u, frames_dropped);
901
902 if (!current_frame || current_frame == frame) {
903 actual_frame_pattern++;
904 } else {
905 ASSERT_EQ(actual_frame_pattern, desired_frame_pattern);
906 actual_frame_pattern = 1;
907 desired_frame_pattern = (desired_frame_pattern == 3 ? 2 : 3);
908 }
909
910 current_frame = frame;
911 ASSERT_FALSE(is_using_cadence());
912 });
913
914 if (HasFatalFailure())
915 return;
916 }
917 }
918
919 // Spot check common display and frame rate pairs for correctness.
920 TEST_F(VideoRendererAlgorithmTest, CadenceCalculations) {
921 ASSERT_FALSE(GetCadence(24, 60));
922 ASSERT_FALSE(GetCadence(NTSC(24), 60));
923 ASSERT_FALSE(GetCadence(25, 60));
924 ASSERT_EQ(2, GetCadence(NTSC(30), 60));
925 ASSERT_EQ(2, GetCadence(30, 60));
926 ASSERT_FALSE(GetCadence(50, 60));
927 ASSERT_EQ(1, GetCadence(NTSC(60), 60));
928 ASSERT_EQ(2, GetCadence(120, 60));
929
930 // 50Hz is common in the EU.
931 ASSERT_FALSE(GetCadence(NTSC(24), 50));
932 ASSERT_FALSE(GetCadence(24, 50));
933 ASSERT_EQ(2, GetCadence(NTSC(25), 50));
934 ASSERT_EQ(2, GetCadence(25, 50));
935 ASSERT_FALSE(GetCadence(NTSC(30), 50));
936 ASSERT_FALSE(GetCadence(30, 50));
937 ASSERT_FALSE(GetCadence(NTSC(60), 50));
938 ASSERT_FALSE(GetCadence(60, 50));
939
940 ASSERT_FALSE(GetCadence(25, NTSC(60)));
941 ASSERT_EQ(2, GetCadence(120, NTSC(60)));
942 ASSERT_EQ(60, GetCadence(1, NTSC(60)));
943 }
944
945 TEST_F(VideoRendererAlgorithmTest, RemoveExpiredFrames) {
946 TickGenerator tg(tick_clock_->NowTicks(), 50);
947
948 algorithm_.EnqueueFrame(CreateFrame(tg.interval(0)));
949 ASSERT_EQ(0u, algorithm_.RemoveExpiredFrames(tg.current()));
950 EXPECT_EQ(1u, algorithm_.EffectiveFramesQueued());
951
952 time_source_.StartTicking();
953
954 size_t frames_dropped = 0;
955 scoped_refptr<VideoFrame> frame = RenderAndStep(&tg, &frames_dropped);
956 ASSERT_TRUE(frame);
957 EXPECT_EQ(tg.interval(0), frame->timestamp());
958 EXPECT_EQ(0u, frames_dropped);
959
960 algorithm_.EnqueueFrame(CreateFrame(tg.interval(1)));
961 algorithm_.EnqueueFrame(CreateFrame(tg.interval(2)));
962 algorithm_.EnqueueFrame(CreateFrame(tg.interval(3)));
963 algorithm_.EnqueueFrame(CreateFrame(tg.interval(4)));
964 EXPECT_EQ(5u, algorithm_.EffectiveFramesQueued());
965
966 tg.step(2);
967 ASSERT_EQ(2u, algorithm_.RemoveExpiredFrames(tg.current()));
968 frame = RenderAndStep(&tg, &frames_dropped);
969 EXPECT_EQ(1u, frames_dropped);
970 EXPECT_EQ(2u, frames_queued());
971 EXPECT_EQ(1u, algorithm_.EffectiveFramesQueued());
972 ASSERT_TRUE(frame);
973 EXPECT_EQ(tg.interval(3), frame->timestamp());
974
975 // Advance expiry enough that one frame is removed, but one remains and is
976 // still counted as effective.
977 ASSERT_EQ(
978 1u, algorithm_.RemoveExpiredFrames(tg.current() + tg.interval(1) * 0.75));
979 EXPECT_EQ(1u, frames_queued());
980 EXPECT_EQ(1u, algorithm_.EffectiveFramesQueued());
981
982 // Advancing expiry once more should mark the frame as ineffective.
983 tg.step();
984 ASSERT_EQ(0u, algorithm_.RemoveExpiredFrames(tg.current()));
985 EXPECT_EQ(1u, frames_queued());
986 EXPECT_EQ(0u, algorithm_.EffectiveFramesQueued());
987 }
988
989 TEST_F(VideoRendererAlgorithmTest, CadenceBasedTest) {
990 // Common display rates.
991 const double kDisplayRates[] = {
992 NTSC(24),
993 24,
994 NTSC(25),
995 25,
996 NTSC(30),
997 30,
998 48,
999 NTSC(50),
1000 50,
1001 NTSC(60),
1002 60,
1003 75,
1004 120,
1005 144,
1006 };
1007
1008 // List of common frame rate values. Values pulled from local test media,
1009 // videostack test matrix, and Wikipedia.
1010 const double kTestRates[] = {
1011 1, 10, 12.5, 15, NTSC(24), 24, NTSC(25), 25,
1012 NTSC(30), 30, 30.12, 48, NTSC(50), 50, 58.74, NTSC(60),
1013 60, 72, 90, 100, 120, 144, 240, 300,
1014 };
1015
1016 for (double display_rate : kDisplayRates) {
1017 for (double frame_rate : kTestRates) {
1018 TickGenerator frame_tg(base::TimeTicks(), frame_rate);
1019 TickGenerator display_tg(tick_clock_->NowTicks(), display_rate);
1020 RunFramePumpTest(
1021 true, &frame_tg, &display_tg,
1022 [](const scoped_refptr<VideoFrame>& frame, size_t frames_dropped) {});
1023 if (HasFatalFailure())
1024 return;
1025 }
1026 }
1027 }
1028
1029 // Rotate through various playback rates and ensure algorithm adapts correctly.
1030 TEST_F(VideoRendererAlgorithmTest, VariableFrameRateCadence) {
1031 TickGenerator frame_tg(base::TimeTicks(), NTSC(30));
1032 TickGenerator display_tg(tick_clock_->NowTicks(), 60);
1033
1034 const double kTestRates[] = {1.0, 2, 0.215, 0.5, 1.0};
1035 const bool kTestRateHasCadence[arraysize(kTestRates)] = {
1036 true, true, false, true, true};
1037
1038 for (size_t i = 0; i < arraysize(kTestRates); ++i) {
1039 const double playback_rate = kTestRates[i];
1040 SCOPED_TRACE(base::StringPrintf("Playback Rate: %.03f", playback_rate));
1041 time_source_.SetPlaybackRate(playback_rate);
1042 RunFramePumpTest(false, &frame_tg, &display_tg,
1043 [this](const scoped_refptr<VideoFrame>& frame,
1044 size_t frames_dropped) {});
1045 if (HasFatalFailure())
1046 return;
1047
1048 ASSERT_EQ(kTestRateHasCadence[i], is_using_cadence());
1049 }
1050
1051 // TODO(dalecurtis): Is there more we can test here?
1052 }
1053
1054 // Ensures media which only expresses timestamps in milliseconds, gets the right
1055 // cadence detection.
1056 TEST_F(VideoRendererAlgorithmTest, UglyTimestampsHaveCadence) {
1057 TickGenerator display_tg(tick_clock_->NowTicks(), 60);
1058 time_source_.StartTicking();
1059
1060 // 59.94fps, timestamp deltas from https://youtu.be/byoLvAo9qjs
1061 const int kBadTimestampsMs[] = {
1062 17, 16, 17, 17, 16, 17, 17, 16, 17, 17, 17, 16, 17, 17, 16, 17, 17, 16,
1063 17, 17, 16, 17, 17, 16, 17, 17, 16, 17, 17, 17, 16, 17, 17, 16, 17, 17,
1064 16, 17, 17, 16, 17, 17, 16, 17, 17, 16, 17, 17, 16, 17, 17, 17};
1065
1066 // Run throught ~1.6 seconds worth of frames.
1067 bool cadence_detected = false;
1068 base::TimeDelta timestamp;
1069 for (size_t i = 0; i < arraysize(kBadTimestampsMs) * 2; ++i) {
1070 while (algorithm_.EffectiveFramesQueued() < 3) {
1071 algorithm_.EnqueueFrame(CreateFrame(timestamp));
1072 timestamp += base::TimeDelta::FromMilliseconds(
1073 kBadTimestampsMs[i % arraysize(kBadTimestampsMs)]);
1074 }
1075
1076 size_t frames_dropped = 0;
1077 RenderAndStep(&display_tg, &frames_dropped);
1078 ASSERT_EQ(0u, frames_dropped);
1079
1080 // Cadence won't be detected immediately on this clip, but it will after
1081 // enough frames are encountered; after which it should not drop out of
1082 // cadence.
1083 if (is_using_cadence())
1084 cadence_detected = true;
1085
1086 if (cadence_detected)
1087 ASSERT_TRUE(is_using_cadence());
1088 }
1089 }
1090
1091 } // namespace media
OLDNEW
« no previous file with comments | « media/filters/video_renderer_algorithm.cc ('k') | media/media.gyp » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698