Implement window capturer for OS X.
R=wez@chromium.org Review URL: https://webrtc-codereview.appspot.com/2055005 git-svn-id: http://webrtc.googlecode.com/svn/trunk@4599 4adac7df-926f-26a2-2b94-8c16560cd09d
This commit is contained in:
parent
d26f791273
commit
eef29ec6cf
@ -11,13 +11,59 @@
|
||||
#include "webrtc/modules/desktop_capture/window_capturer.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <ApplicationServices/ApplicationServices.h>
|
||||
#include <CoreFoundation/CoreFoundation.h>
|
||||
|
||||
#include "webrtc/modules/desktop_capture/desktop_frame.h"
|
||||
#include "webrtc/system_wrappers/interface/logging.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
namespace {
|
||||
|
||||
bool CFStringRefToUtf8(const CFStringRef string, std::string* str_utf8) {
|
||||
assert(string);
|
||||
assert(str_utf8);
|
||||
CFIndex length = CFStringGetLength(string);
|
||||
size_t max_length_utf8 =
|
||||
CFStringGetMaximumSizeForEncoding(length, kCFStringEncodingUTF8);
|
||||
str_utf8->resize(max_length_utf8);
|
||||
CFIndex used_bytes;
|
||||
int result = CFStringGetBytes(
|
||||
string, CFRangeMake(0, length), kCFStringEncodingUTF8, 0, false,
|
||||
reinterpret_cast<UInt8*>(&*str_utf8->begin()), max_length_utf8,
|
||||
&used_bytes);
|
||||
if (result != length) {
|
||||
str_utf8->clear();
|
||||
return false;
|
||||
}
|
||||
str_utf8->resize(used_bytes);
|
||||
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();
|
||||
@ -33,25 +79,81 @@ class WindowCapturerMac : public WindowCapturer {
|
||||
|
||||
private:
|
||||
Callback* callback_;
|
||||
CGWindowID window_id_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(WindowCapturerMac);
|
||||
};
|
||||
|
||||
WindowCapturerMac::WindowCapturerMac()
|
||||
: callback_(NULL) {
|
||||
: callback_(NULL),
|
||||
window_id_(0) {
|
||||
}
|
||||
|
||||
WindowCapturerMac::~WindowCapturerMac() {
|
||||
}
|
||||
|
||||
bool WindowCapturerMac::GetWindowList(WindowList* windows) {
|
||||
// Not implemented yet.
|
||||
return false;
|
||||
// Only get on screen, non-desktop windows.
|
||||
CFArrayRef window_array = CGWindowListCopyWindowInfo(
|
||||
kCGWindowListOptionOnScreenOnly | kCGWindowListExcludeDesktopElements,
|
||||
kCGNullWindowID);
|
||||
if (!window_array)
|
||||
return false;
|
||||
|
||||
// Check windows to make sure they have an id, title, and use window layer
|
||||
// other than 0.
|
||||
CFIndex count = CFArrayGetCount(window_array);
|
||||
for (CFIndex i = 0; i < count; ++i) {
|
||||
CFDictionaryRef window = reinterpret_cast<CFDictionaryRef>(
|
||||
CFArrayGetValueAtIndex(window_array, i));
|
||||
CFStringRef window_title = reinterpret_cast<CFStringRef>(
|
||||
CFDictionaryGetValue(window, kCGWindowName));
|
||||
CFNumberRef window_id = reinterpret_cast<CFNumberRef>(
|
||||
CFDictionaryGetValue(window, kCGWindowNumber));
|
||||
CFNumberRef window_layer = reinterpret_cast<CFNumberRef>(
|
||||
CFDictionaryGetValue(window, kCGWindowLayer));
|
||||
if (window_title && window_id && window_layer) {
|
||||
// Skip windows with layer=0 (menu, dock).
|
||||
int layer;
|
||||
CFNumberGetValue(window_layer, kCFNumberIntType, &layer);
|
||||
if (layer != 0)
|
||||
continue;
|
||||
|
||||
int id;
|
||||
CFNumberGetValue(window_id, kCFNumberIntType, &id);
|
||||
WindowCapturer::Window window;
|
||||
window.id = id;
|
||||
if (!CFStringRefToUtf8(window_title, &(window.title)) ||
|
||||
window.title.empty()) {
|
||||
continue;
|
||||
}
|
||||
windows->push_back(window);
|
||||
}
|
||||
}
|
||||
|
||||
CFRelease(window_array);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool WindowCapturerMac::SelectWindow(WindowId id) {
|
||||
// Not implemented yet.
|
||||
return false;
|
||||
// Request description for the specified window to make sure |id| is valid.
|
||||
CGWindowID ids[1];
|
||||
ids[0] = id;
|
||||
CFArrayRef window_id_array =
|
||||
CFArrayCreate(NULL, reinterpret_cast<const void **>(&ids), 1, NULL);
|
||||
CFArrayRef window_array =
|
||||
CGWindowListCreateDescriptionFromArray(window_id_array);
|
||||
int results_count = window_array ? CFArrayGetCount(window_array) : 0;
|
||||
CFRelease(window_id_array);
|
||||
CFRelease(window_array);
|
||||
|
||||
if (results_count == 0) {
|
||||
// Could not find the window. It might have been closed.
|
||||
return false;
|
||||
}
|
||||
|
||||
window_id_ = id;
|
||||
return true;
|
||||
}
|
||||
|
||||
void WindowCapturerMac::Start(Callback* callback) {
|
||||
@ -62,8 +164,33 @@ void WindowCapturerMac::Start(Callback* callback) {
|
||||
}
|
||||
|
||||
void WindowCapturerMac::Capture(const DesktopRegion& region) {
|
||||
// Not implemented yet.
|
||||
callback_->OnCaptureCompleted(NULL);
|
||||
CGImageRef window_image = CGWindowListCreateImage(
|
||||
CGRectNull, kCGWindowListOptionIncludingWindow,
|
||||
window_id_, kCGWindowImageBoundsIgnoreFraming);
|
||||
|
||||
if (!window_image) {
|
||||
CFRelease(window_image);
|
||||
callback_->OnCaptureCompleted(NULL);
|
||||
return;
|
||||
}
|
||||
|
||||
int bits_per_pixel = CGImageGetBitsPerPixel(window_image);
|
||||
if (bits_per_pixel != 32) {
|
||||
LOG(LS_ERROR) << "Unsupported window image depth: " << bits_per_pixel;
|
||||
CFRelease(window_image);
|
||||
callback_->OnCaptureCompleted(NULL);
|
||||
return;
|
||||
}
|
||||
|
||||
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));
|
||||
CFRelease(window_image);
|
||||
|
||||
callback_->OnCaptureCompleted(frame);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
@ -42,16 +42,13 @@ class WindowCapturerTest : public testing::Test,
|
||||
scoped_ptr<DesktopFrame> frame_;
|
||||
};
|
||||
|
||||
#if defined(WEBRTC_WIN)
|
||||
#if defined(WEBRTC_WIN) || defined(WEBRTC_MAC)
|
||||
|
||||
// Verify that we can enumerate windows.
|
||||
TEST_F(WindowCapturerTest, Enumerate) {
|
||||
WindowCapturer::WindowList windows;
|
||||
EXPECT_TRUE(capturer_->GetWindowList(&windows));
|
||||
|
||||
// Assume that there is at least one window.
|
||||
EXPECT_GT(windows.size(), 0U);
|
||||
|
||||
// Verify that window titles are set.
|
||||
for (WindowCapturer::WindowList::iterator it = windows.begin();
|
||||
it != windows.end(); ++it) {
|
||||
@ -95,6 +92,6 @@ TEST_F(WindowCapturerTest, Capture) {
|
||||
}
|
||||
}
|
||||
|
||||
#endif // defined(WEBRTC_WIN)
|
||||
#endif // defined(WEBRTC_WIN) || defined(WEBRTC_MAC)
|
||||
|
||||
} // namespace webrtc
|
||||
|
Loading…
x
Reference in New Issue
Block a user