diff --git a/webrtc/modules/desktop_capture/desktop_capture.gypi b/webrtc/modules/desktop_capture/desktop_capture.gypi index 6f4a083015..5f967453f8 100644 --- a/webrtc/modules/desktop_capture/desktop_capture.gypi +++ b/webrtc/modules/desktop_capture/desktop_capture.gypi @@ -69,12 +69,6 @@ "win/scoped_gdi_object.h", "win/scoped_thread_desktop.cc", "win/scoped_thread_desktop.h", - "win/screen_capturer_win_gdi.cc", - "win/screen_capturer_win_gdi.h", - "win/screen_capturer_win_magnifier.cc", - "win/screen_capturer_win_magnifier.h", - "win/screen_capture_utils.cc", - "win/screen_capture_utils.h", "win/window_capture_utils.cc", "win/window_capture_utils.h", "window_capturer.cc", diff --git a/webrtc/modules/desktop_capture/desktop_capture_options.cc b/webrtc/modules/desktop_capture/desktop_capture_options.cc index 105853bf94..26044e127f 100644 --- a/webrtc/modules/desktop_capture/desktop_capture_options.cc +++ b/webrtc/modules/desktop_capture/desktop_capture_options.cc @@ -19,10 +19,6 @@ DesktopCaptureOptions::DesktopCaptureOptions() // XDamage is often broken, so don't use it by default. use_update_notifications_ = false; #endif - -#if defined(WEBRTC_WIN) - allow_use_magnification_api_ = false; -#endif } DesktopCaptureOptions::~DesktopCaptureOptions() {} diff --git a/webrtc/modules/desktop_capture/desktop_capture_options.h b/webrtc/modules/desktop_capture/desktop_capture_options.h index c01d3d29de..2a188a03a3 100644 --- a/webrtc/modules/desktop_capture/desktop_capture_options.h +++ b/webrtc/modules/desktop_capture/desktop_capture_options.h @@ -66,15 +66,6 @@ class DesktopCaptureOptions { disable_effects_ = disable_effects; } -#if defined(WEBRTC_WIN) - bool allow_use_magnification_api() const { - return allow_use_magnification_api_; - } - void set_allow_use_magnification_api(bool allow) { - allow_use_magnification_api_ = allow; - } -#endif - private: #if defined(USE_X11) scoped_refptr x_display_; @@ -83,10 +74,6 @@ class DesktopCaptureOptions { #if defined(WEBRTC_MAC) && !defined(WEBRTC_IOS) scoped_refptr configuration_monitor_; #endif - -#if defined(WEBRTC_WIN) - bool allow_use_magnification_api_; -#endif bool use_update_notifications_; bool disable_effects_; }; diff --git a/webrtc/modules/desktop_capture/screen_capturer_unittest.cc b/webrtc/modules/desktop_capture/screen_capturer_unittest.cc index 50ff7a2853..94c1f707e2 100644 --- a/webrtc/modules/desktop_capture/screen_capturer_unittest.cc +++ b/webrtc/modules/desktop_capture/screen_capturer_unittest.cc @@ -106,7 +106,7 @@ TEST_F(ScreenCapturerTest, Capture) { delete frame; } -#if defined(WEBRTC_WIN) +#if defined(OS_WIN) TEST_F(ScreenCapturerTest, UseSharedBuffers) { DesktopFrame* frame = NULL; @@ -129,20 +129,6 @@ TEST_F(ScreenCapturerTest, UseSharedBuffers) { delete frame; } -TEST_F(ScreenCapturerTest, UseMagnifier) { - DesktopCaptureOptions options(DesktopCaptureOptions::CreateDefault()); - options.set_allow_use_magnification_api(true); - capturer_.reset(ScreenCapturer::Create(options)); - - DesktopFrame* frame = NULL; - EXPECT_CALL(callback_, OnCaptureCompleted(_)).WillOnce(SaveArg<0>(&frame)); - - capturer_->Start(&callback_); - capturer_->Capture(DesktopRegion()); - ASSERT_TRUE(frame); - delete frame; -} - -#endif // defined(WEBRTC_WIN) +#endif // defined(OS_WIN) } // namespace webrtc diff --git a/webrtc/modules/desktop_capture/screen_capturer_win.cc b/webrtc/modules/desktop_capture/screen_capturer_win.cc index 5950795d47..fc6ce50817 100644 --- a/webrtc/modules/desktop_capture/screen_capturer_win.cc +++ b/webrtc/modules/desktop_capture/screen_capturer_win.cc @@ -10,20 +10,451 @@ #include "webrtc/modules/desktop_capture/screen_capturer.h" +#include + #include "webrtc/modules/desktop_capture/desktop_capture_options.h" -#include "webrtc/modules/desktop_capture/win/screen_capturer_win_gdi.h" -#include "webrtc/modules/desktop_capture/win/screen_capturer_win_magnifier.h" +#include "webrtc/modules/desktop_capture/desktop_frame.h" +#include "webrtc/modules/desktop_capture/desktop_frame_win.h" +#include "webrtc/modules/desktop_capture/desktop_region.h" +#include "webrtc/modules/desktop_capture/differ.h" +#include "webrtc/modules/desktop_capture/mouse_cursor.h" +#include "webrtc/modules/desktop_capture/mouse_cursor_shape.h" +#include "webrtc/modules/desktop_capture/screen_capture_frame_queue.h" +#include "webrtc/modules/desktop_capture/screen_capturer_helper.h" +#include "webrtc/modules/desktop_capture/win/cursor.h" +#include "webrtc/modules/desktop_capture/win/desktop.h" +#include "webrtc/modules/desktop_capture/win/scoped_thread_desktop.h" +#include "webrtc/system_wrappers/interface/logging.h" +#include "webrtc/system_wrappers/interface/scoped_ptr.h" +#include "webrtc/system_wrappers/interface/tick_util.h" namespace webrtc { +namespace { + +// Constants from dwmapi.h. +const UINT DWM_EC_DISABLECOMPOSITION = 0; +const UINT DWM_EC_ENABLECOMPOSITION = 1; + +typedef HRESULT (WINAPI * DwmEnableCompositionFunc)(UINT); + +const wchar_t kDwmapiLibraryName[] = L"dwmapi.dll"; + +// ScreenCapturerWin captures 32bit RGB using GDI. +// +// ScreenCapturerWin is double-buffered as required by ScreenCapturer. +class ScreenCapturerWin : public ScreenCapturer { + public: + ScreenCapturerWin(const DesktopCaptureOptions& options); + virtual ~ScreenCapturerWin(); + + // Overridden from ScreenCapturer: + virtual void Start(Callback* callback) OVERRIDE; + virtual void Capture(const DesktopRegion& region) OVERRIDE; + virtual void SetMouseShapeObserver( + MouseShapeObserver* mouse_shape_observer) OVERRIDE; + virtual bool GetScreenList(ScreenList* screens) OVERRIDE; + virtual bool SelectScreen(ScreenId id) OVERRIDE; + + private: + // Make sure that the device contexts match the screen configuration. + void PrepareCaptureResources(); + + // 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, relative to the primary + // display's top-left. 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. + ScreenCapturerHelper helper_; + + // Snapshot of the last cursor bitmap we sent to the client. This is used + // to diff against the current cursor so we only send a cursor-change + // message when the shape has changed. + MouseCursorShape last_cursor_; + + ScopedThreadDesktop desktop_; + + // GDI resources used for screen capture. + HDC desktop_dc_; + HDC memory_dc_; + + // Queue of the frames buffers. + ScreenCaptureFrameQueue queue_; + + // Rectangle describing the bounds of the desktop device context, relative to + // the primary display's top-left. + DesktopRect desktop_dc_rect_; + + // Class to calculate the difference between two screen bitmaps. + scoped_ptr differ_; + + HMODULE dwmapi_library_; + DwmEnableCompositionFunc composition_func_; + + // Used to suppress duplicate logging of SetThreadExecutionState errors. + bool set_thread_execution_state_failed_; + + DISALLOW_COPY_AND_ASSIGN(ScreenCapturerWin); +}; + +ScreenCapturerWin::ScreenCapturerWin(const DesktopCaptureOptions& options) + : callback_(NULL), + mouse_shape_observer_(NULL), + current_screen_id_(kFullDesktopScreenId), + desktop_dc_(NULL), + memory_dc_(NULL), + dwmapi_library_(NULL), + composition_func_(NULL), + set_thread_execution_state_failed_(false) { + if (options.disable_effects()) { + // Load dwmapi.dll dynamically since it is not available on XP. + if (!dwmapi_library_) + dwmapi_library_ = LoadLibrary(kDwmapiLibraryName); + + if (dwmapi_library_) { + composition_func_ = reinterpret_cast( + GetProcAddress(dwmapi_library_, "DwmEnableComposition")); + } + } +} + +ScreenCapturerWin::~ScreenCapturerWin() { + if (desktop_dc_) + ReleaseDC(NULL, desktop_dc_); + if (memory_dc_) + DeleteDC(memory_dc_); + + // Restore Aero. + if (composition_func_) + (*composition_func_)(DWM_EC_ENABLECOMPOSITION); + + if (dwmapi_library_) + FreeLibrary(dwmapi_library_); +} + +void ScreenCapturerWin::Capture(const DesktopRegion& region) { + TickTime capture_start_time = TickTime::Now(); + + queue_.MoveToNextFrame(); + + // Request that the system not power-down the system, or the display hardware. + if (!SetThreadExecutionState(ES_DISPLAY_REQUIRED | ES_SYSTEM_REQUIRED)) { + if (!set_thread_execution_state_failed_) { + set_thread_execution_state_failed_ = true; + LOG_F(LS_WARNING) << "Failed to make system & display power assertion: " + << GetLastError(); + } + } + + // Make sure the GDI capture resources are up-to-date. + PrepareCaptureResources(); + + // Copy screen bits to the current buffer. + if (!CaptureImage()) { + callback_->OnCaptureCompleted(NULL); + return; + } + + const DesktopFrame* current_frame = queue_.current_frame(); + const DesktopFrame* last_frame = queue_.previous_frame(); + if (last_frame && last_frame->size().equals(current_frame->size())) { + // Make sure the differencer is set up correctly for these previous and + // current screens. + if (!differ_.get() || + (differ_->width() != current_frame->size().width()) || + (differ_->height() != current_frame->size().height()) || + (differ_->bytes_per_row() != current_frame->stride())) { + differ_.reset(new Differ(current_frame->size().width(), + current_frame->size().height(), + DesktopFrame::kBytesPerPixel, + current_frame->stride())); + } + + // Calculate difference between the two last captured frames. + DesktopRegion region; + differ_->CalcDirtyRegion(last_frame->data(), current_frame->data(), + ®ion); + helper_.InvalidateRegion(region); + } else { + // No previous frame is available, or the screen is resized. Invalidate the + // whole screen. + helper_.InvalidateScreen(current_frame->size()); + } + + helper_.set_size_most_recent(current_frame->size()); + + // Emit the current frame. + DesktopFrame* frame = queue_.current_frame()->Share(); + frame->set_dpi(DesktopVector( + GetDeviceCaps(desktop_dc_, LOGPIXELSX), + GetDeviceCaps(desktop_dc_, LOGPIXELSY))); + frame->mutable_updated_region()->Clear(); + helper_.TakeInvalidRegion(frame->mutable_updated_region()); + frame->set_capture_time_ms( + (TickTime::Now() - capture_start_time).Milliseconds()); + callback_->OnCaptureCompleted(frame); + + // Check for cursor shape update. + CaptureCursor(); +} + +void ScreenCapturerWin::SetMouseShapeObserver( + MouseShapeObserver* mouse_shape_observer) { + assert(!mouse_shape_observer_); + assert(mouse_shape_observer); + + mouse_shape_observer_ = mouse_shape_observer; +} + +bool ScreenCapturerWin::GetScreenList(ScreenList* screens) { + assert(screens->size() == 0); + 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) { + 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; +} + +void ScreenCapturerWin::Start(Callback* callback) { + assert(!callback_); + assert(callback); + + callback_ = callback; + + // Vote to disable Aero composited desktop effects while capturing. Windows + // will restore Aero automatically if the process exits. This has no effect + // under Windows 8 or higher. See crbug.com/124018. + if (composition_func_) + (*composition_func_)(DWM_EC_DISABLECOMPOSITION); +} + +void ScreenCapturerWin::PrepareCaptureResources() { + // Switch to the desktop receiving user input if different from the current + // one. + scoped_ptr input_desktop(Desktop::GetInputDesktop()); + if (input_desktop.get() != NULL && !desktop_.IsSame(*input_desktop)) { + // Release GDI resources otherwise SetThreadDesktop will fail. + if (desktop_dc_) { + ReleaseDC(NULL, desktop_dc_); + desktop_dc_ = NULL; + } + + if (memory_dc_) { + DeleteDC(memory_dc_); + memory_dc_ = NULL; + } + + // If SetThreadDesktop() fails, the thread is still assigned a desktop. + // So we can continue capture screen bits, just from the wrong desktop. + desktop_.SetThreadDesktop(input_desktop.release()); + + // Re-assert our vote to disable Aero. + // See crbug.com/124018 and crbug.com/129906. + if (composition_func_ != NULL) { + (*composition_func_)(DWM_EC_DISABLECOMPOSITION); + } + } + + // If the display bounds have changed then recreate GDI resources. + // TODO(wez): Also check for pixel format changes. + DesktopRect screen_rect(DesktopRect::MakeXYWH( + GetSystemMetrics(SM_XVIRTUALSCREEN), + GetSystemMetrics(SM_YVIRTUALSCREEN), + GetSystemMetrics(SM_CXVIRTUALSCREEN), + GetSystemMetrics(SM_CYVIRTUALSCREEN))); + if (!screen_rect.equals(desktop_dc_rect_)) { + if (desktop_dc_) { + ReleaseDC(NULL, desktop_dc_); + desktop_dc_ = NULL; + } + if (memory_dc_) { + DeleteDC(memory_dc_); + memory_dc_ = NULL; + } + desktop_dc_rect_ = DesktopRect(); + } + + if (desktop_dc_ == NULL) { + assert(memory_dc_ == NULL); + + // Create GDI device contexts to capture from the desktop into memory. + desktop_dc_ = GetDC(NULL); + if (!desktop_dc_) + abort(); + memory_dc_ = CreateCompatibleDC(desktop_dc_); + if (!memory_dc_) + abort(); + desktop_dc_rect_ = screen_rect; + + // Make sure the frame buffers will be reallocated. + queue_.Reset(); + + helper_.ClearInvalidRegion(); + } +} + +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() || + !queue_.current_frame()->size().equals(size)) { + assert(desktop_dc_ != NULL); + assert(memory_dc_ != NULL); + + size_t buffer_size = size.width() * size.height() * + DesktopFrame::kBytesPerPixel; + SharedMemory* shared_memory = + callback_->CreateSharedMemory(buffer_size); + scoped_ptr buffer( + DesktopFrameWin::Create(size, shared_memory, desktop_dc_)); + queue_.ReplaceCurrentFrame(buffer.release()); + } + + // Select the target bitmap into the memory dc and copy the rect from desktop + // to memory. + DesktopFrameWin* current = static_cast( + queue_.current_frame()->GetUnderlyingFrame()); + HGDIOBJ previous_object = SelectObject(memory_dc_, current->bitmap()); + if (previous_object != NULL) { + BitBlt(memory_dc_, + 0, 0, screen_rect.width(), screen_rect.height(), + desktop_dc_, + 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() { + CURSORINFO cursor_info; + cursor_info.cbSize = sizeof(CURSORINFO); + if (!GetCursorInfo(&cursor_info)) { + LOG_F(LS_ERROR) << "Unable to get cursor info. Error = " << GetLastError(); + return; + } + + // Note that |cursor_info.hCursor| does not need to be freed. + scoped_ptr cursor_image( + CreateMouseCursorFromHCursor(desktop_dc_, cursor_info.hCursor)); + if (!cursor_image.get()) + return; + + scoped_ptr cursor(new MouseCursorShape); + cursor->hotspot = cursor_image->hotspot(); + cursor->size = cursor_image->image()->size(); + uint8_t* current_row = cursor_image->image()->data(); + for (int y = 0; y < cursor_image->image()->size().height(); ++y) { + cursor->data.append(current_row, + current_row + cursor_image->image()->size().width() * + DesktopFrame::kBytesPerPixel); + current_row += cursor_image->image()->stride(); + } + + // Compare the current cursor with the last one we sent to the client. If + // they're the same, then don't bother sending the cursor again. + if (last_cursor_.size.equals(cursor->size) && + last_cursor_.hotspot.equals(cursor->hotspot) && + last_cursor_.data == cursor->data) { + return; + } + + LOG(LS_VERBOSE) << "Sending updated cursor: " << cursor->size.width() << "x" + << cursor->size.height(); + + // Record the last cursor image that we sent to the client. + last_cursor_ = *cursor; + + if (mouse_shape_observer_) + 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(device_mode.dmPosition.x, + device_mode.dmPosition.y, + device_mode.dmPelsWidth, + device_mode.dmPelsHeight); + return rect; +} +} // namespace + // static ScreenCapturer* ScreenCapturer::Create(const DesktopCaptureOptions& options) { - scoped_ptr gdi_capturer(new ScreenCapturerWinGdi(options)); - - if (options.allow_use_magnification_api()) - return new ScreenCapturerWinMagnifier(gdi_capturer.Pass()); - - return gdi_capturer.release(); + return new ScreenCapturerWin(options); } } // namespace webrtc diff --git a/webrtc/modules/desktop_capture/win/screen_capture_utils.cc b/webrtc/modules/desktop_capture/win/screen_capture_utils.cc deleted file mode 100644 index 4487e6d549..0000000000 --- a/webrtc/modules/desktop_capture/win/screen_capture_utils.cc +++ /dev/null @@ -1,91 +0,0 @@ -/* - * Copyright (c) 2014 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/win/screen_capture_utils.h" - -#include - -namespace webrtc { - -bool GetScreenList(ScreenCapturer::ScreenList* screens) { - assert(screens->size() == 0); - - 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; - - ScreenCapturer::Screen screen; - screen.id = device_index; - screens->push_back(screen); - } - return true; -} - -bool IsScreenValid(ScreenId screen, std::wstring* device_key) { - if (screen == kFullDesktopScreenId) { - *device_key = L""; - return true; - } - - DISPLAY_DEVICE device; - device.cb = sizeof(device); - BOOL enum_result = EnumDisplayDevices(NULL, screen, &device, 0); - if (enum_result) - *device_key = device.DeviceKey; - - return !!enum_result; -} - -DesktopRect GetScreenRect(ScreenId screen, const std::wstring& device_key) { - 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(); - - // Verifies the device index still maps to the same display device, to make - // sure we are capturing the same device when devices are added or removed. - // 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 (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(); - - return DesktopRect::MakeXYWH(device_mode.dmPosition.x, - device_mode.dmPosition.y, - device_mode.dmPelsWidth, - device_mode.dmPelsHeight); -} - -} // namespace webrtc diff --git a/webrtc/modules/desktop_capture/win/screen_capture_utils.h b/webrtc/modules/desktop_capture/win/screen_capture_utils.h deleted file mode 100644 index 42473e047b..0000000000 --- a/webrtc/modules/desktop_capture/win/screen_capture_utils.h +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright (c) 2014 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_WIN_SCREEN_CAPTURE_UTILS_H_ -#define WEBRTC_MODULES_DESKTOP_CAPTURE_WIN_SCREEN_CAPTURE_UTILS_H_ - -#include "webrtc/modules/desktop_capture/screen_capturer.h" - -namespace webrtc { - -// Output the list of active screens into |screens|. Returns true if succeeded, -// or false if it fails to enumerate the display devices. -bool GetScreenList(ScreenCapturer::ScreenList* screens); - -// Returns true if |screen| is a valid screen. The screen device key is -// returned through |device_key| if the screen is valid. The device key can be -// used in GetScreenRect to verify the screen matches the previously obtained -// id. -bool IsScreenValid(ScreenId screen, std::wstring* device_key); - -// Get the rect of the screen identified by |screen|, relative to the primary -// display's top-left. If the screen device key does not match |device_key|, or -// the screen does not exist, or any error happens, an empty rect is returned. -DesktopRect GetScreenRect(ScreenId screen, const std::wstring& device_key); - -} // namespace webrtc - -#endif // WEBRTC_MODULES_DESKTOP_CAPTURE_WIN_SCREEN_CAPTURE_UTILS_H_ diff --git a/webrtc/modules/desktop_capture/win/screen_capturer_win_gdi.cc b/webrtc/modules/desktop_capture/win/screen_capturer_win_gdi.cc deleted file mode 100644 index 5ab255337b..0000000000 --- a/webrtc/modules/desktop_capture/win/screen_capturer_win_gdi.cc +++ /dev/null @@ -1,322 +0,0 @@ -/* - * Copyright (c) 2014 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/win/screen_capturer_win_gdi.h" - -#include "webrtc/modules/desktop_capture/desktop_capture_options.h" -#include "webrtc/modules/desktop_capture/desktop_frame.h" -#include "webrtc/modules/desktop_capture/desktop_frame_win.h" -#include "webrtc/modules/desktop_capture/desktop_region.h" -#include "webrtc/modules/desktop_capture/differ.h" -#include "webrtc/modules/desktop_capture/mouse_cursor.h" -#include "webrtc/modules/desktop_capture/win/cursor.h" -#include "webrtc/modules/desktop_capture/win/desktop.h" -#include "webrtc/modules/desktop_capture/win/screen_capture_utils.h" -#include "webrtc/system_wrappers/interface/logging.h" -#include "webrtc/system_wrappers/interface/tick_util.h" - -namespace webrtc { - -namespace { - -// Constants from dwmapi.h. -const UINT DWM_EC_DISABLECOMPOSITION = 0; -const UINT DWM_EC_ENABLECOMPOSITION = 1; - -const wchar_t kDwmapiLibraryName[] = L"dwmapi.dll"; - -} // namespace - -ScreenCapturerWinGdi::ScreenCapturerWinGdi(const DesktopCaptureOptions& options) - : callback_(NULL), - mouse_shape_observer_(NULL), - current_screen_id_(kFullDesktopScreenId), - desktop_dc_(NULL), - memory_dc_(NULL), - dwmapi_library_(NULL), - composition_func_(NULL), - set_thread_execution_state_failed_(false) { - if (options.disable_effects()) { - // Load dwmapi.dll dynamically since it is not available on XP. - if (!dwmapi_library_) - dwmapi_library_ = LoadLibrary(kDwmapiLibraryName); - - if (dwmapi_library_) { - composition_func_ = reinterpret_cast( - GetProcAddress(dwmapi_library_, "DwmEnableComposition")); - } - } -} - -ScreenCapturerWinGdi::~ScreenCapturerWinGdi() { - if (desktop_dc_) - ReleaseDC(NULL, desktop_dc_); - if (memory_dc_) - DeleteDC(memory_dc_); - - // Restore Aero. - if (composition_func_) - (*composition_func_)(DWM_EC_ENABLECOMPOSITION); - - if (dwmapi_library_) - FreeLibrary(dwmapi_library_); -} - -void ScreenCapturerWinGdi::Capture(const DesktopRegion& region) { - TickTime capture_start_time = TickTime::Now(); - - queue_.MoveToNextFrame(); - - // Request that the system not power-down the system, or the display hardware. - if (!SetThreadExecutionState(ES_DISPLAY_REQUIRED | ES_SYSTEM_REQUIRED)) { - if (!set_thread_execution_state_failed_) { - set_thread_execution_state_failed_ = true; - LOG_F(LS_WARNING) << "Failed to make system & display power assertion: " - << GetLastError(); - } - } - - // Make sure the GDI capture resources are up-to-date. - PrepareCaptureResources(); - - if (!CaptureImage()) { - callback_->OnCaptureCompleted(NULL); - return; - } - - const DesktopFrame* current_frame = queue_.current_frame(); - const DesktopFrame* last_frame = queue_.previous_frame(); - if (last_frame && last_frame->size().equals(current_frame->size())) { - // Make sure the differencer is set up correctly for these previous and - // current screens. - if (!differ_.get() || - (differ_->width() != current_frame->size().width()) || - (differ_->height() != current_frame->size().height()) || - (differ_->bytes_per_row() != current_frame->stride())) { - differ_.reset(new Differ(current_frame->size().width(), - current_frame->size().height(), - DesktopFrame::kBytesPerPixel, - current_frame->stride())); - } - - // Calculate difference between the two last captured frames. - DesktopRegion region; - differ_->CalcDirtyRegion(last_frame->data(), current_frame->data(), - ®ion); - helper_.InvalidateRegion(region); - } else { - // No previous frame is available, or the screen is resized. Invalidate the - // whole screen. - helper_.InvalidateScreen(current_frame->size()); - } - - helper_.set_size_most_recent(current_frame->size()); - - // Emit the current frame. - DesktopFrame* frame = queue_.current_frame()->Share(); - frame->set_dpi(DesktopVector( - GetDeviceCaps(desktop_dc_, LOGPIXELSX), - GetDeviceCaps(desktop_dc_, LOGPIXELSY))); - frame->mutable_updated_region()->Clear(); - helper_.TakeInvalidRegion(frame->mutable_updated_region()); - frame->set_capture_time_ms( - (TickTime::Now() - capture_start_time).Milliseconds()); - callback_->OnCaptureCompleted(frame); - - // Check for cursor shape update. - CaptureCursor(); -} - -void ScreenCapturerWinGdi::SetMouseShapeObserver( - MouseShapeObserver* mouse_shape_observer) { - assert(!mouse_shape_observer_); - assert(mouse_shape_observer); - - mouse_shape_observer_ = mouse_shape_observer; -} - -bool ScreenCapturerWinGdi::GetScreenList(ScreenList* screens) { - return webrtc::GetScreenList(screens); -} - -bool ScreenCapturerWinGdi::SelectScreen(ScreenId id) { - bool valid = IsScreenValid(id, ¤t_device_key_); - if (valid) - current_screen_id_ = id; - return valid; -} - -void ScreenCapturerWinGdi::Start(Callback* callback) { - assert(!callback_); - assert(callback); - - callback_ = callback; - - // Vote to disable Aero composited desktop effects while capturing. Windows - // will restore Aero automatically if the process exits. This has no effect - // under Windows 8 or higher. See crbug.com/124018. - if (composition_func_) - (*composition_func_)(DWM_EC_DISABLECOMPOSITION); -} - -void ScreenCapturerWinGdi::PrepareCaptureResources() { - // Switch to the desktop receiving user input if different from the current - // one. - scoped_ptr input_desktop(Desktop::GetInputDesktop()); - if (input_desktop.get() != NULL && !desktop_.IsSame(*input_desktop)) { - // Release GDI resources otherwise SetThreadDesktop will fail. - if (desktop_dc_) { - ReleaseDC(NULL, desktop_dc_); - desktop_dc_ = NULL; - } - - if (memory_dc_) { - DeleteDC(memory_dc_); - memory_dc_ = NULL; - } - - // If SetThreadDesktop() fails, the thread is still assigned a desktop. - // So we can continue capture screen bits, just from the wrong desktop. - desktop_.SetThreadDesktop(input_desktop.release()); - - // Re-assert our vote to disable Aero. - // See crbug.com/124018 and crbug.com/129906. - if (composition_func_ != NULL) { - (*composition_func_)(DWM_EC_DISABLECOMPOSITION); - } - } - - // If the display bounds have changed then recreate GDI resources. - // TODO(wez): Also check for pixel format changes. - DesktopRect screen_rect(DesktopRect::MakeXYWH( - GetSystemMetrics(SM_XVIRTUALSCREEN), - GetSystemMetrics(SM_YVIRTUALSCREEN), - GetSystemMetrics(SM_CXVIRTUALSCREEN), - GetSystemMetrics(SM_CYVIRTUALSCREEN))); - if (!screen_rect.equals(desktop_dc_rect_)) { - if (desktop_dc_) { - ReleaseDC(NULL, desktop_dc_); - desktop_dc_ = NULL; - } - if (memory_dc_) { - DeleteDC(memory_dc_); - memory_dc_ = NULL; - } - desktop_dc_rect_ = DesktopRect(); - } - - if (desktop_dc_ == NULL) { - assert(memory_dc_ == NULL); - - // Create GDI device contexts to capture from the desktop into memory. - desktop_dc_ = GetDC(NULL); - if (!desktop_dc_) - abort(); - memory_dc_ = CreateCompatibleDC(desktop_dc_); - if (!memory_dc_) - abort(); - - desktop_dc_rect_ = screen_rect; - - // Make sure the frame buffers will be reallocated. - queue_.Reset(); - - helper_.ClearInvalidRegion(); - } -} - -bool ScreenCapturerWinGdi::CaptureImage() { - DesktopRect screen_rect = - GetScreenRect(current_screen_id_, current_device_key_); - 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() || - !queue_.current_frame()->size().equals(screen_rect.size())) { - assert(desktop_dc_ != NULL); - assert(memory_dc_ != NULL); - - size_t buffer_size = size.width() * size.height() * - DesktopFrame::kBytesPerPixel; - SharedMemory* shared_memory = callback_->CreateSharedMemory(buffer_size); - - scoped_ptr buffer; - buffer.reset( - DesktopFrameWin::Create(size, shared_memory, desktop_dc_)); - queue_.ReplaceCurrentFrame(buffer.release()); - } - - // Select the target bitmap into the memory dc and copy the rect from desktop - // to memory. - DesktopFrameWin* current = static_cast( - queue_.current_frame()->GetUnderlyingFrame()); - HGDIOBJ previous_object = SelectObject(memory_dc_, current->bitmap()); - if (previous_object != NULL) { - BitBlt(memory_dc_, - 0, 0, screen_rect.width(), screen_rect.height(), - desktop_dc_, - 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 ScreenCapturerWinGdi::CaptureCursor() { - CURSORINFO cursor_info; - cursor_info.cbSize = sizeof(CURSORINFO); - if (!GetCursorInfo(&cursor_info)) { - LOG_F(LS_ERROR) << "Unable to get cursor info. Error = " << GetLastError(); - return; - } - - // Note that |cursor_info.hCursor| does not need to be freed. - scoped_ptr cursor_image( - CreateMouseCursorFromHCursor(desktop_dc_, cursor_info.hCursor)); - if (!cursor_image.get()) - return; - - scoped_ptr cursor(new MouseCursorShape); - cursor->hotspot = cursor_image->hotspot(); - cursor->size = cursor_image->image()->size(); - uint8_t* current_row = cursor_image->image()->data(); - for (int y = 0; y < cursor_image->image()->size().height(); ++y) { - cursor->data.append(current_row, - current_row + cursor_image->image()->size().width() * - DesktopFrame::kBytesPerPixel); - current_row += cursor_image->image()->stride(); - } - - // Compare the current cursor with the last one we sent to the client. If - // they're the same, then don't bother sending the cursor again. - if (last_cursor_.size.equals(cursor->size) && - last_cursor_.hotspot.equals(cursor->hotspot) && - last_cursor_.data == cursor->data) { - return; - } - - LOG(LS_VERBOSE) << "Sending updated cursor: " << cursor->size.width() << "x" - << cursor->size.height(); - - // Record the last cursor image that we sent to the client. - last_cursor_ = *cursor; - - if (mouse_shape_observer_) - mouse_shape_observer_->OnCursorShapeChanged(cursor.release()); -} - -} // namespace webrtc diff --git a/webrtc/modules/desktop_capture/win/screen_capturer_win_gdi.h b/webrtc/modules/desktop_capture/win/screen_capturer_win_gdi.h deleted file mode 100644 index 2db87d097c..0000000000 --- a/webrtc/modules/desktop_capture/win/screen_capturer_win_gdi.h +++ /dev/null @@ -1,99 +0,0 @@ -/* - * Copyright (c) 2014 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_WIN_SCREEN_CAPTURER_WIN_GDI_H_ -#define WEBRTC_MODULES_DESKTOP_CAPTURE_WIN_SCREEN_CAPTURER_WIN_GDI_H_ - -#include "webrtc/modules/desktop_capture/screen_capturer.h" - -#include - -#include "webrtc/modules/desktop_capture/mouse_cursor_shape.h" -#include "webrtc/modules/desktop_capture/screen_capture_frame_queue.h" -#include "webrtc/modules/desktop_capture/screen_capturer_helper.h" -#include "webrtc/modules/desktop_capture/win/scoped_thread_desktop.h" -#include "webrtc/system_wrappers/interface/scoped_ptr.h" - -namespace webrtc { - -class Differ; -class MouseShapeObserver; - -// ScreenCapturerWinGdi captures 32bit RGB using GDI. -// -// ScreenCapturerWinGdi is double-buffered as required by ScreenCapturer. -class ScreenCapturerWinGdi : public ScreenCapturer { - public: - explicit ScreenCapturerWinGdi(const DesktopCaptureOptions& options); - virtual ~ScreenCapturerWinGdi(); - - // Overridden from ScreenCapturer: - virtual void Start(Callback* callback) OVERRIDE; - virtual void Capture(const DesktopRegion& region) OVERRIDE; - virtual void SetMouseShapeObserver( - MouseShapeObserver* mouse_shape_observer) OVERRIDE; - virtual bool GetScreenList(ScreenList* screens) OVERRIDE; - virtual bool SelectScreen(ScreenId id) OVERRIDE; - - private: - typedef HRESULT (WINAPI * DwmEnableCompositionFunc)(UINT); - - // Make sure that the device contexts match the screen configuration. - void PrepareCaptureResources(); - - // Captures the current screen contents into the current buffer. Returns true - // if succeeded. - bool CaptureImage(); - - // Capture the current cursor shape. - void CaptureCursor(); - - 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. - ScreenCapturerHelper helper_; - - // Snapshot of the last cursor bitmap we sent to the client. This is used - // to diff against the current cursor so we only send a cursor-change - // message when the shape has changed. - MouseCursorShape last_cursor_; - - ScopedThreadDesktop desktop_; - - // GDI resources used for screen capture. - HDC desktop_dc_; - HDC memory_dc_; - - // Queue of the frames buffers. - ScreenCaptureFrameQueue queue_; - - // Rectangle describing the bounds of the desktop device context, relative to - // the primary display's top-left. - DesktopRect desktop_dc_rect_; - - // Class to calculate the difference between two screen bitmaps. - scoped_ptr differ_; - - HMODULE dwmapi_library_; - DwmEnableCompositionFunc composition_func_; - - // Used to suppress duplicate logging of SetThreadExecutionState errors. - bool set_thread_execution_state_failed_; - - DISALLOW_COPY_AND_ASSIGN(ScreenCapturerWinGdi); -}; - -} // namespace webrtc - -#endif // WEBRTC_MODULES_DESKTOP_CAPTURE_WIN_SCREEN_CAPTURER_WIN_GDI_H_ diff --git a/webrtc/modules/desktop_capture/win/screen_capturer_win_magnifier.cc b/webrtc/modules/desktop_capture/win/screen_capturer_win_magnifier.cc deleted file mode 100644 index 4ce0073280..0000000000 --- a/webrtc/modules/desktop_capture/win/screen_capturer_win_magnifier.cc +++ /dev/null @@ -1,459 +0,0 @@ -/* - * Copyright (c) 2014 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/win/screen_capturer_win_magnifier.h" - -#include "webrtc/modules/desktop_capture/desktop_capture_options.h" -#include "webrtc/modules/desktop_capture/desktop_frame.h" -#include "webrtc/modules/desktop_capture/desktop_frame_win.h" -#include "webrtc/modules/desktop_capture/desktop_region.h" -#include "webrtc/modules/desktop_capture/differ.h" -#include "webrtc/modules/desktop_capture/mouse_cursor.h" -#include "webrtc/modules/desktop_capture/win/cursor.h" -#include "webrtc/modules/desktop_capture/win/desktop.h" -#include "webrtc/modules/desktop_capture/win/screen_capture_utils.h" -#include "webrtc/system_wrappers/interface/logging.h" -#include "webrtc/system_wrappers/interface/tick_util.h" - -namespace webrtc { - -// kMagnifierWindowClass has to be "Magnifier" according to the Magnification -// API. The other strings can be anything. -static LPCTSTR kMagnifierHostClass = L"ScreenCapturerWinMagnifierHost"; -static LPCTSTR kHostWindowName = L"MagnifierHost"; -static LPCTSTR kMagnifierWindowClass = L"Magnifier"; -static LPCTSTR kMagnifierWindowName = L"MagnifierWindow"; - -Atomic32 ScreenCapturerWinMagnifier::tls_index_(TLS_OUT_OF_INDEXES); - -ScreenCapturerWinMagnifier::ScreenCapturerWinMagnifier( - scoped_ptr fallback_capturer) - : fallback_capturer_(fallback_capturer.Pass()), - fallback_capturer_started_(false), - callback_(NULL), - current_screen_id_(kFullDesktopScreenId), - excluded_window_(NULL), - set_thread_execution_state_failed_(false), - desktop_dc_(NULL), - mag_lib_handle_(NULL), - mag_initialize_func_(NULL), - mag_uninitialize_func_(NULL), - set_window_source_func_(NULL), - set_window_filter_list_func_(NULL), - set_image_scaling_callback_func_(NULL), - host_window_(NULL), - magnifier_window_(NULL), - magnifier_initialized_(false), - magnifier_capture_succeeded_(true) { -} - -ScreenCapturerWinMagnifier::~ScreenCapturerWinMagnifier() { - // DestroyWindow must be called before MagUninitialize. magnifier_window_ is - // destroyed automatically when host_window_ is destroyed. - if (host_window_) - DestroyWindow(host_window_); - - if (magnifier_initialized_) - mag_uninitialize_func_(); - - if (mag_lib_handle_) - FreeLibrary(mag_lib_handle_); - - if (desktop_dc_) - ReleaseDC(NULL, desktop_dc_); -} - -void ScreenCapturerWinMagnifier::Start(Callback* callback) { - assert(!callback_); - assert(callback); - callback_ = callback; - - InitializeMagnifier(); -} - -void ScreenCapturerWinMagnifier::Capture(const DesktopRegion& region) { - TickTime capture_start_time = TickTime::Now(); - - queue_.MoveToNextFrame(); - - // Request that the system not power-down the system, or the display hardware. - if (!SetThreadExecutionState(ES_DISPLAY_REQUIRED | ES_SYSTEM_REQUIRED)) { - if (!set_thread_execution_state_failed_) { - set_thread_execution_state_failed_ = true; - LOG_F(LS_WARNING) << "Failed to make system & display power assertion: " - << GetLastError(); - } - } - // Switch to the desktop receiving user input if different from the current - // one. - scoped_ptr input_desktop(Desktop::GetInputDesktop()); - if (input_desktop.get() != NULL && !desktop_.IsSame(*input_desktop)) { - // Release GDI resources otherwise SetThreadDesktop will fail. - if (desktop_dc_) { - ReleaseDC(NULL, desktop_dc_); - desktop_dc_ = NULL; - } - // If SetThreadDesktop() fails, the thread is still assigned a desktop. - // So we can continue capture screen bits, just from the wrong desktop. - desktop_.SetThreadDesktop(input_desktop.release()); - } - - bool succeeded = false; - - // Do not try to use the magnfiier if it's capturing non-primary screen, or it - // failed before. - if (magnifier_initialized_ && IsCapturingPrimaryScreenOnly() && - magnifier_capture_succeeded_) { - DesktopRect rect = GetScreenRect(current_screen_id_, current_device_key_); - CreateCurrentFrameIfNecessary(rect.size()); - - // CaptureImage may fail in some situations, e.g. windows8 metro mode. - succeeded = CaptureImage(rect); - } - - // Defer to the fallback capturer if magnifier capturer did not work. - if (!succeeded) { - LOG_F(LS_WARNING) << "Switching to the fallback screen capturer."; - StartFallbackCapturer(); - fallback_capturer_->Capture(region); - return; - } - - const DesktopFrame* current_frame = queue_.current_frame(); - const DesktopFrame* last_frame = queue_.previous_frame(); - if (last_frame && last_frame->size().equals(current_frame->size())) { - // Make sure the differencer is set up correctly for these previous and - // current screens. - if (!differ_.get() || (differ_->width() != current_frame->size().width()) || - (differ_->height() != current_frame->size().height()) || - (differ_->bytes_per_row() != current_frame->stride())) { - differ_.reset(new Differ(current_frame->size().width(), - current_frame->size().height(), - DesktopFrame::kBytesPerPixel, - current_frame->stride())); - } - - // Calculate difference between the two last captured frames. - DesktopRegion region; - differ_->CalcDirtyRegion( - last_frame->data(), current_frame->data(), ®ion); - helper_.InvalidateRegion(region); - } else { - // No previous frame is available, or the screen is resized. Invalidate the - // whole screen. - helper_.InvalidateScreen(current_frame->size()); - } - - helper_.set_size_most_recent(current_frame->size()); - - // Emit the current frame. - DesktopFrame* frame = queue_.current_frame()->Share(); - frame->set_dpi(DesktopVector(GetDeviceCaps(desktop_dc_, LOGPIXELSX), - GetDeviceCaps(desktop_dc_, LOGPIXELSY))); - frame->mutable_updated_region()->Clear(); - helper_.TakeInvalidRegion(frame->mutable_updated_region()); - frame->set_capture_time_ms( - (TickTime::Now() - capture_start_time).Milliseconds()); - callback_->OnCaptureCompleted(frame); -} - -void ScreenCapturerWinMagnifier::SetMouseShapeObserver( - MouseShapeObserver* mouse_shape_observer) { - assert(false); // NOTREACHED(); -} - -bool ScreenCapturerWinMagnifier::GetScreenList(ScreenList* screens) { - return webrtc::GetScreenList(screens); -} - -bool ScreenCapturerWinMagnifier::SelectScreen(ScreenId id) { - bool valid = IsScreenValid(id, ¤t_device_key_); - - // Set current_screen_id_ even if the fallback capturer is being used, so we - // can switch back to the magnifier when possible. - if (valid) - current_screen_id_ = id; - - if (fallback_capturer_started_) - fallback_capturer_->SelectScreen(id); - - return valid; -} - -void ScreenCapturerWinMagnifier::SetExcludedWindow(WindowId excluded_window) { - excluded_window_ = (HWND)excluded_window; - if (excluded_window_ && magnifier_initialized_) { - set_window_filter_list_func_( - magnifier_window_, MW_FILTERMODE_EXCLUDE, 1, &excluded_window_); - } -} - -bool ScreenCapturerWinMagnifier::CaptureImage(const DesktopRect& rect) { - assert(magnifier_initialized_); - - // Set the magnifier control to cover the captured rect. The content of the - // magnifier control will be the captured image. - BOOL result = SetWindowPos(magnifier_window_, - NULL, - rect.left(), rect.top(), - rect.width(), rect.height(), - 0); - if (!result) { - LOG_F(LS_WARNING) << "Failed to call SetWindowPos: " << GetLastError() - << ". Rect = {" << rect.left() << ", " << rect.top() - << ", " << rect.right() << ", " << rect.bottom() << "}"; - return false; - } - - magnifier_capture_succeeded_ = false; - - RECT native_rect = {rect.left(), rect.top(), rect.right(), rect.bottom()}; - - // OnCaptured will be called via OnMagImageScalingCallback and fill in the - // frame before set_window_source_func_ returns. - result = set_window_source_func_(magnifier_window_, native_rect); - - if (!result) { - LOG_F(LS_WARNING) << "Failed to call MagSetWindowSource: " << GetLastError() - << ". Rect = {" << rect.left() << ", " << rect.top() - << ", " << rect.right() << ", " << rect.bottom() << "}"; - return false; - } - - return magnifier_capture_succeeded_; -} - -BOOL ScreenCapturerWinMagnifier::OnMagImageScalingCallback( - HWND hwnd, - void* srcdata, - MAGIMAGEHEADER srcheader, - void* destdata, - MAGIMAGEHEADER destheader, - RECT unclipped, - RECT clipped, - HRGN dirty) { - assert(tls_index_.Value() != TLS_OUT_OF_INDEXES); - - ScreenCapturerWinMagnifier* owner = - reinterpret_cast( - TlsGetValue(tls_index_.Value())); - - owner->OnCaptured(srcdata, srcheader); - - return TRUE; -} - -bool ScreenCapturerWinMagnifier::InitializeMagnifier() { - assert(!magnifier_initialized_); - - desktop_dc_ = GetDC(NULL); - - mag_lib_handle_ = LoadLibrary(L"Magnification.dll"); - if (!mag_lib_handle_) - return false; - - // Initialize Magnification API function pointers. - mag_initialize_func_ = reinterpret_cast( - GetProcAddress(mag_lib_handle_, "MagInitialize")); - mag_uninitialize_func_ = reinterpret_cast( - GetProcAddress(mag_lib_handle_, "MagUninitialize")); - set_window_source_func_ = reinterpret_cast( - GetProcAddress(mag_lib_handle_, "MagSetWindowSource")); - set_window_filter_list_func_ = reinterpret_cast( - GetProcAddress(mag_lib_handle_, "MagSetWindowFilterList")); - set_image_scaling_callback_func_ = - reinterpret_cast( - GetProcAddress(mag_lib_handle_, "MagSetImageScalingCallback")); - - if (!mag_initialize_func_ || !mag_uninitialize_func_ || - !set_window_source_func_ || !set_window_filter_list_func_ || - !set_image_scaling_callback_func_) { - LOG_F(LS_WARNING) << "Failed to initialize ScreenCapturerWinMagnifier: " - << "library functions missing."; - return false; - } - - BOOL result = mag_initialize_func_(); - if (!result) { - LOG_F(LS_WARNING) << "Failed to initialize ScreenCapturerWinMagnifier: " - << "error from MagInitialize " << GetLastError(); - return false; - } - - HMODULE hInstance = NULL; - result = GetModuleHandleExA(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | - GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT, - reinterpret_cast(&DefWindowProc), - &hInstance); - if (!result) { - mag_uninitialize_func_(); - LOG_F(LS_WARNING) << "Failed to initialize ScreenCapturerWinMagnifier: " - << "error from GetModulehandleExA " << GetLastError(); - return false; - } - - // Register the host window class. See the MSDN documentation of the - // Magnification API for more infomation. - WNDCLASSEX wcex = {}; - wcex.cbSize = sizeof(WNDCLASSEX); - wcex.lpfnWndProc = &DefWindowProc; - wcex.hInstance = hInstance; - wcex.hCursor = LoadCursor(NULL, IDC_ARROW); - wcex.lpszClassName = kMagnifierHostClass; - - // Ignore the error which may happen when the class is already registered. - RegisterClassEx(&wcex); - - // Create the host window. - host_window_ = CreateWindowEx(WS_EX_LAYERED, - kMagnifierHostClass, - kHostWindowName, - 0, - 0, 0, 0, 0, - NULL, - NULL, - hInstance, - NULL); - if (!host_window_) { - mag_uninitialize_func_(); - LOG_F(LS_WARNING) << "Failed to initialize ScreenCapturerWinMagnifier: " - << "error from creating host window " << GetLastError(); - return false; - } - - // Create the magnifier control. - magnifier_window_ = CreateWindow(kMagnifierWindowClass, - kMagnifierWindowName, - WS_CHILD | WS_VISIBLE, - 0, 0, 0, 0, - host_window_, - NULL, - hInstance, - NULL); - if (!magnifier_window_) { - mag_uninitialize_func_(); - LOG_F(LS_WARNING) << "Failed to initialize ScreenCapturerWinMagnifier: " - << "error from creating magnifier window " - << GetLastError(); - return false; - } - - // Hide the host window. - ShowWindow(host_window_, SW_HIDE); - - // Set the scaling callback to receive captured image. - result = set_image_scaling_callback_func_( - magnifier_window_, - &ScreenCapturerWinMagnifier::OnMagImageScalingCallback); - if (!result) { - mag_uninitialize_func_(); - LOG_F(LS_WARNING) << "Failed to initialize ScreenCapturerWinMagnifier: " - << "error from MagSetImageScalingCallback " - << GetLastError(); - return false; - } - - if (excluded_window_) { - result = set_window_filter_list_func_( - magnifier_window_, MW_FILTERMODE_EXCLUDE, 1, &excluded_window_); - if (!result) { - mag_uninitialize_func_(); - LOG_F(LS_WARNING) << "Failed to initialize ScreenCapturerWinMagnifier: " - << "error from MagSetWindowFilterList " - << GetLastError(); - return false; - } - } - - if (tls_index_.Value() == TLS_OUT_OF_INDEXES) { - // More than one threads may get here at the same time, but only one will - // write to tls_index_ using CompareExchange. - DWORD new_tls_index = TlsAlloc(); - if (!tls_index_.CompareExchange(new_tls_index, TLS_OUT_OF_INDEXES)) - TlsFree(new_tls_index); - } - - assert(tls_index_.Value() != TLS_OUT_OF_INDEXES); - TlsSetValue(tls_index_.Value(), this); - - magnifier_initialized_ = true; - return true; -} - -void ScreenCapturerWinMagnifier::OnCaptured(void* data, - const MAGIMAGEHEADER& header) { - DesktopFrame* current_frame = queue_.current_frame(); - - // Verify the format. - // TODO(jiayl): support capturing sources with pixel formats other than RGBA. - int captured_bytes_per_pixel = header.cbSize / header.width / header.height; - if (header.format != GUID_WICPixelFormat32bppRGBA || - header.width != static_cast(current_frame->size().width()) || - header.height != static_cast(current_frame->size().height()) || - header.stride != static_cast(current_frame->stride()) || - captured_bytes_per_pixel != DesktopFrame::kBytesPerPixel) { - LOG_F(LS_WARNING) << "Output format does not match the captured format: " - << "width = " << header.width << ", " - << "height = " << header.height << ", " - << "stride = " << header.stride << ", " - << "bpp = " << captured_bytes_per_pixel << ", " - << "pixel format RGBA ? " - << (header.format == GUID_WICPixelFormat32bppRGBA) << "."; - return; - } - - // Copy the data into the frame. - current_frame->CopyPixelsFrom( - reinterpret_cast(data), - header.stride, - DesktopRect::MakeXYWH(0, 0, header.width, header.height)); - - magnifier_capture_succeeded_ = true; -} - -void ScreenCapturerWinMagnifier::CreateCurrentFrameIfNecessary( - const DesktopSize& 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() || !queue_.current_frame()->size().equals(size)) { - size_t buffer_size = - size.width() * size.height() * DesktopFrame::kBytesPerPixel; - SharedMemory* shared_memory = callback_->CreateSharedMemory(buffer_size); - - scoped_ptr buffer; - if (shared_memory) { - buffer.reset(new SharedMemoryDesktopFrame( - size, size.width() * DesktopFrame::kBytesPerPixel, shared_memory)); - } else { - buffer.reset(new BasicDesktopFrame(size)); - } - queue_.ReplaceCurrentFrame(buffer.release()); - } -} - -bool ScreenCapturerWinMagnifier::IsCapturingPrimaryScreenOnly() const { - if (current_screen_id_ != kFullDesktopScreenId) - return current_screen_id_ == 0; // the primary screen is always '0'. - - return GetSystemMetrics(SM_CMONITORS) == 1; -} - -void ScreenCapturerWinMagnifier::StartFallbackCapturer() { - assert(fallback_capturer_); - if (!fallback_capturer_started_) { - fallback_capturer_started_ = true; - - fallback_capturer_->Start(callback_); - fallback_capturer_->SelectScreen(current_screen_id_); - } -} - -} // namespace webrtc diff --git a/webrtc/modules/desktop_capture/win/screen_capturer_win_magnifier.h b/webrtc/modules/desktop_capture/win/screen_capturer_win_magnifier.h deleted file mode 100644 index 618386a9d3..0000000000 --- a/webrtc/modules/desktop_capture/win/screen_capturer_win_magnifier.h +++ /dev/null @@ -1,159 +0,0 @@ -/* - * Copyright (c) 2014 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_WIN_SCREEN_CAPTURER_WIN_MAGNIFIER_H_ -#define WEBRTC_MODULES_DESKTOP_CAPTURE_WIN_SCREEN_CAPTURER_WIN_MAGNIFIER_H_ - -#include -#include -#include - -#include "webrtc/modules/desktop_capture/screen_capture_frame_queue.h" -#include "webrtc/modules/desktop_capture/screen_capturer_helper.h" -#include "webrtc/modules/desktop_capture/screen_capturer.h" -#include "webrtc/modules/desktop_capture/win/scoped_thread_desktop.h" -#include "webrtc/system_wrappers/interface/atomic32.h" -#include "webrtc/system_wrappers/interface/constructor_magic.h" -#include "webrtc/system_wrappers/interface/scoped_ptr.h" - -namespace webrtc { - -class DesktopFrame; -class DesktopRect; -class Differ; -class MouseShapeObserver; - -// Captures the screen using the Magnification API to support window exclusion. -// Each capturer must run on a dedicated thread because it uses thread local -// storage for redirecting the library callback. Also the thread must have a UI -// message loop to handle the window messages for the magnifier window. -class ScreenCapturerWinMagnifier : public ScreenCapturer { - public: - // |fallback_capturer| will be used to capture the screen if a non-primary - // screen is being captured, or the OS does not support Magnification API, or - // the magnifier capturer fails (e.g. in Windows8 Metro mode). - explicit ScreenCapturerWinMagnifier( - scoped_ptr fallback_capturer); - virtual ~ScreenCapturerWinMagnifier(); - - // Overridden from ScreenCapturer: - virtual void Start(Callback* callback) OVERRIDE; - virtual void Capture(const DesktopRegion& region) OVERRIDE; - virtual void SetMouseShapeObserver( - MouseShapeObserver* mouse_shape_observer) OVERRIDE; - virtual bool GetScreenList(ScreenList* screens) OVERRIDE; - virtual bool SelectScreen(ScreenId id) OVERRIDE; - virtual void SetExcludedWindow(WindowId window) OVERRIDE; - - private: - typedef BOOL(WINAPI* MagImageScalingCallback)(HWND hwnd, - void* srcdata, - MAGIMAGEHEADER srcheader, - void* destdata, - MAGIMAGEHEADER destheader, - RECT unclipped, - RECT clipped, - HRGN dirty); - typedef BOOL(WINAPI* MagInitializeFunc)(void); - typedef BOOL(WINAPI* MagUninitializeFunc)(void); - typedef BOOL(WINAPI* MagSetWindowSourceFunc)(HWND hwnd, RECT rect); - typedef BOOL(WINAPI* MagSetWindowFilterListFunc)(HWND hwnd, - DWORD dwFilterMode, - int count, - HWND* pHWND); - typedef BOOL(WINAPI* MagSetImageScalingCallbackFunc)( - HWND hwnd, - MagImageScalingCallback callback); - - static BOOL WINAPI OnMagImageScalingCallback(HWND hwnd, - void* srcdata, - MAGIMAGEHEADER srcheader, - void* destdata, - MAGIMAGEHEADER destheader, - RECT unclipped, - RECT clipped, - HRGN dirty); - - // Captures the screen within |rect| in the desktop coordinates. Returns true - // if succeeded. - // It can only capture the primary screen for now. The magnification library - // crashes under some screen configurations (e.g. secondary screen on top of - // primary screen) if it tries to capture a non-primary screen. The caller - // must make sure not calling it on non-primary screens. - bool CaptureImage(const DesktopRect& rect); - - // Helper method for setting up the magnifier control. Returns true if - // succeeded. - bool InitializeMagnifier(); - - // Called by OnMagImageScalingCallback to output captured data. - void OnCaptured(void* data, const MAGIMAGEHEADER& header); - - // Makes sure the current frame exists and matches |size|. - void CreateCurrentFrameIfNecessary(const DesktopSize& size); - - // Returns true if we are capturing the primary screen only. - bool IsCapturingPrimaryScreenOnly() const; - - // Start the fallback capturer and select the screen. - void StartFallbackCapturer(); - - static Atomic32 tls_index_; - - scoped_ptr fallback_capturer_; - bool fallback_capturer_started_; - Callback* callback_; - ScreenId current_screen_id_; - std::wstring current_device_key_; - HWND excluded_window_; - - // A thread-safe list of invalid rectangles, and the size of the most - // recently captured screen. - ScreenCapturerHelper helper_; - - // Queue of the frames buffers. - ScreenCaptureFrameQueue queue_; - - // Class to calculate the difference between two screen bitmaps. - scoped_ptr differ_; - - // Used to suppress duplicate logging of SetThreadExecutionState errors. - bool set_thread_execution_state_failed_; - - ScopedThreadDesktop desktop_; - - // Used for getting the screen dpi. - HDC desktop_dc_; - - HMODULE mag_lib_handle_; - MagInitializeFunc mag_initialize_func_; - MagUninitializeFunc mag_uninitialize_func_; - MagSetWindowSourceFunc set_window_source_func_; - MagSetWindowFilterListFunc set_window_filter_list_func_; - MagSetImageScalingCallbackFunc set_image_scaling_callback_func_; - - // The hidden window hosting the magnifier control. - HWND host_window_; - // The magnifier control that captures the screen. - HWND magnifier_window_; - - // True if the magnifier control has been successfully initialized. - bool magnifier_initialized_; - - // True if the last OnMagImageScalingCallback was called and handled - // successfully. Reset at the beginning of each CaptureImage call. - bool magnifier_capture_succeeded_; - - DISALLOW_COPY_AND_ASSIGN(ScreenCapturerWinMagnifier); -}; - -} // namespace webrtc - -#endif // WEBRTC_MODULES_DESKTOP_CAPTURE_WIN_SCREEN_CAPTURER_WIN_MAGNIFIER_H_