Home | History | Annotate | Download | only in desktop_capture
      1 /*
      2  *  Copyright (c) 2013 The WebRTC project authors. All Rights Reserved.
      3  *
      4  *  Use of this source code is governed by a BSD-style license
      5  *  that can be found in the LICENSE file in the root of the source
      6  *  tree. An additional intellectual property rights grant can be found
      7  *  in the file PATENTS.  All contributing project authors may
      8  *  be found in the AUTHORS file in the root of the source tree.
      9  */
     10 
     11 #include "webrtc/modules/desktop_capture/desktop_and_cursor_composer.h"
     12 
     13 #include "gtest/gtest.h"
     14 #include "webrtc/modules/desktop_capture/desktop_capture_options.h"
     15 #include "webrtc/modules/desktop_capture/desktop_frame.h"
     16 #include "webrtc/modules/desktop_capture/mouse_cursor.h"
     17 #include "webrtc/modules/desktop_capture/shared_desktop_frame.h"
     18 #include "webrtc/modules/desktop_capture/window_capturer.h"
     19 #include "webrtc/system_wrappers/interface/logging.h"
     20 #include "webrtc/system_wrappers/interface/scoped_ptr.h"
     21 
     22 namespace webrtc {
     23 
     24 namespace {
     25 
     26 const int kScreenWidth = 100;
     27 const int kScreenHeight = 100;
     28 const int kCursorWidth = 10;
     29 const int kCursorHeight = 10;
     30 
     31 const int kTestCursorSize = 3;
     32 const uint32_t kTestCursorData[kTestCursorSize][kTestCursorSize] = {
     33   { 0xffffffff, 0x99990000, 0xaa222222, },
     34   { 0x88008800, 0xaa0000aa, 0xaa333333, },
     35   { 0x00000000, 0xaa0000aa, 0xaa333333, },
     36 };
     37 
     38 uint32_t GetFakeFramePixelValue(const DesktopVector& p) {
     39   uint32_t r = 100 + p.x();
     40   uint32_t g = 100 + p.y();
     41   uint32_t b = 100 + p.x() + p.y();
     42   return b + (g << 8) + (r << 16) + 0xff000000;
     43 }
     44 
     45 uint32_t GetFramePixel(const DesktopFrame& frame, const DesktopVector& pos) {
     46   return *reinterpret_cast<uint32_t*>(frame.data() + pos.y() * frame.stride() +
     47                                       pos.x() * DesktopFrame::kBytesPerPixel);
     48 }
     49 
     50 // Blends two pixel values taking into account alpha.
     51 uint32_t BlendPixels(uint32_t dest, uint32_t src) {
     52   uint8_t alpha = 255 - ((src & 0xff000000) >> 24);
     53   uint32_t r =
     54       ((dest & 0x00ff0000) >> 16) * alpha / 255 + ((src & 0x00ff0000) >> 16);
     55   uint32_t g =
     56       ((dest & 0x0000ff00) >> 8) * alpha / 255 + ((src & 0x0000ff00) >> 8);
     57   uint32_t b = (dest & 0x000000ff) * alpha / 255 + (src & 0x000000ff);
     58   return b + (g << 8) + (r << 16) + 0xff000000;
     59 }
     60 
     61 DesktopFrame* CreateTestFrame() {
     62   DesktopFrame* frame =
     63       new BasicDesktopFrame(DesktopSize(kScreenWidth, kScreenHeight));
     64   uint32_t* data = reinterpret_cast<uint32_t*>(frame->data());
     65   for (int y = 0; y < kScreenHeight; ++y) {
     66     for (int x = 0; x < kScreenWidth; ++x) {
     67       *(data++) = GetFakeFramePixelValue(DesktopVector(x, y));
     68     }
     69   }
     70   return frame;
     71 }
     72 
     73 class FakeScreenCapturer : public DesktopCapturer {
     74  public:
     75   FakeScreenCapturer() {}
     76 
     77   virtual void Start(Callback* callback) OVERRIDE {
     78     callback_ = callback;
     79   }
     80 
     81   virtual void Capture(const DesktopRegion& region) OVERRIDE {
     82     callback_->OnCaptureCompleted(next_frame_.release());
     83   }
     84 
     85   void SetNextFrame(DesktopFrame* next_frame) {
     86     next_frame_.reset(next_frame);
     87   }
     88 
     89  private:
     90   Callback* callback_;
     91 
     92   scoped_ptr<DesktopFrame> next_frame_;
     93 };
     94 
     95 class FakeMouseMonitor : public MouseCursorMonitor {
     96  public:
     97   FakeMouseMonitor() : changed_(true) {}
     98 
     99   void SetState(CursorState state, const DesktopVector& pos) {
    100     state_ = state;
    101     position_ = pos;
    102   }
    103 
    104   void SetHotspot(const DesktopVector& hotspot) {
    105     if (!hotspot_.equals(hotspot))
    106       changed_ = true;
    107     hotspot_ = hotspot;
    108   }
    109 
    110   virtual void Init(Callback* callback, Mode mode) OVERRIDE {
    111     callback_ = callback;
    112   }
    113 
    114   virtual void Capture() OVERRIDE {
    115     if (changed_) {
    116       scoped_ptr<DesktopFrame> image(
    117           new BasicDesktopFrame(DesktopSize(kCursorWidth, kCursorHeight)));
    118       uint32_t* data = reinterpret_cast<uint32_t*>(image->data());
    119       memset(data, 0, image->stride() * kCursorHeight);
    120 
    121       // Set four pixels near the hotspot and leave all other blank.
    122       for (int y = 0; y < kTestCursorSize; ++y) {
    123         for (int x = 0; x < kTestCursorSize; ++x) {
    124           data[(hotspot_.y() + y) * kCursorWidth + (hotspot_.x() + x)] =
    125               kTestCursorData[y][x];
    126         }
    127       }
    128 
    129       callback_->OnMouseCursor(new MouseCursor(image.release(), hotspot_));
    130     }
    131 
    132     callback_->OnMouseCursorPosition(state_, position_);
    133   }
    134 
    135  private:
    136   Callback* callback_;
    137   CursorState state_;
    138   DesktopVector position_;
    139   DesktopVector hotspot_;
    140   bool changed_;
    141 };
    142 
    143 void VerifyFrame(const DesktopFrame& frame,
    144                  MouseCursorMonitor::CursorState state,
    145                  const DesktopVector& pos) {
    146   // Verify that all other pixels are set to their original values.
    147   DesktopRect image_rect =
    148       DesktopRect::MakeWH(kTestCursorSize, kTestCursorSize);
    149   image_rect.Translate(pos);
    150 
    151   for (int y = 0; y < kScreenHeight; ++y) {
    152     for (int x = 0; x < kScreenWidth; ++x) {
    153       DesktopVector p(x, y);
    154       if (state == MouseCursorMonitor::INSIDE && image_rect.Contains(p)) {
    155         EXPECT_EQ(BlendPixels(GetFakeFramePixelValue(p),
    156                               kTestCursorData[y - pos.y()][x - pos.x()]),
    157                   GetFramePixel(frame, p));
    158       } else {
    159         EXPECT_EQ(GetFakeFramePixelValue(p), GetFramePixel(frame, p));
    160       }
    161     }
    162   }
    163 }
    164 
    165 class DesktopAndCursorComposerTest : public testing::Test,
    166                                      public DesktopCapturer::Callback {
    167  public:
    168   DesktopAndCursorComposerTest()
    169       : fake_screen_(new FakeScreenCapturer()),
    170         fake_cursor_(new FakeMouseMonitor()),
    171         blender_(fake_screen_, fake_cursor_) {
    172   }
    173 
    174   // DesktopCapturer::Callback interface
    175   virtual SharedMemory* CreateSharedMemory(size_t size) OVERRIDE {
    176     return NULL;
    177   }
    178 
    179   virtual void OnCaptureCompleted(DesktopFrame* frame) OVERRIDE {
    180     frame_.reset(frame);
    181   }
    182 
    183  protected:
    184   // Owned by |blender_|.
    185   FakeScreenCapturer* fake_screen_;
    186   FakeMouseMonitor* fake_cursor_;
    187 
    188   DesktopAndCursorComposer blender_;
    189   scoped_ptr<DesktopFrame> frame_;
    190 };
    191 
    192 // Verify DesktopAndCursorComposer can handle the case when the screen capturer
    193 // fails.
    194 TEST_F(DesktopAndCursorComposerTest, Error) {
    195   blender_.Start(this);
    196 
    197   fake_cursor_->SetHotspot(DesktopVector());
    198   fake_cursor_->SetState(MouseCursorMonitor::INSIDE, DesktopVector());
    199   fake_screen_->SetNextFrame(NULL);
    200 
    201   blender_.Capture(DesktopRegion());
    202 
    203   EXPECT_EQ(frame_, static_cast<DesktopFrame*>(NULL));
    204 }
    205 
    206 TEST_F(DesktopAndCursorComposerTest, Blend) {
    207   struct {
    208     int x, y;
    209     int hotspot_x, hotspot_y;
    210     bool inside;
    211   } tests[] = {
    212     {0, 0, 0, 0, true},
    213     {50, 50, 0, 0, true},
    214     {100, 50, 0, 0, true},
    215     {50, 100, 0, 0, true},
    216     {100, 100, 0, 0, true},
    217     {0, 0, 2, 5, true},
    218     {1, 1, 2, 5, true},
    219     {50, 50, 2, 5, true},
    220     {100, 100, 2, 5, true},
    221     {0, 0, 5, 2, true},
    222     {50, 50, 5, 2, true},
    223     {100, 100, 5, 2, true},
    224     {0, 0, 0, 0, false},
    225   };
    226 
    227   blender_.Start(this);
    228 
    229   for (size_t i = 0; i < (sizeof(tests) / sizeof(tests[0])); ++i) {
    230     SCOPED_TRACE(i);
    231 
    232     DesktopVector hotspot(tests[i].hotspot_x, tests[i].hotspot_y);
    233     fake_cursor_->SetHotspot(hotspot);
    234 
    235     MouseCursorMonitor::CursorState state = tests[i].inside
    236                                                 ? MouseCursorMonitor::INSIDE
    237                                                 : MouseCursorMonitor::OUTSIDE;
    238     DesktopVector pos(tests[i].x, tests[i].y);
    239     fake_cursor_->SetState(state, pos);
    240 
    241     scoped_ptr<SharedDesktopFrame> frame(
    242         SharedDesktopFrame::Wrap(CreateTestFrame()));
    243     fake_screen_->SetNextFrame(frame->Share());
    244 
    245     blender_.Capture(DesktopRegion());
    246 
    247     VerifyFrame(*frame_, state, pos);
    248 
    249     // Verify that the cursor is erased before the frame buffer is returned to
    250     // the screen capturer.
    251     frame_.reset();
    252     VerifyFrame(*frame, MouseCursorMonitor::OUTSIDE, DesktopVector());
    253   }
    254 }
    255 
    256 }  // namespace
    257 
    258 }  // namespace webrtc
    259