2 * Copyright (c) 2013 The WebRTC project authors. All Rights Reserved.
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.
11 #include "webrtc/modules/desktop_capture/window_capturer.h"
14 #include <ApplicationServices/ApplicationServices.h>
15 #include <Cocoa/Cocoa.h>
16 #include <CoreFoundation/CoreFoundation.h>
18 #include "webrtc/modules/desktop_capture/desktop_frame.h"
19 #include "webrtc/system_wrappers/interface/logging.h"
25 bool CFStringRefToUtf8(const CFStringRef string, std::string* str_utf8) {
28 CFIndex length = CFStringGetLength(string);
29 size_t max_length_utf8 =
30 CFStringGetMaximumSizeForEncoding(length, kCFStringEncodingUTF8);
31 str_utf8->resize(max_length_utf8);
33 int result = CFStringGetBytes(
34 string, CFRangeMake(0, length), kCFStringEncodingUTF8, 0, false,
35 reinterpret_cast<UInt8*>(&*str_utf8->begin()), max_length_utf8,
37 if (result != length) {
41 str_utf8->resize(used_bytes);
45 class WindowCapturerMac : public WindowCapturer {
48 virtual ~WindowCapturerMac();
50 // WindowCapturer interface.
51 virtual bool GetWindowList(WindowList* windows) OVERRIDE;
52 virtual bool SelectWindow(WindowId id) OVERRIDE;
53 virtual bool BringSelectedWindowToFront() OVERRIDE;
55 // DesktopCapturer interface.
56 virtual void Start(Callback* callback) OVERRIDE;
57 virtual void Capture(const DesktopRegion& region) OVERRIDE;
61 CGWindowID window_id_;
63 DISALLOW_COPY_AND_ASSIGN(WindowCapturerMac);
66 WindowCapturerMac::WindowCapturerMac()
71 WindowCapturerMac::~WindowCapturerMac() {
74 bool WindowCapturerMac::GetWindowList(WindowList* windows) {
75 // Only get on screen, non-desktop windows.
76 CFArrayRef window_array = CGWindowListCopyWindowInfo(
77 kCGWindowListOptionOnScreenOnly | kCGWindowListExcludeDesktopElements,
82 // Check windows to make sure they have an id, title, and use window layer
84 CFIndex count = CFArrayGetCount(window_array);
85 for (CFIndex i = 0; i < count; ++i) {
86 CFDictionaryRef window = reinterpret_cast<CFDictionaryRef>(
87 CFArrayGetValueAtIndex(window_array, i));
88 CFStringRef window_title = reinterpret_cast<CFStringRef>(
89 CFDictionaryGetValue(window, kCGWindowName));
90 CFNumberRef window_id = reinterpret_cast<CFNumberRef>(
91 CFDictionaryGetValue(window, kCGWindowNumber));
92 CFNumberRef window_layer = reinterpret_cast<CFNumberRef>(
93 CFDictionaryGetValue(window, kCGWindowLayer));
94 if (window_title && window_id && window_layer) {
95 // Skip windows with layer=0 (menu, dock).
97 CFNumberGetValue(window_layer, kCFNumberIntType, &layer);
102 CFNumberGetValue(window_id, kCFNumberIntType, &id);
103 WindowCapturer::Window window;
105 if (!CFStringRefToUtf8(window_title, &(window.title)) ||
106 window.title.empty()) {
109 windows->push_back(window);
113 CFRelease(window_array);
117 bool WindowCapturerMac::SelectWindow(WindowId id) {
118 // Request description for the specified window to make sure |id| is valid.
121 CFArrayRef window_id_array =
122 CFArrayCreate(NULL, reinterpret_cast<const void **>(&ids), 1, NULL);
123 CFArrayRef window_array =
124 CGWindowListCreateDescriptionFromArray(window_id_array);
125 int results_count = window_array ? CFArrayGetCount(window_array) : 0;
126 CFRelease(window_id_array);
127 CFRelease(window_array);
129 if (results_count == 0) {
130 // Could not find the window. It might have been closed.
138 bool WindowCapturerMac::BringSelectedWindowToFront() {
144 CFArrayRef window_id_array =
145 CFArrayCreate(NULL, reinterpret_cast<const void **>(&ids), 1, NULL);
147 CFArrayRef window_array =
148 CGWindowListCreateDescriptionFromArray(window_id_array);
149 if (window_array == NULL || 0 == CFArrayGetCount(window_array)) {
150 // Could not find the window. It might have been closed.
151 LOG(LS_INFO) << "Window not found";
152 CFRelease(window_id_array);
156 CFDictionaryRef window = reinterpret_cast<CFDictionaryRef>(
157 CFArrayGetValueAtIndex(window_array, 0));
158 CFNumberRef pid_ref = reinterpret_cast<CFNumberRef>(
159 CFDictionaryGetValue(window, kCGWindowOwnerPID));
162 CFNumberGetValue(pid_ref, kCFNumberIntType, &pid);
164 // TODO(jiayl): this will bring the process main window to the front. We
165 // should find a way to bring only the window to the front.
167 [[NSRunningApplication runningApplicationWithProcessIdentifier: pid]
168 activateWithOptions: NSApplicationActivateIgnoringOtherApps];
170 CFRelease(window_id_array);
171 CFRelease(window_array);
175 void WindowCapturerMac::Start(Callback* callback) {
179 callback_ = callback;
182 void WindowCapturerMac::Capture(const DesktopRegion& region) {
183 CGImageRef window_image = CGWindowListCreateImage(
184 CGRectNull, kCGWindowListOptionIncludingWindow,
185 window_id_, kCGWindowImageBoundsIgnoreFraming);
188 callback_->OnCaptureCompleted(NULL);
192 int bits_per_pixel = CGImageGetBitsPerPixel(window_image);
193 if (bits_per_pixel != 32) {
194 LOG(LS_ERROR) << "Unsupported window image depth: " << bits_per_pixel;
195 CFRelease(window_image);
196 callback_->OnCaptureCompleted(NULL);
200 int width = CGImageGetWidth(window_image);
201 int height = CGImageGetHeight(window_image);
202 CGDataProviderRef provider = CGImageGetDataProvider(window_image);
203 CFDataRef cf_data = CGDataProviderCopyData(provider);
204 DesktopFrame* frame = new BasicDesktopFrame(
205 DesktopSize(width, height));
207 int src_stride = CGImageGetBytesPerRow(window_image);
208 const uint8_t* src_data = CFDataGetBytePtr(cf_data);
209 for (int y = 0; y < height; ++y) {
210 memcpy(frame->data() + frame->stride() * y, src_data + src_stride * y,
211 DesktopFrame::kBytesPerPixel * width);
215 CFRelease(window_image);
217 callback_->OnCaptureCompleted(frame);
223 WindowCapturer* WindowCapturer::Create(const DesktopCaptureOptions& options) {
224 return new WindowCapturerMac();
227 } // namespace webrtc