Home | History | Annotate | Download | only in capture
      1 // Copyright (c) 2013 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/desktop_capture_device.h"
      6 
      7 #include "base/basictypes.h"
      8 #include "base/sequenced_task_runner.h"
      9 #include "base/synchronization/waitable_event.h"
     10 #include "base/test/test_timeouts.h"
     11 #include "base/threading/sequenced_worker_pool.h"
     12 #include "base/threading/thread.h"
     13 #include "base/time/time.h"
     14 #include "testing/gmock/include/gmock/gmock.h"
     15 #include "testing/gtest/include/gtest/gtest.h"
     16 #include "third_party/webrtc/modules/desktop_capture/desktop_frame.h"
     17 #include "third_party/webrtc/modules/desktop_capture/desktop_geometry.h"
     18 #include "third_party/webrtc/modules/desktop_capture/screen_capturer.h"
     19 
     20 using ::testing::_;
     21 using ::testing::AnyNumber;
     22 using ::testing::DoAll;
     23 using ::testing::Expectation;
     24 using ::testing::InvokeWithoutArgs;
     25 using ::testing::SaveArg;
     26 
     27 namespace content {
     28 
     29 namespace {
     30 
     31 MATCHER_P2(EqualsCaptureCapability, width, height, "") {
     32   return arg.width == width && arg.height == height;
     33 }
     34 
     35 const int kTestFrameWidth1 = 100;
     36 const int kTestFrameHeight1 = 100;
     37 const int kTestFrameWidth2 = 200;
     38 const int kTestFrameHeight2 = 150;
     39 
     40 const int kFrameRate = 30;
     41 
     42 class MockDeviceClient : public media::VideoCaptureDevice::Client {
     43  public:
     44   MOCK_METHOD2(ReserveOutputBuffer,
     45                scoped_refptr<Buffer>(media::VideoFrame::Format format,
     46                                      const gfx::Size& dimensions));
     47   MOCK_METHOD1(OnError, void(const std::string& reason));
     48   MOCK_METHOD5(OnIncomingCapturedData,
     49                void(const uint8* data,
     50                     int length,
     51                     const media::VideoCaptureFormat& frame_format,
     52                     int rotation,
     53                     base::TimeTicks timestamp));
     54   MOCK_METHOD4(OnIncomingCapturedVideoFrame,
     55                void(const scoped_refptr<Buffer>& buffer,
     56                     const media::VideoCaptureFormat& buffer_format,
     57                     const scoped_refptr<media::VideoFrame>& frame,
     58                     base::TimeTicks timestamp));
     59 };
     60 
     61 // DesktopFrame wrapper that flips wrapped frame upside down by inverting
     62 // stride.
     63 class InvertedDesktopFrame : public webrtc::DesktopFrame {
     64  public:
     65   // Takes ownership of |frame|.
     66   InvertedDesktopFrame(webrtc::DesktopFrame* frame)
     67       : webrtc::DesktopFrame(
     68             frame->size(), -frame->stride(),
     69             frame->data() + (frame->size().height() - 1) * frame->stride(),
     70             frame->shared_memory()),
     71         original_frame_(frame) {
     72     set_dpi(frame->dpi());
     73     set_capture_time_ms(frame->capture_time_ms());
     74     mutable_updated_region()->Swap(frame->mutable_updated_region());
     75   }
     76   virtual ~InvertedDesktopFrame() {}
     77 
     78  private:
     79   scoped_ptr<webrtc::DesktopFrame> original_frame_;
     80 
     81   DISALLOW_COPY_AND_ASSIGN(InvertedDesktopFrame);
     82 };
     83 
     84 // TODO(sergeyu): Move this to a separate file where it can be reused.
     85 class FakeScreenCapturer : public webrtc::ScreenCapturer {
     86  public:
     87   FakeScreenCapturer()
     88       : callback_(NULL),
     89         frame_index_(0),
     90         generate_inverted_frames_(false) {
     91   }
     92   virtual ~FakeScreenCapturer() {}
     93 
     94   void set_generate_inverted_frames(bool generate_inverted_frames) {
     95     generate_inverted_frames_ = generate_inverted_frames;
     96   }
     97 
     98   // VideoFrameCapturer interface.
     99   virtual void Start(Callback* callback) OVERRIDE {
    100     callback_ = callback;
    101   }
    102 
    103   virtual void Capture(const webrtc::DesktopRegion& region) OVERRIDE {
    104     webrtc::DesktopSize size;
    105     if (frame_index_ % 2 == 0) {
    106       size = webrtc::DesktopSize(kTestFrameWidth1, kTestFrameHeight1);
    107     } else {
    108       size = webrtc::DesktopSize(kTestFrameWidth2, kTestFrameHeight2);
    109     }
    110     frame_index_++;
    111 
    112     webrtc::DesktopFrame* frame = new webrtc::BasicDesktopFrame(size);
    113     if (generate_inverted_frames_)
    114       frame = new InvertedDesktopFrame(frame);
    115     callback_->OnCaptureCompleted(frame);
    116   }
    117 
    118   virtual void SetMouseShapeObserver(
    119       MouseShapeObserver* mouse_shape_observer) OVERRIDE {
    120   }
    121 
    122   virtual bool GetScreenList(ScreenList* screens) OVERRIDE {
    123     return false;
    124   }
    125 
    126   virtual bool SelectScreen(webrtc::ScreenId id) OVERRIDE {
    127     return false;
    128   }
    129 
    130  private:
    131   Callback* callback_;
    132   int frame_index_;
    133   bool generate_inverted_frames_;
    134 };
    135 
    136 }  // namespace
    137 
    138 class DesktopCaptureDeviceTest : public testing::Test {
    139  public:
    140   virtual void SetUp() OVERRIDE {
    141     worker_pool_ = new base::SequencedWorkerPool(3, "TestCaptureThread");
    142   }
    143 
    144   void CreateScreenCaptureDevice(scoped_ptr<webrtc::DesktopCapturer> capturer) {
    145     capture_device_.reset(new DesktopCaptureDevice(
    146         worker_pool_->GetSequencedTaskRunner(worker_pool_->GetSequenceToken()),
    147         thread_.Pass(),
    148         capturer.Pass(),
    149         DesktopMediaID::TYPE_SCREEN));
    150   }
    151 
    152  protected:
    153   scoped_refptr<base::SequencedWorkerPool> worker_pool_;
    154   scoped_ptr<base::Thread> thread_;
    155   scoped_ptr<DesktopCaptureDevice> capture_device_;
    156 };
    157 
    158 // There is currently no screen capturer implementation for ozone. So disable
    159 // the test that uses a real screen-capturer instead of FakeScreenCapturer.
    160 // http://crbug.com/260318
    161 #if defined(USE_OZONE)
    162 #define MAYBE_Capture DISABLED_Capture
    163 #else
    164 #define MAYBE_Capture Capture
    165 #endif
    166 TEST_F(DesktopCaptureDeviceTest, MAYBE_Capture) {
    167   scoped_ptr<webrtc::DesktopCapturer> capturer(
    168       webrtc::ScreenCapturer::Create());
    169   CreateScreenCaptureDevice(capturer.Pass());
    170 
    171   media::VideoCaptureFormat format;
    172   base::WaitableEvent done_event(false, false);
    173   int frame_size;
    174 
    175   scoped_ptr<MockDeviceClient> client(new MockDeviceClient());
    176   EXPECT_CALL(*client, OnError(_)).Times(0);
    177   EXPECT_CALL(*client, OnIncomingCapturedData(_, _, _, _, _)).WillRepeatedly(
    178       DoAll(SaveArg<1>(&frame_size),
    179             SaveArg<2>(&format),
    180             InvokeWithoutArgs(&done_event, &base::WaitableEvent::Signal)));
    181 
    182   media::VideoCaptureParams capture_params;
    183   capture_params.requested_format.frame_size.SetSize(640, 480);
    184   capture_params.requested_format.frame_rate = kFrameRate;
    185   capture_params.requested_format.pixel_format = media::PIXEL_FORMAT_I420;
    186   capture_params.allow_resolution_change = false;
    187   capture_device_->AllocateAndStart(
    188       capture_params, client.PassAs<media::VideoCaptureDevice::Client>());
    189   EXPECT_TRUE(done_event.TimedWait(TestTimeouts::action_max_timeout()));
    190   capture_device_->StopAndDeAllocate();
    191 
    192   EXPECT_GT(format.frame_size.width(), 0);
    193   EXPECT_GT(format.frame_size.height(), 0);
    194   EXPECT_EQ(kFrameRate, format.frame_rate);
    195   EXPECT_EQ(media::PIXEL_FORMAT_ARGB, format.pixel_format);
    196 
    197   EXPECT_EQ(format.frame_size.GetArea() * 4, frame_size);
    198   worker_pool_->FlushForTesting();
    199 }
    200 
    201 // Test that screen capturer behaves correctly if the source frame size changes
    202 // but the caller cannot cope with variable resolution output.
    203 TEST_F(DesktopCaptureDeviceTest, ScreenResolutionChangeConstantResolution) {
    204   FakeScreenCapturer* mock_capturer = new FakeScreenCapturer();
    205 
    206   CreateScreenCaptureDevice(scoped_ptr<webrtc::DesktopCapturer>(mock_capturer));
    207 
    208   media::VideoCaptureFormat format;
    209   base::WaitableEvent done_event(false, false);
    210   int frame_size;
    211 
    212   scoped_ptr<MockDeviceClient> client(new MockDeviceClient());
    213   EXPECT_CALL(*client, OnError(_)).Times(0);
    214   EXPECT_CALL(*client, OnIncomingCapturedData(_, _, _, _, _)).WillRepeatedly(
    215       DoAll(SaveArg<1>(&frame_size),
    216             SaveArg<2>(&format),
    217             InvokeWithoutArgs(&done_event, &base::WaitableEvent::Signal)));
    218 
    219   media::VideoCaptureParams capture_params;
    220   capture_params.requested_format.frame_size.SetSize(kTestFrameWidth1,
    221                                                      kTestFrameHeight1);
    222   capture_params.requested_format.frame_rate = kFrameRate;
    223   capture_params.requested_format.pixel_format = media::PIXEL_FORMAT_I420;
    224   capture_params.allow_resolution_change = false;
    225 
    226   capture_device_->AllocateAndStart(
    227       capture_params, client.PassAs<media::VideoCaptureDevice::Client>());
    228 
    229   // Capture at least two frames, to ensure that the source frame size has
    230   // changed while capturing.
    231   EXPECT_TRUE(done_event.TimedWait(TestTimeouts::action_max_timeout()));
    232   done_event.Reset();
    233   EXPECT_TRUE(done_event.TimedWait(TestTimeouts::action_max_timeout()));
    234 
    235   capture_device_->StopAndDeAllocate();
    236 
    237   EXPECT_EQ(kTestFrameWidth1, format.frame_size.width());
    238   EXPECT_EQ(kTestFrameHeight1, format.frame_size.height());
    239   EXPECT_EQ(kFrameRate, format.frame_rate);
    240   EXPECT_EQ(media::PIXEL_FORMAT_ARGB, format.pixel_format);
    241 
    242   EXPECT_EQ(format.frame_size.GetArea() * 4, frame_size);
    243   worker_pool_->FlushForTesting();
    244 }
    245 
    246 // Test that screen capturer behaves correctly if the source frame size changes
    247 // and the caller can cope with variable resolution output.
    248 TEST_F(DesktopCaptureDeviceTest, ScreenResolutionChangeVariableResolution) {
    249   FakeScreenCapturer* mock_capturer = new FakeScreenCapturer();
    250 
    251   CreateScreenCaptureDevice(scoped_ptr<webrtc::DesktopCapturer>(mock_capturer));
    252 
    253   media::VideoCaptureFormat format;
    254   base::WaitableEvent done_event(false, false);
    255 
    256   scoped_ptr<MockDeviceClient> client(new MockDeviceClient());
    257   EXPECT_CALL(*client, OnError(_)).Times(0);
    258   EXPECT_CALL(*client, OnIncomingCapturedData(_, _, _, _, _)).WillRepeatedly(
    259       DoAll(SaveArg<2>(&format),
    260             InvokeWithoutArgs(&done_event, &base::WaitableEvent::Signal)));
    261 
    262   media::VideoCaptureParams capture_params;
    263   capture_params.requested_format.frame_size.SetSize(kTestFrameWidth2,
    264                                                      kTestFrameHeight2);
    265   capture_params.requested_format.frame_rate = kFrameRate;
    266   capture_params.requested_format.pixel_format = media::PIXEL_FORMAT_I420;
    267   capture_params.allow_resolution_change = false;
    268 
    269   capture_device_->AllocateAndStart(
    270       capture_params, client.PassAs<media::VideoCaptureDevice::Client>());
    271 
    272   // Capture at least three frames, to ensure that the source frame size has
    273   // changed at least twice while capturing.
    274   EXPECT_TRUE(done_event.TimedWait(TestTimeouts::action_max_timeout()));
    275   done_event.Reset();
    276   EXPECT_TRUE(done_event.TimedWait(TestTimeouts::action_max_timeout()));
    277   done_event.Reset();
    278   EXPECT_TRUE(done_event.TimedWait(TestTimeouts::action_max_timeout()));
    279 
    280   capture_device_->StopAndDeAllocate();
    281 
    282   EXPECT_EQ(kTestFrameWidth1, format.frame_size.width());
    283   EXPECT_EQ(kTestFrameHeight1, format.frame_size.height());
    284   EXPECT_EQ(kFrameRate, format.frame_rate);
    285   EXPECT_EQ(media::PIXEL_FORMAT_ARGB, format.pixel_format);
    286   worker_pool_->FlushForTesting();
    287 }
    288 
    289 }  // namespace content
    290