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