From 4985927d36987132d61dfc9f32b3d9bd227af40d Mon Sep 17 00:00:00 2001 From: "jiayl@webrtc.org" Date: Fri, 17 Jan 2014 17:19:16 +0000 Subject: [PATCH] Implement screen enumeration and individual screen capturing for Windows. BUG=2787 R=sergeyu@chromium.org Review URL: https://webrtc-codereview.appspot.com/7239004 git-svn-id: http://webrtc.googlecode.com/svn/trunk@5399 4adac7df-926f-26a2-2b94-8c16560cd09d --- .../desktop_capture/desktop_capture_types.h | 2 + .../mouse_cursor_monitor_win.cc | 53 +++++++++- .../screen_capturer_unittest.cc | 9 ++ .../desktop_capture/screen_capturer_win.cc | 99 ++++++++++++++++--- 4 files changed, 147 insertions(+), 16 deletions(-) diff --git a/webrtc/modules/desktop_capture/desktop_capture_types.h b/webrtc/modules/desktop_capture/desktop_capture_types.h index 30adef02a2..0ad199cc61 100644 --- a/webrtc/modules/desktop_capture/desktop_capture_types.h +++ b/webrtc/modules/desktop_capture/desktop_capture_types.h @@ -31,6 +31,8 @@ typedef int ScreenId; // The screen id corresponds to all screen combined together. const ScreenId kFullDesktopScreenId = -1; +const ScreenId kInvalidScreenId = -2; + } // namespace webrtc #endif // WEBRTC_MODULES_DESKTOP_CAPTURE_DESKTOP_CAPTURE_TYPES_H_ diff --git a/webrtc/modules/desktop_capture/mouse_cursor_monitor_win.cc b/webrtc/modules/desktop_capture/mouse_cursor_monitor_win.cc index 2f69c414ed..01dace904e 100644 --- a/webrtc/modules/desktop_capture/mouse_cursor_monitor_win.cc +++ b/webrtc/modules/desktop_capture/mouse_cursor_monitor_win.cc @@ -20,13 +20,17 @@ namespace webrtc { class MouseCursorMonitorWin : public MouseCursorMonitor { public: explicit MouseCursorMonitorWin(HWND window); + explicit MouseCursorMonitorWin(ScreenId screen); virtual ~MouseCursorMonitorWin(); virtual void Init(Callback* callback, Mode mode) OVERRIDE; virtual void Capture() OVERRIDE; private: + DesktopRect GetScreenRect(); + HWND window_; + ScreenId screen_; Callback* callback_; Mode mode_; @@ -38,12 +42,23 @@ class MouseCursorMonitorWin : public MouseCursorMonitor { MouseCursorMonitorWin::MouseCursorMonitorWin(HWND window) : window_(window), + screen_(kInvalidScreenId), callback_(NULL), mode_(SHAPE_AND_POSITION), desktop_dc_(NULL), last_cursor_(NULL) { } +MouseCursorMonitorWin::MouseCursorMonitorWin(ScreenId screen) + : window_(NULL), + screen_(screen), + callback_(NULL), + mode_(SHAPE_AND_POSITION), + desktop_dc_(NULL), + last_cursor_(NULL) { + assert(screen >= kFullDesktopScreenId); +} + MouseCursorMonitorWin::~MouseCursorMonitorWin() { if (desktop_dc_) ReleaseDC(NULL, desktop_dc_); @@ -94,11 +109,47 @@ void MouseCursorMonitorWin::Capture() { if (inside) inside = (window_ == WindowFromPoint(cursor_info.ptScreenPos)); } + } else { + assert(screen_ != kInvalidScreenId); + DesktopRect rect = GetScreenRect(); + if (inside) + inside = rect.Contains(position); + position = position.subtract(rect.top_left()); } callback_->OnMouseCursorPosition(inside ? INSIDE : OUTSIDE, position); } +DesktopRect MouseCursorMonitorWin::GetScreenRect() { + assert(screen_ != kInvalidScreenId); + if (screen_ == kFullDesktopScreenId) { + return DesktopRect::MakeXYWH( + GetSystemMetrics(SM_XVIRTUALSCREEN), + GetSystemMetrics(SM_YVIRTUALSCREEN), + GetSystemMetrics(SM_CXVIRTUALSCREEN), + GetSystemMetrics(SM_CYVIRTUALSCREEN)); + } + DISPLAY_DEVICE device; + device.cb = sizeof(device); + BOOL result = EnumDisplayDevices(NULL, screen_, &device, 0); + if (!result) + return DesktopRect(); + + DEVMODE device_mode; + device_mode.dmSize = sizeof(device_mode); + device_mode.dmDriverExtra = 0; + result = EnumDisplaySettingsEx( + device.DeviceName, ENUM_CURRENT_SETTINGS, &device_mode, 0); + if (!result) + return DesktopRect(); + + return DesktopRect::MakeXYWH( + GetSystemMetrics(SM_XVIRTUALSCREEN) + device_mode.dmPosition.x, + GetSystemMetrics(SM_YVIRTUALSCREEN) + device_mode.dmPosition.y, + device_mode.dmPelsWidth, + device_mode.dmPelsHeight); +} + MouseCursorMonitor* MouseCursorMonitor::CreateForWindow( const DesktopCaptureOptions& options, WindowId window) { return new MouseCursorMonitorWin(reinterpret_cast(window)); @@ -107,7 +158,7 @@ MouseCursorMonitor* MouseCursorMonitor::CreateForWindow( MouseCursorMonitor* MouseCursorMonitor::CreateForScreen( const DesktopCaptureOptions& options, ScreenId screen) { - return new MouseCursorMonitorWin(NULL); + return new MouseCursorMonitorWin(screen); } } // namespace webrtc diff --git a/webrtc/modules/desktop_capture/screen_capturer_unittest.cc b/webrtc/modules/desktop_capture/screen_capturer_unittest.cc index b4ae128085..94c1f707e2 100644 --- a/webrtc/modules/desktop_capture/screen_capturer_unittest.cc +++ b/webrtc/modules/desktop_capture/screen_capturer_unittest.cc @@ -59,6 +59,15 @@ SharedMemory* ScreenCapturerTest::CreateSharedMemory(size_t size) { return new FakeSharedMemory(new char[size], size); } +TEST_F(ScreenCapturerTest, GetScreenListAndSelectScreen) { + webrtc::ScreenCapturer::ScreenList screens; + EXPECT_TRUE(capturer_->GetScreenList(&screens)); + for(webrtc::ScreenCapturer::ScreenList::iterator it = screens.begin(); + it != screens.end(); ++it) { + EXPECT_TRUE(capturer_->SelectScreen(it->id)); + } +} + TEST_F(ScreenCapturerTest, StartCapturer) { capturer_->SetMouseShapeObserver(&mouse_observer_); capturer_->Start(&callback_); diff --git a/webrtc/modules/desktop_capture/screen_capturer_win.cc b/webrtc/modules/desktop_capture/screen_capturer_win.cc index 2f13bb5e66..8ae755a41f 100644 --- a/webrtc/modules/desktop_capture/screen_capturer_win.cc +++ b/webrtc/modules/desktop_capture/screen_capturer_win.cc @@ -60,14 +60,21 @@ class ScreenCapturerWin : public ScreenCapturer { // Make sure that the device contexts match the screen configuration. void PrepareCaptureResources(); - // Captures the current screen contents into the current buffer. - void CaptureImage(); + // Captures the current screen contents into the current buffer. Returns true + // if succeeded. + bool CaptureImage(); // Capture the current cursor shape. void CaptureCursor(); + // Get the rect of the currently selected screen. If the screen is disabled + // or disconnected, or any error happens, an empty rect is returned. + DesktopRect GetScreenRect(); + Callback* callback_; MouseShapeObserver* mouse_shape_observer_; + ScreenId current_screen_id_; + std::wstring current_device_key_; // A thread-safe list of invalid rectangles, and the size of the most // recently captured screen. @@ -105,6 +112,7 @@ class ScreenCapturerWin : public ScreenCapturer { ScreenCapturerWin::ScreenCapturerWin(const DesktopCaptureOptions& options) : callback_(NULL), mouse_shape_observer_(NULL), + current_screen_id_(kFullDesktopScreenId), desktop_dc_(NULL), memory_dc_(NULL), dwmapi_library_(NULL), @@ -154,7 +162,10 @@ void ScreenCapturerWin::Capture(const DesktopRegion& region) { PrepareCaptureResources(); // Copy screen bits to the current buffer. - CaptureImage(); + if (!CaptureImage()) { + callback_->OnCaptureCompleted(NULL); + return; + } const DesktopFrame* current_frame = queue_.current_frame(); const DesktopFrame* last_frame = queue_.previous_frame(); @@ -208,15 +219,38 @@ void ScreenCapturerWin::SetMouseShapeObserver( bool ScreenCapturerWin::GetScreenList(ScreenList* screens) { assert(screens->size() == 0); - // TODO(jiayl): implement screen enumeration. - Screen default_screen; - default_screen.id = 0; - screens->push_back(default_screen); + BOOL enum_result = TRUE; + for (int device_index = 0; ; ++device_index) { + DISPLAY_DEVICE device; + device.cb = sizeof(device); + enum_result = EnumDisplayDevices(NULL, device_index, &device, 0); + // |enum_result| is 0 if we have enumerated all devices. + if (!enum_result) + break; + + // We only care about active displays. + if (!(device.StateFlags & DISPLAY_DEVICE_ACTIVE)) + continue; + Screen screen; + screen.id = device_index; + screens->push_back(screen); + } return true; } bool ScreenCapturerWin::SelectScreen(ScreenId id) { - // TODO(jiayl): implement screen selection. + if (id == kFullDesktopScreenId) { + current_screen_id_ = id; + return true; + } + DISPLAY_DEVICE device; + device.cb = sizeof(device); + BOOL enum_result = EnumDisplayDevices(NULL, id, &device, 0); + if (!enum_result) + return false; + + current_device_key_ = device.DeviceKey; + current_screen_id_ = id; return true; } @@ -298,17 +332,19 @@ void ScreenCapturerWin::PrepareCaptureResources() { } } -void ScreenCapturerWin::CaptureImage() { +bool ScreenCapturerWin::CaptureImage() { + DesktopRect screen_rect = GetScreenRect(); + if (screen_rect.is_empty()) + return false; + DesktopSize size = screen_rect.size(); // If the current buffer is from an older generation then allocate a new one. // Note that we can't reallocate other buffers at this point, since the caller // may still be reading from them. - if (!queue_.current_frame()) { + if (!queue_.current_frame() || + !queue_.current_frame()->size().equals(size)) { assert(desktop_dc_ != NULL); assert(memory_dc_ != NULL); - DesktopSize size = DesktopSize( - desktop_dc_rect_.width(), desktop_dc_rect_.height()); - size_t buffer_size = size.width() * size.height() * DesktopFrame::kBytesPerPixel; SharedMemory* shared_memory = @@ -325,15 +361,16 @@ void ScreenCapturerWin::CaptureImage() { HGDIOBJ previous_object = SelectObject(memory_dc_, current->bitmap()); if (previous_object != NULL) { BitBlt(memory_dc_, - 0, 0, desktop_dc_rect_.width(), desktop_dc_rect_.height(), + 0, 0, screen_rect.width(), screen_rect.height(), desktop_dc_, - desktop_dc_rect_.left(), desktop_dc_rect_.top(), + screen_rect.left(), screen_rect.top(), SRCCOPY | CAPTUREBLT); // Select back the previously selected object to that the device contect // could be destroyed independently of the bitmap if needed. SelectObject(memory_dc_, previous_object); } + return true; } void ScreenCapturerWin::CaptureCursor() { @@ -379,6 +416,38 @@ void ScreenCapturerWin::CaptureCursor() { mouse_shape_observer_->OnCursorShapeChanged(cursor.release()); } +DesktopRect ScreenCapturerWin::GetScreenRect() { + DesktopRect rect = desktop_dc_rect_; + if (current_screen_id_ == kFullDesktopScreenId) + return rect; + + DISPLAY_DEVICE device; + device.cb = sizeof(device); + BOOL result = EnumDisplayDevices(NULL, current_screen_id_, &device, 0); + if (!result) + return DesktopRect(); + + // Verifies the device index still maps to the same display device. DeviceKey + // is documented as reserved, but it actually contains the registry key for + // the device and is unique for each monitor, while DeviceID is not. + if (current_device_key_ != device.DeviceKey) + return DesktopRect(); + + DEVMODE device_mode; + device_mode.dmSize = sizeof(device_mode); + device_mode.dmDriverExtra = 0; + result = EnumDisplaySettingsEx( + device.DeviceName, ENUM_CURRENT_SETTINGS, &device_mode, 0); + if (!result) + return DesktopRect(); + + rect = DesktopRect::MakeXYWH( + rect.left() + device_mode.dmPosition.x, + rect.top() + device_mode.dmPosition.y, + device_mode.dmPelsWidth, + device_mode.dmPelsHeight); + return rect; +} } // namespace // static