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