Revert "Remove mouse cursor capturer from the ScreenCapturer interface"

This reverts commit 0adc4953512ee0a57cf7f3c0591b024c2316554a. It broke
FYI bots

TBR=sergeyu@chromium.org

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

git-svn-id: http://webrtc.googlecode.com/svn/trunk@7364 4adac7df-926f-26a2-2b94-8c16560cd09d
This commit is contained in:
sergeyu@chromium.org 2014-10-02 01:36:43 +00:00
parent 1fced0f2aa
commit 6138f0f89d
12 changed files with 304 additions and 0 deletions

View File

@ -48,6 +48,7 @@ source_set("desktop_capture") {
"mouse_cursor_monitor.h",
"mouse_cursor_monitor_mac.mm",
"mouse_cursor_monitor_win.cc",
"mouse_cursor_shape.h",
"screen_capture_frame_queue.cc",
"screen_capture_frame_queue.h",
"screen_capturer.cc",

View File

@ -51,6 +51,7 @@
"mouse_cursor_monitor_mac.mm",
"mouse_cursor_monitor_win.cc",
"mouse_cursor_monitor_x11.cc",
"mouse_cursor_shape.h",
"screen_capture_frame_queue.cc",
"screen_capture_frame_queue.h",
"screen_capturer.cc",

View File

@ -0,0 +1,36 @@
/*
* Copyright (c) 2013 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_MOUSE_CURSOR_SHAPE_H_
#define WEBRTC_MODULES_DESKTOP_CAPTURE_MOUSE_CURSOR_SHAPE_H_
#include <string>
#include "webrtc/modules/desktop_capture/desktop_geometry.h"
namespace webrtc {
// Type used to return mouse cursor shape from video capturers.
//
// TODO(sergeyu): Remove this type and use MouseCursor instead.
struct MouseCursorShape {
// Size of the cursor in screen pixels.
DesktopSize size;
// Coordinates of the cursor hotspot relative to upper-left corner.
DesktopVector hotspot;
// Cursor pixmap data in 32-bit BGRA format.
std::string data;
};
} // namespace webrtc
#endif // WEBRTC_MODULES_DESKTOP_CAPTURE_MOUSE_CURSOR_SHAPE_H_

View File

@ -21,6 +21,7 @@
namespace webrtc {
class DesktopCaptureOptions;
struct MouseCursorShape;
// Class used to capture video frames asynchronously.
//
@ -49,6 +50,21 @@ class ScreenCapturer : public DesktopCapturer {
};
typedef std::vector<Screen> ScreenList;
// Provides callbacks used by the capturer to pass captured video frames and
// mouse cursor shapes to the processing pipeline.
//
// TODO(sergeyu): Move cursor shape capturing to a separate class because it's
// unrelated.
class MouseShapeObserver {
public:
// Called when the cursor shape has changed. Must take ownership of
// |cursor_shape|.
virtual void OnCursorShapeChanged(MouseCursorShape* cursor_shape) = 0;
protected:
virtual ~MouseShapeObserver() {}
};
virtual ~ScreenCapturer() {}
// Creates platform-specific capturer.
@ -68,6 +84,11 @@ class ScreenCapturer : public DesktopCapturer {
static ScreenCapturer* CreateWithDisableAero(bool disable_aero);
#endif // defined(WEBRTC_WIN)
// Called at the beginning of a capturing session. |mouse_shape_observer| must
// remain valid until the capturer is destroyed.
virtual void SetMouseShapeObserver(
MouseShapeObserver* mouse_shape_observer) = 0;
// Get the list of screens (not containing kFullDesktopScreenId). Returns
// false in case of a failure.
virtual bool GetScreenList(ScreenList* screens) = 0;

View File

@ -28,6 +28,7 @@
#include "webrtc/modules/desktop_capture/mac/desktop_configuration.h"
#include "webrtc/modules/desktop_capture/mac/desktop_configuration_monitor.h"
#include "webrtc/modules/desktop_capture/mac/scoped_pixel_buffer_object.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/system_wrappers/interface/logging.h"
@ -199,10 +200,14 @@ class ScreenCapturerMac : public ScreenCapturer {
virtual void Start(Callback* callback) OVERRIDE;
virtual void Capture(const DesktopRegion& region) OVERRIDE;
virtual void SetExcludedWindow(WindowId window) OVERRIDE;
virtual void SetMouseShapeObserver(
MouseShapeObserver* mouse_shape_observer) OVERRIDE;
virtual bool GetScreenList(ScreenList* screens) OVERRIDE;
virtual bool SelectScreen(ScreenId id) OVERRIDE;
private:
void CaptureCursor();
void GlBlitFast(const DesktopFrame& frame,
const DesktopRegion& region);
void GlBlitSlow(const DesktopFrame& frame);
@ -234,6 +239,7 @@ class ScreenCapturerMac : public ScreenCapturer {
DesktopFrame* CreateFrame();
Callback* callback_;
MouseShapeObserver* mouse_shape_observer_;
CGLContextObj cgl_context_;
ScopedPixelBufferObject pixel_buffer_object_;
@ -258,6 +264,9 @@ class ScreenCapturerMac : public ScreenCapturer {
// recently captured screen.
ScreenCapturerHelper helper_;
// The last cursor that we sent to the client.
MouseCursorShape last_cursor_;
// Contains an invalid region from the previous capture.
DesktopRegion last_invalid_region_;
@ -309,6 +318,7 @@ class InvertedDesktopFrame : public DesktopFrame {
ScreenCapturerMac::ScreenCapturerMac(
scoped_refptr<DesktopConfigurationMonitor> desktop_config_monitor)
: callback_(NULL),
mouse_shape_observer_(NULL),
cgl_context_(NULL),
current_display_(0),
dip_to_pixel_scale_(1.0f),
@ -448,6 +458,9 @@ void ScreenCapturerMac::Capture(const DesktopRegion& region_to_capture) {
// and accessing display structures.
desktop_config_monitor_->Unlock();
// Capture the current cursor shape and notify |callback_| if it has changed.
CaptureCursor();
new_frame->set_capture_time_ms(
(TickTime::Now() - capture_start_time).Milliseconds());
callback_->OnCaptureCompleted(new_frame);
@ -457,6 +470,13 @@ void ScreenCapturerMac::SetExcludedWindow(WindowId window) {
excluded_window_ = window;
}
void ScreenCapturerMac::SetMouseShapeObserver(
MouseShapeObserver* mouse_shape_observer) {
assert(!mouse_shape_observer_);
assert(mouse_shape_observer);
mouse_shape_observer_ = mouse_shape_observer;
}
bool ScreenCapturerMac::GetScreenList(ScreenList* screens) {
assert(screens->size() == 0);
if (rtc::GetOSVersionName() < rtc::kMacOSLion) {
@ -498,6 +518,61 @@ bool ScreenCapturerMac::SelectScreen(ScreenId id) {
return true;
}
void ScreenCapturerMac::CaptureCursor() {
if (!mouse_shape_observer_)
return;
NSCursor* cursor = [NSCursor currentSystemCursor];
if (cursor == nil)
return;
NSImage* nsimage = [cursor image];
NSPoint hotspot = [cursor hotSpot];
NSSize size = [nsimage size];
CGImageRef image = [nsimage CGImageForProposedRect:NULL
context:nil
hints:nil];
if (image == nil)
return;
if (CGImageGetBitsPerPixel(image) != 32 ||
CGImageGetBytesPerRow(image) != (size.width * 4) ||
CGImageGetBitsPerComponent(image) != 8) {
return;
}
CGDataProviderRef provider = CGImageGetDataProvider(image);
CFDataRef image_data_ref = CGDataProviderCopyData(provider);
if (image_data_ref == NULL)
return;
const char* cursor_src_data =
reinterpret_cast<const char*>(CFDataGetBytePtr(image_data_ref));
int data_size = CFDataGetLength(image_data_ref);
// Create a MouseCursorShape that describes the cursor and pass it to
// the client.
scoped_ptr<MouseCursorShape> cursor_shape(new MouseCursorShape());
cursor_shape->size.set(size.width, size.height);
cursor_shape->hotspot.set(hotspot.x, hotspot.y);
cursor_shape->data.assign(cursor_src_data, cursor_src_data + data_size);
CFRelease(image_data_ref);
// 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_shape->size) &&
last_cursor_.hotspot.equals(cursor_shape->hotspot) &&
last_cursor_.data == cursor_shape->data) {
return;
}
// Record the last cursor image that we sent to the client.
last_cursor_ = *cursor_shape;
mouse_shape_observer_->OnCursorShapeChanged(cursor_shape.release());
}
void ScreenCapturerMac::GlBlitFast(const DesktopFrame& frame,
const DesktopRegion& region) {
// Clip to the size of our current screen.

View File

@ -12,6 +12,7 @@
#define WEBRTC_MODULES_DESKTOP_CAPTURE_SCREEN_CAPTURER_MOCK_OBJECTS_H_
#include "testing/gmock/include/gmock/gmock.h"
#include "webrtc/modules/desktop_capture/mouse_cursor_shape.h"
#include "webrtc/modules/desktop_capture/screen_capturer.h"
namespace webrtc {
@ -23,6 +24,8 @@ class MockScreenCapturer : public ScreenCapturer {
MOCK_METHOD1(Start, void(Callback* callback));
MOCK_METHOD1(Capture, void(const DesktopRegion& region));
MOCK_METHOD1(SetMouseShapeObserver, void(
MouseShapeObserver* mouse_shape_observer));
MOCK_METHOD1(GetScreenList, bool(ScreenList* screens));
MOCK_METHOD1(SelectScreen, bool(ScreenId id));
@ -42,6 +45,24 @@ class MockScreenCapturerCallback : public ScreenCapturer::Callback {
DISALLOW_COPY_AND_ASSIGN(MockScreenCapturerCallback);
};
class MockMouseShapeObserver : public ScreenCapturer::MouseShapeObserver {
public:
MockMouseShapeObserver() {}
virtual ~MockMouseShapeObserver() {}
void OnCursorShapeChanged(MouseCursorShape* cursor_shape) OVERRIDE {
OnCursorShapeChangedPtr(cursor_shape);
delete cursor_shape;
}
MOCK_METHOD1(OnCursorShapeChangedPtr,
void(MouseCursorShape* cursor_shape));
private:
DISALLOW_COPY_AND_ASSIGN(MockMouseShapeObserver);
};
} // namespace webrtc
#endif // WEBRTC_MODULES_DESKTOP_CAPTURE_SCREEN_CAPTURER_MOCK_OBJECTS_H_

View File

@ -37,6 +37,7 @@ class ScreenCapturerTest : public testing::Test {
protected:
scoped_ptr<ScreenCapturer> capturer_;
MockMouseShapeObserver mouse_observer_;
MockScreenCapturerCallback callback_;
};
@ -68,6 +69,7 @@ TEST_F(ScreenCapturerTest, GetScreenListAndSelectScreen) {
}
TEST_F(ScreenCapturerTest, StartCapturer) {
capturer_->SetMouseShapeObserver(&mouse_observer_);
capturer_->Start(&callback_);
}
@ -76,6 +78,8 @@ TEST_F(ScreenCapturerTest, Capture) {
DesktopFrame* frame = NULL;
EXPECT_CALL(callback_, OnCaptureCompleted(_))
.WillOnce(SaveArg<0>(&frame));
EXPECT_CALL(mouse_observer_, OnCursorShapeChangedPtr(_))
.Times(AnyNumber());
EXPECT_CALL(callback_, CreateSharedMemory(_))
.Times(AnyNumber())
@ -108,6 +112,8 @@ TEST_F(ScreenCapturerTest, UseSharedBuffers) {
DesktopFrame* frame = NULL;
EXPECT_CALL(callback_, OnCaptureCompleted(_))
.WillOnce(SaveArg<0>(&frame));
EXPECT_CALL(mouse_observer_, OnCursorShapeChangedPtr(_))
.Times(AnyNumber());
EXPECT_CALL(callback_, CreateSharedMemory(_))
.Times(AnyNumber())

View File

@ -21,6 +21,7 @@
#include "webrtc/modules/desktop_capture/desktop_capture_options.h"
#include "webrtc/modules/desktop_capture/desktop_frame.h"
#include "webrtc/modules/desktop_capture/differ.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/x11/x_server_pixel_buffer.h"
@ -54,6 +55,8 @@ class ScreenCapturerLinux : public ScreenCapturer,
virtual void Capture(const DesktopRegion& region) OVERRIDE;
// ScreenCapturer interface.
virtual void SetMouseShapeObserver(
MouseShapeObserver* mouse_shape_observer) OVERRIDE;
virtual bool GetScreenList(ScreenList* screens) OVERRIDE;
virtual bool SelectScreen(ScreenId id) OVERRIDE;
@ -65,6 +68,9 @@ class ScreenCapturerLinux : public ScreenCapturer,
void InitXDamage();
// Capture the cursor image and notify the delegate if it was captured.
void CaptureCursor();
// Capture screen pixels to the current buffer in the queue. In the DAMAGE
// case, the ScreenCapturerHelper already holds the list of invalid rectangles
// from HandleXEvent(). In the non-DAMAGE case, this captures the
@ -87,6 +93,7 @@ class ScreenCapturerLinux : public ScreenCapturer,
DesktopCaptureOptions options_;
Callback* callback_;
MouseShapeObserver* mouse_shape_observer_;
// X11 graphics context.
GC gc_;
@ -126,6 +133,7 @@ class ScreenCapturerLinux : public ScreenCapturer,
ScreenCapturerLinux::ScreenCapturerLinux()
: callback_(NULL),
mouse_shape_observer_(NULL),
gc_(NULL),
root_window_(BadValue),
has_xfixes_(false),
@ -145,6 +153,10 @@ ScreenCapturerLinux::~ScreenCapturerLinux() {
options_.x_display()->RemoveEventHandler(
damage_event_base_ + XDamageNotify, this);
}
if (has_xfixes_) {
options_.x_display()->RemoveEventHandler(
xfixes_event_base_ + XFixesCursorNotify, this);
}
DeinitXlib();
}
@ -184,6 +196,14 @@ bool ScreenCapturerLinux::Init(const DesktopCaptureOptions& options) {
return false;
}
if (has_xfixes_) {
// Register for changes to the cursor shape.
XFixesSelectCursorInput(display(), root_window_,
XFixesDisplayCursorNotifyMask);
options_.x_display()->AddEventHandler(
xfixes_event_base_ + XFixesCursorNotify, this);
}
if (options_.use_update_notifications()) {
InitXDamage();
}
@ -284,6 +304,14 @@ void ScreenCapturerLinux::Capture(const DesktopRegion& region) {
callback_->OnCaptureCompleted(result);
}
void ScreenCapturerLinux::SetMouseShapeObserver(
MouseShapeObserver* mouse_shape_observer) {
DCHECK(!mouse_shape_observer_);
DCHECK(mouse_shape_observer);
mouse_shape_observer_ = mouse_shape_observer;
}
bool ScreenCapturerLinux::GetScreenList(ScreenList* screens) {
DCHECK(screens->size() == 0);
// TODO(jiayl): implement screen enumeration.
@ -309,10 +337,50 @@ bool ScreenCapturerLinux::HandleXEvent(const XEvent& event) {
} else if (event.type == ConfigureNotify) {
ScreenConfigurationChanged();
return true;
} else if (has_xfixes_ &&
event.type == xfixes_event_base_ + XFixesCursorNotify) {
const XFixesCursorNotifyEvent* cursor_event =
reinterpret_cast<const XFixesCursorNotifyEvent*>(&event);
if (cursor_event->window == root_window_ &&
cursor_event->subtype == XFixesDisplayCursorNotify) {
CaptureCursor();
}
// Always return false for cursor notifications, because there might be
// other listeners for these for the same window.
return false;
}
return false;
}
void ScreenCapturerLinux::CaptureCursor() {
DCHECK(has_xfixes_);
XFixesCursorImage* img = XFixesGetCursorImage(display());
if (!img) {
return;
}
scoped_ptr<MouseCursorShape> cursor(new MouseCursorShape());
cursor->size = DesktopSize(img->width, img->height);
cursor->hotspot = DesktopVector(img->xhot, img->yhot);
int total_bytes = cursor->size.width ()* cursor->size.height() *
DesktopFrame::kBytesPerPixel;
cursor->data.resize(total_bytes);
// Xlib stores 32-bit data in longs, even if longs are 64-bits long.
unsigned long* src = img->pixels;
uint32_t* dst = reinterpret_cast<uint32_t*>(&*(cursor->data.begin()));
uint32_t* dst_end = dst + (img->width * img->height);
while (dst < dst_end) {
*dst++ = static_cast<uint32_t>(*src++);
}
XFree(img);
if (mouse_shape_observer_)
mouse_shape_observer_->OnCursorShapeChanged(cursor.release());
}
DesktopFrame* ScreenCapturerLinux::CaptureScreen() {
DesktopFrame* frame = queue_.current_frame()->Share();
assert(x_server_pixel_buffer_.window_size().equals(frame->size()));

View File

@ -38,6 +38,7 @@ const wchar_t kDwmapiLibraryName[] = L"dwmapi.dll";
ScreenCapturerWinGdi::ScreenCapturerWinGdi(const DesktopCaptureOptions& options)
: callback_(NULL),
mouse_shape_observer_(NULL),
current_screen_id_(kFullDesktopScreenId),
desktop_dc_(NULL),
memory_dc_(NULL),
@ -130,6 +131,18 @@ void ScreenCapturerWinGdi::Capture(const DesktopRegion& region) {
frame->set_capture_time_ms(
(TickTime::Now() - capture_start_time).Milliseconds());
callback_->OnCaptureCompleted(frame);
// Check for cursor shape update.
if (mouse_shape_observer_)
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) {
@ -266,4 +279,48 @@ bool ScreenCapturerWinGdi::CaptureImage() {
return true;
}
void ScreenCapturerWinGdi::CaptureCursor() {
assert(mouse_shape_observer_);
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;
mouse_shape_observer_->OnCursorShapeChanged(cursor.release());
}
} // namespace webrtc

View File

@ -15,6 +15,7 @@
#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"
@ -23,6 +24,7 @@
namespace webrtc {
class Differ;
class MouseShapeObserver;
// ScreenCapturerWinGdi captures 32bit RGB using GDI.
//
@ -35,6 +37,8 @@ class ScreenCapturerWinGdi : public ScreenCapturer {
// 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;
@ -52,6 +56,7 @@ class ScreenCapturerWinGdi : public ScreenCapturer {
void CaptureCursor();
Callback* callback_;
MouseShapeObserver* mouse_shape_observer_;
ScreenId current_screen_id_;
std::wstring current_device_key_;
@ -59,6 +64,11 @@ class ScreenCapturerWinGdi : public ScreenCapturer {
// 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.

View File

@ -166,6 +166,11 @@ void ScreenCapturerWinMagnifier::Capture(const DesktopRegion& region) {
callback_->OnCaptureCompleted(frame);
}
void ScreenCapturerWinMagnifier::SetMouseShapeObserver(
MouseShapeObserver* mouse_shape_observer) {
assert(false); // NOTREACHED();
}
bool ScreenCapturerWinMagnifier::GetScreenList(ScreenList* screens) {
return webrtc::GetScreenList(screens);
}

View File

@ -28,6 +28,7 @@ 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
@ -45,6 +46,8 @@ class ScreenCapturerWinMagnifier : public ScreenCapturer {
// 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;