1 // Copyright (c) 2012 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 "content/browser/renderer_host/media/web_contents_video_capture_device.h" 6 7 #include "base/bind_helpers.h" 8 #include "base/debug/debugger.h" 9 #include "base/run_loop.h" 10 #include "base/test/test_timeouts.h" 11 #include "base/time/time.h" 12 #include "base/timer/timer.h" 13 #include "content/browser/browser_thread_impl.h" 14 #include "content/browser/renderer_host/media/video_capture_buffer_pool.h" 15 #include "content/browser/renderer_host/media/video_capture_oracle.h" 16 #include "content/browser/renderer_host/media/web_contents_capture_util.h" 17 #include "content/browser/renderer_host/render_view_host_factory.h" 18 #include "content/browser/renderer_host/render_widget_host_impl.h" 19 #include "content/browser/renderer_host/test_render_view_host.h" 20 #include "content/port/browser/render_widget_host_view_frame_subscriber.h" 21 #include "content/public/browser/notification_service.h" 22 #include "content/public/browser/notification_types.h" 23 #include "content/public/test/mock_render_process_host.h" 24 #include "content/public/test/test_browser_context.h" 25 #include "content/public/test/test_browser_thread_bundle.h" 26 #include "content/public/test/test_utils.h" 27 #include "content/test/test_web_contents.h" 28 #include "media/base/video_util.h" 29 #include "media/base/yuv_convert.h" 30 #include "media/video/capture/video_capture_types.h" 31 #include "skia/ext/platform_canvas.h" 32 #include "testing/gtest/include/gtest/gtest.h" 33 #include "third_party/skia/include/core/SkColor.h" 34 35 namespace content { 36 namespace { 37 38 const int kTestWidth = 320; 39 const int kTestHeight = 240; 40 const int kTestFramesPerSecond = 20; 41 const SkColor kNothingYet = 0xdeadbeef; 42 const SkColor kNotInterested = ~kNothingYet; 43 44 void DeadlineExceeded(base::Closure quit_closure) { 45 if (!base::debug::BeingDebugged()) { 46 quit_closure.Run(); 47 FAIL() << "Deadline exceeded while waiting, quitting"; 48 } else { 49 LOG(WARNING) << "Deadline exceeded; test would fail if debugger weren't " 50 << "attached."; 51 } 52 } 53 54 void RunCurrentLoopWithDeadline() { 55 base::Timer deadline(false, false); 56 deadline.Start(FROM_HERE, TestTimeouts::action_max_timeout(), base::Bind( 57 &DeadlineExceeded, base::MessageLoop::current()->QuitClosure())); 58 base::MessageLoop::current()->Run(); 59 deadline.Stop(); 60 } 61 62 SkColor ConvertRgbToYuv(SkColor rgb) { 63 uint8 yuv[3]; 64 media::ConvertRGB32ToYUV(reinterpret_cast<uint8*>(&rgb), 65 yuv, yuv + 1, yuv + 2, 1, 1, 1, 1, 1); 66 return SkColorSetRGB(yuv[0], yuv[1], yuv[2]); 67 } 68 69 // Thread-safe class that controls the source pattern to be captured by the 70 // system under test. The lifetime of this class is greater than the lifetime 71 // of all objects that reference it, so it does not need to be reference 72 // counted. 73 class CaptureTestSourceController { 74 public: 75 76 CaptureTestSourceController() 77 : color_(SK_ColorMAGENTA), 78 copy_result_size_(kTestWidth, kTestHeight), 79 can_copy_to_video_frame_(false), 80 use_frame_subscriber_(false) {} 81 82 void SetSolidColor(SkColor color) { 83 base::AutoLock guard(lock_); 84 color_ = color; 85 } 86 87 SkColor GetSolidColor() { 88 base::AutoLock guard(lock_); 89 return color_; 90 } 91 92 void SetCopyResultSize(int width, int height) { 93 base::AutoLock guard(lock_); 94 copy_result_size_ = gfx::Size(width, height); 95 } 96 97 gfx::Size GetCopyResultSize() { 98 base::AutoLock guard(lock_); 99 return copy_result_size_; 100 } 101 102 void SignalCopy() { 103 // TODO(nick): This actually should always be happening on the UI thread. 104 base::AutoLock guard(lock_); 105 if (!copy_done_.is_null()) { 106 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, copy_done_); 107 copy_done_.Reset(); 108 } 109 } 110 111 void SetCanCopyToVideoFrame(bool value) { 112 base::AutoLock guard(lock_); 113 can_copy_to_video_frame_ = value; 114 } 115 116 bool CanCopyToVideoFrame() { 117 base::AutoLock guard(lock_); 118 return can_copy_to_video_frame_; 119 } 120 121 void SetUseFrameSubscriber(bool value) { 122 base::AutoLock guard(lock_); 123 use_frame_subscriber_ = value; 124 } 125 126 bool CanUseFrameSubscriber() { 127 base::AutoLock guard(lock_); 128 return use_frame_subscriber_; 129 } 130 131 void WaitForNextCopy() { 132 { 133 base::AutoLock guard(lock_); 134 copy_done_ = base::MessageLoop::current()->QuitClosure(); 135 } 136 137 RunCurrentLoopWithDeadline(); 138 } 139 140 private: 141 base::Lock lock_; // Guards changes to all members. 142 SkColor color_; 143 gfx::Size copy_result_size_; 144 bool can_copy_to_video_frame_; 145 bool use_frame_subscriber_; 146 base::Closure copy_done_; 147 148 DISALLOW_COPY_AND_ASSIGN(CaptureTestSourceController); 149 }; 150 151 // A stub implementation which returns solid-color bitmaps in calls to 152 // CopyFromCompositingSurfaceToVideoFrame(), and which allows the video-frame 153 // readback path to be switched on and off. The behavior is controlled by a 154 // CaptureTestSourceController. 155 class CaptureTestView : public TestRenderWidgetHostView { 156 public: 157 explicit CaptureTestView(RenderWidgetHostImpl* rwh, 158 CaptureTestSourceController* controller) 159 : TestRenderWidgetHostView(rwh), 160 controller_(controller) {} 161 162 virtual ~CaptureTestView() {} 163 164 // TestRenderWidgetHostView overrides. 165 virtual gfx::Rect GetViewBounds() const OVERRIDE { 166 return gfx::Rect(100, 100, 100 + kTestWidth, 100 + kTestHeight); 167 } 168 169 virtual bool CanCopyToVideoFrame() const OVERRIDE { 170 return controller_->CanCopyToVideoFrame(); 171 } 172 173 virtual void CopyFromCompositingSurfaceToVideoFrame( 174 const gfx::Rect& src_subrect, 175 const scoped_refptr<media::VideoFrame>& target, 176 const base::Callback<void(bool)>& callback) OVERRIDE { 177 SkColor c = ConvertRgbToYuv(controller_->GetSolidColor()); 178 media::FillYUV( 179 target.get(), SkColorGetR(c), SkColorGetG(c), SkColorGetB(c)); 180 callback.Run(true); 181 controller_->SignalCopy(); 182 } 183 184 virtual void BeginFrameSubscription( 185 scoped_ptr<RenderWidgetHostViewFrameSubscriber> subscriber) OVERRIDE { 186 subscriber_.reset(subscriber.release()); 187 } 188 189 virtual void EndFrameSubscription() OVERRIDE { 190 subscriber_.reset(); 191 } 192 193 // Simulate a compositor paint event for our subscriber. 194 void SimulateUpdate() { 195 const base::Time present_time = base::Time::Now(); 196 RenderWidgetHostViewFrameSubscriber::DeliverFrameCallback callback; 197 scoped_refptr<media::VideoFrame> target; 198 if (subscriber_ && subscriber_->ShouldCaptureFrame(present_time, 199 &target, &callback)) { 200 SkColor c = ConvertRgbToYuv(controller_->GetSolidColor()); 201 media::FillYUV( 202 target.get(), SkColorGetR(c), SkColorGetG(c), SkColorGetB(c)); 203 BrowserThread::PostTask(BrowserThread::UI, 204 FROM_HERE, 205 base::Bind(callback, present_time, true)); 206 controller_->SignalCopy(); 207 } 208 } 209 210 private: 211 scoped_ptr<RenderWidgetHostViewFrameSubscriber> subscriber_; 212 CaptureTestSourceController* const controller_; 213 214 DISALLOW_IMPLICIT_CONSTRUCTORS(CaptureTestView); 215 }; 216 217 #if defined(COMPILER_MSVC) 218 // MSVC warns on diamond inheritance. See comment for same warning on 219 // RenderViewHostImpl. 220 #pragma warning(push) 221 #pragma warning(disable: 4250) 222 #endif 223 224 // A stub implementation which returns solid-color bitmaps in calls to 225 // CopyFromBackingStore(). The behavior is controlled by a 226 // CaptureTestSourceController. 227 class CaptureTestRenderViewHost : public TestRenderViewHost { 228 public: 229 CaptureTestRenderViewHost(SiteInstance* instance, 230 RenderViewHostDelegate* delegate, 231 RenderWidgetHostDelegate* widget_delegate, 232 int routing_id, 233 int main_frame_routing_id, 234 bool swapped_out, 235 CaptureTestSourceController* controller) 236 : TestRenderViewHost(instance, delegate, widget_delegate, routing_id, 237 main_frame_routing_id, swapped_out), 238 controller_(controller) { 239 // Override the default view installed by TestRenderViewHost; we need 240 // our special subclass which has mocked-out tab capture support. 241 RenderWidgetHostView* old_view = GetView(); 242 SetView(new CaptureTestView(this, controller)); 243 delete old_view; 244 } 245 246 // TestRenderViewHost overrides. 247 virtual void CopyFromBackingStore( 248 const gfx::Rect& src_rect, 249 const gfx::Size& accelerated_dst_size, 250 const base::Callback<void(bool, const SkBitmap&)>& callback) OVERRIDE { 251 gfx::Size size = controller_->GetCopyResultSize(); 252 SkColor color = controller_->GetSolidColor(); 253 254 // Although it's not necessary, use a PlatformBitmap here (instead of a 255 // regular SkBitmap) to exercise possible threading issues. 256 skia::PlatformBitmap output; 257 EXPECT_TRUE(output.Allocate(size.width(), size.height(), false)); 258 { 259 SkAutoLockPixels locker(output.GetBitmap()); 260 output.GetBitmap().eraseColor(color); 261 } 262 callback.Run(true, output.GetBitmap()); 263 controller_->SignalCopy(); 264 } 265 266 private: 267 CaptureTestSourceController* controller_; 268 269 DISALLOW_IMPLICIT_CONSTRUCTORS(CaptureTestRenderViewHost); 270 }; 271 272 #if defined(COMPILER_MSVC) 273 // Re-enable warning 4250 274 #pragma warning(pop) 275 #endif 276 277 class CaptureTestRenderViewHostFactory : public RenderViewHostFactory { 278 public: 279 explicit CaptureTestRenderViewHostFactory( 280 CaptureTestSourceController* controller) : controller_(controller) { 281 RegisterFactory(this); 282 } 283 284 virtual ~CaptureTestRenderViewHostFactory() { 285 UnregisterFactory(); 286 } 287 288 // RenderViewHostFactory implementation. 289 virtual RenderViewHost* CreateRenderViewHost( 290 SiteInstance* instance, 291 RenderViewHostDelegate* delegate, 292 RenderWidgetHostDelegate* widget_delegate, 293 int routing_id, 294 int main_frame_routing_id, 295 bool swapped_out) OVERRIDE { 296 return new CaptureTestRenderViewHost(instance, delegate, widget_delegate, 297 routing_id, main_frame_routing_id, 298 swapped_out, controller_); 299 } 300 private: 301 CaptureTestSourceController* controller_; 302 303 DISALLOW_IMPLICIT_CONSTRUCTORS(CaptureTestRenderViewHostFactory); 304 }; 305 306 // A stub consumer of captured video frames, which checks the output of 307 // WebContentsVideoCaptureDevice. 308 class StubConsumer : public media::VideoCaptureDevice::EventHandler { 309 public: 310 StubConsumer() 311 : error_encountered_(false), 312 wait_color_yuv_(0xcafe1950) { 313 buffer_pool_ = 314 new VideoCaptureBufferPool(kTestWidth * kTestHeight * 3 / 2, 2); 315 EXPECT_TRUE(buffer_pool_->Allocate()); 316 } 317 virtual ~StubConsumer() {} 318 319 void QuitIfConditionMet(SkColor color) { 320 base::AutoLock guard(lock_); 321 322 if (wait_color_yuv_ == color || error_encountered_) 323 base::MessageLoop::current()->Quit(); 324 } 325 326 void WaitForNextColor(SkColor expected_color) { 327 { 328 base::AutoLock guard(lock_); 329 wait_color_yuv_ = ConvertRgbToYuv(expected_color); 330 error_encountered_ = false; 331 } 332 RunCurrentLoopWithDeadline(); 333 { 334 base::AutoLock guard(lock_); 335 ASSERT_FALSE(error_encountered_); 336 } 337 } 338 339 void WaitForError() { 340 { 341 base::AutoLock guard(lock_); 342 wait_color_yuv_ = kNotInterested; 343 error_encountered_ = false; 344 } 345 RunCurrentLoopWithDeadline(); 346 { 347 base::AutoLock guard(lock_); 348 ASSERT_TRUE(error_encountered_); 349 } 350 } 351 352 bool HasError() { 353 base::AutoLock guard(lock_); 354 return error_encountered_; 355 } 356 357 virtual scoped_refptr<media::VideoFrame> ReserveOutputBuffer() OVERRIDE { 358 return buffer_pool_->ReserveI420VideoFrame(gfx::Size(kTestWidth, 359 kTestHeight), 360 0); 361 } 362 363 virtual void OnIncomingCapturedFrame( 364 const uint8* data, 365 int length, 366 base::Time timestamp, 367 int rotation, 368 bool flip_vert, 369 bool flip_horiz) OVERRIDE { 370 FAIL(); 371 } 372 373 virtual void OnIncomingCapturedVideoFrame( 374 const scoped_refptr<media::VideoFrame>& frame, 375 base::Time timestamp) OVERRIDE { 376 EXPECT_EQ(gfx::Size(kTestWidth, kTestHeight), frame->coded_size()); 377 EXPECT_EQ(media::VideoFrame::I420, frame->format()); 378 EXPECT_LE( 379 0, 380 buffer_pool_->RecognizeReservedBuffer(frame->shared_memory_handle())); 381 uint8 yuv[3]; 382 for (int plane = 0; plane < 3; ++plane) { 383 yuv[plane] = frame->data(plane)[0]; 384 } 385 // TODO(nick): We just look at the first pixel presently, because if 386 // the analysis is too slow, the backlog of frames will grow without bound 387 // and trouble erupts. http://crbug.com/174519 388 PostColorOrError(SkColorSetRGB(yuv[0], yuv[1], yuv[2])); 389 } 390 391 void PostColorOrError(SkColor new_color) { 392 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, base::Bind( 393 &StubConsumer::QuitIfConditionMet, base::Unretained(this), new_color)); 394 } 395 396 virtual void OnError() OVERRIDE { 397 { 398 base::AutoLock guard(lock_); 399 error_encountered_ = true; 400 } 401 PostColorOrError(kNothingYet); 402 } 403 404 virtual void OnFrameInfo(const media::VideoCaptureCapability& info) OVERRIDE { 405 EXPECT_EQ(kTestWidth, info.width); 406 EXPECT_EQ(kTestHeight, info.height); 407 EXPECT_EQ(kTestFramesPerSecond, info.frame_rate); 408 EXPECT_EQ(media::VideoCaptureCapability::kI420, info.color); 409 } 410 411 private: 412 base::Lock lock_; 413 bool error_encountered_; 414 SkColor wait_color_yuv_; 415 scoped_refptr<VideoCaptureBufferPool> buffer_pool_; 416 417 DISALLOW_COPY_AND_ASSIGN(StubConsumer); 418 }; 419 420 // Test harness that sets up a minimal environment with necessary stubs. 421 class WebContentsVideoCaptureDeviceTest : public testing::Test { 422 public: 423 // This is public because C++ method pointer scoping rules are silly and make 424 // this hard to use with Bind(). 425 void ResetWebContents() { 426 web_contents_.reset(); 427 } 428 429 protected: 430 virtual void SetUp() { 431 // TODO(nick): Sadness and woe! Much "mock-the-world" boilerplate could be 432 // eliminated here, if only we could use RenderViewHostTestHarness. The 433 // catch is that we need our TestRenderViewHost to support a 434 // CopyFromBackingStore operation that we control. To accomplish that, 435 // either RenderViewHostTestHarness would have to support installing a 436 // custom RenderViewHostFactory, or else we implant some kind of delegated 437 // CopyFromBackingStore functionality into TestRenderViewHost itself. 438 439 render_process_host_factory_.reset(new MockRenderProcessHostFactory()); 440 // Create our (self-registering) RVH factory, so that when we create a 441 // WebContents, it in turn creates CaptureTestRenderViewHosts. 442 render_view_host_factory_.reset( 443 new CaptureTestRenderViewHostFactory(&controller_)); 444 445 browser_context_.reset(new TestBrowserContext()); 446 447 scoped_refptr<SiteInstance> site_instance = 448 SiteInstance::Create(browser_context_.get()); 449 SiteInstanceImpl::set_render_process_host_factory( 450 render_process_host_factory_.get()); 451 web_contents_.reset( 452 TestWebContents::Create(browser_context_.get(), site_instance.get())); 453 454 // This is actually a CaptureTestRenderViewHost. 455 RenderWidgetHostImpl* rwh = 456 RenderWidgetHostImpl::From(web_contents_->GetRenderViewHost()); 457 458 std::string device_id = 459 WebContentsCaptureUtil::AppendWebContentsDeviceScheme( 460 base::StringPrintf("%d:%d", rwh->GetProcess()->GetID(), 461 rwh->GetRoutingID())); 462 463 device_.reset(WebContentsVideoCaptureDevice::Create(device_id)); 464 465 base::RunLoop().RunUntilIdle(); 466 } 467 468 virtual void TearDown() { 469 // Tear down in opposite order of set-up. 470 471 // The device is destroyed asynchronously, and will notify the 472 // CaptureTestSourceController when it finishes destruction. 473 // Trigger this, and wait. 474 if (device_) { 475 device_->DeAllocate(); 476 device_.reset(); 477 } 478 479 base::RunLoop().RunUntilIdle(); 480 481 // Destroy the browser objects. 482 web_contents_.reset(); 483 browser_context_.reset(); 484 485 base::RunLoop().RunUntilIdle(); 486 487 SiteInstanceImpl::set_render_process_host_factory(NULL); 488 render_view_host_factory_.reset(); 489 render_process_host_factory_.reset(); 490 } 491 492 // Accessors. 493 CaptureTestSourceController* source() { return &controller_; } 494 media::VideoCaptureDevice* device() { return device_.get(); } 495 StubConsumer* consumer() { return &consumer_; } 496 497 void SimulateDrawEvent() { 498 if (source()->CanUseFrameSubscriber()) { 499 // Print 500 CaptureTestView* test_view = static_cast<CaptureTestView*>( 501 web_contents_->GetRenderViewHost()->GetView()); 502 test_view->SimulateUpdate(); 503 } else { 504 // Simulate a non-accelerated paint. 505 NotificationService::current()->Notify( 506 NOTIFICATION_RENDER_WIDGET_HOST_DID_UPDATE_BACKING_STORE, 507 Source<RenderWidgetHost>(web_contents_->GetRenderViewHost()), 508 NotificationService::NoDetails()); 509 } 510 } 511 512 void DestroyVideoCaptureDevice() { device_.reset(); } 513 514 private: 515 // The consumer is the ultimate recipient of captured pixel data. 516 StubConsumer consumer_; 517 518 // The controller controls which pixel patterns to produce. 519 CaptureTestSourceController controller_; 520 521 // Self-registering RenderProcessHostFactory. 522 scoped_ptr<MockRenderProcessHostFactory> render_process_host_factory_; 523 524 // Creates capture-capable RenderViewHosts whose pixel content production is 525 // under the control of |controller_|. 526 scoped_ptr<CaptureTestRenderViewHostFactory> render_view_host_factory_; 527 528 // A mocked-out browser and tab. 529 scoped_ptr<TestBrowserContext> browser_context_; 530 scoped_ptr<WebContents> web_contents_; 531 532 // Finally, the WebContentsVideoCaptureDevice under test. 533 scoped_ptr<media::VideoCaptureDevice> device_; 534 535 TestBrowserThreadBundle thread_bundle_; 536 }; 537 538 TEST_F(WebContentsVideoCaptureDeviceTest, InvalidInitialWebContentsError) { 539 // Before the installs itself on the UI thread up to start capturing, we'll 540 // delete the web contents. This should trigger an error which can happen in 541 // practice; we should be able to recover gracefully. 542 ResetWebContents(); 543 544 media::VideoCaptureCapability capture_format( 545 kTestWidth, 546 kTestHeight, 547 kTestFramesPerSecond, 548 media::VideoCaptureCapability::kI420, 549 0, 550 false, 551 media::ConstantResolutionVideoCaptureDevice); 552 device()->Allocate(capture_format, consumer()); 553 device()->Start(); 554 ASSERT_NO_FATAL_FAILURE(consumer()->WaitForError()); 555 device()->DeAllocate(); 556 } 557 558 TEST_F(WebContentsVideoCaptureDeviceTest, WebContentsDestroyed) { 559 // We'll simulate the tab being closed after the capture pipeline is up and 560 // running. 561 media::VideoCaptureCapability capture_format( 562 kTestWidth, 563 kTestHeight, 564 kTestFramesPerSecond, 565 media::VideoCaptureCapability::kI420, 566 0, 567 false, 568 media::ConstantResolutionVideoCaptureDevice); 569 device()->Allocate(capture_format, consumer()); 570 device()->Start(); 571 572 // Do one capture to prove 573 source()->SetSolidColor(SK_ColorRED); 574 SimulateDrawEvent(); 575 ASSERT_NO_FATAL_FAILURE(consumer()->WaitForNextColor(SK_ColorRED)); 576 577 base::RunLoop().RunUntilIdle(); 578 579 // Post a task to close the tab. We should see an error reported to the 580 // consumer. 581 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, 582 base::Bind(&WebContentsVideoCaptureDeviceTest::ResetWebContents, 583 base::Unretained(this))); 584 ASSERT_NO_FATAL_FAILURE(consumer()->WaitForError()); 585 device()->DeAllocate(); 586 } 587 588 TEST_F(WebContentsVideoCaptureDeviceTest, 589 StopDeviceBeforeCaptureMachineCreation) { 590 media::VideoCaptureCapability capture_format( 591 kTestWidth, 592 kTestHeight, 593 kTestFramesPerSecond, 594 media::VideoCaptureCapability::kI420, 595 0, 596 false, 597 media::ConstantResolutionVideoCaptureDevice); 598 device()->Allocate(capture_format, consumer()); 599 device()->Start(); 600 // Make a point of not running the UI messageloop here. 601 device()->Stop(); 602 device()->DeAllocate(); 603 DestroyVideoCaptureDevice(); 604 605 // Currently, there should be CreateCaptureMachineOnUIThread() and 606 // DestroyCaptureMachineOnUIThread() tasks pending on the current (UI) message 607 // loop. These should both succeed without crashing, and the machine should 608 // wind up in the idle state. 609 base::RunLoop().RunUntilIdle(); 610 } 611 612 TEST_F(WebContentsVideoCaptureDeviceTest, StopWithRendererWorkToDo) { 613 // Set up the test to use RGB copies and an normal 614 source()->SetCanCopyToVideoFrame(false); 615 source()->SetUseFrameSubscriber(false); 616 media::VideoCaptureCapability capture_format( 617 kTestWidth, 618 kTestHeight, 619 kTestFramesPerSecond, 620 media::VideoCaptureCapability::kI420, 621 0, 622 false, 623 media::ConstantResolutionVideoCaptureDevice); 624 device()->Allocate(capture_format, consumer()); 625 626 device()->Start(); 627 // Make a point of not running the UI messageloop here. 628 // TODO(ajwong): Why do we care? 629 base::RunLoop().RunUntilIdle(); 630 631 for (int i = 0; i < 10; ++i) 632 SimulateDrawEvent(); 633 634 device()->Stop(); 635 device()->DeAllocate(); 636 // Currently, there should be CreateCaptureMachineOnUIThread() and 637 // DestroyCaptureMachineOnUIThread() tasks pending on the current message 638 // loop. These should both succeed without crashing, and the machine should 639 // wind up in the idle state. 640 ASSERT_FALSE(consumer()->HasError()); 641 base::RunLoop().RunUntilIdle(); 642 ASSERT_FALSE(consumer()->HasError()); 643 } 644 645 TEST_F(WebContentsVideoCaptureDeviceTest, DeviceRestart) { 646 media::VideoCaptureCapability capture_format( 647 kTestWidth, 648 kTestHeight, 649 kTestFramesPerSecond, 650 media::VideoCaptureCapability::kI420, 651 0, 652 false, 653 media::ConstantResolutionVideoCaptureDevice); 654 device()->Allocate(capture_format, consumer()); 655 device()->Start(); 656 base::RunLoop().RunUntilIdle(); 657 source()->SetSolidColor(SK_ColorRED); 658 SimulateDrawEvent(); 659 SimulateDrawEvent(); 660 ASSERT_NO_FATAL_FAILURE(consumer()->WaitForNextColor(SK_ColorRED)); 661 SimulateDrawEvent(); 662 SimulateDrawEvent(); 663 source()->SetSolidColor(SK_ColorGREEN); 664 SimulateDrawEvent(); 665 ASSERT_NO_FATAL_FAILURE(consumer()->WaitForNextColor(SK_ColorGREEN)); 666 device()->Stop(); 667 668 // Device is stopped, but content can still be animating. 669 SimulateDrawEvent(); 670 SimulateDrawEvent(); 671 base::RunLoop().RunUntilIdle(); 672 673 device()->Start(); 674 source()->SetSolidColor(SK_ColorBLUE); 675 SimulateDrawEvent(); 676 ASSERT_NO_FATAL_FAILURE(consumer()->WaitForNextColor(SK_ColorBLUE)); 677 source()->SetSolidColor(SK_ColorYELLOW); 678 SimulateDrawEvent(); 679 ASSERT_NO_FATAL_FAILURE(consumer()->WaitForNextColor(SK_ColorYELLOW)); 680 device()->DeAllocate(); 681 } 682 683 // The "happy case" test. No scaling is needed, so we should be able to change 684 // the picture emitted from the source and expect to see each delivered to the 685 // consumer. The test will alternate between the three capture paths, simulating 686 // falling in and out of accelerated compositing. 687 TEST_F(WebContentsVideoCaptureDeviceTest, GoesThroughAllTheMotions) { 688 media::VideoCaptureCapability capture_format( 689 kTestWidth, 690 kTestHeight, 691 kTestFramesPerSecond, 692 media::VideoCaptureCapability::kI420, 693 0, 694 false, 695 media::ConstantResolutionVideoCaptureDevice); 696 device()->Allocate(capture_format, consumer()); 697 698 device()->Start(); 699 700 for (int i = 0; i < 6; i++) { 701 const char* name = NULL; 702 switch (i % 3) { 703 case 0: 704 source()->SetCanCopyToVideoFrame(true); 705 source()->SetUseFrameSubscriber(false); 706 name = "VideoFrame"; 707 break; 708 case 1: 709 source()->SetCanCopyToVideoFrame(false); 710 source()->SetUseFrameSubscriber(true); 711 name = "Subscriber"; 712 break; 713 case 2: 714 source()->SetCanCopyToVideoFrame(false); 715 source()->SetUseFrameSubscriber(false); 716 name = "SkBitmap"; 717 break; 718 default: 719 FAIL(); 720 } 721 722 SCOPED_TRACE(base::StringPrintf("Using %s path, iteration #%d", name, i)); 723 724 source()->SetSolidColor(SK_ColorRED); 725 SimulateDrawEvent(); 726 ASSERT_NO_FATAL_FAILURE(consumer()->WaitForNextColor(SK_ColorRED)); 727 728 source()->SetSolidColor(SK_ColorGREEN); 729 SimulateDrawEvent(); 730 ASSERT_NO_FATAL_FAILURE(consumer()->WaitForNextColor(SK_ColorGREEN)); 731 732 source()->SetSolidColor(SK_ColorBLUE); 733 SimulateDrawEvent(); 734 ASSERT_NO_FATAL_FAILURE(consumer()->WaitForNextColor(SK_ColorBLUE)); 735 736 source()->SetSolidColor(SK_ColorBLACK); 737 SimulateDrawEvent(); 738 ASSERT_NO_FATAL_FAILURE(consumer()->WaitForNextColor(SK_ColorBLACK)); 739 } 740 device()->DeAllocate(); 741 } 742 743 TEST_F(WebContentsVideoCaptureDeviceTest, RejectsInvalidAllocateParams) { 744 media::VideoCaptureCapability capture_format( 745 1280, 746 720, 747 -2, 748 media::VideoCaptureCapability::kI420, 749 0, 750 false, 751 media::ConstantResolutionVideoCaptureDevice); 752 BrowserThread::PostTask(BrowserThread::UI, 753 FROM_HERE, 754 base::Bind(&media::VideoCaptureDevice::Allocate, 755 base::Unretained(device()), 756 capture_format, 757 consumer())); 758 ASSERT_NO_FATAL_FAILURE(consumer()->WaitForError()); 759 } 760 761 TEST_F(WebContentsVideoCaptureDeviceTest, BadFramesGoodFrames) { 762 media::VideoCaptureCapability capture_format( 763 kTestWidth, 764 kTestHeight, 765 kTestFramesPerSecond, 766 media::VideoCaptureCapability::kI420, 767 0, 768 false, 769 media::ConstantResolutionVideoCaptureDevice); 770 device()->Allocate(capture_format, consumer()); 771 772 // 1x1 is too small to process; we intend for this to result in an error. 773 source()->SetCopyResultSize(1, 1); 774 source()->SetSolidColor(SK_ColorRED); 775 device()->Start(); 776 777 // These frames ought to be dropped during the Render stage. Let 778 // several captures to happen. 779 ASSERT_NO_FATAL_FAILURE(source()->WaitForNextCopy()); 780 ASSERT_NO_FATAL_FAILURE(source()->WaitForNextCopy()); 781 ASSERT_NO_FATAL_FAILURE(source()->WaitForNextCopy()); 782 ASSERT_NO_FATAL_FAILURE(source()->WaitForNextCopy()); 783 ASSERT_NO_FATAL_FAILURE(source()->WaitForNextCopy()); 784 785 // Now push some good frames through; they should be processed normally. 786 source()->SetCopyResultSize(kTestWidth, kTestHeight); 787 source()->SetSolidColor(SK_ColorGREEN); 788 ASSERT_NO_FATAL_FAILURE(consumer()->WaitForNextColor(SK_ColorGREEN)); 789 source()->SetSolidColor(SK_ColorRED); 790 ASSERT_NO_FATAL_FAILURE(consumer()->WaitForNextColor(SK_ColorRED)); 791 792 device()->Stop(); 793 device()->DeAllocate(); 794 } 795 796 } // namespace 797 } // namespace content 798