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/base/macutils.h"
19 #include "webrtc/modules/desktop_capture/desktop_capture_options.h"
20 #include "webrtc/modules/desktop_capture/desktop_frame.h"
21 #include "webrtc/modules/desktop_capture/mac/desktop_configuration.h"
22 #include "webrtc/modules/desktop_capture/mac/full_screen_chrome_window_detector.h"
23 #include "webrtc/modules/desktop_capture/mac/window_list_utils.h"
24 #include "webrtc/system_wrappers/interface/logging.h"
25 #include "webrtc/system_wrappers/interface/scoped_refptr.h"
26 #include "webrtc/system_wrappers/interface/tick_util.h"
32 // Returns true if the window exists.
33 bool IsWindowValid(CGWindowID id) {
34 CFArrayRef window_id_array =
35 CFArrayCreate(NULL, reinterpret_cast<const void **>(&id), 1, NULL);
36 CFArrayRef window_array =
37 CGWindowListCreateDescriptionFromArray(window_id_array);
38 bool valid = window_array && CFArrayGetCount(window_array);
39 CFRelease(window_id_array);
40 CFRelease(window_array);
45 class WindowCapturerMac : public WindowCapturer {
47 explicit WindowCapturerMac(
48 scoped_refptr<FullScreenChromeWindowDetector>
49 full_screen_chrome_window_detector);
50 virtual ~WindowCapturerMac();
52 // WindowCapturer interface.
53 virtual bool GetWindowList(WindowList* windows) OVERRIDE;
54 virtual bool SelectWindow(WindowId id) OVERRIDE;
55 virtual bool BringSelectedWindowToFront() OVERRIDE;
57 // DesktopCapturer interface.
58 virtual void Start(Callback* callback) OVERRIDE;
59 virtual void Capture(const DesktopRegion& region) OVERRIDE;
64 // The window being captured.
65 CGWindowID window_id_;
67 scoped_refptr<FullScreenChromeWindowDetector>
68 full_screen_chrome_window_detector_;
70 DISALLOW_COPY_AND_ASSIGN(WindowCapturerMac);
73 WindowCapturerMac::WindowCapturerMac(
74 scoped_refptr<FullScreenChromeWindowDetector>
75 full_screen_chrome_window_detector)
78 full_screen_chrome_window_detector_(full_screen_chrome_window_detector) {
81 WindowCapturerMac::~WindowCapturerMac() {
84 bool WindowCapturerMac::GetWindowList(WindowList* windows) {
85 // Only get on screen, non-desktop windows.
86 CFArrayRef window_array = CGWindowListCopyWindowInfo(
87 kCGWindowListOptionOnScreenOnly | kCGWindowListExcludeDesktopElements,
92 // Check windows to make sure they have an id, title, and use window layer
94 CFIndex count = CFArrayGetCount(window_array);
95 for (CFIndex i = 0; i < count; ++i) {
96 CFDictionaryRef window = reinterpret_cast<CFDictionaryRef>(
97 CFArrayGetValueAtIndex(window_array, i));
98 CFStringRef window_title = reinterpret_cast<CFStringRef>(
99 CFDictionaryGetValue(window, kCGWindowName));
100 CFNumberRef window_id = reinterpret_cast<CFNumberRef>(
101 CFDictionaryGetValue(window, kCGWindowNumber));
102 CFNumberRef window_layer = reinterpret_cast<CFNumberRef>(
103 CFDictionaryGetValue(window, kCGWindowLayer));
104 if (window_title && window_id && window_layer) {
105 // Skip windows with layer=0 (menu, dock).
107 CFNumberGetValue(window_layer, kCFNumberIntType, &layer);
112 CFNumberGetValue(window_id, kCFNumberIntType, &id);
113 WindowCapturer::Window window;
115 if (!rtc::ToUtf8(window_title, &(window.title)) ||
116 window.title.empty()) {
119 windows->push_back(window);
123 CFRelease(window_array);
127 bool WindowCapturerMac::SelectWindow(WindowId id) {
128 if (!IsWindowValid(id))
134 bool WindowCapturerMac::BringSelectedWindowToFront() {
140 CFArrayRef window_id_array =
141 CFArrayCreate(NULL, reinterpret_cast<const void **>(&ids), 1, NULL);
143 CFArrayRef window_array =
144 CGWindowListCreateDescriptionFromArray(window_id_array);
145 if (window_array == NULL || 0 == CFArrayGetCount(window_array)) {
146 // Could not find the window. It might have been closed.
147 LOG(LS_INFO) << "Window not found";
148 CFRelease(window_id_array);
152 CFDictionaryRef window = reinterpret_cast<CFDictionaryRef>(
153 CFArrayGetValueAtIndex(window_array, 0));
154 CFNumberRef pid_ref = reinterpret_cast<CFNumberRef>(
155 CFDictionaryGetValue(window, kCGWindowOwnerPID));
158 CFNumberGetValue(pid_ref, kCFNumberIntType, &pid);
160 // TODO(jiayl): this will bring the process main window to the front. We
161 // should find a way to bring only the window to the front.
163 [[NSRunningApplication runningApplicationWithProcessIdentifier: pid]
164 activateWithOptions: NSApplicationActivateIgnoringOtherApps];
166 CFRelease(window_id_array);
167 CFRelease(window_array);
171 void WindowCapturerMac::Start(Callback* callback) {
175 callback_ = callback;
178 void WindowCapturerMac::Capture(const DesktopRegion& region) {
179 if (!IsWindowValid(window_id_)) {
180 callback_->OnCaptureCompleted(NULL);
184 CGWindowID on_screen_window = window_id_;
185 if (full_screen_chrome_window_detector_) {
186 CGWindowID full_screen_window =
187 full_screen_chrome_window_detector_->FindFullScreenWindow(window_id_);
189 if (full_screen_window != kCGNullWindowID)
190 on_screen_window = full_screen_window;
193 CGImageRef window_image = CGWindowListCreateImage(
194 CGRectNull, kCGWindowListOptionIncludingWindow,
195 on_screen_window, kCGWindowImageBoundsIgnoreFraming);
198 callback_->OnCaptureCompleted(NULL);
202 int bits_per_pixel = CGImageGetBitsPerPixel(window_image);
203 if (bits_per_pixel != 32) {
204 LOG(LS_ERROR) << "Unsupported window image depth: " << bits_per_pixel;
205 CFRelease(window_image);
206 callback_->OnCaptureCompleted(NULL);
210 int width = CGImageGetWidth(window_image);
211 int height = CGImageGetHeight(window_image);
212 CGDataProviderRef provider = CGImageGetDataProvider(window_image);
213 CFDataRef cf_data = CGDataProviderCopyData(provider);
214 DesktopFrame* frame = new BasicDesktopFrame(
215 DesktopSize(width, height));
217 int src_stride = CGImageGetBytesPerRow(window_image);
218 const uint8_t* src_data = CFDataGetBytePtr(cf_data);
219 for (int y = 0; y < height; ++y) {
220 memcpy(frame->data() + frame->stride() * y, src_data + src_stride * y,
221 DesktopFrame::kBytesPerPixel * width);
225 CFRelease(window_image);
227 callback_->OnCaptureCompleted(frame);
229 if (full_screen_chrome_window_detector_)
230 full_screen_chrome_window_detector_->UpdateWindowListIfNeeded(window_id_);
236 WindowCapturer* WindowCapturer::Create(const DesktopCaptureOptions& options) {
237 return new WindowCapturerMac(options.full_screen_chrome_window_detector());
240 } // namespace webrtc