Implement screen enumeration and individual screen capturing for Windows.

BUG=2787
R=sergeyu@chromium.org

Review URL: https://webrtc-codereview.appspot.com/7239004

git-svn-id: http://webrtc.googlecode.com/svn/trunk@5399 4adac7df-926f-26a2-2b94-8c16560cd09d
This commit is contained in:
jiayl@webrtc.org
2014-01-17 17:19:16 +00:00
parent ead202b973
commit 4985927d36
4 changed files with 147 additions and 16 deletions

View File

@ -31,6 +31,8 @@ typedef int ScreenId;
// The screen id corresponds to all screen combined together.
const ScreenId kFullDesktopScreenId = -1;
const ScreenId kInvalidScreenId = -2;
} // namespace webrtc
#endif // WEBRTC_MODULES_DESKTOP_CAPTURE_DESKTOP_CAPTURE_TYPES_H_

View File

@ -20,13 +20,17 @@ namespace webrtc {
class MouseCursorMonitorWin : public MouseCursorMonitor {
public:
explicit MouseCursorMonitorWin(HWND window);
explicit MouseCursorMonitorWin(ScreenId screen);
virtual ~MouseCursorMonitorWin();
virtual void Init(Callback* callback, Mode mode) OVERRIDE;
virtual void Capture() OVERRIDE;
private:
DesktopRect GetScreenRect();
HWND window_;
ScreenId screen_;
Callback* callback_;
Mode mode_;
@ -38,12 +42,23 @@ class MouseCursorMonitorWin : public MouseCursorMonitor {
MouseCursorMonitorWin::MouseCursorMonitorWin(HWND window)
: window_(window),
screen_(kInvalidScreenId),
callback_(NULL),
mode_(SHAPE_AND_POSITION),
desktop_dc_(NULL),
last_cursor_(NULL) {
}
MouseCursorMonitorWin::MouseCursorMonitorWin(ScreenId screen)
: window_(NULL),
screen_(screen),
callback_(NULL),
mode_(SHAPE_AND_POSITION),
desktop_dc_(NULL),
last_cursor_(NULL) {
assert(screen >= kFullDesktopScreenId);
}
MouseCursorMonitorWin::~MouseCursorMonitorWin() {
if (desktop_dc_)
ReleaseDC(NULL, desktop_dc_);
@ -94,11 +109,47 @@ void MouseCursorMonitorWin::Capture() {
if (inside)
inside = (window_ == WindowFromPoint(cursor_info.ptScreenPos));
}
} else {
assert(screen_ != kInvalidScreenId);
DesktopRect rect = GetScreenRect();
if (inside)
inside = rect.Contains(position);
position = position.subtract(rect.top_left());
}
callback_->OnMouseCursorPosition(inside ? INSIDE : OUTSIDE, position);
}
DesktopRect MouseCursorMonitorWin::GetScreenRect() {
assert(screen_ != kInvalidScreenId);
if (screen_ == kFullDesktopScreenId) {
return DesktopRect::MakeXYWH(
GetSystemMetrics(SM_XVIRTUALSCREEN),
GetSystemMetrics(SM_YVIRTUALSCREEN),
GetSystemMetrics(SM_CXVIRTUALSCREEN),
GetSystemMetrics(SM_CYVIRTUALSCREEN));
}
DISPLAY_DEVICE device;
device.cb = sizeof(device);
BOOL result = EnumDisplayDevices(NULL, screen_, &device, 0);
if (!result)
return DesktopRect();
DEVMODE device_mode;
device_mode.dmSize = sizeof(device_mode);
device_mode.dmDriverExtra = 0;
result = EnumDisplaySettingsEx(
device.DeviceName, ENUM_CURRENT_SETTINGS, &device_mode, 0);
if (!result)
return DesktopRect();
return DesktopRect::MakeXYWH(
GetSystemMetrics(SM_XVIRTUALSCREEN) + device_mode.dmPosition.x,
GetSystemMetrics(SM_YVIRTUALSCREEN) + device_mode.dmPosition.y,
device_mode.dmPelsWidth,
device_mode.dmPelsHeight);
}
MouseCursorMonitor* MouseCursorMonitor::CreateForWindow(
const DesktopCaptureOptions& options, WindowId window) {
return new MouseCursorMonitorWin(reinterpret_cast<HWND>(window));
@ -107,7 +158,7 @@ MouseCursorMonitor* MouseCursorMonitor::CreateForWindow(
MouseCursorMonitor* MouseCursorMonitor::CreateForScreen(
const DesktopCaptureOptions& options,
ScreenId screen) {
return new MouseCursorMonitorWin(NULL);
return new MouseCursorMonitorWin(screen);
}
} // namespace webrtc

View File

@ -59,6 +59,15 @@ SharedMemory* ScreenCapturerTest::CreateSharedMemory(size_t size) {
return new FakeSharedMemory(new char[size], size);
}
TEST_F(ScreenCapturerTest, GetScreenListAndSelectScreen) {
webrtc::ScreenCapturer::ScreenList screens;
EXPECT_TRUE(capturer_->GetScreenList(&screens));
for(webrtc::ScreenCapturer::ScreenList::iterator it = screens.begin();
it != screens.end(); ++it) {
EXPECT_TRUE(capturer_->SelectScreen(it->id));
}
}
TEST_F(ScreenCapturerTest, StartCapturer) {
capturer_->SetMouseShapeObserver(&mouse_observer_);
capturer_->Start(&callback_);

View File

@ -60,14 +60,21 @@ class ScreenCapturerWin : public ScreenCapturer {
// Make sure that the device contexts match the screen configuration.
void PrepareCaptureResources();
// Captures the current screen contents into the current buffer.
void CaptureImage();
// Captures the current screen contents into the current buffer. Returns true
// if succeeded.
bool CaptureImage();
// Capture the current cursor shape.
void CaptureCursor();
// Get the rect of the currently selected screen. If the screen is disabled
// or disconnected, or any error happens, an empty rect is returned.
DesktopRect GetScreenRect();
Callback* callback_;
MouseShapeObserver* mouse_shape_observer_;
ScreenId current_screen_id_;
std::wstring current_device_key_;
// A thread-safe list of invalid rectangles, and the size of the most
// recently captured screen.
@ -105,6 +112,7 @@ class ScreenCapturerWin : public ScreenCapturer {
ScreenCapturerWin::ScreenCapturerWin(const DesktopCaptureOptions& options)
: callback_(NULL),
mouse_shape_observer_(NULL),
current_screen_id_(kFullDesktopScreenId),
desktop_dc_(NULL),
memory_dc_(NULL),
dwmapi_library_(NULL),
@ -154,7 +162,10 @@ void ScreenCapturerWin::Capture(const DesktopRegion& region) {
PrepareCaptureResources();
// Copy screen bits to the current buffer.
CaptureImage();
if (!CaptureImage()) {
callback_->OnCaptureCompleted(NULL);
return;
}
const DesktopFrame* current_frame = queue_.current_frame();
const DesktopFrame* last_frame = queue_.previous_frame();
@ -208,15 +219,38 @@ void ScreenCapturerWin::SetMouseShapeObserver(
bool ScreenCapturerWin::GetScreenList(ScreenList* screens) {
assert(screens->size() == 0);
// TODO(jiayl): implement screen enumeration.
Screen default_screen;
default_screen.id = 0;
screens->push_back(default_screen);
BOOL enum_result = TRUE;
for (int device_index = 0; ; ++device_index) {
DISPLAY_DEVICE device;
device.cb = sizeof(device);
enum_result = EnumDisplayDevices(NULL, device_index, &device, 0);
// |enum_result| is 0 if we have enumerated all devices.
if (!enum_result)
break;
// We only care about active displays.
if (!(device.StateFlags & DISPLAY_DEVICE_ACTIVE))
continue;
Screen screen;
screen.id = device_index;
screens->push_back(screen);
}
return true;
}
bool ScreenCapturerWin::SelectScreen(ScreenId id) {
// TODO(jiayl): implement screen selection.
if (id == kFullDesktopScreenId) {
current_screen_id_ = id;
return true;
}
DISPLAY_DEVICE device;
device.cb = sizeof(device);
BOOL enum_result = EnumDisplayDevices(NULL, id, &device, 0);
if (!enum_result)
return false;
current_device_key_ = device.DeviceKey;
current_screen_id_ = id;
return true;
}
@ -298,17 +332,19 @@ void ScreenCapturerWin::PrepareCaptureResources() {
}
}
void ScreenCapturerWin::CaptureImage() {
bool ScreenCapturerWin::CaptureImage() {
DesktopRect screen_rect = GetScreenRect();
if (screen_rect.is_empty())
return false;
DesktopSize size = screen_rect.size();
// If the current buffer is from an older generation then allocate a new one.
// Note that we can't reallocate other buffers at this point, since the caller
// may still be reading from them.
if (!queue_.current_frame()) {
if (!queue_.current_frame() ||
!queue_.current_frame()->size().equals(size)) {
assert(desktop_dc_ != NULL);
assert(memory_dc_ != NULL);
DesktopSize size = DesktopSize(
desktop_dc_rect_.width(), desktop_dc_rect_.height());
size_t buffer_size = size.width() * size.height() *
DesktopFrame::kBytesPerPixel;
SharedMemory* shared_memory =
@ -325,15 +361,16 @@ void ScreenCapturerWin::CaptureImage() {
HGDIOBJ previous_object = SelectObject(memory_dc_, current->bitmap());
if (previous_object != NULL) {
BitBlt(memory_dc_,
0, 0, desktop_dc_rect_.width(), desktop_dc_rect_.height(),
0, 0, screen_rect.width(), screen_rect.height(),
desktop_dc_,
desktop_dc_rect_.left(), desktop_dc_rect_.top(),
screen_rect.left(), screen_rect.top(),
SRCCOPY | CAPTUREBLT);
// Select back the previously selected object to that the device contect
// could be destroyed independently of the bitmap if needed.
SelectObject(memory_dc_, previous_object);
}
return true;
}
void ScreenCapturerWin::CaptureCursor() {
@ -379,6 +416,38 @@ void ScreenCapturerWin::CaptureCursor() {
mouse_shape_observer_->OnCursorShapeChanged(cursor.release());
}
DesktopRect ScreenCapturerWin::GetScreenRect() {
DesktopRect rect = desktop_dc_rect_;
if (current_screen_id_ == kFullDesktopScreenId)
return rect;
DISPLAY_DEVICE device;
device.cb = sizeof(device);
BOOL result = EnumDisplayDevices(NULL, current_screen_id_, &device, 0);
if (!result)
return DesktopRect();
// Verifies the device index still maps to the same display device. DeviceKey
// is documented as reserved, but it actually contains the registry key for
// the device and is unique for each monitor, while DeviceID is not.
if (current_device_key_ != device.DeviceKey)
return DesktopRect();
DEVMODE device_mode;
device_mode.dmSize = sizeof(device_mode);
device_mode.dmDriverExtra = 0;
result = EnumDisplaySettingsEx(
device.DeviceName, ENUM_CURRENT_SETTINGS, &device_mode, 0);
if (!result)
return DesktopRect();
rect = DesktopRect::MakeXYWH(
rect.left() + device_mode.dmPosition.x,
rect.top() + device_mode.dmPosition.y,
device_mode.dmPelsWidth,
device_mode.dmPelsHeight);
return rect;
}
} // namespace
// static