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:
@ -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_
|
||||
|
@ -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
|
||||
|
@ -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_);
|
||||
|
@ -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
|
||||
|
Reference in New Issue
Block a user