Upstream version 9.38.198.0
[platform/framework/web/crosswalk.git] / src / third_party / webrtc / modules / desktop_capture / window_capturer_mac.mm
1 /*
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
13 #include <assert.h>
14 #include <ApplicationServices/ApplicationServices.h>
15 #include <Cocoa/Cocoa.h>
16 #include <CoreFoundation/CoreFoundation.h>
17
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"
27
28 namespace webrtc {
29
30 namespace {
31
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);
41
42   return valid;
43 }
44
45 class WindowCapturerMac : public WindowCapturer {
46  public:
47   explicit WindowCapturerMac(
48       scoped_refptr<FullScreenChromeWindowDetector>
49           full_screen_chrome_window_detector);
50   virtual ~WindowCapturerMac();
51
52   // WindowCapturer interface.
53   virtual bool GetWindowList(WindowList* windows) OVERRIDE;
54   virtual bool SelectWindow(WindowId id) OVERRIDE;
55   virtual bool BringSelectedWindowToFront() OVERRIDE;
56
57   // DesktopCapturer interface.
58   virtual void Start(Callback* callback) OVERRIDE;
59   virtual void Capture(const DesktopRegion& region) OVERRIDE;
60
61  private:
62   Callback* callback_;
63
64   // The window being captured.
65   CGWindowID window_id_;
66
67   scoped_refptr<FullScreenChromeWindowDetector>
68       full_screen_chrome_window_detector_;
69
70   DISALLOW_COPY_AND_ASSIGN(WindowCapturerMac);
71 };
72
73 WindowCapturerMac::WindowCapturerMac(
74     scoped_refptr<FullScreenChromeWindowDetector>
75         full_screen_chrome_window_detector)
76     : callback_(NULL),
77       window_id_(0),
78       full_screen_chrome_window_detector_(full_screen_chrome_window_detector) {
79 }
80
81 WindowCapturerMac::~WindowCapturerMac() {
82 }
83
84 bool WindowCapturerMac::GetWindowList(WindowList* windows) {
85   // Only get on screen, non-desktop windows.
86   CFArrayRef window_array = CGWindowListCopyWindowInfo(
87       kCGWindowListOptionOnScreenOnly | kCGWindowListExcludeDesktopElements,
88       kCGNullWindowID);
89   if (!window_array)
90     return false;
91
92   // Check windows to make sure they have an id, title, and use window layer
93   // other than 0.
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).
106       int layer;
107       CFNumberGetValue(window_layer, kCFNumberIntType, &layer);
108       if (layer != 0)
109         continue;
110
111       int id;
112       CFNumberGetValue(window_id, kCFNumberIntType, &id);
113       WindowCapturer::Window window;
114       window.id = id;
115       if (!rtc::ToUtf8(window_title, &(window.title)) ||
116           window.title.empty()) {
117         continue;
118       }
119       windows->push_back(window);
120     }
121   }
122
123   CFRelease(window_array);
124   return true;
125 }
126
127 bool WindowCapturerMac::SelectWindow(WindowId id) {
128   if (!IsWindowValid(id))
129     return false;
130   window_id_ = id;
131   return true;
132 }
133
134 bool WindowCapturerMac::BringSelectedWindowToFront() {
135   if (!window_id_)
136     return false;
137
138   CGWindowID ids[1];
139   ids[0] = window_id_;
140   CFArrayRef window_id_array =
141       CFArrayCreate(NULL, reinterpret_cast<const void **>(&ids), 1, NULL);
142
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);
149     return false;
150   }
151
152   CFDictionaryRef window = reinterpret_cast<CFDictionaryRef>(
153       CFArrayGetValueAtIndex(window_array, 0));
154   CFNumberRef pid_ref = reinterpret_cast<CFNumberRef>(
155       CFDictionaryGetValue(window, kCGWindowOwnerPID));
156
157   int pid;
158   CFNumberGetValue(pid_ref, kCFNumberIntType, &pid);
159
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.
162   bool result =
163       [[NSRunningApplication runningApplicationWithProcessIdentifier: pid]
164           activateWithOptions: NSApplicationActivateIgnoringOtherApps];
165
166   CFRelease(window_id_array);
167   CFRelease(window_array);
168   return result;
169 }
170
171 void WindowCapturerMac::Start(Callback* callback) {
172   assert(!callback_);
173   assert(callback);
174
175   callback_ = callback;
176 }
177
178 void WindowCapturerMac::Capture(const DesktopRegion& region) {
179   if (!IsWindowValid(window_id_)) {
180     callback_->OnCaptureCompleted(NULL);
181     return;
182   }
183
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_);
188
189     if (full_screen_window != kCGNullWindowID)
190       on_screen_window = full_screen_window;
191   }
192
193   CGImageRef window_image = CGWindowListCreateImage(
194       CGRectNull, kCGWindowListOptionIncludingWindow,
195       on_screen_window, kCGWindowImageBoundsIgnoreFraming);
196
197   if (!window_image) {
198     callback_->OnCaptureCompleted(NULL);
199     return;
200   }
201
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);
207     return;
208   }
209
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));
216
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);
222   }
223
224   CFRelease(cf_data);
225   CFRelease(window_image);
226
227   callback_->OnCaptureCompleted(frame);
228
229   if (full_screen_chrome_window_detector_)
230     full_screen_chrome_window_detector_->UpdateWindowListIfNeeded(window_id_);
231 }
232
233 }  // namespace
234
235 // static
236 WindowCapturer* WindowCapturer::Create(const DesktopCaptureOptions& options) {
237   return new WindowCapturerMac(options.full_screen_chrome_window_detector());
238 }
239
240 }  // namespace webrtc