blob: 6268fc011564169c2b300800a947735612a9989a [file] [log] [blame]
sergeyu@chromium.orge032f9f2013-05-19 07:02:48 +00001/*
2 * Copyright (c) 2013 The WebRTC project authors. All Rights Reserved.
3 *
4 * Use of this source code is governed by a BSD-style license
5 * that can be found in the LICENSE file in the root of the source
6 * tree. An additional intellectual property rights grant can be found
7 * in the file PATENTS. All contributing project authors may
8 * be found in the AUTHORS file in the root of the source tree.
9 */
10
11#include "webrtc/modules/desktop_capture/window_capturer.h"
12
pbos@webrtc.org3f45c2e2013-08-05 16:22:53 +000013#include <assert.h>
sergeyu@chromium.orgbb893902013-08-23 00:39:46 +000014#include <ApplicationServices/ApplicationServices.h>
15#include <CoreFoundation/CoreFoundation.h>
sergeyu@chromium.orge032f9f2013-05-19 07:02:48 +000016
17#include "webrtc/modules/desktop_capture/desktop_frame.h"
sergeyu@chromium.orgbb893902013-08-23 00:39:46 +000018#include "webrtc/system_wrappers/interface/logging.h"
sergeyu@chromium.orge032f9f2013-05-19 07:02:48 +000019
20namespace webrtc {
21
22namespace {
23
sergeyu@chromium.orgbb893902013-08-23 00:39:46 +000024bool CFStringRefToUtf8(const CFStringRef string, std::string* str_utf8) {
25 assert(string);
26 assert(str_utf8);
27 CFIndex length = CFStringGetLength(string);
28 size_t max_length_utf8 =
29 CFStringGetMaximumSizeForEncoding(length, kCFStringEncodingUTF8);
30 str_utf8->resize(max_length_utf8);
31 CFIndex used_bytes;
32 int result = CFStringGetBytes(
33 string, CFRangeMake(0, length), kCFStringEncodingUTF8, 0, false,
34 reinterpret_cast<UInt8*>(&*str_utf8->begin()), max_length_utf8,
35 &used_bytes);
36 if (result != length) {
37 str_utf8->clear();
38 return false;
39 }
40 str_utf8->resize(used_bytes);
41 return true;
42}
43
sergeyu@chromium.orge032f9f2013-05-19 07:02:48 +000044class WindowCapturerMac : public WindowCapturer {
45 public:
46 WindowCapturerMac();
47 virtual ~WindowCapturerMac();
48
49 // WindowCapturer interface.
50 virtual bool GetWindowList(WindowList* windows) OVERRIDE;
51 virtual bool SelectWindow(WindowId id) OVERRIDE;
52
53 // DesktopCapturer interface.
54 virtual void Start(Callback* callback) OVERRIDE;
55 virtual void Capture(const DesktopRegion& region) OVERRIDE;
56
57 private:
58 Callback* callback_;
sergeyu@chromium.orgbb893902013-08-23 00:39:46 +000059 CGWindowID window_id_;
sergeyu@chromium.orge032f9f2013-05-19 07:02:48 +000060
61 DISALLOW_COPY_AND_ASSIGN(WindowCapturerMac);
62};
63
64WindowCapturerMac::WindowCapturerMac()
sergeyu@chromium.orgbb893902013-08-23 00:39:46 +000065 : callback_(NULL),
66 window_id_(0) {
sergeyu@chromium.orge032f9f2013-05-19 07:02:48 +000067}
68
69WindowCapturerMac::~WindowCapturerMac() {
70}
71
72bool WindowCapturerMac::GetWindowList(WindowList* windows) {
sergeyu@chromium.orgbb893902013-08-23 00:39:46 +000073 // Only get on screen, non-desktop windows.
74 CFArrayRef window_array = CGWindowListCopyWindowInfo(
75 kCGWindowListOptionOnScreenOnly | kCGWindowListExcludeDesktopElements,
76 kCGNullWindowID);
77 if (!window_array)
78 return false;
79
80 // Check windows to make sure they have an id, title, and use window layer
81 // other than 0.
82 CFIndex count = CFArrayGetCount(window_array);
83 for (CFIndex i = 0; i < count; ++i) {
84 CFDictionaryRef window = reinterpret_cast<CFDictionaryRef>(
85 CFArrayGetValueAtIndex(window_array, i));
86 CFStringRef window_title = reinterpret_cast<CFStringRef>(
87 CFDictionaryGetValue(window, kCGWindowName));
88 CFNumberRef window_id = reinterpret_cast<CFNumberRef>(
89 CFDictionaryGetValue(window, kCGWindowNumber));
90 CFNumberRef window_layer = reinterpret_cast<CFNumberRef>(
91 CFDictionaryGetValue(window, kCGWindowLayer));
92 if (window_title && window_id && window_layer) {
93 // Skip windows with layer=0 (menu, dock).
94 int layer;
95 CFNumberGetValue(window_layer, kCFNumberIntType, &layer);
96 if (layer != 0)
97 continue;
98
99 int id;
100 CFNumberGetValue(window_id, kCFNumberIntType, &id);
101 WindowCapturer::Window window;
102 window.id = id;
103 if (!CFStringRefToUtf8(window_title, &(window.title)) ||
104 window.title.empty()) {
105 continue;
106 }
107 windows->push_back(window);
108 }
109 }
110
111 CFRelease(window_array);
112 return true;
sergeyu@chromium.orge032f9f2013-05-19 07:02:48 +0000113}
114
115bool WindowCapturerMac::SelectWindow(WindowId id) {
sergeyu@chromium.orgbb893902013-08-23 00:39:46 +0000116 // Request description for the specified window to make sure |id| is valid.
117 CGWindowID ids[1];
118 ids[0] = id;
119 CFArrayRef window_id_array =
120 CFArrayCreate(NULL, reinterpret_cast<const void **>(&ids), 1, NULL);
121 CFArrayRef window_array =
122 CGWindowListCreateDescriptionFromArray(window_id_array);
123 int results_count = window_array ? CFArrayGetCount(window_array) : 0;
124 CFRelease(window_id_array);
125 CFRelease(window_array);
126
127 if (results_count == 0) {
128 // Could not find the window. It might have been closed.
129 return false;
130 }
131
132 window_id_ = id;
133 return true;
sergeyu@chromium.orge032f9f2013-05-19 07:02:48 +0000134}
135
136void WindowCapturerMac::Start(Callback* callback) {
137 assert(!callback_);
138 assert(callback);
139
140 callback_ = callback;
141}
142
143void WindowCapturerMac::Capture(const DesktopRegion& region) {
sergeyu@chromium.orgbb893902013-08-23 00:39:46 +0000144 CGImageRef window_image = CGWindowListCreateImage(
145 CGRectNull, kCGWindowListOptionIncludingWindow,
146 window_id_, kCGWindowImageBoundsIgnoreFraming);
147
148 if (!window_image) {
149 CFRelease(window_image);
150 callback_->OnCaptureCompleted(NULL);
151 return;
152 }
153
154 int bits_per_pixel = CGImageGetBitsPerPixel(window_image);
155 if (bits_per_pixel != 32) {
156 LOG(LS_ERROR) << "Unsupported window image depth: " << bits_per_pixel;
157 CFRelease(window_image);
158 callback_->OnCaptureCompleted(NULL);
159 return;
160 }
161
162 int width = CGImageGetWidth(window_image);
163 int height = CGImageGetHeight(window_image);
164 CGDataProviderRef provider = CGImageGetDataProvider(window_image);
sergeyu@chromium.org2873c4c2013-10-17 19:47:18 +0000165 CFDataRef cf_data = CGDataProviderCopyData(provider);
166 DesktopFrame* frame = new BasicDesktopFrame(
167 DesktopSize(width, height));
168
169 int src_stride = CGImageGetBytesPerRow(window_image);
170 const uint8_t* src_data = CFDataGetBytePtr(cf_data);
171 for (int y = 0; y < height; ++y) {
172 memcpy(frame->data() + frame->stride() * y, src_data + src_stride * y,
173 DesktopFrame::kBytesPerPixel * width);
174 }
175
176 CFRelease(cf_data);
sergeyu@chromium.orgbb893902013-08-23 00:39:46 +0000177 CFRelease(window_image);
178
179 callback_->OnCaptureCompleted(frame);
sergeyu@chromium.orge032f9f2013-05-19 07:02:48 +0000180}
181
182} // namespace
183
184// static
sergeyu@chromium.org91685dc2013-10-12 22:40:05 +0000185WindowCapturer* WindowCapturer::Create(const DesktopCaptureOptions& options) {
sergeyu@chromium.orge032f9f2013-05-19 07:02:48 +0000186 return new WindowCapturerMac();
187}
188
189} // namespace webrtc