Home | History | Annotate | Download | only in media
      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