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
This commit is contained in:
sergeyu@chromium.org 2013-10-17 19:47:18 +00:00
parent 6b426ba5c5
commit 2df89c0c8b
12 changed files with 379 additions and 91 deletions

View File

@ -10,6 +10,8 @@
#include "webrtc/modules/desktop_capture/desktop_frame.h"
#include <string.h>
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,

View File

@ -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);
};

View File

@ -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

View File

@ -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<DesktopFrame> image_;

View File

@ -10,19 +10,209 @@
#include "webrtc/modules/desktop_capture/mouse_cursor_monitor.h"
#include <cstddef>
#include <assert.h>
#include <ApplicationServices/ApplicationServices.h>
#include <Cocoa/Cocoa.h>
#include <CoreFoundation/CoreFoundation.h>
#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<MouseCursor> 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<CFDictionaryRef>(
CFArrayGetValueAtIndex(window_array, i));
// Skip the Dock window. Dock window covers the whole screen, but it is
// transparent.
CFStringRef window_name = reinterpret_cast<CFStringRef>(
CFDictionaryGetValue(window, kCGWindowName));
if (window_name && CFStringCompare(window_name, CFSTR("Dock"), 0) == 0)
continue;
CFDictionaryRef window_bounds = reinterpret_cast<CFDictionaryRef>(
CFDictionaryGetValue(window, kCGWindowBounds));
CFNumberRef window_number = reinterpret_cast<CFNumberRef>(
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<int>(nshotspot.x))),
std::min(0, std::max(size.height(), static_cast<int>(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<size_t>(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<const uint8_t*>(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<DesktopFrame> 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<MouseCursor> 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

View File

@ -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

View File

@ -10,19 +10,103 @@
#include "webrtc/modules/desktop_capture/mouse_cursor_monitor.h"
#include <cstddef>
#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<MouseCursor> 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<HWND>(window));
}
MouseCursorMonitor* MouseCursorMonitor::CreateForScreen(
const DesktopCaptureOptions& options) {
return NULL;
return new MouseCursorMonitorWin(NULL);
}
} // namespace webrtc

View File

@ -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<MouseCursorShape> cursor(
CreateMouseCursorShapeFromCursor(desktop_dc_, cursor_info.hCursor));
if (!cursor.get())
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();
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) &&

View File

@ -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<const RGBQUAD*>(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<uint32_t> color_data;
uint32_t* color_plane = NULL;
int color_stride = 0;
scoped_ptr<DesktopFrame> 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<BITMAPINFO*>(&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<uint32_t*>(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<uint32_t*>(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<uint32_t*>(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<uint32_t*>(image->data()), width, height);
scoped_ptr<MouseCursorShape> result(new MouseCursorShape());
result->data.assign(reinterpret_cast<char*>(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

View File

@ -13,13 +13,12 @@
#include <windows.h>
#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

View File

@ -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<MouseCursorShape> mouse_shape(
CreateMouseCursorShapeFromCursor(dc, cursor));
scoped_ptr<MouseCursor> 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));

View File

@ -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<uint8_t*>(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);