TestVideoTrackEncoder.cpp (53354B)
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 /* vim: set ts=2 et sw=2 tw=80: */ 3 /* This Source Code Form is subject to the terms of the Mozilla Public 4 * License, v. 2.0. If a copy of the MPL was not distributed with this 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 7 #include <algorithm> 8 9 #include "DriftCompensation.h" 10 #include "MediaTrackGraph.h" 11 #include "MediaTrackListener.h" 12 #include "VP8TrackEncoder.h" 13 #include "WebMWriter.h" // TODO: it's weird to include muxer header to get the class definition of VP8 METADATA 14 #include "YUVBufferGenerator.h" 15 #include "gmock/gmock.h" 16 #include "gtest/gtest.h" 17 #include "prtime.h" 18 19 #define VIDEO_TRACK_RATE 90000 20 21 using ::testing::_; 22 using ::testing::Invoke; 23 using ::testing::NiceMock; 24 using ::testing::TestWithParam; 25 using ::testing::Values; 26 27 using namespace mozilla::layers; 28 using namespace mozilla; 29 30 struct InitParam { 31 bool mShouldSucceed; // This parameter should cause success or fail result 32 int mWidth; // frame width 33 int mHeight; // frame height 34 }; 35 36 class MockDriftCompensator : public DriftCompensator { 37 public: 38 MockDriftCompensator() 39 : DriftCompensator(GetCurrentSerialEventTarget(), VIDEO_TRACK_RATE) { 40 ON_CALL(*this, GetVideoTime(_, _)) 41 .WillByDefault(Invoke([](TimeStamp, TimeStamp t) { return t; })); 42 } 43 44 MOCK_METHOD2(GetVideoTime, TimeStamp(TimeStamp, TimeStamp)); 45 }; 46 47 class TestVP8TrackEncoder : public VP8TrackEncoder { 48 public: 49 explicit TestVP8TrackEncoder(Maybe<float> aKeyFrameIntervalFactor = Nothing()) 50 : VP8TrackEncoder(MakeRefPtr<NiceMock<MockDriftCompensator>>(), 51 VIDEO_TRACK_RATE, mEncodedVideoQueue, 52 FrameDroppingMode::DISALLOW, aKeyFrameIntervalFactor) {} 53 54 MockDriftCompensator* DriftCompensator() { 55 return static_cast<MockDriftCompensator*>(mDriftCompensator.get()); 56 } 57 58 ::testing::AssertionResult TestInit(const InitParam& aParam) { 59 nsresult result = 60 Init(aParam.mWidth, aParam.mHeight, aParam.mWidth, aParam.mHeight, 30); 61 62 if (((NS_FAILED(result) && aParam.mShouldSucceed)) || 63 (NS_SUCCEEDED(result) && !aParam.mShouldSucceed)) { 64 return ::testing::AssertionFailure() 65 << " width = " << aParam.mWidth << " height = " << aParam.mHeight; 66 } 67 68 return ::testing::AssertionSuccess(); 69 } 70 71 MediaQueue<EncodedFrame> mEncodedVideoQueue; 72 }; 73 74 // Init test 75 TEST(VP8VideoTrackEncoder, Initialization) 76 { 77 InitParam params[] = { 78 // Failure cases. 79 {false, 0, 0}, // Height/ width should be larger than 1. 80 {false, 0, 1}, // Height/ width should be larger than 1. 81 {false, 1, 0}, // Height/ width should be larger than 1. 82 83 // Success cases 84 {true, 640, 480}, // Standard VGA 85 {true, 800, 480}, // Standard WVGA 86 {true, 960, 540}, // Standard qHD 87 {true, 1280, 720} // Standard HD 88 }; 89 90 for (const InitParam& param : params) { 91 TestVP8TrackEncoder encoder; 92 EXPECT_TRUE(encoder.TestInit(param)); 93 } 94 } 95 96 // Get MetaData test 97 TEST(VP8VideoTrackEncoder, FetchMetaData) 98 { 99 InitParam params[] = { 100 // Success cases 101 {true, 640, 480}, // Standard VGA 102 {true, 800, 480}, // Standard WVGA 103 {true, 960, 540}, // Standard qHD 104 {true, 1280, 720} // Standard HD 105 }; 106 107 for (const InitParam& param : params) { 108 TestVP8TrackEncoder encoder; 109 EXPECT_TRUE(encoder.TestInit(param)); 110 111 RefPtr<TrackMetadataBase> meta = encoder.GetMetadata(); 112 RefPtr<VP8Metadata> vp8Meta(static_cast<VP8Metadata*>(meta.get())); 113 114 // METADATA should be depend on how to initiate encoder. 115 EXPECT_EQ(vp8Meta->mWidth, param.mWidth); 116 EXPECT_EQ(vp8Meta->mHeight, param.mHeight); 117 } 118 } 119 120 // Encode test 121 TEST(VP8VideoTrackEncoder, FrameEncode) 122 { 123 TestVP8TrackEncoder encoder; 124 TimeStamp now = TimeStamp::Now(); 125 126 // Create YUV images as source. 127 nsTArray<RefPtr<Image>> images; 128 YUVBufferGenerator generator; 129 generator.Init(mozilla::gfx::IntSize(640, 480)); 130 images.AppendElement(generator.GenerateI420Image()); 131 images.AppendElement(generator.GenerateNV12Image()); 132 images.AppendElement(generator.GenerateNV21Image()); 133 134 // Put generated YUV frame into video segment. 135 // Duration of each frame is 1 second. 136 VideoSegment segment; 137 for (nsTArray<RefPtr<Image>>::size_type i = 0; i < images.Length(); i++) { 138 RefPtr<Image> image = images[i]; 139 segment.AppendFrame(image.forget(), generator.GetSize(), 140 PRINCIPAL_HANDLE_NONE, false, 141 now + TimeDuration::FromSeconds(i)); 142 } 143 144 encoder.SetStartOffset(now); 145 encoder.AppendVideoSegment(std::move(segment)); 146 encoder.AdvanceCurrentTime(now + TimeDuration::FromSeconds(images.Length())); 147 encoder.NotifyEndOfStream(); 148 149 EXPECT_TRUE(encoder.IsEncodingComplete()); 150 EXPECT_TRUE(encoder.mEncodedVideoQueue.IsFinished()); 151 EXPECT_FALSE(encoder.mEncodedVideoQueue.AtEndOfStream()); 152 } 153 154 // Test that encoding a single frame gives useful output. 155 TEST(VP8VideoTrackEncoder, SingleFrameEncode) 156 { 157 TestVP8TrackEncoder encoder; 158 TimeStamp now = TimeStamp::Now(); 159 YUVBufferGenerator generator; 160 generator.Init(mozilla::gfx::IntSize(640, 480)); 161 162 // Pass a half-second frame to the encoder. 163 VideoSegment segment; 164 segment.AppendFrame(generator.GenerateI420Image(), generator.GetSize(), 165 PRINCIPAL_HANDLE_NONE, false, now); 166 167 encoder.SetStartOffset(now); 168 encoder.AppendVideoSegment(std::move(segment)); 169 encoder.AdvanceCurrentTime(now + TimeDuration::FromSeconds(0.5)); 170 encoder.NotifyEndOfStream(); 171 172 EXPECT_TRUE(encoder.IsEncodingComplete()); 173 EXPECT_TRUE(encoder.mEncodedVideoQueue.IsFinished()); 174 EXPECT_FALSE(encoder.mEncodedVideoQueue.AtEndOfStream()); 175 176 // Read out encoded data, and verify. 177 RefPtr<EncodedFrame> frame = encoder.mEncodedVideoQueue.PopFront(); 178 EXPECT_EQ(EncodedFrame::VP8_I_FRAME, frame->mFrameType) 179 << "We only have one frame, so it should be a keyframe"; 180 181 const uint64_t halfSecond = PR_USEC_PER_SEC / 2; 182 EXPECT_EQ(halfSecond, frame->mDuration); 183 184 EXPECT_TRUE(encoder.mEncodedVideoQueue.AtEndOfStream()); 185 } 186 187 // Test that encoding a couple of identical images gives useful output. 188 TEST(VP8VideoTrackEncoder, SameFrameEncode) 189 { 190 TestVP8TrackEncoder encoder; 191 TimeStamp now = TimeStamp::Now(); 192 YUVBufferGenerator generator; 193 generator.Init(mozilla::gfx::IntSize(640, 480)); 194 195 // Pass 15 100ms frames to the encoder. 196 RefPtr<Image> image = generator.GenerateI420Image(); 197 VideoSegment segment; 198 for (uint32_t i = 0; i < 15; ++i) { 199 segment.AppendFrame(do_AddRef(image), generator.GetSize(), 200 PRINCIPAL_HANDLE_NONE, false, 201 now + TimeDuration::FromSeconds(i * 0.1)); 202 } 203 204 encoder.SetStartOffset(now); 205 encoder.AppendVideoSegment(std::move(segment)); 206 encoder.AdvanceCurrentTime(now + TimeDuration::FromSeconds(1.5)); 207 encoder.NotifyEndOfStream(); 208 209 EXPECT_TRUE(encoder.IsEncodingComplete()); 210 EXPECT_TRUE(encoder.mEncodedVideoQueue.IsFinished()); 211 EXPECT_FALSE(encoder.mEncodedVideoQueue.AtEndOfStream()); 212 213 // Verify total duration being 1.5s. 214 uint64_t totalDuration = 0; 215 while (RefPtr<EncodedFrame> frame = encoder.mEncodedVideoQueue.PopFront()) { 216 totalDuration += frame->mDuration; 217 } 218 const uint64_t oneAndAHalf = (PR_USEC_PER_SEC / 2) * 3; 219 EXPECT_EQ(oneAndAHalf, totalDuration); 220 } 221 222 // Test encoding a track that has to skip frames. 223 TEST(VP8VideoTrackEncoder, SkippedFrames) 224 { 225 TestVP8TrackEncoder encoder; 226 YUVBufferGenerator generator; 227 generator.Init(mozilla::gfx::IntSize(640, 480)); 228 TimeStamp now = TimeStamp::Now(); 229 230 // Pass 100 frames of the shortest possible duration where we don't get 231 // rounding errors between input/output rate. 232 VideoSegment segment; 233 for (uint32_t i = 0; i < 100; ++i) { 234 segment.AppendFrame(generator.GenerateI420Image(), generator.GetSize(), 235 PRINCIPAL_HANDLE_NONE, false, 236 now + TimeDuration::FromMilliseconds(i)); 237 } 238 239 encoder.SetStartOffset(now); 240 encoder.AppendVideoSegment(std::move(segment)); 241 encoder.AdvanceCurrentTime(now + TimeDuration::FromMilliseconds(100)); 242 encoder.NotifyEndOfStream(); 243 244 EXPECT_TRUE(encoder.IsEncodingComplete()); 245 EXPECT_TRUE(encoder.mEncodedVideoQueue.IsFinished()); 246 EXPECT_FALSE(encoder.mEncodedVideoQueue.AtEndOfStream()); 247 248 // Verify total duration being 100 * 1ms = 100ms. 249 uint64_t totalDuration = 0; 250 while (RefPtr<EncodedFrame> frame = encoder.mEncodedVideoQueue.PopFront()) { 251 totalDuration += frame->mDuration; 252 } 253 const uint64_t hundredMillis = PR_USEC_PER_SEC / 10; 254 EXPECT_EQ(hundredMillis, totalDuration); 255 } 256 257 // Test encoding a track with frames subject to rounding errors. 258 TEST(VP8VideoTrackEncoder, RoundingErrorFramesEncode) 259 { 260 TestVP8TrackEncoder encoder; 261 YUVBufferGenerator generator; 262 generator.Init(mozilla::gfx::IntSize(640, 480)); 263 TimeStamp now = TimeStamp::Now(); 264 265 // Pass nine frames with timestamps not expressable in 90kHz sample rate, 266 // then one frame to make the total duration close to one second. 267 VideoSegment segment; 268 uint32_t usPerFrame = 99999; // 99.999ms 269 for (uint32_t i = 0; i < 9; ++i) { 270 segment.AppendFrame(generator.GenerateI420Image(), generator.GetSize(), 271 PRINCIPAL_HANDLE_NONE, false, 272 now + TimeDuration::FromMicroseconds(i * usPerFrame)); 273 } 274 275 // This last frame has timestamp start + 0.9s and duration 0.1s. 276 segment.AppendFrame(generator.GenerateI420Image(), generator.GetSize(), 277 PRINCIPAL_HANDLE_NONE, false, 278 now + TimeDuration::FromSeconds(0.9)); 279 280 encoder.SetStartOffset(now); 281 encoder.AppendVideoSegment(std::move(segment)); 282 encoder.AdvanceCurrentTime(now + TimeDuration::FromSeconds(1)); 283 encoder.NotifyEndOfStream(); 284 285 EXPECT_TRUE(encoder.IsEncodingComplete()); 286 EXPECT_TRUE(encoder.mEncodedVideoQueue.IsFinished()); 287 EXPECT_FALSE(encoder.mEncodedVideoQueue.AtEndOfStream()); 288 289 // Verify total duration being 1s. 290 uint64_t totalDuration = 0; 291 while (RefPtr<EncodedFrame> frame = encoder.mEncodedVideoQueue.PopFront()) { 292 totalDuration += frame->mDuration; 293 } 294 // Not exact, the stream is encoded in time base 90kHz. 295 const uint64_t oneSecond = PR_USEC_PER_SEC - 1; 296 EXPECT_EQ(oneSecond, totalDuration); 297 } 298 299 // Test that we're encoding timestamps rather than durations. 300 TEST(VP8VideoTrackEncoder, TimestampFrameEncode) 301 { 302 TestVP8TrackEncoder encoder; 303 YUVBufferGenerator generator; 304 generator.Init(mozilla::gfx::IntSize(640, 480)); 305 TimeStamp now = TimeStamp::Now(); 306 307 VideoSegment segment; 308 segment.AppendFrame(generator.GenerateI420Image(), generator.GetSize(), 309 PRINCIPAL_HANDLE_NONE, false, now); 310 segment.AppendFrame(generator.GenerateI420Image(), generator.GetSize(), 311 PRINCIPAL_HANDLE_NONE, false, 312 now + TimeDuration::FromSeconds(0.05)); 313 segment.AppendFrame(generator.GenerateI420Image(), generator.GetSize(), 314 PRINCIPAL_HANDLE_NONE, false, 315 now + TimeDuration::FromSeconds(0.2)); 316 317 encoder.SetStartOffset(now); 318 encoder.AppendVideoSegment(std::move(segment)); 319 encoder.AdvanceCurrentTime(now + TimeDuration::FromSeconds(0.3)); 320 encoder.NotifyEndOfStream(); 321 322 EXPECT_TRUE(encoder.IsEncodingComplete()); 323 EXPECT_TRUE(encoder.mEncodedVideoQueue.IsFinished()); 324 EXPECT_FALSE(encoder.mEncodedVideoQueue.AtEndOfStream()); 325 326 // Verify total duration being 0.3s and individual frames being [0.05s, 0.15s, 327 // 0.1s] 328 uint64_t expectedDurations[] = {(PR_USEC_PER_SEC / 10) / 2, 329 (PR_USEC_PER_SEC / 10) * 3 / 2, 330 (PR_USEC_PER_SEC / 10)}; 331 uint64_t totalDuration = 0; 332 size_t i = 0; 333 while (RefPtr<EncodedFrame> frame = encoder.mEncodedVideoQueue.PopFront()) { 334 EXPECT_EQ(expectedDurations[i], frame->mDuration); 335 i++; 336 totalDuration += frame->mDuration; 337 } 338 const uint64_t pointThree = (PR_USEC_PER_SEC / 10) * 3; 339 EXPECT_EQ(pointThree, totalDuration); 340 } 341 342 // Test that we're compensating for drift when encoding. 343 TEST(VP8VideoTrackEncoder, DriftingFrameEncode) 344 { 345 TestVP8TrackEncoder encoder; 346 YUVBufferGenerator generator; 347 generator.Init(mozilla::gfx::IntSize(640, 480)); 348 TimeStamp now = TimeStamp::Now(); 349 350 // Set up major drift -- audio that goes twice as fast as video. 351 // This should make the given video durations double as they get encoded. 352 EXPECT_CALL(*encoder.DriftCompensator(), GetVideoTime(_, _)) 353 .WillRepeatedly(Invoke( 354 [&](TimeStamp, TimeStamp aTime) { return now + (aTime - now) * 2; })); 355 356 VideoSegment segment; 357 segment.AppendFrame(generator.GenerateI420Image(), generator.GetSize(), 358 PRINCIPAL_HANDLE_NONE, false, now); 359 segment.AppendFrame(generator.GenerateI420Image(), generator.GetSize(), 360 PRINCIPAL_HANDLE_NONE, false, 361 now + TimeDuration::FromSeconds(0.05)); 362 segment.AppendFrame(generator.GenerateI420Image(), generator.GetSize(), 363 PRINCIPAL_HANDLE_NONE, false, 364 now + TimeDuration::FromSeconds(0.2)); 365 366 encoder.SetStartOffset(now); 367 encoder.AppendVideoSegment(std::move(segment)); 368 encoder.AdvanceCurrentTime(now + TimeDuration::FromSeconds(0.3)); 369 encoder.NotifyEndOfStream(); 370 371 EXPECT_TRUE(encoder.IsEncodingComplete()); 372 EXPECT_TRUE(encoder.mEncodedVideoQueue.IsFinished()); 373 EXPECT_FALSE(encoder.mEncodedVideoQueue.AtEndOfStream()); 374 375 // Verify total duration being 0.6s and individual frames being [0.1s, 0.3s, 376 // 0.2s] 377 uint64_t expectedDurations[] = {(PR_USEC_PER_SEC / 10), 378 (PR_USEC_PER_SEC / 10) * 3, 379 (PR_USEC_PER_SEC / 10) * 2}; 380 uint64_t totalDuration = 0; 381 size_t i = 0; 382 while (RefPtr<EncodedFrame> frame = encoder.mEncodedVideoQueue.PopFront()) { 383 EXPECT_EQ(expectedDurations[i], frame->mDuration); 384 i++; 385 totalDuration += frame->mDuration; 386 } 387 const uint64_t pointSix = (PR_USEC_PER_SEC / 10) * 6; 388 EXPECT_EQ(pointSix, totalDuration); 389 } 390 391 // Test that suspending an encoding works. 392 TEST(VP8VideoTrackEncoder, Suspended) 393 { 394 TestVP8TrackEncoder encoder; 395 TimeStamp now = TimeStamp::Now(); 396 YUVBufferGenerator generator; 397 generator.Init(mozilla::gfx::IntSize(640, 480)); 398 399 // Pass 3 frames with duration 0.1s. We suspend before and resume after the 400 // second frame. 401 402 { 403 VideoSegment segment; 404 segment.AppendFrame(generator.GenerateI420Image(), generator.GetSize(), 405 PRINCIPAL_HANDLE_NONE, false, now); 406 407 encoder.SetStartOffset(now); 408 encoder.AppendVideoSegment(std::move(segment)); 409 encoder.AdvanceCurrentTime(now + TimeDuration::FromSeconds(0.1)); 410 } 411 412 encoder.Suspend(now + TimeDuration::FromSeconds(0.1)); 413 414 { 415 VideoSegment segment; 416 segment.AppendFrame(generator.GenerateI420Image(), generator.GetSize(), 417 PRINCIPAL_HANDLE_NONE, false, 418 now + TimeDuration::FromSeconds(0.1)); 419 encoder.AppendVideoSegment(std::move(segment)); 420 encoder.AdvanceCurrentTime(now + TimeDuration::FromSeconds(0.2)); 421 } 422 423 encoder.Resume(now + TimeDuration::FromSeconds(0.2)); 424 425 { 426 VideoSegment segment; 427 segment.AppendFrame(generator.GenerateI420Image(), generator.GetSize(), 428 PRINCIPAL_HANDLE_NONE, false, 429 now + TimeDuration::FromSeconds(0.2)); 430 encoder.AppendVideoSegment(std::move(segment)); 431 encoder.AdvanceCurrentTime(now + TimeDuration::FromSeconds(0.3)); 432 } 433 434 encoder.NotifyEndOfStream(); 435 436 EXPECT_TRUE(encoder.IsEncodingComplete()); 437 EXPECT_TRUE(encoder.mEncodedVideoQueue.IsFinished()); 438 EXPECT_FALSE(encoder.mEncodedVideoQueue.AtEndOfStream()); 439 440 // Verify that we have two encoded frames and a total duration of 0.2s. 441 uint64_t count = 0; 442 uint64_t totalDuration = 0; 443 while (RefPtr<EncodedFrame> frame = encoder.mEncodedVideoQueue.PopFront()) { 444 ++count; 445 totalDuration += frame->mDuration; 446 } 447 const uint64_t two = 2; 448 EXPECT_EQ(two, count); 449 const uint64_t pointTwo = (PR_USEC_PER_SEC / 10) * 2; 450 EXPECT_EQ(pointTwo, totalDuration); 451 } 452 453 // Test that ending a track while the video track encoder is suspended works. 454 TEST(VP8VideoTrackEncoder, SuspendedUntilEnd) 455 { 456 TestVP8TrackEncoder encoder; 457 YUVBufferGenerator generator; 458 generator.Init(mozilla::gfx::IntSize(640, 480)); 459 TimeStamp now = TimeStamp::Now(); 460 461 // Pass 2 frames with duration 0.1s. We suspend before the second frame. 462 463 { 464 VideoSegment segment; 465 segment.AppendFrame(generator.GenerateI420Image(), generator.GetSize(), 466 PRINCIPAL_HANDLE_NONE, false, now); 467 468 encoder.SetStartOffset(now); 469 encoder.AppendVideoSegment(std::move(segment)); 470 encoder.AdvanceCurrentTime(now + TimeDuration::FromSeconds(0.1)); 471 } 472 473 encoder.Suspend(now + TimeDuration::FromSeconds(0.1)); 474 475 { 476 VideoSegment segment; 477 segment.AppendFrame(generator.GenerateI420Image(), generator.GetSize(), 478 PRINCIPAL_HANDLE_NONE, false, 479 now + TimeDuration::FromSeconds(0.1)); 480 encoder.AppendVideoSegment(std::move(segment)); 481 encoder.AdvanceCurrentTime(now + TimeDuration::FromSeconds(0.2)); 482 } 483 484 encoder.NotifyEndOfStream(); 485 486 EXPECT_TRUE(encoder.IsEncodingComplete()); 487 EXPECT_TRUE(encoder.mEncodedVideoQueue.IsFinished()); 488 EXPECT_FALSE(encoder.mEncodedVideoQueue.AtEndOfStream()); 489 490 // Verify that we have one encoded frames and a total duration of 0.1s. 491 uint64_t count = 0; 492 uint64_t totalDuration = 0; 493 while (RefPtr<EncodedFrame> frame = encoder.mEncodedVideoQueue.PopFront()) { 494 ++count; 495 totalDuration += frame->mDuration; 496 } 497 const uint64_t one = 1; 498 EXPECT_EQ(one, count); 499 const uint64_t pointOne = PR_USEC_PER_SEC / 10; 500 EXPECT_EQ(pointOne, totalDuration); 501 } 502 503 // Test that ending a track that was always suspended works. 504 TEST(VP8VideoTrackEncoder, AlwaysSuspended) 505 { 506 TestVP8TrackEncoder encoder; 507 YUVBufferGenerator generator; 508 generator.Init(mozilla::gfx::IntSize(640, 480)); 509 TimeStamp now = TimeStamp::Now(); 510 511 // Suspend and then pass a frame with duration 2s. 512 513 encoder.Suspend(now); 514 515 VideoSegment segment; 516 segment.AppendFrame(generator.GenerateI420Image(), generator.GetSize(), 517 PRINCIPAL_HANDLE_NONE, false, now); 518 519 encoder.SetStartOffset(now); 520 encoder.AppendVideoSegment(std::move(segment)); 521 encoder.AdvanceCurrentTime(now + TimeDuration::FromSeconds(2)); 522 523 encoder.NotifyEndOfStream(); 524 525 // Verify that we have no encoded frames. 526 EXPECT_TRUE(encoder.IsEncodingComplete()); 527 EXPECT_TRUE(encoder.mEncodedVideoQueue.IsFinished()); 528 EXPECT_TRUE(encoder.mEncodedVideoQueue.AtEndOfStream()); 529 } 530 531 // Test that encoding a track that is suspended in the beginning works. 532 TEST(VP8VideoTrackEncoder, SuspendedBeginning) 533 { 534 TestVP8TrackEncoder encoder; 535 YUVBufferGenerator generator; 536 generator.Init(mozilla::gfx::IntSize(640, 480)); 537 TimeStamp now = TimeStamp::Now(); 538 539 // Suspend and pass a frame with duration 0.5s. Then resume and pass one more. 540 encoder.Suspend(now); 541 542 { 543 VideoSegment segment; 544 segment.AppendFrame(generator.GenerateI420Image(), generator.GetSize(), 545 PRINCIPAL_HANDLE_NONE, false, now); 546 547 encoder.SetStartOffset(now); 548 encoder.AppendVideoSegment(std::move(segment)); 549 encoder.AdvanceCurrentTime(now + TimeDuration::FromSeconds(0.5)); 550 } 551 552 encoder.Resume(now + TimeDuration::FromSeconds(0.5)); 553 554 { 555 VideoSegment segment; 556 segment.AppendFrame(generator.GenerateI420Image(), generator.GetSize(), 557 PRINCIPAL_HANDLE_NONE, false, 558 now + TimeDuration::FromSeconds(0.5)); 559 encoder.AppendVideoSegment(std::move(segment)); 560 encoder.AdvanceCurrentTime(now + TimeDuration::FromSeconds(1)); 561 } 562 563 encoder.NotifyEndOfStream(); 564 565 EXPECT_TRUE(encoder.IsEncodingComplete()); 566 EXPECT_TRUE(encoder.mEncodedVideoQueue.IsFinished()); 567 EXPECT_FALSE(encoder.mEncodedVideoQueue.AtEndOfStream()); 568 569 // Verify that we have one encoded frames and a total duration of 0.1s. 570 uint64_t count = 0; 571 uint64_t totalDuration = 0; 572 while (RefPtr<EncodedFrame> frame = encoder.mEncodedVideoQueue.PopFront()) { 573 ++count; 574 totalDuration += frame->mDuration; 575 } 576 const uint64_t one = 1; 577 EXPECT_EQ(one, count); 578 const uint64_t half = PR_USEC_PER_SEC / 2; 579 EXPECT_EQ(half, totalDuration); 580 } 581 582 // Test that suspending and resuming in the middle of already pushed data 583 // works. 584 TEST(VP8VideoTrackEncoder, SuspendedOverlap) 585 { 586 TestVP8TrackEncoder encoder; 587 YUVBufferGenerator generator; 588 generator.Init(mozilla::gfx::IntSize(640, 480)); 589 TimeStamp now = TimeStamp::Now(); 590 591 { 592 // Pass a 1s frame and suspend after 0.5s. 593 VideoSegment segment; 594 segment.AppendFrame(generator.GenerateI420Image(), generator.GetSize(), 595 PRINCIPAL_HANDLE_NONE, false, now); 596 597 encoder.SetStartOffset(now); 598 encoder.AppendVideoSegment(std::move(segment)); 599 } 600 601 encoder.AdvanceCurrentTime(now + TimeDuration::FromSeconds(0.5)); 602 encoder.Suspend(now + TimeDuration::FromSeconds(0.5)); 603 604 { 605 // Pass another 1s frame and resume after 0.3 of this new frame. 606 VideoSegment segment; 607 segment.AppendFrame(generator.GenerateI420Image(), generator.GetSize(), 608 PRINCIPAL_HANDLE_NONE, false, 609 now + TimeDuration::FromSeconds(1)); 610 encoder.AppendVideoSegment(std::move(segment)); 611 } 612 613 encoder.AdvanceCurrentTime(now + TimeDuration::FromSeconds(1.3)); 614 encoder.Resume(now + TimeDuration::FromSeconds(1.3)); 615 encoder.AdvanceCurrentTime(now + TimeDuration::FromSeconds(2)); 616 encoder.NotifyEndOfStream(); 617 618 EXPECT_TRUE(encoder.IsEncodingComplete()); 619 EXPECT_TRUE(encoder.mEncodedVideoQueue.IsFinished()); 620 EXPECT_FALSE(encoder.mEncodedVideoQueue.AtEndOfStream()); 621 622 // Verify that we have two encoded frames and a total duration of 0.1s. 623 RefPtr<EncodedFrame> frame = encoder.mEncodedVideoQueue.PopFront(); 624 const uint64_t pointFive = (PR_USEC_PER_SEC / 10) * 5; 625 EXPECT_EQ(pointFive, frame->mDuration); 626 frame = encoder.mEncodedVideoQueue.PopFront(); 627 const uint64_t pointSeven = (PR_USEC_PER_SEC / 10) * 7; 628 EXPECT_EQ(pointSeven, frame->mDuration); 629 EXPECT_TRUE(encoder.mEncodedVideoQueue.AtEndOfStream()); 630 } 631 632 // Test that ending a track in the middle of already pushed data works. 633 TEST(VP8VideoTrackEncoder, PrematureEnding) 634 { 635 TestVP8TrackEncoder encoder; 636 YUVBufferGenerator generator; 637 generator.Init(mozilla::gfx::IntSize(640, 480)); 638 TimeStamp now = TimeStamp::Now(); 639 640 // Pass a 1s frame and end the track after 0.5s. 641 VideoSegment segment; 642 segment.AppendFrame(generator.GenerateI420Image(), generator.GetSize(), 643 PRINCIPAL_HANDLE_NONE, false, now); 644 645 encoder.SetStartOffset(now); 646 encoder.AppendVideoSegment(std::move(segment)); 647 encoder.AdvanceCurrentTime(now + TimeDuration::FromSeconds(0.5)); 648 encoder.NotifyEndOfStream(); 649 650 EXPECT_TRUE(encoder.IsEncodingComplete()); 651 EXPECT_TRUE(encoder.mEncodedVideoQueue.IsFinished()); 652 EXPECT_FALSE(encoder.mEncodedVideoQueue.AtEndOfStream()); 653 654 uint64_t totalDuration = 0; 655 while (RefPtr<EncodedFrame> frame = encoder.mEncodedVideoQueue.PopFront()) { 656 totalDuration += frame->mDuration; 657 } 658 const uint64_t half = PR_USEC_PER_SEC / 2; 659 EXPECT_EQ(half, totalDuration); 660 } 661 662 // Test that a track that starts at t > 0 works as expected. 663 TEST(VP8VideoTrackEncoder, DelayedStart) 664 { 665 TestVP8TrackEncoder encoder; 666 YUVBufferGenerator generator; 667 generator.Init(mozilla::gfx::IntSize(640, 480)); 668 TimeStamp now = TimeStamp::Now(); 669 670 // Pass a 2s frame, start (pass first CurrentTime) at 0.5s, end at 1s. 671 // Should result in a 0.5s encoding. 672 VideoSegment segment; 673 segment.AppendFrame(generator.GenerateI420Image(), generator.GetSize(), 674 PRINCIPAL_HANDLE_NONE, false, now); 675 676 encoder.SetStartOffset(now + TimeDuration::FromSeconds(0.5)); 677 encoder.AppendVideoSegment(std::move(segment)); 678 encoder.AdvanceCurrentTime(now + TimeDuration::FromSeconds(1)); 679 encoder.NotifyEndOfStream(); 680 681 EXPECT_TRUE(encoder.IsEncodingComplete()); 682 EXPECT_TRUE(encoder.mEncodedVideoQueue.IsFinished()); 683 EXPECT_FALSE(encoder.mEncodedVideoQueue.AtEndOfStream()); 684 685 uint64_t totalDuration = 0; 686 while (RefPtr<EncodedFrame> frame = encoder.mEncodedVideoQueue.PopFront()) { 687 totalDuration += frame->mDuration; 688 } 689 const uint64_t half = PR_USEC_PER_SEC / 2; 690 EXPECT_EQ(half, totalDuration); 691 } 692 693 // Test that a track that starts at t > 0 works as expected, when 694 // SetStartOffset comes after AppendVideoSegment. 695 TEST(VP8VideoTrackEncoder, DelayedStartOtherEventOrder) 696 { 697 TestVP8TrackEncoder encoder; 698 YUVBufferGenerator generator; 699 generator.Init(mozilla::gfx::IntSize(640, 480)); 700 TimeStamp now = TimeStamp::Now(); 701 702 // Pass a 2s frame, start (pass first CurrentTime) at 0.5s, end at 1s. 703 // Should result in a 0.5s encoding. 704 VideoSegment segment; 705 segment.AppendFrame(generator.GenerateI420Image(), generator.GetSize(), 706 PRINCIPAL_HANDLE_NONE, false, now); 707 708 encoder.AppendVideoSegment(std::move(segment)); 709 encoder.SetStartOffset(now + TimeDuration::FromSeconds(0.5)); 710 encoder.AdvanceCurrentTime(now + TimeDuration::FromSeconds(1)); 711 encoder.NotifyEndOfStream(); 712 713 EXPECT_TRUE(encoder.IsEncodingComplete()); 714 EXPECT_TRUE(encoder.mEncodedVideoQueue.IsFinished()); 715 EXPECT_FALSE(encoder.mEncodedVideoQueue.AtEndOfStream()); 716 717 uint64_t totalDuration = 0; 718 while (RefPtr<EncodedFrame> frame = encoder.mEncodedVideoQueue.PopFront()) { 719 totalDuration += frame->mDuration; 720 } 721 const uint64_t half = PR_USEC_PER_SEC / 2; 722 EXPECT_EQ(half, totalDuration); 723 } 724 725 // Test that a track that starts at t >>> 0 works as expected. 726 TEST(VP8VideoTrackEncoder, VeryDelayedStart) 727 { 728 TestVP8TrackEncoder encoder; 729 YUVBufferGenerator generator; 730 generator.Init(mozilla::gfx::IntSize(640, 480)); 731 TimeStamp now = TimeStamp::Now(); 732 733 // Pass a 1s frame, start (pass first CurrentTime) at 10s, end at 10.5s. 734 // Should result in a 0.5s encoding. 735 VideoSegment segment; 736 segment.AppendFrame(generator.GenerateI420Image(), generator.GetSize(), 737 PRINCIPAL_HANDLE_NONE, false, now); 738 739 encoder.SetStartOffset(now + TimeDuration::FromSeconds(10)); 740 encoder.AppendVideoSegment(std::move(segment)); 741 encoder.AdvanceCurrentTime(now + TimeDuration::FromSeconds(10.5)); 742 encoder.NotifyEndOfStream(); 743 744 EXPECT_TRUE(encoder.IsEncodingComplete()); 745 EXPECT_TRUE(encoder.mEncodedVideoQueue.IsFinished()); 746 EXPECT_FALSE(encoder.mEncodedVideoQueue.AtEndOfStream()); 747 748 uint64_t totalDuration = 0; 749 while (RefPtr<EncodedFrame> frame = encoder.mEncodedVideoQueue.PopFront()) { 750 totalDuration += frame->mDuration; 751 } 752 const uint64_t half = PR_USEC_PER_SEC / 2; 753 EXPECT_EQ(half, totalDuration); 754 } 755 756 // Test that a video frame that hangs around for a long time gets encoded 757 // every second. 758 TEST(VP8VideoTrackEncoder, LongFramesReEncoded) 759 { 760 TestVP8TrackEncoder encoder; 761 YUVBufferGenerator generator; 762 generator.Init(mozilla::gfx::IntSize(640, 480)); 763 TimeStamp now = TimeStamp::Now(); 764 765 // Pass a frame at t=0 and start encoding. 766 // Advancing the current time by 6.5s should encode six 1s frames. 767 // Advancing the current time by another 5.5s should encode another five 1s 768 // frames. 769 770 VideoSegment segment; 771 segment.AppendFrame(generator.GenerateI420Image(), generator.GetSize(), 772 PRINCIPAL_HANDLE_NONE, false, now); 773 774 encoder.SetStartOffset(now); 775 encoder.AppendVideoSegment(std::move(segment)); 776 777 { 778 encoder.AdvanceCurrentTime(now + TimeDuration::FromSeconds(6.5)); 779 780 EXPECT_FALSE(encoder.IsEncodingComplete()); 781 EXPECT_FALSE(encoder.mEncodedVideoQueue.IsFinished()); 782 783 uint64_t count = 0; 784 uint64_t totalDuration = 0; 785 while (RefPtr<EncodedFrame> frame = encoder.mEncodedVideoQueue.PopFront()) { 786 ++count; 787 totalDuration += frame->mDuration; 788 } 789 const uint64_t sixSec = 6 * PR_USEC_PER_SEC; 790 EXPECT_EQ(sixSec, totalDuration); 791 EXPECT_EQ(6U, count); 792 } 793 794 { 795 encoder.AdvanceCurrentTime(now + TimeDuration::FromSeconds(11)); 796 encoder.NotifyEndOfStream(); 797 798 EXPECT_TRUE(encoder.IsEncodingComplete()); 799 EXPECT_TRUE(encoder.mEncodedVideoQueue.IsFinished()); 800 801 uint64_t count = 0; 802 uint64_t totalDuration = 0; 803 while (RefPtr<EncodedFrame> frame = encoder.mEncodedVideoQueue.PopFront()) { 804 ++count; 805 totalDuration += frame->mDuration; 806 } 807 const uint64_t fiveSec = 5 * PR_USEC_PER_SEC; 808 EXPECT_EQ(fiveSec, totalDuration); 809 EXPECT_EQ(5U, count); 810 } 811 } 812 813 // Test that an encoding with no defined key frame interval encodes keyframes 814 // as expected. Default interval should be 10s. 815 TEST(VP8VideoTrackEncoder, DefaultKeyFrameInterval) 816 { 817 // Set the factor high to only test the keyframe-forcing logic 818 TestVP8TrackEncoder encoder(Some(2.0)); 819 YUVBufferGenerator generator; 820 generator.Init(mozilla::gfx::IntSize(640, 480)); 821 TimeStamp now = TimeStamp::Now(); 822 823 // Pass a frame at t=0, and the frame-duplication logic will encode frames 824 // every second. Keyframes are expected at t=0, 10s and 20s. 825 VideoSegment segment; 826 segment.AppendFrame(generator.GenerateI420Image(), generator.GetSize(), 827 PRINCIPAL_HANDLE_NONE, false, now); 828 829 encoder.SetStartOffset(now); 830 encoder.AppendVideoSegment(std::move(segment)); 831 encoder.AdvanceCurrentTime(now + TimeDuration::FromSeconds(21.5)); 832 encoder.NotifyEndOfStream(); 833 834 EXPECT_TRUE(encoder.IsEncodingComplete()); 835 EXPECT_TRUE(encoder.mEncodedVideoQueue.IsFinished()); 836 EXPECT_FALSE(encoder.mEncodedVideoQueue.AtEndOfStream()); 837 838 // Duplication logic ensures no frame duration is longer than 1 second. 839 840 // [0, 1000ms) - key-frame. 841 RefPtr<EncodedFrame> frame = encoder.mEncodedVideoQueue.PopFront(); 842 EXPECT_EQ(PR_USEC_PER_SEC / 1000 * 1000UL, frame->mDuration); 843 EXPECT_EQ(EncodedFrame::VP8_I_FRAME, frame->mFrameType); 844 845 // [1000ms, 10000ms) - non-key-frames 846 for (int i = 0; i < 9; ++i) { 847 frame = encoder.mEncodedVideoQueue.PopFront(); 848 EXPECT_EQ(PR_USEC_PER_SEC / 1000 * 1000UL, frame->mDuration) 849 << "Start time: " << frame->mTime.ToMicroseconds() << "us"; 850 EXPECT_EQ(EncodedFrame::VP8_P_FRAME, frame->mFrameType) 851 << "Start time: " << frame->mTime.ToMicroseconds() << "us"; 852 } 853 854 // [10000ms, 11000ms) - key-frame 855 frame = encoder.mEncodedVideoQueue.PopFront(); 856 EXPECT_EQ(PR_USEC_PER_SEC / 1000 * 1000UL, frame->mDuration); 857 EXPECT_EQ(EncodedFrame::VP8_I_FRAME, frame->mFrameType); 858 859 // [11000ms, 20000ms) - non-key-frames 860 for (int i = 0; i < 9; ++i) { 861 frame = encoder.mEncodedVideoQueue.PopFront(); 862 EXPECT_EQ(PR_USEC_PER_SEC / 1000 * 1000UL, frame->mDuration) 863 << "Start time: " << frame->mTime.ToMicroseconds() << "us"; 864 EXPECT_EQ(EncodedFrame::VP8_P_FRAME, frame->mFrameType) 865 << "Start time: " << frame->mTime.ToMicroseconds() << "us"; 866 } 867 868 // [20000ms, 21000ms) - key-frame 869 frame = encoder.mEncodedVideoQueue.PopFront(); 870 EXPECT_EQ(PR_USEC_PER_SEC / 1000 * 1000UL, frame->mDuration); 871 EXPECT_EQ(EncodedFrame::VP8_I_FRAME, frame->mFrameType); 872 873 // [21000ms, 21500ms) - non-key-frame 874 frame = encoder.mEncodedVideoQueue.PopFront(); 875 EXPECT_EQ(PR_USEC_PER_SEC / 1000 * 500UL, frame->mDuration); 876 EXPECT_EQ(EncodedFrame::VP8_P_FRAME, frame->mFrameType); 877 878 EXPECT_TRUE(encoder.mEncodedVideoQueue.AtEndOfStream()); 879 } 880 881 // Test that an encoding which is disabled on a frame timestamp encodes 882 // frames as expected. 883 TEST(VP8VideoTrackEncoder, DisableOnFrameTime) 884 { 885 TestVP8TrackEncoder encoder; 886 YUVBufferGenerator generator; 887 generator.Init(mozilla::gfx::IntSize(640, 480)); 888 TimeStamp now = TimeStamp::Now(); 889 890 // Pass a frame in at t=0. 891 // Pass another frame in at t=100ms. 892 // Disable the track at t=100ms. 893 // Stop encoding at t=200ms. 894 // Should yield 2 frames, 1 real; 1 black. 895 896 VideoSegment segment; 897 segment.AppendFrame(generator.GenerateI420Image(), generator.GetSize(), 898 PRINCIPAL_HANDLE_NONE, false, now); 899 segment.AppendFrame(generator.GenerateI420Image(), generator.GetSize(), 900 PRINCIPAL_HANDLE_NONE, false, 901 now + TimeDuration::FromMilliseconds(100)); 902 903 encoder.SetStartOffset(now); 904 encoder.AppendVideoSegment(std::move(segment)); 905 906 // Advancing 100ms, for simplicity. 907 encoder.AdvanceCurrentTime(now + TimeDuration::FromMilliseconds(100)); 908 909 encoder.Disable(now + TimeDuration::FromMilliseconds(100)); 910 encoder.AdvanceCurrentTime(now + TimeDuration::FromMilliseconds(200)); 911 encoder.NotifyEndOfStream(); 912 913 EXPECT_TRUE(encoder.IsEncodingComplete()); 914 EXPECT_TRUE(encoder.mEncodedVideoQueue.IsFinished()); 915 EXPECT_FALSE(encoder.mEncodedVideoQueue.AtEndOfStream()); 916 917 // [0, 100ms) 918 RefPtr<EncodedFrame> frame = encoder.mEncodedVideoQueue.PopFront(); 919 EXPECT_EQ(PR_USEC_PER_SEC / 1000 * 100UL, frame->mDuration); 920 921 // [100ms, 200ms) 922 frame = encoder.mEncodedVideoQueue.PopFront(); 923 EXPECT_EQ(PR_USEC_PER_SEC / 1000 * 100UL, frame->mDuration); 924 925 EXPECT_TRUE(encoder.mEncodedVideoQueue.AtEndOfStream()); 926 } 927 928 // Test that an encoding which is disabled between two frame timestamps 929 // encodes frames as expected. 930 TEST(VP8VideoTrackEncoder, DisableBetweenFrames) 931 { 932 TestVP8TrackEncoder encoder; 933 YUVBufferGenerator generator; 934 generator.Init(mozilla::gfx::IntSize(640, 480)); 935 TimeStamp now = TimeStamp::Now(); 936 937 // Pass a frame in at t=0. 938 // Disable the track at t=50ms. 939 // Pass another frame in at t=100ms. 940 // Stop encoding at t=200ms. 941 // Should yield 3 frames, 1 real [0, 50); 2 black [50, 100) and [100, 200). 942 943 VideoSegment segment; 944 segment.AppendFrame(generator.GenerateI420Image(), generator.GetSize(), 945 PRINCIPAL_HANDLE_NONE, false, now); 946 segment.AppendFrame(generator.GenerateI420Image(), generator.GetSize(), 947 PRINCIPAL_HANDLE_NONE, false, 948 now + TimeDuration::FromMilliseconds(100)); 949 950 encoder.SetStartOffset(now); 951 encoder.AppendVideoSegment(std::move(segment)); 952 953 encoder.Disable(now + TimeDuration::FromMilliseconds(50)); 954 encoder.AdvanceCurrentTime(now + TimeDuration::FromMilliseconds(200)); 955 encoder.NotifyEndOfStream(); 956 957 EXPECT_TRUE(encoder.IsEncodingComplete()); 958 EXPECT_TRUE(encoder.mEncodedVideoQueue.IsFinished()); 959 EXPECT_FALSE(encoder.mEncodedVideoQueue.AtEndOfStream()); 960 961 // [0, 50ms) 962 RefPtr<EncodedFrame> frame = encoder.mEncodedVideoQueue.PopFront(); 963 EXPECT_EQ(PR_USEC_PER_SEC / 1000 * 50UL, frame->mDuration); 964 965 // [50ms, 100ms) 966 frame = encoder.mEncodedVideoQueue.PopFront(); 967 EXPECT_EQ(PR_USEC_PER_SEC / 1000 * 50UL, frame->mDuration); 968 969 // [100ms, 200ms) 970 frame = encoder.mEncodedVideoQueue.PopFront(); 971 EXPECT_EQ(PR_USEC_PER_SEC / 1000 * 100UL, frame->mDuration); 972 973 EXPECT_TRUE(encoder.mEncodedVideoQueue.AtEndOfStream()); 974 } 975 976 // Test that an encoding which is disabled before the first frame becomes 977 // black immediately. 978 TEST(VP8VideoTrackEncoder, DisableBeforeFirstFrame) 979 { 980 TestVP8TrackEncoder encoder; 981 YUVBufferGenerator generator; 982 generator.Init(mozilla::gfx::IntSize(640, 480)); 983 TimeStamp now = TimeStamp::Now(); 984 985 // Disable the track at t=0. 986 // Pass a frame in at t=50ms. 987 // Enable the track at t=100ms. 988 // Stop encoding at t=200ms. 989 // Should yield 2 frames, 1 black [0, 100); 1 real [100, 200). 990 991 VideoSegment segment; 992 segment.AppendFrame(generator.GenerateI420Image(), generator.GetSize(), 993 PRINCIPAL_HANDLE_NONE, false, 994 now + TimeDuration::FromMilliseconds(50)); 995 996 encoder.SetStartOffset(now); 997 encoder.Disable(now); 998 encoder.AppendVideoSegment(std::move(segment)); 999 1000 encoder.Enable(now + TimeDuration::FromMilliseconds(100)); 1001 encoder.AdvanceCurrentTime(now + TimeDuration::FromMilliseconds(200)); 1002 encoder.NotifyEndOfStream(); 1003 1004 EXPECT_TRUE(encoder.IsEncodingComplete()); 1005 EXPECT_TRUE(encoder.mEncodedVideoQueue.IsFinished()); 1006 EXPECT_FALSE(encoder.mEncodedVideoQueue.AtEndOfStream()); 1007 1008 // [0, 100ms) 1009 RefPtr<EncodedFrame> frame = encoder.mEncodedVideoQueue.PopFront(); 1010 EXPECT_EQ(PR_USEC_PER_SEC / 1000 * 100UL, frame->mDuration); 1011 1012 // [100ms, 200ms) 1013 frame = encoder.mEncodedVideoQueue.PopFront(); 1014 EXPECT_EQ(PR_USEC_PER_SEC / 1000 * 100UL, frame->mDuration); 1015 1016 EXPECT_TRUE(encoder.mEncodedVideoQueue.AtEndOfStream()); 1017 } 1018 1019 // Test that an encoding which is enabled on a frame timestamp encodes 1020 // frames as expected. 1021 TEST(VP8VideoTrackEncoder, EnableOnFrameTime) 1022 { 1023 TestVP8TrackEncoder encoder; 1024 YUVBufferGenerator generator; 1025 generator.Init(mozilla::gfx::IntSize(640, 480)); 1026 TimeStamp now = TimeStamp::Now(); 1027 1028 // Disable the track at t=0. 1029 // Pass a frame in at t=0. 1030 // Pass another frame in at t=100ms. 1031 // Enable the track at t=100ms. 1032 // Stop encoding at t=200ms. 1033 // Should yield 2 frames, 1 black; 1 real. 1034 1035 VideoSegment segment; 1036 segment.AppendFrame(generator.GenerateI420Image(), generator.GetSize(), 1037 PRINCIPAL_HANDLE_NONE, false, now); 1038 segment.AppendFrame(generator.GenerateI420Image(), generator.GetSize(), 1039 PRINCIPAL_HANDLE_NONE, false, 1040 now + TimeDuration::FromMilliseconds(100)); 1041 1042 encoder.SetStartOffset(now); 1043 encoder.Disable(now); 1044 encoder.AppendVideoSegment(std::move(segment)); 1045 1046 // Advancing 100ms, for simplicity. 1047 encoder.AdvanceCurrentTime(now + TimeDuration::FromMilliseconds(100)); 1048 1049 encoder.Enable(now + TimeDuration::FromMilliseconds(100)); 1050 encoder.AdvanceCurrentTime(now + TimeDuration::FromMilliseconds(200)); 1051 encoder.NotifyEndOfStream(); 1052 1053 EXPECT_TRUE(encoder.IsEncodingComplete()); 1054 EXPECT_TRUE(encoder.mEncodedVideoQueue.IsFinished()); 1055 EXPECT_FALSE(encoder.mEncodedVideoQueue.AtEndOfStream()); 1056 1057 // [0, 100ms) 1058 RefPtr<EncodedFrame> frame = encoder.mEncodedVideoQueue.PopFront(); 1059 EXPECT_EQ(PR_USEC_PER_SEC / 1000 * 100UL, frame->mDuration); 1060 1061 // [100ms, 200ms) 1062 frame = encoder.mEncodedVideoQueue.PopFront(); 1063 EXPECT_EQ(PR_USEC_PER_SEC / 1000 * 100UL, frame->mDuration); 1064 1065 EXPECT_TRUE(encoder.mEncodedVideoQueue.AtEndOfStream()); 1066 } 1067 1068 // Test that an encoding which is enabled between two frame timestamps encodes 1069 // frames as expected. 1070 TEST(VP8VideoTrackEncoder, EnableBetweenFrames) 1071 { 1072 TestVP8TrackEncoder encoder; 1073 YUVBufferGenerator generator; 1074 generator.Init(mozilla::gfx::IntSize(640, 480)); 1075 TimeStamp now = TimeStamp::Now(); 1076 1077 // Disable the track at t=0. 1078 // Pass a frame in at t=0. 1079 // Enable the track at t=50ms. 1080 // Pass another frame in at t=100ms. 1081 // Stop encoding at t=200ms. 1082 // Should yield 3 frames, 1 black [0, 50); 2 real [50, 100) and [100, 200). 1083 1084 VideoSegment segment; 1085 segment.AppendFrame(generator.GenerateI420Image(), generator.GetSize(), 1086 PRINCIPAL_HANDLE_NONE, false, now); 1087 segment.AppendFrame(generator.GenerateI420Image(), generator.GetSize(), 1088 PRINCIPAL_HANDLE_NONE, false, 1089 now + TimeDuration::FromMilliseconds(100)); 1090 1091 encoder.SetStartOffset(now); 1092 encoder.Disable(now); 1093 encoder.AppendVideoSegment(std::move(segment)); 1094 1095 encoder.Enable(now + TimeDuration::FromMilliseconds(50)); 1096 encoder.AdvanceCurrentTime(now + TimeDuration::FromMilliseconds(200)); 1097 encoder.NotifyEndOfStream(); 1098 1099 EXPECT_TRUE(encoder.IsEncodingComplete()); 1100 EXPECT_TRUE(encoder.mEncodedVideoQueue.IsFinished()); 1101 EXPECT_FALSE(encoder.mEncodedVideoQueue.AtEndOfStream()); 1102 1103 // [0, 50ms) 1104 RefPtr<EncodedFrame> frame = encoder.mEncodedVideoQueue.PopFront(); 1105 EXPECT_EQ(PR_USEC_PER_SEC / 1000 * 50UL, frame->mDuration); 1106 1107 // [50ms, 100ms) 1108 frame = encoder.mEncodedVideoQueue.PopFront(); 1109 EXPECT_EQ(PR_USEC_PER_SEC / 1000 * 50UL, frame->mDuration); 1110 1111 // [100ms, 200ms) 1112 frame = encoder.mEncodedVideoQueue.PopFront(); 1113 EXPECT_EQ(PR_USEC_PER_SEC / 1000 * 100UL, frame->mDuration); 1114 1115 EXPECT_TRUE(encoder.mEncodedVideoQueue.AtEndOfStream()); 1116 } 1117 1118 // Test that making time go backwards removes any future frames in the 1119 // encoder. 1120 TEST(VP8VideoTrackEncoder, BackwardsTimeResets) 1121 { 1122 TestVP8TrackEncoder encoder; 1123 YUVBufferGenerator generator; 1124 generator.Init(mozilla::gfx::IntSize(640, 480)); 1125 TimeStamp now = TimeStamp::Now(); 1126 1127 encoder.SetStartOffset(now); 1128 1129 // Pass frames in at t=0, t=100ms, t=200ms, t=300ms. 1130 // Advance time to t=125ms. 1131 // Pass frames in at t=150ms, t=250ms, t=350ms. 1132 // Stop encoding at t=300ms. 1133 // Should yield 4 frames, at t=0, t=100ms, t=150ms, t=250ms. 1134 1135 { 1136 VideoSegment segment; 1137 segment.AppendFrame(generator.GenerateI420Image(), generator.GetSize(), 1138 PRINCIPAL_HANDLE_NONE, false, now); 1139 segment.AppendFrame(generator.GenerateI420Image(), generator.GetSize(), 1140 PRINCIPAL_HANDLE_NONE, false, 1141 now + TimeDuration::FromMilliseconds(100)); 1142 segment.AppendFrame(generator.GenerateI420Image(), generator.GetSize(), 1143 PRINCIPAL_HANDLE_NONE, false, 1144 now + TimeDuration::FromMilliseconds(200)); 1145 segment.AppendFrame(generator.GenerateI420Image(), generator.GetSize(), 1146 PRINCIPAL_HANDLE_NONE, false, 1147 now + TimeDuration::FromMilliseconds(300)); 1148 1149 encoder.AppendVideoSegment(std::move(segment)); 1150 } 1151 1152 encoder.AdvanceCurrentTime(now + TimeDuration::FromMilliseconds(125)); 1153 1154 { 1155 VideoSegment segment; 1156 segment.AppendFrame(generator.GenerateI420Image(), generator.GetSize(), 1157 PRINCIPAL_HANDLE_NONE, false, 1158 now + TimeDuration::FromMilliseconds(150)); 1159 segment.AppendFrame(generator.GenerateI420Image(), generator.GetSize(), 1160 PRINCIPAL_HANDLE_NONE, false, 1161 now + TimeDuration::FromMilliseconds(250)); 1162 segment.AppendFrame(generator.GenerateI420Image(), generator.GetSize(), 1163 PRINCIPAL_HANDLE_NONE, false, 1164 now + TimeDuration::FromMilliseconds(350)); 1165 1166 encoder.AppendVideoSegment(std::move(segment)); 1167 } 1168 1169 encoder.AdvanceCurrentTime(now + TimeDuration::FromMilliseconds(300)); 1170 encoder.NotifyEndOfStream(); 1171 1172 EXPECT_TRUE(encoder.IsEncodingComplete()); 1173 EXPECT_TRUE(encoder.mEncodedVideoQueue.IsFinished()); 1174 EXPECT_FALSE(encoder.mEncodedVideoQueue.AtEndOfStream()); 1175 1176 // [0, 100ms) 1177 RefPtr<EncodedFrame> frame = encoder.mEncodedVideoQueue.PopFront(); 1178 EXPECT_EQ(PR_USEC_PER_SEC / 1000 * 100UL, frame->mDuration); 1179 1180 // [100ms, 150ms) 1181 frame = encoder.mEncodedVideoQueue.PopFront(); 1182 EXPECT_EQ(PR_USEC_PER_SEC / 1000 * 50UL, frame->mDuration); 1183 1184 // [150ms, 250ms) 1185 frame = encoder.mEncodedVideoQueue.PopFront(); 1186 EXPECT_EQ(PR_USEC_PER_SEC / 1000 * 100UL, frame->mDuration); 1187 1188 // [250ms, 300ms) 1189 frame = encoder.mEncodedVideoQueue.PopFront(); 1190 EXPECT_EQ(PR_USEC_PER_SEC / 1000 * 50UL, frame->mDuration); 1191 1192 EXPECT_TRUE(encoder.mEncodedVideoQueue.AtEndOfStream()); 1193 } 1194 1195 // Test that trying to encode a null image removes any future frames in the 1196 // encoder. 1197 TEST(VP8VideoTrackEncoder, NullImageResets) 1198 { 1199 TestVP8TrackEncoder encoder; 1200 YUVBufferGenerator generator; 1201 generator.Init(mozilla::gfx::IntSize(640, 480)); 1202 TimeStamp now = TimeStamp::Now(); 1203 1204 encoder.SetStartOffset(now); 1205 1206 // Pass frames in at t=0, t=100ms, t=200ms, t=300ms. 1207 // Advance time to t=125ms. 1208 // Pass in a null image at t=125ms. 1209 // Pass frames in at t=250ms, t=350ms. 1210 // Stop encoding at t=300ms. 1211 // Should yield 3 frames, at t=0, t=100ms, t=250ms. 1212 1213 { 1214 VideoSegment segment; 1215 segment.AppendFrame(generator.GenerateI420Image(), generator.GetSize(), 1216 PRINCIPAL_HANDLE_NONE, false, now); 1217 segment.AppendFrame(generator.GenerateI420Image(), generator.GetSize(), 1218 PRINCIPAL_HANDLE_NONE, false, 1219 now + TimeDuration::FromMilliseconds(100)); 1220 segment.AppendFrame(generator.GenerateI420Image(), generator.GetSize(), 1221 PRINCIPAL_HANDLE_NONE, false, 1222 now + TimeDuration::FromMilliseconds(200)); 1223 segment.AppendFrame(generator.GenerateI420Image(), generator.GetSize(), 1224 PRINCIPAL_HANDLE_NONE, false, 1225 now + TimeDuration::FromMilliseconds(300)); 1226 1227 encoder.AppendVideoSegment(std::move(segment)); 1228 } 1229 1230 encoder.AdvanceCurrentTime(now + TimeDuration::FromMilliseconds(125)); 1231 1232 { 1233 VideoSegment segment; 1234 segment.AppendFrame(nullptr, generator.GetSize(), PRINCIPAL_HANDLE_NONE, 1235 false, now + TimeDuration::FromMilliseconds(125)); 1236 segment.AppendFrame(generator.GenerateI420Image(), generator.GetSize(), 1237 PRINCIPAL_HANDLE_NONE, false, 1238 now + TimeDuration::FromMilliseconds(250)); 1239 segment.AppendFrame(generator.GenerateI420Image(), generator.GetSize(), 1240 PRINCIPAL_HANDLE_NONE, false, 1241 now + TimeDuration::FromMilliseconds(350)); 1242 1243 encoder.AppendVideoSegment(std::move(segment)); 1244 } 1245 1246 encoder.AdvanceCurrentTime(now + TimeDuration::FromMilliseconds(300)); 1247 encoder.NotifyEndOfStream(); 1248 1249 EXPECT_TRUE(encoder.IsEncodingComplete()); 1250 EXPECT_TRUE(encoder.mEncodedVideoQueue.IsFinished()); 1251 EXPECT_FALSE(encoder.mEncodedVideoQueue.AtEndOfStream()); 1252 1253 // [0, 100ms) 1254 RefPtr<EncodedFrame> frame = encoder.mEncodedVideoQueue.PopFront(); 1255 EXPECT_EQ(PR_USEC_PER_SEC / 1000 * 100UL, frame->mDuration); 1256 1257 // [100ms, 250ms) 1258 frame = encoder.mEncodedVideoQueue.PopFront(); 1259 EXPECT_EQ(PR_USEC_PER_SEC / 1000 * 150UL, frame->mDuration); 1260 1261 // [250ms, 300ms) 1262 frame = encoder.mEncodedVideoQueue.PopFront(); 1263 EXPECT_EQ(PR_USEC_PER_SEC / 1000 * 50UL, frame->mDuration); 1264 1265 EXPECT_TRUE(encoder.mEncodedVideoQueue.AtEndOfStream()); 1266 } 1267 1268 TEST(VP8VideoTrackEncoder, MaxKeyFrameDistanceLowFramerate) 1269 { 1270 TestVP8TrackEncoder encoder; 1271 YUVBufferGenerator generator; 1272 generator.Init(mozilla::gfx::IntSize(240, 180)); 1273 TimeStamp now = TimeStamp::Now(); 1274 1275 encoder.SetStartOffset(now); 1276 1277 // Pass 10s worth of frames at 2 fps and verify that the key frame interval 1278 // is ~7.5s. 1279 const TimeDuration duration = TimeDuration::FromSeconds(10); 1280 const uint32_t numFrames = 10 * 2; 1281 const TimeDuration frameDuration = duration / static_cast<int64_t>(numFrames); 1282 1283 { 1284 VideoSegment segment; 1285 for (uint32_t i = 0; i < numFrames; ++i) { 1286 segment.AppendFrame(generator.GenerateI420Image(), generator.GetSize(), 1287 PRINCIPAL_HANDLE_NONE, false, 1288 now + frameDuration * i); 1289 } 1290 encoder.AppendVideoSegment(std::move(segment)); 1291 } 1292 1293 encoder.AdvanceCurrentTime(now + duration); 1294 encoder.NotifyEndOfStream(); 1295 1296 EXPECT_TRUE(encoder.IsEncodingComplete()); 1297 EXPECT_TRUE(encoder.mEncodedVideoQueue.IsFinished()); 1298 EXPECT_FALSE(encoder.mEncodedVideoQueue.AtEndOfStream()); 1299 1300 for (uint32_t i = 0; i < numFrames; ++i) { 1301 const RefPtr<EncodedFrame> frame = encoder.mEncodedVideoQueue.PopFront(); 1302 EXPECT_EQ(PR_USEC_PER_SEC / 1000 * 500UL, frame->mDuration) 1303 << "Frame " << i << ", with start: " << frame->mTime.ToMicroseconds() 1304 << "us"; 1305 // 7.5s key frame interval at 2 fps becomes the 15th frame. 1306 EXPECT_EQ( 1307 i % 15 == 0 ? EncodedFrame::VP8_I_FRAME : EncodedFrame::VP8_P_FRAME, 1308 frame->mFrameType) 1309 << "Frame " << i << ", with start: " << frame->mTime.ToMicroseconds() 1310 << "us"; 1311 } 1312 1313 EXPECT_TRUE(encoder.mEncodedVideoQueue.AtEndOfStream()); 1314 } 1315 1316 // This is "High" framerate, as in higher than the test for "Low" framerate. 1317 // We don't make it too high because the test takes considerably longer to 1318 // run. 1319 TEST(VP8VideoTrackEncoder, MaxKeyFrameDistanceHighFramerate) 1320 { 1321 TestVP8TrackEncoder encoder; 1322 YUVBufferGenerator generator; 1323 generator.Init(mozilla::gfx::IntSize(240, 180)); 1324 TimeStamp now = TimeStamp::Now(); 1325 1326 encoder.SetStartOffset(now); 1327 1328 // Pass 10s worth of frames at 8 fps and verify that the key frame interval 1329 // is ~7.5s. 1330 const TimeDuration duration = TimeDuration::FromSeconds(10); 1331 const uint32_t numFrames = 10 * 8; 1332 const TimeDuration frameDuration = duration / static_cast<int64_t>(numFrames); 1333 1334 { 1335 VideoSegment segment; 1336 for (uint32_t i = 0; i < numFrames; ++i) { 1337 segment.AppendFrame(generator.GenerateI420Image(), generator.GetSize(), 1338 PRINCIPAL_HANDLE_NONE, false, 1339 now + frameDuration * i); 1340 } 1341 encoder.AppendVideoSegment(std::move(segment)); 1342 } 1343 1344 encoder.AdvanceCurrentTime(now + duration); 1345 encoder.NotifyEndOfStream(); 1346 1347 EXPECT_TRUE(encoder.IsEncodingComplete()); 1348 EXPECT_TRUE(encoder.mEncodedVideoQueue.IsFinished()); 1349 EXPECT_FALSE(encoder.mEncodedVideoQueue.AtEndOfStream()); 1350 1351 for (uint32_t i = 0; i < numFrames; ++i) { 1352 const RefPtr<EncodedFrame> frame = encoder.mEncodedVideoQueue.PopFront(); 1353 EXPECT_EQ(PR_USEC_PER_SEC / 1000 * 125UL, frame->mDuration) 1354 << "Frame " << i << ", with start: " << frame->mTime.ToMicroseconds() 1355 << "us"; 1356 // 7.5s key frame interval at 8 fps becomes the 60th frame. 1357 EXPECT_EQ( 1358 i % 60 == 0 ? EncodedFrame::VP8_I_FRAME : EncodedFrame::VP8_P_FRAME, 1359 frame->mFrameType) 1360 << "Frame " << i << ", with start: " << frame->mTime.ToMicroseconds() 1361 << "us"; 1362 } 1363 1364 EXPECT_TRUE(encoder.mEncodedVideoQueue.AtEndOfStream()); 1365 } 1366 1367 TEST(VP8VideoTrackEncoder, MaxKeyFrameDistanceAdaptiveFramerate) 1368 { 1369 TestVP8TrackEncoder encoder; 1370 YUVBufferGenerator generator; 1371 generator.Init(mozilla::gfx::IntSize(240, 180)); 1372 TimeStamp now = TimeStamp::Now(); 1373 1374 encoder.SetStartOffset(now); 1375 1376 // Pass 11s worth of frames at 2 fps and verify that there is a key frame 1377 // at 7.5s. Then pass 14s worth of frames at 10 fps and verify that there is 1378 // a key frame at 15s (due to re-init) and then one at 22.5s. 1379 1380 const TimeDuration firstDuration = TimeDuration::FromSeconds(11); 1381 const uint32_t firstNumFrames = 11 * 2; 1382 const TimeDuration firstFrameDuration = 1383 firstDuration / static_cast<int64_t>(firstNumFrames); 1384 { 1385 VideoSegment segment; 1386 for (uint32_t i = 0; i < firstNumFrames; ++i) { 1387 segment.AppendFrame(generator.GenerateI420Image(), generator.GetSize(), 1388 PRINCIPAL_HANDLE_NONE, false, 1389 now + firstFrameDuration * i); 1390 } 1391 encoder.AppendVideoSegment(std::move(segment)); 1392 } 1393 encoder.AdvanceCurrentTime(now + firstDuration); 1394 1395 const TimeDuration secondDuration = TimeDuration::FromSeconds(14); 1396 const uint32_t secondNumFrames = 14 * 10; 1397 const TimeDuration secondFrameDuration = 1398 secondDuration / static_cast<int64_t>(secondNumFrames); 1399 { 1400 VideoSegment segment; 1401 for (uint32_t i = 0; i < secondNumFrames; ++i) { 1402 segment.AppendFrame(generator.GenerateI420Image(), generator.GetSize(), 1403 PRINCIPAL_HANDLE_NONE, false, 1404 now + firstDuration + secondFrameDuration * i); 1405 } 1406 encoder.AppendVideoSegment(std::move(segment)); 1407 } 1408 encoder.AdvanceCurrentTime(now + firstDuration + secondDuration); 1409 1410 encoder.NotifyEndOfStream(); 1411 1412 EXPECT_TRUE(encoder.IsEncodingComplete()); 1413 EXPECT_TRUE(encoder.mEncodedVideoQueue.IsFinished()); 1414 EXPECT_FALSE(encoder.mEncodedVideoQueue.AtEndOfStream()); 1415 1416 // [0, 11s) - keyframe distance is now 7.5s@2fps = 15. 1417 for (uint32_t i = 0; i < 22; ++i) { 1418 const RefPtr<EncodedFrame> frame = encoder.mEncodedVideoQueue.PopFront(); 1419 EXPECT_EQ(PR_USEC_PER_SEC / 1000 * 500UL, frame->mDuration) 1420 << "Frame " << i << ", with start: " << frame->mTime.ToMicroseconds() 1421 << "us"; 1422 // 7.5s key frame interval at 2 fps becomes the 15th frame. 1423 EXPECT_EQ( 1424 i % 15 == 0 ? EncodedFrame::VP8_I_FRAME : EncodedFrame::VP8_P_FRAME, 1425 frame->mFrameType) 1426 << "Frame " << i << ", with start: " << frame->mTime.ToMicroseconds() 1427 << "us"; 1428 } 1429 1430 // Input framerate is now 10fps. 1431 // Framerate re-evaluation every 5s, so the keyframe distance changed at 1432 // 15s. 1433 for (uint32_t i = 22; i < 162; ++i) { 1434 const RefPtr<EncodedFrame> frame = encoder.mEncodedVideoQueue.PopFront(); 1435 EXPECT_EQ(PR_USEC_PER_SEC / 1000 * 100UL, frame->mDuration) 1436 << "Frame " << i << ", with start: " << frame->mTime.ToMicroseconds() 1437 << "us"; 1438 if (i < 22 + 40) { 1439 // [11s, 15s) - 40 frames at 10fps but with the 2fps keyframe distance. 1440 EXPECT_EQ( 1441 i % 15 == 0 ? EncodedFrame::VP8_I_FRAME : EncodedFrame::VP8_P_FRAME, 1442 frame->mFrameType) 1443 << "Frame " << i << ", with start: " << frame->mTime.ToMicroseconds() 1444 << "us"; 1445 } else { 1446 // [15s, 25s) - 100 frames at 10fps. Keyframe distance 75. Starts with 1447 // keyframe due to re-init. 1448 EXPECT_EQ((i - 22 - 40) % 75 == 0 ? EncodedFrame::VP8_I_FRAME 1449 : EncodedFrame::VP8_P_FRAME, 1450 frame->mFrameType) 1451 << "Frame " << i << ", with start: " << frame->mTime.ToMicroseconds() 1452 << "us"; 1453 } 1454 } 1455 1456 EXPECT_TRUE(encoder.mEncodedVideoQueue.AtEndOfStream()); 1457 } 1458 1459 // EOS test 1460 TEST(VP8VideoTrackEncoder, EncodeComplete) 1461 { 1462 TestVP8TrackEncoder encoder; 1463 1464 // NotifyEndOfStream should wrap up the encoding immediately. 1465 encoder.NotifyEndOfStream(); 1466 EXPECT_TRUE(encoder.mEncodedVideoQueue.IsFinished()); 1467 }