From 9d1c54ace0dc9f68da0152aa1ded2a8dba0a43ae Mon Sep 17 00:00:00 2001 From: zijiehe Date: Fri, 2 Sep 2016 19:10:34 -0700 Subject: [PATCH] [WebRTC] A real ScreenCapturer test We do not have a real ScreenCapturer test before. And after CL 2210443002, a new ScreenDrawer interface is added to the code base to draw various shapes on the screen. This change is to use ScreenDrawer to test ScreenCapturer. Besides test cases, some other changes are included, 1. A WaitForPendingPaintings() function in ScreenDrawer, to wait for a ScreenDrawer to finish all the pending draws. This function now only sleeps 50 milliseconds on X11 and 100 milliseconds on Windows. 2. A Color structure to help handle a big-endian or little-endian safe color and provide functions to compare with DesktopFrame::data(). Both ScreenDrawer and DesktopFrameGenerator (in change 2202443002) can use this class to create colors and compare with or paint to a DesktopFrame. 3. ScreenDrawer now uses Color structure instead of uint32_t. BUG=314516 TBR=kjellander@chromium.org Review-Url: https://codereview.webrtc.org/2268093002 Cr-Commit-Position: refs/heads/master@{#14058} --- webrtc/modules/BUILD.gn | 2 + webrtc/modules/desktop_capture/rgba_color.cc | 48 +++++ webrtc/modules/desktop_capture/rgba_color.h | 54 ++++++ .../screen_capturer_unittest.cc | 169 ++++++++++++++++-- .../modules/desktop_capture/screen_drawer.h | 28 ++- .../desktop_capture/screen_drawer_linux.cc | 76 +++++--- .../desktop_capture/screen_drawer_unittest.cc | 15 +- .../desktop_capture/screen_drawer_win.cc | 72 ++++++-- webrtc/modules/modules.gyp | 2 + 9 files changed, 400 insertions(+), 66 deletions(-) create mode 100644 webrtc/modules/desktop_capture/rgba_color.cc create mode 100644 webrtc/modules/desktop_capture/rgba_color.h diff --git a/webrtc/modules/BUILD.gn b/webrtc/modules/BUILD.gn index 367c8eed09..c3e24a1de7 100644 --- a/webrtc/modules/BUILD.gn +++ b/webrtc/modules/BUILD.gn @@ -508,6 +508,8 @@ if (rtc_include_tests) { sources += [ "desktop_capture/desktop_and_cursor_composer_unittest.cc", "desktop_capture/mouse_cursor_monitor_unittest.cc", + "desktop_capture/rgba_color.cc", + "desktop_capture/rgba_color.h", "desktop_capture/screen_capturer_helper_unittest.cc", "desktop_capture/screen_capturer_mac_unittest.cc", "desktop_capture/screen_capturer_mock_objects.h", diff --git a/webrtc/modules/desktop_capture/rgba_color.cc b/webrtc/modules/desktop_capture/rgba_color.cc new file mode 100644 index 0000000000..2342b46915 --- /dev/null +++ b/webrtc/modules/desktop_capture/rgba_color.cc @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2016 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "webrtc/modules/desktop_capture/rgba_color.h" + +namespace webrtc { + +namespace { + +bool AlphaEquals(uint8_t i, uint8_t j) { + // On Linux and Windows 8 or early version, '0' was returned for alpha channel + // from capturer APIs, on Windows 10, '255' was returned. So a workaround is + // to treat 0 as 255. + return i == j || ((i == 0 || i == 255) && (j == 0 || j == 255)); +} + +} // namespace + +RgbaColor::RgbaColor(uint8_t blue, uint8_t green, uint8_t red, uint8_t alpha) { + this->blue = blue; + this->green = green; + this->red = red; + this->alpha = alpha; +} + +RgbaColor::RgbaColor(uint8_t blue, uint8_t green, uint8_t red) + : RgbaColor(blue, green, red, 0xff) {} + +RgbaColor::RgbaColor(const uint8_t* bgra) + : RgbaColor(bgra[0], bgra[1], bgra[2], bgra[3]) {} + +bool RgbaColor::operator==(const RgbaColor& right) const { + return blue == right.blue && green == right.green && red == right.red && + AlphaEquals(alpha, right.alpha); +} + +bool RgbaColor::operator!=(const RgbaColor& right) const { + return !(*this == right); +} + +} // namespace webrtc diff --git a/webrtc/modules/desktop_capture/rgba_color.h b/webrtc/modules/desktop_capture/rgba_color.h new file mode 100644 index 0000000000..ff4a258415 --- /dev/null +++ b/webrtc/modules/desktop_capture/rgba_color.h @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2016 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef WEBRTC_MODULES_DESKTOP_CAPTURE_RGBA_COLOR_H_ +#define WEBRTC_MODULES_DESKTOP_CAPTURE_RGBA_COLOR_H_ + +#include + +#include "webrtc/modules/desktop_capture/desktop_frame.h" + +namespace webrtc { + +// A four-byte structure to store a color in BGRA format. This structure also +// provides functions to be created from uint8_t array, say, +// DesktopFrame::data(). It always uses BGRA order for internal storage to match +// DesktopFrame::data(). +// +// This struct is for testing purpose only, and should not be used in production +// logic. +struct RgbaColor final { + // Creates a color with BGRA channels. + RgbaColor(uint8_t blue, uint8_t green, uint8_t red, uint8_t alpha); + + // Creates a color with BGR channels, and set alpha channel to 255 (opaque). + RgbaColor(uint8_t blue, uint8_t green, uint8_t red); + + // Creates a color from four-byte in BGRA order, i.e. DesktopFrame::data(). + explicit RgbaColor(const uint8_t* bgra); + + // Returns true if |this| and |right| is the same color. + bool operator==(const RgbaColor& right) const; + + // Returns true if |this| and |right| are different colors. + bool operator!=(const RgbaColor& right) const; + + uint8_t blue; + uint8_t green; + uint8_t red; + uint8_t alpha; +}; +static_assert( + DesktopFrame::kBytesPerPixel == sizeof(RgbaColor), + "A pixel in DesktopFrame should be safe to be represented by a RgbaColor"); + +} // namespace webrtc + +#endif // WEBRTC_MODULES_DESKTOP_CAPTURE_RGBA_COLOR_H_ diff --git a/webrtc/modules/desktop_capture/screen_capturer_unittest.cc b/webrtc/modules/desktop_capture/screen_capturer_unittest.cc index 6d2c0eb065..a2ef7f9d73 100644 --- a/webrtc/modules/desktop_capture/screen_capturer_unittest.cc +++ b/webrtc/modules/desktop_capture/screen_capturer_unittest.cc @@ -8,6 +8,10 @@ * be found in the AUTHORS file in the root of the source tree. */ +#include + +#include +#include #include #include @@ -15,12 +19,16 @@ #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" +#include "webrtc/base/checks.h" #include "webrtc/base/constructormagic.h" #include "webrtc/base/logging.h" +#include "webrtc/modules/desktop_capture/rgba_color.h" #include "webrtc/modules/desktop_capture/desktop_capture_options.h" #include "webrtc/modules/desktop_capture/desktop_frame.h" #include "webrtc/modules/desktop_capture/desktop_region.h" #include "webrtc/modules/desktop_capture/screen_capturer_mock_objects.h" +#include "webrtc/modules/desktop_capture/screen_drawer.h" +#include "webrtc/system_wrappers/include/sleep.h" #if defined(WEBRTC_WIN) #include "webrtc/modules/desktop_capture/win/screen_capturer_win_directx.h" @@ -34,6 +42,48 @@ const int kTestSharedMemoryId = 123; namespace webrtc { +namespace { + +ACTION_P(SaveUniquePtrArg, dest) { + *dest = std::move(*arg1); +} + +// Expects |capturer| to successfully capture a frame, and returns it. +std::unique_ptr CaptureFrame( + ScreenCapturer* capturer, + MockScreenCapturerCallback* callback) { + std::unique_ptr frame; + EXPECT_CALL(*callback, + OnCaptureResultPtr(DesktopCapturer::Result::SUCCESS, _)) + .WillOnce(SaveUniquePtrArg(&frame)); + capturer->Capture(DesktopRegion()); + EXPECT_TRUE(frame); + return frame; +} + +// Expects color in |rect| of |frame| is |color|. +void ExpectPixelsAreColoredBy(const DesktopFrame& frame, + DesktopRect rect, + RgbaColor color) { + // updated_region() should cover the painted area. + DesktopRegion updated_region(frame.updated_region()); + updated_region.IntersectWith(rect); + ASSERT_TRUE(updated_region.Equals(DesktopRegion(rect))); + + // Color in the |rect| should be |color|. + uint8_t* row = frame.GetFrameDataAtPos(rect.top_left()); + for (int i = 0; i < rect.height(); i++) { + uint8_t* column = row; + for (int j = 0; j < rect.width(); j++) { + ASSERT_EQ(color, RgbaColor(column)); + column += DesktopFrame::kBytesPerPixel; + } + row += frame.stride(); + } +} + +} // namespace + class ScreenCapturerTest : public testing::Test { public: void SetUp() override { @@ -42,6 +92,84 @@ class ScreenCapturerTest : public testing::Test { } protected: + void TestCaptureUpdatedRegion( + std::initializer_list capturers) { + // A large enough area for the tests, which should be able to fulfill by + // most of systems. + const int kTestArea = 512; + const int kRectSize = 32; + std::unique_ptr drawer = ScreenDrawer::Create(); + if (!drawer || drawer->DrawableRegion().is_empty()) { + LOG(LS_WARNING) << "No ScreenDrawer implementation for current platform."; + return; + } + if (drawer->DrawableRegion().width() < kTestArea || + drawer->DrawableRegion().height() < kTestArea) { + LOG(LS_WARNING) << "ScreenDrawer::DrawableRegion() is too small for the " + "CaptureUpdatedRegion tests."; + return; + } + + for (ScreenCapturer* capturer : capturers) { + capturer->Start(&callback_); + } + +#if defined(WEBRTC_LINUX) + // TODO(zijiehe): ScreenCapturerX11 won't be able to capture correct images + // in the first several capture attempts. + for (int i = 0; i < 10; i++) { + for (ScreenCapturer* capturer : capturers) { + std::unique_ptr frame = + CaptureFrame(capturer, &callback_); + if (!frame) { + return; + } + } + } +#endif + + for (int c = 0; c < 3; c++) { + for (int i = 0; i < kTestArea - kRectSize; i += 16) { + DesktopRect rect = DesktopRect::MakeXYWH(i, i, kRectSize, kRectSize); + rect.Translate(drawer->DrawableRegion().top_left()); + RgbaColor color((c == 0 ? (i & 0xff) : 0x7f), + (c == 1 ? (i & 0xff) : 0x7f), + (c == 2 ? (i & 0xff) : 0x7f)); + drawer->Clear(); + drawer->DrawRectangle(rect, color); + drawer->WaitForPendingDraws(); + + for (ScreenCapturer* capturer : capturers) { + std::unique_ptr frame = + CaptureFrame(capturer, &callback_); + if (!frame) { + return; + } + + ExpectPixelsAreColoredBy(*frame, rect, color); + } + } + } + } + + void TestCaptureUpdatedRegion() { + TestCaptureUpdatedRegion({capturer_.get()}); + } + +#if defined(WEBRTC_WIN) + bool SetDirectxCapturerMode() { + if (!ScreenCapturerWinDirectx::IsSupported()) { + LOG(LS_WARNING) << "Directx capturer is not supported"; + return false; + } + + DesktopCaptureOptions options(DesktopCaptureOptions::CreateDefault()); + options.set_allow_directx_capturer(true); + capturer_.reset(ScreenCapturer::Create(options)); + return true; + } +#endif // defined(WEBRTC_WIN) + std::unique_ptr capturer_; MockScreenCapturerCallback callback_; }; @@ -74,10 +202,6 @@ class FakeSharedMemoryFactory : public SharedMemoryFactory { RTC_DISALLOW_COPY_AND_ASSIGN(FakeSharedMemoryFactory); }; -ACTION_P(SaveUniquePtrArg, dest) { - *dest = std::move(*arg1); -} - TEST_F(ScreenCapturerTest, GetScreenListAndSelectScreen) { webrtc::ScreenCapturer::ScreenList screens; EXPECT_TRUE(capturer_->GetScreenList(&screens)); @@ -117,6 +241,10 @@ TEST_F(ScreenCapturerTest, Capture) { EXPECT_TRUE(it.IsAtEnd()); } +TEST_F(ScreenCapturerTest, CaptureUpdatedRegion) { + TestCaptureUpdatedRegion(); +} + #if defined(WEBRTC_WIN) TEST_F(ScreenCapturerTest, UseSharedBuffers) { @@ -151,15 +279,10 @@ TEST_F(ScreenCapturerTest, UseMagnifier) { } TEST_F(ScreenCapturerTest, UseDirectxCapturer) { - if (!ScreenCapturerWinDirectx::IsSupported()) { - LOG(LS_WARNING) << "Directx capturer is not supported"; + if (!SetDirectxCapturerMode()) { return; } - DesktopCaptureOptions options(DesktopCaptureOptions::CreateDefault()); - options.set_allow_directx_capturer(true); - capturer_.reset(ScreenCapturer::Create(options)); - std::unique_ptr frame; EXPECT_CALL(callback_, OnCaptureResultPtr(DesktopCapturer::Result::SUCCESS, _)) @@ -171,15 +294,10 @@ TEST_F(ScreenCapturerTest, UseDirectxCapturer) { } TEST_F(ScreenCapturerTest, UseDirectxCapturerWithSharedBuffers) { - if (!ScreenCapturerWinDirectx::IsSupported()) { - LOG(LS_WARNING) << "Directx capturer is not supported"; + if (!SetDirectxCapturerMode()) { return; } - DesktopCaptureOptions options(DesktopCaptureOptions::CreateDefault()); - options.set_allow_directx_capturer(true); - capturer_.reset(ScreenCapturer::Create(options)); - std::unique_ptr frame; EXPECT_CALL(callback_, OnCaptureResultPtr(DesktopCapturer::Result::SUCCESS, _)) @@ -194,6 +312,25 @@ TEST_F(ScreenCapturerTest, UseDirectxCapturerWithSharedBuffers) { EXPECT_EQ(frame->shared_memory()->id(), kTestSharedMemoryId); } +TEST_F(ScreenCapturerTest, CaptureUpdatedRegionWithDirectxCapturer) { + if (!SetDirectxCapturerMode()) { + return; + } + + TestCaptureUpdatedRegion(); +} + +// TODO(zijiehe): Enable this test after CL 2299663003 has been submitted. +TEST_F(ScreenCapturerTest, DISABLED_TwoDirectxCapturers) { + if (!SetDirectxCapturerMode()) { + return; + } + + std::unique_ptr capturer2(capturer_.release()); + RTC_CHECK(SetDirectxCapturerMode()); + TestCaptureUpdatedRegion({capturer_.get(), capturer2.get()}); +} + #endif // defined(WEBRTC_WIN) } // namespace webrtc diff --git a/webrtc/modules/desktop_capture/screen_drawer.h b/webrtc/modules/desktop_capture/screen_drawer.h index 899fc0f043..d7ec5d186c 100644 --- a/webrtc/modules/desktop_capture/screen_drawer.h +++ b/webrtc/modules/desktop_capture/screen_drawer.h @@ -15,30 +15,40 @@ #include +#include "webrtc/modules/desktop_capture/rgba_color.h" #include "webrtc/modules/desktop_capture/desktop_geometry.h" namespace webrtc { -// A set of platform independent functions to draw various of shapes on the -// screen. This class is for testing ScreenCapturer* implementations only, and -// should not be used in production logic. +// A set of basic platform dependent functions to draw various shapes on the +// screen. class ScreenDrawer { public: - // Creates a ScreenDrawer for the current platform. + // Creates a ScreenDrawer for the current platform, returns nullptr if no + // ScreenDrawer implementation available. static std::unique_ptr Create(); ScreenDrawer() {} virtual ~ScreenDrawer() {} - // Returns a rect, on which this instance can draw. + // Returns the region inside which DrawRectangle() function are expected to + // work, in capturer coordinates (assuming ScreenCapturer::SelectScreen has + // not been called). This region may exclude regions of the screen reserved by + // the OS for things like menu bars or app launchers. virtual DesktopRect DrawableRegion() = 0; - // Draws a rectangle to cover |rect| with color |rgba|. Note, rect.bottom() - // and rect.right() two lines are not included. - virtual void DrawRectangle(DesktopRect rect, uint32_t rgba) = 0; + // Draws a rectangle to cover |rect| with |color|. Note, rect.bottom() and + // rect.right() two lines are not included. The part of |rect| which is out of + // DrawableRegion() will be ignored. + virtual void DrawRectangle(DesktopRect rect, RgbaColor color) = 0; - // Clears all content on the screen. + // Clears all content on the screen by filling the area with black. virtual void Clear() = 0; + + // Blocks current thread until OS finishes previous DrawRectangle() actions. + // ScreenCapturer should be able to capture the changes after this function + // finish. + virtual void WaitForPendingDraws() = 0; }; } // namespace webrtc diff --git a/webrtc/modules/desktop_capture/screen_drawer_linux.cc b/webrtc/modules/desktop_capture/screen_drawer_linux.cc index 2aff80b6c7..08d8195b42 100644 --- a/webrtc/modules/desktop_capture/screen_drawer_linux.cc +++ b/webrtc/modules/desktop_capture/screen_drawer_linux.cc @@ -13,6 +13,7 @@ #include "webrtc/base/checks.h" #include "webrtc/modules/desktop_capture/screen_drawer.h" #include "webrtc/modules/desktop_capture/x11/shared_x_display.h" +#include "webrtc/system_wrappers/include/sleep.h" namespace webrtc { @@ -26,12 +27,12 @@ class ScreenDrawerLinux : public ScreenDrawer { // ScreenDrawer interface. DesktopRect DrawableRegion() override; - void DrawRectangle(DesktopRect rect, uint32_t rgba) override; + void DrawRectangle(DesktopRect rect, RgbaColor color) override; void Clear() override; + void WaitForPendingDraws() override; private: rtc::scoped_refptr display_; - Screen* screen_; int screen_num_; DesktopRect rect_; Window window_; @@ -42,15 +43,18 @@ class ScreenDrawerLinux : public ScreenDrawer { ScreenDrawerLinux::ScreenDrawerLinux() { display_ = SharedXDisplay::CreateDefault(); RTC_CHECK(display_.get()); - screen_ = DefaultScreenOfDisplay(display_->display()); - RTC_CHECK(screen_); screen_num_ = DefaultScreen(display_->display()); - rect_ = DesktopRect::MakeWH(screen_->width, screen_->height); - window_ = XCreateSimpleWindow(display_->display(), - RootWindow(display_->display(), screen_num_), 0, - 0, rect_.width(), rect_.height(), 0, - BlackPixel(display_->display(), screen_num_), - BlackPixel(display_->display(), screen_num_)); + XWindowAttributes root_attributes; + if (!XGetWindowAttributes(display_->display(), + RootWindow(display_->display(), screen_num_), + &root_attributes)) { + RTC_DCHECK(false) << "Failed to get root window size."; + } + window_ = XCreateSimpleWindow( + display_->display(), RootWindow(display_->display(), screen_num_), 0, 0, + root_attributes.width, root_attributes.height, 0, + BlackPixel(display_->display(), screen_num_), + BlackPixel(display_->display(), screen_num_)); XSelectInput(display_->display(), window_, StructureNotifyMask); XMapWindow(display_->display(), window_); while (true) { @@ -61,8 +65,23 @@ ScreenDrawerLinux::ScreenDrawerLinux() { } } XFlush(display_->display()); + Window child; + int x, y; + if (!XTranslateCoordinates(display_->display(), window_, + RootWindow(display_->display(), screen_num_), 0, 0, + &x, &y, &child)) { + RTC_DCHECK(false) << "Failed to get window position."; + } + // Some window manager does not allow a window to cover two or more monitors. + // So if the window is on the first monitor of a two-monitor system, the + // second half won't be able to show up without changing configurations of WM, + // and its DrawableRegion() is not accurate. + rect_ = DesktopRect::MakeLTRB(x, y, root_attributes.width, + root_attributes.height); context_ = DefaultGC(display_->display(), screen_num_); colormap_ = DefaultColormap(display_->display(), screen_num_); + // Wait for window animations. + SleepMs(200); } ScreenDrawerLinux::~ScreenDrawerLinux() { @@ -74,33 +93,42 @@ DesktopRect ScreenDrawerLinux::DrawableRegion() { return rect_; } -void ScreenDrawerLinux::DrawRectangle(DesktopRect rect, uint32_t rgba) { - int r = (rgba & 0xff00) >> 8; - int g = (rgba & 0xff0000) >> 16; - int b = (rgba & 0xff000000) >> 24; +void ScreenDrawerLinux::DrawRectangle(DesktopRect rect, RgbaColor color) { + rect.Translate(-rect_.left(), -rect_.top()); + XColor xcolor; // X11 does not support Alpha. - XColor color; - // X11 uses 16 bits for each primary color. - color.red = r * 256; - color.green = g * 256; - color.blue = b * 256; - color.flags = DoRed | DoGreen | DoBlue; - XAllocColor(display_->display(), colormap_, &color); - XSetForeground(display_->display(), context_, color.pixel); + // X11 uses 16 bits for each primary color, so we need to slightly normalize + // a 8 bits channel to 16 bits channel, by setting the low 8 bits as its high + // 8 bits to avoid a mismatch of color returned by capturer. + xcolor.red = (color.red << 8) + color.red; + xcolor.green = (color.green << 8) + color.green; + xcolor.blue = (color.blue << 8) + color.blue; + xcolor.flags = DoRed | DoGreen | DoBlue; + XAllocColor(display_->display(), colormap_, &xcolor); + XSetForeground(display_->display(), context_, xcolor.pixel); XFillRectangle(display_->display(), window_, context_, rect.left(), rect.top(), rect.width(), rect.height()); XFlush(display_->display()); } void ScreenDrawerLinux::Clear() { - DrawRectangle(DrawableRegion(), 0); + DrawRectangle(rect_, RgbaColor(0, 0, 0)); +} + +// TODO(zijiehe): Find the right signal from X11 to indicate the finish of all +// pending paintings. +void ScreenDrawerLinux::WaitForPendingDraws() { + SleepMs(50); } } // namespace // static std::unique_ptr ScreenDrawer::Create() { - return std::unique_ptr(new ScreenDrawerLinux()); + if (SharedXDisplay::CreateDefault().get()) { + return std::unique_ptr(new ScreenDrawerLinux()); + } + return nullptr; } } // namespace webrtc diff --git a/webrtc/modules/desktop_capture/screen_drawer_unittest.cc b/webrtc/modules/desktop_capture/screen_drawer_unittest.cc index 345a962223..b5f6ba5c1b 100644 --- a/webrtc/modules/desktop_capture/screen_drawer_unittest.cc +++ b/webrtc/modules/desktop_capture/screen_drawer_unittest.cc @@ -15,6 +15,7 @@ #include "testing/gtest/include/gtest/gtest.h" #include "webrtc/base/random.h" #include "webrtc/base/timeutils.h" +#include "webrtc/system_wrappers/include/logging.h" #include "webrtc/system_wrappers/include/sleep.h" namespace webrtc { @@ -26,11 +27,16 @@ namespace webrtc { TEST(ScreenDrawerTest, DISABLED_DrawRectangles) { std::unique_ptr drawer = ScreenDrawer::Create(); if (!drawer) { - // No ScreenDrawer implementation for current platform. + LOG(LS_WARNING) << "No ScreenDrawer implementation for current platform."; + return; + } + + if (drawer->DrawableRegion().is_empty()) { + LOG(LS_WARNING) << "ScreenDrawer of current platform does not provide a " + "non-empty DrawableRegion()."; return; } - drawer->Clear(); DesktopRect rect = drawer->DrawableRegion(); Random random(rtc::TimeMicros()); for (int i = 0; i < 100; i++) { @@ -40,16 +46,15 @@ TEST(ScreenDrawerTest, DISABLED_DrawRectangles) { drawer->DrawRectangle( DesktopRect::MakeLTRB(left, top, random.Rand(left + 1, rect.right()), random.Rand(top + 1, rect.bottom())), - random.Rand()); + RgbaColor(random.Rand(), random.Rand(), + random.Rand(), random.Rand())); if (i == 50) { SleepMs(10000); - drawer->Clear(); } } SleepMs(10000); - drawer->Clear(); } } // namespace webrtc diff --git a/webrtc/modules/desktop_capture/screen_drawer_win.cc b/webrtc/modules/desktop_capture/screen_drawer_win.cc index 061a40539e..6ef414dda9 100644 --- a/webrtc/modules/desktop_capture/screen_drawer_win.cc +++ b/webrtc/modules/desktop_capture/screen_drawer_win.cc @@ -13,6 +13,7 @@ #include #include "webrtc/modules/desktop_capture/screen_drawer.h" +#include "webrtc/system_wrappers/include/sleep.h" namespace webrtc { @@ -34,6 +35,11 @@ HWND CreateDrawerWindow(DesktopRect rect) { return hwnd; } +COLORREF ColorToRef(RgbaColor color) { + // Windows device context does not support alpha. + return RGB(color.red, color.green, color.blue); +} + // A ScreenDrawer implementation for Windows. class ScreenDrawerWin : public ScreenDrawer { public: @@ -42,10 +48,17 @@ class ScreenDrawerWin : public ScreenDrawer { // ScreenDrawer interface. DesktopRect DrawableRegion() override; - void DrawRectangle(DesktopRect rect, uint32_t rgba) override; + void DrawRectangle(DesktopRect rect, RgbaColor color) override; void Clear() override; + void WaitForPendingDraws() override; private: + // Draw a line with |color|. + void DrawLine(DesktopVector start, DesktopVector end, RgbaColor color); + + // Draw a dot with |color|. + void DrawDot(DesktopVector vect, RgbaColor color); + const DesktopRect rect_; HWND window_; HDC hdc_; @@ -57,8 +70,12 @@ ScreenDrawerWin::ScreenDrawerWin() window_(CreateDrawerWindow(rect_)), hdc_(GetWindowDC(window_)) { // We do not need to handle any messages for the |window_|, so disable Windows - // process windows ghosting feature. + // from processing windows ghosting feature. DisableProcessWindowsGhosting(); + + // Always use stock pen (DC_PEN) and brush (DC_BRUSH). + SelectObject(hdc_, GetStockObject(DC_PEN)); + SelectObject(hdc_, GetStockObject(DC_BRUSH)); } ScreenDrawerWin::~ScreenDrawerWin() { @@ -71,20 +88,51 @@ DesktopRect ScreenDrawerWin::DrawableRegion() { return rect_; } -void ScreenDrawerWin::DrawRectangle(DesktopRect rect, uint32_t rgba) { - int r = (rgba & 0xff00) >> 8; - int g = (rgba & 0xff0000) >> 16; - int b = (rgba & 0xff000000) >> 24; - // Windows device context does not support Alpha. - SelectObject(hdc_, GetStockObject(DC_PEN)); - SelectObject(hdc_, GetStockObject(DC_BRUSH)); - SetDCBrushColor(hdc_, RGB(r, g, b)); - SetDCPenColor(hdc_, RGB(r, g, b)); +void ScreenDrawerWin::DrawRectangle(DesktopRect rect, RgbaColor color) { + if (rect.width() == 1 && rect.height() == 1) { + // Rectangle function cannot draw a 1 pixel rectangle. + DrawDot(rect.top_left(), color); + return; + } + + if (rect.width() == 1 || rect.height() == 1) { + // Rectangle function cannot draw a 1 pixel rectangle. + DrawLine(rect.top_left(), DesktopVector(rect.right(), rect.bottom()), + color); + return; + } + + SetDCBrushColor(hdc_, ColorToRef(color)); + SetDCPenColor(hdc_, ColorToRef(color)); Rectangle(hdc_, rect.left(), rect.top(), rect.right(), rect.bottom()); } void ScreenDrawerWin::Clear() { - DrawRectangle(DrawableRegion(), 0); + DrawRectangle(rect_, RgbaColor(0, 0, 0)); +} + +// TODO(zijiehe): Find the right signal to indicate the finish of all pending +// paintings. +void ScreenDrawerWin::WaitForPendingDraws() { + // DirectX capturer reads data from GPU, so there is a certain delay before + // Windows sends the data to GPU. + SleepMs(100); +} + +void ScreenDrawerWin::DrawLine(DesktopVector start, + DesktopVector end, + RgbaColor color) { + POINT points[2]; + points[0].x = start.x(); + points[0].y = start.y(); + points[1].x = end.x(); + points[1].y = end.y(); + SetDCPenColor(hdc_, ColorToRef(color)); + Polyline(hdc_, points, 2); +} + +void ScreenDrawerWin::DrawDot(DesktopVector vect, RgbaColor color) { + SetPixel(hdc_, vect.x(), vect.y(), ColorToRef(color)); } } // namespace diff --git a/webrtc/modules/modules.gyp b/webrtc/modules/modules.gyp index 16e21a2ae1..1a139a02f5 100644 --- a/webrtc/modules/modules.gyp +++ b/webrtc/modules/modules.gyp @@ -433,6 +433,8 @@ 'sources': [ 'desktop_capture/desktop_and_cursor_composer_unittest.cc', 'desktop_capture/mouse_cursor_monitor_unittest.cc', + 'desktop_capture/rgba_color.cc', + 'desktop_capture/rgba_color.h', 'desktop_capture/screen_capturer_helper_unittest.cc', 'desktop_capture/screen_capturer_mac_unittest.cc', 'desktop_capture/screen_capturer_mock_objects.h',