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

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: Even moar tests. Bug fixes. 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
miu 2015/04/21 07:04:22 nit: Add more parens: #define NTSC(fps) ((fps)
DaleCurtis 2015/04/23 21:45:41 Done, as a function now, I only needed this for gl
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) {
miu 2015/04/21 07:04:22 nit: This and some of the other methods (and some
DaleCurtis 2015/04/23 21:45:41 Done.
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 int ideal_cadence() const { return algorithm_.ideal_cadence_; }
100
101 bool is_using_cadence() const { return algorithm_.ideal_cadence_ > 0; }
102
103 size_t frames_queued() const { return algorithm_.frame_queue_.size(); }
104
105 int GetCadence(double frame_rate, double display_rate) {
106 TickGenerator display_tg(tick_clock_->NowTicks(), display_rate);
107 TickGenerator frame_tg(base::TimeTicks(), frame_rate);
108 time_source_.StartTicking();
109
110 // Enqueue enough frames for cadence detection.
111 size_t frames_dropped = 0;
112 disable_cadence_hysteresis();
113 algorithm_.EnqueueFrame(CreateFrame(frame_tg.interval(0)));
114 algorithm_.EnqueueFrame(CreateFrame(frame_tg.interval(1)));
115 EXPECT_TRUE(algorithm_.Render(display_tg.current(), display_tg.step(),
116 &frames_dropped));
117
118 // Store cadence before reseting the algorithm.
119 const int cadence = algorithm_.fractional_cadence_
120 ? algorithm_.fractional_cadence_
121 : algorithm_.ideal_cadence_;
122
123 time_source_.StopTicking();
124 algorithm_.Reset();
125 return cadence;
126 }
127
128 base::TimeDelta CalculateDriftForFrame(base::TimeTicks deadline_min,
129 int frame_index) {
130 return algorithm_.CalculateDriftForFrame(deadline_min, frame_index);
131 }
132
133 bool DriftOfLastRenderWasWithinTolerance(base::TimeTicks deadline_min) {
134 return CalculateDriftForFrame(deadline_min, 0) <=
135 algorithm_.max_acceptable_drift_;
136 }
137
138 // Allows tests to run a Render() loop with sufficient frames for the various
139 // rendering modes. Upon each Render() |render_test_func| will be called with
140 // the rendered frame and the number of frames dropped.
141 template <typename OnRenderCallback>
miu 2015/04/21 07:04:22 Instead of templating, consider passing the 4th ar
xhwang 2015/04/22 06:11:38 C++11 library features are not allowed yet: https:
142 void RunFramePumpTest(bool reset,
143 TickGenerator* frame_tg,
144 TickGenerator* display_tg,
145 OnRenderCallback render_test_func) {
146 SCOPED_TRACE(base::StringPrintf("Rendering %.03f fps into %0.03f",
147 frame_tg->hertz(), display_tg->hertz()));
148 tick_clock_->Advance(display_tg->current() - tick_clock_->NowTicks());
149 time_source_.StartTicking();
150
151 const bool fresh_algorithm = !algorithm_.have_rendered_frames_;
152
153 base::TimeDelta last_frame_timestamp = kNoTimestamp();
154 bool should_use_cadence = false;
155 int glitch_count = 0;
156 const base::TimeTicks start_time = tick_clock_->NowTicks();
157 while (tick_clock_->NowTicks() - start_time < minimum_glitch_time()) {
158 while (algorithm_.EffectiveFramesQueued() < 3 ||
159 frame_tg->current() - time_source_.CurrentMediaTime() <
160 base::TimeTicks()) {
161 algorithm_.EnqueueFrame(
162 CreateFrame(frame_tg->current() - base::TimeTicks()));
163 frame_tg->step();
164 }
165
166 size_t frames_dropped = 0;
167 const base::TimeTicks deadline_min = display_tg->current();
168 const base::TimeTicks deadline_max = display_tg->step();
169 scoped_refptr<VideoFrame> frame =
170 algorithm_.Render(deadline_min, deadline_max, &frames_dropped);
171
172 render_test_func(frame, frames_dropped);
173 tick_clock_->Advance(display_tg->current() - tick_clock_->NowTicks());
174
175 if (HasFatalFailure())
176 return;
177
178 // Render() should always return a frame within drift tolerances.
179 ASSERT_TRUE(DriftOfLastRenderWasWithinTolerance(deadline_min));
180
181 // If we have a frame, the timestamps should always be monotonically
182 // increasing.
183 if (frame) {
184 if (last_frame_timestamp != kNoTimestamp())
185 ASSERT_LE(last_frame_timestamp, frame->timestamp());
186 else
187 last_frame_timestamp = frame->timestamp();
188 }
189
190 // Only verify certain properties for fresh instances.
191 if (fresh_algorithm) {
192 ASSERT_EQ(frame_tg->interval(1), algorithm_.average_frame_duration());
193
194 if (is_using_cadence() && last_render_had_glitch())
195 ++glitch_count;
196
197 // Once cadence starts, it should never stop for the current set of
198 // tests.
199 if (is_using_cadence())
200 should_use_cadence = true;
201 ASSERT_EQ(is_using_cadence(), should_use_cadence);
202 }
203
204 // When there are no frames, we're not using cadence based selection, or
205 // a frame is under cadence the two queue size reports should be the same.
206 if (!is_using_cadence() || !frames_queued() ||
207 GetCurrentFrameDisplayCount() < ideal_cadence()) {
208 ASSERT_EQ(frames_queued(), algorithm_.EffectiveFramesQueued());
209 } else if (!algorithm_.fractional_cadence_) {
210 // If there was no glitch in the last render, the two queue sizes should
211 // be off by exactly one frame; i.e., the current frame doesn't count.
212 if (!last_render_had_glitch())
213 ASSERT_EQ(frames_queued() - 1, algorithm_.EffectiveFramesQueued());
214 } else {
215 // The frame estimate should be off by at most one frame.
216 size_t estimated_frames_queued =
217 frames_queued() / algorithm_.fractional_cadence_;
218 ASSERT_NEAR(algorithm_.EffectiveFramesQueued(), estimated_frames_queued,
219 1);
220 }
221 }
222
223 // When using cadence, the glitch count should be at most one for when
224 // rendering for the less than minimum_glitch_time().
225 if (fresh_algorithm && is_using_cadence())
226 ASSERT_LE(glitch_count, 1);
227
228 time_source_.StopTicking();
229 if (reset) {
230 algorithm_.Reset();
231 time_source_.SetMediaTime(base::TimeDelta());
232 }
233 }
234
235 int FindBestFrameByCoverage(base::TimeTicks deadline_min,
236 base::TimeTicks deadline_max,
237 int* second_best) {
238 return algorithm_.FindBestFrameByCoverage(deadline_min, deadline_max,
239 second_best);
240 }
241
242 int FindBestFrameByDrift(base::TimeTicks deadline_min) {
243 return algorithm_.FindBestFrameByDrift(deadline_min);
244 }
245
246 int GetCurrentFrameDisplayCount() const {
247 DCHECK_GT(frames_queued(), 0u);
248 return algorithm_.frame_queue_[algorithm_.last_frame_index_].render_count;
249 }
250
251 int AccountForMissedIntervals(base::TimeTicks deadline_min,
252 base::TimeTicks deadline_max) {
253 algorithm_.AccountForMissedIntervals(deadline_min, deadline_max);
254 return algorithm_.frames_queued() ? GetCurrentFrameDisplayCount() : -1;
255 }
256
257 protected:
258 VideoFramePool frame_pool_;
259 WallClockTimeSource time_source_;
260 base::SimpleTestTickClock* tick_clock_; // Owned by |time_source_|.
261 VideoRendererAlgorithm algorithm_;
262
263 private:
264 DISALLOW_COPY_AND_ASSIGN(VideoRendererAlgorithmTest);
265 };
266
267 TEST_F(VideoRendererAlgorithmTest, Empty) {
268 TickGenerator tg(tick_clock_->NowTicks(), 50);
269 size_t frames_dropped = 0;
270 EXPECT_EQ(0u, frames_queued());
271 EXPECT_FALSE(algorithm_.Render(tg.current(), tg.step(), &frames_dropped));
272 EXPECT_EQ(0u, frames_dropped);
273 EXPECT_EQ(0u, frames_queued());
274 EXPECT_NE(base::TimeDelta(), max_acceptable_drift());
275 }
276
277 TEST_F(VideoRendererAlgorithmTest, Reset) {
278 TickGenerator tg(tick_clock_->NowTicks(), 50);
279 algorithm_.EnqueueFrame(CreateFrame(tg.interval(0)));
280 EXPECT_EQ(1u, frames_queued());
281 EXPECT_NE(base::TimeDelta(), max_acceptable_drift());
282 algorithm_.Reset();
283 EXPECT_EQ(0u, frames_queued());
284 EXPECT_NE(base::TimeDelta(), max_acceptable_drift());
285 }
286
287 TEST_F(VideoRendererAlgorithmTest, AccountForMissingIntervals) {
288 TickGenerator tg(tick_clock_->NowTicks(), 50);
289 time_source_.StartTicking();
290
291 // Disable hysteresis since AccountForMissingIntervals() only affects cadence
292 // based rendering.
293 disable_cadence_hysteresis();
294
295 // Simulate Render() called before any frames are present.
296 EXPECT_EQ(-1, AccountForMissedIntervals(tg.current(), tg.step()));
297
298 algorithm_.EnqueueFrame(CreateFrame(tg.interval(0)));
299 algorithm_.EnqueueFrame(CreateFrame(tg.interval(1)));
300 algorithm_.EnqueueFrame(CreateFrame(tg.interval(2)));
301 algorithm_.EnqueueFrame(CreateFrame(tg.interval(3)));
302
303 // Simulate Render() called before any frames have been rendered.
304 EXPECT_EQ(0, AccountForMissedIntervals(tg.current(), tg.step()));
305
306 // Render one frame (several are in the past and will be dropped).
307 base::TimeTicks deadline_min = tg.current();
308 base::TimeTicks deadline_max = tg.step();
309 size_t frames_dropped = 0;
310 scoped_refptr<VideoFrame> frame =
311 algorithm_.Render(deadline_min, deadline_max, &frames_dropped);
312 ASSERT_TRUE(frame);
313 EXPECT_EQ(tg.interval(2), frame->timestamp());
314 EXPECT_EQ(2u, frames_dropped);
315
316 ASSERT_EQ(1, GetCurrentFrameDisplayCount());
317
318 // Now calling AccountForMissingIntervals with the an interval which overlaps
xhwang 2015/04/22 06:11:38 s/the//
DaleCurtis 2015/04/23 21:45:41 Done.
319 // the previous should do nothing.
320 deadline_min += tg.interval(1) / 2;
321 deadline_max += tg.interval(1) / 2;
322 EXPECT_EQ(1, AccountForMissedIntervals(deadline_min, deadline_max));
323
324 // Steping by 1.5 intervals, is not enough to increase the count.
325 deadline_min += tg.interval(1);
326 deadline_max += tg.interval(1);
327 EXPECT_EQ(1, AccountForMissedIntervals(deadline_min, deadline_max));
328
329 // Calling it after a full skipped interval should increase the count by 1 for
330 // each skipped interval.
331 tg.step();
332 EXPECT_EQ(2, AccountForMissedIntervals(tg.current(), tg.step()));
333
334 // 4 because [tg.current(), tg.step()] now represents 2 additional intervals.
335 EXPECT_EQ(4, AccountForMissedIntervals(tg.current(), tg.step()));
336
337 // Frame should be way over cadence and no good frames remain, so last frame
338 // should be returned.
339 frame = algorithm_.Render(tg.current(), tg.step(), &frames_dropped);
340 ASSERT_TRUE(frame);
341 EXPECT_EQ(tg.interval(3), frame->timestamp());
342 EXPECT_EQ(0u, frames_dropped);
343 }
344
345 TEST_F(VideoRendererAlgorithmTest, OnLastFrameDropped) {
346 TickGenerator frame_tg(base::TimeTicks(), 25);
347 TickGenerator display_tg(tick_clock_->NowTicks(), 50);
348 time_source_.StartTicking();
349
350 // Disable hysteresis since OnLastFrameDropped() only affects cadence based
351 // rendering.
352 disable_cadence_hysteresis();
353
354 algorithm_.EnqueueFrame(CreateFrame(frame_tg.interval(0)));
355 algorithm_.EnqueueFrame(CreateFrame(frame_tg.interval(1)));
356 algorithm_.EnqueueFrame(CreateFrame(frame_tg.interval(2)));
357
358 // Render one frame (several are in the past and will be dropped).
359 size_t frames_dropped = 0;
360 scoped_refptr<VideoFrame> frame = algorithm_.Render(
361 display_tg.current(), display_tg.step(), &frames_dropped);
362 ASSERT_TRUE(frame);
363 EXPECT_EQ(frame_tg.interval(0), frame->timestamp());
364 EXPECT_EQ(0u, frames_dropped);
365
366 // The frame should have its display count decremented once it's reported as
367 // dropped.
368 ASSERT_EQ(1, GetCurrentFrameDisplayCount());
369 algorithm_.OnLastFrameDropped();
370 ASSERT_EQ(0, GetCurrentFrameDisplayCount());
371
372 // Render the frame again and then force another drop.
373 frame = algorithm_.Render(display_tg.current(), display_tg.step(),
374 &frames_dropped);
375 ASSERT_TRUE(frame);
376 EXPECT_EQ(frame_tg.interval(0), frame->timestamp());
377 EXPECT_EQ(0u, frames_dropped);
378
379 ASSERT_EQ(1, GetCurrentFrameDisplayCount());
380 algorithm_.OnLastFrameDropped();
381 ASSERT_EQ(0, GetCurrentFrameDisplayCount());
382
383 // The next Render() call should now count this frame as dropped.
384 frame = algorithm_.Render(display_tg.current(), display_tg.step(),
385 &frames_dropped);
386 ASSERT_TRUE(frame);
387 EXPECT_EQ(frame_tg.interval(1), frame->timestamp());
388 EXPECT_EQ(1u, frames_dropped);
389
390 // Rendering again should result in the same frame being displayed.
391 frame = algorithm_.Render(display_tg.current(), display_tg.step(),
392 &frames_dropped);
393 ASSERT_TRUE(frame);
394 EXPECT_EQ(frame_tg.interval(1), frame->timestamp());
395 EXPECT_EQ(0u, frames_dropped);
396
397 // The display count for this frame is now two, so OnLastFrameDropped() should
398 // not change its value if its called again (we assume it will be repeated if
399 // any glitches occur).
400 ASSERT_EQ(2, GetCurrentFrameDisplayCount());
401 algorithm_.OnLastFrameDropped();
402 ASSERT_EQ(2, GetCurrentFrameDisplayCount());
403
404 // The third frame should be rendered correctly now and the previous frame not
405 // counted as having been dropped.
406 frame = algorithm_.Render(display_tg.current(), display_tg.step(),
407 &frames_dropped);
408 ASSERT_TRUE(frame);
409 EXPECT_EQ(frame_tg.interval(2), frame->timestamp());
410 EXPECT_EQ(0u, frames_dropped);
411 }
412
413 TEST_F(VideoRendererAlgorithmTest, EffectiveFramesQueued) {
414 TickGenerator frame_tg(base::TimeTicks(), 50);
415 TickGenerator display_tg(tick_clock_->NowTicks(), 25);
416
417 // Disable hysteresis since EffectiveFramesQueued() is tested as part of the
418 // normal frame pump tests when cadence is not present.
419 disable_cadence_hysteresis();
420
421 EXPECT_EQ(0u, algorithm_.EffectiveFramesQueued());
422 time_source_.StartTicking();
423
424 algorithm_.EnqueueFrame(CreateFrame(frame_tg.interval(0)));
425 EXPECT_EQ(1u, algorithm_.EffectiveFramesQueued());
426
427 algorithm_.EnqueueFrame(CreateFrame(frame_tg.interval(1)));
428 EXPECT_EQ(2u, algorithm_.EffectiveFramesQueued());
429
430 algorithm_.EnqueueFrame(CreateFrame(frame_tg.interval(2)));
431 EXPECT_EQ(3u, algorithm_.EffectiveFramesQueued());
432
433 algorithm_.EnqueueFrame(CreateFrame(frame_tg.interval(3)));
434 EXPECT_EQ(4u, algorithm_.EffectiveFramesQueued());
435 EXPECT_EQ(4u, algorithm_.frames_queued());
436
437 // Render one frame which will detect cadence...
438 size_t frames_dropped = 0;
439 scoped_refptr<VideoFrame> frame = algorithm_.Render(
440 display_tg.current(), display_tg.step(), &frames_dropped);
441 ASSERT_TRUE(frame);
442 EXPECT_EQ(frame_tg.interval(0), frame->timestamp());
443 EXPECT_EQ(0u, frames_dropped);
444
445 // Fractional cadence should be detected and the count will decrease.
446 ASSERT_TRUE(is_using_cadence());
447 EXPECT_EQ(1u, algorithm_.EffectiveFramesQueued());
448 EXPECT_EQ(4u, algorithm_.frames_queued());
449
450 // Dropping the last rendered frame should do nothing, since the last frame
451 // is already excluded from the count if it has a display count of 1.
452 algorithm_.OnLastFrameDropped();
453 EXPECT_EQ(1u, algorithm_.EffectiveFramesQueued());
454 }
455
456 // The maximum acceptable drift should be updated once we have two frames.
457 TEST_F(VideoRendererAlgorithmTest, AcceptableDriftUpdated) {
458 TickGenerator tg(tick_clock_->NowTicks(), 50);
459
460 size_t frames_dropped = 0;
461 const base::TimeDelta original_drift = max_acceptable_drift();
462 algorithm_.EnqueueFrame(CreateFrame(tg.interval(0)));
463 EXPECT_EQ(1u, frames_queued());
464 EXPECT_TRUE(algorithm_.Render(tg.current(), tg.step(), &frames_dropped));
465 EXPECT_EQ(original_drift, max_acceptable_drift());
466
467 // Time must be ticking to get wall clock times for frames.
468 time_source_.StartTicking();
469
470 algorithm_.EnqueueFrame(CreateFrame(tg.interval(1)));
471 EXPECT_EQ(2u, frames_queued());
472 EXPECT_TRUE(algorithm_.Render(tg.current(), tg.step(), &frames_dropped));
473 EXPECT_NE(original_drift, max_acceptable_drift());
474 }
475
476 // Verifies behavior when time stops.
477 TEST_F(VideoRendererAlgorithmTest, TimeIsStopped) {
478 TickGenerator tg(tick_clock_->NowTicks(), 50);
479
480 // Prior to rendering the first frame, the algorithm should always return the
481 // first available frame.
482 size_t frames_dropped = 0;
483 algorithm_.EnqueueFrame(CreateFrame(tg.interval(0)));
484 EXPECT_EQ(1u, frames_queued());
485 scoped_refptr<VideoFrame> frame =
486 algorithm_.Render(tg.current(), tg.step(), &frames_dropped);
487 ASSERT_TRUE(frame);
488 EXPECT_EQ(tg.interval(0), frame->timestamp());
489 EXPECT_EQ(0u, frames_dropped);
490 EXPECT_EQ(1u, frames_queued());
491 EXPECT_EQ(1u, algorithm_.EffectiveFramesQueued());
492
493 // The same timestamp should be returned after time starts.
494 tick_clock_->Advance(tg.interval(1));
495 time_source_.StartTicking();
496 frame = algorithm_.Render(tg.current(), tg.step(), &frames_dropped);
497 ASSERT_TRUE(frame);
498 EXPECT_EQ(tg.interval(0), frame->timestamp());
499 EXPECT_EQ(0u, frames_dropped);
500 EXPECT_EQ(1u, frames_queued());
501 EXPECT_EQ(1u, algorithm_.EffectiveFramesQueued());
502
503 // Ensure the next suitable frame is vended as time advances.
504 algorithm_.EnqueueFrame(CreateFrame(tg.interval(1)));
505 EXPECT_EQ(2u, frames_queued());
506 EXPECT_EQ(2u, algorithm_.EffectiveFramesQueued());
507 frame = algorithm_.Render(tg.current(), tg.step(), &frames_dropped);
508 ASSERT_TRUE(frame);
509 EXPECT_EQ(tg.interval(1), frame->timestamp());
510 EXPECT_EQ(0u, frames_dropped);
511 EXPECT_EQ(1u, frames_queued());
512 EXPECT_EQ(1u, algorithm_.EffectiveFramesQueued());
513
514 // Once time stops ticking, any further frames shouldn't be returned, even if
515 // the interval requested more closely matches.
516 algorithm_.EnqueueFrame(CreateFrame(tg.interval(2)));
517 time_source_.StopTicking();
518 frame = algorithm_.Render(tg.current(), tg.step(), &frames_dropped);
519 ASSERT_TRUE(frame);
520 EXPECT_EQ(tg.interval(1), frame->timestamp());
521 EXPECT_EQ(0u, frames_dropped);
522 EXPECT_EQ(2u, frames_queued());
523 EXPECT_EQ(2u, algorithm_.EffectiveFramesQueued());
524 }
525
526 // Verify frames inserted out of order end up in the right spot and are rendered
527 // according to the API contract.
528 TEST_F(VideoRendererAlgorithmTest, SortedFrameQueue) {
529 TickGenerator tg(tick_clock_->NowTicks(), 50);
530
531 // Ensure frames handed in out of order before time starts ticking are sorted
532 // and returned in the correct order upon Render().
533 algorithm_.EnqueueFrame(CreateFrame(tg.interval(3)));
534 algorithm_.EnqueueFrame(CreateFrame(tg.interval(2)));
535 EXPECT_EQ(2u, frames_queued());
536 EXPECT_EQ(2u, algorithm_.EffectiveFramesQueued());
537
538 time_source_.StartTicking();
539
540 // The first call should return the earliest frame appended.
541 size_t frames_dropped = 0;
542 scoped_refptr<VideoFrame> frame =
543 algorithm_.Render(tg.current(), tg.step(), &frames_dropped);
544 EXPECT_EQ(0u, frames_dropped);
545 EXPECT_EQ(tg.interval(2), frame->timestamp());
546 EXPECT_EQ(2u, frames_queued());
547 EXPECT_EQ(2u, algorithm_.EffectiveFramesQueued());
548
549 // Since a frame has already been rendered, queuing this frame and calling
550 // Render() should result in it being dropped; even though it's a better
551 // candidate for the desired interval.
552 algorithm_.EnqueueFrame(CreateFrame(tg.interval(1)));
553 EXPECT_EQ(3u, frames_queued());
554 EXPECT_EQ(2u, algorithm_.EffectiveFramesQueued());
555 frame = algorithm_.Render(tg.current(), tg.step(), &frames_dropped);
556 EXPECT_EQ(1u, frames_dropped);
557 EXPECT_EQ(tg.interval(2), frame->timestamp());
558 EXPECT_EQ(2u, frames_queued());
559 EXPECT_EQ(2u, algorithm_.EffectiveFramesQueued());
560 }
561
562 // Run through integer cadence selection for 1, 2, 3, and 4.
563 TEST_F(VideoRendererAlgorithmTest, BestFrameByCadence) {
564 const double kTestRates[][2] = {{60, 60}, {30, 60}, {25, 75}, {25, 100}};
565
566 for (const auto& test_rate : kTestRates) {
567 disable_cadence_hysteresis();
568
569 TickGenerator frame_tg(base::TimeTicks(), test_rate[0]);
570 TickGenerator display_tg(tick_clock_->NowTicks(), test_rate[1]);
571
572 int actual_frame_pattern = 0;
573 const int desired_frame_pattern = test_rate[1] / test_rate[0];
574 scoped_refptr<VideoFrame> current_frame;
575 RunFramePumpTest(
576 true, &frame_tg, &display_tg,
577 [&current_frame, &actual_frame_pattern, desired_frame_pattern, this](
578 const scoped_refptr<VideoFrame>& frame, size_t frames_dropped) {
579 ASSERT_TRUE(frame);
580 ASSERT_EQ(0u, frames_dropped);
581
582 // Each frame should display for exactly it's desired cadence pattern.
583 if (!current_frame || current_frame == frame) {
584 actual_frame_pattern++;
585 } else {
586 ASSERT_EQ(actual_frame_pattern, desired_frame_pattern);
587 actual_frame_pattern = 1;
588 }
589
590 current_frame = frame;
591 ASSERT_TRUE(is_using_cadence());
592 });
593
594 if (HasFatalFailure())
595 return;
596 }
597 }
598
599 TEST_F(VideoRendererAlgorithmTest, BestFrameByCadenceOverdisplayed) {
600 TickGenerator frame_tg(base::TimeTicks(), 25);
601 TickGenerator display_tg(tick_clock_->NowTicks(), 50);
602 time_source_.StartTicking();
603 disable_cadence_hysteresis();
604
605 algorithm_.EnqueueFrame(CreateFrame(frame_tg.interval(0)));
606 algorithm_.EnqueueFrame(CreateFrame(frame_tg.interval(1)));
607
608 // Render frames until we've exhausted available frames and the last frame is
609 // forced to be overdisplayed.
610 for (int i = 0; i < 5; ++i) {
611 size_t frames_dropped = 0;
612 scoped_refptr<VideoFrame> frame = algorithm_.Render(
613 display_tg.current(), display_tg.step(), &frames_dropped);
614 ASSERT_TRUE(frame);
615 EXPECT_EQ(frame_tg.interval(i < 4 ? i / 2 : 1), frame->timestamp());
616 EXPECT_EQ(0u, frames_dropped);
617 ASSERT_EQ(2, ideal_cadence());
618 }
619
620 // Verify last frame is above cadence (2 in this case)
621 ASSERT_EQ(ideal_cadence() + 1, GetCurrentFrameDisplayCount());
622 algorithm_.EnqueueFrame(CreateFrame(frame_tg.interval(2)));
623 algorithm_.EnqueueFrame(CreateFrame(frame_tg.interval(3)));
624
625 // The next frame should only be displayed once, since the previous one was
626 // overdisplayed by one frame.
627 size_t frames_dropped = 0;
628 scoped_refptr<VideoFrame> frame = algorithm_.Render(
629 display_tg.current(), display_tg.step(), &frames_dropped);
630 ASSERT_TRUE(frame);
631 EXPECT_EQ(frame_tg.interval(2), frame->timestamp());
632 EXPECT_EQ(0u, frames_dropped);
633 ASSERT_EQ(2, ideal_cadence());
634
635 frame = algorithm_.Render(display_tg.current(), display_tg.step(),
636 &frames_dropped);
637 ASSERT_TRUE(frame);
638 EXPECT_EQ(frame_tg.interval(3), frame->timestamp());
639 EXPECT_EQ(0u, frames_dropped);
640 ASSERT_EQ(2, ideal_cadence());
641 }
642
643 TEST_F(VideoRendererAlgorithmTest, BestFrameByCoverage) {
644 TickGenerator tg(tick_clock_->NowTicks(), 50);
645 time_source_.StartTicking();
646
647 algorithm_.EnqueueFrame(CreateFrame(tg.interval(0)));
648 algorithm_.EnqueueFrame(CreateFrame(tg.interval(1)));
649 algorithm_.EnqueueFrame(CreateFrame(tg.interval(2)));
650
651 base::TimeTicks deadline_min = tg.current();
652 base::TimeTicks deadline_max = deadline_min + tg.interval(1);
653
654 size_t frames_dropped = 0;
655 scoped_refptr<VideoFrame> frame =
656 algorithm_.Render(deadline_min, deadline_max, &frames_dropped);
657 ASSERT_TRUE(frame);
658 EXPECT_EQ(tg.interval(0), frame->timestamp());
659 EXPECT_EQ(0u, frames_dropped);
660
661 int second_best = 0;
662
663 // Coverage should be 1 for if the frame overlaps the interval entirely, no
664 // second best should be found.
665 EXPECT_EQ(0,
666 FindBestFrameByCoverage(deadline_min, deadline_max, &second_best));
667 EXPECT_EQ(-1, second_best);
668
669 // 49/51 coverage for frame 0 and frame 1 should be within tolerance such that
670 // the earlier frame should still be chosen.
671 deadline_min = tg.current() + tg.interval(1) / 2 +
672 base::TimeDelta::FromMicroseconds(250);
673 deadline_max = deadline_min + tg.interval(1);
674 EXPECT_EQ(0,
675 FindBestFrameByCoverage(deadline_min, deadline_max, &second_best));
676 EXPECT_EQ(1, second_best);
677
678 // 48/52 coverage should result in the second frame being chosen.
679 deadline_min = tg.current() + tg.interval(1) / 2 +
680 base::TimeDelta::FromMicroseconds(500);
681 deadline_max = deadline_min + tg.interval(1);
682 EXPECT_EQ(1,
683 FindBestFrameByCoverage(deadline_min, deadline_max, &second_best));
684 EXPECT_EQ(0, second_best);
685
686 // Overlapping three frames should choose the one with the most coverage and
687 // the second best should be the earliest frame.
688 deadline_min = tg.current() + tg.interval(1) / 2;
689 deadline_max = deadline_min + tg.interval(2);
690 EXPECT_EQ(1,
691 FindBestFrameByCoverage(deadline_min, deadline_max, &second_best));
692 EXPECT_EQ(0, second_best);
693
694 // Requesting coverage outside of all known frames should return -1 for both
695 // best indices.
696 deadline_min = tg.current() + tg.interval(frames_queued());
697 deadline_max = deadline_min + tg.interval(1);
698 EXPECT_EQ(-1,
699 FindBestFrameByCoverage(deadline_min, deadline_max, &second_best));
700 EXPECT_EQ(-1, second_best);
701 }
702
703 TEST_F(VideoRendererAlgorithmTest, BestFrameByDriftAndDriftCalculations) {
704 TickGenerator tg(tick_clock_->NowTicks(), 50);
705 time_source_.StartTicking();
706
707 algorithm_.EnqueueFrame(CreateFrame(tg.interval(0)));
708 algorithm_.EnqueueFrame(CreateFrame(tg.interval(1)));
709
710 size_t frames_dropped = 0;
711 scoped_refptr<VideoFrame> frame = algorithm_.Render(
712 tg.current(), tg.current() + tg.interval(1), &frames_dropped);
713 ASSERT_TRUE(frame);
714 EXPECT_EQ(tg.interval(0), frame->timestamp());
715 EXPECT_EQ(0u, frames_dropped);
716
717 base::TimeDelta zero_drift, half_drift = tg.interval(1) / 2;
718
719 // Frame_0 overlaps the deadline, Frame_1 is a full interval away.
720 base::TimeTicks deadline = tg.current();
721 EXPECT_EQ(zero_drift, CalculateDriftForFrame(deadline, 0));
722 EXPECT_EQ(tg.interval(1), CalculateDriftForFrame(deadline, 1));
723 EXPECT_EQ(0, FindBestFrameByDrift(deadline));
724
725 // Frame_0 overlaps the deadline, Frame_1 is a half interval away.
726 deadline += half_drift;
727 EXPECT_EQ(zero_drift, CalculateDriftForFrame(deadline, 0));
728 EXPECT_EQ(half_drift, CalculateDriftForFrame(deadline, 1));
729 EXPECT_EQ(0, FindBestFrameByDrift(deadline));
730
731 // Both frames overlap the deadline.
732 deadline += half_drift;
733 EXPECT_EQ(zero_drift, CalculateDriftForFrame(deadline, 0));
734 EXPECT_EQ(zero_drift, CalculateDriftForFrame(deadline, 1));
735 EXPECT_EQ(1, FindBestFrameByDrift(deadline));
736
737 // Frame_0 is half an interval away, Frame_1 overlaps the deadline.
738 deadline += half_drift;
739 EXPECT_EQ(half_drift, CalculateDriftForFrame(deadline, 0));
740 EXPECT_EQ(zero_drift, CalculateDriftForFrame(deadline, 1));
741 EXPECT_EQ(1, FindBestFrameByDrift(deadline));
742
743 // Frame_0 is a full interval away, Frame_1 overlaps the deadline.
744 deadline += half_drift;
745 EXPECT_EQ(tg.interval(1), CalculateDriftForFrame(deadline, 0));
746 EXPECT_EQ(zero_drift, CalculateDriftForFrame(deadline, 1));
747 EXPECT_EQ(1, FindBestFrameByDrift(deadline));
748
749 // Both frames are entirely before the deadline.
750 deadline += half_drift;
751 EXPECT_EQ(tg.interval(1) + half_drift, CalculateDriftForFrame(deadline, 0));
752 EXPECT_EQ(half_drift, CalculateDriftForFrame(deadline, 1));
753 EXPECT_EQ(1, FindBestFrameByDrift(deadline));
754 }
755
756 // Run through fractional cadence selection for 1/2, 1/3, and 1/4.
757 TEST_F(VideoRendererAlgorithmTest, BestFrameByFractionalCadence) {
758 const double kTestRates[][2] = {{120, 60}, {72, 24}, {100, 25}};
759
760 for (const auto& test_rate : kTestRates) {
761 disable_cadence_hysteresis();
762
763 TickGenerator frame_tg(base::TimeTicks(), test_rate[0]);
764 TickGenerator display_tg(tick_clock_->NowTicks(), test_rate[1]);
765
766 const size_t desired_drop_pattern = test_rate[0] / test_rate[1] - 1;
767 scoped_refptr<VideoFrame> current_frame;
768 RunFramePumpTest(
769 true, &frame_tg, &display_tg,
770 [&current_frame, desired_drop_pattern, this](
771 const scoped_refptr<VideoFrame>& frame, size_t frames_dropped) {
772 ASSERT_TRUE(frame);
773
774 // The first frame should have zero dropped frames, but each Render()
775 // call after should drop the same number of frames based on the
776 // fractional cadence.
777 if (!current_frame)
778 ASSERT_EQ(0u, frames_dropped);
779 else
780 ASSERT_EQ(desired_drop_pattern, frames_dropped);
781
782 ASSERT_NE(current_frame, frame);
783 ASSERT_TRUE(is_using_cadence());
784 current_frame = frame;
785 });
786
787 if (HasFatalFailure())
788 return;
789 }
790 }
791 // Verify a 3:2 frame pattern for 23.974fps in 60Hz; doubles as a test for best
792 // frame by coverage.
793 TEST_F(VideoRendererAlgorithmTest, FilmCadence) {
794 const double kTestRates[] = {NTSC(24), 24};
795
796 for (double frame_rate : kTestRates) {
797 scoped_refptr<VideoFrame> current_frame;
798 int actual_frame_pattern = 0, desired_frame_pattern = 3;
799
800 TickGenerator frame_tg(base::TimeTicks(), frame_rate);
801 TickGenerator display_tg(tick_clock_->NowTicks(), 60);
802
803 RunFramePumpTest(
804 true, &frame_tg, &display_tg,
805 [&current_frame, &actual_frame_pattern, &desired_frame_pattern, this](
806 const scoped_refptr<VideoFrame>& frame, size_t frames_dropped) {
807 ASSERT_TRUE(frame);
808 ASSERT_EQ(0u, frames_dropped);
809
810 if (!current_frame || current_frame == frame) {
811 actual_frame_pattern++;
812 } else {
813 ASSERT_EQ(actual_frame_pattern, desired_frame_pattern);
814 actual_frame_pattern = 1;
815 desired_frame_pattern = (desired_frame_pattern == 3 ? 2 : 3);
816 }
817
818 current_frame = frame;
xhwang 2015/04/22 06:11:38 style nit: "Keep unnamed lambdas short. If a lam
DaleCurtis 2015/04/23 21:45:41 - whitespace and comments it's only 8 lines... clo
819 ASSERT_FALSE(is_using_cadence());
820 });
821
822 if (HasFatalFailure())
823 return;
824 }
825 }
826
827 // Spot check common display and frame rate pairs for correctness.
828 TEST_F(VideoRendererAlgorithmTest, CadenceCalculations) {
829 ASSERT_FALSE(GetCadence(24, 60));
830 ASSERT_FALSE(GetCadence(NTSC(24), 60));
831 ASSERT_FALSE(GetCadence(25, 60));
832 ASSERT_EQ(2, GetCadence(NTSC(30), 60));
833 ASSERT_EQ(2, GetCadence(30, 60));
834 ASSERT_FALSE(GetCadence(50, 60));
835 ASSERT_EQ(1, GetCadence(NTSC(60), 60));
836 ASSERT_EQ(2, GetCadence(120, 60));
837
838 // 50Hz is common in the EU.
839 ASSERT_FALSE(GetCadence(NTSC(24), 50));
840 ASSERT_FALSE(GetCadence(24, 50));
841 ASSERT_EQ(2, GetCadence(NTSC(25), 50));
842 ASSERT_EQ(2, GetCadence(25, 50));
843 ASSERT_FALSE(GetCadence(NTSC(30), 50));
844 ASSERT_FALSE(GetCadence(30, 50));
845 ASSERT_FALSE(GetCadence(NTSC(60), 50));
846 ASSERT_FALSE(GetCadence(60, 50));
847
848 ASSERT_FALSE(GetCadence(25, NTSC(60)));
849 ASSERT_EQ(2, GetCadence(120, NTSC(60)));
850 ASSERT_EQ(60, GetCadence(1, NTSC(60)));
851 }
852
853 TEST_F(VideoRendererAlgorithmTest, RemoveExpiredFrames) {
854 TickGenerator tg(tick_clock_->NowTicks(), 50);
855
856 algorithm_.EnqueueFrame(CreateFrame(tg.interval(0)));
857 ASSERT_EQ(0u, algorithm_.RemoveExpiredFrames(tg.current()));
858 EXPECT_EQ(1u, algorithm_.EffectiveFramesQueued());
859
860 time_source_.StartTicking();
861
862 size_t frames_dropped = 0;
863 scoped_refptr<VideoFrame> frame =
864 algorithm_.Render(tg.current(), tg.step(), &frames_dropped);
865 ASSERT_TRUE(frame);
866 EXPECT_EQ(tg.interval(0), frame->timestamp());
867 EXPECT_EQ(0u, frames_dropped);
868
869 algorithm_.EnqueueFrame(CreateFrame(tg.interval(1)));
870 algorithm_.EnqueueFrame(CreateFrame(tg.interval(2)));
871 algorithm_.EnqueueFrame(CreateFrame(tg.interval(3)));
872 algorithm_.EnqueueFrame(CreateFrame(tg.interval(4)));
873 EXPECT_EQ(5u, algorithm_.EffectiveFramesQueued());
874
875 tg.step(2);
876 ASSERT_EQ(2u, algorithm_.RemoveExpiredFrames(tg.current()));
877 frame = algorithm_.Render(tg.current(), tg.step(), &frames_dropped);
878 EXPECT_EQ(1u, frames_dropped);
879 EXPECT_EQ(2u, frames_queued());
880 EXPECT_EQ(2u, algorithm_.EffectiveFramesQueued());
881 ASSERT_TRUE(frame);
882 EXPECT_EQ(tg.interval(3), frame->timestamp());
883 }
884
885 TEST_F(VideoRendererAlgorithmTest, CadenceBasedTest) {
886 // Common display rates.
887 const double kDisplayRates[] = {
888 NTSC(24),
889 24,
890 NTSC(25),
891 25,
892 NTSC(30),
893 30,
894 48,
895 NTSC(50),
896 50,
897 NTSC(60),
898 60,
899 75,
900 120,
901 144,
902 };
903
904 // List of common frame rate values. Values pulled from local test media,
905 // videostack test matrix, and Wikipedia.
906 const double kTestRates[] = {
907 1, 10, 12.5, 15, NTSC(24), 24, NTSC(25), 25,
908 NTSC(30), 30, 30.12, 48, NTSC(50), 50, 58.74, NTSC(60),
909 60, 72, 90, 100, 120, 144, 240, 300,
910 };
911
912 for (double display_rate : kDisplayRates) {
913 for (double frame_rate : kTestRates) {
914 TickGenerator frame_tg(base::TimeTicks(), frame_rate);
915 TickGenerator display_tg(tick_clock_->NowTicks(), display_rate);
916 RunFramePumpTest(
917 true, &frame_tg, &display_tg,
918 [](const scoped_refptr<VideoFrame>& frame, size_t frames_dropped) {});
919 if (HasFatalFailure())
920 return;
921 }
922 }
923 }
924
925 // Rotate through various playback rates and ensure algorithm adapts correctly.
926 TEST_F(VideoRendererAlgorithmTest, VariableFrameRateCadence) {
927 TickGenerator frame_tg(base::TimeTicks(), NTSC(30));
928 TickGenerator display_tg(tick_clock_->NowTicks(), 60);
929
930 const double kTestRates[] = {1.0, 2, 0.215, 0.5, 1.0};
931 const bool kTestRateHasCadence[arraysize(kTestRates)] = {
932 true, true, false, true, true};
933
934 for (size_t i = 0; i < arraysize(kTestRates); ++i) {
935 const double playback_rate = kTestRates[i];
936 SCOPED_TRACE(base::StringPrintf("Playback Rate: %.03f", playback_rate));
937 time_source_.SetPlaybackRate(playback_rate);
938 RunFramePumpTest(false, &frame_tg, &display_tg,
939 [this](const scoped_refptr<VideoFrame>& frame,
940 size_t frames_dropped) {});
941 if (HasFatalFailure())
942 return;
943
944 ASSERT_EQ(kTestRateHasCadence[i], is_using_cadence());
945 }
946
947 // TODO(dalecurtis): It seems there should be some more things we can test
948 // here...
949 }
950
951 } // namespace media
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698