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

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

Powered by Google App Engine
This is Rietveld 408576698