Revert 6048 "Implement the Windows screen capturer using the Mag..."
> Implement the Windows screen capturer using the Magnification API. > The original ScreenCapturerWin is renamed ScreenCapturerWinGdi. > > BUG=2789 > TESTED=full desktop cast and single monitor cast works on win7 and win8 desktop mode. Have to use GDI capturer on win8 metro mode. Changing display configuration work on the fly. > R=sergeyu@chromium.org, wez@chromium.org > > Review URL: https://webrtc-codereview.appspot.com/12149004 TBR=jiayl@webrtc.org Review URL: https://webrtc-codereview.appspot.com/15429005 git-svn-id: http://webrtc.googlecode.com/svn/trunk@6052 4adac7df-926f-26a2-2b94-8c16560cd09d
This commit is contained in:
parent
633aff6bd0
commit
7dccce3948
@ -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",
|
||||
|
@ -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() {}
|
||||
|
@ -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<SharedXDisplay> x_display_;
|
||||
@ -83,10 +74,6 @@ class DesktopCaptureOptions {
|
||||
#if defined(WEBRTC_MAC) && !defined(WEBRTC_IOS)
|
||||
scoped_refptr<DesktopConfigurationMonitor> configuration_monitor_;
|
||||
#endif
|
||||
|
||||
#if defined(WEBRTC_WIN)
|
||||
bool allow_use_magnification_api_;
|
||||
#endif
|
||||
bool use_update_notifications_;
|
||||
bool disable_effects_;
|
||||
};
|
||||
|
@ -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
|
||||
|
@ -10,20 +10,451 @@
|
||||
|
||||
#include "webrtc/modules/desktop_capture/screen_capturer.h"
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
#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> 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<DwmEnableCompositionFunc>(
|
||||
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<Desktop> 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<DesktopFrameWin> 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<DesktopFrameWin*>(
|
||||
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<MouseCursor> cursor_image(
|
||||
CreateMouseCursorFromHCursor(desktop_dc_, cursor_info.hCursor));
|
||||
if (!cursor_image.get())
|
||||
return;
|
||||
|
||||
scoped_ptr<MouseCursorShape> 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<ScreenCapturer> 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
|
||||
|
@ -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 <windows.h>
|
||||
|
||||
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
|
@ -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_
|
@ -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<DwmEnableCompositionFunc>(
|
||||
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<Desktop> 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<DesktopFrame> 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<DesktopFrameWin*>(
|
||||
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<MouseCursor> cursor_image(
|
||||
CreateMouseCursorFromHCursor(desktop_dc_, cursor_info.hCursor));
|
||||
if (!cursor_image.get())
|
||||
return;
|
||||
|
||||
scoped_ptr<MouseCursorShape> 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
|
@ -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 <windows.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/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> 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_
|
@ -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<ScreenCapturer> 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<Desktop> 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<ScreenCapturerWinMagnifier*>(
|
||||
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<MagInitializeFunc>(
|
||||
GetProcAddress(mag_lib_handle_, "MagInitialize"));
|
||||
mag_uninitialize_func_ = reinterpret_cast<MagUninitializeFunc>(
|
||||
GetProcAddress(mag_lib_handle_, "MagUninitialize"));
|
||||
set_window_source_func_ = reinterpret_cast<MagSetWindowSourceFunc>(
|
||||
GetProcAddress(mag_lib_handle_, "MagSetWindowSource"));
|
||||
set_window_filter_list_func_ = reinterpret_cast<MagSetWindowFilterListFunc>(
|
||||
GetProcAddress(mag_lib_handle_, "MagSetWindowFilterList"));
|
||||
set_image_scaling_callback_func_ =
|
||||
reinterpret_cast<MagSetImageScalingCallbackFunc>(
|
||||
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<char*>(&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<UINT>(current_frame->size().width()) ||
|
||||
header.height != static_cast<UINT>(current_frame->size().height()) ||
|
||||
header.stride != static_cast<UINT>(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<uint8_t*>(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<DesktopFrame> 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
|
@ -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 <magnification.h>
|
||||
#include <wincodec.h>
|
||||
#include <windows.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/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<ScreenCapturer> 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<ScreenCapturer> 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> 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_
|
Loading…
Reference in New Issue
Block a user