OLD | NEW |
(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 [¤t_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 [¤t_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 [¤t_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 |
OLD | NEW |