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

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: Moar tests! Created 5 years, 8 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
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
DaleCurtis 2015/04/15 02:20:20 Notes to self: Still need tests for AccountForMiss
DaleCurtis 2015/04/18 01:29:20 All tests added!
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 #define NTSC(fps) fps / 1.001
21
22 // Helper class for generating TimeTicks in a sequence according to a frequency.
23 class TickGenerator {
24 public:
25 TickGenerator(base::TimeTicks base_timestamp, double hertz)
26 : tick_count_(0),
27 hertz_(hertz),
28 microseconds_per_tick_(base::Time::kMicrosecondsPerSecond / hertz),
29 base_time_(base_timestamp) {}
30
31 base::TimeDelta interval(int tick_count) {
32 return base::TimeDelta::FromMicroseconds(tick_count *
33 microseconds_per_tick_);
34 }
35
36 base::TimeTicks current() { return base_time_ + interval(tick_count_); }
37 base::TimeTicks step() { return step(1); }
38 base::TimeTicks step(int n) {
39 tick_count_ += n;
40 return current();
41 }
42
43 const double hertz() const { return hertz_; }
44
45 void Reset(base::TimeTicks base_timestamp) {
46 base_time_ = base_timestamp;
47 tick_count_ = 0;
48 }
49
50 private:
51 // Track a tick count and seconds per tick value to ensure we don't drift too
52 // far due to accumulated errors during testing.
53 int64_t tick_count_;
54 double hertz_;
55 double microseconds_per_tick_;
56 base::TimeTicks base_time_;
57
58 DISALLOW_COPY_AND_ASSIGN(TickGenerator);
59 };
60
61 class VideoRendererAlgorithmTest : public testing::Test {
62 public:
63 VideoRendererAlgorithmTest()
64 : tick_clock_(new base::SimpleTestTickClock()),
65 algorithm_(base::Bind(&WallClockTimeSource::GetWallClockTime,
66 base::Unretained(&time_source_))) {
67 // Always start the TickClock at a non-zero value since null values have
68 // special connotations.
69 tick_clock_->Advance(base::TimeDelta::FromMicroseconds(10000));
70 time_source_.SetTickClockForTesting(
71 scoped_ptr<base::TickClock>(tick_clock_));
72 }
73 ~VideoRendererAlgorithmTest() override {}
74
75 scoped_refptr<VideoFrame> CreateFrame(base::TimeDelta timestamp) {
76 const gfx::Size natural_size(8, 8);
77 return frame_pool_.CreateFrame(VideoFrame::YV12, natural_size,
78 gfx::Rect(natural_size), natural_size,
79 timestamp);
80 }
81
82 base::TimeDelta minimum_glitch_time() const {
83 return base::TimeDelta::FromSeconds(
84 VideoRendererAlgorithm::kMinimumAcceptableTimeBetweenGlitchesSecs);
85 }
86
87 base::TimeDelta max_acceptable_drift() const {
88 return algorithm_.max_acceptable_drift_;
89 }
90
91 bool is_using_cadence() const { return algorithm_.ideal_cadence_ > 0; }
92
93 size_t frames_queued() const { return algorithm_.frame_queue_.size(); }
94
95 int GetCadence(double frame_rate, double display_rate) {
96 TickGenerator display_tg(tick_clock_->NowTicks(), display_rate);
97 TickGenerator frame_tg(base::TimeTicks(), frame_rate);
98 time_source_.StartTicking();
99
100 // Enqueue enough frames for cadence detection.
101 int frames_dropped = 0;
102 algorithm_.disable_cadence_hysteresis_for_testing();
103 algorithm_.EnqueueFrame(CreateFrame(frame_tg.interval(0)));
104 algorithm_.EnqueueFrame(CreateFrame(frame_tg.interval(1)));
105 EXPECT_TRUE(algorithm_.Render(display_tg.current(), display_tg.step(),
106 &frames_dropped));
107
108 // Store cadence before reseting the algorithm.
109 const int cadence = algorithm_.fractional_cadence_
110 ? algorithm_.fractional_cadence_
111 : algorithm_.ideal_cadence_;
112
113 time_source_.StopTicking();
114 algorithm_.Reset();
115 return cadence;
116 }
117
118 bool DriftOfLastRenderWasWithinTolerance(base::TimeTicks deadline_min) {
119 return algorithm_.CalculateDriftForFrame(deadline_min, 0) <=
120 algorithm_.max_acceptable_drift_;
121 }
122
123 // Allows tests to run a Render() loop with sufficient frames for the various
124 // rendering modes. Upon each Render() |render_test_func| will be called with
125 // the rendered frame and the number of frames dropped.
126 template <typename OnRenderCallback>
127 void RunFramePumpTest(bool reset,
128 TickGenerator* frame_tg,
129 TickGenerator* display_tg,
130 OnRenderCallback render_test_func) {
131 SCOPED_TRACE(base::StringPrintf("Rendering %.03f fps into %0.03f",
132 frame_tg->hertz(), display_tg->hertz()));
133 tick_clock_->Advance(display_tg->current() - tick_clock_->NowTicks());
134 time_source_.StartTicking();
135
136 const bool fresh_algorithm = !algorithm_.have_rendered_frames_;
137
138 base::TimeDelta last_frame_timestamp = kNoTimestamp();
139 bool should_use_cadence = false;
140 int glitch_count = 0;
141 const base::TimeTicks start_time = tick_clock_->NowTicks();
142 while (tick_clock_->NowTicks() - start_time < minimum_glitch_time()) {
143 while (algorithm_.EffectiveFramesQueued() < 3 ||
144 frame_tg->current() - time_source_.CurrentMediaTime() <
145 base::TimeTicks()) {
146 algorithm_.EnqueueFrame(
147 CreateFrame(frame_tg->current() - base::TimeTicks()));
148 frame_tg->step();
149 }
150
151 int frames_dropped = 0;
152 const base::TimeTicks deadline_min = display_tg->current();
153 const base::TimeTicks deadline_max = display_tg->step();
154 scoped_refptr<VideoFrame> frame =
155 algorithm_.Render(deadline_min, deadline_max, &frames_dropped);
156
157 render_test_func(frame, frames_dropped);
158 tick_clock_->Advance(display_tg->current() - tick_clock_->NowTicks());
159
160 if (HasFatalFailure())
161 return;
162
163 // Render() should always return a frame within drift tolerances.
164 ASSERT_TRUE(DriftOfLastRenderWasWithinTolerance(deadline_min));
165
166 // If we have a frame, the timestamps should always be monotonically
167 // increasing.
168 if (frame) {
169 if (last_frame_timestamp != kNoTimestamp())
170 ASSERT_LE(last_frame_timestamp, frame->timestamp());
171 else
172 last_frame_timestamp = frame->timestamp();
173 }
174
175 // Only verify certain properties for fresh instances.
176 if (fresh_algorithm) {
177 ASSERT_EQ(frame_tg->interval(1), algorithm_.average_frame_duration());
178
179 if (is_using_cadence() && algorithm_.last_render_had_glitch())
180 ++glitch_count;
181
182 // Once cadence starts, it should never stop for the current set of
183 // tests.
184 if (is_using_cadence())
185 should_use_cadence = true;
186 ASSERT_EQ(is_using_cadence(), should_use_cadence);
187 }
188 }
189
190 // When using cadence, the glitch count should be at most one for when
191 // rendering for the less than minimum_glitch_time().
192 if (fresh_algorithm && is_using_cadence())
193 ASSERT_LE(glitch_count, 1);
194
195 time_source_.StopTicking();
196 if (reset) {
197 algorithm_.Reset();
198 time_source_.SetMediaTime(base::TimeDelta());
199 }
200 }
201
202 int FindBestFrameByCoverage(base::TimeTicks deadline_min,
203 base::TimeTicks deadline_max,
204 int* second_best) {
205 return algorithm_.FindBestFrameByCoverage(deadline_min, deadline_max,
206 second_best);
207 }
208
209 int FindBestFrameByDrift(base::TimeTicks deadline_min) {
210 return algorithm_.FindBestFrameByDrift(deadline_min);
211 }
212
213 protected:
214 VideoFramePool frame_pool_;
215 WallClockTimeSource time_source_;
216 base::SimpleTestTickClock* tick_clock_; // Owned by |time_source_|.
217 VideoRendererAlgorithm algorithm_;
218
219 private:
220 DISALLOW_COPY_AND_ASSIGN(VideoRendererAlgorithmTest);
221 };
222
223 TEST_F(VideoRendererAlgorithmTest, Empty) {
224 TickGenerator tg(tick_clock_->NowTicks(), 50);
225 int frames_dropped = 0;
226 EXPECT_EQ(0u, frames_queued());
227 EXPECT_FALSE(algorithm_.Render(tg.current(), tg.step(), &frames_dropped));
228 EXPECT_EQ(0, frames_dropped);
229 EXPECT_EQ(0u, frames_queued());
230 EXPECT_NE(base::TimeDelta(), max_acceptable_drift());
231 }
232
233 TEST_F(VideoRendererAlgorithmTest, Reset) {
234 TickGenerator tg(tick_clock_->NowTicks(), 50);
235 algorithm_.EnqueueFrame(CreateFrame(tg.interval(0)));
236 EXPECT_EQ(1u, frames_queued());
237 EXPECT_NE(base::TimeDelta(), max_acceptable_drift());
238 algorithm_.Reset();
239 EXPECT_EQ(0u, frames_queued());
240 EXPECT_NE(base::TimeDelta(), max_acceptable_drift());
241 }
242
243 // The maximum acceptable drift should be updated once we have two frames.
244 TEST_F(VideoRendererAlgorithmTest, AcceptableDriftUpdated) {
245 TickGenerator tg(tick_clock_->NowTicks(), 50);
246
247 int frames_dropped = 0;
248 const base::TimeDelta original_drift = max_acceptable_drift();
249 algorithm_.EnqueueFrame(CreateFrame(tg.interval(0)));
250 EXPECT_EQ(1u, frames_queued());
251 EXPECT_TRUE(algorithm_.Render(tg.current(), tg.step(), &frames_dropped));
252 EXPECT_EQ(original_drift, max_acceptable_drift());
253
254 // Time must be ticking to get wall clock times for frames.
255 time_source_.StartTicking();
256
257 algorithm_.EnqueueFrame(CreateFrame(tg.interval(1)));
258 EXPECT_EQ(2u, frames_queued());
259 EXPECT_TRUE(algorithm_.Render(tg.current(), tg.step(), &frames_dropped));
260 EXPECT_NE(original_drift, max_acceptable_drift());
261 }
262
263 // Verifies behavior when time stops.
264 TEST_F(VideoRendererAlgorithmTest, TimeIsStopped) {
265 TickGenerator tg(tick_clock_->NowTicks(), 50);
266
267 // Prior to rendering the first frame, the algorithm should always return the
268 // first available frame.
269 int frames_dropped = 0;
270 algorithm_.EnqueueFrame(CreateFrame(tg.interval(0)));
271 EXPECT_EQ(1u, frames_queued());
272 scoped_refptr<VideoFrame> frame =
273 algorithm_.Render(tg.current(), tg.step(), &frames_dropped);
274 ASSERT_TRUE(frame);
275 EXPECT_EQ(tg.interval(0), frame->timestamp());
276 EXPECT_EQ(0, frames_dropped);
277 EXPECT_EQ(1u, frames_queued());
278
279 // The same timestamp should be returned after time starts.
280 tick_clock_->Advance(tg.interval(1));
281 time_source_.StartTicking();
282 frame = algorithm_.Render(tg.current(), tg.step(), &frames_dropped);
283 ASSERT_TRUE(frame);
284 EXPECT_EQ(tg.interval(0), frame->timestamp());
285 EXPECT_EQ(0, frames_dropped);
286 EXPECT_EQ(1u, frames_queued());
287
288 // Ensure the next suitable frame is vended as time advances.
289 algorithm_.EnqueueFrame(CreateFrame(tg.interval(1)));
290 EXPECT_EQ(2u, frames_queued());
291 frame = algorithm_.Render(tg.current(), tg.step(), &frames_dropped);
292 ASSERT_TRUE(frame);
293 EXPECT_EQ(tg.interval(1), frame->timestamp());
294 EXPECT_EQ(0, frames_dropped);
295 EXPECT_EQ(1u, frames_queued());
296
297 // Once time stops ticking, any further frames shouldn't be returned, even if
298 // the interval requested more closely matches.
299 algorithm_.EnqueueFrame(CreateFrame(tg.interval(2)));
300 time_source_.StopTicking();
301 frame = algorithm_.Render(tg.current(), tg.step(), &frames_dropped);
302 ASSERT_TRUE(frame);
303 EXPECT_EQ(tg.interval(1), frame->timestamp());
304 EXPECT_EQ(0, frames_dropped);
305 EXPECT_EQ(2u, frames_queued());
306 }
307
308 // Verify frames inserted out of order end up in the right spot and are rendered
309 // according to the API contract.
310 TEST_F(VideoRendererAlgorithmTest, SortedFrameQueue) {
311 TickGenerator tg(tick_clock_->NowTicks(), 50);
312
313 // Ensure frames handed in out of order before time starts ticking are sorted
314 // and returned in the correct order upon Render().
315 algorithm_.EnqueueFrame(CreateFrame(tg.interval(3)));
316 algorithm_.EnqueueFrame(CreateFrame(tg.interval(2)));
317 EXPECT_EQ(2u, frames_queued());
318
319 time_source_.StartTicking();
320
321 // The first call should return the earliest frame appended.
322 int frames_dropped = 0;
323 scoped_refptr<VideoFrame> frame =
324 algorithm_.Render(tg.current(), tg.step(), &frames_dropped);
325 EXPECT_EQ(0, frames_dropped);
326 EXPECT_EQ(tg.interval(2), frame->timestamp());
327 EXPECT_EQ(2u, frames_queued());
328
329 // Since a frame has already been rendered, enqueing this frame and calling
330 // Render() should result in it being dropped; even though it's a better
331 // candidate for the desired interval.
332 algorithm_.EnqueueFrame(CreateFrame(tg.interval(1)));
333 frame = algorithm_.Render(tg.current(), tg.step(), &frames_dropped);
334 EXPECT_EQ(1, frames_dropped);
335 EXPECT_EQ(tg.interval(2), frame->timestamp());
336 EXPECT_EQ(2u, frames_queued());
337 }
338
339 // Run through integer cadence selection for 1, 2, 3, and 4.
340 TEST_F(VideoRendererAlgorithmTest, BestFrameByCadence) {
341 const double kTestRates[][2] = {{60, 60}, {30, 60}, {25, 75}, {25, 100}};
342
343 for (const auto& test_rate : kTestRates) {
344 algorithm_.disable_cadence_hysteresis_for_testing();
345
346 TickGenerator frame_tg(base::TimeTicks(), test_rate[0]);
347 TickGenerator display_tg(tick_clock_->NowTicks(), test_rate[1]);
348
349 int actual_frame_pattern = 0;
350 const int desired_frame_pattern = test_rate[1] / test_rate[0];
351 scoped_refptr<VideoFrame> current_frame;
352 RunFramePumpTest(
353 true, &frame_tg, &display_tg,
354 [&current_frame, &actual_frame_pattern, desired_frame_pattern, this](
355 const scoped_refptr<VideoFrame>& frame, int frames_dropped) {
356 ASSERT_TRUE(frame);
357 ASSERT_EQ(0, frames_dropped);
358
359 // Each frame should display for exactly it's desired cadence pattern.
360 if (!current_frame || current_frame == frame) {
361 actual_frame_pattern++;
362 } else {
363 ASSERT_EQ(actual_frame_pattern, desired_frame_pattern);
364 actual_frame_pattern = 1;
365 }
366
367 current_frame = frame;
368 ASSERT_TRUE(is_using_cadence());
369 });
370
371 if (HasFatalFailure())
372 return;
373 }
374 }
375
376 TEST_F(VideoRendererAlgorithmTest, BestFrameByCoverage) {
377 TickGenerator tg(tick_clock_->NowTicks(), 50);
378 time_source_.StartTicking();
379
380 algorithm_.EnqueueFrame(CreateFrame(tg.interval(0)));
381 algorithm_.EnqueueFrame(CreateFrame(tg.interval(1)));
382 algorithm_.EnqueueFrame(CreateFrame(tg.interval(2)));
383
384 base::TimeTicks deadline_min = tg.current();
385 base::TimeTicks deadline_max = deadline_min + tg.interval(1);
386
387 int frames_dropped = 0;
388 scoped_refptr<VideoFrame> frame =
389 algorithm_.Render(deadline_min, deadline_max, &frames_dropped);
390 ASSERT_TRUE(frame);
391 EXPECT_EQ(tg.interval(0), frame->timestamp());
392 EXPECT_EQ(0, frames_dropped);
393
394 int second_best = 0;
395
396 // Coverage should be 1 for if the frame overlaps the interval entirely, no
397 // second best should be found.
398 EXPECT_EQ(0,
399 FindBestFrameByCoverage(deadline_min, deadline_max, &second_best));
400 EXPECT_EQ(-1, second_best);
401
402 // 49/51 coverage for frame 0 and frame 1 should be within tolerance such that
403 // the earlier frame should still be chosen.
404 deadline_min = tg.current() + tg.interval(1) / 2 +
405 base::TimeDelta::FromMicroseconds(250);
406 deadline_max = deadline_min + tg.interval(1);
407 EXPECT_EQ(0,
408 FindBestFrameByCoverage(deadline_min, deadline_max, &second_best));
409 EXPECT_EQ(1, second_best);
410
411 // 48/52 coverage should result in the second frame being chosen.
412 deadline_min = tg.current() + tg.interval(1) / 2 +
413 base::TimeDelta::FromMicroseconds(500);
414 deadline_max = deadline_min + tg.interval(1);
415 EXPECT_EQ(1,
416 FindBestFrameByCoverage(deadline_min, deadline_max, &second_best));
417 EXPECT_EQ(0, second_best);
418
419 // Overlapping three frames should choose the one with the most coverage and
420 // the second best should be the earliest frame.
421 deadline_min = tg.current() + tg.interval(1) / 2;
422 deadline_max = deadline_min + tg.interval(2);
423 EXPECT_EQ(1,
424 FindBestFrameByCoverage(deadline_min, deadline_max, &second_best));
425 EXPECT_EQ(0, second_best);
426
427 // Requesting coverage outside of all known frames should return -1 for both
428 // best indices.
429 deadline_min = tg.current() + tg.interval(frames_queued());
430 deadline_max = deadline_min + tg.interval(1);
431 EXPECT_EQ(-1,
432 FindBestFrameByCoverage(deadline_min, deadline_max, &second_best));
433 EXPECT_EQ(-1, second_best);
434 }
435
436 // Run through fractional cadence selection for 1/2, 1/3, and 1/4.
437 TEST_F(VideoRendererAlgorithmTest, BestFrameByFractionalCadence) {
438 const double kTestRates[][2] = {{120, 60}, {72, 24}, {100, 25}};
439
440 for (const auto& test_rate : kTestRates) {
441 algorithm_.disable_cadence_hysteresis_for_testing();
442
443 TickGenerator frame_tg(base::TimeTicks(), test_rate[0]);
444 TickGenerator display_tg(tick_clock_->NowTicks(), test_rate[1]);
445
446 const int desired_drop_pattern = test_rate[0] / test_rate[1] - 1;
447 scoped_refptr<VideoFrame> current_frame;
448 RunFramePumpTest(
449 true, &frame_tg, &display_tg,
450 [&current_frame, desired_drop_pattern, this](
451 const scoped_refptr<VideoFrame>& frame, int frames_dropped) {
452 ASSERT_TRUE(frame);
453
454 // The first frame should have zero dropped frames, but each Render()
455 // call after should drop the same number of frames based on the
456 // fractional cadence.
457 if (!current_frame)
458 ASSERT_EQ(0, frames_dropped);
459 else
460 ASSERT_EQ(desired_drop_pattern, frames_dropped);
461
462 ASSERT_NE(current_frame, frame);
463 ASSERT_TRUE(is_using_cadence());
464 current_frame = frame;
465 });
466
467 if (HasFatalFailure())
468 return;
469 }
470 }
471 // Verify a 3:2 frame pattern for 23.974fps in 60Hz; doubles as a test for best
472 // frame by coverage.
473 TEST_F(VideoRendererAlgorithmTest, FilmCadence) {
474 const double kTestRates[] = {NTSC(24), 24};
475
476 for (double frame_rate : kTestRates) {
477 scoped_refptr<VideoFrame> current_frame;
478 int actual_frame_pattern = 0, desired_frame_pattern = 3;
479
480 TickGenerator frame_tg(base::TimeTicks(), frame_rate);
481 TickGenerator display_tg(tick_clock_->NowTicks(), 60);
482
483 RunFramePumpTest(
484 true, &frame_tg, &display_tg,
485 [&current_frame, &actual_frame_pattern, &desired_frame_pattern, this](
486 const scoped_refptr<VideoFrame>& frame, int frames_dropped) {
487 ASSERT_TRUE(frame);
488 ASSERT_EQ(0, frames_dropped);
489
490 if (!current_frame || current_frame == frame) {
491 actual_frame_pattern++;
492 } else {
493 ASSERT_EQ(actual_frame_pattern, desired_frame_pattern);
494 actual_frame_pattern = 1;
495 desired_frame_pattern = (desired_frame_pattern == 3 ? 2 : 3);
496 }
497
498 current_frame = frame;
499 ASSERT_FALSE(is_using_cadence());
500 });
501
502 if (HasFatalFailure())
503 return;
504 }
505 }
506
507 // Spot check common display and frame rate pairs for correctness.
508 TEST_F(VideoRendererAlgorithmTest, CadenceCalculations) {
509 ASSERT_FALSE(GetCadence(24, 60));
510 ASSERT_FALSE(GetCadence(NTSC(24), 60));
511 ASSERT_FALSE(GetCadence(25, 60));
512 ASSERT_EQ(2, GetCadence(NTSC(30), 60));
513 ASSERT_EQ(2, GetCadence(30, 60));
514 ASSERT_FALSE(GetCadence(50, 60));
515 ASSERT_EQ(1, GetCadence(NTSC(60), 60));
516 ASSERT_EQ(2, GetCadence(120, 60));
517
518 // 50Hz is common in the EU.
519 ASSERT_FALSE(GetCadence(NTSC(24), 50));
520 ASSERT_FALSE(GetCadence(24, 50));
521 ASSERT_EQ(2, GetCadence(NTSC(25), 50));
522 ASSERT_EQ(2, GetCadence(25, 50));
523 ASSERT_FALSE(GetCadence(NTSC(30), 50));
524 ASSERT_FALSE(GetCadence(30, 50));
525 ASSERT_FALSE(GetCadence(NTSC(60), 50));
526 ASSERT_FALSE(GetCadence(60, 50));
527
528 ASSERT_FALSE(GetCadence(25, NTSC(60)));
529 ASSERT_EQ(2, GetCadence(120, NTSC(60)));
530 ASSERT_EQ(60, GetCadence(1, NTSC(60)));
531 }
532
533 TEST_F(VideoRendererAlgorithmTest, RemoveExpiredFrames) {
534 TickGenerator tg(tick_clock_->NowTicks(), 50);
535
536 algorithm_.EnqueueFrame(CreateFrame(tg.interval(0)));
537 ASSERT_EQ(0, algorithm_.RemoveExpiredFrames(tg.current()));
538
539 time_source_.StartTicking();
540
541 int frames_dropped = 0;
542 scoped_refptr<VideoFrame> frame =
543 algorithm_.Render(tg.current(), tg.step(), &frames_dropped);
544 ASSERT_TRUE(frame);
545 EXPECT_EQ(tg.interval(0), frame->timestamp());
546 EXPECT_EQ(0, frames_dropped);
547
548 algorithm_.EnqueueFrame(CreateFrame(tg.interval(1)));
549 algorithm_.EnqueueFrame(CreateFrame(tg.interval(2)));
550 algorithm_.EnqueueFrame(CreateFrame(tg.interval(3)));
551 algorithm_.EnqueueFrame(CreateFrame(tg.interval(4)));
552
553 tg.step(2);
554 ASSERT_EQ(2, algorithm_.RemoveExpiredFrames(tg.current()));
555 frame = algorithm_.Render(tg.current(), tg.step(), &frames_dropped);
556 EXPECT_EQ(1, frames_dropped);
557 EXPECT_EQ(2u, frames_queued());
558 ASSERT_TRUE(frame);
559 EXPECT_EQ(tg.interval(3), frame->timestamp());
560 }
561
562 TEST_F(VideoRendererAlgorithmTest, CadenceBasedTest) {
563 // Common display rates.
564 const double kDisplayRates[] = {
565 NTSC(24),
566 24,
567 NTSC(25),
568 25,
569 NTSC(30),
570 30,
571 48,
572 NTSC(50),
573 50,
574 NTSC(60),
575 60,
576 75,
577 120,
578 144,
579 };
580
581 // List of common frame rate values. Values pulled from local test media,
582 // videostack test matrix, and Wikipedia.
583 const double kTestRates[] = {
584 1, 10, 12.5, 15, NTSC(24), 24, NTSC(25), 25,
585 NTSC(30), 30, 30.12, 48, NTSC(50), 50, 58.74, NTSC(60),
586 60, 72, 90, 100, 120, 144, 240, 300,
587 };
588
589 for (double display_rate : kDisplayRates) {
590 for (double frame_rate : kTestRates) {
591 TickGenerator frame_tg(base::TimeTicks(), frame_rate);
592 TickGenerator display_tg(tick_clock_->NowTicks(), display_rate);
593 RunFramePumpTest(
594 true, &frame_tg, &display_tg,
595 [](const scoped_refptr<VideoFrame>& frame, int frames_dropped) {});
596 if (HasFatalFailure())
597 return;
598 }
599 }
600 }
601
602 // Rotate through various playback rates and ensure algorithm adapts correctly.
603 TEST_F(VideoRendererAlgorithmTest, VariableFrameRateCadence) {
604 TickGenerator frame_tg(base::TimeTicks(), NTSC(30));
605 TickGenerator display_tg(tick_clock_->NowTicks(), 60);
606
607 RunFramePumpTest(
608 false, &frame_tg, &display_tg,
609 [this](const scoped_refptr<VideoFrame>& frame, int frames_dropped) {});
610 if (HasFatalFailure())
611 return;
612 ASSERT_TRUE(is_using_cadence());
613
614 time_source_.SetPlaybackRate(2);
615
616 RunFramePumpTest(
617 false, &frame_tg, &display_tg,
618 [this](const scoped_refptr<VideoFrame>& frame, int frames_dropped) {});
619 if (HasFatalFailure())
620 return;
621 ASSERT_TRUE(is_using_cadence());
622
623 time_source_.SetPlaybackRate(0.215);
624
625 RunFramePumpTest(
626 false, &frame_tg, &display_tg,
627 [this](const scoped_refptr<VideoFrame>& frame, int frames_dropped) {});
628 if (HasFatalFailure())
629 return;
630 ASSERT_FALSE(is_using_cadence());
631
632 time_source_.SetPlaybackRate(0.5);
633
634 RunFramePumpTest(
635 false, &frame_tg, &display_tg,
636 [this](const scoped_refptr<VideoFrame>& frame, int frames_dropped) {});
637 if (HasFatalFailure())
638 return;
639 ASSERT_TRUE(is_using_cadence());
640
641 time_source_.SetPlaybackRate(1.0);
642
643 RunFramePumpTest(
644 false, &frame_tg, &display_tg,
645 [this](const scoped_refptr<VideoFrame>& frame, int frames_dropped) {});
646 if (HasFatalFailure())
647 return;
648 ASSERT_TRUE(is_using_cadence());
649
650 // TODO(dalecurtis): It seems there should be some more things we can test
651 // here...
652 }
653
654 } // namespace media
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698