From 2df89c0c8b6416c69d70da5f29615733986a0716 Mon Sep 17 00:00:00 2001 From: "sergeyu@chromium.org" Date: Thu, 17 Oct 2013 19:47:18 +0000 Subject: [PATCH] MouseCursorMonitor implementation for OSX and Windows. BUG=crbug.com/173265 R=wez@chromium.org Review URL: https://webrtc-codereview.appspot.com/2388004 git-svn-id: http://webrtc.googlecode.com/svn/trunk@4994 4adac7df-926f-26a2-2b94-8c16560cd09d --- .../modules/desktop_capture/desktop_frame.cc | 16 ++ .../modules/desktop_capture/desktop_frame.h | 3 + .../modules/desktop_capture/mouse_cursor.cc | 6 + webrtc/modules/desktop_capture/mouse_cursor.h | 6 +- .../mouse_cursor_monitor_mac.mm | 198 +++++++++++++++++- .../mouse_cursor_monitor_unittest.cc | 7 +- .../mouse_cursor_monitor_win.cc | 92 +++++++- .../desktop_capture/screen_capturer_win.cc | 15 +- webrtc/modules/desktop_capture/win/cursor.cc | 62 +++--- webrtc/modules/desktop_capture/win/cursor.h | 9 +- .../desktop_capture/win/cursor_unittest.cc | 18 +- .../desktop_capture/window_capturer_mac.cc | 38 ++-- 12 files changed, 379 insertions(+), 91 deletions(-) diff --git a/webrtc/modules/desktop_capture/desktop_frame.cc b/webrtc/modules/desktop_capture/desktop_frame.cc index 90e1fbd3e..f293baff0 100644 --- a/webrtc/modules/desktop_capture/desktop_frame.cc +++ b/webrtc/modules/desktop_capture/desktop_frame.cc @@ -10,6 +10,8 @@ #include "webrtc/modules/desktop_capture/desktop_frame.h" +#include + namespace webrtc { DesktopFrame::DesktopFrame(DesktopSize size, @@ -35,6 +37,20 @@ BasicDesktopFrame::~BasicDesktopFrame() { delete[] data_; } +DesktopFrame* BasicDesktopFrame::CopyOf(const DesktopFrame& frame) { + DesktopFrame* result = new BasicDesktopFrame(frame.size()); + for (int y = 0; y < frame.size().height(); ++y) { + memcpy(result->data() + y * result->stride(), + frame.data() + y * frame.stride(), + frame.size().width() * kBytesPerPixel); + } + result->set_dpi(frame.dpi()); + result->set_capture_time_ms(frame.capture_time_ms()); + *result->mutable_updated_region() = frame.updated_region(); + return result; +} + + SharedMemoryDesktopFrame::SharedMemoryDesktopFrame( DesktopSize size, int stride, diff --git a/webrtc/modules/desktop_capture/desktop_frame.h b/webrtc/modules/desktop_capture/desktop_frame.h index a39eff745..b420a3c56 100644 --- a/webrtc/modules/desktop_capture/desktop_frame.h +++ b/webrtc/modules/desktop_capture/desktop_frame.h @@ -83,6 +83,9 @@ class BasicDesktopFrame : public DesktopFrame { explicit BasicDesktopFrame(DesktopSize size); virtual ~BasicDesktopFrame(); + // Creates a BasicDesktopFrame that contains copy of |frame|. + static DesktopFrame* CopyOf(const DesktopFrame& frame); + private: DISALLOW_COPY_AND_ASSIGN(BasicDesktopFrame); }; diff --git a/webrtc/modules/desktop_capture/mouse_cursor.cc b/webrtc/modules/desktop_capture/mouse_cursor.cc index 67eb4bf78..3f1ab3ddf 100644 --- a/webrtc/modules/desktop_capture/mouse_cursor.cc +++ b/webrtc/modules/desktop_capture/mouse_cursor.cc @@ -23,4 +23,10 @@ MouseCursor::MouseCursor(DesktopFrame* image, const DesktopVector& hotspot) MouseCursor::~MouseCursor() {} +// static +MouseCursor* MouseCursor::CopyOf(const MouseCursor& cursor) { + return new MouseCursor(BasicDesktopFrame::CopyOf(cursor.image()), + cursor.hotspot()); +} + } // namespace webrtc diff --git a/webrtc/modules/desktop_capture/mouse_cursor.h b/webrtc/modules/desktop_capture/mouse_cursor.h index f37eeb332..4cf770830 100644 --- a/webrtc/modules/desktop_capture/mouse_cursor.h +++ b/webrtc/modules/desktop_capture/mouse_cursor.h @@ -25,8 +25,10 @@ class MouseCursor { MouseCursor(DesktopFrame* image, const DesktopVector& hotspot); ~MouseCursor(); - const DesktopFrame& image() { return *image_; } - const DesktopVector& hotspot() { return hotspot_; } + static MouseCursor* CopyOf(const MouseCursor& cursor); + + const DesktopFrame& image() const { return *image_; } + const DesktopVector& hotspot() const { return hotspot_; } private: scoped_ptr image_; diff --git a/webrtc/modules/desktop_capture/mouse_cursor_monitor_mac.mm b/webrtc/modules/desktop_capture/mouse_cursor_monitor_mac.mm index f742dfaa4..6f9380af3 100644 --- a/webrtc/modules/desktop_capture/mouse_cursor_monitor_mac.mm +++ b/webrtc/modules/desktop_capture/mouse_cursor_monitor_mac.mm @@ -10,19 +10,209 @@ #include "webrtc/modules/desktop_capture/mouse_cursor_monitor.h" -#include +#include +#include +#include +#include + +#include "webrtc/modules/desktop_capture/desktop_frame.h" +#include "webrtc/modules/desktop_capture/mouse_cursor.h" +#include "webrtc/system_wrappers/interface/scoped_ptr.h" namespace webrtc { -// TODO(sergeyu): Implement MouseCursorMonitor for Mac. +class MouseCursorMonitorMac : public MouseCursorMonitor { + public: + MouseCursorMonitorMac(CGWindowID window_id); + virtual ~MouseCursorMonitorMac(); + + virtual void Init(Callback* callback, Mode mode) OVERRIDE; + virtual void Capture() OVERRIDE; + + private: + void CaptureImage(); + + CGWindowID window_id_; + + Callback* callback_; + Mode mode_; + + scoped_ptr last_cursor_; +}; + +MouseCursorMonitorMac::MouseCursorMonitorMac(CGWindowID window_id) + : window_id_(window_id), + callback_(NULL), + mode_(SHAPE_AND_POSITION) { +} + +MouseCursorMonitorMac::~MouseCursorMonitorMac() {} + +void MouseCursorMonitorMac::Init(Callback* callback, Mode mode) { + assert(!callback_); + assert(callback); + + callback_ = callback; + mode_ = mode; +} + +void MouseCursorMonitorMac::Capture() { + assert(callback_); + + CaptureImage(); + + if (mode_ != SHAPE_AND_POSITION) + return; + + CursorState state = INSIDE; + + CGEventRef event = CGEventCreate(NULL); + CGPoint gc_position = CGEventGetLocation(event); + CFRelease(event); + + DesktopVector position(gc_position.x, gc_position.y); + + // If we are capturing cursor for a specific window then we need to figure out + // if the current mouse position is covered by another window and also adjust + // |position| to make it relative to the window origin. + if (window_id_ != kCGNullWindowID) { + // Get list of windows that may be covering parts of |window_id_|. + // CGWindowListCopyWindowInfo() returns windows in order from front to back, + // so |window_id_| is expected to be the last in the list. + CFArrayRef window_array = + CGWindowListCopyWindowInfo(kCGWindowListOptionOnScreenOnly | + kCGWindowListOptionOnScreenAboveWindow | + kCGWindowListOptionIncludingWindow, + window_id_); + bool found_window = false; + if (window_array) { + CFIndex count = CFArrayGetCount(window_array); + for (CFIndex i = 0; i < count; ++i) { + CFDictionaryRef window = reinterpret_cast( + CFArrayGetValueAtIndex(window_array, i)); + + // Skip the Dock window. Dock window covers the whole screen, but it is + // transparent. + CFStringRef window_name = reinterpret_cast( + CFDictionaryGetValue(window, kCGWindowName)); + if (window_name && CFStringCompare(window_name, CFSTR("Dock"), 0) == 0) + continue; + + CFDictionaryRef window_bounds = reinterpret_cast( + CFDictionaryGetValue(window, kCGWindowBounds)); + CFNumberRef window_number = reinterpret_cast( + CFDictionaryGetValue(window, kCGWindowNumber)); + + if (window_bounds && window_number) { + CGRect gc_window_rect; + if (!CGRectMakeWithDictionaryRepresentation(window_bounds, + &gc_window_rect)) { + continue; + } + DesktopRect window_rect = + DesktopRect::MakeXYWH(gc_window_rect.origin.x, + gc_window_rect.origin.y, + gc_window_rect.size.width, + gc_window_rect.size.height); + + CGWindowID window_id; + if (!CFNumberGetValue(window_number, kCFNumberIntType, &window_id)) + continue; + + if (window_id == window_id_) { + found_window = true; + if (!window_rect.Contains(position)) + state = OUTSIDE; + position = position.subtract(window_rect.top_left()); + + assert(i == count - 1); + break; + } else if (window_rect.Contains(position)) { + state = OUTSIDE; + position.set(-1, -1); + break; + } + } + } + + CFRelease(window_array); + } + + if (!found_window) { + // If we failed to get list of windows or the window wasn't in the list + // pretend that the cursor is outside the window. This can happen, e.g. if + // the window was closed. + state = OUTSIDE; + position.set(-1, -1); + } + } + + callback_->OnMouseCursorPosition(state, position); +} + +void MouseCursorMonitorMac::CaptureImage() { + NSCursor* nscursor = [NSCursor currentSystemCursor]; + + NSImage* nsimage = [nscursor image]; + NSSize nssize = [nsimage size]; + DesktopSize size(nssize.width, nssize.height); + NSPoint nshotspot = [nscursor hotSpot]; + DesktopVector hotspot( + std::min(0, std::max(size.width(), static_cast(nshotspot.x))), + std::min(0, std::max(size.height(), static_cast(nshotspot.y)))); + CGImageRef cg_image = + [nsimage CGImageForProposedRect:NULL context:nil hints:nil]; + if (!cg_image) + return; + + if (CGImageGetBitsPerPixel(cg_image) != DesktopFrame::kBytesPerPixel * 8 || + CGImageGetBytesPerRow(cg_image) != + static_cast(DesktopFrame::kBytesPerPixel * size.width()) || + CGImageGetBitsPerComponent(cg_image) != 8) { + return; + } + + CGDataProviderRef provider = CGImageGetDataProvider(cg_image); + CFDataRef image_data_ref = CGDataProviderCopyData(provider); + if (image_data_ref == NULL) + return; + + const uint8_t* src_data = + reinterpret_cast(CFDataGetBytePtr(image_data_ref)); + + // Compare the cursor with the previous one. + if (last_cursor_.get() && + last_cursor_->image().size().equals(size) && + last_cursor_->hotspot().equals(hotspot) && + memcmp(last_cursor_->image().data(), src_data, + last_cursor_->image().stride() * size.height()) == 0) { + return; + } + + // Create a MouseCursor that describes the cursor and pass it to + // the client. + scoped_ptr image( + new BasicDesktopFrame(DesktopSize(size.width(), size.height()))); + memcpy(image->data(), src_data, + size.width() * size.height() * DesktopFrame::kBytesPerPixel); + + CFRelease(image_data_ref); + + scoped_ptr cursor(new MouseCursor(image.release(), hotspot)); + last_cursor_.reset(MouseCursor::CopyOf(*cursor)); + + callback_->OnMouseCursor(cursor.release()); +} + + MouseCursorMonitor* MouseCursorMonitor::CreateForWindow( const DesktopCaptureOptions& options, WindowId window) { - return NULL; + return new MouseCursorMonitorMac(window); } MouseCursorMonitor* MouseCursorMonitor::CreateForScreen( const DesktopCaptureOptions& options) { - return NULL; + return new MouseCursorMonitorMac(kCGNullWindowID); } } // namespace webrtc diff --git a/webrtc/modules/desktop_capture/mouse_cursor_monitor_unittest.cc b/webrtc/modules/desktop_capture/mouse_cursor_monitor_unittest.cc index bbd5be4eb..18bf1ca40 100644 --- a/webrtc/modules/desktop_capture/mouse_cursor_monitor_unittest.cc +++ b/webrtc/modules/desktop_capture/mouse_cursor_monitor_unittest.cc @@ -46,8 +46,11 @@ class MouseCursorMonitorTest : public testing::Test, bool position_received_; }; -// TODO(sergeyu): Enable tests on all platforms. -#if defined(USE_X11) +// TODO(sergeyu): On Mac we need to initialize NSApplication before running the +// tests. Figure out how to do that without breaking other tests in +// modules_unittests and enable these tests on Mac. +// https://code.google.com/p/webrtc/issues/detail?id=2532 +#if !defined(WEBRTC_MAC) #define MAYBE(x) x #else #define MAYBE(x) DISABLED_##x diff --git a/webrtc/modules/desktop_capture/mouse_cursor_monitor_win.cc b/webrtc/modules/desktop_capture/mouse_cursor_monitor_win.cc index 907129b1a..82f7d2447 100644 --- a/webrtc/modules/desktop_capture/mouse_cursor_monitor_win.cc +++ b/webrtc/modules/desktop_capture/mouse_cursor_monitor_win.cc @@ -10,19 +10,103 @@ #include "webrtc/modules/desktop_capture/mouse_cursor_monitor.h" -#include +#include "webrtc/modules/desktop_capture/desktop_frame.h" +#include "webrtc/modules/desktop_capture/mouse_cursor.h" +#include "webrtc/modules/desktop_capture/win/cursor.h" +#include "webrtc/system_wrappers/interface/logging.h" namespace webrtc { -// TODO(sergeyu): Implement MouseCursorMonitor for Windows. +class MouseCursorMonitorWin : public MouseCursorMonitor { + public: + explicit MouseCursorMonitorWin(HWND window); + virtual ~MouseCursorMonitorWin(); + + virtual void Init(Callback* callback, Mode mode) OVERRIDE; + virtual void Capture() OVERRIDE; + + private: + HWND window_; + + Callback* callback_; + Mode mode_; + + HDC desktop_dc_; + + HCURSOR last_cursor_; +}; + +MouseCursorMonitorWin::MouseCursorMonitorWin(HWND window) + : window_(window), + callback_(NULL), + mode_(SHAPE_AND_POSITION), + desktop_dc_(NULL), + last_cursor_(NULL) { +} + +MouseCursorMonitorWin::~MouseCursorMonitorWin() { + if (desktop_dc_) + ReleaseDC(NULL, desktop_dc_); +} + +void MouseCursorMonitorWin::Init(Callback* callback, Mode mode) { + assert(!callback_); + assert(callback); + + callback_ = callback; + mode_ = mode; + + desktop_dc_ = GetDC(NULL); +} + +void MouseCursorMonitorWin::Capture() { + assert(callback_); + + CURSORINFO cursor_info; + cursor_info.cbSize = sizeof(CURSORINFO); + if (!GetCursorInfo(&cursor_info)) { + LOG_F(LS_ERROR) << "Unable to get cursor info. Error = " << GetLastError(); + return; + } + + if (last_cursor_ != cursor_info.hCursor) { + last_cursor_ = cursor_info.hCursor; + // Note that |cursor_info.hCursor| does not need to be freed. + scoped_ptr cursor( + CreateMouseCursorFromHCursor(desktop_dc_, cursor_info.hCursor)); + if (cursor.get()) + callback_->OnMouseCursor(cursor.release()); + } + + if (mode_ != SHAPE_AND_POSITION) + return; + + DesktopVector position(cursor_info.ptScreenPos.x, cursor_info.ptScreenPos.y); + bool inside = cursor_info.flags == CURSOR_SHOWING; + + if (window_) { + RECT rect; + if (!GetWindowRect(window_, &rect)) { + position.set(0, 0); + inside = false; + } else { + position = position.subtract(DesktopVector(rect.left, rect.top)); + if (inside) + inside = (window_ == WindowFromPoint(cursor_info.ptScreenPos)); + } + } + + callback_->OnMouseCursorPosition(inside ? INSIDE : OUTSIDE, position); +} + MouseCursorMonitor* MouseCursorMonitor::CreateForWindow( const DesktopCaptureOptions& options, WindowId window) { - return NULL; + return new MouseCursorMonitorWin(reinterpret_cast(window)); } MouseCursorMonitor* MouseCursorMonitor::CreateForScreen( const DesktopCaptureOptions& options) { - return NULL; + return new MouseCursorMonitorWin(NULL); } } // namespace webrtc diff --git a/webrtc/modules/desktop_capture/screen_capturer_win.cc b/webrtc/modules/desktop_capture/screen_capturer_win.cc index af9b18b0a..42a71922e 100644 --- a/webrtc/modules/desktop_capture/screen_capturer_win.cc +++ b/webrtc/modules/desktop_capture/screen_capturer_win.cc @@ -17,6 +17,7 @@ #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" @@ -328,11 +329,19 @@ void ScreenCapturerWin::CaptureCursor() { } // Note that |cursor_info.hCursor| does not need to be freed. - scoped_ptr cursor( - CreateMouseCursorShapeFromCursor(desktop_dc_, cursor_info.hCursor)); - if (!cursor.get()) + scoped_ptr cursor_image( + CreateMouseCursorFromHCursor(desktop_dc_, cursor_info.hCursor)); + if (!cursor_image.get()) return; + scoped_ptr cursor(new MouseCursorShape); + cursor->hotspot = cursor_image->hotspot(); + cursor->size = cursor_image->image().size(); + cursor->data.assign( + cursor_image->image().data(), + cursor_image->image().data() + + cursor_image->image().stride() * DesktopFrame::kBytesPerPixel); + // 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) && diff --git a/webrtc/modules/desktop_capture/win/cursor.cc b/webrtc/modules/desktop_capture/win/cursor.cc index 76eed7742..6e0fd4eb4 100644 --- a/webrtc/modules/desktop_capture/win/cursor.cc +++ b/webrtc/modules/desktop_capture/win/cursor.cc @@ -15,6 +15,7 @@ #include "webrtc/modules/desktop_capture/win/scoped_gdi_object.h" #include "webrtc/modules/desktop_capture/desktop_frame.h" #include "webrtc/modules/desktop_capture/desktop_geometry.h" +#include "webrtc/modules/desktop_capture/mouse_cursor.h" #include "webrtc/system_wrappers/interface/compile_assert.h" #include "webrtc/system_wrappers/interface/logging.h" #include "webrtc/system_wrappers/interface/scoped_ptr.h" @@ -92,29 +93,24 @@ void AlphaMul(uint32_t* data, int width, int height) { } // Scans a 32bpp bitmap looking for any pixels with non-zero alpha component. -// |*has_alpha| is set to true if non-zero alpha is found. |stride| is expressed -// in pixels. -bool HasAlphaChannel(const uint32_t* data, int stride, int width, int height, - bool* has_alpha) { +// Returns true if non-zero alpha is found. |stride| is expressed in pixels. +bool HasAlphaChannel(const uint32_t* data, int stride, int width, int height) { const RGBQUAD* plane = reinterpret_cast(data); for (int y = 0; y < height; ++y) { for (int x = 0; x < width; ++x) { - if (plane->rgbReserved != 0) { - *has_alpha = true; + if (plane->rgbReserved != 0) return true; - } plane += 1; } plane += stride - width; } - *has_alpha = false; - return true; + return false; } } // namespace -MouseCursorShape* CreateMouseCursorShapeFromCursor(HDC dc, HCURSOR cursor) { +MouseCursor* CreateMouseCursorFromHCursor(HDC dc, HCURSOR cursor) { ICONINFO iinfo; if (!GetIconInfo(cursor, &iinfo)) { LOG_F(LS_ERROR) << "Unable to get cursor icon info. Error = " @@ -167,20 +163,18 @@ MouseCursorShape* CreateMouseCursorShapeFromCursor(HDC dc, HCURSOR cursor) { } uint32_t* mask_plane = mask_data.get(); - - scoped_array color_data; - uint32_t* color_plane = NULL; - int color_stride = 0; + scoped_ptr image( + new BasicDesktopFrame(DesktopSize(width, height))); bool has_alpha = false; if (is_color) { + image.reset(new BasicDesktopFrame(DesktopSize(width, height))); // Get the pixels from the color bitmap. - color_data.reset(new uint32_t[width * height]); if (!GetDIBits(dc, scoped_color, 0, height, - color_data.get(), + image->data(), reinterpret_cast(&bmi), DIB_RGB_COLORS)) { LOG_F(LS_ERROR) << "Unable to get bitmap bits. Error = " @@ -188,30 +182,28 @@ MouseCursorShape* CreateMouseCursorShapeFromCursor(HDC dc, HCURSOR cursor) { return NULL; } - color_plane = color_data.get(); - color_stride = width; - // GetDIBits() does not provide any indication whether the bitmap has alpha // channel, so we use HasAlphaChannel() below to find it out. - if (!HasAlphaChannel(color_plane, color_stride, width, height, &has_alpha)) - return NULL; + has_alpha = HasAlphaChannel(reinterpret_cast(image->data()), + width, width, height); } else { // For non-color cursors, the mask contains both an AND and an XOR mask and // the height includes both. Thus, the width is correct, but we need to // divide by 2 to get the correct mask height. height /= 2; + image.reset(new BasicDesktopFrame(DesktopSize(width, height))); + // The XOR mask becomes the color bitmap. - color_plane = mask_plane + (width * height); - color_stride = width; + memcpy( + image->data(), mask_plane + (width * height), image->stride() * width); } // Reconstruct transparency from the mask if the color image does not has // alpha channel. if (!has_alpha) { bool add_outline = false; - uint32_t* color = color_plane; - uint32_t* dst = color_plane; + uint32_t* dst = reinterpret_cast(image->data()); uint32_t* mask = mask_plane; for (int y = 0; y < height; y++) { for (int x = 0; x < width; x++) { @@ -226,36 +218,32 @@ MouseCursorShape* CreateMouseCursorShapeFromCursor(HDC dc, HCURSOR cursor) { // with black. In this case, we also add an outline around the cursor // so that it is visible against a dark background. if (*mask == kPixelRgbWhite) { - if (*color != 0) { + if (*dst != 0) { add_outline = true; *dst = kPixelRgbaBlack; } else { *dst = kPixelRgbaTransparent; } } else { - *dst = kPixelRgbaBlack ^ *color; + *dst = kPixelRgbaBlack ^ *dst; } - ++color; ++dst; ++mask; } } if (add_outline) { - AddCursorOutline(width, height, color_plane); + AddCursorOutline( + width, height, reinterpret_cast(image->data())); } } - // Pre-multiply the resulting pixels since MouseCursorShape uses premultiplied + // Pre-multiply the resulting pixels since MouseCursor uses premultiplied // images. - AlphaMul(color_plane, width, height); + AlphaMul(reinterpret_cast(image->data()), width, height); - scoped_ptr result(new MouseCursorShape()); - result->data.assign(reinterpret_cast(color_plane), - height * width * kBytesPerPixel); - result->size.set(width, height); - result->hotspot.set(hotspot_x, hotspot_y); - return result.release(); + return new MouseCursor( + image.release(), DesktopVector(hotspot_x, hotspot_y)); } } // namespace webrtc diff --git a/webrtc/modules/desktop_capture/win/cursor.h b/webrtc/modules/desktop_capture/win/cursor.h index 08a6c4a0e..d521cc081 100644 --- a/webrtc/modules/desktop_capture/win/cursor.h +++ b/webrtc/modules/desktop_capture/win/cursor.h @@ -13,13 +13,12 @@ #include -#include "webrtc/modules/desktop_capture/mouse_cursor_shape.h" - namespace webrtc { -// Converts a cursor into a |MouseCursorShape| instance. -MouseCursorShape* CreateMouseCursorShapeFromCursor( - HDC dc, HCURSOR cursor); +class MouseCursor; + +// Converts an HCURSOR into a |MouseCursor| instance. +MouseCursor* CreateMouseCursorFromHCursor(HDC dc, HCURSOR cursor); } // namespace webrtc diff --git a/webrtc/modules/desktop_capture/win/cursor_unittest.cc b/webrtc/modules/desktop_capture/win/cursor_unittest.cc index c1c741736..f590bd255 100644 --- a/webrtc/modules/desktop_capture/win/cursor_unittest.cc +++ b/webrtc/modules/desktop_capture/win/cursor_unittest.cc @@ -9,7 +9,9 @@ */ #include "testing/gmock/include/gmock/gmock.h" +#include "webrtc/modules/desktop_capture/desktop_frame.h" #include "webrtc/modules/desktop_capture/desktop_geometry.h" +#include "webrtc/modules/desktop_capture/mouse_cursor.h" #include "webrtc/modules/desktop_capture/win/cursor.h" #include "webrtc/modules/desktop_capture/win/cursor_unittest_resources.h" #include "webrtc/modules/desktop_capture/win/scoped_gdi_object.h" @@ -19,9 +21,9 @@ namespace webrtc { namespace { -// Loads |left| from resources, converts it to a |MouseCursorShape| instance -// and compares pixels with |right|. Returns true of MouseCursorShape bits -// match |right|. |right| must be a 32bpp cursor with alpha channel. +// Loads |left| from resources, converts it to a |MouseCursor| instance and +// compares pixels with |right|. Returns true of MouseCursor bits match |right|. +// |right| must be a 32bpp cursor with alpha channel. bool ConvertToMouseShapeAndCompare(unsigned left, unsigned right) { HMODULE instance = GetModuleHandle(NULL); @@ -32,8 +34,8 @@ bool ConvertToMouseShapeAndCompare(unsigned left, unsigned right) { // Convert |cursor| to |mouse_shape|. HDC dc = GetDC(NULL); - scoped_ptr mouse_shape( - CreateMouseCursorShapeFromCursor(dc, cursor)); + scoped_ptr mouse_shape( + CreateMouseCursorFromHCursor(dc, cursor)); ReleaseDC(NULL, dc); EXPECT_TRUE(mouse_shape.get()); @@ -56,7 +58,7 @@ bool ConvertToMouseShapeAndCompare(unsigned left, unsigned right) { int width = bitmap_info.bmWidth; int height = bitmap_info.bmHeight; - EXPECT_TRUE(DesktopSize(width, height).equals(mouse_shape->size)); + EXPECT_TRUE(DesktopSize(width, height).equals(mouse_shape->image().size())); // Get the pixels from |scoped_color|. int size = width * height; @@ -64,13 +66,13 @@ bool ConvertToMouseShapeAndCompare(unsigned left, unsigned right) { EXPECT_TRUE(GetBitmapBits(scoped_color, size * sizeof(uint32_t), data.get())); // Compare the 32bpp image in |mouse_shape| with the one loaded from |right|. - return memcmp(data.get(), mouse_shape->data.data(), + return memcmp(data.get(), mouse_shape->image().data(), size * sizeof(uint32_t)) == 0; } } // namespace -TEST(MouseCursorShapeTest, MatchCursors) { +TEST(MouseCursorTest, MatchCursors) { EXPECT_TRUE(ConvertToMouseShapeAndCompare(IDD_CURSOR1_24BPP, IDD_CURSOR1_32BPP)); diff --git a/webrtc/modules/desktop_capture/window_capturer_mac.cc b/webrtc/modules/desktop_capture/window_capturer_mac.cc index 78f618d26..6268fc011 100755 --- a/webrtc/modules/desktop_capture/window_capturer_mac.cc +++ b/webrtc/modules/desktop_capture/window_capturer_mac.cc @@ -41,29 +41,6 @@ bool CFStringRefToUtf8(const CFStringRef string, std::string* str_utf8) { return true; } -// DesktopFrame that stores data in CFData. -class CFDataDesktopFrame : public DesktopFrame { - public: - // Consumes |cf_data| reference. - // - // TODO(sergeyu): Here we const_cast<> the buffer used in CFDataRef. CFDataRef - // buffer is immutable, but DesktopFrame is always mutable. This shouldn't be - // a problem because frames generated by WindowCapturers are normally not - // mutated. To avoid this hack consider making DesktopFrame immutable and add - // MutableDesktopFrame. - CFDataDesktopFrame(DesktopSize size, int stride, CFDataRef cf_data) - : DesktopFrame(size, stride, - const_cast(CFDataGetBytePtr(cf_data)), NULL), - cf_data_(cf_data) { - } - virtual ~CFDataDesktopFrame() { - CFRelease(cf_data_); - } - - private: - CFDataRef cf_data_; -}; - class WindowCapturerMac : public WindowCapturer { public: WindowCapturerMac(); @@ -185,9 +162,18 @@ void WindowCapturerMac::Capture(const DesktopRegion& region) { int width = CGImageGetWidth(window_image); int height = CGImageGetHeight(window_image); CGDataProviderRef provider = CGImageGetDataProvider(window_image); - DesktopFrame* frame = new CFDataDesktopFrame( - DesktopSize(width, height), CGImageGetBytesPerRow(window_image), - CGDataProviderCopyData(provider)); + CFDataRef cf_data = CGDataProviderCopyData(provider); + DesktopFrame* frame = new BasicDesktopFrame( + DesktopSize(width, height)); + + int src_stride = CGImageGetBytesPerRow(window_image); + const uint8_t* src_data = CFDataGetBytePtr(cf_data); + for (int y = 0; y < height; ++y) { + memcpy(frame->data() + frame->stride() * y, src_data + src_stride * y, + DesktopFrame::kBytesPerPixel * width); + } + + CFRelease(cf_data); CFRelease(window_image); callback_->OnCaptureCompleted(frame);